SuperAPI AI Directory

ohos-dev-distributed-mmi-ram-rom-measurement

General & other

View repository

Updated 10-15 16:44

About this skill

MMI RAM/ROM Impact Measurement

Measure changes in ROM (binary size) and RAM (runtime memory) usage before and after code changes in the multimodalinput subsystem.

Prerequisite skill: For infrastructure such as build deployment and device connection, see ohos-dev-distributed-mmi-device-test-harness。

When to Use

  • Before submitting a PR, the impact of code changes on binary size needs to be quantified
  • Need to prove that refactoring/new features do not introduce memory bloat
  • Review requires RAM/ROM data to be provided

Safety and Comparability Gates

Replacing system .so files, remounting / read-write, and rebooting a device are destructive test operations. Do them only after explicit user approval, keep .bak copies of every replaced library, and restore the device to the requested final state before ending the workflow.

A RAM/ROM conclusion is only comparable when these conditions are recorded:

  • baseline and current image/build or .so provenance
  • exact libraries replaced
  • whether the same functional path was executed before both captures
  • whether process-level numbers are idle-vs-idle, exercised-vs-exercised, or mixed

If baseline is idle but current is post-exercise, do not present process-level PSS delta as feature RAM cost. Use it only as context and rely on library PSS plus code-level theoretical analysis.

Three Pillars of Measurement

This skill requires measuring RAM impact from three mutually corroborating dimensions:

Dimension Method What it reflects Limitation
ROM section-level readelf -S Statically mapped memory (.text/.rodata/.bss) Does not reflect heap allocation
RAM measured smaps + hidumper real-device collection Actual physical memory of the process Affected by collection conditions/compilation differences
RAM theoretical git diff analysis of newly added containers + struct estimation Heap allocation and runtime data structures Manual estimation, depends on correct struct size analysis

Three dimensionsmust be completed simultaneously, cross-validating each other. Section-level estimation alone will miss heap allocation; measurement alone will be affected by noise; theoretical analysis alone cannot confirm actual behavior.

Measuring target libraries and process scope

First determine the impact scope based on code changes; do not default to only looking at the server process. At minimum, check whether the changes reach the following libraries:

Library Role RAM analysis scope
libmmi-server.z.so Server main logic multimodalinput process
libmmi-server-common.z.so Server common multimodalinput process
libmmi-util.z.so Utility library All target processes that load this library
libmmi-client.z.so Client API/proxy Client processes that trigger this API; cannot be represented by the server process PSS

Rules:

  • ROM analysis should cover all affected libmmi .so, including libmmi-client.z.so。
  • RAM analysis must be attributed according to the actual loading process.libmmi-client.z.so is usually not loaded in the multimodalinput server process; if the client API, proxy, NAPI, or test binary path is affected, a client process that loads libmmi-client.z.so needs to be selected to execute the functional path and collect smaps/hidumper data for that process.
  • If the changes only affect server-internal paths, it can be stated that the client so is not loaded into the server process, but code diff, symbols, or section-level data must still be used to confirm whether libmmi-client.z.so truly has no change or is not involved in this RAM conclusion.
  • The final report should list the mapping of "library → process → whether the functional path is executed → whether included in the RAM conclusion", to avoid mistaking server PSS for the full MMI RAM cost.

ROM Measurement Method

1. File-level comparison (quick)

BUILD_OUT="code/out/rk3568/multimodalinput/input"
ls -la $BUILD_OUT/libmmi-*.z.so

2. Section-level comparison (precise, recommended)

Use readelf -S to analyze key sections:

readelf -S libmmi-server.z.so | grep -E '\.text|\.rodata|\.data|\.bss'
Section Meaning Impact
.text Executable code Directly affects ROM; mapped as shared clean affects RAM PSS
.rodata Read-only data (string constants, etc.) Affects ROM and shared clean PSS
.data Initialized global variables Affects ROM and private dirty
.bss Zero-initialized global variables Does not affect ROM; affects anonymous RAM
.data.rel.ro Read-only after relocation Affects ROM

3. Baseline acquisition strategy

Option A: Device .bak file(Recommended)

Before deploying the modified .so, back up the original .so first:

$HDC shell "cp /system/lib/libmmi-server.z.so /data/local/tmp/libmmi-server.z.so.bak"
# 部署新 .so...
# 之后拉取基线
$HDC file recv /data/local/tmp/libmmi-server.z.so.bak ./libmmi-server.z.so.bak

Option B: CI baseline build

Extract the same version of the .so from the CI daily build image as the baseline.

RAM measurement methods

1. Find the process PID

$HDC shell "pidof multimodalinput"

