OpenHarmony 源碼解析之DFX子系統(tǒng)-Hiview(上)
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
1 簡(jiǎn)介
DFX(Design for X)子系統(tǒng)是為了提升軟件質(zhì)量設(shè)計(jì)的工具集,目前包含的內(nèi)容主要有:DFR(Design for Reliability,可靠性)和DFT(Design for Testability,可測(cè)試性)特性。
已實(shí)現(xiàn)以下功能:
- HiLog:流水日志。
- HiSysEvent:系統(tǒng)事件記錄接口。
- HiView:插件平臺(tái)。
- FaultLoggerd:應(yīng)用故障訂閱和收集。
- HiAppEvent: js應(yīng)用事件記錄接口。
1.1 OpenHarmony架構(gòu)圖

1.2 Hiview簡(jiǎn)介
Hiview是一個(gè)跨平臺(tái)的終端設(shè)備維測(cè)服務(wù)集。目前開源部分僅包含插件管理平臺(tái)和系統(tǒng)事件源。
Hiview架構(gòu)圖如下:

Hiview由框架和插件組成,分別為:
- 操作系統(tǒng)適配層(adapter),對(duì)使用的系統(tǒng)服務(wù)的接口進(jìn)行適配。
- Hiview基礎(chǔ)定義(hiview base),包括插件基類、管道的定義,事件、事件隊(duì)列定義以及一些工具類。
- Hiview的核心模塊(hiview core),包括插件配置,插件管理以及事件源。
- Hiview服務(wù)(hiview services),目前僅包括hiview運(yùn)行信息dump功能。
- Hiview插件(plugins),為獨(dú)立功能的業(yè)務(wù)模塊。
- Hiview維測(cè)服務(wù)是由事件驅(qū)動(dòng)的,其核心為分布在系統(tǒng)各處的HiSysEvent樁點(diǎn)。
格式化的事件通過HiSysEvent API上報(bào)至hiview進(jìn)行處理,如下圖:

