社区编辑申请
注册/登录
OpenHarmony 源码解析之DFX子系统-Hiview(上)
开发 开发工具
DFX(Design for X)子系统是为了提升软件质量设计的工具集,目前包含的内容主要有:DFR(Design for Reliability,可靠性)和DFT(Design for Testability,可测试性)特性。

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

1 简介

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

已实现以下功能:

  • HiLog:流水日志。
  • HiSysEvent:系统事件记录接口。
  • HiView:插件平台。
  • FaultLoggerd:应用故障订阅和收集。
  • HiAppEvent: js应用事件记录接口。

1.1 OpenHarmony架构图

OpenHarmony 源码解析之DFX子系统-Hiview(上)-鸿蒙HarmonyOS技术社区

1.2 Hiview简介

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

Hiview架构图如下:

OpenHarmony 源码解析之DFX子系统-Hiview(上)-鸿蒙HarmonyOS技术社区

Hiview由框架和插件组成,分别为:

  • 操作系统适配层(adapter),对使用的系统服务的接口进行适配。
  • Hiview基础定义(hiview base),包括插件基类、管道的定义,事件、事件队列定义以及一些工具类。
  • Hiview的核心模块(hiview core),包括插件配置,插件管理以及事件源。
  • Hiview服务(hiview services),目前仅包括hiview运行信息dump功能。
  • Hiview插件(plugins),为独立功能的业务模块。
  • Hiview维测服务是由事件驱动的,其核心为分布在系统各处的HiSysEvent桩点。

格式化的事件通过HiSysEvent API上报至hiview进行处理,如下图:

OpenHarmony 源码解析之DFX子系统-Hiview(上)-鸿蒙HarmonyOS技术社区
  • 应用框架、系统服务使用HiSysEvent组件上报系统事件。
  • Hiview中SysEventSource获取消息,解析并组装成管道事件分发给插件处理。

1.3 Hiview代码目录

  1. /base/hiviewdfx/hiview. 
  2. ├── adapter             #平台适配 
  3. │   ├── service        #服务适配 
  4. │   └── system_service #系统接口适配 
  5. ├── base                #模块定义,工具类 
  6. │   └── utility 
  7. ├── build               #编译脚本 
  8. ├── core                #插件管理核心代码 
  9. ├── include             #公共定义 
  10. ├── plugins             #插件 
  11. ├── service             #HiviewService服务 
  12. └── utility             #工具类 

2 源码分析

本文主要分析hiview插件管理平台的初始化,和事件处理流程。

hiview是个常驻服务,在开机阶段启动。

base/hiviewdfx/hiview/service/config/hiview.cfg定义如下

  1.     "jobs" : [{ 
  2.             "name" : "post-fs-data"
  3.             "cmds" : [ 
  4.                 "mkdir /data/log/ 0770 system log"
  5.                 "mkdir /data/log/faultlog/ 0770 system system"
  6.                 "mkdir /data/log/faultlog/temp/ 0770 system system"
  7.                 "mkdir /data/log/faultlog/faultlogger/ 0770 system system"
  8.                 "start hiview" 
  9.             ] 
  10.         } 
  11.     ], 
  12.     "services" : [{ 
  13.             "name" : "hiview"
  14.             "path" : ["/system/bin/hiview"], 
  15.             "uid" : "system"
  16.             "gid" : ["system""log"], 
  17.             "writepid" : [ 
  18.                 "/dev/cpuset/system-background/tasks" 
  19.             ], 
  20.             "socket" : [ 
  21.                 "hisysevent dgram 0666 root system passcred" 
  22.             ] 
  23.         } 
  24.     ] 

2.1 初始化

