Perfetto heapprofd 深度指南:从采样原理到火焰图与 SQL 分析
Perfetto heapprofd 深度指南从采样原理到火焰图与 SQL 分析【免费下载链接】perfettoProduction-grade client-side tracing, profiling, and analysis for complex software systems.项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto进程 RSS 持续爬升heap dump 里却找不到大对象这时你需要把内存占用归因到具体调用栈callstack。Perfetto 的 heapprofd 干的就是这件事hook 住malloc/free按采样间隔记录谁分配了多少、有没有释放最终变成可查询的火焰图。本文覆盖从一条命令跑通到采样机制、SQL 后处理与排错的完整闭环。五分钟跑通一条命令拿到调用栈火焰图最省事的路径是tools/heap_profile脚本它会替你完成配置下发、profiling、拉回 trace、符号化、转 pprof 全套动作tools/heap_profile android -n com.example.myapp按包名指定目标profiling 持续到按CtrlC为止。结束后输出Wrote profiles to /tmp/heap_profile-XXXXXX (symlink /tmp/heap_profile-latest) The raw-trace and heap_dump.* (pprof) files can be visualized with https://ui.perfetto.dev.把raw-trace拖进 Perfetto UI在进程轨道找到 Native heap profile 轨道点击 slice 即弹出火焰图。上面这条命令等于sampling_interval_bytes: 4096默认采样间隔 按名称匹配 运行中与新启动进程都分析 block_client模式。常用参数速查完整清单以脚本--help为准参数类型 / 取值作用备注-n/--name逗号分隔的进程名按名称匹配同时覆盖运行中与新启动进程-p/--pid逗号分隔的 PID按 PID 匹配用于 watermark 触发或本地调试-i/--intervalint默认 4096采样间隔字节设 1 即全量记录-c/--continuous-dumpintms周期转储间隔0 为禁用-d/--durationintmsprofiling 时长0 为持续到 CtrlC--heaps逗号分隔如com.android.art指定要采样的堆仅 Android 12--shmem-sizeint字节共享内存缓冲大小须为 4096 的倍数、≥8192、2 的幂--no-startup/--no-runningflag只匹配已运行 / 只匹配新启动进程Android 11二者互斥--print-configflag只打印生成的 trace config调试配置时有用不想走脚本时也可以在 trace config 里手写HeapprofdConfig完整字段见 heapprofd 配置定义好处是能与其他数据源CPU、调度、日志并行采集data_sources { config { name: android.heapprofd heapprofd_config { sampling_interval_bytes: 4096 process_cmdline: com.example.myapp } } }效果与上面第一条命令等价每 4096 字节采样一次按名称匹配目标。机制拆解采样、缓冲、转储时机分别发生了什么采样把分配流按 1/n 抽稀Heapprofd hookmalloc/free与 C 的operator new/delete间隔n字节意味着平均每分配n字节记录一次从而压低对目标进程的性能开销。可以想象成一条 1 字节的分配流每个字节以 1/n 概率被选中被选中的字节记上完整的n字节——期望上总量是准的但单条记录有噪声。大于采样间隔的分配绕过采样逻辑按真实大小记录大对象不会低估。sampling_interval_bytes必须为非零值否则 producer 不启动设 1 即全量精度。类比每 4096 笔流水抽查一笔并放大 4096 倍月底对账时总量吻合但单月波动看不准。缓冲shmem 是分配与消费之间的蓄水池目标进程把分配样本写进客户端与 heapprofd 之间的共享内存缓冲shmem_size_bytes默认 8 MiB超过 500 MiB 会被截断heapprofd 再异步消费。缓冲写满且block_client: true脚本默认时客户端阻塞等待空间分配不丢但目标进程明显变慢可配block_client_timeout_us设超时Android 11须 100us。--no-block-client则相反缓冲满时直接提前结束会话用丢数据换目标进程速度。proto 里还有自适应采样剩余空间低于adaptive_sampling_shmem_threshold时采样间隔翻倍封顶adaptive_sampling_max_sampling_interval_bytes两者为 0 时禁用。原理细节见 heapprofd 采样设计文档。转储三个写入 trace 的时机堆快照在三个时机写入 trace会话结束时默认必然发生配置了continuous_dump_config时按周期dump_phase_ms首转储延迟、dump_interval_ms间隔随时手动触发对所有正在分析的进程做一次快照adb shell killall -USR1 heapprofd手动转储叠加在会话结束转储之上可多次触发——适合让目标处于某个特定状态此刻留个底的实验。启动分析与运行时分析的生效时机差按名称而非 PID指定目标时新启动且匹配的进程从启动起就被分析。Android 上应用并非从零exec()而是 zygote fork 后特化specialize匹配时分析在特化阶段启用所以 profile 覆盖特化点 → 会话结束特化最早的一小段分配不计入。按名称或 PID 请求时已有进程会被立即枚举并通知但分析要等下一次分配发生后才真正生效——目标若当时空闲随后的分配突发可能漏掉。另一个硬边界同一目标被多个会话指定时只有第一个会话能分析其余会话的ProcessHeapSamples内容为空且rejected_concurrent为 true转换 pprof 时会报进程已被分析过。数据模型分配数据落在哪四张表所有数据经 Trace Processor 入库堆内存分配调用栈查询就围绕这四张表表内容heap_profile_allocation分配事件callsite_id、size释放为负、count、ts、upidstack_profile_mapping二进制/库build_id、namestack_profile_frame帧name、rel_pcstack_profile_callsite调用点parent_id构成调用树heap_profile_allocation在 views.sql 中是对 intrinsic 表__intrinsic_heap_profile_allocation的视图封装离线符号化数据在stack_profile_symbol。组合进阶host 模式 连续转储 SQL 后处理三个能力串起来就是完整的本地二进制 → 周期快照 → 累计归因工作流。第一步host 子命令Perfetto v58仅 Linux让非 Android 进程也能被分析tools/heap_profile host -c 5000 -- ./my_binary --some-flag脚本自动下载tracebox与libheapprofd_glibc_preload.so到~/.local/share/perfetto/prebuilts/通过tracebox --system-sockets拉起内置traced再以LD_PRELOAD启动目标。关键在于它设置了PERFETTO_HEAPPROFD_BLOCKING_INIT1默认 heapprofd 懒初始化、不阻塞主线程会漏掉启动阶段分配该变量让第一次malloc阻塞到挂接完成每个分配都不丢——这正是 host 场景想要的从进程出生就开始采。-c 5000使时间线每 5 秒出现一个 snapshot slice。平台预编译产物缺失时可从源码构建后用--preload-library out/linux_clang_release/libheapprofd_glibc_preload.so传入构建步骤见 构建说明。第二步验证配置加--print-config可先看到完整 trace config 而不实际执行例如heapprofd_config { shmem_size_bytes: 8388608 sampling_interval_bytes: 4096 process_cmdline: my_binary block_client: true continuous_dump_config { dump_phase_ms: 0 dump_interval_ms: 5000 } }第三步SQL 后处理。火焰图看形状SQL 看数字。对raw-trace运行标准库模块一步得到按函数累积的未释放量INCLUDE PERFETTO MODULE android.memory.heap_profile.summary_tree; SELECT name, -- 该调用栈中帧的函数名 mapping_name AS map_name, -- 原生二进制、库、JAR 或 APK cumulative_size -- 函数出现在栈任意位置时分配且未释放的字节数 FROM android_heap_profile_summary_tree ORDER BY abs(cumulative_size) DESC;模块实现在 summary_tree.sql先按 callsite 聚合self_size/self_alloc_size再沿parent_id向上累加出cumulative_size。同目录的intervals.sql支持按时间窗口聚合。这样组合的理由火焰图回答分配发生在哪条路径summary_tree 回答谁累计持有最多且没还两者互证。数据解读四种火焰图视图与 RSS 的口径差图中是 UI 火焰图视图顶部下拉可切换四种指标重点关注左侧轨道选中的 sliceUnreleased malloc size该栈分配但未释放的字节数排查泄漏的主视图Total malloc size该栈分配的字节总数含已配对释放Unreleased / Total malloc count对应的次数版本。对 ART 分配采样churn profiling则只有两个视图Total allocation size / count——只记对象创建时的栈不跟踪 GC 回收用于判断大额分配应归因于哪段代码。 分析应用时给过滤器加Hide Frame: libart.so滤掉 ART 运行时内部帧火焰图聚焦业务代码如上图顶部。UI 录制页#!/record/memory把同样的参数做成了面板目标进程、采样间隔Sampling interval、转储周期Continuous dump interval、共享内存缓冲Shared memory buffer须 4096 的倍数且为 2 的幂与 Block client 开关。native heap profiling 怎么配到这里勾选即可不用手写 config。别拿错尺子。heapprofd、malloc_info、RSS 三者口径不同维度heapprofdmalloc_infoRSS含义程序向默认分配器请求的字节数分配器自报userdebug 下am dumpheap -m PID /data/local/tmp/heap.txt触发分配器向 OS 拿到的内存dumpsys meminfo的 Private Dirtyzygote 初始化前的分配✗✓✓线程缓存jemalloc thread cache✗✓✓页对齐与碎片化✗✗✓ZRAM 换出不受影响不受影响可能小于前两者从 zygote 启动的 Java 应用早期初始化分配对 heapprofd 不可见所以 heapprofd 数字系统性偏小属正常反过来高 RSS 而 heapprofd 对不上时要怀疑分配器碎片化。排错链路从症状到解法Profile 为空→ 目标无资格被分析 → 查下文版本/构建类型表确认 manifest 带debuggable或profileable android:shelltrue/放在application段内。会话提前结束buffer overrun→ 分配速率超过 heapprofd 消费能力 → 若为短暂尖峰加大--shmem-size否则加大-i如--interval16000牺牲精度。调用栈逻辑上不可能→ DEDUPED 帧或 ICF 链接 → 方法名含[DEDUPED]说明多方法共享同一份代码显示的不一定是实际调用的方法若链接器传过-Wl,--icf...平凡函数常见是构造/析构函数会被别名到不相干类的等价函数。符号显示为原始地址 / could not find library / 只有一帧→ 离线符号化问题 → 运行trace_processor bundle生成增强归档或走PERFETTO_BINARY_PATH/PERFETTO_PROGUARD_MAP传统方式详见 符号化文档。提示进程已被分析过→ 存在并发会话 →adb shell killall perfetto清掉残留后重试。平台与版本限制heapprofd 要求 Android 10Java 分配分析要求 Android 12能力 / 限制版本边界heapprofd 基本功能Android 10heaps/all_heaps/target_installed_byJava 分配分析Android 12no_startup/no_running/dump_at_max/min_anonymous_memory_kbAndroid 11process_cmdline的*通配符仅匹配运行中进程需同时设no_startupPerfetto v57tools/heap_profile host子命令Perfetto v58仅 Linuxuser 构建可分析范围仅debuggable/profileable应用Android 10/11 的sampling_interval_bytes: 0会崩溃目标进程非法配置Android 11 64 位设备分析 32 位程序不支持Android 10 x86 / x86_64含 Cuttlefish不支持⚠️all: true分析全部合格进程在未修改的 userdebug 构建上会引发系统崩溃zygote 启动新进程时遇到意外的 heapprofd socket。收尾本文覆盖的工作流边界heap_profile脚本 / UI 录制 / 手写 config 三种启动方式采样与 shmem 缓冲机制启动 vs 运行时分析、连续与手动转储四张堆 profile 表与 summary_tree SQL 后处理host 模式本地分析以及按症状检索的排错链。延伸阅读heapprofd 完整文档、采样设计文档、heap_profile CLI 参考。【免费下载链接】perfettoProduction-grade client-side tracing, profiling, and analysis for complex software systems.项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考