暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

OpenHarmony源码解析:DFX子系统

鸿蒙技术社区 2022-01-12
1870

DFX(Design for X)子系统是为了提升软件质量设计的工具集,目前包含的内容主要有:DFR(Design for Reliability,可靠性)和 DFT(Design for Testability,可测试性)特性。


目前标准系统已实现以下功能:

  • HiLog:流水日志。

  • HiSysEvent:系统事件记录接口。

  • HiView:插件平台。

  • FaultLoggerd:应用故障订阅和收集。

  • HiAppEvent:js 应用事件记录接口。


OpenHarmony 架构图如下:

注:本文只介绍 DFX 各组件的使用,后续会有文章单独分析各组件的源码。


Hilog


HiLog 是日志系统,提供给系统框架、服务、以及应用打印日志,记录用户操作、系统运行状态等。


用户态 Process 通过日志接口将日志内容写入 hilogd buffer 中,用户态的 hilog 工具支持将日志输出到控制台(console)进行查看。


同时也支持通过 hilog 工具给 hilogd 发送命令将日志落盘,设置指定 <type> 日志类型缓冲区的大小等。

HiLog 架构图如下:

注:目前代码暂未看到有支持读取 kernel 日志。

代码结构:

/base/hiviewdfx/hilog
├── frameworks           # 框架代码
│   └── native          # HiLog native实现代码
├── interfaces           # 接口
│   └── native          # 对外C/C++接口
│       └── innerkits   # 对内部子系统暴露的头文件
│       └── kits        # 对应用暴露的头文件
├── services
│   └── hilogd          # 日志常驻服务实现
│   └── hilogtool       # 日志工具实现


从使用者的角度,只需要关心 hilog 日志接口和 hilog 命令行工具的使用方法。


①hilog 接口使用说明


主要 API 说明,如下图:

使用方法:

  • 在模块 BUILD.gn 文件中添加依赖

external_deps = [ "hilog_native:libhilog" ]
  • include 头文件"hilog/log.h"

  • 接口调用


代码示例(以下代码从系统源码中摘录):

#include <vector>
+#include "hilog/log.h"
#include "string_ex.h"
#include "uri.h"

using std::string;
using std::regex;
+using OHOS::HiviewDFX::HiLog;

namespace OHOS {
namespace {
@@ -39,6 +41,7 @@ namespace {
    const size_t POS_INC_MORE = 2;
    const size_t POS_INC_AGAIN = 3;
    const regex SCHEME_REGEX("[a-zA-Z][a-zA-Z|\\d|+|-|.]*$");
+    const HiviewDFX::HiLogLabel LABEL = {LOG_CORE, 0xD001800"URI"};
}; // namespace

Uri::Uri(const string& uriString)
@@ -48,6 +51,7 @@ Uri::Uri(const string& uriString)
    port_ = NOT_CALCULATED;

    if (uriString.empty()) {
+        HiLog::Error(LABEL, "Input empty!");
        return;
    }


②hilog 命令行工具使用说明


如下表:

注:落盘日志文件保存路径为"/data/log/hilog/"。


HiSysEvent


Hisysevent 组件定义了 HiSysEvent 埋点接口供应用框架、系统服务使用,用于向 hiview 上报系统事件信息。


通过在关键路径埋点记录系统在运行过程中的重要信息,辅助开发者定位问题。

①接口说明:

/**
     * @brief 写系统事件
     * @param domain    事件的domain
     * @param eventName 事件名
     * @param type      事件类型
     * @param keyValues 可变参数,键值对
     * @return 0 成功,其他失败
     */