Note on PID stability: multimodalinput is a system service and may be restarted by the watchdog, causing the PID to change. The PID may differ between multiple hdc commands.Solution: Use command substitution to complete PID lookup and data collection within a single hdc shell invocation:

$HDC shell "cat /proc/\$(pidof multimodalinput)/smaps_rollup"

2. smaps_rollup (process-level summary)

$HDC shell "cat /proc/\$(pidof multimodalinput)/smaps_rollup"

Key fields:

  • Pss: Actual physical memory after apportioning by sharing ratio (best reflects real usage)
  • Rss: Resident physical memory (includes the full amount of shared pages)
  • Size: Virtual address space size

3. smaps per-library PSS (important)

$HDC shell "cat /proc/\$(pidof multimodalinput)/smaps" > smaps.txt

For the parsing script and precautions, see references/smaps-pss-attribution.md. The key requirement is that each mapping header must reset the current library name, to avoid misattributing anonymous PSS to the previous .so。

4. hidumper --mem (system-level)

$HDC shell "hidumper --mem \$(pidof multimodalinput)"

The output includes: native heap, .so mmap PSS, total PSS, and other categories.

Note: The hidumper output may be very long and may be truncated through hdc shell. You can redirect it to a file and then pull it:

$HDC shell "hidumper --mem \$(pidof multimodalinput) > /data/local/tmp/hidumper_mem.txt"
$HDC file recv /data/local/tmp/hidumper_mem.txt ./hidumper_mem.txt

5. RAM increment attribution (required)

Every RAM growth conclusion must answer "where does the growth come from". You cannot just report total PSS or multimodalinput process PSS difference. At minimum, break down the growth into the following sources and provide evidence:

Source Evidence Handling requirement
New/changed .so mappings Mapped .so list and PSS in baseline/current smaps List each .so 's baseline PSS, current PSS, difference, and whether it is newly loaded
libmmi code segment changes readelf -S + smaps per-library PSS List separately the PSS increment of each libmmi library and the corresponding section differences
native heap growth hidumper --mem native heap + code diff Map to newly added containers, objects, caches, RS resources, or lifecycle states
anonymous/file PSS growth smaps_rollup Pss_Anon/Pss_File or smaps classification Explain whether it comes from the heap, anonymous mappings, file mappings, the test framework, or system state
Test environment/framework noise Newly loaded test binary dependencies, input-test framework, etc. .so Separate from the target feature; must not be directly attributed to code changes
Unattributed residual total delta - explained delta List separately; when the residual is too large, the conclusion can only be marked as "incomplete attribution"

It is recommended to aggregate all .so PSS entries from smaps by full path, rather than only matching libmmi. For a reference script, see references/smaps-pss-attribution.md。

Perform an outer join on the baseline and current results to generate a table of "newly loaded/changed loaded .so.so" entries:

Process .so .so path Baseline PSS (kB) Current PSS (kB) Difference (kB) Newly loaded Attribution
multimodalinput /system/lib/libmmi-server.z.so X Y +/-Z no libmmi server code segment change
multimodalinput /system/lib/libxxx.z.so 0 Y +Y yes Feature path trigger / test framework / environment noise

If the memory growth mainly comes from loading new .so, the report must list these .so names, paths, PSS increments, and trigger reasons; it must not merely state ".so mmap PSS growth." If it cannot be proven that a newly loaded library was triggered by the target feature, it should be classified under "environment/test framework differences" or "unattributed," and must not be classified under target feature cost.

6. Baseline RAM acquisition

Exact method: temporarily restore the .bak .so → restart → collect → restore the modified .so

# 1. 备份当前修改版
$HDC shell "mount -o rw,remount /"    # RK3568 根文件系统在 /,不是 /system
$HDC shell "cp /system/lib/libmmi-server.z.so /data/local/tmp/libmmi-server.z.so.new"
# 对 libmmi-server-common.z.so, libmmi-util.z.so 同样操作

# 2. 恢复基线
$HDC shell "cp /data/local/tmp/libmmi-server.z.so.bak /system/lib/libmmi-server.z.so"
# 对每个库同样恢复 .bak
$HDC shell "reboot"
# 等待重启 (~60s)...

# 3. 采集基线 RAM
$HDC shell "cat /proc/\$(pidof multimodalinput)/smaps_rollup"
$HDC shell "cat /proc/\$(pidof multimodalinput)/smaps" > smaps_baseline.txt
$HDC shell "hidumper --mem \$(pidof multimodalinput) > /data/local/tmp/hidumper_baseline.txt"

# 4. 恢复修改版
$HDC shell "mount -o rw,remount /"
$HDC shell "cp /data/local/tmp/libmmi-server.z.so.new /system/lib/libmmi-server.z.so"
# 对每个库同样恢复 .new
$HDC shell "reboot"

