HiDebug接口使用示例(C/C++)
HiDebug C/C++接口功能独立,需要获取调试信息时直接调用。具体调用示例请参考下文。
通用开发示例
下文展示如何在应用内使用HiDebug NDK接口以进行线程栈回溯,且获取进程内线程的CPU使用率:
步骤一:创建项目
-
使用DevEco Studio新建一个Native C++工程,并新增文件“test_backtrace.cpp”与“test_backtrace.h”,目录结构如下:
entry:src:main:cpp:- types:- libentry:- index.d.ts- CMakeLists.txt- napi_init.cpp- test_backtrace.cpp- test_backtrace.hets:pages:- Index.ets -
编辑“test_backtrace.h”文件,内容如下:
#ifndef MYAPPLICATION_TESTBACKTRACE_H#define MYAPPLICATION_TESTBACKTRACE_Hvoid BacktraceCurrentThread();#endif // MYAPPLICATION_TESTBACKTRACE_H -
编辑“test_backtrace.cpp”文件, 内容如下:
#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) // 栈回溯获取pc地址。{if (size <= MAX_FRAME_SIZE) {return OH_HiDebug_BacktraceFromFp(backtraceObject_, startFp, pcs_, size); // OH_HiDebug_BacktraceFromFp接口调用示例。}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);}); // 调用OH_HiDebug_SymbolicAddress接口解析栈。}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); // 主线程获取pc后,对pc值进行栈解析。}BackTraceObject::GetInstance().Release(); // 栈回溯并且解析结束后,及时释放资源。} -
编辑“CMakeLists.txt”文件,添加库依赖:
# 新增动态库依赖libohhidebug.so和libhilog_ndk.z.so(日志输出)add_library(entry SHARED napi_init.cpp test_backtrace.cpp)target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so libohhidebug.so) -
编辑“napi_init.cpp”文件,导入依赖文件并定义测试方法:
#include <thread>#include "hidebug/hidebug.h"#include "hilog/log.h"#include "test_backtrace.h"#undef LOG_TAG#define LOG_TAG "testTag"__attribute((noinline)) __attribute((optnone)) void TestNativeFrames(int i){if (i > 0) {TestNativeFrames(i - 1);return;}BacktraceCurrentThread();}__attribute((noinline)) __attribute((optnone)) napi_value TestBackTrace(napi_env env, napi_callback_info info){TestNativeFrames(1);return nullptr;}napi_value TestGetThreadCpuUsage(napi_env env, napi_callback_info info){HiDebug_ThreadCpuUsagePtr cpuUsage = OH_HiDebug_GetAppThreadCpuUsage();while (cpuUsage != nullptr) {OH_LOG_INFO(LogType::LOG_APP,"GetAppThreadCpuUsage: threadId %{public}d, cpuUsage: %{public}f", cpuUsage->threadId, cpuUsage->cpuUsage);cpuUsage = cpuUsage->next; // 获取下一个线程的cpu使用率对象指针。}OH_HiDebug_FreeThreadCpuUsage(&cpuUsage); // 释放内存,防止内存泄露。return nullptr;}注册“TestHiDebugNdk”为ArkTS接口并初始化主线程的信号处理函数:
napi_property_descriptor desc[] = {{ "testGetThreadCpuUsage", nullptr, TestGetThreadCpuUsage, nullptr, nullptr, nullptr, napi_default, nullptr },{ "testBackTrace", nullptr, TestBackTrace, nullptr, nullptr, nullptr, napi_default, nullptr },}; -
编辑“index.d.ts”文件,声明ArkTS接口:
export const testGetThreadCpuUsage: () => void;export const testBackTrace: () => void; -
编辑“Index.ets”文件,添加触发接口调用的按钮,示例代码如下:
导入依赖:
import testNapi from 'libentry.so';定义测试方法:
function testBackTraceJsFrame(i : number) : void {if (i > 0) {return testBackTraceJsFrame(i-1);}return testNapi.testBackTrace();}function testBackTrace() : void {testBackTraceJsFrame(3);}function testGetThreadCpuUsage() : void {testNapi.testGetThreadCpuUsage();}添加按钮以触发接口调用:
Button('testGetThreadCpuUsage').type(ButtonType.Capsule).margin({top: 20}).backgroundColor('#0D9FFB').width('60%').height('5%')// 添加点击事件.onClick(testGetThreadCpuUsage);Button('testHiDebugBackTrace').type(ButtonType.Capsule).margin({top: 20}).backgroundColor('#0D9FFB').width('60%').height('5%')// 添加点击事件.onClick(testBackTrace);
步骤二:运行工程
-
点击DevEco Studio界面中的运行按钮,然后分别单击应用界面上的“testGetThreadCpuUsage”和“testHiDebugBackTrace”按钮。
-
在DevEco Studio底部切换到“Log”窗口,设置日志过滤条件为“testTag”,即可查看相关日志:
...10-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19261, cpuUsage: 0.00010410-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19381, cpuUsage: 0.00000010-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19382, cpuUsage: 0.00004010-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19383, cpuUsage: 0.00001010-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19384, cpuUsage: 0.00000110-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19386, cpuUsage: 0.00003810-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19387, cpuUsage: 0.00000010-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19388, cpuUsage: 0.00000710-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19389, cpuUsage: 0.00000410-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19390, cpuUsage: 0.00000710-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19391, cpuUsage: 0.00000610-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19393, cpuUsage: 0.00000110-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19394, cpuUsage: 0.00000410-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19397, cpuUsage: 0.00000210-22 15:46:05.933 19261-19261 A00000/com.sam...gtool/testTag com.sampl...ebugtool I GetAppThreadCpuUsage: threadId 19401, cpuUsage: 0.000001...10-22 15:46:13.351 19261-19261 A0FF00/com.sam...gtool/testTag com.sampl...ebugtool I native stack frame info for pc: ************ is relativePc: ****** funcOffset: 0x38 mapName: /data/storage/el1/bundle/libs/arm64/libentry.so functionName: TestNativeFrames(int) buildId: b6d3429f6e2e594b1c696e13049dae7e51694099 reserved: (null).10-22 15:46:13.351 19261-19261 A0FF00/com.sam...gtool/testTag com.sampl...ebugtool I native stack frame info for pc: ************ is relativePc: ****** funcOffset: 0x30 mapName: /data/storage/el1/bundle/libs/arm64/libentry.so functionName: TestNativeFrames(int) buildId: b6d3429f6e2e594b1c696e13049dae7e51694099 reserved: (null).10-22 15:46:13.351 19261-19261 A0FF00/com.sam...gtool/testTag com.sampl...ebugtool I native stack frame info for pc: ************ is relativePc: ****** funcOffset: 0x1c mapName: /data/storage/el1/bundle/libs/arm64/libentry.so functionName: TestBackTrace(napi_env__*, napi_callback_info__*) buildId: b6d3429f6e2e594b1c696e13049dae7e51694099 reserved: (null)....10-22 15:46:13.354 19261-19261 A0FF00/com.sam...gtool/testTag com.sampl...ebugtool I js stack frame info for pc: ************ is relativePc: ****** line: 27 column: 21 mapName: /data/storage/el1/bundle/entry.hap functionName: testBackTraceJsFrame url: entry|entry|1.0.0|src/main/ets/pages/Index.ts packageName: .10-22 15:46:13.354 19261-19261 A0FF00/com.sam...gtool/testTag com.sampl...ebugtool I js stack frame info for pc: ************ is relativePc: ****** line: 25 column: 16 mapName: /data/storage/el1/bundle/entry.hap functionName: testBackTraceJsFrame url: entry|entry|1.0.0|src/main/ets/pages/Index.ts packageName: .10-22 15:46:13.354 19261-19261 A0FF00/com.sam...gtool/testTag com.sampl...ebugtool I js stack frame info for pc: ************ is relativePc: ****** line: 25 column: 16 mapName: /data/storage/el1/bundle/entry.hap functionName: testBackTraceJsFrame url: entry|entry|1.0.0|src/main/ets/pages/Index.ts packageName: .10-22 15:46:13.354 19261-19261 A0FF00/com.sam...gtool/testTag com.sampl...ebugtool I js stack frame info for pc: ************ is relativePc: ****** line: 25 column: 16 mapName: /data/storage/el1/bundle/entry.hap functionName: testBackTraceJsFrame url: entry|entry|1.0.0|src/main/ets/pages/Index.ts packageName: .......