订阅地址越界事件（ArkTS）
===============

#### 接口说明

API接口的具体使用说明（参数使用限制、具体取值范围等）请参考[@ohos.hiviewdfx.hiAppEvent](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-hiviewdfx-hiappevent)。

| 接口名 | 描述 |
| :-- | :-- |
| addWatcher(watcher: Watcher): AppEventPackageHolder | 添加应用事件观察者，以添加对应用事件的订阅。 |
| removeWatcher(watcher: Watcher): void | 移除应用事件观察者，以移除对应用事件的订阅。 |

#### 开发步骤

以实现对写数组越界场景生成的地址越界事件订阅为例，说明开发步骤。

#### \[h2\]步骤一：新建工程

1.  新建Native C++工程，目录结构如下：
    
    entry:
      src:
        main:
          cpp:
            - types:
                libentry:
                  - index.d.ts
            - CMakeLists.txt
            - napi\_init.cpp
          ets:
            - entryability:
                - EntryAbility.ets
            - pages:
                - Index.ets
    
2.  编辑工程中的“entry > src > main > ets > entryability > EntryAbility.ets”文件，导入依赖模块：
    
    import { hiAppEvent, hilog } from '@kit.PerformanceAnalysisKit';
    import { deviceInfo, BusinessError } from '@kit.BasicServicesKit';
    

#### \[h2\]步骤二：订阅地址越界事件

1.  编辑工程中的“entry > src > main > ets > entryability > EntryAbility.ets”文件，在onCreate函数中添加系统事件的订阅，示例代码如下
    
    if (deviceInfo.sdkApiVersion >= 24) {  // API Version 24及以后版本，支持设置页面切换日志
      // 配置页面切换日志
      let switchLogPolicy : hiAppEvent.EventPolicy = {
        "addressSanitizerPolicy": {
          "pageSwitchLogEnable": true
        }
      };
      // 开发者可以设置地址越界日志配置参数
      hiAppEvent.configEventPolicy(switchLogPolicy).then(() => {
        hilog.info(0x0000, 'testTag', \`HiAppEvent success to config event policy.\`);
      }).catch((err: BusinessError) => {
        hilog.error(0x0000, 'testTag', \`HiAppEvent code: ${err.code}, message: ${err.message}\`);
      });
    }
    
    hiAppEvent.addWatcher({
      // 开发者可以自定义观察者名称，系统会使用名称来标识不同的观察者
      name: "watcher",
      // 开发者可以订阅感兴趣的系统事件，此处是订阅了地址越界事件
      appEventFilters: \[
        {
          domain: hiAppEvent.domain.OS,
          names: \[hiAppEvent.event.ADDRESS\_SANITIZER\]
        }
      \],
      // 开发者可以自行实现订阅系统事件回调函数，以便对订阅获取到的事件数据进行自定义处理
      onReceive: (domain: string, appEventGroups: Array<hiAppEvent.AppEventGroup>) => {
        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.domain=${eventInfo.domain}\`);
            hilog.info(0x0000, 'testTag', \`HiAppEvent eventInfo.name=${eventInfo.name}\`);
            hilog.info(0x0000, 'testTag', \`HiAppEvent eventInfo.eventType=${eventInfo.eventType}\`);
            hilog.info(0x0000, 'testTag', \`HiAppEvent eventInfo.time=${eventInfo.params\['time'\]}\`);
            hilog.info(0x0000, 'testTag', \`HiAppEvent eventInfo.bundle\_version=${eventInfo.params\['bundle\_version'\]}\`);
            hilog.info(0x0000, 'testTag', \`HiAppEvent eventInfo.bundle\_name=${eventInfo.params\['bundle\_name'\]}\`);
            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.log\_over\_limit=${eventInfo.params\['log\_over\_limit'\]}\`);
            // 开发者可以获取到地址越界事件的页面切换日志
            hilog.info(0x0000, 'testTag', \`HiAppEvent eventInfo.params.page\_switch\_log=${JSON.stringify(eventInfo.params\['page\_switch\_log'\])}\`);
          }
        }
      }
    });
    

#### \[h2\]步骤三：构造地址越界错误

1.  编辑“entry > src > main > cpp > napi\_init.cpp”文件，该文件实现地址越界场景，并提供NAPI接口给应用层代码调用，完整示例代码如下：
    
    #include "napi/native\_api.h"
    
    static napi\_value Test(napi\_env env, napi\_callback\_info info)
    {
        int a\[10\];
        // 构造数组越界写入
        a\[10\] = 1;
        return {};
    }
    
    EXTERN\_C\_START
    static napi\_value Init(napi\_env env, napi\_value exports)
    {
        napi\_property\_descriptor desc\[\] = {
            { "test", nullptr, Test, nullptr, nullptr, nullptr, napi\_default, nullptr }
        };
        napi\_define\_properties(env, exports, sizeof(desc) / sizeof(desc\[0\]), desc);
        return exports;
    }
    EXTERN\_C\_END
    
    static napi\_module demoModule = {
        .nm\_version = 1,
        .nm\_flags = 0,
        .nm\_filename = nullptr,
        .nm\_register\_func = Init,
        .nm\_modname = "entry",
        .nm\_priv = ((void\*)0),
        .reserved = { 0 }
    };
    
    extern "C" \_\_attribute\_\_((constructor)) void RegisterEntryModule(void)
    {
        napi\_module\_register(&demoModule);
    }
    
2.  编辑“entry > src > main > cpp > types > libentry > index.d.ts”文件，完整示例代码如下：
    
    export const test: () => void;
    
3.  编辑工程中的“entry > src > main > ets > pages > Index.ets”文件，新增按钮触发地址越界事件：
    
    import testNapi from 'libentry.so';
    
    @Entry
    @Component
    struct Index {
      build() {
        Row() {
          Column() {
            Button("address-sanitizer").onClick(() => {
              testNapi.test();
            })
          }
          .width('100%')
        }
        .height('100%')
      }
    }
    
4.  点击DevEco Studio界面中的“entry”，点击“Edit Configurations”，点击“Diagnostics”，勾选“Address Sanitizer”，保存设置。点击DevEco Studio界面中的运行按钮，运行应用工程，然后在应用界面中点击按钮“address-sanitizer”，触发一次地址越界事件。应用崩溃后重新进入应用，可以在Log窗口看到对系统事件数据的处理日志：
    
    HiAppEvent onReceive: domain=OS
    HiAppEvent eventName=ADDRESS\_SANITIZER
    HiAppEvent eventInfo.domain=OS
    HiAppEvent eventInfo.name=ADDRESS\_SANITIZER
    HiAppEvent eventInfo.eventType=1
    HiAppEvent eventInfo.time=1713161197957
    HiAppEvent eventInfo.bundle\_version=1.0.0
    HiAppEvent eventInfo.bundle\_name=com.example.myapplication
    HiAppEvent eventInfo.pid=12889
    HiAppEvent eventInfo.uid=20020140
    HiAppEvent eventInfo.type=stack-buffer-overflow
    HiAppEvent eventInfo.external\_log=\["/data/storage/el2/log/hiappevent/ADDRESS\_SANITIZER\_1713161197960\_12889.log"\]
    HiAppEvent eventInfo.log\_over\_limit=false