RK3568 mount note: the root filesystem is mounted at /, not /system。mount -o rw,remount /system . mount -o rw,remount /system will report the error "not in /proc/mounts". The correct command is mount -o rw,remount /。

Estimation method(no restart required):

Estimate the RAM change based on segment-level analysis:

  • .text + .rodata difference → affects shared clean PSS
  • .bss difference → affects anonymous memory
  • .data difference → affects private dirty

When the segment difference is very small (< 1%) and .bss is unchanged, the estimation method can be used directly without restarting.

7. The feature path must be executed before collection

Key constraint: merely deploying the .so and restarting is not enough. If the change introduces a lazily allocated data structure, the complete feature path must be executed before collection to ensure that all runtime state is actually allocated:

部署 .so → 重启 → 等待系统稳定 (30s) → 执行功能路径 → 等待稳定 (5s) → 采集

The feature path is defined by the change content; do not fix it to a single feature test case. General template:

  1. Initialize the system state that the change depends on (such as display, window, device, session, permission).
  2. Call the newly added or modified public API, IPC, server handler, callback, or event path.
  3. Trigger lazy allocation and cache paths (such as first event, repeated event, boundary device, window, display group).
  4. Cover cleanup paths (unbinding, destruction, disconnection, window deletion, state before and after service restart).
  5. Record the steps actually executed, and explain which affected libraries and processes are covered by that path.

For example, display group binding can be triggered with UpdateDisplayInfo、UpdateWindowInfo、 BindDeviceToDisplayGroupByDisplay and event injection; other features must replace this with their own minimal complete feature path.

RAM collection without executing the feature path willunderestimateactual memory usage, because lazily allocated containers are still empty.

Code theoretical analysis method (third pillar)

Segment-level estimation can only reflect statically mapped memory and cannot reflect heap allocation (new/malloc). For feature changes that introduce new data structures,you mustanalyze the newly added containers and objects in the code diff.

For detailed estimation rules, see references/theoretical-ram-estimation.md. The main process only requires outputting:

  • Add the list of containers, objects, caches, and RS resources.
  • Typical scenarios and upper-bound scenario estimates for each type of object.
  • Allocation timing, cleanup paths, and leak risks.
  • Cross-validation conclusions between theoretical estimates and measured libmmi PSS/native heap/Pss_Anon.

Output report template

## ROM 影响

### 文件级对比

| 库 | 基线(bytes) | 当前(bytes) | 差值 | 比例 |
|----|------------|------------|------|------|
| libmmi-server.z.so | X | Y | +/-Z | +/-N% |
| ... | | | | |
| **合计** | | | | |

### libmmi-server.z.so 段级分析

| 段 | 基线 | 当前 | 差值 |
|----|------|------|------|
| .text | X | Y | +/-Z |
| .rodata | | | |
| .data | | | |
| .bss | | | |

### 影响范围映射

| 库 | 受影响代码路径 | 目标进程 | 是否采集 RAM | 说明 |
|----|--------------|----------|--------------|------|
| libmmi-server.z.so | server handler / event path | multimodalinput | yes/no | |
| libmmi-client.z.so | client API / proxy / NAPI | <client-process-or-test-binary> | yes/no | |

## RAM 影响

### RAM 增长原因归因

#### 进程级增量拆解

| 指标/类别 | 基线(kB) | 当前(kB) | 差值(kB) | 证据来源 | 归因 |
|-----------|----------|----------|----------|----------|------|
| total PSS | X | Y | +/-Z | smaps_rollup / hidumper | 汇总,仅作入口 |
| .so mmap PSS | X | Y | +/-Z | hidumper + smaps `.so` 聚合 | 见“已加载 .so 增量”表 |
| native heap | X | Y | +/-Z | hidumper | 见“heap/anonymous 增量”表 |
| Pss_Anon | X | Y | +/-Z | smaps_rollup | heap/anonymous/运行时状态 |
| Pss_File | X | Y | +/-Z | smaps_rollup | 文件映射或加载库变化 |

#### 已加载 .so 增量

| 进程 | `.so` 路径 | 基线 PSS(kB) | 当前 PSS(kB) | 差值(kB) | 是否新增加载 | 触发路径/归因 |
|------|-----------|--------------|--------------|----------|--------------|---------------|
| multimodalinput | /system/lib/libmmi-server.z.so | X | Y | +/-Z | no | 目标 libmmi 代码变化 |
| <process> | /system/lib/libxxx.z.so | 0 | Y | +Y | yes | 功能路径触发 / 测试框架 / 环境噪声 |

#### libmmi PSS 增量

