* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "fp_backtrace.h"
#if is_ohos && !is_mingw && (defined(__aarch64__) || defined(__x86_64__))
#include <mutex>
#include "dfx_ark.h"
#include "dfx_log.h"
#include "dfx_maps.h"
#include "dfx_symbols.h"
#include "dfx_util.h"
#include "memory_reader.h"
#include "smart_fd.h"
#include "stack_utils.h"
#include "unwinder.h"
#endif
namespace OHOS {
namespace HiviewDFX {
#if is_ohos && !is_mingw && (defined(__aarch64__) || defined(__x86_64__))
namespace {
#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_TAG "FpBacktrace"
#define LOG_DOMAIN 0xD002D11
std::atomic<uintptr_t> g_arkStubBegin{0};
std::atomic<uintptr_t> g_arkStubEnd{0};
std::atomic_bool g_updateArkStubFlag{false};
}
extern "C" bool ffrt_get_current_coroutine_stack(void** stackAddr, size_t* size) __attribute__((weak));
class FpBacktraceImpl : public FpBacktrace {
public:
bool Init();
uint32_t BacktraceFromFp(void* startFp, void** pcArray, uint32_t size, bool adjustPc = false) override;
DfxFrame* SymbolicAddress(void* pc) override;
private:
void BacktraceArkFrame(ArkStepParam &arkParam, MemoryReader& memoryReader, uint64_t& staticArkFrameIndex) const;
uint32_t BacktraceFromFp(void* startFp, void** pcArray, uint32_t size,
MemoryReader& memoryReader, bool adjustPc) const;
bool GetCurrentThreadRange(uintptr_t startFp, uintptr_t& threadBegin, uintptr_t& threadEnd);
std::shared_ptr<DfxMaps> maps_ = nullptr;
Unwinder unwinder_{false};
uintptr_t mainStackBegin_{0};
uintptr_t mainStackEnd_{0};
uintptr_t staticArkBegin_{0};
uintptr_t staticArkEnd_{0};
std::map<void*, std::unique_ptr<DfxFrame>> cachedFrames_;
std::mutex mutex_;
};
bool FpBacktraceImpl::Init()
{
static std::once_flag flag;
std::call_once(flag, []() {
if (ffrt_get_current_coroutine_stack) {
void* stackBegin = 0;
size_t stackSize = 0;
ffrt_get_current_coroutine_stack(&stackBegin, &stackSize);
}
DfxArk::Instance().InitArkFunction("step_ark");
});
maps_ = DfxMaps::Create(0, false);
if (maps_ == nullptr) {
DFXLOGI("failed creat maps");
return false;
}
maps_->GetStackRange(mainStackBegin_, mainStackEnd_);
maps_->GetStaticArkRange(staticArkBegin_, staticArkEnd_);
return true;
}
uint32_t FpBacktraceImpl::BacktraceFromFp(void* startFp, void** pcArray, uint32_t size, bool adjustPc)
{
if (!maps_ || startFp == nullptr || pcArray == nullptr || size == 0) {
return 0;
}
uintptr_t stackBegin = 0;
uintptr_t stackEnd = 0;
if (GetCurrentThreadRange(reinterpret_cast<uintptr_t>(startFp), stackBegin, stackEnd)) {
ThreadMemoryReader memoryReader(stackBegin, stackEnd);
return BacktraceFromFp(startFp, pcArray, size, memoryReader, adjustPc);
}
ProcessMemoryReader memoryReader;
return BacktraceFromFp(startFp, pcArray, size, memoryReader, adjustPc);
}
void FpBacktraceImpl::BacktraceArkFrame(ArkStepParam &arkParam, MemoryReader& memoryReader,
uint64_t &staticArkFrameIndex) const
{
if (*(arkParam.pc) >= staticArkBegin_ && *(arkParam.pc) < staticArkEnd_) {
staticArkFrameIndex = 0;
arkParam.frameIndex = staticArkFrameIndex;
*(arkParam.frameType) = FrameType::STATIC_JS_FRAME;
}
DfxArk::Instance().StepArkFrame(&memoryReader, [](void* memoryReader, uintptr_t addr, uintptr_t* val) {
return reinterpret_cast<MemoryReader*>(memoryReader)->ReadMemory(addr, val, sizeof(uintptr_t));
}, &arkParam);
if (*(arkParam.frameType) == FrameType::STATIC_JS_FRAME) {
staticArkFrameIndex++;
}
}
uint32_t FpBacktraceImpl::BacktraceFromFp(void* startFp, void** pcArray, uint32_t size,
MemoryReader& memoryReader, bool adjustPc) const
{
uint32_t index = 0;
bool isJsFrame = false;
FrameType frameType = FrameType::NATIVE_FRAME;
uint64_t staticArkFrameIndex = 0;
uintptr_t registerState[] = {reinterpret_cast<uintptr_t>(startFp), 0};
uintptr_t sp = 0;
uintptr_t arkStubBegin{0};
uintptr_t arkStubEnd{0};
if (!g_updateArkStubFlag.load(std::memory_order_acquire)) {
maps_->GetArkStackRange(arkStubBegin, arkStubEnd);
} else {
arkStubBegin = g_arkStubBegin.load(std::memory_order_relaxed);
arkStubEnd = g_arkStubEnd.load(std::memory_order_relaxed);
}
while (index < size) {
constexpr auto fpIndex = 0;
constexpr auto pcIndex = 1;
uintptr_t preFp = registerState[fpIndex];
if (isJsFrame || (registerState[pcIndex] < arkStubEnd && registerState[pcIndex] >= arkStubBegin) ||
(registerState[pcIndex] < staticArkEnd_ && registerState[pcIndex] >= staticArkBegin_)) {
ArkStepParam arkParam(®isterState[fpIndex], &sp, ®isterState[pcIndex], &isJsFrame, &frameType,
staticArkFrameIndex);
BacktraceArkFrame(arkParam, memoryReader, staticArkFrameIndex);
} else {
if (!memoryReader.ReadMemory(registerState[fpIndex], registerState, sizeof(registerState))) {
break;
}
}
constexpr auto pcBound = 0x1000;
if (registerState[pcIndex] <= pcBound) {
if (index != 0) {
break;
}
ArkStepParam arkParam(®isterState[fpIndex], &sp, ®isterState[pcIndex], &isJsFrame, &frameType,
staticArkFrameIndex);
BacktraceArkFrame(arkParam, memoryReader, staticArkFrameIndex);
}
uintptr_t curPc = registerState[pcIndex];
#if defined(__aarch64__)
if (adjustPc && !isJsFrame && frameType == FrameType::NATIVE_FRAME && curPc > 0x4) {
curPc -= 0x4;
}
#endif
uintptr_t pc = StripPac(curPc, 0);
auto realPc = reinterpret_cast<void *>(pc);
if (realPc != nullptr) {
pcArray[index++] = realPc;
}
if (frameType != FrameType::STATIC_JS_FRAME &&
(registerState[fpIndex] <= preFp || registerState[fpIndex] == 0)) {
break;
}
}
return index;
}
bool FpBacktraceImpl::GetCurrentThreadRange(uintptr_t startFp, uintptr_t& threadBegin, uintptr_t& threadEnd)
{
if (getpid() == gettid() && startFp >= mainStackBegin_ && startFp < mainStackEnd_) {
threadBegin = mainStackBegin_;
threadEnd = mainStackEnd_;
return true;
}
if (StackUtils::GetSelfStackRange(threadBegin, threadEnd)) {
if (startFp >= threadBegin && startFp < threadEnd) {
return true;
}
}
size_t stackSize = 0;
if (ffrt_get_current_coroutine_stack &&
ffrt_get_current_coroutine_stack(reinterpret_cast<void**>(&threadBegin), &stackSize)) {
if (startFp >= threadBegin && startFp < threadBegin + stackSize) {
threadEnd = threadBegin + stackSize;
return true;
}
}
return false;
}
DfxFrame* FpBacktraceImpl::SymbolicAddress(void* pc)
{
std::unique_lock<std::mutex> lock(mutex_);
auto cachedFrame = cachedFrames_.emplace(pc, nullptr);
auto& frame = cachedFrame.first->second;
if (!cachedFrame.second) {
return frame.get();
}
frame = std::unique_ptr<DfxFrame>(new (std::nothrow) DfxFrame());
if (!frame) {
return nullptr;
}
unwinder_.GetFrameByPc(reinterpret_cast<uintptr_t>(pc), maps_, *(frame));
if (frame->map == nullptr) {
frame = nullptr;
return nullptr;
}
return frame.get();
}
#endif
FpBacktrace* FpBacktrace::CreateInstance()
{
#if is_ohos && !is_mingw && (defined(__aarch64__) || defined(__x86_64__))
auto fpBacktraceImpl = new (std::nothrow) FpBacktraceImpl();
if (fpBacktraceImpl == nullptr) {
return nullptr;
}
if (fpBacktraceImpl->Init()) {
return fpBacktraceImpl;
}
delete fpBacktraceImpl;
#endif
return nullptr;
}
void FpBacktrace::UpdateArkStackRange(uintptr_t arkStubBegin, uintptr_t arkStubEnd)
{
#if is_ohos && !is_mingw && (defined(__aarch64__) || defined(__x86_64__))
DFXLOGI("UpdateArkStackRange.");
g_arkStubBegin.store(arkStubBegin, std::memory_order_relaxed);
g_arkStubEnd.store(arkStubEnd, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_release);
g_updateArkStubFlag.store(true, std::memory_order_relaxed);
#endif
}
}
}