hiview的入口函数定义在base/hiviewdfx/hiview/main.cpp中

  1. int main(int argc __UNUSED, char* argv[] __UNUSED) 
  2.     auto& hiview = OHOS::HiviewDFX::HiviewPlatform::GetInstance(); 
  3.     // process cmdline 
  4.     hiview.ProcessArgsRequest(argc, argv); 
  5.  
  6.     // 初始化环境,主要解析配置文件,加载插件 
  7.     if (!hiview.InitEnvironment()) { 
  8.         HIVIEW_LOGW("Fail to init plugin environment. exit"); 
  9.         return -1; 
  10.     } 
  11.  
  12.     // 启动HiviewService服务,该服务提供了hiview运行信息dump功能 
  13.     auto hiviewService = std::make_unique<OHOS::HiviewDFX::HiviewService>(); 
  14.     hiviewService->StartService(); 
  15.     return 0; 

 HiviewPlatform::InitEnvironment()函数实现如下:

  1. bool HiviewPlatform::InitEnvironment(const std::string& defaultDir, const std::string& cloudUpdateDir, 
  2.                                      const std::string& workDir, const std::string& persistDir) 
  3.     // 创建工作目录,目前目录名使用了默认的空字符串,所以并未实际创建工作目录 
  4.     ValidateAndCreateDirectories(defaultDir, cloudUpdateDir, workDir, persistDir); 
  5.  
  6.     // update beta config 
  7.     UpdateBetaConfigIfNeed(); 
  8.  
  9.     // check whether hiview is already started 
  10.     ExitHiviewIfNeed(); 
  11.  
  12.     // 解析"/system/etc/hiview/plugin_config"插件配置文件 
  13.     std::string cfgPath = GetPluginConfigPath(); 
  14.     PluginConfig config(cfgPath); 
  15.     if (!config.StartParse()) { //...........注[1] 
  16.         HIVIEW_LOGE("Fail to parse plugin config. exit!"); 
  17.         return false
  18.     } 
  19.     // 启动插件管理平台消息队列 
  20.     StartPlatformDispatchQueue(); 
  21.  
  22.     // init global context helper, remove in the future 
  23.     HiviewGlobal::CreateInstance(static_cast<HiviewContext&>(*this)); 
  24.     //加载插件 
  25.     LoadBusinessPlugin(config); 
  26.  
  27.     isReady_ = true
  28.     NotifyPluginReady(); 
  29.     return true

注[1]处PluginConfig::StartParse()函数会按照特定规则去解析插件配置文件:

  1. if (field == "plugins") { 
  2.        ParsePlugin(strTmp); 
  3.    } else if (field == "pipelines") { 
  4.        ParsePipeline(strTmp); 
  5.    } else if (field == "pipelinegroups") { 
  6.        ParsePipelineGroup(strTmp); 
  7.    } 

 目前使用的插件配置文件/system/etc/hiview/plugin_config内容如下:

  1. plugins:3 
  2. SysEventSource[thread:sysevent_source]:0 static 
  3. Faultlogger[]:0 static 
  4. SysEventService[thread:sysevent_service]:0 static 
  5. pipelines:1 
  6. SysEventPipeline:SysEventService Faultlogger 
  7. pipelinegroups:1 
  8. SysEventSource:SysEventPipeline 

 注:base/hiviewdfx/hiview/plugins 下有eventlogger eventservice faultlogger freeze_detector hicollie_collector五个插件目录,而目前插件配置文件里实际只用到了Faultlogger和SysEventService。

PluginConfig::StartParse()解析完之后会把相关信息保存到列表中。类图如下:

OpenHarmony 源码解析之DFX子系统-Hiview(上)-鸿蒙HarmonyOS技术社区

PluginConfig::ParsePipelineGroup()代码如下

  1. void PluginConfig::ParsePipelineGroup(const std::string& pipelineGroup) 
  2.     std::smatch result; 
  3.     // EventSourceExample:FromTwo2Three FromThree2Two 
  4.     if (!regex_search(pipelineGroup, result, std::regex("(\\S+)\\s*:(.+)"))) { 
  5.         HIVIEW_LOGW("Fail to match pipeline group expression."); 
  6.         return
  7.     } 
  8.  
  9.     const int pipelineGroupNameField = 1; 
  10.     const int pipelineNameListField = 2; 
  11.     std::string eventSourceName = StringUtil::TrimStr(result.str(pipelineGroupNameField)); 
  12.     auto ret = std::find_if(pluginInfoList_.begin(), pluginInfoList_.end(), [&](PluginInfo& info) { 
  13.         if (info.name == eventSourceName) { 
  14.             info.isEventSource = true
  15.             info.pipelineNameList = StringUtil::SplitStr(result.str(pipelineNameListField)); 
  16.             return true
  17.         } 
  18.         return false
  19.     }); 
  20.     if (ret != std::end(pluginInfoList_)) { 
  21.         HIVIEW_LOGD("%s is an event source.", eventSourceName.c_str()); 
  22.     } 

说明:

  • 在解析pipelinegroups时,如果发现pipelineGroupName和pluginInfoList中某个插件的name一致,则把该插件PluginInfo.isEventSource置为true并且把pipelineNameList赋值给PluginInfo.pipelineNameList。
  • 结合/system/etc/hiview/plugin_config配置文件,可以看到SysEventSource插件是带管道(SysEventPipeline)的,插件SysEventService和Faultlogger隶属于管道SysEventPipeline,用于处理SysEventSource扔给管道的事件。

