* 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 "test_backtrace.h"
#include <condition_variable>
#include <csignal>
#include <unistd.h>
#include <sys/syscall.h>
#include "hidebug/hidebug.h"
#include "hilog/log.h"
#define MAX_FRAME_SIZE 256
namespace {
constexpr auto LOG_PRINT_DOMAIN = 0xFF00;
}
class BackTraceObject {
public:
static BackTraceObject& GetInstance();
BackTraceObject(const BackTraceObject&) = delete;
BackTraceObject& operator=(const BackTraceObject&) = delete;
BackTraceObject(BackTraceObject&&) = delete;
BackTraceObject& operator=(BackTraceObject&&) = delete;
bool Init(uint32_t size);
void Release();
int BackTraceFromFp(void* startFp, int size);
void SymbolicAddress(int index);
void PrintStackFrame(void* pc, const HiDebug_StackFrame& frame);
private:
BackTraceObject() = default;
~BackTraceObject() = default;
HiDebug_Backtrace_Object backtraceObject_ = nullptr;
void** pcs_ = nullptr;
};
BackTraceObject& BackTraceObject::GetInstance()
{
static BackTraceObject instance;
return instance;
}
bool BackTraceObject::Init(uint32_t size)
{
backtraceObject_ = OH_HiDebug_CreateBacktraceObject();
if (backtraceObject_ == nullptr || size > MAX_FRAME_SIZE) {
return false;
}
pcs_ = new (std::nothrow) void* [size]{nullptr};
if (pcs_ == nullptr) {
return false;
}
return true;
}
void BackTraceObject::Release()
{
OH_HiDebug_DestroyBacktraceObject(backtraceObject_);
backtraceObject_ = nullptr;
delete[] pcs_;
pcs_ = nullptr;
}
int BackTraceObject::BackTraceFromFp(void* startFp, int size)
{
if (size <= MAX_FRAME_SIZE) {
return OH_HiDebug_BacktraceFromFp(backtraceObject_, startFp, pcs_, size);
}
return 0;
}
void BackTraceObject::PrintStackFrame(void* pc, const HiDebug_StackFrame& frame)
{
if (frame.type == HIDEBUG_STACK_FRAME_TYPE_JS) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "testTag",
"js stack frame info for pc: %{public}p is "
"relativePc: %{public}p "
"line: %{public}d "
"column: %{public}d "
"mapName: %{public}s "
"functionName: %{public}s "
"url: %{public}s "
"packageName: %{public}s.",
pc,
reinterpret_cast<void*>(frame.frame.js.relativePc),
frame.frame.js.line,
frame.frame.js.column,
frame.frame.js.mapName,
frame.frame.js.functionName,
frame.frame.js.url,
frame.frame.js.packageName);
} else {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "testTag",
"native stack frame info for pc: %{public}p is "
"relativePc: %{public}p "
"funcOffset: %{public}p "
"mapName: %{public}s "
"functionName: %{public}s "
"buildId: %{public}s "
"reserved: %{public}s.",
pc,
reinterpret_cast<void*>(frame.frame.native.relativePc),
reinterpret_cast<void*>(frame.frame.native.funcOffset),
frame.frame.native.mapName,
frame.frame.native.functionName,
frame.frame.native.buildId,
frame.frame.native.reserved);
}
}
void BackTraceObject::SymbolicAddress(int index)
{
if (index < 0 || index >= MAX_FRAME_SIZE) {
return;
}
OH_HiDebug_SymbolicAddress(backtraceObject_, pcs_[index], this,
[] (void* pc, void* arg, const HiDebug_StackFrame* frame) {
reinterpret_cast<BackTraceObject*>(arg)->PrintStackFrame(pc, *frame);
});
}
void BacktraceCurrentThread()
{
if (!BackTraceObject::GetInstance().Init(MAX_FRAME_SIZE)) {
BackTraceObject::GetInstance().Release();
OH_LOG_Print(LOG_APP, LOG_WARN, LOG_PRINT_DOMAIN, "testTag", "failed init backtrace object.");
return;
}
int pcSize = BackTraceObject::GetInstance().BackTraceFromFp(__builtin_frame_address(0), MAX_FRAME_SIZE);
for (int i = 0; i < pcSize; i++) {
BackTraceObject::GetInstance().SymbolicAddress(i);
}
BackTraceObject::GetInstance().Release();
}