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

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

MMI RAM/ROM 影响度量

MMI RAM/ROM Impact Measurement

衡量 multimodalinput 子系统代码变更前后的 ROM(二进制体积)和 RAM(运行时内存)占用变化。
前置 skill: 编译部署、设备连接等基础设施见
ohos-dev-distributed-mmi-device-test-harness
。
Measure changes in ROM (binary size) and RAM (runtime memory) usage of the
multimodalinput
subsystem before and after code changes.
Prerequisite skill: For infrastructure such as build and deployment and device connectivity, see
ohos-dev-distributed-mmi-device-test-harness
.

When to Use

When to Use

  • 提交 PR 前需要量化代码变更对二进制体积的影响
  • 需要证明重构/新特性不引入内存膨胀
  • 评审要求提供 RAM/ROM 数据
  • You need to quantify the impact of a code change on binary size before submitting a PR
  • You need to demonstrate that a refactoring or new feature does not introduce memory bloat
  • A review requires RAM/ROM data

Safety and Comparability Gates

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.
Replacing system
.so
files, remounting
/
read-write, and rebooting a device are destructive test operations. Perform 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 the following conditions are recorded:
  • The provenance of the baseline and current image/build or
    .so
    files
  • The exact libraries replaced
  • Whether the same functional path was executed before both captures
  • Whether process-level numbers represent idle-vs-idle, exercised-vs-exercised, or mixed states
If the baseline is idle but the current measurement is post-exercise, do not present the process-level PSS delta as the feature's RAM cost. Use it only as context and rely on library PSS plus code-level theoretical analysis.

度量三支柱

Three Pillars of Measurement

本 skill 要求从三个互相佐证的维度度量 RAM 影响:
维度方法反映什么局限
ROM 段级
readelf -S
静态映射内存(.text/.rodata/.bss)不反映堆分配
RAM 实测smaps + hidumper 真机采集进程实际物理内存受采集条件/编译差异影响
RAM 理论git diff 分析新增容器 + 结构体估算堆分配和运行时数据结构人工估算,依赖正确的结构体大小分析
三个维度必须同时完成,互相交叉验证。仅做段级估算会遗漏堆分配;仅做实测会受噪声影响;仅做理论分析无法确认实际表现。
This skill requires RAM impact to be measured from three mutually corroborating dimensions:
DimensionMethodWhat It ReflectsLimitations
ROM section-level
readelf -S
Statically mapped memory (
.text
/
.rodata
/
.bss
)
Does not reflect heap allocations
RAM measuredsmaps + on-device hidumper collectionActual physical memory used by a processAffected by collection conditions and build differences
RAM theoreticalgit diff analysis of new containers + structure-size estimationHeap allocations and runtime data structuresManual estimation that depends on correct structure-size analysis
All three dimensionsmust be completed and cross-validated. Section-level estimation alone misses heap allocations; measurements alone are affected by noise; theoretical analysis alone cannot confirm actual behavior.

度量目标库和进程范围

Target Libraries and Process Scope

先根据代码变更确定影响面,不要默认只看服务端进程。至少检查改动是否触达以下库:
库角色RAM 分析范围
libmmi-server.z.so
服务端主逻辑
multimodalinput
进程
libmmi-server-common.z.so
服务端公共
multimodalinput
进程
libmmi-util.z.so
工具库所有加载该库的目标进程
libmmi-client.z.so
客户端 API/代理触发该 API 的客户端进程,不能用服务端进程 PSS 代表
规则:
  • ROM 分析应覆盖所有受影响的 libmmi
    .so
    ,包括
    libmmi-client.z.so
    。
  • RAM 分析必须按实际加载进程归属。
    libmmi-client.z.so
    通常不在
    multimodalinput
    服务端进程中加载;如果客户端 API、proxy、NAPI 或测试二进制路径受影响,需要选择一个会加载
    libmmi-client.z.so
    的客户端进程执行功能路径并采集该进程的 smaps/hidumper 数据。
  • 如果改动只影响服务端内部路径,可以说明 client so 未加载到服务端进程,但仍要用代码 diff、符号 或段级数据确认
    libmmi-client.z.so
    是否真的无变化或不参与本次 RAM 结论。
  • 最终报告要列出“库 → 进程 → 是否执行功能路径 → 是否纳入 RAM 结论”的映射,避免把服务端 PSS 误当成全量 MMI RAM 成本。
