头图

原文链接:https://mp.weixin.qq.com/s/Y44jUEB3ttlijbMDPrBcNg,点击链接查看更多技术内容;

作为基础软件服务子系统的HarmonyOS HiViewDFX(以下简称HiViewDFX)框架,是HarmonyOS的公共基础设施。包括日志、事件、跟踪、故障管理及观测剖析五大部分,同时也提供了故障检测、定位和性能观测剖析的开发套件,以及将端侧数据直接对接云侧大数据质量分析平台和IDE(Integrated Development Environment, 集成开发环境)调试调优工具。为应对应用开发难题,HiViewDFX提供了高保障能力。

图片
HarmonyOS HiViewDFX框架图

一、HiViewDFX介绍

一般地,捕获异常信号需要自行增加捕获异常的机制,通过信号来感知异常及采集对应的异常日志,但是往往这类信息无法有效的与系统信息关联。

HiViewDFX为应用开发者提供了具有轻量级故障检测、精准的故障定位日志以及快速恢复功能的开发套件,能够迅速提高应用的可靠性。

如下图所示,在HarmonyOS系统中首先内置崩溃、泄漏、卡死等轻量级故障检测器,用来记录应用在系统侧的异常状态。

其次,在应用程序框架内及运行时增加了异常捕获能力,使得系统和应用能够分层检测和记录异常信息,通过开放查询、订阅、恢复三个API(Application Programming Interface, 应用程序编程接口)提供给开发者。

图片
HarmonyOS应用异常处理框架图

在应用崩溃和卡死的时候,HiViewDFX提供的精准日志定位功能能够详细地记录异常发生时的日志。

HiViewDFX 提供了对应的应用异常日志查询接口,将JS_CRASH, CPP_CRASH, APP_FREEZE这三类故障日志提供给开发者,并且是结构化的日志信息,开发者可以从反馈的信息中快速获取到故障的相关信息。

为给用户提供更佳的体验,在日志信息反馈异常数据的基础上,HiViewDFX提供了应用快速恢复的框架(如下图),当系统感知到JS_CRASH, CPP_CRASH, APP_FREEZE, KILL等故障之后,能快速通知应用,应用将之前的状态进行保存,而后系统会自动拉起应用,然后恢复到故障前的原界面。

图片
应用恢复框架图

二、HIViewDFX相关工具介绍

HiViewDFX的工具入口---Insight,是DevEco Studio中的插件,拥有众多系统能力支撑,如图所示,有调试连接器、HiTrace、HiPerf 以及HiProfiler 框架。

HiViewDFX提供的系统能力包含日志、事件、分布式跟踪、故障等。

图片
HiViewDFX调试调优架构图

接下来,就让我们一起了解下HiViewDFX提供的部分工具吧!

1)调试连接器
如下图所示,调试连接器是连接上位机和下位机的通道,通常用做嵌入式开发,常用的连接工具,比如,基于串口或者网口的Telnet、SSH等。

HarmonyOS面向不同形态的设备时,这些设备可能不具备网口或者USB端口,只有一个串口,要支持这个,串口上需要具备Shell和文件IO等功能。有了调试连接器作为中转,就可以让开发者使用的IDE和其他工具脚本无需面临硬件的复杂性,更好的关注调试和调优本身。

图片
调试连接器原理示意图

2)HiTrace
如下图所示,HiTrace工具用于追踪进程轨迹,进行程序性能分析,支持内核FTrace预置埋点和用户态打点。

在性能分析中,Trace是最常用的方式,可以说Trace就是性能的日志,把Trace按照模块分门别类,这就是Trace的Tag。例如,Sched是操作系统内核的调度信息打点;Ability是Ability模块在用户态的关键生命周期打点。

假设定位某应用掉帧的问题,在分析时,打开Graphic、Ability、Sched等tag点,可以在Insight里面分析应用在送显、图形模块绘制相关的耗时。

图片
HiTrace工具原理示意图

3)HiPerf
如下图所示,HiPerf是为开发者提供的采样调优分析工具,通过采样的方式,可以采集CPU PMU、Tracepoints以及程序热点函数信息,并且和Insight联动,提供离线和实时分析的能力。

HiPerf采集定位过程中会遇到一个难点:使用跨编程语言,在程序运行时,可能会存在一些跨语言的调用。例如,从JS调用NAPI到C++接口等。

因为是抽样的调用栈采集,如果只采集其中一种语言的调用栈会导致两个语言之间的耗时数据无法同步,从而产生冲突,影响性能问题的度量和定位,所以在调用栈采集的时候进行缝合。

图片
HiPerf工具原理示意图

如下图所示,这段JS代码调用了三个内存相关的数据获取接口,均是NAPI实现的NATIVE接口。

在HiPerf中,首先采集NAPI调用JS的调用栈信息,当采集到一个C++的调用栈时,此时栈顶是函数NativeFunctionCallBack()的NAPI回调,则这个NATIVE调用栈就可以和前一次采集到的JS调用栈合并,最终拼接出一个完整的调用栈。

图片
NAPI调用中JS-CPP栈缝合示意图

