33,000 行 XML 告诉你 heavyWork() 函数耗时过长:如何驯服 xctrace 应对大语言模型 (LLM)
上周,我在用 Instruments 工具对一个 Swift 应用进行性能分析。没什么稀奇的:运行 xctrace record,再运行 xctrace export,然后把导出的 XML 拷贝到 Claude Code 的上下文,让它帮忙分析热点。 结果 Claude 跟我说:"XML 文件太大,无法可靠地处理。" 33,553 行 XML,只为分析一个只有两个函数的程序。 真正的问题 xctrace export 是一个很棒的工具。它什么都给你:每一个采样点、每一个调用栈、每一帧的二进制信息、内存地址、UUID,简直面面俱到,精准无比,完美无缺。 但问题,也正是源于它的完美无缺。 对一个应用程序进行性能分析时,我并不需要所有的 3,044 个细节采样点。我不需要知道第 1,847 个采样点在 00:02.847.882 捕捉到了 libswiftCore.dylib 内存地址为 0x1027ec9a8 的内容。我只需要知道 heavyWork() 花掉了 70% 的时间,而 lightWork() 只用了 30%。 用大白话来说:我需要的是 10 行总结,而不是 33,000 行繁文缛节。 为什么 XML 格式是正确的选择(但噪音不可取) 在有人提出“2026 年了还用 XML 才是问题”之前——并不是这样。 XML 对于 xctrace 的功能来说,是非常理想的格式。试想一下: 层次结构:一个调用栈是一个框架的树状结构。一个采样点包含了一个调用栈,一个线程,一个进程。XML 自然而然地可以建模这些内容。 自描述性:每个元素都有名字、带类型的属性,并且结构可以被验证。你不用去猜 CSV 第七列的内容代表什么。 优雅的去重:xctrace 使用了 id 和 ref 系统,首次定义一个框架时是这样的:id="59" name="heavyWork()",后续只需引用 ref="59"。可以看作是一种序列化的 flyweight pattern。 可以用标准工具解析:XPath、xmllint、xml.etree.ElementTree…… 不需要专属解析器。 xctrace 的 XML 格式并不是冗余。它是 Instruments 所需的结构化信息,用来重建交互式调用树、对比运行情况,以及按线程和进程进行筛选。它是专门为一个可以展开和折叠节点的 GUI 工具设计的。 ...