First determine the impact scope from the code changes. Do not assume that only the server process needs to be inspected. At minimum, check whether the changes affect the following libraries:
LibraryRoleRAM Analysis Scope
libmmi-server.z.so
Main server logic
multimodalinput
process
libmmi-server-common.z.so
Shared server code
multimodalinput
process
libmmi-util.z.so
Utility libraryEvery target process that loads the library
libmmi-client.z.so
Client API/proxyThe client process that invokes the API; server-process PSS is not a valid substitute
Rules:
  • ROM analysis must cover every affected libmmi
    .so
    , including
    libmmi-client.z.so
    .
  • RAM analysis must follow the actual process that loads each library.
    libmmi-client.z.so
    is generally not loaded in the
    multimodalinput
    server process. If the client API, proxy, NAPI, or test-binary path is affected, select a client process that loads
    libmmi-client.z.so
    , execute the functional path in that process, and collect its smaps/hidumper data.
  • If the changes affect only an internal server path, you may state that the client
    .so
    is not loaded in the server process. However, you must still use the code diff, symbols, or section-level data to confirm that
    libmmi-client.z.so
    is genuinely unchanged or does not participate in the RAM conclusion.
  • The final report must include a “library → process → functional path executed → included in RAM conclusion” mapping to avoid presenting server-process PSS as the total MMI RAM cost.

ROM 度量方法

ROM Measurement Methods

1. 文件级对比(快速)

1. File-Level Comparison (Quick)

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

2. 段级对比(精确,推荐)

2. Section-Level Comparison (Precise, Recommended)

使用
readelf -S
分析关键段:
bash
readelf -S libmmi-server.z.so | grep -E '\.text|\.rodata|\.data|\.bss'
段含义影响
.text
可执行代码直接影响 ROM;映射为 shared clean 影响 RAM PSS
.rodata
只读数据(字符串常量等)影响 ROM 和 shared clean PSS
.data
已初始化全局变量影响 ROM 和 private dirty
.bss
零初始化全局变量不影响 ROM;影响 anonymous RAM
.data.rel.ro
重定位后只读影响 ROM
Use
readelf -S
to analyze the key sections:
bash
readelf -S libmmi-server.z.so | grep -E '\.text|\.rodata|\.data|\.bss'
SectionMeaningImpact
.text
Executable codeDirectly affects ROM; mapped as shared clean and affects RAM PSS
.rodata
Read-only data, such as string constantsAffects ROM and shared clean PSS
.data
Initialized global variablesAffects ROM and private dirty memory
.bss
Zero-initialized global variablesDoes not affect ROM; affects anonymous RAM
.data.rel.ro
Read-only after relocationAffects ROM

3. 基线获取策略

3. Baseline Acquisition Strategy

方案 A:设备 .bak 文件(推荐)
部署修改版 .so 前,先备份原始 .so:
bash
$HDC shell "cp /system/lib/libmmi-server.z.so /data/local/tmp/libmmi-server.z.so.bak"
Option A: Device
.bak
Files
(Recommended)
Before deploying the modified
.so
files, back up the original files:
bash
$HDC shell "cp /system/lib/libmmi-server.z.so /data/local/tmp/libmmi-server.z.so.bak"

部署新 .so...

Deploy the new .so...

之后拉取基线

Then pull the baseline

$HDC file recv /data/local/tmp/libmmi-server.z.so.bak ./libmmi-server.z.so.bak

**方案 B:CI 基线构建**

从 CI 每日构建镜像中提取同版本 .so 作为基线。
$HDC file recv /data/local/tmp/libmmi-server.z.so.bak ./libmmi-server.z.so.bak

**Option B: CI Baseline Build**

Extract the `.so` files from the same version in the daily CI-built image as the baseline.

RAM 度量方法

RAM Measurement Methods

1. 找到进程 PID

1. Find the Process PID