解析完插件配置信息之后会调用HiviewPlatform::LoadBusinessPlugin(const PluginConfig& config)去装载插件和管道。

代码如下

  1. void HiviewPlatform::LoadBusinessPlugin(const PluginConfig& config) 
  2.     // start to load plugin 
  3.     // 1. 遍历pluginInfoList,根据插件名创建插件。因为目前配置的插件加载延时(loadDelay)为0,所以直接走[2],调用CreatePlugin()创建插件并添加到pluginMap_中。 
  4.     auto const& pluginInfoList = config.GetPluginInfoList(); 
  5.     for (auto const& pluginInfo : pluginInfoList) { 
  6.         HIVIEW_LOGI("Start to create plugin %{public}s delay:%{public}d", pluginInfo.name.c_str(), 
  7.                     pluginInfo.loadDelay); 
  8.         if (pluginInfo.loadDelay > 0) { //.............[1] 
  9.             auto task = std::bind(&HiviewPlatform::ScheduleCreateAndInitPlugin, this, pluginInfo); 
  10.             sharedWorkLoop_->AddTimerEvent(nullptr, nullptr, task, pluginInfo.loadDelay, false); 
  11.         } else {       //...............[2] 
  12.             CreatePlugin(pluginInfo); 
  13.         } 
  14.     } 
  15.     // 2. 遍历pipelineInfoList,调用CreatePipeline()创建管道并添加到pipelines_中。 
  16.     auto const& pipelineInfoList = config.GetPipelineInfoList(); 
  17.     for (auto const& pipelineInfo : pipelineInfoList) { 
  18.         HIVIEW_LOGI("Start to create pipeline %{public}s", pipelineInfo.name.c_str()); 
  19.         CreatePipeline(pipelineInfo); 
  20.     } 
  21.  
  22.     // 3. 遍历pluginInfoList,调用InitPlugin()初始化插件 
  23.     for (auto const& pluginInfo : pluginInfoList) { 
  24.         HIVIEW_LOGI("Start to Load plugin %{public}s", pluginInfo.name.c_str()); 
  25.         InitPlugin(config, pluginInfo); //............注[1] 
  26.     } 
  27.  
  28.     CleanupUnusedResources(); 

说明:

注[1]InitPlugin()这一步中,如果插件的workHandlerType为thread,则绑定工作线程EventLoop。如果插件是EventSource类型,则绑定管道Pipeline,并且调用StartEventSource开启消息监听。代码如下:

  1. void HiviewPlatform::InitPlugin(const PluginConfig& config __UNUSED, const PluginConfig::PluginInfo& pluginInfo) 
  2.     auto& plugin = pluginMap_[pluginInfo.name]; 
  3.     if (plugin == nullptr) { 
  4.         return
  5.     } 
  6.     // 如果插件的workHandlerType为thread,则绑定工作线程EventLoop。 
  7.     if (pluginInfo.workHandlerType == "thread") { 
  8.         auto workLoop = GetAvaliableWorkLoop(pluginInfo.workHandlerName); 
  9.         plugin->BindWorkLoop(workLoop); 
  10.     } 
  11.  
  12.     auto begin = TimeUtil::GenerateTimestamp(); 
  13.     plugin->OnLoad(); 
  14.     // 如果插件是EventSource类型,则添加管道Pipeline,并且调用StartEventSource开启消息监听 
  15.     if (pluginInfo.isEventSource) { 
  16.         auto sharedSource = std::static_pointer_cast<EventSource>(plugin); 
  17.         if (sharedSource == nullptr) { 
  18.             HIVIEW_LOGE("Fail to cast plugin to event source!"); 
  19.             return
  20.         } 
  21.         for (auto& pipelineName : pluginInfo.pipelineNameList) { 
  22.             sharedSource->AddPipeline(pipelines_[pipelineName]); 
  23.         } 
  24.         StartEventSource(sharedSource); 
  25.     } 
  26.     auto end = TimeUtil::GenerateTimestamp(); 
  27.     HIVIEW_LOGI("Plugin %{public}s loadtime:%{public}" PRIu64 ".", pluginInfo.name.c_str(), end - begin); 

至此,插件管理平台初始化工作已完成。

2.2 事件处理流程

