资讯动态

Perfetto:用 3 条 trace SQL 查询查清启动卡顿、内存泄漏与掉帧

发布时间:2026/9/24 13:54:13 来源:尧图企业网站定制
Perfetto用 3 条 trace SQL 查询查清启动卡顿、内存泄漏与掉帧【免费下载链接】perfettoProduction-grade client-side tracing, profiling, and analysis for complex software systems.项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto冷启动比基线多 1.9 秒、RSS 十分钟涨 200MB、重场景掉到 40fps——这些症状都能靠 trace 分析回答前提是你会查。本文用 Perfetto 的 trace 分析跑一遍完整路径先保证配置抓对再用 CPU 切片、heapprofd 内存剖析、GPU 计数器三条 trace SQL 查询分别定位启动瓶颈、真泄漏和掉帧归因。先把 trace 抓对配置错了后面全白查。三条硬要求缓冲区开 RING_BUFFER。默认线性缓冲在长 trace 下会溢出丢尾部环形缓冲永远保住最近的数据开 sched 切换和 cpu_frequency 计数器。millicycles 的计算依赖每段的 CPU 频率缺了这个事件下面所有切片查询的循环数全是 NULLheapprofd 单独挂一块 buffer采样间隔决定能解析到多细的调用点。buffers { size_kb: 16384 fill_policy: RING_BUFFER } data_sources { config { name: linux.perf target_buffer: 0 perf_config { timebase { frequency: 997 } callstack_sampling { user_frames: true kernel_frames: true // 内核栈留着否则分不清切片在跑还是在等 } } } } data_sources { config { name: linux.heapprofd heapprofd_config { sampling_interval_bytes: 2048 shmem_size_bytes: 16777216 block_client: true } target_buffers { size_kb: 8192 } } }为什么这么取值997Hz 避开整百频率防止采样点和系统时钟对齐造成偏采2048 字节的采样间隔比 4096 更细小分配点也能被记录代价是 trace 体积翻倍。跑完先在 UI 里确认 perf 火焰图和堆曲线都存在再进入查询环节。抓取过程的界面长这样调查一启动时间花在哪现象相机应用冷启动约 4.2 秒比基线慢 1.9 秒但主线程看不到哪个单一函数卡住——时间是蒸发掉的。先跑这条查询启动窗口内每个 CPU 切片烧掉了多少真实循环数。注意它查的是 millicycles纳周期而不是 duration——duration 长不代表烧得多低主频下跑 50ms 可能比高主频下 5ms 还便宜。INCLUDE PERFETTO MODULE linux.cpu.utilization.slice; SELECT name AS op_name, thread_name AS owner_thread, process_name AS proc, millicycles, megacycles FROM cpu_cycles_per_thread_slice WHERE process_name com.google.android.GoogleCamera AND ts BETWEEN :boot_start AND :boot_end ORDER BY megacycles DESC LIMIT 15;结果怎么读按 megacycles 倒排头部就是真瓶颈。本例 Top 3 依次是类加载器命名空间创建约 370M 微周期、libframework-connectivity-jni.so的 dlopen 与 JNI 注册、线程池批量预创建。判断依据370M 微周期约占总启动循环数的 11%且全部压在首帧关键路径上JNI 库加载约 190M 微周期其中一半发生在主线程。优化方向把 dlopen 挪到首帧之后的后台线程命名空间创建做延迟初始化线程池按需扩容。动作落到代码就是改 Application 初始化序列而不是全局再优化一下。该视图的表定义在 src/trace_processor/perfetto_sql/stdlib/linux/cpu/utilization/slice.sql注释写明了 millicycles 为 NULL 的含义。调查二内存是不是真漏了现象后台长驻十分钟RSS 从 310MB 涨到 510MB之后不再陡增但仍在缓慢爬坡。陡增段更像一次性缓存填充缓坡段才像泄漏。先跑这条查询按调用点聚合 heapprofd 的净存活字节。heap_profile_allocation 里 free 记为负值SUM 之后正值即仍被持有的量——谁持有最多谁先查。SELECT p.name AS proc, h.heap_name, h.callsite_id, SUM(h.size) AS live_bytes, SUM(h.count) AS live_allocs FROM heap_profile_allocation h JOIN process p ON p.id h.upid WHERE p.name com.example.demo GROUP BY p.name, h.heap_name, h.callsite_id ORDER BY live_bytes DESC LIMIT 10;拿到头部 callsite_id 后还要区分泄漏与缓存。判断依据同一个调用点trace 前段和后段的持有量对比——随操作次数单调上涨是泄漏涨到某值后进入平台期是池或缓存不是 bug。下面这条查询把同一个 callsite 在两个时间窗的净持有量并排放出来近似口径足够定性SELECT h.callsite_id, SUM(CASE WHEN h.ts :t_split THEN h.size END) AS early_live, SUM(CASE WHEN h.ts :t_split THEN h.size END) AS late_live FROM heap_profile_allocation h JOIN process p ON p.id h.upid WHERE p.name com.example.demo AND h.callsite_id IN (14, 87, 231) -- 上一步查出的头部调用点 GROUP BY h.callsite_id;结果怎么读callsite 14 的 early_live 约 6MB、late_live 约 48MB 且斜率不收敛——泄漏对应栈顶是渲染线程的纹理缓存路径补一条释放/失效逻辑即可callsite 87 涨到 90MB 后持平——这是对象池加容量上限和淘汰策略不动代码结构。⚠️ 误区提示只跑第一条聚合查询就动手改代码是最常见的误伤——把缓存当泄漏清掉线上会表现为频繁重建、卡顿反弹。调查三掉帧卡不卡在 GPU现象重场景 FPS 从 60 掉到 35–45同期 CPU 利用率只有 40%。主线程不忙、帧还在丢第一嫌疑是 GPU 侧排队。先跑这条查询整个采样期每类 GPU 计数器GPU 计数器由gpu.counters数据源产出的分布看哪个环节顶格。SELECT t.name AS counter, MIN(c.value) AS lo, AVG(c.value) AS avg_v, MAX(c.value) AS hi FROM counter c JOIN gpu_counter_track t ON t.id c.track_id WHERE t.name GLOB %Utilized% OR t.name GLOB %Busy% GROUP BY t.name ORDER BY avg_v DESC LIMIT 10;结果怎么读看均值贴近上限的计数器而不是看单帧尖峰。按 Adreno 风格命名给出判断线计数器判断线处置% Fragment Capacity Utilized均值 95%fill rate 瓶颈降渲染分辨率、治理 overdraw% Texture Capacity Utilized均值 85%合图、缩小 atlas、降各向异性倍数% Geometry Capacity Utilized均值 90%简化网格、合并 draw call优化方向分两种走向有计数器贴顶说明 GPU-bound去动 shader 和资源CPU 侧怎么压线程都无效所有计数器均值都低但帧还在丢说明卡点在队列同步CPU 提交不及时或等 vsync要回到 sched 轨道查主线程与渲染线程的等待段而不是继续调图形。本例 % Texture Capacity Utilized 均值 91%把三张大 atlas 各砍一半后帧率回到 57 并稳定。一套可复用的排查清单先定窗口只抓覆盖故障的 5–10 秒别盲目录长 trace。验数据确认 sched、cpu_frequency、ftrace 事件都在否则 millicycles 为 NULL。CPU 侧按 megacycles 倒排 Top 15 切片圈出关键路径上的大消耗者。内存侧callsite 聚合 前后时间窗对比先定性再修。GPU 侧看计数器均值是否贴顶决定调图形还是调提交时序。常见误区⚠️ 把缓存当泄漏曲线先陡增后进平台期是池的正常形态泄漏的特征是斜率随操作次数不收敛。定性前别改代码。只采用户态漏掉内核栈kernel_frames 关掉后火焰图顶部全是用户态函数你分不清一段切片是在真跑还是在等锁、等调度——等待段的根因全在内核侧。用 duration 代替循环数主频波动时两个 5ms 的切片成本可能差 3 倍。duration 会骗人cycles 不会频率计数器不抓这个对比就永远做不了。收尾启动瓶颈、真泄漏、掉帧归因本质都是同一件事在正确的时间窗口里用循环数、净存活字节、计数器均值三个量把感觉慢变成可比较的数字。查询写不准的时候去 docs/analysis/trace-processor.md 对照内置表结构或者直接在 UI 的查询窗口里验证。【免费下载链接】perfettoProduction-grade client-side tracing, profiling, and analysis for complex software systems.项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价