| 库 | 基线 PSS (kB) | 当前 PSS (kB) | 差值 | section 证据 | 归因 |
|----|-------------|---------------|------|--------------|------|
| libmmi-server.z.so | X | Y | +/-Z | .text/.rodata/.data/.bss 差异 | server 代码路径 |
| libmmi-client.z.so | X | Y | +/-Z | .text/.rodata/.data/.bss 差异 | client API/proxy 路径 |

#### heap/anonymous 增量

| 增长项 | 估算增量 | 实测对应指标 | 代码证据 | 分配/释放路径 | 结论 |
|--------|----------|--------------|----------|---------------|------|
| 新增 map/vector/cache | X KB | native heap / Pss_Anon | 文件:行号或 diff 摘要 | 创建/清理函数 | 目标特性成本 |
| 测试框架对象 | X KB | native heap / Pss_Anon | 测试入口或依赖 | 测试进程生命周期 | 环境差异 |

#### 未归因残差

| total PSS 差值 | 已解释 .so 差值 | 已解释 heap/anon 差值 | 未归因残差 | 处理 |
|----------------|----------------|----------------------|------------|------|
| +X kB | +Y kB | +Z kB | +N kB | N 过大时标记结论不完整,并继续补采 smaps/hidumper/功能路径证据 |

### 实测对比(libmmi 库 PSS)

| 库 | 基线 PSS (kB) | 修改版 PSS (kB) | 差值 | 归因 |
|----|-------------|---------------|------|------|
| libmmi-server.z.so | X | Y | +/-Z | section/code path evidence |
| ... | | | |

### 理论分析

| 场景 | 当前实现 | 后续/完整实现(如适用) |
|------|---------|------------------------|
| 不使用路径 | ~X B | ~X B |
| 典型功能路径 | ~X KB | ~X KB |
| 极端上界 | ~X KB/MB | ~X KB/MB |

### 实测 vs 理论对照

[说明两者是否一致,差异原因]

## 结论

ROM: +/-X bytes (+/-N%)
RAM: 服务端进程 PSS +/-X kB;客户端进程 PSS +/-Y kB(如适用);理论典型场景 ~Z KB

Common Mistakes

Error Consequence Fix
Use ls -l file size to estimate RAM File size includes ELF headers, symbol tables, and other parts that are not mapped into memory Use readelf for segment-level analysis
Ignore the .bss segment .bss does not occupy ROM but affects anonymous RAM Check .bss separately
Use RSS for inter-process comparison RSS includes the full amount of shared pages and is counted multiple times Use PSS
Compare RAM data across different system versions System library version differences cause noise Ensure only the target .so is replaced and the system baseline is consistent
libmmi-client.z.so Only look at the server process The client so is not loaded in the server process, so the RAM conclusion is missing Select the client process that loads the client so or the test binary for separate collection
Collect without executing the functional path after restart Lazily allocated data structures are not actually allocated, so RAM is underestimated Before collection, the full functional path must be executed to trigger all state allocations
Only do segment-level estimation without theoretical analysis Segment-level estimation does not reflect heap allocations (new/malloc) and misses runtime data structures Combine with newly added containers in the diff to perform theoretical memory estimation
Only do theoretical analysis without measurement Theoretical estimates may miss or overestimate, and actual behavior cannot be confirmed Cross-validate with the three pillars (segment-level + measurement + theory)
Only report total PSS or .so mmap PSS growth Cannot determine whether the growth comes from which library, heap object, or test environment Must list process-level classification, the PSS increment of each loaded .so PSS increment, heap/anonymous increment, and unattributed residual
.so mmap PSS growth but not list newly added .so Cannot determine whether it is caused by target feature loading libraries For baseline/current complete .so perform an outer join on the list and list newly added loads and PSS changes
Attribute the extra loaded by the test framework .so is attributed to the target feature Exaggerates the RAM cost of the feature Not proven to be triggered by the target function .so classify into environment/test framework differences or unattributed
smaps parser does not reset current_lib The PSS of anonymous mapping is incorrectly attributed to the previous libmmi library, severely overestimating Each mapping header line must reset current_lib
PID changes across multiple hdc commands Read smaps of different processes Use $(pidof multimodalinput) complete within a single shell call
mount -o rw,remount /system RK3568 root filesystem is at /, reports "not in /proc/mounts" Use mount -o rw,remount /
Ignore Parcelable/RefBase inheritance Underestimate the size of objects allocated on the heap containing a RefCounter Analyze the complete inheritance chain, including the vtable and RefCounter
Ignore RS SurfaceNode/BufferQueue Miss the largest single-item memory overhead (50-200 KB per group) Analyze the RS resource creation path, distinguishing nullptr vs actual creation
Theoretical analysis only looks at sizeof, not heap allocation Miss the heap allocation of std::string long strings, vector elements, and map nodes Analyze the actual memory layout of each field