结合插件配置文件,目前实际使用的插件类图如下:

OpenHarmony 源码解析之DFX子系统-Hiview(上)-鸿蒙HarmonyOS技术社区

说明:

  • 前文最后的代码段提到,如果插件是EventSource类型,则绑定管道,并且调用StartEventSource开启消息监听。结合类图,只有SysEventSource是EventSource类型的插件,所以只有SysEventSource持有管道。
  • EventSource类型的插件会监听HiSysEvent接口发来的消息,解析并组装成管道事件,分发给管道中的插件处理。

SysEventSource::StartEventSource()函数实现如下。

  1. void SysEventSource::StartEventSource() 
  2.     HIVIEW_LOGI("SysEventSource start"); 
  3.     std::shared_ptr<EventReceiver> sysEventReceiver = std::make_shared<SysEventReceiver>(*this); 
  4.     eventServer.AddReceiver(sysEventReceiver); 
  5.  
  6.     eventServer.Start(); 

SysEventSource有个成员变量eventServer,EventServer会开启socketserver端用于接收HiSysEvent接口发来的socket消息。

类图如下:

OpenHarmony 源码解析之DFX子系统-Hiview(上)-鸿蒙HarmonyOS技术社区
OpenHarmony 源码解析之DFX子系统-Hiview(上)-鸿蒙HarmonyOS技术社区

说明:

1.SysEventSource::StartEventSource()中把SysEventReceiver对象加入到EventServer的receivers_容器中。

2.EventServer收到socket消息之后,调用SysEventReceiver::HandlerEvent(const std::string& rawMsg)方法处理接收到的消息。

3.SysEventReceiver::HandlerEvent()方法中会调用SysEventParser::Parser(const std::string& rawMsg)方法解析消息并组装成SysEvent对象,然后调用EventSource::PublishPipelineEvent(std::shared_ptr event)方法把管道事件发布出去。

EventSource::PublishPipelineEvent()代码如下:

  1. bool EventSource::PublishPipelineEvent(std::shared_ptr<PipelineEvent> event) 
  2.     HIVIEW_LOGD("EventSource PublishPipelineEvent"); 
  3.     if (event == nullptr) { 
  4.         return false
  5.     } 
  6.  
  7.     if (Audit::IsEnabled()) { 
  8.         auto digest = GetPluginInfo() + Audit::DOMAIN_DELIMITER + event->GetEventInfo(); 
  9.         Audit::WriteAuditEvent(Audit::StatsEvent::PIPELINE_EVENT_CREATE, event->createTime_, digest); 
  10.     } 
  11.  
  12.     for (auto& pipeline : listeners_) { 
  13.         if ((pipeline != nullptr) && (pipeline->CanProcessEvent(event))) { 
  14.             // one event can only be processed by one pipeline 
  15.             pipeline->ProcessEvent(event); 
  16.             return true
  17.         } 
  18.     } 
  19.     return false

结合上面的类图,EventSource持有管道列表对象listeners_,EventSource::PublishPipelineEvent()函数中会遍历该列表,调用Pipeline::ProcessEvent(std::shared_ptr event)函数去处理管道事件。(Pipeline对象是在HiviewPlatform::InitPlugin()函数中被加入到EventSource.listeners_中的。)

Pipeline类型有个成员变量std::list

在插件平台初始化的时候,HiviewPlatform::CreatePipeline()函数中已经把Plugin插件对象加入到了processors_列表中。

接下来分析Pipeline::ProcessEvent()函数做了什么。

  1. void Pipeline::ProcessEvent(std::shared_ptr<PipelineEvent> event) 
  2.     event->SetPipelineInfo(name_, processors_); 
  3.     event->OnContinue(); 

 把Pipeline的processors_对象赋给了PipelineEvent的processors_对象,然后调用

PipelineEvent::OnContinue()函数。