bash
$HDC shell "pidof multimodalinput"
注意 PID 稳定性:multimodalinput 是系统服务,可能被 watchdog 重启导致 PID 变化。在多条 hdc 命令之间 PID 可能不同。解决方案:使用命令替换在单次 hdc shell 调用中完成 PID 查找和数据采集:
bash
$HDC shell "cat /proc/\$(pidof multimodalinput)/smaps_rollup"
bash
$HDC shell "pidof multimodalinput"
PID stability:
multimodalinput
is a system service and may be restarted by the watchdog, causing its PID to change. The PID may differ between multiple HDC commands. Solution: Use command substitution to find the PID and collect the data within a single HDC shell invocation:
bash
$HDC shell "cat /proc/\$(pidof multimodalinput)/smaps_rollup"

2. smaps_rollup(进程级汇总)

2. smaps_rollup (Process-Level Summary)

bash
$HDC shell "cat /proc/\$(pidof multimodalinput)/smaps_rollup"
关键字段:
  • Pss: 按共享比例分摊后的实际物理内存(最能反映真实占用)
  • Rss: 驻留物理内存(包含共享页全量)
  • Size: 虚拟地址空间大小
bash
$HDC shell "cat /proc/\$(pidof multimodalinput)/smaps_rollup"
Key fields:
  • Pss: Actual physical memory after shared pages are proportionally allocated; it best reflects real usage
  • Rss: Resident physical memory, including the full amount of shared pages
  • Size: Virtual address-space size

3. smaps per-library PSS(重要)

3. smaps Per-Library PSS (Important)

bash
$HDC shell "cat /proc/\$(pidof multimodalinput)/smaps" > smaps.txt
解析脚本和注意事项见
references/smaps-pss-attribution.md
。关键要求是每个 mapping header 都要重置当前库名,避免把 anonymous PSS 错归到上一个
.so
。
bash
$HDC shell "cat /proc/\$(pidof multimodalinput)/smaps" > smaps.txt
See
references/smaps-pss-attribution.md
for the parsing script and precautions. A key requirement is to reset the current library name at every mapping header so that anonymous PSS is not incorrectly attributed to the preceding
.so
.

4. hidumper --mem(系统级)

4. hidumper --mem (System-Level)

bash
$HDC shell "hidumper --mem \$(pidof multimodalinput)"
输出包含:native heap、.so mmap PSS、total PSS 等分类。
注意:hidumper 输出可能很长,通过 hdc shell 可能被截断。可重定向到文件后拉取:
bash
$HDC shell "hidumper --mem \$(pidof multimodalinput) > /data/local/tmp/hidumper_mem.txt"
$HDC file recv /data/local/tmp/hidumper_mem.txt ./hidumper_mem.txt
bash
$HDC shell "hidumper --mem \$(pidof multimodalinput)"
The output includes categories such as native heap,
.so
mmap PSS, and total PSS.
Note: The hidumper output may be very long and can be truncated when passed through HDC shell. Redirect it to a file and then pull the file:
bash
$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 增量归因(必须)

5. RAM Delta Attribution (Mandatory)