- 應(yīng)用框架、系統(tǒng)服務(wù)使用HiSysEvent組件上報(bào)系統(tǒng)事件。
- Hiview中SysEventSource獲取消息,解析并組裝成管道事件分發(fā)給插件處理。
1.3 Hiview代碼目錄
- /base/hiviewdfx/hiview.
- ├── adapter #平臺(tái)適配
- │ ├── service #服務(wù)適配
- │ └── system_service #系統(tǒng)接口適配
- ├── base #模塊定義,工具類
- │ └── utility
- ├── build #編譯腳本
- ├── core #插件管理核心代碼
- ├── include #公共定義
- ├── plugins #插件
- ├── service #HiviewService服務(wù)
- └── utility #工具類
2 源碼分析
本文主要分析hiview插件管理平臺(tái)的初始化,和事件處理流程。
hiview是個(gè)常駐服務(wù),在開機(jī)階段啟動(dòng)。
base/hiviewdfx/hiview/service/config/hiview.cfg定義如下
- {
- "jobs" : [{
- "name" : "post-fs-data",
- "cmds" : [
- "mkdir /data/log/ 0770 system log",
- "mkdir /data/log/faultlog/ 0770 system system",
- "mkdir /data/log/faultlog/temp/ 0770 system system",
- "mkdir /data/log/faultlog/faultlogger/ 0770 system system",
- "start hiview"
- ]
- }
- ],
- "services" : [{
- "name" : "hiview",
- "path" : ["/system/bin/hiview"],
- "uid" : "system",
- "gid" : ["system", "log"],
- "writepid" : [
- "/dev/cpuset/system-background/tasks"
- ],
- "socket" : [
- "hisysevent dgram 0666 root system passcred"
- ]
- }
- ]
- }
2.1 初始化
hiview的入口函數(shù)定義在base/hiviewdfx/hiview/main.cpp中
- int main(int argc __UNUSED, char* argv[] __UNUSED)
- {
- auto& hiview = OHOS::HiviewDFX::HiviewPlatform::GetInstance();
- // process cmdline
- hiview.ProcessArgsRequest(argc, argv);
- // 初始化環(huán)境,主要解析配置文件,加載插件
- if (!hiview.InitEnvironment()) {
- HIVIEW_LOGW("Fail to init plugin environment. exit");
- return -1;
- }
- // 啟動(dòng)HiviewService服務(wù),該服務(wù)提供了hiview運(yùn)行信息dump功能
- auto hiviewService = std::make_unique<OHOS::HiviewDFX::HiviewService>();
- hiviewService->StartService();
- return 0;
- }
HiviewPlatform::InitEnvironment()函數(shù)實(shí)現(xiàn)如下:
- bool HiviewPlatform::InitEnvironment(const std::string& defaultDir, const std::string& cloudUpdateDir,
- const std::string& workDir, const std::string& persistDir)
- {
- // 創(chuàng)建工作目錄,目前目錄名使用了默認(rèn)的空字符串,所以并未實(shí)際創(chuàng)建工作目錄
- ValidateAndCreateDirectories(defaultDir, cloudUpdateDir, workDir, persistDir);
- // update beta config
- UpdateBetaConfigIfNeed();
- // check whether hiview is already started
- ExitHiviewIfNeed();
- // 解析"/system/etc/hiview/plugin_config"插件配置文件
- std::string cfgPath = GetPluginConfigPath();
- PluginConfig config(cfgPath);
- if (!config.StartParse()) { //...........注[1]
- HIVIEW_LOGE("Fail to parse plugin config. exit!");
- return false;
- }
- // 啟動(dòng)插件管理平臺(tái)消息隊(duì)列
- StartPlatformDispatchQueue();
- // init global context helper, remove in the future
- HiviewGlobal::CreateInstance(static_cast<HiviewContext&>(*this));
- //加載插件
- LoadBusinessPlugin(config);
- isReady_ = true;
- NotifyPluginReady();
- return true;
- }
注[1]處PluginConfig::StartParse()函數(shù)會(huì)按照特定規(guī)則去解析插件配置文件:
- if (field == "plugins") {
- ParsePlugin(strTmp);
- } else if (field == "pipelines") {
- ParsePipeline(strTmp);
- } else if (field == "pipelinegroups") {
- ParsePipelineGroup(strTmp);
- }
目前使用的插件配置文件/system/etc/hiview/plugin_config內(nèi)容如下:
- plugins:3
- SysEventSource[thread:sysevent_source]:0 static
- Faultlogger[]:0 static
- SysEventService[thread:sysevent_service]:0 static
- pipelines:1
- SysEventPipeline:SysEventService Faultlogger
- pipelinegroups:1
- SysEventSource:SysEventPipeline
注:base/hiviewdfx/hiview/plugins 下有eventlogger eventservice faultlogger freeze_detector hicollie_collector五個(gè)插件目錄,而目前插件配置文件里實(shí)際只用到了Faultlogger和SysEventService。
PluginConfig::StartParse()解析完之后會(huì)把相關(guān)信息保存到列表中。類圖如下:

