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