每一个 RAM 增长结论都必须回答“增长来自哪里”。不能只报告 total PSS 或
multimodalinput
进程 PSS 差值。至少把增长拆成以下来源并给出证据:
来源证据处理要求
新增/变化的
.so
映射
baseline/current smaps 中的 mapped
.so
列表和 PSS
列出每个
.so
的基线 PSS、当前 PSS、差值、是否新增加载
libmmi 代码段变化
readelf -S
+ smaps per-library PSS
单独列出 libmmi 各库 PSS 增量和对应 section 差异
native heap 增长
hidumper --mem
native heap + 代码 diff
对应到新增容器、对象、缓存、RS 资源或生命周期状态
anonymous/file PSS 增长smaps_rollup
Pss_Anon
/
Pss_File
或 smaps 分类
说明是否来自堆、匿名映射、文件映射、测试框架或系统状态
测试环境/框架噪声新增加载的测试二进制依赖、input-test 框架等
.so
与目标特性分开,不得直接归因给代码变更
未归因残差total delta - 已解释 delta单独列出;残差过大时结论只能标记为“不完整归因”
推荐从 smaps 中按完整路径聚合所有
.so
PSS,而不是只匹配
libmmi
;参考脚本见
references/smaps-pss-attribution.md
。
对 baseline/current 两份结果做 outer join,生成“新增/变化的已加载
.so
”表:
进程
.so
路径
基线 PSS(kB)当前 PSS(kB)差值(kB)是否新增加载归因
multimodalinput/system/lib/libmmi-server.z.soXY+/-Znolibmmi server 代码段变化
multimodalinput/system/lib/libxxx.z.so0Y+Yyes功能路径触发 / 测试框架 / 环境噪声
如果内存增长主要来自加载了新的
.so
,报告必须列出这些
.so
的名称、路径、 PSS 增量和触发原因;不能只写“.so mmap PSS 增长”。如果无法证明某个新加载库由目标 特性触发,应归入“环境/测试框架差异”或“未归因”,不要归入目标特性成本。
Every RAM-growth conclusion must answer “where did the growth come from?” It is not sufficient to report only the total PSS or the delta in the
multimodalinput
process PSS. At minimum, break the growth down into the following sources and provide evidence:
SourceEvidenceProcessing Requirement
New or changed
.so
mappings
Mapped
.so
lists and PSS in the baseline/current smaps
List each
.so
's baseline PSS, current PSS, delta, and whether it was newly loaded
Changes to libmmi code sections
readelf -S
+ per-library smaps PSS
Separately list the PSS delta of each libmmi library and the corresponding section differences
Native heap growth
hidumper --mem
native heap + code diff
Correlate it with new containers, objects, caches, RS resources, or lifecycle state
Anonymous/file PSS growthsmaps_rollup
Pss_Anon
/
Pss_File
or smaps categories
Determine whether it comes from the heap, anonymous mappings, file mappings, the test framework, or system state
Test environment/framework noiseNewly loaded test-binary dependencies and input-test framework
.so
files
Keep them separate from the target feature; do not attribute them directly to the code change
Unattributed residualtotal delta - explained deltaList separately; if the residual is too large, the conclusion must be marked as “incomplete attribution”
It is recommended to aggregate PSS for all
.so
files from smaps using their complete paths rather than matching only
libmmi
. See
references/smaps-pss-attribution.md
for a reference script.
Perform an outer join on the baseline/current results to generate a table of newly loaded or changed
.so
files:
Process
.so
Path
Baseline PSS (kB)Current PSS (kB)Delta (kB)Newly LoadedAttribution
multimodalinput/system/lib/libmmi-server.z.soXY+/-Znolibmmi server code-section changes
multimodalinput/system/lib/libxxx.z.so0Y+YyesFunctional path trigger / test framework / environment noise
If memory growth mainly comes from newly loaded
.so
files, the report must list each library's name, path, PSS delta, and trigger. It is not sufficient to state only that “.so mmap PSS increased.” If a newly loaded library cannot be proven to have been triggered by the target feature, classify it as an “environment/test-framework difference” or “unattributed,” not as the target feature's cost.

6. 基线 RAM 获取

6. Baseline RAM Acquisition

精确方法:临时恢复 .bak .so → 重启 → 采集 → 恢复修改版 .so
bash
undefined
Precise method: Temporarily restore the
.bak
.so
files → reboot → collect data → restore the modified
.so
files
bash
undefined

1. 备份当前修改版

1. Back up the current modified files

$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"
$HDC shell "mount -o rw,remount /" # The RK3568 root filesystem is mounted at /, not /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 同样操作

Do the same for libmmi-server-common.z.so and libmmi-util.z.so

2. 恢复基线

2. Restore the baseline

$HDC shell "cp /data/local/tmp/libmmi-server.z.so.bak /system/lib/libmmi-server.z.so"
$HDC shell "cp /data/local/tmp/libmmi-server.z.so.bak /system/lib/libmmi-server.z.so"

对每个库同样恢复 .bak

Restore the .bak file for each library in the same way

$HDC shell "reboot"
$HDC shell "reboot"

等待重启 (~60s)...

Wait for the reboot (~60s)...

3. 采集基线 RAM

3. Collect baseline RAM data

$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"
$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. 恢复修改版

4. Restore the modified files

$HDC shell "mount -o rw,remount /" $HDC shell "cp /data/local/tmp/libmmi-server.z.so.new /system/lib/libmmi-server.z.so"
$HDC shell "mount -o rw,remount /" $HDC shell "cp /data/local/tmp/libmmi-server.z.so.new /system/lib/libmmi-server.z.so"

对每个库同样恢复 .new

