1# 使用HiDumper命令行工具优化性能 2 3## 简介 4 5HiDumper是系统为开发、测试人员、IDE工具提供的系统信息获取工具,帮助开发者分析、定位问题。在应用开发过程中,开发者可以使用HiDumper命令行工具获取UI界面组件树信息,配合ArkUI Inspector等图形化工具定位布局性能问题;还可以使用该命令行工具获取如内存和CPU使用情况等各项系统数据,对应用性能进行评估。本文通过一些示例介绍在优化应用性能过程中如何使用HiDumper命令行工具。 6 7开发者可参考下面步骤,通过使用HiDumper提供的-s、--mem、--cpuusage等命令进行性能分析。有关HiDumper其他功能的详细介绍可查看[《HiDumper概述》](../../device-dev/subsystems/subsys-dfx-hidumper.md)。 8 9 10## HiDumper查看组件信息 11 12开发者可以按照以下步骤获取组件信息,相比ArkUI Inspector可更灵活的获取组件的细粒度信息。 13 141. 开启ArkUI的debug模式。 15 ``` 16 hdc shell param set persist.ace.debug.enabled 1 17 ``` 182. 重新启动应用。 193. 获取当前页面对应应用的window ID。 20 ``` 21 hdc shell hidumper -s WindowManagerService -a '-a' 22 ``` 23 24  25 26 27 * `hidumper -s`: 导出系统全部的元能力信息。 28 * `hidumper -s WindowManagerService -a ['-a']`: 以 `-a` 为参数导出当前设备打开的窗口的系统元能力信息,其中 `-a` 表示执行打印操作,`['-a']` 表示打印的具体信息。 29 * `WindowName`: 表示当前打开应用的窗口名,其中 `demo0` 为示例应用的默认窗口名。 30 * 常见的WindowName与内置应用窗口对应关系如下: 31 32 | WindowName | 说明 | 33 |----------------------|------| 34 | EntryView | 桌面 | 35 | RecentView | 最近任务 | 36 | SystemUi_NavigationB | 三键导航 | 37 | SystemUi_StatusBar | 状态栏 | 38 | ScreenLockWindow | 锁屏 | 39 404. 通过WinId获取对应页面的控件树文件。 41 ``` 42 hdc shell hidumper -s WindowManagerService -a '-w 28 -element -c' // 28 即为查找到的WinId 43 ``` 44 45  46 47 485. 下载组件树文件到本地。由于安全机制此处的路径非真实路径,需要使用 `find` 命令查找对应文件的准确路径。 49 ``` 50 hdc shell find /data/ -name arkui.dump 51 ``` 52 53  54 55 ``` 56 hdc file recv /data/app/el2/100/base/com.example.demo/haps/entry/files/arkui.dump // 获取文件到本地 57 ``` 586. 打开文件查看应用组件树。组件树文件详细列出了每个组件的各项属性,如子组件数量childSize、组件ID、背景色BackgroundColor等。 59 ``` 60 // arkui.dump文件内容片断 61 |-> GridItem childSize:1 62 | ID: 22 63 | Depth: 9 64 | IsDisappearing: 0 65 | FrameRect: RectT (360.00, 0.00) - [180.00 x 29.00] 66 | BackgroundColor: #00000000 67 // ... 68 |-> Stack childSize:1 69 | ID: 23 70 | Depth: 10 71 | IsDisappearing: 0 72 | FrameRect: RectT (0.00, 0.00) - [180.00 x 29.00] 73 | BackgroundColor: #FFFFFF00 74 // ... 75 |-> Stack childSize:1 76 | ID: 24 77 | Depth: 11 78 | IsDisappearing: 0 79 | FrameRect: RectT (0.00, 0.00) - [180.00 x 29.00] 80 | BackgroundColor: #FF0000FF 81 // ... 82 |-> Stack childSize:1 83 | ID: 25 84 | Depth: 12 85 | IsDisappearing: 0 86 | FrameRect: RectT (0.00, 0.00) - [180.00 x 29.00] 87 | BackgroundColor: #00000000 88 // ... 89 |-> Text childSize:0 90 ID: 26 91 Depth: 13 92 IsDisappearing: 0 93 FrameRect: RectT (83.00, 0.00) - [14.00 x 29.00] 94 BackgroundColor: #00000000 95 // ... 96 ``` 97 98### 查看if/else控件 99 100当使用if/else时,if/else语句会被当成一个组件,作为节点存在于组件树上。使用HiDumper命令时,打印的组件树内容包含if/else组件信息(使用ArkUI Inspector工具,if/else组件不会被作为节点项显示在组件树上)。下述代码中通过if语句: `if(this.isShow)` 实现Row组件的创建和销毁。 101``` 102@Entry 103@Component 104struct ConditionComponent { 105 @State isShow: boolean = true; 106 107 build() { 108 Column() { 109 Button("显隐切换") 110 .onClick(() => { 111 this.isShow = !this.isShow 112 }) 113 if (this.isShow) { 114 Row() 115 .width(300).height(300).backgroundColor(Color.Pink) 116 } 117 }.width('100%') 118 } 119} 120``` 121当isShow为true时,Row组件显示,此时使用HiDumper查看组件树文件,可以发现使用if/else条件语句时,if/else组件也被当作节点被创建,Row组件被当作子组件嵌套在其中。 122``` 123|-> IfElse childSize:1 124 | ID: 9 125 | Depth: 6 126 | IsDisappearing: 0 127 |-> Row childSize:0 128 ID: 12 129 Depth: 7 130 IsDisappearing: 0 131 FrameRect: RectT (135.00, 60.00) - [450.00 x 450.00] 132 BackgroundColor: #FFFFC0CB 133 ParentLayoutConstraint: minSize: [[0.00 x 0.00]]maxSize: [[720.00 x 1136.00]]percentReference: [[720.00 x 1136.00]]parentIdealSize: [[720.00 x NA]]selfIdealSize: [[NA x NA]] 134 top: 60.000000 left: 135.000000 135 Active: 1 136 Visible: 0 137 ... 138``` 139当isShow为false时,Row组件隐藏,此时使用HiDumper查看组件树文件,可以发现使用if/else条件语句时,if/else组件也被当作节点被创建,但Row组件并不会被加载。 140``` 141|-> IfElse childSize:0 142 ID: 9 143 Depth: 6 144 IsDisappearing: 0 145``` 146 147### 查看visibility属性 148 149开发者可以使用visibility属性控制组件的显隐。下述代码中通过visibility属性: `visibility(this.isVisible)` 实现Row组件的显示和隐藏。 150``` 151@Entry 152@Component 153struct VisibilityComponent { 154 @State isVisible: Visibility = Visibility.Visible; 155 156 build() { 157 Column() { 158 Button("Visible") 159 .onClick(() => { 160 this.isVisible = Visibility.Visible 161 }) 162 Button("Hidden") 163 .onClick(() => { 164 this.isVisible = Visibility.Hidden 165 }) 166 Button("None") 167 .onClick(() => { 168 this.isVisible = Visibility.None 169 }) 170 Row().visibility(this.isVisible) 171 .width(720).height(300).backgroundColor(Color.Pink) 172 }.width('100%') 173 } 174} 175``` 176当isVisible为Visible时,Row组件显示,此时使用HiDumper查看控件树文件,Visible属性为0,FrameRect属性内组件的宽和高为450。 177``` 178|-> Row childSize:0 179 ID: 13 180 Depth: 6 181 IsDisappearing: 0 182 FrameRect: RectT (135.00, 180.00) - [450.00 x 450.00] 183 BackgroundColor: #FFFFC0CB 184 ParentLayoutConstraint: minSize: [[0.00 x 0.00]]maxSize: [[720.00 x 1136.00]]percentReference: [[720.00 x 1136.00]]parentIdealSize: [[720.00 x NA]]selfIdealSize: [[NA x NA]] 185 top: 180.000000 left: 135.000000 186 Active: 1 187 Visible: 0 188 ... 189``` 190当isVisible为Hidden时,Row组件隐藏,此时使用HiDumper查看控件树文件,Visible属性为1,FrameRect属性内组件的宽和高为450。 191``` 192|-> Row childSize:0 193 ID: 13 194 Depth: 6 195 IsDisappearing: 0 196 FrameRect: RectT (135.00, 180.00) - [450.00 x 450.00] 197 BackgroundColor: #FFFFC0CB 198 ParentLayoutConstraint: minSize: [[0.00 x 0.00]]maxSize: [[720.00 x 1136.00]]percentReference: [[720.00 x 1136.00]]parentIdealSize: [[720.00 x NA]]selfIdealSize: [[NA x NA]] 199 top: 180.000000 left: 135.000000 200 Active: 1 201 Visible: 1 202 ... 203``` 204当isVisible为None时,Row组件隐藏,此时使用HiDumper查看控件树文件,Visible属性为2,FrameRect属性内组件的宽和高为0。 205``` 206|-> Row childSize:0 207 ID: 13 208 Depth: 6 209 IsDisappearing: 0 210 FrameRect: RectT (135.00, 180.00) - [0.00 x 0.00] 211 BackgroundColor: #FFFFC0CB 212 ParentLayoutConstraint: minSize: [[0.00 x 0.00]]maxSize: [[720.00 x 1136.00]]percentReference: [[720.00 x 1136.00]]parentIdealSize: [[720.00 x NA]]selfIdealSize: [[NA x NA]] 213 top: 180.000000 left: 135.000000 214 Active: 1 215 Visible: 2 216 ... 217``` 218对比三种情况的组件数可以发现: 219* 当使用visibility属性控制组件显隐时,该组件通过改变Visible属性实现功能,但组件的其他结构依然会被创建。 220* 当visibility属性为Hidden时,组件虽然不会显示,但仍然在页面中占用实际位置。 221 222## HiDumper查看内存信息 223 224开发者可以通过以下步骤,获取到当前应用的内存信息。 225 2261. 打开示例应用,运行 `hdc shell hidumper -s WindowManagerService -a '-a'` ,获取到当前应用的pid。 2272. 输入 `hidumper --mem [pid]` ,并将命令中的 `[pid]` 换成当前应用的pid,就可以获取到示例应用的内存信息了,如下图所示: 228``` 229hdc shell hidumper --mem [pid] 230``` 231 232 233 234一般情况下,开发者只需要关注PSS (Proportional Set Size(实际使用物理内存))Total一列的数据,即示例应用实际使用的物理内存。在上图中可以看到,应用总共占用了53395KB的内存,主要包括ark ts heap(ArkUI堆内存)的3411KB以及native heap的45846KB。 235 236## HiDumper查看CPU信息 237 238在应用开发中,经常会遇到需要大量计算的场景,HiDumper提供了查看CPU使用率的功能,方便开发者进行性能优化。下面将以[Chat](https://gitee.com/openharmony/applications_app_samples/tree/master/code/Solutions/IM/Chat)为例,展示如何使用HiDumper查看CPU信息。 239 2401. 编译项目、安装并打开Chat应用,运行以下HiDumper命令获取当前应用的Pid。 241 ``` 242 hdc shell hidumper -s WindowManagerService -a '-a' 243 ``` 2442. 运行 `hidumper --cpuusage [pid]` ,获取Chat应用的CPU信息,如下图所示: 245 ``` 246 hdc shell hidumper --cpuusage [pid] 247 ``` 248 249  250 251 一般情况下,只需要关注Total Usage(总使用率)、User Space(用户程序的运行空间)、Kernel Space(内核的运行空间)。其中,Total Usage用于统计应用的总CPU使用率,User Space用于执行简单的运算,Kernel Space用于调用系统的资源。通过上图可以看到,当前示例的三项数据分别是11%,11%,0%,表示当前应用并没有调用系统资源,只需要查看应用的运行空间即可。如果想要查看CPU在一段时间内的使用率,则可以通过Shell脚本,多次执行 `hdc shell hidumper --cpuusage [pid]` 命令,并通过 `hdc shell hidumper --zip --cpuusage` 将结果输出到手机的/data/log/hidumper目录下,方便进行分析。