    template<typename... Types> static int Write(const std::string &domain, const std::string &eventName,
        EventType type, Types... keyValues)


枚举类型 EventType 定义了事件类型:

enum EventType {
        FAULT     = 1,    // system fault event
        STATISTIC = 2,    // system statistic event
        SECURITY  = 3,    // system security event
        BEHAVIOR  = 4     // system behavior event
    };


HiSysEvent 内部类 Domain 中定义了一些字符串常量,用于表示不同的 domain。


②接口使用


在 BUILD.gn 中增加依赖:
external_deps = [ "hisysevent_native:libhisysevent" ]


在类定义头文件或者类实现源文件中,包含 HiSysEvent 头文件:
#include "hisysevent.h"


示例:(以下代码摘自源码)
void EventReport::SendEvent(const EventInfo& eventInfo)
{
    auto packageName = AceApplicationInfo::GetInstance().GetPackageName();
    if (packageName.size() > MAX_PACKAGE_NAME_LENGTH) {
        StrTrim(packageName);
    }
    OHOS::HiviewDFX::HiSysEvent::Write(OHOS::HiviewDFX::HiSysEvent::Domain::ACE, eventInfo.eventType,
        OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
        EVENT_KEY_ERROR_TYPE, eventInfo.errorType,
        EVENT_KEY_PACKAGE_NAME, packageName);
}


hiview 在收到消息后会打印日志,并把事件保存到 data/log/LogService/sys_event_db/hisysevent.db 数据库中。

日志如下:

行 10430: 12-06 15:41:03.176   369   537 D 02d10/HiView-EventServer: Start: receive data from client <private>
行 1043112-06 15:41:03.176   369   537 D 02d10/HiView-SysEventSource: Parser: parser raw message size=301, {"domain_":"ACE","name_":"JS_ERROR","type_":1,"time_":1638805263175,"pid_":821,"tid_":839,"PACKAGE_NAME":"","REASON":"Js Crash","SUMMARY":"Lifetime: 0.000000s
行 10432: 12-06 15:41:03.176   369   537 D 02d10/HiView-SysEventSource: Js-Engine: Quick JS
行 10433: 12-06 15:41:03.176   369   537 D 02d10/HiView-SysEventSource: Stacktrace: TypeError: cannot read property 'getAppPageStartConfig' of undefined
行 10434: 12-06 15:41:03.176   369   537 D 02d10/HiView-SysEventSource:     at onPageShow (pages/EntryView.js)
行 10435: 12-06 15:41:03.176   369   537 D 02d10/HiView-SysEventSource:     at onPageShow (pages/EntryView.js)
行 10472: 12-06 15:41:03.180   369   537 I 02d10/HiView-SysEventSource: Parser: parser result domain_=ACE eventName_=JS_ERROR
行 10473: 12-06 15:41:03.180   369   537 D 02d10/HiView-EventSource: PublishPipelineEvent: EventSource PublishPipelineEvent
行 10474: 12-06 15:41:03.180   369   547 I 02d10/HiView-SysEventService: Convert2SysEvent: domain is ACE, eventName is JS_ERROR.
行 10485: 12-06 15:41:03.180   369   547 D 02d10/HiView-SysEventDao: Insert: insert db file /data/log/LogService/sys_event_db/hisysevent.db with JS_ERROR
行 10837: 12-06 15:41:03.232   369   547 I 02d10/HiView-DOCDB: open ejdb success
行 10838: 12-06 15:41:03.232   369   547 I 02d10/HiView-DOCDB: open doc store
行 11141: 12-06 15:41:03.249   369   547 D 02d10/HiView-DOCDB: put data to doc store success
行 11142: 12-06 15:41:03.250   369   547 D 02d10/HiView-SysEventDbMgr: SaveToStore: save sys event 1, JS_ERROR
行 11227: 12-06 15:41:03.253   369   547 I 02d10/Faultlogger: AddFaultLogIfNeed: Invalid module name
行 11229: 12-06 15:41:03.253   369   547 I 02d10/HiView-SysEventSource: Recycle: recycle resource


HiView


Hiview 是一个跨平台的终端设备维测服务集。目前开源部分仅包含插件管理平台和系统事件源。

架构图如下:

Hiview 由框架和插件组成,主要包含以下几部分:
  • 操作系统适配层(adapter),对使用的系统服务的接口进行适配。

  • Hiview 基础定义(hiview base),包括插件基类、管道的定义,事件、事件队列定义以及一些工具类。

  • Hiview 的核心模块(hiview core),包括插件配置,插件管理以及事件源。

  • Hiview 服务(hiview services),目前仅包括 hiview 运行信息 dump 功能。

  • Hiview 插件(plugins),为独立功能的业务模块。


Hiview 维测服务是由事件驱动的,其核心为分布在系统各处的 HiSysEvent 桩点。


格式化的事件通过 HiSysEvent API 上报至 hiview 进行处理,请参考上面的 HiSysEvent 的架构图。


应用框架、系统服务使用 HiSysEvent 组件上报系统事件。Hiview 中 SysEventSource 获取消息,解析并组装成管道事件分发给插件处理。


注:hiview 目前并未对外提供接口。


FaultLoggerd


faultloggerd 是 OpenHarmony 中的 C/C++ 运行时崩溃临时日志的生成及管理模块。


主要流程如下:
  • 进程 A 调用接口订阅故障收集功能。

  • 进程 A 的异常信号处理器检测到异常信号后 Fork 出子进程运行 processdump 程序。

  • processdump 程序 Ptrace 到父进程上,读取异常线程相关信息,包括寄存器以及调用栈。

  • processdump 程序在读取异常信息后将其写入到 data/log/faultlog/temp 目录下中做临时存储。


接口使用方法:在模块的 BUILD.gn 文件中添加依赖,包含"dfx_signal_handler.h"头文件,调用 DFX_InstallSignalHandler() 方法订阅故障收集功能。

deps = ["//base/hiviewdfx/faultloggerd/interfaces/innerkits/signal_handler:dfx_signalhandler"]


示例:(以下代码摘自源码)


base/telephony/ril_adapter/hril_hdf/hril_hdf.c 中:

 #include "hril_hdf.h"
 #include <pthread.h>
 #include "dfx_signal_handler.h" //................[1]头文件
 #include "telephony_log_c.h"

 static int32_t RilAdapterInit(struct HdfDeviceObject *device)
 
{
     if (device == NULL) {
         return HDF_ERR_INVALID_OBJECT;
     }
     DFX_InstallSignalHandler(); //................[2]订阅故障收集功能
     struct HdfSBuf *sbuf = HdfSBufTypedObtain(SBUF_IPC);
     if (sbuf == NULL) {
         TELEPHONY_LOGE("HdfSampleDriverBind, failed to obtain ipc sbuf");
         return HDF_ERR_INVALID_OBJECT;
     }
     if (!HdfSbufWriteString(sbuf, "string")) {
         TELEPHONY_LOGE("HdfSampleDriverBind, failed to write string to ipc sbuf");
         HdfSBufRecycle(sbuf);
         return HDF_FAILURE;
     }
     if (sbuf != NULL) {
         HdfSBufRecycle(sbuf);
     }
     TELEPHONY_LOGD("sbuf IPC obtain test success!");
     LoadVendor();
     return HDF_SUCCESS;
 }


注:程序崩溃后会在 data/log/faultlog/temp 路径下生成临时文件。系统开发者可以通过日志定位崩溃问题。


HiAppEvent


HiAppEvent 为 JS 应用提供事件打点接口,用于帮助应用记录在运行过程中发生的故障信息、统计信息、安全信息、用户行为信息,以支撑开发者分析应用的运行情况。

①接口说明


js 接口定义文件:

interface/sdk-js/api/phone/@ohos.hiAppEvent.d.ts


打点接口:

  • JS 事件类型枚举——EventType

  • function write(eventName:string,eventType:EventType, keyValues:object):Promise<void>;应用事件异步打点方法,使用 promise 方式作为异步回调。

  • function write(string eventName, EventType type,object keyValues,AsyncCallback<void> callback): void 应用事件异步打点方法,使用 callback 方式作为异步回调。

| 类型 | 描述|
|
 ----- | ----- |
| FAULT | 故障类型事件 |
|
 STATISTIC | 统计类型事件 |
| SECURITY | 安全类型事件 |
|
 BEHAVIOR | 行为类型事件 |


输入参数说明:
  • eventName:事件名称。

  • eventType:事件类型。

  • keyValues:事件参数键值对,为 Json 对象类型。

  • callback:回调函数,可以在回调函数中处理接口返回值。返回值为 0 表示事件参数校验成功,事件正常异步写入事件文件;大于 0 表示事件存在异常参数,事件在忽略异常参数后再异步写入事件文件;小于 0 表示事件校验失败,不执行事件异步打点操作。


打点配置接口:

  • function configure(config: ConfigOption):boolean;应用事件打点配置方法,可以对打点功能进行自定义配置。

  • 参数 config:应用事件打点配置项。

  • 返回值:boolean,true 表示配置成功,false 表示配置失败。

  • ConfigOption 应用打点配置选项。

| 配置名 | 类型|必填 |说明|
|
 ----- | ----- |----- | ----- |
| disable | boolean |否|应用打点功能开关,true表示关闭打点功能,false表示不关闭打点功能|
|
 maxStorage | string ||打点数据本地存储文件所在目录的配额大小,默认限额为“10M”。所在目录大小超出限额后会对目录进行清理操作,会按从旧到新的顺序逐个删除打点数据文件,直到目录大小不超出限额时停止。|


②接口使用


引入模块:

import hiAppEvent from ‘@ohos.hiAppEvent’


应用事件打点:


callback 方式:
hiAppEvent.write("testevent", hiAppEvent.EventType.BEHAVIOR, {"key":"value"},
          (err, value) => {
              console.log(`HiAppEvent testevent callback`);
              if (err) {
                  // 事件写入异常:事件存在异常参数或者事件校验失败不执行写入
                  console.error(`HiAppEvent json-callback-error code=${err.code}`);
              } else {
                  console.log(`HiAppEvent json-callback-success value=${value}`)
              }
          });


Promise 方式:

hiAppEvent.write("test_event", hiAppEvent.EventType.FAULT, {"int_data":100"str_data":"strValue"})
  .then((value) => {
      // 事件写入正常
      console.log(`success to write event: ${value}`);
  }).catch((err) => {
      // 事件写入异常:事件存在异常参数或者事件校验失败不执行写入
      console.error(`failed to write event because ${err.code}`);
  });


总结


本文对标准系统目前已支持的 DFX 功能模块进行了介绍。demo 请下载附件:

https://harmonyos.51cto.com/posts/9922


作者:吴文璐

👇扫码报名今晚的鸿蒙直播课👇

👇点击关注鸿蒙技术社区👇
了解鸿蒙一手资讯

求分享

求点赞

求在看

文章转载自鸿蒙技术社区,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论