Restore the .new file for each library in the same way

$HDC shell "reboot"

> **RK3568 mount 注意**:根文件系统挂载在 `/`,不是 `/system`。`mount -o rw,remount /system` 会报 "not in /proc/mounts" 错误。正确命令是 `mount -o rw,remount /`。

**估算方法**(不需要重启):

基于段级分析推算 RAM 变化:
- `.text` + `.rodata` 差值 → 影响 shared clean PSS
- `.bss` 差值 → 影响 anonymous 内存
- `.data` 差值 → 影响 private dirty

当段差值很小(< 1%)且 `.bss` 无变化时,可以直接用估算方法而无需重启。
$HDC shell "reboot"

> **RK3568 mount note**: The root filesystem is mounted at `/`, not `/system`. Running `mount -o rw,remount /system` produces a “not in /proc/mounts” error. The correct command is `mount -o rw,remount /`.

**Estimation method** (no reboot required):

Estimate RAM changes based on section-level analysis:
- `.text` + `.rodata` delta → affects shared clean PSS
- `.bss` delta → affects anonymous memory
- `.data` delta → affects private dirty memory

When the section delta is very small (< 1%) and `.bss` is unchanged, the estimation method can be used directly without rebooting.

7. 采集前必须执行功能路径

7. The Functional Path Must Be Executed Before Collection

关键约束:仅部署 .so 并重启是不够的。如果变更引入了懒分配(lazy allocation)的数据结构,必须在采集前执行完整的功能路径,确保所有运行时状态被实际分配:
部署 .so → 重启 → 等待系统稳定 (30s) → 执行功能路径 → 等待稳定 (5s) → 采集
功能路径按变更内容定义,不要固定成某一个特性用例。通用模板:
  1. 初始化变更依赖的系统状态(如 display、window、device、session、permission)。
  2. 调用新增或修改的 public API、IPC、server handler、callback 或事件路径。
  3. 触发懒分配和缓存路径(如首次事件、重复事件、边界设备、窗口、显示组)。
  4. 覆盖清理路径(解绑、销毁、断连、窗口删除、服务重启前后状态)。
  5. 记录实际执行的步骤,并说明哪些受影响库和进程被该路径覆盖。
例如 display group 绑定可以用
UpdateDisplayInfo
、
UpdateWindowInfo
、
BindDeviceToDisplayGroupByDisplay
和事件注入触发;其他特性必须替换成自己的最小完备功能路径。
不执行功能路径的 RAM 采集会低估实际内存占用,因为懒分配的容器仍为空。
Critical constraint: Deploying the
.so
files and rebooting is not sufficient. If a change introduces a lazily allocated data structure, the complete functional path must be executed before collection to ensure that all runtime state has actually been allocated:
Deploy .so → Reboot → Wait for system stability (30s) → Execute functional path → Wait for stability (5s) → Collect data
Define the functional path according to the change rather than fixing it to one feature test case. A general template is:
  1. Initialize the system state required by the change, such as display, window, device, session, and permission state.
  2. Invoke the new or modified public API, IPC, server handler, callback, or event path.
  3. Trigger lazy-allocation and cache paths, such as the first event, repeated events, boundary devices, windows, and display groups.
  4. Cover cleanup paths, including unbinding, destruction, disconnection, window deletion, and state before and after a service restart.
  5. Record the steps actually executed and explain which affected libraries and processes are covered by the path.
For example, display-group binding can be triggered through
UpdateDisplayInfo
,
UpdateWindowInfo
,
BindDeviceToDisplayGroupByDisplay
, and event injection. Other features must use their own minimal complete functional path.
Collecting RAM data without executing the functional path willunderestimate actual memory usage because lazily allocated containers will still be empty.

代码理论分析方法(第三支柱)

Code-Level Theoretical Analysis Method (Third Pillar)

段级估算只能反映静态映射内存,无法反映堆分配(new/malloc)。对于引入新数据结构的特性变更,必须分析代码 diff 中的新增容器和对象。
详细估算规则见
references/theoretical-ram-estimation.md
。主流程只要求输出:
  • 新增容器、对象、缓存和 RS 资源清单。
  • 每类对象的典型场景和上界场景估算。
  • 分配时机、清理路径和泄漏风险。
  • 理论估算与实测 libmmi PSS/native heap/Pss_Anon 的交叉验证结论。
