1# 使用HiTraceMeter跟踪性能(C/C++) 2 3<!--Kit: Performance Analysis Kit--> 4<!--Subsystem: HiviewDFX--> 5<!--Owner: @qq_437963121--> 6<!--Designer: @kutcherzhou1; @MontSaintMichel--> 7<!--Tester: @gcw_KuLfPSbe--> 8<!--Adviser: @foryourself--> 9 10## 简介 11 12HiTraceMeter提供系统性能打点接口。开发者在关键代码位置调用这些API,能够有效跟踪进程轨迹,查看系统和应用性能。 13 14 15## 接口说明 16 17性能打点跟踪接口由HiTraceMeter模块提供,详细API请参考[Hitrace C API参考](../reference/apis-performance-analysis-kit/capi-trace-h.md)。 18 19| 方法 | 接口描述 | 20| -------- | -------- | 21| void OH_HiTrace_StartTraceEx(HiTrace_Output_Level level, const char\* name, const char\* customArgs) | 开启一个同步时间片跟踪事件,分级控制跟踪输出。<br/>**说明**:从API version 19开始,支持该接口。 | 22| void OH_HiTrace_FinishTraceEx(HiTrace_Output_Level level) | 结束一个同步时间片跟踪事件,分级控制跟踪输出。<br/>level必须与流程开始的OH_HiTrace_StartTraceEx()对应参数值保持一致。<br/>**说明**:从API version 19开始,支持该接口。 | 23| void OH_HiTrace_StartAsyncTraceEx(HiTrace_Output_Level level, const char\* name, int32_t taskId, const char\* customCategory, const char\* customArgs) | 开启一个异步时间片跟踪事件,分级控制跟踪输出。<br/>taskId是trace中用来表示关联的ID,如果有多个name相同的任务并行执行,则开发者每次调用OH_HiTrace_StartAsyncTraceEx()时,传入的taskId需不同;如果具有相同name的任务是串行执行的,则taskId可以相同。<br/>**说明**:从API version 19开始,支持该接口。 | 24| void OH_HiTrace_FinishAsyncTraceEx(HiTrace_Output_Level level, const char\* name, int32_t taskId) | 结束一个异步时间片跟踪事件,分级控制跟踪输出。<br/>level、name和taskId必须与流程开始的OH_HiTrace_StartAsyncTraceEx()对应参数值保持一致。<br/>**说明**:从API version 19开始,支持该接口。 | 25| void OH_HiTrace_CountTraceEx(HiTrace_Output_Level level, const char\* name, int64_t count) | 整数跟踪事件,分级控制跟踪输出。<br/>name、count两个参数分别用来标记一个跟踪的整数变量名及整数值。<br/>**说明**:从API version 19开始,支持该接口。 | 26| bool OH_HiTrace_IsTraceEnabled(void) | 判断当前是否开启应用trace捕获。<br/>使用hitrace命令行工具等方式开启采集时返回true,未开启采集或停止采集后返回false,此时调用HiTraceMeter性能跟踪打点接口无效。<br/>**说明**:从API version 19开始,支持该接口。 | 27 28> **注意:** 29> 30> [用户态trace格式](hitracemeter-view.md#用户态trace格式说明)使用竖线 | 作为分隔符,所以通过HiTraceMeter接口传递的字符串类型参数应避免包含该字符,以防止trace解析异常。 31 32 33### 接口分类 34 35HiTraceMeter打点接口主要分为三类:同步时间片跟踪接口、异步时间片跟踪接口和整数跟踪接口。HiTraceMeter接口实现均为同步,同步和异步针对的是被跟踪的业务。同步业务使用同步时间片跟踪接口,异步业务使用异步时间片跟踪接口。HiTraceMeter打点接口可与[HiTraceChain](hitracechain-guidelines-ndk.md)一起使用,进行跨设备、跨进程或跨线程的打点关联与分析。 36 37 38### 接口使用场景 39 40 41- 同步时间片跟踪接口 42 用于顺序执行的打点场景,需按序成对使用OH_HiTrace_StartTraceEx()接口和OH_HiTrace_FinishTraceEx()接口,否则会导致trace文件在smartperf等可视化工具上显示异常。 43 44- 异步时间片跟踪接口 45 在异步操作执行前调用OH_HiTrace_StartAsyncTraceEx()接口进行开始打点,在异步操作完成后调用OH_HiTrace_FinishAsyncTraceEx()接口进行结束打点。 46 解析trace时,通过name和taskId参数识别不同的异步跟踪。所以这两个接口必须按序成对使用,并传入相同的name和taskId。 47 不同的异步流程中应使用不同的name和taskId,但在异步跟踪流程不会同时发生的情况下,可以使用相同的name和taskId。 48 调用错误会导致trace文件在smartperf等可视化工具上显示异常。 49 50- 整数跟踪接口 51 用于跟踪整数变量。整数值变动时调用OH_HiTrace_CountTraceEx()接口,可在smartperf的泳道图中观察变动情况。由于从开始采集到首次打点存在时间差,这段时间的数值无法查看。 52 53 54### 参数解析 55 56 57| 参数名 | 类型 | 说明 | 58| -------- | -------- | -------- | 59| level | enum | 跟踪输出级别,低于系统阈值的跟踪将不会被输出。<br/>log版本阈值为HITRACE_LEVEL_INFO,nolog版本阈值为HITRACE_LEVEL_COMMERCIAL。 | 60| name | const char\* | 要跟踪的任务名称或整数变量名称。 | 61| taskId | int32_t | 用来表示关联的ID,如果有多个name相同的任务并行执行,则开发者每次调用OH_HiTrace_StartAsyncTraceEx()时,传入的taskId需不同。 | 62| count | int64_t | 整数变量的值。 | 63| customCategory | const char\* | 自定义聚类名称,用于聚合同一类异步跟踪打点。<br/>若不需要聚类,可传入一个空字符串。 | 64| customArgs | const char\* | 自定义键值对,若有多组键值对,使用逗号进行分隔,例"key1=value1,key2=value2"。<br/>若不需要该参数,可传入一个空字符串。 | 65 66 67> **说明:** 68> 69> [用户态trace](hitracemeter-view.md#用户态trace格式说明)总长度限制为512字符,超过部分将会被截断。建议name、customCategory和customArgs三个字段的总长度不超过420字符,以避免trace被截断。 70 71 72## 开发步骤 73 74以下为一个使用HiTraceMeter打点接口的Native C++应用示例。 75 76 77### 步骤一:创建项目 78 79 801. 在DevEco Studio中新建工程,选择“Native C++”,工程的目录结构如下。 81 82 ```text 83 ├── entry 84 │ ├── src 85 │ ├── main 86 │ │ ├── cpp 87 │ │ │ ├── CMakeLists.txt 88 │ │ │ ├── napi_init.cpp 89 │ │ │ └── types 90 │ │ │ └── libentry 91 │ │ │ ├── Index.d.ts 92 │ │ │ └── oh-package.json5 93 │ │ ├── ets 94 │ │ │ ├── entryability 95 │ │ │ │ └── EntryAbility.ets 96 │ │ │ ├── entrybackupability 97 │ │ │ │ └── EntryBackupAbility.ets 98 │ │ │ └── pages 99 │ │ │ └── Index.ets 100 ``` 101 1022. 在“entry > src > main > cpp > CMakeLists.txt”文件中新增libhitrace_ndk.z.so和libhilog_ndk.z.so动态链接库,完整的文件内容如下。 103 104 ```cmake 105 # the minimum version of CMake. 106 cmake_minimum_required(VERSION 3.5.0) 107 project(HiTraceChainTest03) 108 109 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 110 111 if(DEFINED PACKAGE_FIND_FILE) 112 include(${PACKAGE_FIND_FILE}) 113 endif() 114 115 include_directories(${NATIVERENDER_ROOT_PATH} 116 ${NATIVERENDER_ROOT_PATH}/include) 117 118 add_library(entry SHARED napi_init.cpp) 119 target_link_libraries(entry PUBLIC libace_napi.z.so libhitrace_ndk.z.so libhilog_ndk.z.so) 120 ``` 121 1223. 编辑“entry > src > main > cpp > napi_init.cpp”文件,在Add函数中调用HiTraceMeter NDK_C接口进行性能打点跟踪,完整的示例代码如下。 123 124 ```c++ 125 #include <cstdio> 126 #include <cstring> 127 128 #include "hilog/log.h" 129 #include "hitrace/trace.h" 130 #include "napi/native_api.h" 131 132 #undef LOG_TAG 133 #define LOG_TAG "traceTest" 134 135 static napi_value Add(napi_env env, napi_callback_info info) 136 { 137 // 第一个异步跟踪任务开始 138 OH_HiTrace_StartAsyncTraceEx(HITRACE_LEVEL_COMMERCIAL, "myTestAsyncTrace", 1001, "categoryTest", "key=value"); 139 // 开始计数任务 140 int64_t traceCount = 0; 141 traceCount++; 142 OH_HiTrace_CountTraceEx(HITRACE_LEVEL_COMMERCIAL, "myTestCountTrace", traceCount); 143 // 业务流程 144 OH_LOG_INFO(LogType::LOG_APP, "myTraceTest running, taskId: 1001"); 145 146 // 第二个异步跟踪任务开始,同时第一个跟踪的同名任务还没结束,出现了并行执行,对应接口的taskId需要不同 147 OH_HiTrace_StartAsyncTraceEx(HITRACE_LEVEL_COMMERCIAL, "myTestAsyncTrace", 1002, "categoryTest", "key=value"); 148 // 开始计数任务 149 traceCount++; 150 OH_HiTrace_CountTraceEx(HITRACE_LEVEL_COMMERCIAL, "myTestCountTrace", traceCount); 151 // 业务流程 152 OH_LOG_INFO(LogType::LOG_APP, "myTraceTest running, taskId: 1002"); 153 154 // 结束taskId为1001的异步跟踪任务 155 OH_HiTrace_FinishAsyncTraceEx(HITRACE_LEVEL_COMMERCIAL, "myTestAsyncTrace", 1001); 156 // 结束taskId为1002的异步跟踪任务 157 OH_HiTrace_FinishAsyncTraceEx(HITRACE_LEVEL_COMMERCIAL, "myTestAsyncTrace", 1002); 158 159 // 开始同步跟踪任务 160 OH_HiTrace_StartTraceEx(HITRACE_LEVEL_COMMERCIAL, "myTestSyncTrace", "key=value"); 161 // 业务流程 162 OH_LOG_INFO(LogType::LOG_APP, "myTraceTest running, synchronizing trace"); 163 // 结束同步跟踪任务 164 OH_HiTrace_FinishTraceEx(HITRACE_LEVEL_COMMERCIAL); 165 166 // 若通过HiTraceMeter性能打点接口传递的参数的生成过程比较复杂,此时可以通过isTraceEnabled判断当前是否开启应用trace捕获, 167 // 在未开启应用trace捕获时,避免该部分性能损耗 168 if (OH_HiTrace_IsTraceEnabled()) { 169 char customArgs[128] = "key0=value0"; 170 for (int index = 1; index < 10; index++) { 171 char buffer[16]; 172 snprintf(buffer, sizeof(buffer), ",key%d=value%d", index, index); 173 strncat(customArgs, buffer, sizeof(customArgs) - strlen(customArgs) - 1); 174 } 175 OH_HiTrace_StartAsyncTraceEx(HITRACE_LEVEL_COMMERCIAL, "myTestAsyncTrace", 1003, "categoryTest", customArgs); 176 OH_LOG_INFO(LogType::LOG_APP, "myTraceTest running, taskId: 1003"); 177 OH_HiTrace_FinishAsyncTraceEx(HITRACE_LEVEL_COMMERCIAL, "myTestAsyncTrace", 1003); 178 } else { 179 OH_LOG_INFO(LogType::LOG_APP, "myTraceTest running, trace is not enabled"); 180 } 181 182 size_t requireArgc = 2; 183 size_t argc = 2; 184 napi_value args[2] = {nullptr}; 185 186 napi_get_cb_info(env, info, &argc, args , nullptr, nullptr); 187 188 napi_valuetype valuetype0; 189 napi_typeof(env, args[0], &valuetype0); 190 191 napi_valuetype valuetype1; 192 napi_typeof(env, args[1], &valuetype1); 193 194 double value0; 195 napi_get_value_double(env, args[0], &value0); 196 197 double value1; 198 napi_get_value_double(env, args[1], &value1); 199 200 napi_value sum; 201 napi_create_double(env, value0 + value1, &sum); 202 203 return sum; 204 } 205 206 EXTERN_C_START 207 static napi_value Init(napi_env env, napi_value exports) 208 { 209 napi_property_descriptor desc[] = { 210 { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr } 211 }; 212 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 213 return exports; 214 } 215 EXTERN_C_END 216 217 static napi_module demoModule = { 218 .nm_version = 1, 219 .nm_flags = 0, 220 .nm_filename = nullptr, 221 .nm_register_func = Init, 222 .nm_modname = "entry", 223 .nm_priv = ((void*)0), 224 .reserved = { 0 }, 225 }; 226 227 extern "C" __attribute__((constructor)) void RegisterEntryModule(void) 228 { 229 napi_module_register(&demoModule); 230 } 231 ``` 232 233 234### 步骤二:采集trace信息并查看 235 2361. 在DevEco Studio Terminal窗口中执行如下命令,开启应用trace捕获。 237 238 ```shell 239 PS D:\xxx\xxx> hdc shell 240 $ hitrace --trace_begin app 241 ``` 242 2432. 单击DevEco Studio界面上的运行按钮,启动应用。点击应用界面的“Hello World”文本,执行包含HiTraceMeter打点的业务逻辑。然后执行如下命令抓取trace数据,并使用“myTest”关键字过滤trace数据(示例打点接口传递的name字段前缀均为“myTest”)。 244 245 ```shell 246 $ hitrace --trace_dump | grep myTest 247 ``` 248 249 成功抓取的trace数据如下所示。 250 251 ```text 252 <...>-49837 (-------) [002] .... 349137.708093: tracing_mark_write: S|49837|H:myTestAsyncTrace|1001|M62|categoryTest|key=value 253 <...>-49837 (-------) [002] .... 349137.708103: tracing_mark_write: C|49837|H:myTestCountTrace|1|M62 254 <...>-49837 (-------) [002] .... 349137.708201: tracing_mark_write: S|49837|H:myTestAsyncTrace|1002|M62|categoryTest|key=value 255 <...>-49837 (-------) [002] .... 349137.708209: tracing_mark_write: C|49837|H:myTestCountTrace|2|M62 256 <...>-49837 (-------) [002] .... 349137.708239: tracing_mark_write: F|49837|H:myTestAsyncTrace|1001|M62 257 <...>-49837 (-------) [002] .... 349137.708246: tracing_mark_write: F|49837|H:myTestAsyncTrace|1002|M62 258 <...>-49837 (-------) [002] .... 349137.708252: tracing_mark_write: B|49837|H:myTestSyncTrace|M62|key=value 259 <...>-49837 (-------) [002] .... 349137.708301: tracing_mark_write: S|49837|H:myTestAsyncTrace|1003|M62|categoryTest|key0=value0,key1=value1,key2=value2,key3=value3,key4=value4,key5=value5,key6=value6,key7=value7,key8=value8,key9=value9 260 <...>-49837 (-------) [002] .... 349137.708323: tracing_mark_write: F|49837|H:myTestAsyncTrace|1003|M62 261 ``` 262 263 264### 步骤三:停止采集trace 265 2661. 执行以下命令,结束应用trace捕获。 267 268 ```shell 269 $ hitrace --trace_finish 270 ``` 271 2722. 再次点击应用界面的“Hello World”文本,此时应用trace捕获已关闭,OH_HiTrace_IsTraceEnabled()接口返回false。在DevEco Studio Log窗口使用关键字“not enabled”进行过滤,会打印如下日志。 273 274 ```text 275 myTraceTest running, trace is not enabled 276 ``` 277 278 > **说明:** 279 > 280 > log版本在使用hitrace --trace_finish命令停止采集后会自动拉起快照模式,打开应用trace捕获,此时OH_HiTrace_IsTraceEnabled()接口返回true,不会打印上述日志。 281