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 794. **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. 80 815. 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. 82 836. After the main thread jank event is reported, the system calls **onReceive()**. You can view the following event information in the **Log** window. 84 85 Tracing data of the main thread jank event is as follows: 86 87 ```text 88 HiAppEvent eventInfo.domain=OS 89 HiAppEvent eventInfo.name=MAIN_THREAD_JANK 90 HiAppEvent eventInfo.eventType=1 91 HiAppEvent eventInfo.params.time=1717593620518 92 HiAppEvent eventInfo.params.bundle_version=1.0.0 93 HiAppEvent eventInfo.params.bundle_name=com.example.main_thread_jank 94 HiAppEvent eventInfo.params.pid=40986 95 HiAppEvent eventInfo.params.uid=20020150 96 HiAppEvent eventInfo.params.begin_time=1717593620016 97 HiAppEvent eventInfo.params.end_time=1717593620518 98 HiAppEvent eventInfo.params.external_log=["/data/storage/el2/log/watchdog/MAIN_THREAD_JANK_20240613211739_40986.txt"] 99 HiAppEvent eventInfo.params.log_over_limit=false 100 ``` 101 102 The sampling stack of the main thread jank event is similar to the trace result. The differences are as follows: 103 104 Stack: 105 external_log=["/data/storage/el2/log/watchdog/MAIN_THREAD_JANK_yyyyMMDDHHmmss_xxxx.txt"]. *xxxx* indicates the process ID. 106 107 Trace: 108 external_log=[""/data/storage/el2/log/watchdog/MAIN_THREAD_JANK_unix timestamp_xxxx.trace"]. *xxxx* indicates the process ID. 109 110## Main Thread Jank Event Time Specifications 1111. Begin time 112 113 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.** 114 115 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.** 116 1172. Stack capture time 118 119 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: 120 121 (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. 122 123  124 125 (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. 126 127  128 129 (3) If no jank event is detected in the two checks, the check ends. 130 131  132 1333. Trace capture time 134 135 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. 136 137 (1) No main thread jank event is detected. 138 139  140 141 (2) At least one main thread jank event is detected. 142 143  144 145## Main Thread Jank Event Specifications 146 1471. Log aging 148 149 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**. 150 1512. You can obtain the log path from **EXTERNAL_LOG**. 152 1533. Currently, stack capturing supports only the ARM64 architecture. The stack capture result contains both native frames and JS frames parsed. 154 155 An example of the stack capture result is as follows: 156 ```text 157 9 #00 pc 0000757c /system/bin/appspawn(55679d09bcdea35bb1e0d4e1d9a3e58f) 158 9 #01 pc 000731c0 /system/lib/ld-musl-aarch64.so.1(add9e521e4eaf5cb009d4260f3b69ccd) 159 9 #02 pc 000090a9 /system/bin/appspawn(main+396)(55679d09bcdea35bb1e0d4e1d9a3e58f) 160 9 #03 pc 0000ab5d /system/bin/appspawn(AppSpawnRun+100)(55679d09bcdea35bb1e0d4e1d9a3e58f) 161 9 #04 pc 0000e7f1 /system/lib/chipset-pub-sdk/libbegetutil.z.so(RunLoop_+200)(52ace27d827ad482439bf32cc75bb17b) 162 ...... 163 9 #21 pc 00107aec /system/lib/ld-musl-aarch64.so.1(__pthread_cond_timedwait+628)(add9e521e4eaf5cb009d4260f3b69ccd) 164 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) 165 1 #01 pc 0000312e5 /system/lib/platformsdk/libmmi-client.z.so(OHOS::MMI::ClientMsgHandler::OnMsgHandler(OHOS::MMI::UDSClient const&, OHOS::MMI::NetPacket&)+340)(66ac85e964777ae89f0c26c339093cd1) 166 1 #02 pc 0003016b /system/lib/platformsdk/libmmi-client.z.so(OHOS::MMI::ClientMsgHandler::OnPointerEvent(OHOS::MMI::UDSClient const&, OHOS::MMI::NetPacket&)+1222)(66ac85e964777ae89f0c26c339093cd1) 167 1 #03 pc 0003b96b /system/lib/platformsdk/libmmi-client.z.so(OHOS::MMI::InputManagerImpl::OnPointerEvent(std::__h::shared_ptr<OHOS::MMI::PointerEvent>)+1370)(66ac85e964777ae89f0c26c339093cd1) 168 1 #04 pc 00095903 /system/lib/platformsdk/libwm.z.so(OHOS::Rosen::InputEventListener::OnInputEvent(std::__h::shared_ptr<OHOS::MMI::PointerEvent>) const+478)(9c40c5f416d6f830435126998fbcad42) 169 ...... 170 1 #21 pc 003f5c55 /system/lib/platformsdk/libark_jsruntime.so(4e6a2651ec80a7f639233f414d6486fe) 171 1 #22 at anonymous (/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/Index.js:67:17) 172 1 #23 at wait2 (/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/Index.js:16:12) 173 ...... 174 ``` 175 176 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. 177 178 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: 179 180 Native frame: 181 182 ```text 183 9 #02 pc 000090a9 /system/bin/appspawn(main+396)(55679d09bcdea35bb1e0d4e1d9a3e58f) 184 ^ ^ ^ ^ ^ ^ 185 1 2 3 4 5 6 186 187 1 indicates the number of times that the frame is sampled. 188 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). 189 3 indicates the PC value of the native frame. 190 4 indicates the path of the called file. 191 5 indicates the name of the called function and code line offset (available in unstripped version, and may not available in stripped version). 192 6 indicates the MD5 value of the .so file. 193 ``` 194 195 JS frame: 196 197 ```text 198 1 #23 at wait2 (/entry/build/default/cache/default/XXX/entry/src/main/ets/pages/Index.js:16:12) 199 ^ ^ ^ ^ 200 1 2 3 4 201 202 1 indicates the number of times that the frame is sampled. The maximum value is the sampling times. 203 2 indicates the invoking level of the frame, which is the same as that of the native frame. 204 3 indicates the name of the called function, which is **wait2**. 205 4 indicates the path, file, row number, and column number of the called function. 206 ``` 207 2084. Trace specifications 209 210 The size of a trace file is 1 MB to 5 MB. You can parse the trace file using [SmartPerf](https://www.smartperf.host). 211 212 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. 213 214 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). 215