Section-level estimation can reflect only statically mapped memory and cannot reflect heap allocations (
new
/
malloc
). For feature changes that introduce new data structures, youmust analyze new containers and objects in the code diff.
See
references/theoretical-ram-estimation.md
for detailed estimation rules. The main process only needs to produce the following:
  • A list of new containers, objects, caches, and RS resources
  • Typical-scenario and upper-bound-scenario estimates for each object type
  • Allocation timing, cleanup paths, and leak risks
  • A cross-validation conclusion comparing theoretical estimates with measured libmmi PSS, native heap, and Pss_Anon

输出报告模板

Output Report Template

markdown
undefined
markdown
undefined

ROM 影响

ROM Impact

文件级对比

File-Level Comparison

库基线(bytes)当前(bytes)差值比例
libmmi-server.z.soXY+/-Z+/-N%
...
合计
LibraryBaseline (bytes)Current (bytes)DeltaPercentage
libmmi-server.z.soXY+/-Z+/-N%
...
Total

libmmi-server.z.so 段级分析

Section-Level Analysis of libmmi-server.z.so

段基线当前差值
.textXY+/-Z
.rodata
.data
.bss
SectionBaselineCurrentDelta
.textXY+/-Z
.rodata
.data
.bss

影响范围映射

Impact Scope Mapping

库受影响代码路径目标进程是否采集 RAM说明
libmmi-server.z.soserver handler / event pathmultimodalinputyes/no
libmmi-client.z.soclient API / proxy / NAPI<client-process-or-test-binary>yes/no
LibraryAffected Code PathTarget ProcessRAM CollectedDescription
libmmi-server.z.soserver handler / event pathmultimodalinputyes/no
libmmi-client.z.soclient API / proxy / NAPI<client-process-or-test-binary>yes/no

RAM 影响

RAM Impact

RAM 增长原因归因

RAM Growth Attribution

进程级增量拆解

Process-Level Delta Breakdown

指标/类别基线(kB)当前(kB)差值(kB)证据来源归因
total PSSXY+/-Zsmaps_rollup / hidumper汇总,仅作入口
.so mmap PSSXY+/-Zhidumper + smaps
.so
聚合
见“已加载 .so 增量”表
native heapXY+/-Zhidumper见“heap/anonymous 增量”表
Pss_AnonXY+/-Zsmaps_rollupheap/anonymous/运行时状态
Pss_FileXY+/-Zsmaps_rollup文件映射或加载库变化
Metric/CategoryBaseline (kB)Current (kB)Delta (kB)Evidence SourceAttribution
total PSSXY+/-Zsmaps_rollup / hidumperSummary only; entry point
.so mmap PSSXY+/-Zhidumper + aggregated smaps
.so
values
See “Loaded .so Delta” table
native heapXY+/-ZhidumperSee “Heap/Anonymous Delta” table
Pss_AnonXY+/-Zsmaps_rollupHeap/anonymous/runtime state
Pss_FileXY+/-Zsmaps_rollupFile mappings or changes in loaded libraries

已加载 .so 增量

Loaded .so Delta

进程
.so
路径
基线 PSS(kB)当前 PSS(kB)差值(kB)是否新增加载触发路径/归因
multimodalinput/system/lib/libmmi-server.z.soXY+/-Zno目标 libmmi 代码变化
<process>/system/lib/libxxx.z.so0Y+Yyes功能路径触发 / 测试框架 / 环境噪声
Process
.so
Path
Baseline PSS (kB)Current PSS (kB)Delta (kB)Newly LoadedTrigger Path/Attribution
multimodalinput/system/lib/libmmi-server.z.soXY+/-ZnoTarget libmmi code changes
<process>/system/lib/libxxx.z.so0Y+YyesFunctional path trigger / test framework / environment noise

libmmi PSS 增量

libmmi PSS Delta

库基线 PSS (kB)当前 PSS (kB)差值section 证据归因
libmmi-server.z.soXY+/-Z.text/.rodata/.data/.bss 差异server 代码路径
libmmi-client.z.soXY+/-Z.text/.rodata/.data/.bss 差异client API/proxy 路径
LibraryBaseline PSS (kB)Current PSS (kB)DeltaSection EvidenceAttribution
libmmi-server.z.soXY+/-Z.text/.rodata/.data/.bss differencesServer code path
libmmi-client.z.soXY+/-Z.text/.rodata/.data/.bss differencesClient API/proxy path

