• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# HiDebug接口使用示例(C/C++)
2
3<!--Kit: Performance Analysis Kit-->
4<!--Subsystem: HiviewDFX-->
5<!--Owner: @hello_harmony; @yu_haoqiaida-->
6<!--Designer: @kutcherzhou1-->
7<!--Tester: @gcw_KuLfPSbe-->
8<!--Adviser: @foryourself-->
9
10HiDebug C/C++接口功能独立,需要获取调试信息时直接调用。具体调用示例请参考下文。
11
12## 通用开发示例
13
14下文将展示如何在应用内调用HiDebug Ndk接口:
15
16步骤一:创建项目
17
181. 使用DevEco Studio新建工程,选择“Native C++”选项。
19
202. 编辑“CMakeLists.txt”文件,添加库依赖:
21
22   ```cmake
23   # 新增动态库依赖libhiappevent_ndk.z.solibhilog_ndk.z.so(日志输出)
24   target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so libohhidebug.so)
25   ```
26
273. 编辑“napi_init.cpp”文件,导入依赖的文件,并定义LOG_TAG及测试方法:
28
29   ```c++
30   #include "napi/native_api.h"
31   #include "hilog/log.h"
32   #include "hidebug/hidebug.h"
33
34   #undef LOG_TAG
35   #define LOG_TAG "testTag"
36   napi_value TestHiDebugNdk(napi_env env, napi_callback_info info) {
37       // todo 调用需测试的Ndk接口
38       return nullptr;
39   }
40   ```
41
42   将测试方法注册为ArkTS接口:
43
44   ```c++
45   static napi_value Init(napi_env env, napi_value exports)
46   {
47       napi_property_descriptor desc[] = {
48         {"testHiDebugNdk", nullptr, TestHiDebugNdk, nullptr, nullptr, nullptr, napi_default, nullptr}
49       };
50       napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
51       return exports;
52   }
53   ```
54
554. 编辑“index.d.ts”文件,定义ArkTS接口:
56
57   ```typescript
58   export const testHiDebugNdk: () => void;
59   ```
60
615. 编辑“Index.ets”文件,为Text组件添加点击事件。示例代码如下:
62
63   ```typescript
64   import testNapi from 'libentry.so';
65
66   @Entry
67   @Component
68   struct Index {
69     build() {
70       Row() {
71         Column() {
72           Button("testHiDebugNdk")
73             .type(ButtonType.Capsule)
74             .margin({
75               top: 20
76             })
77             .backgroundColor('#0D9FFB')
78             .width('60%')
79             .height('5%')
80             // 添加点击事件
81             .onClick(testNapi.testHiDebugNdk);
82         }
83         .width('100%')
84       }
85       .height('100%')
86     }
87   }
88   ```
89
90步骤二:运行工程
91
92点击DevEco Studio界面中的运行按钮,运行应用工程,然后点击“testHiDebugNdk”触发NDK接口调用。
93
94### 获取线程CPU使用率
95
96OH_HiDebug_GetAppThreadCpuUsage接口返回的数据为链表结构。使用完毕后,需通过OH_HiDebug_FreeThreadCpuUsage接口回收内存。详情请参考如下示例。
97
981. 参考[通用开发示例](#通用开发示例),并定义“TestHiDebugNdk”方法,在该方法中调用接口:
99
100   ```c++
101    static napi_value TestHiDebugNdk(napi_env env, napi_callback_info info)
102    {
103        HiDebug_ThreadCpuUsagePtr cpuUsage = OH_HiDebug_GetAppThreadCpuUsage();
104        if (cpuUsage != nullptr) {
105            do {
106                OH_LOG_INFO(LogType::LOG_APP,
107                            "GetAppThreadCpuUsage: threadId %{public}d, cpuUsage: %{public}f", cpuUsage->threadId, cpuUsage->cpuUsage);
108                cpuUsage = cpuUsage->next; // 获取下一个线程的cpu使用率对象指针。
109            } while(cpuUsage != nullptr);
110            OH_HiDebug_FreeThreadCpuUsage(&cpuUsage); // 释放内存,防止内存泄露。
111        }
112        return nullptr;
113    }
114   ```
115
1162. 触发接口调用并查看控制台输出,日志输出示例如下:
117   ```Text
118    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15025, cpuUsage: 0.000762
119    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15143, cpuUsage: 0.000000
120    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15144, cpuUsage: 0.000055
121    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15152, cpuUsage: 0.000000
122    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15154, cpuUsage: 0.000000
123    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15155, cpuUsage: 0.000000
124    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15156, cpuUsage: 0.000000
125    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15157, cpuUsage: 0.000000
126    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15158, cpuUsage: 0.000000
127    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15159, cpuUsage: 0.000000
128    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15160, cpuUsage: 0.000033
129    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15161, cpuUsage: 0.000077
130    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15162, cpuUsage: 0.000000
131    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15163, cpuUsage: 0.000033
132    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15171, cpuUsage: 0.000000
133    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15175, cpuUsage: 0.000011
134    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15176, cpuUsage: 0.000033
135   ```
136
137### 获取堆栈信息
138
139下文展示如何在应用内使用HiDebug进行主线程栈回溯。
140
141步骤一:创建项目
142
1431. 使用DevEco Studio新建一个Native C++工程,并新增文件“test_backtrace.cpp”与“test_backtrace.h”,目录结构如下:
144
145   ```yml
146   entry:
147     src:
148       main:
149         cpp:
150           - types:
151             - libentry:
152               - index.d.ts
153           - CMakeLists.txt
154           - napi_init.cpp
155           - test_backtrace.cpp
156           - test_backtrace.h
157         ets:
158           pages:
159             - Index.ets
160   ```
161
1622. 编辑“test_backtrace.h”文件,内容如下:
163
164   ```c++
165   #ifndef MYAPPLICATION_TESTBACKTRACE_H
166   #define MYAPPLICATION_TESTBACKTRACE_H
167
168   void InitSignalHandle();
169   void BacktraceFrames();
170
171   #endif // MYAPPLICATION_TESTBACKTRACE_H
172   ```
173
1743. 编辑“test_backtrace.cpp”文件, 内容如下:
175
176   ```c++
177   #include "test_backtrace.h"
178   #include <condition_variable>
179   #include <csignal>
180   #include <unistd.h>
181   #include <sys/syscall.h>
182   #include "hidebug/hidebug.h"
183   #include "hilog/log.h"
184
185   #define MAX_FRAME_SIZE 256 // 最大栈回溯深度,应根据业务场景调整该值。
186
187   namespace {
188       constexpr auto LOG_PRINT_DOMAIN = 0xFF00;
189       std::condition_variable cv_; // 用于控制线程与主线程的逻辑顺序。
190       std::mutex mutex_; // 用于控制线程与主线程的逻辑顺序。
191       int pcSize = -1;
192   }
193
194   class BackTraceObject { // 封装了抓栈过程中需要使用的资源,在使用过程中请注意线程安全和异步信号安全。
195   public:
196       static BackTraceObject& GetInstance();
197       BackTraceObject(const BackTraceObject&) = delete;
198       BackTraceObject& operator=(const BackTraceObject&) = delete;
199       BackTraceObject(BackTraceObject&&) = delete;
200       BackTraceObject& operator=(BackTraceObject&&) = delete;
201       bool Init(uint32_t size);
202       void Release();
203       int BackTraceFromFp(void* startFp, int size); // 该函数异步信号安全。
204       void SymbolicAddress(int index); // 该函数耗费性能,请避免频繁调用。
205       void PrintStackFrame(void* pc, const HiDebug_StackFrame& frame);
206   private:
207       BackTraceObject() = default;
208       ~BackTraceObject() = default;
209       HiDebug_Backtrace_Object backtraceObject_ = nullptr;
210       void** pcs_ = nullptr;
211   };
212
213   BackTraceObject& BackTraceObject::GetInstance() { // 单例模式,用于信号处理和请求抓栈线程的数据交互。注意该类非异步信号安全,业务逻辑应确保同一时刻仅单个线程访问。
214       static BackTraceObject instance;
215       return instance;
216   }
217
218   bool BackTraceObject::Init(uint32_t size) { // 初始化资源。
219       backtraceObject_ = OH_HiDebug_CreateBacktraceObject();
220       if (backtraceObject_ == nullptr) {
221           return false;
222       }
223       pcs_  = new (std::nothrow) void*[size]{nullptr};
224       if (pcs_ == nullptr) {
225           return false;
226       }
227       return true;
228   }
229
230   void BackTraceObject::Release() { // 释放资源。
231       OH_HiDebug_DestroyBacktraceObject(backtraceObject_);
232       backtraceObject_ = nullptr;
233       delete[] pcs_;
234       pcs_ = nullptr;
235   }
236
237   int BackTraceObject::BackTraceFromFp(void* startFp, int size) { // 栈回溯获取pc地址。
238       if (size <= MAX_FRAME_SIZE) {
239           return OH_HiDebug_BacktraceFromFp(backtraceObject_, startFp, pcs_, size); // OH_HiDebug_BacktraceFromFp接口调用示例。
240       }
241       return 0;
242   }
243
244   void BackTraceObject::PrintStackFrame(void* pc, const HiDebug_StackFrame& frame) { // 输出栈内容。
245       if (frame.type == HIDEBUG_STACK_FRAME_TYPE_JS) { // 根据栈帧的类型,区分不同的栈帧输出方式。
246           OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "TestTag",
247               "js stack frame info for pc: %{public}p is "
248               "relativePc: %{public}p "
249               "line: %{public}d "
250               "column: %{public}d "
251               "mapName: %{public}s "
252               "functionName: %{public}s "
253               "url: %{public}s "
254               "packageName: %{public}s.",
255               pc,
256               reinterpret_cast<void*>(frame.frame.js.relativePc),
257               frame.frame.js.line,
258               frame.frame.js.column,
259               frame.frame.js.mapName,
260               frame.frame.js.functionName,
261               frame.frame.js.url,
262               frame.frame.js.packageName
263           );
264       } else {
265           OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "TestTag",
266               "native stack frame info for pc: %{public}p is "
267               "relativePc: %{public}p "
268               "funcOffset: %{public}p "
269               "mapName: %{public}s "
270               "functionName: %{public}s "
271               "buildId: %{public}s "
272               "reserved: %{public}s.",
273               pc,
274               reinterpret_cast<void*>(frame.frame.native.relativePc),
275               reinterpret_cast<void*>(frame.frame.native.funcOffset),
276               frame.frame.native.mapName,
277               frame.frame.native.functionName,
278               frame.frame.native.buildId,
279               frame.frame.native.reserved
280           );
281       }
282   }
283
284   void BackTraceObject::SymbolicAddress(int index) { // 栈解析接口。
285       if (index < 0 || index >= MAX_FRAME_SIZE) {
286           return;
287       }
288       OH_HiDebug_SymbolicAddress(backtraceObject_, pcs_[index], this, [](void* pc, void* arg, const HiDebug_StackFrame* frame) {
289           reinterpret_cast<BackTraceObject*>(arg)->PrintStackFrame(pc, *frame);
290       }); // 调用OH_HiDebug_SymbolicAddress接口解析栈。
291   }
292
293   void SignalHandler(int sig, siginfo_t *si, void* context) { // 信号处理函数。
294       if (sig != SIGUSR1) {
295           return;
296       }
297       auto startFp = reinterpret_cast<ucontext_t *>(context)->uc_mcontext.regs[29]; // 读取寄存器X29中存放的fp地址。
298       std::unique_lock<std::mutex> lock(mutex_);
299       pcSize = BackTraceObject::GetInstance().BackTraceFromFp(reinterpret_cast<void*>(startFp), MAX_FRAME_SIZE); // 该函数异步信号安全,仅异步信号安全函数可在信号处理函数内使用。
300       cv_.notify_all(); // 栈回溯结束,通知给请求回栈的线程。
301   }
302
303   void InitSignalHandle() { // 注册信号处理函数。
304       struct sigaction action{0};
305       sigfillset(&action.sa_mask);
306       action.sa_sigaction = SignalHandler;
307       action.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
308       sigaction(SIGUSR1, &action, nullptr); // 注意: 所使用的信号应避免与原有信号冲突。
309   }
310
311   void BacktraceFrames() { // 该接口非线程安全,同一时刻只能由一个线程使用。
312       if (!BackTraceObject::GetInstance().Init(MAX_FRAME_SIZE)) { // 注意:在调用栈回溯函数之前,需申请资源,且不可重复初始化。
313           BackTraceObject::GetInstance().Release();
314           OH_LOG_Print(LOG_APP, LOG_WARN, LOG_PRINT_DOMAIN, "TestTag", "failed init backtrace object.");
315           return;
316       }
317       pcSize = -1; // 初始化pcSize为-1。
318       siginfo_t si{0};
319       si.si_signo = SIGUSR1;
320       if (syscall(SYS_rt_tgsigqueueinfo, getpid(), getpid(), si.si_signo, &si) != 0) { // 发送信号给主线程以触发信号处理函数。
321           BackTraceObject::GetInstance().Release();
322           OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "TestTag", "failed send sig");
323           return;
324       }
325       {
326           std::unique_lock<std::mutex> lock(mutex_);
327           cv_.wait(lock, []{return pcSize >= 0;}); // 等待主线程在信号处理函数内进行栈回溯。
328       }
329       for (int i = 0; i < pcSize; i++) {
330           BackTraceObject::GetInstance().SymbolicAddress(i); // 主线程获取pc后,对pc值进行栈解析。
331       }
332       BackTraceObject::GetInstance().Release(); // 栈回溯并且解析结束后,及时释放资源。
333   }
334   ```
335
3364. 编辑“CMakeLists.txt”文件,添加库依赖:
337
338   ```cmake
339   # 新增动态库依赖libohhidebug.solibhilog_ndk.z.so(日志输出)
340   add_library(entry SHARED napi_init.cpp test_backtrace.cpp)
341   target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so libohhidebug.so)
342   ```
343
3445. 编辑“napi_init.cpp”文件,导入依赖文件并定义测试方法。
345
346   ```c++
347   #include "napi/native_api.h"
348   #include "test_backtrace.h"
349   #include <thread>
350
351   #include "napi/native_api.h"
352   #include "test_backtrace.h"
353
354   __attribute((noinline)) __attribute((optnone)) void TestNativeFrames(int i)
355   {
356       if (i > 0) {
357           TestNativeFrames(i - 1);
358           return;
359       }
360       std::this_thread::sleep_for(std::chrono::seconds(3)); //模拟主线程阻塞。
361   }
362   __attribute((noinline)) __attribute((optnone)) napi_value TestHiDebugNdk(napi_env env, napi_callback_info info)
363   {
364       std::thread([]{
365           std::this_thread::sleep_for(std::chrono::seconds(1));
366           BacktraceFrames();
367       }).detach(); // 启用第二个线程,对主线程进行抓栈。
368       TestNativeFrames(1);
369       return nullptr;
370   }
371   ```
372
373   注册“TestHiDebugNdk”为ArkTS接口并初始化主线程的信号处理函数:
374
375   ```c++
376   static napi_value Init(napi_env env, napi_value exports) {
377       napi_property_descriptor desc[] = {
378           {"testHiDebugNdk", nullptr, TestHiDebugNdk, nullptr, nullptr, nullptr, napi_default, nullptr}};
379       napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
380       InitSignalHandle(); // 初始化信号处理函数。
381       return exports;
382   }
383   ```
384
3856. 编辑“index.d.ts”文件,声明ArkTS接口:
386
387   ```typescript
388   export const testHiDebugNdk: () => void;
389   ```
390
3917. 编辑“Index.ets”文件,添加触发接口调用的按钮,示例代码如下:
392
393   ```typescript
394   import testNapi from 'libentry.so';
395   function testJsFrame(i : number) : void {
396     if (i > 0) {
397       return testJsFrame(i-1);
398     }
399     return testNapi.testHiDebugNdk();
400   }
401
402   @Entry
403   @Component
404   struct Index {
405     @State message: string = 'Hello World';
406     build() {
407       Row() {
408         Column() {
409           Text(this.message)
410             .fontSize($r('app.float.page_text_font_size'))
411             .fontWeight(FontWeight.Bold)
412             .onClick(() => {
413               testJsFrame(3);
414             })
415         }
416         .width('100%')
417       }
418       .height('100%')
419     }
420   }
421   ```
422
423步骤二:运行工程
424
4251. 点击DevEco Studio界面中的运行按钮,然后单击应用界面上的“Hello World”文本。
426
4272. 在DevEco Studio底部切换到“Log”窗口,设置日志过滤条件为“TestTag”,即可查看相关日志:
428
429   ```Text
430   ...
431   05-10 17:26:24.229 8324-8865 A0FF00/com.exa...ation/TestTag com.examp...lication I native stack frame info for pc: 0x5b3e193d24 is relativePc: 0xd3d24 funcOffset: 0x60 mapName: 0x5b3e831880 functionName: std::__n1::this_thread::sleep_for(std::__n1::chrono::duration<long long, std::__n1::ratio<1l, 1000000000l>> const&) buildId: 459a4d9b28503b85a67ca37bda676b03da86e7d6 reserved: (null).
432   05-10 17:26:24.229 8324-8865 A0FF00/com.exa...ation/TestTag com.examp...lication I native stack frame info for pc: 0x5b3e208524 is relativePc: 0x8524 funcOffset: 0xbc mapName: 0x5b3e822e30 functionName: void std::__n1::this_thread::sleep_for<long long, std::__n1::ratio<1l, 1l>>(std::n1::chrono::duration<long long, std::n1::ratio<1l, 1l>> const&) buildId: 18a155ee0e687664c5f2c552762efb5ff9ee3724 reserved: (null).
433   05-10 17:26:24.229 8324-8865 A0FF00/com.exa...ation/TestTag com.examp...lication I native stack frame info for pc: 0x5b3e208434 is relativePc: 0x8434 funcOffset: 0x68 mapName: 0x5b3e822d10 functionName: TestNativeFrames(int) buildId: 18a155ee0e687664c5f2c552762efb5ff9ee3724 reserved: (null).
434   05-10 17:26:24.229 8324-8865 A0FF00/com.exa...ation/TestTag com.examp...lication I native stack frame info for pc: 0x5b3e20840c is relativePc: 0x840c funcOffset: 0x40 mapName: 0x5b3e822ec0 functionName: TestNativeFrames(int) buildId: 18a155ee0e687664c5f2c552762efb5ff9ee3724 reserved: (null).
435   05-10 17:26:24.229 8324-8865 A0FF00/com.exa...ation/TestTag com.examp...lication I native stack frame info for pc: 0x5b3e208654 is relativePc: 0x8654 funcOffset: 0xd4 mapName: 0x5b3e822f50 functionName: Add(napi_env*, napi_callback_info*) buildId: 18a155ee0e687664c5f2c552762efb5ff9ee3724 reserved: (null).
436   ...
437   05-10 17:26:24.234 8324-8865 A0FF00/com.exa...ation/TestTag com.examp...lication I js stack frame info for pc: 0x5a3d773f92 is relativePc: 0x2f92 line: 17 column: 16 mapName: /data/storage/el1/bundle/entry.hap functionName: testJsFrame url: entry|entry|1.0.0|src/main/ets/pages/Index.ts packageName: .
438   05-10 17:26:24.235 8324-8865 A0FF00/com.exa...ation/TestTag com.examp...lication I js stack frame info for pc: 0x5a3d773f6c is relativePc: 0x2f6c line: 13 column: 16 mapName: /data/storage/el1/bundle/entry.hap functionName: testJsFrame url: entry|entry|1.0.0|src/main/ets/pages/Index.ts packageName: .
439   05-10 17:26:24.235 8324-8865 A0FF00/com.exa...ation/TestTag com.examp...lication I js stack frame info for pc: 0x5a3d773f6c is relativePc: 0x2f6c line: 13 column: 16 mapName: /data/storage/el1/bundle/entry.hap functionName: testJsFrame url: entry|entry|1.0.0|src/main/ets/pages/Index.ts packageName: .
440   05-10 17:26:24.235 8324-8865 A0FF00/com.exa...ation/TestTag com.examp...lication I js stack frame info for pc: 0x5a3d773f6c is relativePc: 0x2f6c line: 13 column: 16 mapName: /data/storage/el1/bundle/entry.hap functionName: testJsFrame url: entry|entry|1.0.0|src/main/ets/pages/Index.ts packageName: .
441   05-10 17:26:24.235 8324-8865 A0FF00/com.exa...ation/TestTag com.examp...lication I js stack frame info for pc: 0x5a3d7743f0 is relativePc: 0x33f0 line: 69 column: 17 mapName: /data/storage/el1/bundle/entry.hap functionName: anonymous url: entry|entry|1.0.0|src/main/ets/pages/Index.ts packageName: .
442   ...
443   ```