诊断应用自定义资源的性能问题(OSDI 2026)
原题:Diagnosing Performance Issues in Application-Defined Resources
一句话总结:gigiprofiler 把 buffer pool、UNDO、query cache 等应用内部资源的行为统一成 WAIT、ACQUIRE、USE、RELEASE 四类事件,再让 LLM 找候选、静态分析和运行时证据排除误报;在五个大型系统的 15 个已知问题中,它都把真正根因排在第一位,还找到了两个获开发者确认并修复的 MariaDB 问题。
问题与动机
传统性能分析器看到的是 CPU、内存、锁和函数热点,却不知道应用自己定义的资源是什么意思。例如,MySQL 的 buffer pool 在操作系统看来只是早已分配好的内存;当一个长事务制造大量 UNDO,后台 purge 又占满 buffer pool 时,受害查询会因为换页和 I/O 变慢,但 CPU profiler 只能看到受害查询的慢路径,难以找到更早的资源占用者。
作者先人工研究 MySQL、PostgreSQL 和 Elasticsearch 的 45 个真实问题,涉及 38 种应用资源。根因包括资源争用(26.7%)、低效策略(22.2%)、分配不足(20.0%)、状态不一致(15.6%)、无界增长(8.9%)和泄漏(6.7%)。其中 39/45,也就是 87%,可以用资源的“等待—获得—使用—释放”过程解释。这项观察给出了统一接口,但剩余 13% 也明确说明:复杂策略本身不能被四类事件完整表达。
真正困难的是自动找到这些事件。资源语义藏在类名、变量名、注释和跨函数的数据流里,只靠传统静态分析很难知道某个对象代表“buffer slot”还是普通指针;只靠 LLM 又会把看起来像资源操作的代码大量误判。gigiprofiler 因此把二者分工:LLM 负责语义召回,程序分析负责检查代码行为,运行时轨迹再验证事件是否真的形成合理生命周期。
关键观察 / 隐含假设
- 观察 1:多数问题可以投影为四种资源事件。 WAIT 表示获得资源前进入慢路径,ACQUIRE/RELEASE 表示持有量变化,USE 表示任务实际消费资源。45 个问题中有 87% 符合这个模型(表 1)。
- 隐含假设:数量、对象 ID 和任务 ID 足以近似资源所有权;资源不会在工具看不见的线程、进程或远端节点间转移。
- 失效场景:复杂优先级、概率缓存、异步清理和跨资源联合策略可能没有清楚的 acquire/release 边界。
- 观察 2:LLM 和静态分析的错误方式互补。 LLM 能读懂名字与注释,却会“看起来合理就算”;静态分析不理解业务含义,却能验证返回值、控制流、释放后使用等具体行为。五种较新模型单独使用时,候选事件的误报率仍约为 45%–60%(图 12)。
- 隐含假设:源码中还保留足够的命名、类型或注释线索,并且项目可以被 LLVM 工具链分析和插桩。
- 观察 3:诊断不必要求每个事件都识别正确。 工具用等待时间、使用占比、持有时间和获得速率等聚合统计判断异常,而不是匹配一条必须完整的固定序列。因此少量漏报或误报不一定破坏最终排序。
- 隐含假设:复现负载确实触发问题,而且同一任务的事件能被可靠关联;否则聚合关系可能只是相关而非因果。
- 观察 4:应用资源问题经常跨任务传播。 占用资源的任务和最后变慢的任务可能不是同一个。诊断必须同时找“受害者”和“责任者”,不能只沿热点调用栈向上看。
核心方法
1. 从源码中找资源与事件
离线分析器把源码整理成受限的有向无环层次:文件 → class/struct → 成员变量 → 函数 → 具体代码。LLM 先在较高层识别可能的独占或共享资源,再进入相关函数判断 WAIT、ACQUIRE、USE、RELEASE 候选,而不是把整个百万行项目一次塞进上下文。这种两阶段遍历减少成本,也限制了搜索范围。
候选之后必须经过静态验证。例如,ACQUIRE 候选需要把资源值向返回值或后续查找传播;WAIT 还需要存在由运行时条件控制的慢路径;RELEASE 要能证明释放效果,并检查释放后是否继续使用;USE 则先确认代码触碰资源,再留给动态阶段验证。论文明确说这套分析既不完备也不可靠证明程序正确性,它只是验证 LLM 提出的候选。
2. 低开销记录运行时交互
LLVM pass 在确认的位置自动插入探针,不要求开发者手写注解。每条事件记录时间、线程、任务、资源、事件参数和值采样,线程本地 ring buffer 再异步刷盘;工具还可合并 perf 的调用栈与硬件计数器样本。运行后处理会删除从未形成合理 ACQUIRE→USE→RELEASE 关系的候选,也会检查持有者与等待者是否真正共享同一资源。
3. 用聚合模式诊断并定位代码
诊断器计算四组量:WAIT 的总等待时间、USE/ACQUIRE 的利用率、ACQUIRE 到 RELEASE 的持有时间,以及单位时间的获得量。不同组合对应资源争用、策略低效、容量不足、泄漏或无界增长等故障模式。随后,工具把受害任务连接到占用资源的责任任务,再用静态反向控制流和数据流把异常值追到源码位置。
论文中的 MySQL 例子展示了完整因果链:长事务反复写入,使 UNDO 的 ACQUIRE−RELEASE 差持续增长;purge 线程随后大量 USE buffer pool;其他查询进入换页和 I/O 慢路径,QPS 从约 700 跌到接近 0。单看最后的热点无法发现最初的长事务,但两种资源和两个任务的轨迹可以把链条接起来。
设计取舍
- 用统一模型换覆盖范围:四事件模型简单且能覆盖 87% 的研究案例,但无法直接解释剩余复杂策略问题。
- 用混合分析换自动化:LLM 提高语义召回,静态和动态验证压低误报;代价是需要模型调用、源码、可构建的 LLVM IR 和一次离线准备过程。
- 用全量事件换因果分辨率:实验默认记录每次事件,能看到跨任务关系,但高频资源会增加运行和轨迹开销。论文只提到可进一步 sampling/batching,没有评估采样后的诊断退化。
- 用复现时分析换线上常驻成本:静态结果可按应用版本复用,动态追踪只在能观察到问题时打开。它更像按需诊断工具,而不是已经证明可以长期常驻生产的监控器。
- 适用边界:最适合有源码、C/C++、任务边界清楚且问题可复现的服务器程序。异步 actor、跨进程队列、JIT 代码和分布式资源并未被验证。
实验与结果
- 问题诊断:五个被测系统最多约 200 万行源码;主实验使用 GPT-4o,服务器为 10 核 Intel Xeon E5-2640、64 GB DRAM、480 GB SSD 和 Ubuntu 20.04。作者在 MySQL、MariaDB、PostgreSQL、Apache HTTP Server 和 llama.cpp 上复现 15 个真实问题;gigiprofiler 对 15/15 都检测成功并把真实根因排在第一,三个多根因案例中的全部真实原因也排在无关候选之前。得到轨迹后,检测和诊断平均用 95.15 秒(表 3–4)。
- 与已有工具比较:
perf只检测到 5/15;PCatch+SyncFinder 检测到 5/15;Perspect+SyncFinder 检测到 4/15。这里对 Perspect 的比较并不完全公平:Perspect 需要正常和异常两条轨迹做差,而实验只给了异常轨迹,因为 gigiprofiler 本身不需要正常运行作为基线(表 4)。 - 新问题:工具找到两个此前未知的 MariaDB 问题。MDEV-34989 是空表
SELECT持有TABLE_SHARE元数据过久而阻塞INSERT;MDEV-34836 是 Galera 读查询访问子表时仍不必要地持有父表,阻塞 DDL。两者都获开发者确认并修复(§4.2)。 - 事件精度与消融:五个项目中,最终事件误报率最高为 MySQL 的 13.6%;单用不同 LLM 时约为 45%–60%。按论文口径,静态验证让误报率平均再降低 22%,运行后验证再降低 24%(图 11–13)。后一个数字只统计测试负载实际触发的事件,和全体静态候选的分母并不完全相同。
- 事件覆盖:MySQL 交叉检查中,gigiprofiler 找到 154 个事件,漏掉 PCatch 发现的 23 个,漏报率为 13.0%;PCatch 则漏掉约 100 个 gigiprofiler 发现的事件,漏报率为 66.7%。在更严格的 buffer-pool 人工真值集上,五种 LLM 平均漏掉约 23% 的事件和 12.1% 的函数(表 5)。
- 运行开销:能稳定测吞吐的 9/15 个案例中,平均吞吐损失为 3.7%,最高 7.8%(图 14)。正文和图中的 c1–c9 都表明这里是 9 个案例,图注写成“6 cases”应是笔误。另外六个没有进入该统计:三个会令 CPU 挂住,一个泄漏案例没有可比吞吐变化,两个 Web 案例缺少客户端插桩。因此这不是全部 15 个问题上的统一开销结果;论文也没有给出最终完整源码扫描的时间和模型费用。
论断—证据表
| 论断 | 论文证据 | 证据边界 | 置信度 |
|---|---|---|---|
| 四事件抽象能表达大多数已观察到的应用资源问题 | 表 1:45 个问题中 39 个适配;表 4:15/15 复现案例根因排名第一 | 人工收集的问题,且诊断集是可复现、属于工具范围的案例 | 强 |
| LLM 后接静态和动态验证能显著压低事件误报 | 图 11–13:LLM 单独误报约 45%–60%,两层验证继续下降 | 模型、项目和触发负载有限;运行后统计分母不同 | 强 |
| 工具不只会重现已知答案,还能发现新问题 | 两个 MariaDB 问题获开发者确认并修复 | 只有一个项目、两个案例,没有盲测 | 中 |
| 全量追踪的运行代价较低 | 图 14:九个可测案例平均 3.7%,最高 7.8% | 不是全部案例,也不是长期生产负载 | 中 |
| 聚合诊断可容忍事件漏报 | buffer-pool 真值集平均漏约 23% 事件,但 15 个案例仍全部诊断正确 | 没有系统扫描“漏多少开始失败”的敏感性曲线 | 中 |
批判性分析
论证链条
论文的链条很完整:先用 45 个问题说明应用资源是一类真实且普遍的盲区,再提出四事件抽象,随后用 LLM、静态验证、动态验证逐级缩小候选,最后把资源异常连接到责任任务和代码。15/15 的结果说明这条链在所选案例上有效,但不能直接解释为“对任意性能问题都有 100% 召回率”;测试集本来就由可复现、适合该抽象的问题组成。
假设压力测试
若任务跨线程池、callback、消息队列或远端服务传播,线程和任务标识可能接不上;后台 compaction 之类工作也不一定有自然的请求 ID。ACQUIRE−RELEASE 长期为正可能是正常 cache warm-up,而不是泄漏;高 USE 占比也可能只是热点数据而不是不公平占用。遇到这类情况,工具需要 workload 阶段、策略状态和更强的因果标识,四类事件本身不够。
实验可信度
五个成熟项目、多个故障类别、三种传统基线、多模型消融、人工事件真值和两个新 bug,使证据比只做小程序实验扎实。不过,Perspect 的输入条件不匹配;95.15 秒只覆盖拿到轨迹后的分析,不含 LLM 扫码、编译、插桩和复现准备;开销只统计九个案例。论文也没有在“没有已知问题”的长时间运行中测误报警告数量,或让不知答案的开发者盲测修复时间。
系统性缺陷
源码版本变化会使 LLM 候选、静态分析结果和探针位置失效,需要重新生成并审计。LLM 读取私有源码还带来模型部署、数据治理和结果可重复性问题。全量事件流可能丢 ring-buffer 数据,但论文没有给 trace drop 对根因排序的影响。最重要的是,系统依赖能够稳定复现问题;一次性、低频或跨机器问题可能在打开追踪后不再出现。
局限与后续工作
- 局限 1:四事件模型只覆盖经验研究中的 87%,复杂策略和多资源联合决策仍需专门语义。
- 局限 2:严格真值集仍平均漏约 23% 事件,正确结果依赖关键路径没有一起漏掉。
- 局限 3:未覆盖动态语言、无源码组件、跨进程/跨节点资源,以及长期生产追踪。
- 后续工作 1:在异步和分布式服务器中传播因果任务 ID,报告跨边界误归因率,而不只报告最终 top-1。
- 后续工作 2:系统评估 sampling、batching 和 ring-buffer 丢失,在 1% 以内开销下画出事件丢失与诊断准确率曲线。
- 后续工作 3:做盲测开发者实验,同时记录完整准备时间、模型成本、误报清理时间和从报告到修复的时间。