接下来分析PipelineEvent::OnContinue()函数:

  1. bool PipelineEvent::OnContinue() 
  2.     // 判断hasFinish_标志位和processors_列表是否已经为空,如已为空,结束本次事件传递 
  3.     if ((!hasFinish_) && processors_.empty()) { 
  4.         return OnFinish(); 
  5.     } 
  6.  
  7.     // once the event start delivering 
  8.     // the call OnContinue means one has done the processing of the event 
  9.     // this may be called by upstream event processor or the framework 
  10.     if (Audit::IsEnabled() && startDeliver_) { 
  11.         Audit::WriteAuditEvent(Audit::StatsEvent::PIPELINE_EVENT_HANDLE_OUT, 
  12.             createTime_, std::to_string(Thread::GetTid())); 
  13.     } 
  14.  
  15.     // the framework will call OnContinue when the event is assigned to a pipeline 
  16.     if (!startDeliver_) { 
  17.         startDeliver_ = true
  18.     } 
  19.  
  20.     // 取出processors_列表中第一个Plugin元素,并从列表弹出 
  21.     std::weak_ptr<Plugin> plugin = processors_.front(); 
  22.     processors_.pop_front(); 
  23.     if (auto pluginPtr = plugin.lock()) { 
  24.         if (!pluginPtr->CanProcessMoreEvents()) { 
  25.             handler_->PauseDispatch(plugin); 
  26.         } 
  27.  
  28.         if (Audit::IsEnabled()) { 
  29.             Audit::WriteAuditEvent(Audit::StatsEvent::PIPELINE_EVENT_HANDLE_IN, createTime_, 
  30.                                    pluginPtr->GetHandlerInfo()); 
  31.         } 
  32.         // 判断当前Plugin是否开启了事件工作队列,如有则加入事件队列处理,如没有直接调用OnEventProxy 
  33.         if (auto workLoop = pluginPtr->GetWorkLoop()) { 
  34.             workLoop->AddEvent(pluginPtr, shared_from_this()); //............[1] 
  35.         } else { 
  36.             pluginPtr->OnEventProxy(shared_from_this()); //..........[2] 
  37.         } 
  38.     } else { 
  39.         return OnContinue(); 
  40.     } 
  41.     return true

不管插件是否使用了事件队列,最终都会调用到Plugin::OnEventProxy()函数来处理管道事件。

Plugin::OnEventProxy()函数实现如下:

  1. bool Plugin::OnEventProxy(std::shared_ptr<Event> event) 
  2.     if (event == nullptr) { 
  3.  
  4.         return false
  5.     } 
  6.  
  7.     std::shared_ptr<Event> dupEvent = event; 
  8.     auto processorSize = dupEvent->GetPendingProcessorSize(); 
  9.     dupEvent->ResetPendingStatus(); 
  10.     bool ret = OnEvent(dupEvent); //..............注[1] 
  11.  
  12.     if (!dupEvent->IsPipelineEvent()) { 
  13.         if (Audit::IsEnabled()) { 
  14.             Audit::WriteAuditEvent(Audit::StatsEvent::QUEUE_EVENT_OUT, dupEvent->createTime_, 
  15.                 std::to_string(Thread::GetTid())); 
  16.         } 
  17.     } else { 
  18.         if ((!dupEvent->HasFinish() && !dupEvent->HasPending()) && 
  19.             (processorSize == dupEvent->GetPendingProcessorSize())) { 
  20.             dupEvent->OnContinue();//.............注[2] 
  21.         } 
  22.     } 
  23.     return ret; 

先调用注[1]OnEvent()函数处理事件,再判断管道事件传递是否已结束,如未结束则调用注[2]PipelineEvent::OnContinue()函数继续把管道事件传递给后面的

插件处理。

结合目前的插件配置文件,整个插件管理平台的消息处理流程大致如下:

EventServer接收到HiSysEvent组件接口发来的消息 --> SysEventReceiver::HandlerEvent() --> SysEventParser::Parser() --> EventSource::PublishPipelineEvent() --> Pipeline::ProcessEvent() --> PipelineEvent::OnContinue() --> SysEventService::OnEventProxy --> SysEventService::OnEvent --> PipelineEvent::OnContinue() --> Faultlogger::OnEventProxy --> Faultlogger::OnEvent --> 结束

至此,事件处理流程已分析完。

3 总结

以上内容首先分析了hiview插件管理平台的初始化,然后对事件的处理流程做了分析。后续会详细讲解base/hiviewdfx/hiview/plugins目录下每个插件的源码。

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区

同话题下的热门内容

使用VScode的几点感受,对比Pycharm、Jupyter优劣势用 Antlr 重构脚本解释器GitHub 添加工具以简化软件开发管理

编辑推荐

终于有人把Elasticsearch原理讲透了!花了一个星期,我终于把RPC框架整明白了!这可能是把ZooKeeper概念讲的最清楚的一篇文章论如何下载一个在线的m3u8文件到本地成为一个mp4!拜托!面试不要再问我Spring Cloud底层原理
我收藏的内容
点赞
收藏

51CTO技术栈公众号