heap/anonymous 增量

Heap/Anonymous Delta

增长项估算增量实测对应指标代码证据分配/释放路径结论
新增 map/vector/cacheX KBnative heap / Pss_Anon文件:行号或 diff 摘要创建/清理函数目标特性成本
测试框架对象X KBnative heap / Pss_Anon测试入口或依赖测试进程生命周期环境差异
Growth ItemEstimated DeltaCorresponding Measured MetricCode EvidenceAllocation/Release PathConclusion
New map/vector/cacheX KBnative heap / Pss_AnonFile:line number or diff summaryCreation/cleanup functionTarget feature cost
Test framework objectsX KBnative heap / Pss_AnonTest entry point or dependencyTest process lifecycleEnvironment difference

未归因残差

Unattributed Residual

total PSS 差值已解释 .so 差值已解释 heap/anon 差值未归因残差处理
+X kB+Y kB+Z kB+N kBN 过大时标记结论不完整,并继续补采 smaps/hidumper/功能路径证据
Total PSS DeltaExplained .so DeltaExplained Heap/Anon DeltaUnattributed ResidualHandling
+X kB+Y kB+Z kB+N kBIf N is too large, mark the conclusion as incomplete and continue collecting smaps/hidumper/functional-path evidence

实测对比(libmmi 库 PSS)

Measured Comparison (libmmi Library PSS)

库基线 PSS (kB)修改版 PSS (kB)差值归因
libmmi-server.z.soXY+/-Zsection/code path evidence
...
LibraryBaseline PSS (kB)Modified PSS (kB)DeltaAttribution
libmmi-server.z.soXY+/-Zsection/code path evidence
...

理论分析

Theoretical Analysis

场景当前实现后续/完整实现(如适用)
不使用路径~X B~X B
典型功能路径~X KB~X KB
极端上界~X KB/MB~X KB/MB
ScenarioCurrent ImplementationFuture/Complete Implementation, if applicable
Unused path~X B~X B
Typical functional path~X KB~X KB
Extreme upper bound~X KB/MB~X KB/MB

实测 vs 理论对照

Measured vs. Theoretical Comparison

[说明两者是否一致,差异原因]
[Explain whether the two are consistent and the reasons for any differences.]

结论

Conclusion

ROM: +/-X bytes (+/-N%) RAM: 服务端进程 PSS +/-X kB;客户端进程 PSS +/-Y kB(如适用);理论典型场景 ~Z KB
undefined
ROM: +/-X bytes (+/-N%) RAM: server-process PSS +/-X kB; client-process PSS +/-Y kB (if applicable); theoretical typical scenario ~Z KB
undefined

Common Mistakes

Common Mistakes