除了上述系统内置的分析点,开发者也可以通过HiTrace接口增加自定义的性能分析打点。

如下列代码所示,HiTraceMeter的接口比较简单,找到一段流程的开始和结束,加上Trace打点,就能在Insight中看到Start-End的耗时。

// API
declare namespace hiTraceMeter {
    // Async trace
    function startTrace(name: string, taskId: number, exceptedTime?: number): void;
    function finishTrace(name: string, taskId: number): void;

    // Counter trace
    function traceByValue(name: string, count: number): void;
}

//example
onWindowStageCreate(windowStage) {
    ...
    hiTraceMeter.startTrace('getMainWindow');
    windowStage.getMainWindow().then((win) => {
        Appstorage.SetOrCreate(Constants.MAIN_WINDOW, win);
        hiTraceMeter.finishTrace('getMainWindow');
        ...
    });
    ...
}

HiTrace API介绍及开发样例图

4)HiProfilerHiProfiler
框架是基于HiViewDFX基础能力构建的一个插件集,可以为Insight提供调优数据采集。该组件整体分为PC端和设备端两部分。

PC端最终作为DevEco Studio的插件进行发布,内部主要包括分为UI绘制、设备管理、进程管理、插件管理、数据导入、数据存储、 数据分析、Session管理、配置管理等模块。设备端主要包括命令行工具、服务进程、插件集合、应用程序组件等模块。

设备端提供了插件扩展能力,对外提供了插件接口,基于该扩展能力可以按需定义自己的能力,并集成到框架中。

图片
HiProfiler框架

三、如何查询内存信息
操作系统对内存是分级定义的,从物理地址空间到虚拟地址空间,再分为用户态和内核态。应用内存调优分析的时候,还需要分解到虚拟地址、Ark JS的内存、NATIVE的内存、字体图标等资源、So的映射、线程栈等,这些都属于内存观测的范围。

HiViewDFX提供了HiDumper工具,作用是系统信息查询,它提供了系统版本、CPU占用率、内存以及Sa信息,开发者可以使用HiDumper来分析应用的内存(如下图)。开发者分析内存比较关注的是Ark JS Heap以及NATIVE Heap、Pss、Dirty这些指标,如果程序有内存泄漏或者一般的内存膨胀的问题,可以看到这些值会不断变大。

图片
HiDumper查看内存信息示意图

如果应用要在程序中监控内存,可以使用这组HiDebug接口(如下列代码所示),前三个接口是NATIVE内存分配器的统计信息,可以获取NATIVE分配器的总大小、分配大小和可用大小,后三个接口是从系统Smaps获取的统计信息,注意这两个信息不是一个维度上的,不能做数据的等同,在使用场景上也有差异。

// API
declare namespace hidebug {
    function getNativeHeapSize(): bigint;
    function getNativeHeapAllocatedSize(): bigint;
    function getNativeHeapFreeSize(): bigint;
    function getPss(): bigint;
    function getSharedDirty(): bigint;
    function getPrivateDirty(): bigint;
}

HiDebug接口示意图

分配器的信息经常用于统计程序中对Native内存的分配情况,不代表这些内存实际被使用,这部分内存是开发者可以控制且可以进行优化的。

而系统Smaps统计信息,常用于程序感知自身内存的实际占用大小,这个大小经常受到分配器延迟释放、系统延迟回收、Copy-on-write、分配器MetaData额外损耗等,造成统计出来的内存信息和分配器控制的内存不完全等同,往往不能作为内存优化的直接依据,而是作为内存压力统计的依据。

四、如何进行内存调优分析

我们通过信息查询得知了内存的大小信息,那么如何进行内存分析呢?

如下图所示,右侧部分是开发者使用Insight进行分析的样例。

首先分析泳道图上的内存曲线,得到三类数据,JS、Native和虚拟内存,它们采集的分配信息基本都比较相似。例如,图中分配信息部分,名字和调用栈,是区分一块内存的重要信息,地址和大小是一块内存的基本信息。引用关系可以帮助我们建立内存之间的关系树,帮助我们更快找到内存的引入点。分配时间则可以帮助开发者了解哪些内存会长时间存留,长时间存留的内存是需要重点关注的。

图片
内存分析数据采集原理图

另外,虽然虚拟内存在64位上可能不是一个痛点问题,但是在32位程序上经常会导致问题。32位程序的地址空间只有4GB,如果是32位内核,那么用户态一般情况只有3GB地址空间,这种情况下开发者需要关注虚拟内存的使用情况,HarmonyOS的做法是在Mmap的地方进行Hook,拿到分配的调用栈,并且对系统映射的绝大多数匿名页都进行了命名。因此不论是文件页还是匿名页,在分配信息中都能看到页的命名信息,这对于内存分析非常有帮助。

以上就是HiViewDFX提供的可靠性和性能优化调试调优能力的相关介绍了,欢迎广大开发者使用HiViewDFX框架来开发一个高可靠高性能的应用!

HarmonyOS.png


HarmonyOS开发者
72 声望36 粉丝

提供HarmonyOS关键技术解析、版本更新、开发者实践和活动资讯,欢迎各位开发者加入HarmonyOS生态,一起创造无限可能!