1# Subscribing to Main Thread Jank Events (ArkTS) 2 3## Available APIs 4 5For details about how to use the APIs, see [Application Event Logging](../reference/apis-performance-analysis-kit/js-apis-hiviewdfx-hiappevent.md). 6 7| API | Description | 8| --------------------------------------------------- | -------------------------------------------- | 9| addWatcher(watcher: Watcher): AppEventPackageHolder | Adds a watcher to listen for application events.| 10| removeWatcher(watcher: Watcher): void | Removes a watcher to unsubscribe from application events.| 11 12## How to Develop 13 14The following describes how to subscribe to the main thread jank event, which is reported when a task running in the main thread times out. 15 161. Create an ArkTS application project. In the **entry/src/main/ets/entryability/EntryAbility.ets** file, import the dependent modules. 17 18 ```ts 19 import { hiAppEvent, hilog } from '@kit.PerformanceAnalysisKit'; 20 ``` 21 222. In the **entry/src/main/ets/entryability/EntryAbility.ets** file, add a watcher in **onCreate()** to subscribe to system events. The sample code is as follows: 23 24 ```ts 25 hiAppEvent.addWatcher({ 26 // Set the watcher name. The system identifies different watchers based on their names. 27 name: "watcher", 28 // Add the system events to watch, for example, the main thread jank event. 29 appEventFilters: [ 30 { 31 domain: hiAppEvent.domain.OS, 32 names: [hiAppEvent.event.MAIN_THREAD_JANK] 33 } 34 ], 35 // Implement a callback for the registered system event so that you can apply custom processing to the event data obtained. 36 onReceive: (domain: string, appEventGroups: Array<hiAppEvent.AppEventGroup>) => { 37 hilog.info(0x0000, 'testTag', `HiAppEvent onReceive: domain=${domain}`); 38 for (const eventGroup of appEventGroups) { 39 // The event name uniquely identifies a system event. 40 hilog.info(0x0000, 'testTag', `HiAppEvent eventName=${eventGroup.name}`); 41 for (const eventInfo of eventGroup.appEventInfos) { 42 // Apply custom processing to the event data obtained, for example, print the event data in the log. 43 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.domain=${eventInfo.domain}`); 44 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.name=${eventInfo.name}`); 45 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.eventType=${eventInfo.eventType}`); 46 // Obtain the timestamp when the main thread jank event occurs. 47 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.time=${eventInfo.params['time']}`); 48 // Obtain the version information of the app. 49 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.bundle_version=${eventInfo.params['bundle_version']}`); 50 // Obtain the bundle name of the app. 51 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.bundle_name=${eventInfo.params['bundle_name']}`); 52 // Obtain the PID and UID of the app. 53 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.pid=${eventInfo.params['pid']}`); 54 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.uid=${eventInfo.params['uid']}`); 55 // Obtain the begin time and end time on the main thread. 56 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.begin_time=${eventInfo.params['begin_time']}`); 57 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.end_time=${eventInfo.params['end_time']}`); 58 // Obtain the error log file generated when the main thread jank event occurs. 59 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.external_log=${JSON.stringify(eventInfo.params['external_log'])}`); 60 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.log_over_limit=${eventInfo.params['log_over_limit']}`); 61 } 62 } 63 } 64 }); 65 ``` 66 67 683. In the **entry/src/main/ets/pages/Index.ets** file, add the **timeOut500** button with **onClick()** to trigger a main thread jank event when the button is clicked. The sample code is as follows: 69 ```ts 70 Button("timeOut350") 71 .fontSize(50) 72 .fontWeight(FontWeight.Bold) 73 .onClick(() => { 74 let t = Date.now(); 75 while (Date.now() - t <= 350){ 76 77 } 78 }) 79 ``` 80 814. **If the nolog version is used and the developer mode is disabled**, the main thread checker will collect tracing data when a task times out. 82 835. In DevEco Studio, click the **Run** button to run the application project. Click the **timeOut350** button twice consecutively to trigger a main thread jank event. 84 856. After the main thread jank event is reported, the system calls **onReceive()**. You can view the following event information in the **Log** window. 86 87 Tracing data of the main thread jank event is as follows: 88 89 ```text 90 HiAppEvent eventInfo.domain=OS 91 HiAppEvent eventInfo.name=MAIN_THREAD_JANK 92 HiAppEvent eventInfo.eventType=1 93 HiAppEvent eventInfo.params.time=1717593620518 94 HiAppEvent eventInfo.params.bundle_version=1.0.0 95 HiAppEvent eventInfo.params.bundle_name=com.example.main_thread_jank 96 HiAppEvent eventInfo.params.pid=40986 97 HiAppEvent eventInfo.params.uid=20020150 98 HiAppEvent eventInfo.params.begin_time=1717593620016 99 HiAppEvent eventInfo.params.end_time=1717593620518 100 HiAppEvent eventInfo.params.external_log=["/data/storage/el2/log/watchdog/MAIN_THREAD_JANK_20240613211739_40986.txt"] 101 HiAppEvent eventInfo.params.log_over_limit=false 102 ``` 103 104 The sampling stack of the main thread jank event is similar to the trace result. The differences are as follows: 105 106 Stack: 107 external_log=["/data/storage/el2/log/watchdog/MAIN_THREAD_JANK_yyyyMMDDHHmmss_xxxx.txt"]. *xxxx* indicates the process ID. 108 109 Trace: 110 external_log=[""/data/storage/el2/log/watchdog/MAIN_THREAD_JANK_unix timestamp_xxxx.trace"]. *xxxx* indicates the process ID. 111 112## Main Thread Jank Event Time Specifications 1131. Begin time 114 115 If the main thread processing time is greater than 150 ms but less than 450 ms, trigger a stack sampling. **Only one sampling stack can be triggered based on the PID of an app in its lifecycle.** 116 117 If the main thread processing time exceeds 450 ms, trigger a trace sampling. **Only one sampling trace can be triggered based on the UID of an app in a day.** 118 1192. Stack capture time 120 121 When the main thread jank event occurs, the main thread checker starts to check whether the jank event occurs again every 155 ms (1 ≤ number of check times ≤ 2). There are three cases: 122 123 (1) If a jank event is detected during the first check, the main thread checker starts stack sampling every 155 ms for 10 times. The stack sampling data is collected and an event is reported at the next interval. Then the check ends. 124 125  126 127 (2) If a jank event is detected during the second check, the main thread checker starts stack sampling every 155 ms for 10 times. The stack sampling data is collected and an event is reported at the next interval. Then the check ends. 128 129  130 131 (3) If no jank event is detected in the two checks, the check ends. 132 133  134 1353. Trace capture time 136 137 After the function is called to capture tracing data, the main thread checker checks for a main thread jank event every 150 ms for 20 times. If a main thread jank event occurs in any of the 20 checks, the check ends in 3s and the tracing data is stored. 138 139 (1) No main thread jank event is detected. 140 141  142 143 (2) At least one main thread jank event is detected. 144 145  146 147## Main Thread Jank Event Specifications 148 1491. Event specifications 150 151 You can run the **hdc shell** command (**hisysevent -l | grep MAIN_THREAD_JANK**) to view the main thread jank events. For details, see [HiSysEvent](./hisysevent.md). 152 153 The information of the reported event is described as follows. 154 155 | Type | Description | 156 | -------------- | ------------------------------------- | 157 | BUNDLE_VERSION | App version number. | 158 | BUNDLE_NAME | App process name. | 159 | BEGIN_TIME | Begin timestamp of the main thread jank event. | 160 | END_TIME | End timestamp of the main thread jank event. | 161 | EXTERNAL_LOG | Stack file storage path. | 162 | STACK | Stack content. | 163 | JANK_LEVEL | Flag (**0**: stack sampling; **1**: trace sampling). | 164 | THREAD_NAME | Thread name. | 165 | FOREGROUND | Whether the app is in the foreground. | 166 | LOG_TIME | Log timestamp. | 167 1682. Log aging 169 170 Generally, the size of a stack file is 7 KB to 10 KB, and the size of a trace file is 3 MB to 6 MB. The **watchdog** directory in the app sandbox can store a maximum of 10 MB files. If the total file size exceeds 10 MB, the user needs to manually delete files. The path to **watchdog** is **/data/app/el2/100/log/*app_bundle_name*/watchdog**. 171 1723. You can obtain the log path from **EXTERNAL_LOG**. 173 1744. Currently, stack capturing supports only the ARM64 architecture. The stack capture result contains both native frames and JS frames parsed. 175 176 An example of the stack capture result is as follows: 177 ```text 178 9 #00 pc 0000757c /system/bin/appspawn(55679d09bcdea35bb1e0d4e1d9a3e58f) 179 9 #01 pc 000731c0 /system/lib/ld-musl-aarch64.so.1(add9e521e4eaf5cb009d4260f3b69ccd) 180 9 #02 pc 000090a9 /system/bin/appspawn(main+396)(55679d09bcdea35bb1e0d4e1d9a3e58f) 181 9 #03 pc 0000ab5d /system/bin/appspawn(AppSpawnRun+100)(55679d09bcdea35bb1e0d4e1d9a3e58f) 182 9 #04 pc 0000e7f1 /system/lib/chipset-pub-sdk/libbegetutil.z.so(RunLoop_+200)(52ace27d827ad482439bf32cc75bb17b) 183 ...... 184 9 #21 pc 00107aec /system/lib/ld-musl-aarch64.so.1(__pthread_cond_timedwait+628)(add9e521e4eaf5cb009d4260f3b69ccd) 185 1 #00 pc 00032e67 /system/lib/platformsdk/libmmi-util.z.so(OHOS::MMI::UDSSocket::OnReadPackets(OHOS::MMI::CircleStreamBuffer&, std::__h::function<void (OHOS::MMI::NetPacket&)>)+158)(99e56bc765f9208f7b7ba8b268886a59) 186 1 #01 pc 0000312e5 /system/lib/platformsdk/libmmi-client.z.so(OHOS::MMI::ClientMsgHandler::OnMsgHandler(OHOS::MMI::UDSClient const&, OHOS::MMI::NetPacket&)+340)(66ac85e964777ae89f0c26c339093cd1) 187 1 #02 pc 0003016b /system/lib/platformsdk/libmmi-client.z.so(OHOS::MMI::ClientMsgHandler::OnPointerEvent(OHOS::MMI::UDSClient const&, OHOS::MMI::NetPacket&)+1222)(66ac85e964777ae89f0c26c339093cd1) 188 1 #03 pc 0003b96b /system/lib/platformsdk/libmmi-client.z.so(OHOS::MMI::InputManagerImpl::OnPointerEvent(std::__h::shared_ptr<OHOS::MMI::PointerEvent>)+1370)(66ac85e964777ae89f0c26c339093cd1) 189 1 #04 pc 00095903 /system/lib/platformsdk/libwm.z.so(OHOS::Rosen::InputEventListener::OnInputEvent(std::__h::shared_ptr<OHOS::MMI::PointerEvent>) const+478)(9c40c5f416d6f830435126998fbcad42) 190 ...... 191 1 #21 pc 003f5c55 /system/lib/platformsdk/libark_jsruntime.so(4e6a2651ec80a7f639233f414d6486fe) 192 1 #22 at anonymous (/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/Index.js:67:17) 193 1 #23 at wait2 (/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/Index.js:16:12) 194 ...... 195 ``` 196 197 Each stack capture records 16 KB call stack information of the main thread for stack unwinding. Therefore, each stack capture result contains a maximum of 16 KB invocation information of the process for 10 times. The captured data is displayed in a tree view, with repeated stack frames aggregated and different call layers distinguished by line indentation. If the stack fails to be captured (for example, the main thread is blocked in the kernel or signals are masked), the content of the **/proc/self/wchan** file is output. 198 199 In the result, each row indicates a piece of stack information. The meaning of a row of stack frame information can be interpreted as follows: 200 201 Native frame: 202 203 ```text 204 9 #02 pc 000090a9 /system/bin/appspawn(main+396)(55679d09bcdea35bb1e0d4e1d9a3e58f) 205 ^ ^ ^ ^ ^ ^ 206 1 2 3 4 5 6 207 208 1 indicates the number of times that the frame is sampled. 209 2 indicates the invoking level of the frame. The line indentation size corresponds to this level. The number of sampling times of all frames at the same level cannot be greater than 10. The number of sampling times of #00 is 10 (set the sampling times). 210 3 indicates the PC value of the native frame. 211 4 indicates the path of the called file. 212 5 indicates the name of the called function and code line offset (available in unstripped version, and may not available in stripped version). 213 6 indicates the MD5 value of the .so file. 214 ``` 215 216 JS frame: 217 218 ```text 219 1 #23 at wait2 (/entry/build/default/cache/default/XXX/entry/src/main/ets/pages/Index.js:16:12) 220 ^ ^ ^ ^ 221 1 2 3 4 222 223 1 indicates the number of times that the frame is sampled. The maximum value is the sampling times. 224 2 indicates the invoking level of the frame, which is the same as that of the native frame. 225 3 indicates the name of the called function, which is **wait2**. 226 4 indicates the path, file, row number, and column number of the called function. 227 ``` 228 2295. Trace specifications 230 231 The size of a trace file is 1 MB to 5 MB. You can parse the trace file using [SmartPerf](https://www.smartperf.host). 232 233 After the trace file is imported, the page displays the time axis, CPU usage, CPU load, IPC method calling, and process, thread, and method calling information from the top down. In this way, the data is displayed from the event dimension. 234 235 For details about how to use trace files, see [How to Load Trace Files on the Web Client](https://gitee.com/openharmony/developtools_smartperf_host/blob/master/ide/src/doc/md/quickstart_systemtrace.md). 236