ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Android性能测试自动化:Perfetto+AI诊断方案实践

Android性能测试自动化:Perfetto+AI诊断方案实践 最近我把 Perfetto 和 AI 放在一起在 Android 性能测试上搭了一套自动化诊断方案。过去要花半天才能看完的 trace 文件现在通过自动化采集、SQL 特征提取和 AI 归因半小时内能出一份相对靠谱的问题报告。这套方案解决的核心问题就是性能测试中“采集靠人工、分析靠经验、报告靠手写”的效率瓶颈。如果你也在做 Android 应用或者系统级性能测试或者正被启动慢、掉帧、周期性卡顿这类问题反复折磨这篇内容应该能给你一条能照着搭的路线。一个很现实的情况是很多团队并不是没有性能数据而是数据拿到之后不知道怎么高效使用。尤其是 Perfetto 抓出来的 trace 动辄几百 MB打开界面要加载半天火焰图点开一层又一层没有足够经验的人根本看不出门道。我搭这套自动化诊断方案本质上就是把“trace 采集、特征提取、归因建议”这条链路固化下来让 AI 先做一轮初筛人工再去验证和深挖。下面我把整个方案的选型思路、落地细节和踩坑过程都摊开讲。1. 为什么做这套方案性能测试的日常其实比想象中更磨人1.1 手动抓 Trace 的典型一天重复劳动占了大头先还原一个性能测试最典型的场景。测试人员拿到一台待测设备安装好版本连上 adb然后开始手动执行一系列操作打开应用、滑动列表、进入二级页面再退出。为了抓一帧掉帧问题往往要提前打开 systrace 或者 Perfetto录个 10 到 20 秒操作完再保存 trace。问题偶现的时候更痛苦一个场景要反复跑好几轮每个 trace 都要重新分析。这样一套流程下来真正用于思考“为什么卡”的时间可能不到三分之一大量时间都耗在采集、保存、打开、拖动时间轴这些事情上。如果只是偶尔做一两次性能专项手动流程还能接受。但性能问题一旦进入迭代回归阶段比如每次发版前都要把所有核心场景跑一遍手动方案的短板就非常明显。团队里真正能熟练看火焰图、能分清主线程忙碌和 Binder 等待的人本来就不多让他们每天对着几十个 trace 做重复劳动既浪费人力也很难保证分析标准的一致。不同人看同一份 trace给出的结论甚至可能完全不同。我最早想做的自动化其实就是把采集这一步先固定下来测试脚本启动Perfetto 就开始录测试脚本结束trace 自动保存并拉回服务端。这个事听起来简单但真正落地时牵扯到 TraceConfig 怎么写、什么时候采集、怎么保证文件不丢失、怎么跟用例执行时间对齐细节非常多。等采集的问题解决完我又发现新的瓶颈转移到了“分析”这步——trace 还是得有人打开看。1.2 为什么选 Perfetto它不只是新一代 systrace我用过挺长一段时间的 systrace后来切到 Perfetto 之后最明显的感受是它不只是一个升级版工具而是把性能数据的处理方式彻底换了一套。systrace 输出的是传统 HTML 报告能看但很难程序化处理。Perfetto 的核心理念是“数据源插件化 统一 trace 格式 SQL 化分析”其中 SQL 化分析这一点对我来说是关键中的关键。Perfetto 采集到的.perfetto-trace文件经过 trace_processor 解析后会变成一个 SQLite 数据库。进程表、线程表、slice 表、调度表、计数器表全都结构化管理。也就是说我可以不用再靠肉眼去界面里找某一段调用栈而是直接用 SQL 查询主线程最长耗时函数 top 20 是什么、某一帧为什么超过了 100 毫秒、哪个进程在持续抢占 CPU。这种能力天然适合自动化因为查询逻辑一旦写死每次拿到新 trace 都能用同一套规则去提取指标。这也是我选择 Perfetto 而不是自己开发埋点方案的原因。自定义埋点当然能拿到业务层面的精准信息但需要业务方配合打点而且很难拿到内核调度、CPU 频率这些底层数据。Perfetto 覆盖了从内核 ftrace 事件到用户态 slice 的完整链路拿到的是系统级视角很多问题不用一开始就猜是不是业务代码的问题而是先看系统资源分配和调度再定位到具体进程或方法。作为自动化诊断的数据底座Perfetto 的完整度和可编程性目前没有更好的替代方案。2. 方案整体设计与关键选型2.1 三层结构采集层、分析层、诊断报告层整个方案我拆成三层采集层、分析层、诊断报告层。采集层负责在设备上跑 Perfetto产出原始 trace分析层负责用 trace_processor 把 trace 转成可计算的 SQL 表再用一组预置查询提取关键指标诊断报告层则把分析层输出的结构化数据喂给 AI由 AI 给出归因判断和优化建议最后生成一份可读的报告。这样分层的好处是每一层都能独立替换和演进。比如说采集层今天用 Perfetto明天如果想把 trace 的采集能力扩展到云真机集群只需要改这一层的调度逻辑分析层如果发现某些 SQL 指标不够准确可以直接调整查询语句不影响前后两层诊断报告层如果换一个更强的模型或者要接入本地私有化部署的模型也只涉及这一层的接口调用。层与层之间通过标准的数据格式衔接这个边界必须拉清楚否则后面迭代会很痛苦谁都不敢动代码。我在最初的版本里其实把分析层和诊断报告层混在一起写过AI 那边既负责提指标又负责给结论结果模型的输出很不稳定。后来把“指标提取”这个动作完全交给确定性的 SQL 规则AI 只做归因和解释稳定性才上来。这个经验我觉得值得提前说AI 适合做综合判断但不适合做精确计算。2.2 为什么把 AI 放在“归因”环节而不是让它直接看图AI 在这个方案里到底承担什么角色我反复调整过几次。最开始我试过直接把.perfetto-trace文件的内容塞给大模型让模型告诉我问题在哪效果非常糟糕。原因有两个层面一是 token 限制一份 trace 解压成文本后可能有几百万行模型根本吃不进去二是 trace 里的原始事件非常琐碎绝大多数内容对定位某一个具体问题来说是噪声如果缺少特征提取这个步骤模型很容易被无关信息带偏。所以我把 AI 放在归因和解释这一环。也就是说先通过一组可解释的指标把“现象”明确下来主线程出现了一个 800 毫秒的耗时切片期间进程的 CPU 占用不高有一个很长的 Binder 等待。这些指标是我手工总结出的模式逻辑上是清楚的。AI 拿到这些指标组合后去判断它更符合哪种典型问题比如是主线程被 I/O 阻塞还是在等待远端进程响应还是因为内存抖动导致频繁 GC。这种用法把 AI 的长处放在“综合上下文做判断”上而不是让它去从原始数据里捞针。这样做还有一个附带好处AI 的输出是可以被人工复核的。因为输入的是结构化指标模型给出的根因能够对应到具体的 evidence。如果发现模型结论不对我可以回头检查是指标提取错了还是 prompt 引导有问题而不是面对一个黑盒输出无从下手。2.3 关于数据流的一点思考从 Trace 到指标再到结论整个诊断过程可以看成一条数据流水线原始 trace、关键指标、诊断结论每一层都在做降维和抽象。原始 trace 信息最全但噪声最大关键指标保留的是与问题强相关的信息片段诊断结论则是基于这些片段给出的可能性判断。每一层都应该保留向下追溯的能力否则 AI 说“主线程有长时间阻塞”但报告里看不到对应的数据和原始轨迹这份结论的可信度就大打折扣。我最终的实现里每份诊断报告都会附带三个东西执行场景名、AI 判断的根因、对应的指标证据列表。指标证据里会带上这条数据是从哪张 SQL 表查出来的原始值是多少。这样如果测试同学对结论有疑问可以顺着证据列表回到 trace 里做人工二次确认。我自己的体会是这种“可追溯”的设计比模型本身的准确率还重要。因为自动化诊断最大的风险不是模型偶尔说错而是错误结论被直接拿去拦截版本造成误杀。3. 采集层落地在自动化流程里稳定拿到高质量 Trace3.1 ADB 启动 Perfetto先写一份能跑的 TraceConfigPerfetto 的采集靠 TraceConfig 驱动它是一个 protobuf text 格式的配置文件描述要采集哪些数据源、 buffer 开多大、采多久。很多第一次用的人容易直接在命令行里敲perfetto -t 10s -o /data/misc/perfetto-traces/trace.perfetto-trace这样确实能抓到东西但抓到的事件类型很有限无法支撑后续的分析。我建议从一开始就维护一份自己的 TraceConfig按需开启数据源。下面这份是我在自动化测试场景里常用的配置核心思路是开启 ftrace 调度事件、进程内存统计、CPU 频率变化以及 Power 相关的事件。其中sched_switch用于看线程切换和阻塞原因sched_wakeup用于看唤醒源cpu_frequency用于判断是否降频。buffer 大小这里给了两个一个给主 buffer一个给内存统计用的辅 buffer。buffers { size_kb: 262144 fill_policy: RING_BUFFER } buffers { size_kb: 40960 fill_policy: RING_BUFFER } data_sources { config { name: linux.ftrace ftrace_config { ftrace_events: sched/sched_switch ftrace_events: sched/sched_wakeup ftrace_events: power/cpu_frequency ftrace_events: power/cpu_idle ftrace_events: sched/sched_process_exit } } } data_sources { config { name: android.process_stats } } data_sources { config { name: android.incremental_state } } duration_ms: 30000 write_into_file: true启动命令我习惯用 stdin 传配置的方式这样不需要先 push 文件到设备上adb shell perfetto -c - -o - --txt EOF trace.perfetto-trace buffers { size_kb: 262144 fill_policy: RING_BUFFER } data_sources { config { name: linux.ftrace ftrace_config { ftrace_events: sched/sched_switch ftrace_events: sched/sched_wakeup ftrace_events: power/cpu_frequency } } } duration_ms: 30000 write_into_file: true EOF-c -表示从标准输入读配置-o -表示把 trace 输出到标准输出然后在 PC 端重定向成文件。这种方式在 CI 环境里比较友好不用在设备上额外创建配置文件。需要注意Perfetto 的版本和 Android 系统版本相关不同版本支持的 TraceConfig 字段有差异配置写好之后先在小范围试跑一次看有没有报错或者字段不识别的问题。3.2 配合自动化用例什么时候开始采什么时候停自动化采集最容易犯的错是直接用固定duration_ms来控制采集时长。如果用例执行了 40 秒而配置里只写了 30 秒那么最后 10 秒的问题数据就完全没录到反过来如果用例只跑了 8 秒后面 22 秒录到的都是空数据会白白增加文件体积。所以我后来改成由测试框架主动控制 Perfetto 的启停用例开始前 5 秒启动采集用例结束并留出必要的余量后再停止。一个典型的时序是这样的先执行adb shell perfetto -c - -o /data/misc/perfetto-traces/trace.perfetto-trace --txt并让它后台运行等待 2 到 3 秒确认采集真的启动了然后开始跑 UI 用例用例结束之后等 1 秒让残留事件写完再执行adb shell killall perfetto触发正常终止最后用adb pull把 trace 拉到本地。整个流程可以用一个脚本包起来避免手动敲命令的环节。adb shell perfetto -c /data/local/tmp/trace_config.pbtxt -o /data/misc/perfetto-traces/trace.perfetto-trace --txt sleep 3 # 这里执行你的 UI 自动化测试脚本 python3 run_ui_test.py # 测试结束后停止采集并拉回文件 adb shell killall perfetto adb pull /data/misc/perfetto-traces/trace.perfetto-trace ./traces/scene_001.perfetto-trace这里有个小坑如果直接通过adb shell启动后台进程adb 断开时进程可能被一并杀掉。我试过几种方式比较稳的是在设备端加nohup或者通过setsid启动或者用adb shell perfetto ... 后不要让 adb 立即退出。再有就是测试机如果开了多个监控工具比如同时跑着 Perfetto、内存 profiler、GPU 监控很容易互相抢占资源导致 trace 里出现假阳性。我的建议是性能测试机上不要同时开多套采集工具一批用例跑一类监控。3.3 用 trace_processor 把 .perfetto-trace 变成 SQL 表拿到 trace 文件之后如果只会用官方界面看自动化还是没有闭环。Perfetto 提供了 trace_processor可以在本地执行trace_processor_shell trace.perfetto-trace进入一个 SQLite 交互环境但我更常用的是 Python API直接在脚本里执行查询并拿结果做进一步处理。安装方式很简单pip install perfetto即可然后用 TraceProcessor 加载 trace 文件。from perfetto.trace_processor import TraceProcessor tp TraceProcessor(file_pathtrace.perfetto-trace) res tp.query( SELECT t.name AS thread_name, s.name AS slice_name, CAST(s.dur AS REAL) / 1e6 AS dur_ms FROM slice s LEFT JOIN thread t USING (utid) WHERE s.dur 50_000_000 ORDER BY s.dur DESC LIMIT 20; ) for row in res: print(row)这段 SQL 的意思是在所有 slice 里找出时长大于 50 毫秒的切片并关联线程名按耗时排序。slice 表里存的是用户态的事件切片可以理解为函数或方法的执行区间。需要注意 ts 和 dur 的默认单位都是纳秒所以示例里把 dur 除以 1e6 转成了毫秒方便阅读。另一个常用表是process和thread分别存进程和线程的元信息sched表则记录线程在 CPU 上的调度区间用它可以分析线程是否有过长时间等待。我刚接触这套 SQL 表结构时最不适应的一点是表之间靠utid、upid、ts、dur这些 ID 和时间列关联不像业务数据库里直接用主键和业务名称关联。但只要把slice和thread、process这三张表的关系记住大部分查询都能写出来。分析层做指标提取本质就是写一堆这种带过滤条件的 SQL然后定时回归校验这些查询得到的数值是否符合人工观察。4. 分析层与 AI 诊断让模型看懂 Trace 数据4.1 为什么不能直接把原文件丢给大模型把整份 trace 直接丢给大模型这个念头我相信很多人在一开始都会动。毕竟现在大模型上下文窗口越来越大好像什么都能接。但实际试过就知道不现实一份 30 秒的 trace 展开成文本能达到几十万甚至上百万行而且里面的原始事件绝大多数是调度切换、频率更新这类底层日志模型如果要从中自己找问题等于让它在草垛里找针还要保证不找错。就算模型说出了一个看起来合理的结论你也很难判断它依据的是哪一段数据因为输入数据本身已经超出了人类的核查能力。所以我坚持先做特征提取让模型只看到一小部分高价值的信息。比如我不关心过去 30 秒里每毫秒的线程状态我只关心“主线程是否有一个超过 200 毫秒的 slice”“这个 slice 执行期间该县城是否长时间处于 Running 状态”“当时 Binder 线程是否有堆积”。这些特征由 SQL 查询直接产出数值准确、逻辑清晰模型拿到这些压缩后的信息才能给出有依据的分析。4.2 构建诊断摘要关键指标与事件文本的生产方法诊断摘要我一般分成几个模块环境信息、场景信息、核心性能指标、异常事件列表。环境信息包括设备型号、系统版本、应用版本场景信息就是当前测试在做什么比如“冷启动进入首页并滑动列表 30 秒”核心性能指标包括主线程慢函数 top N、Jank 次数、慢 Binder 调用 top N、进程内存占用等异常事件列表则是那些超过阈值、值得重点关注的切片和调度片段。下面这段代码展示了如何把 SQL 查询结果拼成一段适合 AI 阅读的文本。关键点在于每个指标块前面都加上一个明确的标签比如[SLOW_FUNCTIONS]、[JANK_INFO]、[BINDER_WAIT]。这样模型能快速定位不同信息的含义输出结构也更容易保持稳定。def build_ai_summary(tp, fname, scene): lines [] lines.append(f设备: {device_model}) lines.append(f系统: {android_version}) lines.append(f测试场景: {scene}) lines.append(ftrace 文件: {fname}) slowness tp.query( SELECT t.name AS thread_name, s.name AS slice_name, CAST(s.dur AS REAL) / 1e6 AS dur_ms FROM slice s LEFT JOIN thread t USING (utid) WHERE s.dur 100_000_000 ORDER BY s.dur DESC LIMIT 10; ) lines.append([SLOW_FUNCTIONS]) for row in slowness: lines.append(f- {row.thread_name}: {row.slice_name} {row.dur_ms:.1f}ms) binder tp.query( SELECT p.name AS process_name, s.name AS slice_name, CAST(s.dur AS REAL) / 1e6 AS dur_ms FROM slice s JOIN thread t USING (utid) JOIN process p USING (upid) WHERE s.name LIKE %binder% ORDER BY s.dur DESC LIMIT 10; ) lines.append([BINDER_WAIT]) for row in binder: lines.append(f- {row.process_name}: {row.slice_name} {row.dur_ms:.1f}ms) jank tp.query( SELECT COUNT(*) AS frames, SUM(CASE WHEN dur 100_000_000 THEN 1 ELSE 0 END) AS jank_frames FROM slice WHERE name Choreographer#doFrame; ) for row in jank: lines.append(f[JANK_INFO] total_frames{row.frames}, jank_frames{row.jank_frames}) return \n.join(lines)用文本摘要而不是 JSON 喂给模型我自己试下来效果更好。因为模型在解读自然语言段落时更顺手而 JSON 里有太多括号和引号模型反而容易在格式上出错。但要注意摘要里的每个数值来源必须是可复现的 SQL 查询而不是模型自己生成的东西。分析层的角色是“确定性计算”这一点不能动摇。4.3 Prompt 设计与结构化返回从“泛泛而谈”到可落地的建议Prompt 写得好不好直接影响 AI 诊断结果的质量。我最早用的 prompt 只是简单说“请分析这份性能数据并给出优化建议”结果模型给了一堆正确的废话比如“建议优化主线程耗时”“建议减少内存分配”看了等于没看。后来我改成强约束模式给模型一个明确的角色任务、输入字段说明、输出 JSON 格式约定并且要求它必须引用证据。我现在的 prompt 大致长这样你是一名 Android 性能优化工程师请根据以下 Perfetto trace 摘要信息定位性能问题根因。 要求 1. 只基于提供的字段做判断不要编造数据。 2. 输出 JSON不要输出其他内容。 3. JSON 结构: { is_problem: true/false, severity: low|medium|high, category: UI_JANK|IO_BLOCK|BINDER_WAIT|MEMORY|OTHER, root_cause: 一句话根因描述, evidence: [证据1, 证据2], suggestion: 具体可执行的优化建议 } 输入信息 SUMMARY {summary} /SUMMARY加上固定的 JSON schema 之后模型的输出至少是可解析的后续代码可以直接把结果映射成报告字段。如果能再配一个 few-shot 示例比如“如果 SLOW_FUNCTIONS 中主线程出现 800ms 的 view 绘制切片且 BINDER_WAIT 中没有明显慢调用则判断为 UI 层耗时问题”输出的专业度会再上一个台阶。还有一点要提醒性能数据可能涉及用户隐私或业务敏感信息尤其 trace 里可能包含应用内部类名、包名、甚至一些路径信息。如果模型走的是外部 API必须处理好脱敏或者干脆用私有化部署的模型。我们最后选型时考虑到数据合规和稳定性用的是本地部署的开源模型效果上对我来说完全够用。5. 自动化诊断的 CI 接入与落地效果5.1 串起整条链路一个可落地的执行脚本所有模块准备完之后最后要做的就是把它们串成一条流水线。我写了一个 Python 脚本接收场景名和测试用例名作为参数内部按顺序执行启动 Perfetto、跑自动化用例、停止采集、拉取 trace、用 trace_processor 解析提取摘要、调用 AI 接口、生成报告。def run_diagnose(serial, scene_name, cases): start_trace(serial) run_ui_test(cases) stop_and_pull_trace(serial, scene_name) tp TraceProcessor(file_pathftraces/{scene_name}.perfetto-trace) summary build_ai_summary(tp, f{scene_name}.perfetto-trace, scene_name) diagnosis call_ai_model(summary) report render_report(scene_name, diagnosis, summary) save_report(report, freports/{scene_name}.md) return diagnosis这个脚本最开始的版本跑得很不顺问题主要出在时序。比如 Perfetto 还没真正启动用例就开始跑了导致前几秒的数据丢失或者用例执行完毕但 trace 文件还没有刷新完成直接 pull 回来一个损坏文件。后来我在几个关键节点都加了等待和重试逻辑宁可慢一两秒也不能漏数据。尤其是start_trace这一步加了一个检查逻辑先执行adb shell perfetto --version确认设备端可执行文件存在再执行启动命令启动后主动 sleep 2 秒然后检查设备上的输出文件是否在增长确认没问题才继续跑用例。5.2 接入 CI 门禁什么情况下拦截版本流水线跑通之后下一步就是把诊断结果接进 CI 门禁。但不是每次提交都跑全套性能诊断那样成本太高也没必要。我目前的策略是在重要的集成分支上每逢关键性能场景的自动化用例跑完后自动触发诊断诊断结果里如果出现severityhigh且is_problemtrue则把这条构建标记为失败并在报告里附上根因和证据。这样相当于把性能问题当成和功能用例失败同等重要的拦截条件。为了防止偶发问题造成误杀我加了一个“连续三次才失败”的策略。也就是说单次诊断出现 high 级问题只记录告警连续三次同一场景同一类型的问题才真正拦截合并。这个策略来自我踩过的一个坑有一次设备后台正好在跑系统应用更新trace 里出现了明显的主线程阻塞AI 也准确判断出来了但问题并不是这次版本引入的如果直接拦截就会误伤。加上连续多次确认的机制之后稳定性好了很多。报告输出我用的是一份 Markdown 文件包含摘要信息和 AI 诊断结论并统一归档到流水线产物里。测试同学和研发同学在 MR 页面上点开就能看到不需要额外登录一套系统。5.3 跑了一段时间后的实际效果到底能省多少时间这套方案上线后我统计过一个小周期内的数据。之前一个性能专项从开始抓 trace 到人工分析完 5 个核心场景大约需要一个有经验的工程师半天时间现在自动化链路跑完单场景从采集到产出诊断报告只需要 10 分钟左右而且大部分时间是在等设备执行用例和分析模型返回人的参与时间大概只有 3 到 5 分钟主要用来确认报告结论是不是合理。必须承认AI 的结论不是每次都能一把定位到根因。我的体感是对于常见的 UI Jank、Binder 等待、主线程 I/O 阻塞这类问题准确率已经比较高但对于比较隐蔽的内存碎片化、底层驱动相关问题AI 给出的建议常常还需要人工去追。但就算这样它也已经把排查范围从“整份 trace”缩小到“某一类问题”这个收益已经很可观了。后续随着样本积累和 prompt 迭代准确率应该还能继续往上走。6. 踩坑清单与经验速查6.1 我遇到的典型问题与排查方法症状常见原因解决办法trace 文件只有 0 字节或明显过小TraceConfig 语法错误、设备权限不足、采集未真正启动先执行adb shell perfetto --version确认命令可用用--txt配置时检查字段拼写非 root 设备建议用 userdebug 版本trace 中前段数据缺失Perfetto 还没启动完成用例就开始执行启动后主动 sleep 2 到 3 秒并检查输出文件大小是否持续增长buffer 溢出中间数据被覆盖单次采集时间过长或 buffer 太小调大size_kb或者缩短单段采集时长改为分段录制SQL 查不到预期的 slice 数据TraceConfig 中未开启对应数据源检查是否有linux.ftrace数据源以及ftrace_events里是否包含目标事件AI 输出不稳定时好时坏摘要信息结构混乱或 prompt 约束不足固定摘要模块顺序增加 JSON schema 和 few-shot 示例必要时增加重试逻辑模型编造不存在的证据输入摘要中噪声太多模型“脑补”了因果关系强制要求模型只基于输入字段输出并明确“未命中不要猜测”同一问题被重复告警设备后台环境不稳定偶发因素被当成版本问题增加连续多次确认机制或设置问题排除名单这些坑里最隐蔽的是第二个。Perfetto 启动本身需要一点时间如果自动化脚本刚发出启动命令就立刻跑 UI 用例前几秒几乎必然是空的而且从 trace 表面上不容易看出来因为后面数据看起来都很正常。后来我在启动步骤做了文件增长检查这个问题才彻底解决。6.2 一些我从第一天起就建议养成的习惯第一个习惯是“先校准再自动化”。不要写完 SQL 提取逻辑就立刻全部交给 AI而是先拿过去已经人工分析过的 trace 样本跑一遍对比 AI 结论和人工结论是否一致找到偏差后调整指标口径和 prompt。我大概用了两周时间来回校准之后产出的报告才比较能看。第二个习惯是“trace 文件一定要归档”。自动化诊断跑多了之后你会发现 trace 文件本身才是最有价值的资产。AI 的结论可以重新生成SQL 查询也可以反复执行但原始 trace 如果被覆盖了后面想再挖新特征就完全没有依据。我在 CI 产物里设置了按日期归档的规则trace 文件默认保留 30 天关键版本的 trace 单独标记长期保留。第三个习惯是“保持环境干净”。性能测试对设备状态特别敏感后台有 App 在更新、系统在做 dex2oat、充电电流不稳都会直接影响结果。我们专门准备了两台测试机一台连着充电器但电量稳定在 80% 以上一台用独立电源平时不装任何无关应用。这样采集到的 trace 噪声小很多AI 分析的准确率也相应提高。最后说一点我自己的体会。这套方案最花时间的不是写 prompt也不是调模型而是把 Perfetto 采集和对齐自动化测试场景这个基础做稳。AI 在这里其实更像一个读数据很快的初级分析师它能给出什么质量的结论取决于你喂给它的输入结构化程度。别指望第一天就全自动拦截所有性能劣化先让链路跑起来把每次 AI 判断和人工结论做对比攒一批样本指标口径和提示词自然会越来越准。这个方向后续还能继续扩展比如增加更多 trace 类型、做版本间性能趋势对比但底层逻辑始终是同一句话采集标准化、分析特征化、结论可追溯。
返回列表