错误后果修复
用
ls -l
文件大小评估 RAM
文件大小包含 ELF 头、符号表等不映射到内存的部分用 readelf 段级分析
忽略 .bss 段.bss 不占 ROM 但影响 anonymous RAM单独检查 .bss
拿 RSS 做进程间对比RSS 包含共享页全量,被多次计算用 PSS
对比不同版本系统的 RAM 数据系统库版本差异导致噪声确保只替换目标 .so,系统基线一致
libmmi-client.z.so
只看服务端进程
client so 不在服务端进程加载,RAM 结论缺失选择加载 client so 的客户端进程或测试二进制单独采集
重启后不执行功能路径就采集懒分配的数据结构未实际分配,RAM 被低估采集前必须执行完整功能路径触发所有状态分配
只做段级估算不做理论分析段级估算不反映堆分配(new/malloc),遗漏运行时数据结构结合 diff 中的新增容器做理论内存估算
只做理论分析不做实测理论估算可能遗漏/高估,无法确认实际表现三支柱(段级 + 实测 + 理论)交叉验证
只报 total PSS 或 .so mmap PSS 增长无法判断增长来自哪个库、堆对象还是测试环境必须列出进程级分类、每个已加载
.so
的 PSS 增量、heap/anonymous 增量和未归因残差
.so mmap PSS
增长但不列新增
.so
无法判断是否由目标特性加载库导致对 baseline/current 的完整
.so
列表做 outer join,列出新增加载和 PSS 变化
把测试框架额外加载的
.so
归因给目标特性
夸大特性 RAM 成本未证明由目标功能触发的
.so
归入环境/测试框架差异或未归因
smaps 解析器不重置 current_libanonymous mapping 的 PSS 被错误归入上一个 libmmi 库,严重高估每个 mapping header 行必须重置 current_lib
PID 在多条 hdc 命令间变化读取了不同进程的 smaps使用
$(pidof multimodalinput)
在单次 shell 调用内完成
mount -o rw,remount /system
RK3568 根文件系统在 /,报 "not in /proc/mounts"使用
mount -o rw,remount /
忽略 Parcelable/RefBase 继承低估含 RefCounter 堆分配的对象大小分析完整继承链,包含 vtable 和 RefCounter
忽略 RS SurfaceNode/BufferQueue遗漏最大的单项内存开销(每 group 50-200 KB)分析 RS 资源创建路径,区分 nullptr vs 实际创建
理论分析只看 sizeof 不看堆分配遗漏 std::string 长字符串、vector 元素、map 节点的堆分配分析每个字段的实际内存布局
MistakeConsequenceFix
Using
ls -l
file size to evaluate RAM
File size includes the ELF header, symbol table, and other parts that are not mapped into memoryUse readelf section-level analysis
Ignoring the
.bss
section
.bss
does not consume ROM but affects anonymous RAM
Check
.bss
separately
Using RSS for cross-process comparisonsRSS includes the full amount of shared pages and causes double countingUse PSS
Comparing RAM data from different system versionsDifferences in system library versions introduce noiseEnsure that only the target
.so
files are replaced and that the system baseline is consistent
Inspecting
libmmi-client.z.so
only in the server process
The client
.so
is not loaded in the server process, resulting in an incomplete RAM conclusion
Select a client process or test binary that loads the client
.so
and collect its data separately
Collecting data after reboot without executing the functional pathLazily allocated data structures are not actually allocated, underestimating RAMAlways execute the complete functional path before collection to trigger all state allocations
Performing only section-level estimation without theoretical analysisSection-level estimation does not reflect heap allocations (
new
/
malloc
) and misses runtime data structures
Combine it with theoretical memory estimation of new containers in the diff
Performing only theoretical analysis without measurementTheoretical estimates may miss or overestimate actual behavior and cannot confirm actual performanceCross-validate the three pillars: section-level analysis, measurement, and theoretical analysis
Reporting only total PSS or
.so
mmap PSS growth
It is impossible to determine whether the growth came from a library, heap objects, or the test environmentInclude process-level categories, the PSS delta for every loaded
.so
, heap/anonymous growth, and the unattributed residual
Observing
.so mmap PSS
growth without listing the new
.so
files
It is impossible to determine whether the target feature caused the libraries to be loadedPerform an outer join on the complete baseline/current
.so
lists and list newly loaded libraries and PSS changes
Attributing
.so
files additionally loaded by the test framework to the target feature
This overstates the feature's RAM costClassify
.so
files that are not proven to be triggered by the target functionality as environment/test-framework differences or unattributed
The smaps parser does not reset
current_lib
Anonymous mapping PSS is incorrectly attributed to the preceding libmmi library, severely overestimating its usageReset
current_lib
at every mapping header line
The PID changes between multiple HDC commandsData is read from different processesUse
$(pidof multimodalinput)
within a single shell invocation
mount -o rw,remount /system
The RK3568 root filesystem is mounted at
/
, resulting in a “not in /proc/mounts” error
Use
mount -o rw,remount /
Ignoring Parcelable/RefBase inheritanceThis underestimates the heap allocation size of objects containing a RefCounterAnalyze the complete inheritance chain, including the vtable and RefCounter
Ignoring RS SurfaceNode/BufferQueueThis misses the largest individual memory cost, at 50–200 KB per groupAnalyze the RS resource creation path and distinguish
nullptr
from actual creation
Considering only
sizeof
during theoretical analysis and ignoring heap allocations
This misses heap allocations for long
std::string
values, vector elements, and map nodes
Analyze the actual memory layout of every field