• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Using HiDebug APIs (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
10The HiDebug C/C++ APIs are independent. You can call them to obtain debugging information. For details, see the following examples.
11
12## How to Develop
13
14The following describes how to call the HiDebug NDK APIs in an application.
15
16Step 1: Create a project
17
181. Use DevEco Studio to create a project and select **Native C++**.
19
202. In the **CMakeLists.txt** file, add the dependencies.
21
22   ```cmake
23   # Add **libhiappevent_ndk.z.so** and **libhilog_ndk.z.so** (log output).
24   target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so libohhidebug.so)
25   ```
26
273. Import the dependencies to the **napi_init.cpp** file, and define **LOG_TAG** and the test method.
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       // Call the NDK APIs to test.
38       return nullptr;
39   }
40   ```
41
42   Register the test method as an ArkTS API.
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. In the **index.d.ts** file, define the ArkTS API.
56
57   ```typescript
58   export const testHiDebugNdk: () => void;
59   ```
60
615. In the **Index.ets** file, add a click event to the **Text** component. The sample code is as follows:
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             // Add a click event.
81             .onClick(testNapi.testHiDebugNdk);
82         }
83         .width('100%')
84       }
85       .height('100%')
86     }
87   }
88   ```
89
90Step 2: Run the project
91
92Click the **Run** button in DevEco Studio to run the project, and click **testHiDebugNdk** to trigger the calling of the NDK API.
93
94### Obtaining the CPU Usage of a Thread
95
96The data returned by the **OH_HiDebug_GetAppThreadCpuUsage** API is in the linked list structure. After using the data, call **OH_HiDebug_FreeThreadCpuUsage** to reclaim the memory. For details, see the following example.
97
981. Define the **TestHiDebugNdk** method by referring to [How to Develop](#how-to-develop) and call the APIs in the method.
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, vssLimit: %{public}f", cpuUsage->threadId, cpuUsage->cpuUsage);
108                cpuUsage = cpuUsage->next; // Obtain the CPU usage object pointer of the next thread.
109            } while(cpuUsage != nullptr);
110            OH_HiDebug_FreeThreadCpuUsage(&cpuUsage); // Release the memory to prevent memory leaks.
111        }
112        return nullptr;
113    }
114   ```
115
1162. Trigger the API call and view the console output. The following is an example of the log output:
117   ```Text
118    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15025, vssLimit: 0.000762
119    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15143, vssLimit: 0.000000
120    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15144, vssLimit: 0.000055
121    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15152, vssLimit: 0.000000
122    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15154, vssLimit: 0.000000
123    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15155, vssLimit: 0.000000
124    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15156, vssLimit: 0.000000
125    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15157, vssLimit: 0.000000
126    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15158, vssLimit: 0.000000
127    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15159, vssLimit: 0.000000
128    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15160, vssLimit: 0.000033
129    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15161, vssLimit: 0.000077
130    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15162, vssLimit: 0.000000
131    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15163, vssLimit: 0.000033
132    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15171, vssLimit: 0.000000
133    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15175, vssLimit: 0.000011
134    06-04 15:18:27.585   15025-15025   A00000/com.exa...ation/testTag  com.examp...lication  I     GetAppThreadCpuUsage: threadId 15176, vssLimit: 0.000033
135   ```
136
137### Obtaining Stack Information
138
139The following describes how to use HiDebug to perform a main-thread stack backtrace in an application.
140
141Step 1: Create a project
142
1431. Use DevEco Studio to create a native C++ project and add the **test_backtrace.cpp** and **test_backtrace.h** files. The directory structure is as follows:
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. Edit the **test_backtrace.h** file as follows:
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. Edit the **test_backtrace.cpp** file as follows:
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 // Maximum stack backtrace depth. You need to adjust the value based on the service scenario.
186
187   namespace {
188       constexpr auto LOG_PRINT_DOMAIN = 0xFF00;
189       std::condition_variable cv_; // Used to control the logical sequence of the thread and main thread.
190       std::mutex mutex_; // Used to control the logical sequence of the thread and main thread.
191       int pcSize = -1;
192   }
193
194   class BackTraceObject { // Encapsulate the resources required for capturing stacks. You must ensure thread safety and asynchronous signal safety.
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); // This function is async-signal-safe.
204       void SymbolicAddress(int index); // This function is performance-intensive. Do not call it frequently.
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() { // Singleton used for exchanging data between the signal handler and the thread requesting stack capture. Note that this class is not async-signal-safe; application logic must guarantee single-thread access at any given time.
214       static BackTraceObject instance;
215       return instance;
216   }
217
218   bool BackTraceObject::Init(uint32_t size) { // Initialize resources.
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() { // Release resources.
231       OH_HiDebug_DestroyBacktraceObject(backtraceObject_);
232       backtraceObject_ = nullptr;
233       delete[] pcs_;
234       pcs_ = nullptr;
235   }
236
237   int BackTraceObject::BackTraceFromFp(void* startFp, int size) { // Perform stack backtracing to obtain the PC address.
238       if (size <= MAX_FRAME_SIZE) {
239           return OH_HiDebug_BacktraceFromFp(backtraceObject_, startFp, pcs_, size); // Example of calling the OH_HiDebug_BacktraceFromFp API.
240       }
241       return 0;
242   }
243
244   void BackTraceObject::PrintStackFrame(void* pc, const HiDebug_StackFrame& frame) { // Output the stack content.
245       if (frame.type == HIDEBUG_STACK_FRAME_TYPE_JS) { // Use different stack frame output modes based on the stack frame type.
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) { // Stack parsing API.
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       }); // Call the OH_HiDebug_SymbolicAddress API to parse the stack.
291   }
292
293   void SignalHandler(int sig, siginfo_t *si, void* context) { // This function is used to process signals.
294       if (sig != SIGUSR1) {
295           return;
296       }
297       auto startFp = reinterpret_cast<ucontext_t *>(context)->uc_mcontext.regs[29]; // Read the fp address stored in register X29.
298       std::unique_lock<std::mutex> lock(mutex_);
299       pcSize = BackTraceObject::GetInstance().BackTraceFromFp(reinterpret_cast<void*>(startFp), MAX_FRAME_SIZE); // This function is asyn-signal-safe. Only the asyn-signal-safe function can be used in the signal processing function.
300       cv_.notify_all(); // Notify the thread that requests stack trace data when the stack back tracing is complete.
301   }
302
303   void InitSignalHandle() { // Register the signal processing function.
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); // Ensure that the signal does not conflict with the original signal.
309   }
310
311   void BacktraceFrames() { // This API is not thread-safe. It can be used by only one thread at a time.
312       if (!BackTraceObject::GetInstance().Init(MAX_FRAME_SIZE)) { // Ensure that resources are requested before stack backtracing. Repeated initialization is not allowed.
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; // Initialize pcSize to -1.
318       siginfo_t si{0};
319       si.si_signo = SIGUSR1;
320       if (syscall(SYS_rt_tgsigqueueinfo, getpid(), getpid(), si.si_signo, &si) != 0) { // Send a signal to the main thread to trigger the signal handling function.
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;}); // Wait for the main thread to perform stack back tracing in the signal processing function.
328       }
329       for (int i = 0; i < pcSize; i++) {
330           BackTraceObject::GetInstance().SymbolicAddress(i); // The main thread parses the value of PC after obtaining it.
331       }
332       BackTraceObject::GetInstance().Release (); // Release resources in time after stack back tracing and parsing are complete.
333   }
334   ```
335
3364. In the **CMakeLists.txt** file, add the dependencies.
337
338   ```cmake
339   # Add libohhidebug.so and libhilog_ndk.z.so (log output).
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. In the **napi_init.cpp** file, import the dependency files and define the test method.
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)); // Simulate a main thread block event.
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 (); // Start the second thread to capture the stack of the main thread.
368       TestNativeFrames(1);
369       return nullptr;
370   }
371   ```
372
373   Register **TestHiDebugNdk** as an ArkTS API and initialize the signal handling function of the main thread.
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(); // Initialize the signal handling function.
381       return exports;
382   }
383   ```
384
3856. In the **index.d.ts** file, declare the ArkTS API.
386
387   ```typescript
388   export const testHiDebugNdk: () => void;
389   ```
390
3917. In the **Index.ets** file, add a button to trigger the API call. The sample code is as follows:
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
423Step 2: Run the project
424
4251. Click the **Run** button in DevEco Studio to run the project, and click "Hello world".
426
4272. At the bottom of DevEco Studio, switch to the **Log** tab and set the filter criteria to **TestTag** to view related logs.
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   ```
444