PluginConfig::ParsePipelineGroup()代碼如下
- void PluginConfig::ParsePipelineGroup(const std::string& pipelineGroup)
- {
- std::smatch result;
- // EventSourceExample:FromTwo2Three FromThree2Two
- if (!regex_search(pipelineGroup, result, std::regex("(\\S+)\\s*:(.+)"))) {
- HIVIEW_LOGW("Fail to match pipeline group expression.");
- return;
- }
- const int pipelineGroupNameField = 1;
- const int pipelineNameListField = 2;
- std::string eventSourceName = StringUtil::TrimStr(result.str(pipelineGroupNameField));
- auto ret = std::find_if(pluginInfoList_.begin(), pluginInfoList_.end(), [&](PluginInfo& info) {
- if (info.name == eventSourceName) {
- info.isEventSource = true;
- info.pipelineNameList = StringUtil::SplitStr(result.str(pipelineNameListField));
- return true;
- }
- return false;
- });
- if (ret != std::end(pluginInfoList_)) {
- HIVIEW_LOGD("%s is an event source.", eventSourceName.c_str());
- }
- }
說明:
- 在解析pipelinegroups時(shí),如果發(fā)現(xiàn)pipelineGroupName和pluginInfoList中某個(gè)插件的name一致,則把該插件PluginInfo.isEventSource置為true并且把pipelineNameList賦值給PluginInfo.pipelineNameList。
- 結(jié)合/system/etc/hiview/plugin_config配置文件,可以看到SysEventSource插件是帶管道(SysEventPipeline)的,插件SysEventService和Faultlogger隸屬于管道SysEventPipeline,用于處理SysEventSource扔給管道的事件。
解析完插件配置信息之后會(huì)調(diào)用HiviewPlatform::LoadBusinessPlugin(const PluginConfig& config)去裝載插件和管道。
代碼如下
- void HiviewPlatform::LoadBusinessPlugin(const PluginConfig& config)
- {
- // start to load plugin
- // 1. 遍歷pluginInfoList,根據(jù)插件名創(chuàng)建插件。因?yàn)槟壳芭渲玫牟寮虞d延時(shí)(loadDelay)為0,所以直接走[2],調(diào)用CreatePlugin()創(chuàng)建插件并添加到pluginMap_中。
- auto const& pluginInfoList = config.GetPluginInfoList();
- for (auto const& pluginInfo : pluginInfoList) {
- HIVIEW_LOGI("Start to create plugin %{public}s delay:%{public}d", pluginInfo.name.c_str(),
- pluginInfo.loadDelay);
- if (pluginInfo.loadDelay > 0) { //.............[1]
- auto task = std::bind(&HiviewPlatform::ScheduleCreateAndInitPlugin, this, pluginInfo);
- sharedWorkLoop_->AddTimerEvent(nullptr, nullptr, task, pluginInfo.loadDelay, false);
- } else { //...............[2]
- CreatePlugin(pluginInfo);
- }
- }
- // 2. 遍歷pipelineInfoList,調(diào)用CreatePipeline()創(chuàng)建管道并添加到pipelines_中。
- auto const& pipelineInfoList = config.GetPipelineInfoList();
- for (auto const& pipelineInfo : pipelineInfoList) {
- HIVIEW_LOGI("Start to create pipeline %{public}s", pipelineInfo.name.c_str());
- CreatePipeline(pipelineInfo);
- }
- // 3. 遍歷pluginInfoList,調(diào)用InitPlugin()初始化插件
- for (auto const& pluginInfo : pluginInfoList) {
- HIVIEW_LOGI("Start to Load plugin %{public}s", pluginInfo.name.c_str());
- InitPlugin(config, pluginInfo); //............注[1]
- }
- CleanupUnusedResources();
- }
說明:
注[1]InitPlugin()這一步中,如果插件的workHandlerType為thread,則綁定工作線程EventLoop。如果插件是EventSource類型,則綁定管道Pipeline,并且調(diào)用StartEventSource開啟消息監(jiān)聽。代碼如下:
- void HiviewPlatform::InitPlugin(const PluginConfig& config __UNUSED, const PluginConfig::PluginInfo& pluginInfo)
- {
- auto& plugin = pluginMap_[pluginInfo.name];
- if (plugin == nullptr) {
- return;
- }
- // 如果插件的workHandlerType為thread,則綁定工作線程EventLoop。
- if (pluginInfo.workHandlerType == "thread") {
- auto workLoop = GetAvaliableWorkLoop(pluginInfo.workHandlerName);
- plugin->BindWorkLoop(workLoop);
- }
- auto begin = TimeUtil::GenerateTimestamp();
- plugin->OnLoad();
- // 如果插件是EventSource類型,則添加管道Pipeline,并且調(diào)用StartEventSource開啟消息監(jiān)聽
- if (pluginInfo.isEventSource) {
- auto sharedSource = std::static_pointer_cast<EventSource>(plugin);
- if (sharedSource == nullptr) {
- HIVIEW_LOGE("Fail to cast plugin to event source!");
- return;
- }
- for (auto& pipelineName : pluginInfo.pipelineNameList) {
- sharedSource->AddPipeline(pipelines_[pipelineName]);
- }
- StartEventSource(sharedSource);
- }
- auto end = TimeUtil::GenerateTimestamp();
- HIVIEW_LOGI("Plugin %{public}s loadtime:%{public}" PRIu64 ".", pluginInfo.name.c_str(), end - begin);
- }
至此,插件管理平臺(tái)初始化工作已完成。
2.2 事件處理流程
結(jié)合插件配置文件,目前實(shí)際使用的插件類圖如下:

