关于 ios-ettrace-performance
一项用于采集和分析 iOS 模拟器应用符号化 ETTrace 性能数据的技能,指导开发者完成配置、保存追踪文件,并报告性能热点与注意事项。
主要功能
- 01采集单个聚焦应用流程
- 02为模拟器性能分析链接 ETTrace
- 03收集并验证匹配的 dSYM
- 04保存处理后的火焰图 JSON
- 05报告热点与分析注意事项
适用场景
- 01测量应用启动或首次渲染
- 02分析一次运行时交互
- 03检查已符号化的自有调用栈
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ openai/ plugins --skill ios-ettrace-performance安装后在 Claude Code 中输入/ios-ettrace-performance确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlscripts/analyze_flamegraph_json.pycollect_ios_dsyms.sh详细介绍
iOS ETTrace 性能分析
使用此技能从 iOS 模拟器应用中采集聚焦且已符号化的 ETTrace 性能分析数据。如果任务还需要构建、安装、启动模拟器、驱动 UI、查看日志或截取屏幕截图,请搭配使用 ../ios-debugger-agent/SKILL.md。
核心工作流
- 选择一个聚焦的流程,并记下预期的开始和结束节点。
- 构建将要安装并进行性能分析的确切模拟器应用。
- 临时将 ETTrace 链接到该应用目标,以便进行模拟器/调试性能分析。
- 收集与 UUID 匹配的应用可执行文件和内嵌动态框架的 dSYM。
- 采集一次启动或运行时跟踪。
- 运行后立即保留处理后的火焰图 JSON。
- 仅分析处理后的 JSON,并报告流程、产物、热点和注意事项。
避免进行宽泛的“使用应用一段时间”式采集。一次跟踪应对应一个用户可见的流程。
设置
为每次性能分析会话使用一个可写的运行目录:
if [ -z "${RUN_DIR:-}" ]; then
RUN_DIR="$(mktemp -d "${TMPDIR:-/tmp}/codex-ios-ettrace.XXXXXX")"
fi
mkdir -p "$RUN_DIR"
如果尚未安装 ETTrace runner CLI,请安装:
brew install emergetools/homebrew-tap/ettrace
ettrace 是运行于 macOS 主机端的 runner。应用还必须链接适用于 iOS Simulator 架构的 ETTrace.xcframework。
此工作流已针对 ETTrace v1.1.0 处理后的顶层包含 nodes 的 output_<thread>.json 文件进行验证。
将 ETTrace 链接到应用
将 ETTrace 接入正在进行性能分析的确切应用目标。将此集成保留为明确的临时补丁,性能分析任务完成后将其移除,除非用户明确要求保留。
优先选择以下方式:
- 如果仓库已有适用于模拟器的
ETTrace.xcframework,则复用该版本。 - 如果没有,则从上游 ETTrace 软件包构建仅适用于模拟器的副本,并放入
RUN_DIR。 - 将框架直接链接到应用目标,而不只是链接到测试、资源、数据文件或嵌套的启动器目标。
- 确认启动日志会打印
Starting ETTrace。 - 一次只对一个已插桩 ETTrace 的模拟器应用进行性能分析,因为模拟器模式会监听固定的 localhost 端口。
需要时构建模拟器框架:
ETTRACE_TAG="${ETTRACE_TAG:-v1.1.0}" # Override to match the installed runner when Homebrew updates.
ETTRACE_SRC="$RUN_DIR/ETTrace-src"
if [ ! -d "$ETTRACE_SRC" ]; then
git clone --depth 1 --branch "$ETTRACE_TAG" https://github.com/EmergeTools/ETTrace "$ETTRACE_SRC"
fi
rm -rf "$RUN_DIR/ETTrace-iphonesimulator.xcarchive" "$RUN_DIR/ETTrace.xcframework"
pushd "$ETTRACE_SRC" >/dev/null
xcodebuild archive \
-scheme ETTrace \
-archivePath "$RUN_DIR/ETTrace-iphonesimulator.xcarchive" \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
INSTALL_PATH='Library/Frameworks' \
SKIP_INSTALL=NO \
CLANG_CXX_LANGUAGE_STANDARD=c++17
xcodebuild -create-xcframework \
-framework "$RUN_DIR/ETTrace-iphonesimulator.xcarchive/Products/Library/Frameworks/ETTrace.framework" \
-output "$RUN_DIR/ETTrace.xcframework"
popd >/dev/null
对于 Bazel 应用,临时导入通常如下所示:
load("@rules_apple//apple:apple.bzl", "apple_dynamic_xcframework_import")
package(default_visibility = ["//visibility:public"])
apple_dynamic_xcframework_import(
name = "ETTrace",
xcframework_imports = glob(["ETTrace.xcframework/**"]),
)
对于 Xcode 项目,临时将适用于模拟器的 ETTrace.xcframework 添加到正在进行性能分析的调试模拟器构建的应用目标中的 Link Binary With Libraries / Embed Frameworks 阶段,然后在性能分析后移除该配置。
符号化检查
不要根据未符号化的火焰图得出结论。每次采集前,准备一个 dSYM 文件夹,其中包含应用 dSYM 以及所有内嵌的第一方动态框架 dSYM。
在生成已安装应用的最终构建完成后收集 dSYM:
SKILL_DIR="<absolute path to this loaded skill folder>"
APP="<path-to-built-simulator-App.app>"
DSYMS="$RUN_DIR/dsyms"
"$SKILL_DIR/scripts/collect_ios_dsyms.sh" \
--app "$APP" \
--out-dir "$DSYMS" \
--search-root "$(dirname "$APP")" \
--search-root "$PWD" \
--extra-dsym "$RUN_DIR/ETTrace-iphonesimulator.xcarchive/dSYMs/ETTrace.framework.dSYM"
对于必须完成符号化的应用自有动态框架,添加 --require-framework <FrameworkName>;仅当所有内嵌框架均由应用所有或预计都具有符号时,才使用 --require-all-frameworks。如果辅助工具报告缺少必需的应用或框架 dSYM,请重新构建确切的模拟器应用并启用 dSYM 生成,然后再进行跟踪;或者将包含这些 dSYM 的构建输出目录添加为另一个 --search-root。
如果报告看起来可疑,请在跟踪前验证重要的 UUID:
dwarfdump --uuid "$APP/$(/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "$APP/Info.plist")"
find "$DSYMS" -maxdepth 1 -type d -name '*.dSYM' -print -exec dwarfdump --uuid {} \;
ETTrace 退出后,查看其符号化摘要。除非只是少量噪声,否则应将有意义的第一方“have library but no symbol”行视为跟踪失败。未符号化的系统框架或 ETTrace 内部分类通常可以接受。
采集
对于启动跟踪:
cd "$RUN_DIR"
CAPTURE_MARKER="$RUN_DIR/.ettrace-capture-start"
: > "$CAPTURE_MARKER"
find "$RUN_DIR" -maxdepth 1 \( -name 'output.json' -o -name 'output_*.json' \) -delete
ettrace --simulator --launch --verbose --dsyms "$DSYMS"
仅在测量启动或首次渲染时使用 --launch。首次启动时的连接可能会强制退出应用;如果系统提示,请从模拟器主屏幕重新启动,而不是从 Xcode 启动。对于安装后的首次启动跟踪,请暂时在应用的 Info.plist 中设置 ETTraceRunAtStartup=YES,然后运行 ettrace --simulator 并从主屏幕启动应用。
对于运行时流程跟踪:
cd "$RUN_DIR"
CAPTURE_MARKER="$RUN_DIR/.ettrace-capture-start"
: > "$CAPTURE_MARKER"
find "$RUN_DIR" -maxdepth 1 \( -name 'output.json' -o -name 'output_*.json' \) -delete
ettrace --simulator --verbose --dsyms "$DSYMS"
从稳定的界面开始,启动 ETTrace,执行一个明确聚焦的流程,等待所有可见操作完成,然后停止运行器。如需进行更广泛的归因分析,请添加 --multi-thread;否则从主线程开始。
在 Codex 中,使用 TTY 运行 ettrace,并通过 write_stdin 回答提示。没有 TTY 时,运行器可能会退出,无法生成有用的跟踪结果。
保留输出
下一次 ETTrace 运行可能会覆盖已处理的火焰图文件,因此请立即保留新生成的 output_<thread-id>.json 文件。不要分析保存的 output.json;ETTrace 也会提供一个名为该名称的查看器路由,而且原始的 emerge-output/output.json 文件并非此工作流所需的已处理火焰图产物。
PRESERVED_DIR="$(mktemp -d "$RUN_DIR/run-$(date +%Y%m%d-%H%M%S).XXXXXX")"
: > "$PRESERVED_DIR/summary.txt"
if [ ! -e "$CAPTURE_MARKER" ]; then
echo "error: capture marker missing; start a fresh ETTrace capture before preserving outputs" >&2
exit 1
fi
find "$RUN_DIR" -maxdepth 1 -name 'output_*.json' -newer "$CAPTURE_MARKER" -print | while IFS= read -r json; do
preserved="$PRESERVED_DIR/${json##*/}"
cp "$json" "$preserved"
{
echo "## ${preserved##*/}"
python3 "$SKILL_DIR/scripts/analyze_flamegraph_json.py" "$preserved"
} >> "$PRESERVED_DIR/summary.txt"
done
if [ ! -s "$PRESERVED_DIR/summary.txt" ]; then
echo "error: no fresh processed ETTrace output JSON found in $RUN_DIR" >&2
exit 1
fi
仅分析 RUN_DIR 中已处理的 output_*.json 文件。除非是在调试 ETTrace 本身,否则请忽略 output.json 和原始的 emerge-output/output.json 文件。如果分析器拒绝该 JSON 结构,请使用 Homebrew ETTrace 运行器和匹配的应用端 ETTrace.xcframework 标签重新采集,而不要尝试解读被拒绝的文件。
阅读性能分析结果
从 run-*/summary.txt 开始;如有需要,再直接检查已处理的 JSON。
报告:
- 确切的流程、应用构建版本、模拟器型号和运行时,以及运行次数
- 已处理的火焰图 JSON 文件路径
- 活跃度最高的叶节点和包含样本权重或百分比的第一方调用栈
- 应用自有二进制文件的符号是否完整
- 首次运行设置、仅模拟器环境下的开销、网络波动或样本数量较少等注意事项
- 仅当在可比设置下采集了相同流程时,才报告前后差异
清理
性能分析完成后,除非用户要求保留,否则请移除临时的 ETTrace 应用接入代码。根据当前任务决定保留或丢弃运行产物。