From 1ce1ccba6eeaee9ace0c4b61b798860f514a05f4 Mon Sep 17 00:00:00 2001 From: mlkgeek Date: Mon, 27 Oct 2025 08:41:03 +0000 Subject: [PATCH 1/2] cherry-pick hiappevent sample from https://gitcode.com/openharmony/applications_app_samples/pull/6920 https://gitcode.com/openharmony/applications_app_samples/pull/6863 Signed-off-by: mlkgeek --- .../HiAppEvent/EventSub/README_zh.md | 62 +- .../entry/src/main/cpp/CMakeLists.txt | 4 +- .../EventSub/entry/src/main/cpp/napi_init.cpp | 669 +++++++++++++++--- .../src/main/cpp/types/libentry/Index.d.ts | 21 +- .../main/ets/entryability/EntryAbility.ets | 107 ++- .../entry/src/main/ets/pages/Index.ets | 68 +- 6 files changed, 811 insertions(+), 120 deletions(-) diff --git a/PerformanceAnalysisKit/HiAppEvent/EventSub/README_zh.md b/PerformanceAnalysisKit/HiAppEvent/EventSub/README_zh.md index 9b83c21fa49..bd7013fb030 100644 --- a/PerformanceAnalysisKit/HiAppEvent/EventSub/README_zh.md +++ b/PerformanceAnalysisKit/HiAppEvent/EventSub/README_zh.md @@ -4,7 +4,7 @@ 本示例主要展示用HiAppEvent如何实现事件订阅(AppEvent),并列举了订阅如下系统事件的例子: -崩溃事件(CrashEvent)、系统卡死事件(FreezeEvent)、系统资源泄漏事件(PssLeakEvent)、系统踩内存事件(ASANEvent)以及主线程超时事件 +崩溃事件(CrashEvent)、系统卡死事件(FreezeEvent)、系统资源泄漏事件(PssLeakEvent)、系统地址越界事件(ASANEvent)以及主线程超时事件 (TimeOutEvent)。 @@ -22,23 +22,29 @@ - [订阅卡死事件(C/C++)](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/dfx/hiappevent-watcher-freeze-events-ndk.md) -- [订阅资源泄漏事件(ArkTS)](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/dfx/hiappevent-watcher-resourceleak-events-arkts.md) +- [订阅资源泄漏事件(ArkTS)](https://gitcode.com/openharmony/docs/blob/master/zh-cn/application-dev/dfx/hiappevent-watcher-resourceleak-events-arkts.md) -- [订阅资源泄漏事件(C/C++)](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/dfx/hiappevent-watcher-resourceleak-events-ndk.md) +- [订阅资源泄漏事件(C/C++)](https://gitcode.com/openharmony/docs/blob/master/zh-cn/application-dev/dfx/hiappevent-watcher-resourceleak-events-ndk.md) -- [订阅踩内存事件(ArkTS)](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/dfx/hiappevent-watcher-address-sanitizer-events-arkts.md) +- [订阅地址越界事件(ArkTS)](https://gitcode.com/openharmony/docs/blob/master/zh-cn/application-dev/dfx/hiappevent-watcher-address-sanitizer-events-arkts.md) -- [订阅踩内存事件(C/C++)](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/dfx/hiappevent-watcher-address-sanitizer-events-ndk.md) +- [订阅地址越界事件(C/C++)](https://gitcode.com/openharmony/docs/blob/master/zh-cn/application-dev/dfx/hiappevent-watcher-address-sanitizer-events-ndk.md) - [订阅主线程超时事件(ArkTS)](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/dfx/hiappevent-watcher-mainthreadjank-events-arkts.md) - [订阅主线程超时事件(C/C++)](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/dfx/hiappevent-watcher-mainthreadjank-events-ndk.md) +- [订阅任务执行超时事件(C/C++)](https://gitcode.com/openharmony/docs/blob/master/zh-cn/application-dev/dfx/hiappevent-watcher-apphicollie-events-ndk.md) + +- [订阅应用终止事件(ArkTS)](https://gitcode.com/openharmony/docs/blob/master/zh-cn/application-dev/dfx/hiappevent-watcher-app-killed-events-arkts.md) + +- [订阅应用终止事件(C/C++)](https://gitcode.com/openharmony/docs/blob/master/zh-cn/application-dev/dfx/hiappevent-watcher-app-killed-events-ndk.md) + ### 效果预览 | 主页 | | :----------------------------------------------------------: | -| z | +| z | #### 使用说明 @@ -290,7 +296,7 @@ HiAppEvent eventInfo.params.test_data=100 1.先在设备“开发者选项”中打开“系统资源泄漏日志”,并重启设备; -2.在应用侧主界面,点击"pssLeak ArkTS&C++"按钮触发资源泄露事件,等待15~30分钟,会上报应用内存泄漏事件; +2.在应用侧主界面,点击"pss leak"按钮触发资源泄露事件,等待15~30分钟,会上报应用内存泄漏事件; 3.在DevEco Studio侧下方导航栏,切换到"Log"窗口,日志过滤选择"All log of selected app",搜索内容设置为"HiAppevent"。此时窗口仅显示符合条件的日志,打印日志结果为: @@ -337,11 +343,11 @@ HiAppEvent eventInfo={"domain":"OS","name":"RESOURCE_OVERLIMIT","eventType":1,"p 3.C++实现了onReceive和onTrigger两种观察者,ArkTS实现了onReceive观察者。 -##### 7.订阅踩内存事件(ArkTS&C++) +##### 7.订阅地址越界事件(ArkTS&C++) 1.点击DevEco Studio界面中的“entry”,点击“Edit Configurations”,点击“Diagnostics”,勾选“Address Sanitizer”,保存设置。 -2.在应用侧主界面,点击"appAsanEvent ArkTS&C++"按钮触发踩内存事件,应用退出后重启应用; +2.在应用侧主界面,点击"address-sanitizer"按钮触发地址越界事件,应用退出后重启应用; 3.在DevEco Studio侧下方导航栏,切换到"Log"窗口,日志过滤选择"All log of selected app",搜索内容设置为"HiAppevent"。此时窗口仅显示符合条件的日志,打印日志结果为: @@ -444,6 +450,40 @@ HiAppEvent eventInfo.params.log_over_limit=0 3.C++实现了onReceive观察者,ArkTS实现了onReceive观察者。 +##### 9.订阅任务执行超时事件(C/C++) + +本示例主要展示了订阅任务执行超时事件的功能,包括构造任务执行超时事件及其订阅处理。OH_HiAppEvent_AddWatcher接口用于添加对该超时事件的订阅。 + +1.在应用侧主界面,点击“TestHiCollieTimerNdk”按钮; +2.在DevEco Studio侧下方导航栏,切换到"Log"窗口,日志过滤选择"No filters",搜索内容设置为"testTag"。此时窗口仅显示符合条件的日志,打印日志示例结果为: + +```text +HiAppEvent eventInfo.domain=OS +HiAppEvent eventInfo.name=APP_HICOLLIE +HiAppEvent eventInfo.eventType=1 +HiAppEvent eventInfo.params.time=xx +HiAppEvent eventInfo.params.foreground=1 +HiAppEvent eventInfo.params.bundle_version=1.0.0 +HiAppEvent eventInfo.params.process_name=xx +HiAppEvent eventInfo.params.pid=xx +HiAppEvent eventInfo.params.uid=xx +HiAppEvent eventInfo.params.uuid=xx +HiAppEvent eventInfo.params.exception={"message":"","name":"APP_HICOLLIE"} +HiAppEvent eventInfo.params.hilog.size=xx +HiAppEvent eventInfo.params.peer_binder.size=xx +HiAppEvent eventInfo.params.memory={"pss":0,"rss":xx,"sys_avail_mem":xx,"sys_free_mem":xx,"sys_total_mem":xx,"vss":xx} +HiAppEvent eventInfo.params.external_log=["/data/storage/el2/log/hiappevent/APP_HICOLLIE_xx_xx.log"] +HiAppEvent eventInfo.params.log_over_limit=xx +``` + +注意: + +1.本示例适配API19及以上版本SDK。 + +2.要确认日志输出中的eventInfo.name为APP_HICOLLIE。 + +3.C++实现了onReceive和onTrigger两种观察者。 + ### 工程目录 ```text @@ -484,8 +524,8 @@ entry/src/main ### 约束与限制 -1. 本示例仅支持标准系统上运行,支持设备:华为手机; -2. 本示例已适配API14版本SDK,版本号:5.0.2.58; +1. 本示例仅支持标准系统上运行,支持设备:RK3568; +2. 本示例已适配API14版本SDK,版本号:5.0.2.58,镜像版本号:OpenHarmony5.0.2.58; 3. 本示例需要使用DevEco Studio(5.0.3.910)及以上版本才可编译运行。 ### 下载 diff --git a/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/cpp/CMakeLists.txt b/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/cpp/CMakeLists.txt index fa2f9febd17..44af7a4b358 100644 --- a/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/cpp/CMakeLists.txt +++ b/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/cpp/CMakeLists.txt @@ -13,5 +13,5 @@ include_directories(${NATIVERENDER_ROOT_PATH} # 新增jsoncpp.cpp(解析订阅事件中的json字符串)源文件 add_library(entry SHARED napi_init.cpp jsoncpp.cpp) -# 新增动态库依赖libhiappevent_ndk.z.so和libhilog_ndk.z.so(日志输出) -target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so libhiappevent_ndk.z.so) +# 新增动态库依赖libhiappevent_ndk.z.so和libhilog_ndk.z.so(日志输出),libohhicollie.so(HiCollie对外检测接口) +target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so libhiappevent_ndk.z.so libohhicollie.so) \ No newline at end of file diff --git a/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/cpp/napi_init.cpp b/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/cpp/napi_init.cpp index e525f468ff2..859eec7d53a 100644 --- a/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/cpp/napi_init.cpp +++ b/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/cpp/napi_init.cpp @@ -24,6 +24,11 @@ #define LOG_TAG "testTag" // [End EventSub_napi_Header] +// [Start Hicollie_Set_Timer_h] +#include +#include "hicollie/hicollie.h" +// [End Hicollie_Set_Timer_h] + // 定义一变量,用来缓存创建的观察者的指针。 // [Start EventSub_onReceive_ptr] static HiAppEvent_Watcher *eventWatcherR; @@ -32,6 +37,15 @@ static HiAppEvent_Watcher *eventWatcherR; static HiAppEvent_Watcher *eventWatcherT; // [End EventSub_onTrigger_ptr] +// [Start App_Hicollie_Watcher_R_ptr] +// 定义一变量,用来缓存创建的观察者的指针。 +static HiAppEvent_Watcher *appHicollieWatcherR; +// [End App_Hicollie_Watcher_R_ptr] +// [Start App_Hicollie_Watcher_T_ptr] +// 定义一变量,用来缓存创建的观察者的指针。 +static HiAppEvent_Watcher *appHicollieWatcherT; +// [End App_Hicollie_Watcher_T_ptr] + // [Start CrashEvent_OnReceive] static void OnReceiveCrashEvent(const struct HiAppEvent_AppEventGroup *appEventGroups, int i, int j) { @@ -142,14 +156,20 @@ static void OnReceiveFreezeEvent(const struct HiAppEvent_AppEventGroup *appEvent } // [End FreezeEvent_OnReceive] // [Start PssLeakEvent_OnReceive] -static void OnReceivePssLeakEvent(const struct HiAppEvent_AppEventGroup *appEventGroups, int i, int j) +//定义一变量,用来缓存创建的观察者的指针。 +static HiAppEvent_Watcher *resouceLeakWatcherR; + +static void HandleLeakEventInfo(const struct HiAppEvent_AppEventInfo &appEventInfos) { - if (strcmp(appEventGroups[i].appEventInfos[j].domain, DOMAIN_OS) == 0 && - strcmp(appEventGroups[i].appEventInfos[j].name, EVENT_RESOURCE_OVERLIMIT) == 0) { + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.domain=%{public}s", appEventInfos.domain); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.name=%{public}s", appEventInfos.name); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.eventType=%{public}d", appEventInfos.type); + if (strcmp(appEventInfos.domain, DOMAIN_OS) == 0 && + strcmp(appEventInfos.name, EVENT_RESOURCE_OVERLIMIT) == 0) { Json::Value params; Json::Reader reader(Json::Features::strictMode()); Json::FastWriter writer; - if (reader.parse(appEventGroups[i].appEventInfos[j].params, params)) { + if (reader.parse(appEventInfos.params, params)) { auto time = params["time"].asInt64(); auto pid = params["pid"].asInt(); auto uid = params["uid"].asInt(); @@ -158,58 +178,166 @@ static void OnReceivePssLeakEvent(const struct HiAppEvent_AppEventGroup *appEven auto bundleVersion = params["bundle_version"].asString(); auto memory = writer.write(params["memory"]); auto externalLog = writer.write(params["external_log"]); - std::string logOverLimit = params["log_over_limit"].asBool() ? "true" : "false"; + std::string logOverLimit = params["log_over_limit"].asBool() ? "true":"false"; OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.time=%{public}lld", time); OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.pid=%{public}d", pid); OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uid=%{public}d", uid); OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.resource_type=%{public}s", - resourceType.c_str()); + resourceType.c_str()); OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_name=%{public}s", - bundleName.c_str()); + bundleName.c_str()); OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_version=%{public}s", - bundleVersion.c_str()); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.memory=%{public}s", memory.c_str()); + bundleVersion.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.memory=%{public}s", + memory.c_str()); OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.external_log=%{public}s", - externalLog.c_str()); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.log_over_limit=%{public}d", - logOverLimit.c_str()); + externalLog.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.log_over_limit=%{public}s", + logOverLimit.c_str()); } } } + +static void OnReceiveLeakEvent(const char *domain, const struct HiAppEvent_AppEventGroup *appEventGroups, + uint32_t groupLen) +{ + for (int i = 0; i < groupLen; ++i) { + for (int j = 0; j < appEventGroups[i].infoLen; ++j) { + HandleLeakEventInfo(appEventGroups[i].appEventInfos[j]); + } + } +} + +static napi_value RegisterLeakReceiveWatcher(napi_env env, napi_callback_info info) +{ + // 开发者自定义观察者名称,系统根据不同的名称来识别不同的观察者。 + resouceLeakWatcherR = OH_HiAppEvent_CreateWatcher("onLeakReceiveWatcher"); + // 设置订阅的事件为EVENT_RESOURCE_OVERLIMIT。 + const char *names[] = {EVENT_RESOURCE_OVERLIMIT}; + // 开发者订阅感兴趣的事件,此处订阅了系统事件。 + OH_HiAppEvent_SetAppEventFilter(resouceLeakWatcherR, DOMAIN_OS, 0, names, 1); + // 开发者设置已实现的回调函数,观察者接收到事件后回立即触发OnReceive回调。 + OH_HiAppEvent_SetWatcherOnReceive(resouceLeakWatcherR, OnReceiveLeakEvent); + // 使观察者开始监听订阅的事件。 + OH_HiAppEvent_AddWatcher(resouceLeakWatcherR); + return {}; +} // [End PssLeakEvent_OnReceive] +// [Start AppKillEvent_OnReceive] +// 定义一变量,用来缓存创建的观察者的指针。 +static HiAppEvent_Watcher *appKillWatcherR; + +static void HandleAppKillEventInfo(const struct HiAppEvent_AppEventInfo &appEventInfos) +{ + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.domain=%{public}s", appEventInfos.domain); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.name=%{public}s", appEventInfos.name); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.eventType=%{public}d", appEventInfos.type); + if (strcmp(appEventInfos.domain, DOMAIN_OS) == 0 && + strcmp(appEventInfos.name, EVENT_APP_KILLED) == 0) { + Json::Value params; + Json::Reader reader(Json::Features::strictMode()); + Json::FastWriter writer; + if (reader.parse(appEventInfos.params, params)) { + auto time = params["time"].asInt64(); + auto reason = params["reason"].asString(); + auto foreground = params["foreground"].asString(); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.time=%{public}lld", time); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.reason=%{public}s", reason.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.foreground=%{public}s", foreground.c_str()); + } + } +} + +static void OnReceiveAppKillEvent(const char *domain, const struct HiAppEvent_AppEventGroup *appEventGroups, + uint32_t groupLen) +{ + for (int i = 0; i < groupLen; ++i) { + for (int j = 0; j < appEventGroups[i].infoLen; ++j) { + HandleAppKillEventInfo(appEventGroups[i].appEventInfos[j]); + } + } +} + +static napi_value RegisterAppKillReceiveWatcher(napi_env env, napi_callback_info info) +{ + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent RegisterWatcher"); + // 开发者自定义观察者名称,系统根据不同的名称来识别不同的观察者。 + appKillWatcherR = OH_HiAppEvent_CreateWatcher("onAppKillReceiverWatcher"); + // 设置订阅的事件为EVENT_APP_KILLED。 + const char *names[] = {EVENT_APP_KILLED}; + // 开发者订阅感兴趣的事件,此处订阅了系统事件。 + OH_HiAppEvent_SetAppEventFilter(appKillWatcherR, DOMAIN_OS, 0, names, 1); + // 开发者设置已实现的回调函数,观察者接收到事件后回立即触发OnReceive回调。 + OH_HiAppEvent_SetWatcherOnReceive(appKillWatcherR, OnReceiveAppKillEvent); + // 使观察者开始监听订阅的事件。 + OH_HiAppEvent_AddWatcher(appKillWatcherR); + return {}; +} +// [End AppKillEvent_OnReceive] // [Start AsanEvent_OnReceive] -static void OnReceiveAsanEvent(const struct HiAppEvent_AppEventGroup *appEventGroups, int i, int j) +//定义一变量,用来缓存创建的观察者的指针。 +static HiAppEvent_Watcher *sanitizerWatcherR; + +static void HandleSanitizerEventInfo(const struct HiAppEvent_AppEventInfo &appEventInfos) { - if (strcmp(appEventGroups[i].appEventInfos[j].domain, DOMAIN_OS) == 0 && - strcmp(appEventGroups[i].appEventInfos[j].name, EVENT_ADDRESS_SANITIZER) == 0) { + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.domain=%{public}s", appEventInfos.domain); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.name=%{public}s", appEventInfos.name); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.eventType=%{public}d", appEventInfos.type); + if (strcmp(appEventInfos.domain, DOMAIN_OS) == 0 && + strcmp(appEventInfos.name, EVENT_ADDRESS_SANITIZER) == 0) { Json::Value params; Json::Reader reader(Json::Features::strictMode()); Json::FastWriter writer; - if (reader.parse(appEventGroups[i].appEventInfos[j].params, params)) { + if (reader.parse(appEventInfos.params, params)) { auto time = params["time"].asInt64(); auto bundleVersion = params["bundle_version"].asString(); auto bundleName = params["bundle_name"].asString(); auto pid = params["pid"].asInt(); auto uid = params["uid"].asInt(); auto type = params["type"].asString(); - auto boolFlag = params["log_over_limit"].asBool(); std::string logOverLimit = params["log_over_limit"].asBool() ? "true" : "false"; auto externalLog = writer.write(params["external_log"]); OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.time=%{public}lld", time); OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_version=%{public}s", - bundleVersion.c_str()); + bundleVersion.c_str()); OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_name=%{public}s", - bundleName.c_str()); + bundleName.c_str()); OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.pid=%{public}d", pid); OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uid=%{public}d", uid); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.type=%{public}s", type.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.type=%{public}s", + type.c_str()); OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.external_log=%{public}s", - externalLog.c_str()); + externalLog.c_str()); OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.log_over_limit=%{public}s", - logOverLimit.c_str()); + logOverLimit.c_str()); } } } + +static void OnReceiveSanitizerEvent(const char *domain, const struct HiAppEvent_AppEventGroup *appEventGroups, + uint32_t groupLen) +{ + for (int i = 0; i < groupLen; ++i) { + for (int j = 0; j < appEventGroups[i].infoLen; ++j) { + HandleSanitizerEventInfo(appEventGroups[i].appEventInfos[j]); + } + } +} + +static napi_value RegisterSanitizerReceiveWatcher(napi_env env, napi_callback_info info) +{ + // 开发者自定义观察者名称,系统根据不同的名称来识别不同的观察者。 + sanitizerWatcherR = OH_HiAppEvent_CreateWatcher("onSanitizerReceiveWatcher"); + // 设置订阅的事件为EVENT_ADDRESS_SANITIZER。 + const char *names[] = {EVENT_ADDRESS_SANITIZER}; + // 开发者订阅感兴趣的事件,此处订阅了系统事件。 + OH_HiAppEvent_SetAppEventFilter(sanitizerWatcherR, DOMAIN_OS, 0, names, 1); + // 开发者设置已实现的回调函数,观察者接收到事件后回立即触发OnReceive回调。 + OH_HiAppEvent_SetWatcherOnReceive(sanitizerWatcherR, OnReceiveSanitizerEvent); + // 使观察者开始监听订阅的事件。 + OH_HiAppEvent_AddWatcher(sanitizerWatcherR); + return {}; +} // [End AsanEvent_OnReceive] // [Start TimeOutEvent_OnReceive] static void OnReceiveTimeOutEvent(const struct HiAppEvent_AppEventGroup *appEventGroups, int i, int j) @@ -367,10 +495,6 @@ static void OnReceive(const char *domain, const struct HiAppEvent_AppEventGroup OnReceiveCrashEvent(appEventGroups, i, j); // 处理卡死事件 OnReceiveFreezeEvent(appEventGroups, i, j); - // 处理内存泄漏事件 - OnReceivePssLeakEvent(appEventGroups, i, j); - // 处理ASAN事件 - OnReceiveAsanEvent(appEventGroups, i, j); // 处理主线程卡顿事件 OnReceiveTimeOutEvent(appEventGroups, i, j); } @@ -460,59 +584,145 @@ static void OnTriggerFreezeEvent(std::string domain, std::string name, Json::Val } // [End FreezeEvent_OnTrigger] // [Start PssLeakEvent_OnTrigger] -static void OnTriggerPssLeakEvent(std::string domain, std::string name, Json::Value eventInfo, Json::FastWriter writer) +//定义一变量,用来缓存创建的观察者的指针。 +static HiAppEvent_Watcher *resouceLeakWatcherT; + +// 开发者可以自行实现获取已监听到事件的回调函数,其中events指针指向内容仅在该函数内有效。 +static void OnTakeLeakEvent(const char *const *events, uint32_t eventLen) { - if (domain == DOMAIN_OS && name == EVENT_RESOURCE_OVERLIMIT) { - auto time = eventInfo["time"].asInt64(); - auto pid = eventInfo["pid"].asInt(); - auto uid = eventInfo["uid"].asInt(); - auto resourceType = eventInfo["resourceType"].asString(); - auto bundleName = eventInfo["bundle_name"].asString(); - auto bundleVersion = eventInfo["bundle_version"].asString(); - auto memory = writer.write(eventInfo["memory"]); - auto externalLog = writer.write(eventInfo["external_log"]); - std::string logOverLimit = eventInfo["log_over_limit"].asBool() ? "true" : "false"; - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.time=%{public}lld", time); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.pid=%{public}d", pid); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uid=%{public}d", uid); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.resource_type=%{public}s", + Json::Reader reader(Json::Features::strictMode()); + Json::FastWriter writer; + for (int i = 0; i < eventLen; ++i) { + Json::Value eventInfo; + if (reader.parse(events[i], eventInfo)) { + auto domain = eventInfo["domain_"].asString(); + auto name = eventInfo["name_"].asString(); + auto type = eventInfo["type_"].asInt(); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.domain=%{public}s", domain.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.name=%{public}s", name.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.eventType=%{public}d", type); + if (domain == DOMAIN_OS && name == EVENT_RESOURCE_OVERLIMIT) { + auto time = eventInfo["time"].asInt64(); + auto pid = eventInfo["pid"].asInt(); + auto uid = eventInfo["uid"].asInt(); + auto resourceType = eventInfo["resourceType"].asString(); + auto bundleName = eventInfo["bundle_name"].asString(); + auto bundleVersion = eventInfo["bundle_version"].asString(); + auto memory = writer.write(eventInfo["memory"]); + auto externalLog = writer.write(eventInfo["external_log"]); + std::string logOverLimit = eventInfo["log_over_limit"].asBool() ? "true":"false"; + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.time=%{public}lld", time); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.pid=%{public}d", pid); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uid=%{public}d", uid); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.resource_type=%{public}s", resourceType.c_str()); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_name=%{public}s", bundleName.c_str()); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_version=%{public}s", + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_name=%{public}s", + bundleName.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_version=%{public}s", bundleVersion.c_str()); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.memory=%{public}s", memory.c_str()); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.external_log=%{public}s", + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.memory=%{public}s", + memory.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.external_log=%{public}s", externalLog.c_str()); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.log_over_limit=%{public}d", + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.log_over_limit=%{public}s", logOverLimit.c_str()); + } + } } } + +// 开发者可以自行实现订阅回调函数,以便对获取到的事件打点数据进行自定义处理。 +static void OnTriggerLeakEvent(int row, int size) +{ + // 接收回调后,获取指定数量的已接收事件。 + OH_HiAppEvent_TakeWatcherData(resouceLeakWatcherT, row, OnTakeLeakEvent); +} + +static napi_value RegisterLeakTriggerWatcher(napi_env env, napi_callback_info info) +{ + // 开发者自定义观察者名称,系统根据不同的名称来识别不同的观察者。 + resouceLeakWatcherT = OH_HiAppEvent_CreateWatcher("onLeakTriggerWatcher"); + // 设置订阅的事件为EVENT_RESOURCE_OVERLIMIT。 + const char *names[] = {EVENT_RESOURCE_OVERLIMIT}; + // 开发者订阅感兴趣的事件,此处订阅了系统事件。 + OH_HiAppEvent_SetAppEventFilter(resouceLeakWatcherT, DOMAIN_OS, 0, names, 1); + // 开发者设置已实现的回调函数,需OH_HiAppEvent_SetTriggerCondition设置的条件满足方可触发。 + OH_HiAppEvent_SetWatcherOnTrigger(resouceLeakWatcherT, OnTriggerLeakEvent); + // 开发者可以设置订阅触发回调的条件,此处是设置新增事件打点数量为2个时,触发onTrigger回调。 + OH_HiAppEvent_SetTriggerCondition(resouceLeakWatcherT, 1, 0, 0); + // 使观察者开始监听订阅的事件。 + OH_HiAppEvent_AddWatcher(resouceLeakWatcherT); + return {}; +} // [End PssLeakEvent_OnTrigger] // [Start AsanEvent_OnTrigger] -static void OnTriggerAsanEvent(std::string domain, std::string name, Json::Value eventInfo, Json::FastWriter writer) +//定义一变量,用来缓存创建的观察者的指针。 +static HiAppEvent_Watcher *sanitizerTriggerWatcher; + +// 开发者可以自行实现获取已监听到事件的回调函数,其中events指针指向内容仅在该函数内有效。 +static void OnTakeSanitizerEvent(const char *const *events, uint32_t eventLen) { - if (domain == DOMAIN_OS && name == EVENT_ADDRESS_SANITIZER) { - auto time = eventInfo["time"].asInt64(); - auto bundleVersion = eventInfo["bundle_version"].asString(); - auto bundleName = eventInfo["bundle_name"].asString(); - auto pid = eventInfo["pid"].asInt(); - auto uid = eventInfo["uid"].asInt(); - auto asanType = eventInfo["type"].asString(); - auto externalLog = writer.write(eventInfo["external_log"]); - std::string logOverLimit = eventInfo["log_over_limit"].asBool() ? "true" : "false"; - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.time=%{public}lld", time); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_version=%{public}s", + Json::Reader reader(Json::Features::strictMode()); + Json::FastWriter writer; + for (int i = 0; i < eventLen; ++i) { + Json::Value eventInfo; + if (reader.parse(events[i], eventInfo)) { + auto domain = eventInfo["domain_"].asString(); + auto name = eventInfo["name_"].asString(); + auto type = eventInfo["type_"].asInt(); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.domain=%{public}s", domain.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.name=%{public}s", name.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.eventType=%{public}d", type); + if (domain == DOMAIN_OS && name == EVENT_ADDRESS_SANITIZER) { + auto time = eventInfo["time"].asInt64(); + auto bundleVersion = eventInfo["bundle_version"].asString(); + auto bundleName = eventInfo["bundle_name"].asString(); + auto pid = eventInfo["pid"].asInt(); + auto uid = eventInfo["uid"].asInt(); + auto asanType = eventInfo["type"].asString(); + auto externalLog = writer.write(eventInfo["external_log"]); + std::string logOverLimit = eventInfo["log_over_limit"].asBool() ? "true" : "false"; + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.time=%{public}lld", time); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_version=%{public}s", bundleVersion.c_str()); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_name=%{public}s", bundleName.c_str()); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.pid=%{public}d", pid); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uid=%{public}d", uid); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.crash_type=%{public}s", asanType.c_str()); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.external_log=%{public}s", + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_name=%{public}s", + bundleName.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.pid=%{public}d", pid); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uid=%{public}d", uid); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.crash_type=%{public}s", + asanType.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.external_log=%{public}s", externalLog.c_str()); - OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.log_over_limit=%{public}s", + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.log_over_limit=%{public}s", logOverLimit.c_str()); + } + } } } + +// 开发者可以自行实现订阅回调函数,以便对获取到的事件打点数据进行自定义处理。 +static void OnTriggerSanitizerEvent(int row, int size) +{ + // 接收回调后,获取指定数量的已接收事件。 + OH_HiAppEvent_TakeWatcherData(sanitizerTriggerWatcher, row, OnTakeSanitizerEvent); +} + +static napi_value RegisterSanitizerTriggerWatcher(napi_env env, napi_callback_info info) +{ + // 开发者自定义观察者名称,系统根据不同的名称来识别不同的观察者。 + sanitizerTriggerWatcher = OH_HiAppEvent_CreateWatcher("sanitizerTriggerWatcher"); + // 设置订阅的事件为EVENT_ADDRESS_SANITIZER。 + const char *names[] = {EVENT_ADDRESS_SANITIZER}; + // 开发者订阅感兴趣的事件,此处订阅了系统事件。 + OH_HiAppEvent_SetAppEventFilter(sanitizerTriggerWatcher, DOMAIN_OS, 0, names, 1); + // 开发者设置已实现的回调函数,需OH_HiAppEvent_SetTriggerCondition设置的条件满足方可触发。 + OH_HiAppEvent_SetWatcherOnTrigger(sanitizerTriggerWatcher, OnTriggerSanitizerEvent); + // 开发者可以设置订阅触发回调的条件,此处是设置新增事件打点数量为1个时,触发onTrigger回调。 + OH_HiAppEvent_SetTriggerCondition(sanitizerTriggerWatcher, 1, 0, 0); + // 使观察者开始监听订阅的事件。 + OH_HiAppEvent_AddWatcher(sanitizerTriggerWatcher); + return {}; +} // [End AsanEvent_OnTrigger] // [Start EventSub_OnTake_All] // 开发者可以自行实现获取已监听到事件的回调函数,其中events指针指向内容仅在该函数内有效。 @@ -535,10 +745,6 @@ static void OnTake(const char *const *events, uint32_t eventLen) OnTriggerCrashEvent(domain, name, eventInfo, writer); // 处理卡死事件 OnTriggerFreezeEvent(domain, name, eventInfo, writer); - // 处理内存泄漏事件 - OnTriggerPssLeakEvent(domain, name, eventInfo, writer); - // 处理ASAN事件 - OnTriggerAsanEvent(domain, name, eventInfo, writer); } } } @@ -558,8 +764,7 @@ static napi_value RegisterWatcher(napi_env env, napi_callback_info info) eventWatcherT = OH_HiAppEvent_CreateWatcher("onTriggerWatcher"); eventWatcherR = OH_HiAppEvent_CreateWatcher("onReceiverWatcher"); // 设置订阅的事件名称为click, EVENT_APP_CRASH。 - const char *names[] = {"click", EVENT_APP_CRASH, EVENT_APP_FREEZE, EVENT_RESOURCE_OVERLIMIT, - EVENT_ADDRESS_SANITIZER, EVENT_MAIN_THREAD_JANK}; + const char *names[] = {"click", EVENT_APP_CRASH, EVENT_APP_FREEZE, EVENT_MAIN_THREAD_JANK}; int namesSize = sizeof(names) / sizeof(names[0]); // 开发者订阅感兴趣的应用事件 OH_HiAppEvent_SetAppEventFilter(eventWatcherT, "button", 0, names, namesSize); @@ -580,26 +785,65 @@ static napi_value RegisterWatcher(napi_env env, napi_callback_info info) return {}; } // [End EventSub_RegisterWatcher_All] + // [Start EventSub_RemoveWatcher_All] // [Start AppEvent_C++_RemoveWatcher] +// [Start APP_Hicollie_RemoveWatcher] +// [Start AsanEvent_RemoveWatcher] +// [Start PssLeakEvent_RemoveWatcher] +// [Start AppKillEvent_RemoveWatcher] static napi_value RemoveWatcher(napi_env env, napi_callback_info info) { // 使观察者停止监听事件 + // [StartExclude AppKillEvent_RemoveWatcher] + // [StartExclude PssLeakEvent_RemoveWatcher] + // [StartExclude AsanEvent_RemoveWatcher] + // [StartExclude APP_Hicollie_RemoveWatcher] // [StartExclude AppEvent_C++_RemoveWatcher] OH_HiAppEvent_RemoveWatcher(eventWatcherT); OH_HiAppEvent_RemoveWatcher(eventWatcherR); // [EndExclude AppEvent_C++_RemoveWatcher] OH_HiAppEvent_RemoveWatcher(eventWatcherT1); OH_HiAppEvent_RemoveWatcher(eventWatcherR1); + // [EndExclude APP_Hicollie_RemoveWatcher] + // [StartExclude AppEvent_C++_RemoveWatcher] + OH_HiAppEvent_RemoveWatcher(appHicollieWatcherR); + OH_HiAppEvent_RemoveWatcher(appHicollieWatcherT); + // [EndExclude AsanEvent_RemoveWatcher] + // [StartExclude APP_Hicollie_RemoveWatcher] + OH_HiAppEvent_RemoveWatcher(sanitizerWatcherR); + // [StartExclude AsanEvent_RemoveWatcher] + // [EndExclude PssLeakEvent_RemoveWatcher] + OH_HiAppEvent_RemoveWatcher(resouceLeakWatcherR); + // [EndExclude AppKillEvent_RemoveWatcher] + // [StartExclude PssLeakEvent_RemoveWatcher] + OH_HiAppEvent_RemoveWatcher(appKillWatcherR); + // [EndExclude AppEvent_C++_RemoveWatcher] + // [EndExclude AsanEvent_RemoveWatcher] + // [EndExclude PssLeakEvent_RemoveWatcher] + // [EndExclude APP_Hicollie_RemoveWatcher] return {}; } +// [End AppKillEvent_RemoveWatcher] +// [End PssLeakEvent_RemoveWatcher] +// [End AsanEvent_RemoveWatcher] +// [End APP_Hicollie_RemoveWatcher] // [End AppEvent_C++_RemoveWatcher] // [End EventSub_RemoveWatcher_All] + // [Start EventSub_DestroyWatcher_All] // [Start AppEvent_C++_DestroyWatcher] +// [Start APP_Hicollie_DestroyWatcher] +// [Start AsanEvent_DestroyWatcher] +// [Start PssLeakEvent_DestroyWatcher] +// [Start AppKillEvent_DestroyWatcher] static napi_value DestroyWatcher(napi_env env, napi_callback_info info) { // 销毁创建的观察者,并置eventWatcher为nullptr。 + // [StartExclude AppKillEvent_DestroyWatcher] + // [StartExclude PssLeakEvent_DestroyWatcher] + // [StartExclude AsanEvent_DestroyWatcher] + // [StartExclude APP_Hicollie_DestroyWatcher] // [StartExclude AppEvent_C++_DestroyWatcher] OH_HiAppEvent_DestroyWatcher(eventWatcherT); OH_HiAppEvent_DestroyWatcher(eventWatcherR); @@ -610,10 +854,37 @@ static napi_value DestroyWatcher(napi_env env, napi_callback_info info) OH_HiAppEvent_DestroyWatcher(eventWatcherR1); eventWatcherT1 = nullptr; eventWatcherR1 = nullptr; + // [EndExclude APP_Hicollie_DestroyWatcher] + // [StartExclude AppEvent_C++_DestroyWatcher] + OH_HiAppEvent_DestroyWatcher(appHicollieWatcherR); + OH_HiAppEvent_DestroyWatcher(appHicollieWatcherT); + appHicollieWatcherR = nullptr; + appHicollieWatcherT = nullptr; + // [EndExclude AsanEvent_DestroyWatcher] + // [StartExclude APP_Hicollie_DestroyWatcher] + OH_HiAppEvent_DestroyWatcher(sanitizerWatcherR); + sanitizerWatcherR = nullptr; + // [EndExclude PssLeakEvent_DestroyWatcher] + // [StartExclude AsanEvent_DestroyWatcher] + OH_HiAppEvent_DestroyWatcher(resouceLeakWatcherR); + resouceLeakWatcherR = nullptr; + // [EndExclude AppKillEvent_DestroyWatcher] + // [StartExclude PssLeakEvent_DestroyWatcher] + OH_HiAppEvent_DestroyWatcher(appKillWatcherR); + appKillWatcherR = nullptr; + // [EndExclude APP_Hicollie_DestroyWatcher] + // [EndExclude AppEvent_C++_DestroyWatcher] + // [EndExclude AsanEvent_DestroyWatcher] + // [EndExclude PssLeakEvent_DestroyWatcher] return {}; } +// [End AppKillEvent_DestroyWatcher] +// [End PssLeakEvent_DestroyWatcher] +// [End AsanEvent_DestroyWatcher] +// [End APP_Hicollie_DestroyWatcher] // [End AppEvent_C++_DestroyWatcher] // [End EventSub_DestroyWatcher_All] + // [Start AppEvent_Click_C++_WriteAppEvent] static napi_value WriteAppEvent(napi_env env, napi_callback_info info) { @@ -626,33 +897,271 @@ static napi_value WriteAppEvent(napi_env env, napi_callback_info info) } // [End AppEvent_Click_C++_WriteAppEvent] // [Start AsanEvent_AddressTest] -static napi_value AddressTest(napi_env env, napi_callback_info info) -{ - // 任意实数 - int num = 1; - int length = 10; - // 构造数组越界写入 - int a[length]; - a[length] = num; +static napi_value AddressSanitizerTest(napi_env env, napi_callback_info info) +{ + constexpr int bufferSize = 42; + int subscript = 43; + char buffer[bufferSize]; + buffer[subscript] = subscript; + printf("address: %p", buffer); return {}; } // [End AsanEvent_AddressTest] +// [Start AppKillEvent_NativeLeak] +#include + +static void NativeLeak() +{ + constexpr int leakSizePerTime = 500000; + constexpr int sleepTimeMs = 10; + while (true) { + char *p = (char *)malloc(leakSizePerTime + 1); + if (!p) { + break; + } + memset(p, 'a', leakSizePerTime); + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTimeMs)); + } +} + +static napi_value Leak(napi_env env, napi_callback_info info) +{ + std::thread t1(NativeLeak); + t1.detach(); + return {}; +} +// [End AppKillEvent_NativeLeak] // [Start EventSub_Init_All] // [Start AppEvent_C++_Init] + +// [StartExclude AppEvent_C++_Init] +// [Start Hicollie_Set_Timer] +//定义回调函数 +void CallBack(void*) +{ + OH_LOG_INFO(LogType::LOG_APP, "HiCollieTimerNdk CallBack"); // 回调函数中打印日志 +} + +static napi_value TestHiCollieTimerNdk(napi_env env, napi_callback_info info) +{ + int id; + // 设置HiCollieTimer 参数(Timer任务名,超时时间,回调函数,回调函数参数,超时发生后行为) + HiCollie_SetTimerParam param = {"testTimer", 1, CallBack, nullptr, HiCollie_Flag::HICOLLIE_FLAG_LOG}; + HiCollie_ErrorCode errorCode = OH_HiCollie_SetTimer(param, &id); // 注册HiCollieTimer函数执行时长超时检测一次性任务 + if (errorCode == HICOLLIE_SUCCESS) { // HiCollieTimer任务注册成功 + OH_LOG_INFO(LogType::LOG_APP, "HiCollieTimer taskId: %{public}d", id); // 打印任务id + sleep(2); // 模拟执行耗时函数,在这里简单的将线程阻塞2s + OH_HiCollie_CancelTimer(id); // 根据id取消已注册任务 + } + return nullptr; +} +// [End Hicollie_Set_Timer] + +// [Start App_Hicollie_OnReceive] +static void OnReceiveAppHicollie(const struct HiAppEvent_AppEventGroup *appEventGroups, int i, int j) +{ + if (strcmp(appEventGroups[i].appEventInfos[j].domain, DOMAIN_OS) == 0 && + strcmp(appEventGroups[i].appEventInfos[j].name, EVENT_APP_HICOLLIE) == 0) { + Json::Value params; + Json::Reader reader(Json::Features::strictMode()); + Json::FastWriter writer; + if (reader.parse(appEventGroups[i].appEventInfos[j].params, params)) { + auto time = params["time"].asInt64(); + auto foreground = params["foreground"].asBool(); + auto bundleVersion = params["bundle_version"].asString(); + auto processName = params["process_name"].asString(); + auto pid = params["pid"].asInt(); + auto uid = params["uid"].asInt(); + auto uuid = params["uuid"].asString(); + auto exception = writer.write(params["exception"]); + auto hilogSize = params["hilog"].size(); + auto peerBindSize = params["peer_binder"].size(); + auto memory = writer.write(params["memory"]); + auto externalLog = writer.write(params["external_log"]); + auto logOverLimit = params["log_over_limit"].asBool(); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.time=%{public}lld", time); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.foreground=%{public}d", foreground); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_version=%{public}s", + bundleVersion.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.process_name=%{public}s", processName.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.pid=%{public}d", pid); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uid=%{public}d", uid); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uuid=%{public}s", uuid.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.exception=%{public}s", exception.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.hilog.size=%{public}d", hilogSize); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.peer_binder.size=%{public}d", peerBindSize); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.memory=%{public}s", memory.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.external_log=%{public}s", externalLog.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.log_over_limit=%{public}d", logOverLimit); + } + } +} + +static void AppHicollieOnReceive(const char *domain, const struct HiAppEvent_AppEventGroup *appEventGroups, + uint32_t groupLen) +{ + for (int i = 0; i < groupLen; ++i) { + for (int j = 0; j < appEventGroups[i].infoLen; ++j) { + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.domain=%{public}s", + appEventGroups[i].appEventInfos[j].domain); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.name=%{public}s", + appEventGroups[i].appEventInfos[j].name); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.eventType=%{public}d", + appEventGroups[i].appEventInfos[j].type); + OnReceiveAppHicollie(appEventGroups, i, j); + } + } +} + +static napi_value RegisterAppHicollieWatcherR(napi_env env, napi_callback_info info) +{ + // 开发者自定义观察者名称,系统根据不同的名称来识别不同的观察者。 + appHicollieWatcherR = OH_HiAppEvent_CreateWatcher("appHicollieWatcherR"); + // 设置订阅的事件为EVENT_APP_HICOLLIE。 + const char *names[] = {EVENT_APP_HICOLLIE}; + // 开发者订阅感兴趣的事件,此处订阅了系统事件。 + OH_HiAppEvent_SetAppEventFilter(appHicollieWatcherR, DOMAIN_OS, 0, names, 1); + // 开发者设置已实现的回调函数,观察者接收到事件后回立即触发OnReceive回调。 + OH_HiAppEvent_SetWatcherOnReceive(appHicollieWatcherR, AppHicollieOnReceive); + // 使观察者开始监听订阅的事件。 + OH_HiAppEvent_AddWatcher(appHicollieWatcherR); + return {}; +} +// [End App_Hicollie_OnReceive] + +// [Start App_Hicollie_Trigger] +// 开发者可以自行实现获取已监听到事件的回调函数,其中events指针指向内容仅在该函数内有效。 +static void AppHicollieOnTake(const char *const *events, uint32_t eventLen) +{ + Json::Reader reader(Json::Features::strictMode()); + Json::FastWriter writer; + for (int i = 0; i < eventLen; ++i) { + Json::Value eventInfo; + if (reader.parse(events[i], eventInfo)) { + auto domain = eventInfo["domain_"].asString(); + auto name = eventInfo["name_"].asString(); + auto type = eventInfo["type_"].asInt(); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.domain=%{public}s", domain.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.name=%{public}s", name.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.eventType=%{public}d", type); + if (domain == DOMAIN_OS && name == EVENT_APP_HICOLLIE) { + auto time = eventInfo["time"].asInt64(); + auto foreground = eventInfo["foreground"].asBool(); + auto bundleVersion = eventInfo["bundle_version"].asString(); + auto processName = eventInfo["process_name"].asString(); + auto pid = eventInfo["pid"].asInt(); + auto uid = eventInfo["uid"].asInt(); + auto uuid = eventInfo["uuid"].asString(); + auto exception = writer.write(eventInfo["exception"]); + auto hilogSize = eventInfo["hilog"].size(); + auto peerBindSize = eventInfo["peer_binder"].size(); + auto memory = writer.write(eventInfo["memory"]); + auto externalLog = writer.write(eventInfo["external_log"]); + auto logOverLimit = eventInfo["log_over_limit"].asBool(); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.time=%{public}lld", time); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.foreground=%{public}d", foreground); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_version=%{public}s", + bundleVersion.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.process_name=%{public}s", + processName.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.pid=%{public}d", pid); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uid=%{public}d", uid); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uuid=%{public}s", uuid.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.exception=%{public}s", exception.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.hilog.size=%{public}d", hilogSize); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.peer_binder.size=%{public}d", peerBindSize); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.memory=%{public}s", memory.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.external_log=%{public}s", + externalLog.c_str()); + OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.log_over_limit=%{public}d", logOverLimit); + } + } + } +} + +// 开发者可以自行实现订阅回调函数,以便对获取到的事件打点数据进行自定义处理。 +static void AppHicollieOnTrigger(int row, int size) +{ + // 接收回调后,获取指定数量的已接收事件。 + OH_HiAppEvent_TakeWatcherData(appHicollieWatcherT, row, AppHicollieOnTake); +} + +static napi_value RegisterAppHicollieWatcherT(napi_env env, napi_callback_info info) +{ + // 开发者自定义观察者名称,系统根据不同的名称来识别不同的观察者。 + appHicollieWatcherT = OH_HiAppEvent_CreateWatcher("appHicollieWatcherT"); + // 设置订阅的事件为EVENT_APP_HICOLLIE。 + const char *names[] = {EVENT_APP_HICOLLIE}; + // 开发者订阅感兴趣的事件,此处订阅了系统事件。 + OH_HiAppEvent_SetAppEventFilter(appHicollieWatcherT, DOMAIN_OS, 0, names, 1); + // 开发者设置已实现的回调函数,需OH_HiAppEvent_SetTriggerCondition设置的条件满足方可触发。 + OH_HiAppEvent_SetWatcherOnTrigger(appHicollieWatcherT, AppHicollieOnTrigger); + // 开发者可以设置订阅触发回调的条件,此处是设置新增事件打点数量为1个时,触发onTrigger回调。 + OH_HiAppEvent_SetTriggerCondition(appHicollieWatcherT, 1, 0, 0); + // 使观察者开始监听订阅的事件。 + OH_HiAppEvent_AddWatcher(appHicollieWatcherT); + return {}; +} +// [End App_Hicollie_Trigger] +// [EndExclude AppEvent_C++_Init] + +// [Start AsanEvent_Init] +// [Start AsanEventTS_Init] +// [Start PssLeakEvent_Init] +// [Start AppKillEvent_Init] static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor desc[] = { + // [StartExclude AppKillEvent_Init] + // [StartExclude PssLeakEvent_Init] + // [StartExclude AsanEvent_Init] + // [StartExclude AsanEventTS_Init] // [StartExclude AppEvent_C++_Init] {"registerWatcher", nullptr, RegisterWatcher, nullptr, nullptr, nullptr, napi_default, nullptr}, - {"addressTest", nullptr, AddressTest, nullptr, nullptr, nullptr, napi_default, nullptr}, // [EndExclude AppEvent_C++_Init] { "registerWatcherCrash", nullptr, RegisterWatcherCrash, nullptr, nullptr, nullptr, napi_default, nullptr }, { "registerWatcherClick", nullptr, RegisterWatcherClick, nullptr, nullptr, nullptr, napi_default, nullptr }, - { "writeAppEvent", nullptr, WriteAppEvent, nullptr, nullptr, nullptr, napi_default, nullptr } + { "writeAppEvent", nullptr, WriteAppEvent, nullptr, nullptr, nullptr, napi_default, nullptr }, + // [StartExclude AppEvent_C++_Init] + // [Start test_hicollie_timer] + // 将TestHiCollieTimerNdk注册为ArkTS接口 + { "TestHiCollieTimerNdk", nullptr, TestHiCollieTimerNdk, nullptr, nullptr, nullptr, napi_default, nullptr }, + // [End test_hicollie_timer] + // [Start register_app_hicollie_watcherR] + { "RegisterAppHicollieWatcherR", nullptr, RegisterAppHicollieWatcherR, nullptr, nullptr, nullptr, + napi_default, nullptr }, + // [End register_app_hicollie_watcherR] + // [Start register_app_hicollie_watcherT] + { "RegisterAppHicollieWatcherT", nullptr, RegisterAppHicollieWatcherT, nullptr, nullptr, nullptr, + napi_default, nullptr }, + // [End register_app_hicollie_watcherT] + // [EndExclude AsanEvent_Init] + { "registerSanitizerReceiveWatcher", nullptr, RegisterSanitizerReceiveWatcher, nullptr, nullptr, nullptr, + napi_default, nullptr }, + // [EndExclude AsanEventTS_Init] + { "addressSanitizerTest", nullptr, AddressSanitizerTest, nullptr, nullptr, nullptr, napi_default, nullptr}, + // [EndExclude PssLeakEvent_Init] + // [StartExclude AsanEventTS_Init] + // [StartExclude AsanEvent_Init] + { "registerLeakReceiveWatcher", nullptr, RegisterLeakReceiveWatcher, nullptr, nullptr, nullptr, + napi_default, nullptr }, + // [StartExclude PssLeakEvent_Init] + // [EndExclude AppKillEvent_Init] + { "registerAppKillReceiveWatcher", nullptr, RegisterAppKillReceiveWatcher, nullptr, nullptr, nullptr, + napi_default, nullptr }, + { "leak", nullptr, Leak, nullptr, nullptr, nullptr, napi_default, nullptr} + // [EndExclude AsanEvent_Init] + // [EndExclude AsanEventTS_Init] + // [EndExclude PssLeakEvent_Init] + // [EndExclude AppEvent_C++_Init] }; napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); return exports; } +// [End AppKillEvent_Init] +// [End PssLeakEvent_Init] +// [End AsanEventTS_Init] +// [End AsanEvent_Init] // [End AppEvent_C++_Init] // [End EventSub_Init_All] static napi_module demoModule = { diff --git a/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/cpp/types/libentry/Index.d.ts b/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/cpp/types/libentry/Index.d.ts index 6829fba35cd..d57348cc5d9 100644 --- a/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/cpp/types/libentry/Index.d.ts +++ b/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/cpp/types/libentry/Index.d.ts @@ -15,10 +15,29 @@ // [Start EventSub_Index.d.ts_All] export const registerWatcher: () => void; -export const addressTest: () => void; // [Start AppEvent_C++_Index.d.ts] export const registerWatcherCrash: () => void; export const registerWatcherClick: () => void; export const writeAppEvent: () => void; // [End AppEvent_C++_Index.d.ts] +// [Start test_hicollie_timer_Index.d.ts] +export const TestHiCollieTimerNdk: () => void; +// [End test_hicollie_timer_Index.d.ts] +// [Start AsanEvent_Index.d.ts] +export const registerSanitizerReceiveWatcher: () => void; +export const addressSanitizerTest: () => void; +// [End AsanEvent_Index.d.ts] +// [Start PssLeakEvent_Index.d.ts] +export const registerLeakReceiveWatcher: () => void; +// [End PssLeakEvent_Index.d.ts] +// [Start AppKillEvent_Index.d.ts] +export const registerAppKillReceiveWatcher: () => void; +export const leak: () => void; +// [End AppKillEvent_Index.d.ts] +// [Start Register_AppHicollie_WatcherR.d.ts] +export const RegisterAppHicollieWatcherR: () => void; +// [End Register_AppHicollie_WatcherR.d.ts] +// [Start Register_AppHicollie_WatcherT.d.ts] +export const RegisterAppHicollieWatcherT: () => void; +// [End Register_AppHicollie_WatcherT.d.ts] // [End EventSub_Index.d.ts_All] diff --git a/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/ets/entryability/EntryAbility.ets b/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/ets/entryability/EntryAbility.ets index e049d357c7e..eaa43d42207 100644 --- a/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/ets/entryability/EntryAbility.ets +++ b/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/ets/entryability/EntryAbility.ets @@ -15,11 +15,13 @@ // [Start EventSub_EntryAbility_Header] import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +// [Start PssLeakEvent_ArkTS_Header] // [Start AppEvent_Crash_Click_ArkTS_Header] import { hiAppEvent, hilog } from '@kit.PerformanceAnalysisKit'; // [End AppEvent_Crash_Click_ArkTS_Header] -import { hidebug } from '@kit.PerformanceAnalysisKit'; import { BusinessError } from '@kit.BasicServicesKit'; +// [End PssLeakEvent_ArkTS_Header] +import { hidebug } from '@kit.PerformanceAnalysisKit'; import { window } from '@kit.ArkUI'; // [End EventSub_EntryAbility_Header] // [Start EventSub_Capi_Header] @@ -89,21 +91,26 @@ export default class EntryAbility extends UIAbility { } }); // [End AppEvent_Click_ArkTS_Add_Watcher] - // [Start Crash&FreezeEvent_ArkTS_Add_Event] + // [Start ArkTS_Add_Event_Params] // 开发者完成参数键值对赋值 let params: Record = { 'testData': 100, }; + // [End ArkTS_Add_Event_Params] + // [Start Crash_ArkTS_Add_Event] // 开发者可以设置事件的自定义参数 hiAppEvent.setEventParam(params, hiAppEvent.domain.OS, hiAppEvent.event.APP_CRASH).then(() => { }).catch((err: BusinessError) => { hilog.error(0x0000, 'testTag', `HiAppEvent code: ${err.code}, message: ${err.message}`); }); + // [End Crash_ArkTS_Add_Event] + // [Start FreezeEvent_ArkTS_Add_Event] + // 开发者可以设置事件的自定义参数 hiAppEvent.setEventParam(params, hiAppEvent.domain.OS, hiAppEvent.event.APP_FREEZE).then(() => { }).catch((err: BusinessError) => { hilog.error(0x0000, 'testTag', `HiAppEvent code: ${err.code}, message: ${err.message}`); }); - // [End Crash&FreezeEvent_ArkTS_Add_Event] + // [End FreezeEvent_ArkTS_Add_Event] // [Start CrashEvent_ArkTS_Add_Watcher] // 添加崩溃事件观察者 hiAppEvent.addWatcher({ @@ -231,18 +238,58 @@ export default class EntryAbility extends UIAbility { } }); // [End FreezeEvent_ArkTS_Add_Watcher] - // [Start PssleakEvent_ArkTS_Add_Watcher] - // 添加资源泄露事件观察者 + // [Start PssLeakEvent_ArkTS_Add_Watcher] + // 完成参数键值对赋值 + let pssParams: Record = { + "test_data": 100, + }; + // 设置资源泄漏事件的自定义参数 + hiAppEvent.setEventParam(pssParams, hiAppEvent.domain.OS, hiAppEvent.event.RESOURCE_OVERLIMIT).then(() => { + hilog.info(0x0000, 'testTag', `HiAppEvent success to set event param`); + }).catch((err: BusinessError) => { + hilog.error(0x0000, 'testTag', `HiAppEvent code: ${err.code}, message: ${err.message}`); + }); + // 完成自定义配置键值对赋值 + let configParams: Record = { + 'js_heap_logtype': 'event', // 仅获取事件 + } + // 设置资源泄漏事件的自定义配置 + hiAppEvent.setEventConfig(hiAppEvent.event.RESOURCE_OVERLIMIT, configParams); hiAppEvent.addWatcher({ - // 开发者可以自定义观察者名称,系统会使用名称来标识不同的观察者 - name: 'pssleakwatcher', - // 开发者可以订阅感兴趣的系统事件,此处是订阅了资源泄漏事件 + // 自定义观察者名称,系统会使用名称来标识不同的观察者 + name: 'pssLeakWatcher', + // 订阅感兴趣的系统事件,此处是订阅了资源泄漏事件 appEventFilters: [ { domain: hiAppEvent.domain.OS, names: [hiAppEvent.event.RESOURCE_OVERLIMIT] } ], + // 自行实现订阅实时回调函数,以便对订阅获取到的事件数据进行自定义处理 + onReceive: (domain: string, appEventGroups: Array) => { + hilog.info(0x0000, 'testTag', `HiAppEvent onReceive: domain=${domain}`); + for (const eventGroup of appEventGroups) { + // 根据事件集合中的事件名称区分不同的系统事件 + hilog.info(0x0000, 'testTag', `HiAppEvent eventName=${eventGroup.name}`); + for (const eventInfo of eventGroup.appEventInfos) { + // 获取到资源泄漏事件发生时内存信息 + hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo=${JSON.stringify(eventInfo)}`); + } + } + } + }); + // [End PssLeakEvent_ArkTS_Add_Watcher] + // [Start AppKillEvent_ArkTS_Add_Watcher] + hiAppEvent.addWatcher({ + // 开发者可以自定义观察者名称,系统会使用名称来标识不同的观察者 + name: 'leakWatcher', + // 开发者可以订阅感兴趣的系统事件,此处是订阅了应用终止事件 + appEventFilters: [ + { + domain: hiAppEvent.domain.OS, + names: [hiAppEvent.event.APP_KILLED] + } + ], // 开发者可以自行实现订阅实时回调函数,以便对订阅获取到的事件数据进行自定义处理 onReceive: (domain: string, appEventGroups: Array) => { hilog.info(0x0000, 'testTag', `HiAppEvent onReceive: domain=${domain}`); @@ -250,19 +297,25 @@ export default class EntryAbility extends UIAbility { // 开发者可以根据事件集合中的事件名称区分不同的系统事件 hilog.info(0x0000, 'testTag', `HiAppEvent eventName=${eventGroup.name}`); for (const eventInfo of eventGroup.appEventInfos) { - // 开发者可以获取到资源泄漏事件发生时内存信息 - hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.memory=${JSON.stringify(eventInfo)}`); + // 开发者可以对事件集合中的事件数据进行自定义处理,此处是将事件数据打印在日志中 + hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.domain=${eventInfo.domain}`); + hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.name=${eventInfo.name}`); + // 开发者可以获取到应用终止事件发生的时间戳 + hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.time=${eventInfo.params['time']}`); + // 开发者可以获取到应用的前后台状态 + hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.foreground=${eventInfo.params['foreground']}`); + // 开发者可以获取到应用终止事件发生的原因 + hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.reason=${eventInfo.params['reason']}`); } } } }); - // [End PssleakEvent_ArkTS_Add_Watcher] + // [End AppKillEvent_ArkTS_Add_Watcher] // [Start AsanEvent_ArkTS_Add_Watcher] - // 添加踩地址事件观察者 hiAppEvent.addWatcher({ // 开发者可以自定义观察者名称,系统会使用名称来标识不同的观察者 - name: 'addressSanitizerWatcher', - // 开发者可以订阅感兴趣的系统事件,此处是订阅了踩内存事件 + name: 'sanitzierWatcher', + // 开发者可以订阅感兴趣的系统事件,此处是订阅了地址越界事件 appEventFilters: [ { domain: hiAppEvent.domain.OS, @@ -286,8 +339,7 @@ export default class EntryAbility extends UIAbility { hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.pid=${eventInfo.params['pid']}`); hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.uid=${eventInfo.params['uid']}`); hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.type=${eventInfo.params['type']}`); - hilog.info(0x0000, 'testTag', - `HiAppEvent eventInfo.external_log=${JSON.stringify(eventInfo.params['external_log'])}`); + hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.external_log=${JSON.stringify(eventInfo.params['external_log'])}`); hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.log_over_limit=${eventInfo.params['log_over_limit']}`); } } @@ -351,6 +403,29 @@ export default class EntryAbility extends UIAbility { // 启动时,注册按钮点击事件观察者 testNapi.registerWatcherClick(); // [End AppEvent_Call_Capi_Function] + // [Start Register_AppHicollie_WatcherR] + // 在onCreate()函数中新增接口调用,启动时注册系统事件观察者R + testNapi.RegisterAppHicollieWatcherR(); + // [End Register_AppHicollie_WatcherR] + // [Start Register_AppHicollie_WatcherT] + // 在onCreate()函数中新增接口调用,启动时注册系统事件观察者T + testNapi.RegisterAppHicollieWatcherT(); + // [End Register_AppHicollie_WatcherT] + // [Start AsanEvent_Call_Capi_Function] + // 在onCreate()函数中新增接口调用 + // 启动时,注册系统事件观察者 + testNapi.registerSanitizerReceiveWatcher(); + // [End AsanEvent_Call_Capi_Function] + // [Start PssLeakEvent_Call_Capi_Function] + // 在onCreate()函数中新增接口调用 + // 启动时,注册系统事件观察者 + testNapi.registerLeakReceiveWatcher(); + // [End PssLeakEvent_Call_Capi_Function] + // [Start AppKillEvent_Call_Capi_Function] + // 在onCreate()函数中新增接口调用 + // 启动时,注册系统事件观察者 + testNapi.registerAppKillReceiveWatcher(); + // [End AppKillEvent_Call_Capi_Function] } onDestroy(): void { diff --git a/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/ets/pages/Index.ets b/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/ets/pages/Index.ets index 012f585f486..741da179c79 100644 --- a/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/ets/pages/Index.ets +++ b/PerformanceAnalysisKit/HiAppEvent/EventSub/entry/src/main/ets/pages/Index.ets @@ -17,9 +17,9 @@ import { BusinessError } from '@kit.BasicServicesKit'; import { hiAppEvent, hilog } from '@kit.PerformanceAnalysisKit'; // [End EventSub_Header] -// [Start PssleakEvent_Header] -import hidebug from '@ohos.hidebug'; -// [End PssleakEvent_Header] +// [Start PssleakEvent_Button] +import { hidebug } from '@kit.PerformanceAnalysisKit'; +// [End PssleakEvent_Button] // [Start EventSub_Index_Capi_Header] import testNapi from 'libentry.so'; // [End EventSub_Index_Capi_Header] @@ -27,7 +27,9 @@ import testNapi from 'libentry.so'; @Component struct Index { @State leakedArray: string[][] = []; - + // [Start AppKillEvent_Button] + @State message: string = 'Start To Leak'; + // [End AppKillEvent_Button] build() { Row() { Column() { @@ -88,8 +90,9 @@ struct Index { JSON.parse(''); }) // [End CrashEvent_Button] + // [Start PssleakEventTS_Button] // [Start PssleakEvent_Button] - Button($r('app.string.Pssleak_Button')) + Button('pss leak') .type(ButtonType.Capsule) .margin({ top: 20 @@ -104,9 +107,40 @@ struct Index { this.leakedArray.push(new Array(1).fill('leak')); } }) - // [End PssleakEvent_Button] - // [Start TimeOutEvent_Button] - Button($r('app.string.TimeOut_Button')) + // [End PssleakEvent_Button] + Button('js leak') + .type(ButtonType.Capsule) + .margin({ + top: 20 + }) + .backgroundColor('#0D9FFB') + .width('80%') + .height('5%') + .onClick(() => { + for (let i = 0; i < 10000; i++) { + this.leakedArray.push(new Array(500000).fill(1)); + } + }) + // [End PssleakEventTS_Button] + // [Start AppKillEvent_Button] + Button(this.message) + .type(ButtonType.Capsule) + .margin({ + top: 20 + }) + .backgroundColor('#0D9FFB') + .width('80%') + .height('5%') + .onClick(() => { + if (this.message != 'Leaking') { + this.message = 'Leaking'; + hilog.info(0x0000, 'testTag', 'Start leaking'); + testNapi.leak(); + } + }) + // [End AppKillEvent_Button] + // [Start TimeOutEvent_Button] + Button($r('app.string.TimeOut_Button')) .type(ButtonType.Capsule) .margin({ top: 20 @@ -154,7 +188,7 @@ struct Index { }) // [End FreezeEvent_Button] // [Start AsanEvent_Button] - Button($r('app.string.AsanEvent_Button')) + Button('address-sanitizer') .type(ButtonType.Capsule) .margin({ top: 20 @@ -163,9 +197,23 @@ struct Index { .width('80%') .height('5%') .onClick(() => { - testNapi.addressTest(); + testNapi.addressSanitizerTest(); }) // [End AsanEvent_Button] + // [Start hicollie_timer_ndk_Button] + //添加点击事件,触发TestHiCollieTimerNdk方法。 + Button('TestHiCollieTimerNdk') + .type(ButtonType.Capsule) + .margin({ + top: 20 + }) + .backgroundColor('#0D9FFB') + .width('80%') + .height('5%') + .onClick(() => { + testNapi.TestHiCollieTimerNdk(); + }) + // [End hicollie_timer_ndk_Button] } .width('50%') } -- Gitee From c2f91e7a871567178fdd3f68d85a8d4fd3ed0ffc Mon Sep 17 00:00:00 2001 From: mlkgeek Date: Mon, 27 Oct 2025 08:44:05 +0000 Subject: [PATCH 2/2] cherry sample Signed-off-by: mlkgeek --- .../screenshots/Screenshot_EventSub_Index.jpg | Bin 0 -> 53163 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 PerformanceAnalysisKit/HiAppEvent/EventSub/screenshots/Screenshot_EventSub_Index.jpg diff --git a/PerformanceAnalysisKit/HiAppEvent/EventSub/screenshots/Screenshot_EventSub_Index.jpg b/PerformanceAnalysisKit/HiAppEvent/EventSub/screenshots/Screenshot_EventSub_Index.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fc42d269ce8fc8faaff78b003810830d699218f6 GIT binary patch literal 53163 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 literal 0 HcmV?d00001 -- Gitee