說明:
- 前文最后的代碼段提到,如果插件是EventSource類型,則綁定管道,并且調(diào)用StartEventSource開啟消息監(jiān)聽。結(jié)合類圖,只有SysEventSource是EventSource類型的插件,所以只有SysEventSource持有管道。
- EventSource類型的插件會(huì)監(jiān)聽HiSysEvent接口發(fā)來的消息,解析并組裝成管道事件,分發(fā)給管道中的插件處理。
SysEventSource::StartEventSource()函數(shù)實(shí)現(xiàn)如下。
- void SysEventSource::StartEventSource()
- {
- HIVIEW_LOGI("SysEventSource start");
- std::shared_ptr<EventReceiver> sysEventReceiver = std::make_shared<SysEventReceiver>(*this);
- eventServer.AddReceiver(sysEventReceiver);
- eventServer.Start();
- }
SysEventSource有個(gè)成員變量eventServer,EventServer會(huì)開啟socketserver端用于接收HiSysEvent接口發(fā)來的socket消息。
類圖如下:


說明:
1.SysEventSource::StartEventSource()中把SysEventReceiver對(duì)象加入到EventServer的receivers_容器中。
2.EventServer收到socket消息之后,調(diào)用SysEventReceiver::HandlerEvent(const std::string& rawMsg)方法處理接收到的消息。
3.SysEventReceiver::HandlerEvent()方法中會(huì)調(diào)用SysEventParser::Parser(const std::string& rawMsg)方法解析消息并組裝成SysEvent對(duì)象,然后調(diào)用EventSource::PublishPipelineEvent(std::shared_ptr
EventSource::PublishPipelineEvent()代碼如下:
- bool EventSource::PublishPipelineEvent(std::shared_ptr<PipelineEvent> event)
- {
- HIVIEW_LOGD("EventSource PublishPipelineEvent");
- if (event == nullptr) {
- return false;
- }
- if (Audit::IsEnabled()) {
- auto digest = GetPluginInfo() + Audit::DOMAIN_DELIMITER + event->GetEventInfo();
- Audit::WriteAuditEvent(Audit::StatsEvent::PIPELINE_EVENT_CREATE, event->createTime_, digest);
- }
- for (auto& pipeline : listeners_) {
- if ((pipeline != nullptr) && (pipeline->CanProcessEvent(event))) {
- // one event can only be processed by one pipeline
- pipeline->ProcessEvent(event);
- return true;
- }
- }
- return false;
- }
結(jié)合上面的類圖,EventSource持有管道列表對(duì)象listeners_,EventSource::PublishPipelineEvent()函數(shù)中會(huì)遍歷該列表,調(diào)用Pipeline::ProcessEvent(std::shared_ptr
Pipeline類型有個(gè)成員變量std::list
在插件平臺(tái)初始化的時(shí)候,HiviewPlatform::CreatePipeline()函數(shù)中已經(jīng)把Plugin插件對(duì)象加入到了processors_列表中。
接下來分析Pipeline::ProcessEvent()函數(shù)做了什么。
- void Pipeline::ProcessEvent(std::shared_ptr<PipelineEvent> event)
- {
- event->SetPipelineInfo(name_, processors_);
- event->OnContinue();
- }
把Pipeline的processors_對(duì)象賦給了PipelineEvent的processors_對(duì)象,然后調(diào)用
PipelineEvent::OnContinue()函數(shù)。
接下來分析PipelineEvent::OnContinue()函數(shù):
- bool PipelineEvent::OnContinue()
- {
- // 判斷hasFinish_標(biāo)志位和processors_列表是否已經(jīng)為空,如已為空,結(jié)束本次事件傳遞
- if ((!hasFinish_) && processors_.empty()) {
- return OnFinish();
- }
- // once the event start delivering
- // the call OnContinue means one has done the processing of the event
- // this may be called by upstream event processor or the framework
- if (Audit::IsEnabled() && startDeliver_) {
- Audit::WriteAuditEvent(Audit::StatsEvent::PIPELINE_EVENT_HANDLE_OUT,
- createTime_, std::to_string(Thread::GetTid()));
- }
- // the framework will call OnContinue when the event is assigned to a pipeline
- if (!startDeliver_) {
- startDeliver_ = true;
- }
- // 取出processors_列表中第一個(gè)Plugin元素,并從列表彈出
- std::weak_ptr<Plugin> plugin = processors_.front();
- processors_.pop_front();
- if (auto pluginPtr = plugin.lock()) {
- if (!pluginPtr->CanProcessMoreEvents()) {
- handler_->PauseDispatch(plugin);
- }
- if (Audit::IsEnabled()) {
- Audit::WriteAuditEvent(Audit::StatsEvent::PIPELINE_EVENT_HANDLE_IN, createTime_,
- pluginPtr->GetHandlerInfo());
- }
- // 判斷當(dāng)前Plugin是否開啟了事件工作隊(duì)列,如有則加入事件隊(duì)列處理,如沒有直接調(diào)用OnEventProxy
- if (auto workLoop = pluginPtr->GetWorkLoop()) {
- workLoop->AddEvent(pluginPtr, shared_from_this()); //............[1]
- } else {
- pluginPtr->OnEventProxy(shared_from_this()); //..........[2]
- }
- } else {
- return OnContinue();
- }
- return true;
- }
不管插件是否使用了事件隊(duì)列,最終都會(huì)調(diào)用到Plugin::OnEventProxy()函數(shù)來處理管道事件。
Plugin::OnEventProxy()函數(shù)實(shí)現(xiàn)如下:
- bool Plugin::OnEventProxy(std::shared_ptr<Event> event)
- {
- if (event == nullptr) {
- return false;
- }
- std::shared_ptr<Event> dupEvent = event;
- auto processorSize = dupEvent->GetPendingProcessorSize();
- dupEvent->ResetPendingStatus();
- bool ret = OnEvent(dupEvent); //..............注[1]
- if (!dupEvent->IsPipelineEvent()) {
- if (Audit::IsEnabled()) {
- Audit::WriteAuditEvent(Audit::StatsEvent::QUEUE_EVENT_OUT, dupEvent->createTime_,
- std::to_string(Thread::GetTid()));
- }
- } else {
- if ((!dupEvent->HasFinish() && !dupEvent->HasPending()) &&
- (processorSize == dupEvent->GetPendingProcessorSize())) {
- dupEvent->OnContinue();//.............注[2]
- }
- }
- return ret;
- }
先調(diào)用注[1]OnEvent()函數(shù)處理事件,再判斷管道事件傳遞是否已結(jié)束,如未結(jié)束則調(diào)用注[2]PipelineEvent::OnContinue()函數(shù)繼續(xù)把管道事件傳遞給后面的
插件處理。
結(jié)合目前的插件配置文件,整個(gè)插件管理平臺(tái)的消息處理流程大致如下:
EventServer接收到HiSysEvent組件接口發(fā)來的消息 --> SysEventReceiver::HandlerEvent() --> SysEventParser::Parser() --> EventSource::PublishPipelineEvent() --> Pipeline::ProcessEvent() --> PipelineEvent::OnContinue() --> SysEventService::OnEventProxy --> SysEventService::OnEvent --> PipelineEvent::OnContinue() --> Faultlogger::OnEventProxy --> Faultlogger::OnEvent --> 結(jié)束
至此,事件處理流程已分析完。
3 總結(jié)
以上內(nèi)容首先分析了hiview插件管理平臺(tái)的初始化,然后對(duì)事件的處理流程做了分析。后續(xù)會(huì)詳細(xì)講解base/hiviewdfx/hiview/plugins目錄下每個(gè)插件的源碼。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)