This reference is for AI agents and contributors working inside the AppleTrace repository. It summarizes how the project is organized, how to run and verify changes, and the expectations for contributions.
- AppleTrace instruments iOS apps so you can analyze performance hotspots in Perfetto.
- Developers can either add manual
APTBeginSection/APTEndSectionmarkers (plusAPTInstant/APTCounter/APTAsyncBegin/APTAsyncEndevents) or hook everyobjc_msgSendvia a fishhook-style direct symbol rebind (arm64 only; seeappletrace/appletrace/src/objc/hook_objc_msgSend.m). merge.pyandscripts/appletrace_cli.py(and the helpergo.sh) merge sandbox fragments into atrace.jsonyou open directly in Perfetto. Visualization is Perfetto-only; there is no Catapult/Chrome HTML pipeline.- Releases bundle a loader tweaked for arm64, but the source can be rebuilt via the included Xcode projects.
appletrace/— Core framework sources (appletrace.xcodeproj, Objective-C runtime hooks, exported headers).loader/— Loader/packaging project plusresign.shfor re-signing the embeddedappletrace.framework.sample/TraceAllMsgDemo— Objective-C Xcode sample showing both manual instrumentation (APTBeginSection) and automaticobjc_msgSendtracing.sample/AppleTraceSwiftDemo— Swift sample (SwiftUI) that consumes the local SwiftPM package and demonstrates both Swift routes: the@Traced/@TraceAll/withSpanmacros and theAppleTraceAutoSwiftTrace bridge. Build with-destinationonly (no-sdk, which would force the macro plugin onto the wrong SDK).springboard/AppleTraceSpringBoard— Additional loader project for SpringBoard-focused experiments.hookzz/— Legacy embedded HookZz dependency (the currentobjc_msgSendhook uses a direct symbol rebind instead).go.sh,merge.py,scripts/appletrace_cli.py— Scripts for merging trace fragments intotrace.jsonand opening Perfetto.sampledata/— Ready-made trace (trace.json) for verifying the visualization pipeline in Perfetto.release/— Notes and artifacts for the prebuilt loader (arm64).image/,wechat.png— Documentation assets.
- Clone & prerequisites
git clone https://github.com/everettjf/AppleTrace.git- Install Xcode, Python 3, LLDB, and
ldid(for re-signing loader builds). - Optional:
python3 -m pip install -r requirements.txtfor local test tooling. - Visualization is browser-based at ui.perfetto.dev; nothing to download.
- Build instrumentation
- For Objective-C (manual sections and/or the
objc_msgSendhook), openappletrace/appletrace.xcodeproj, build the framework, and embed it into your target (seesample/TraceAllMsgDemo). - For Swift, add the SwiftPM package and use the
@Traced/@TraceAll/withSpanmacros (seesample/AppleTraceSwiftDemo).
- For Objective-C (manual sections and/or the
- Collect data
- Run the instrumented app; trace segments are written to
<app sandbox>/Library/appletracedata. - Pull the folder from the Simulator or device.
- Run the instrumented app; trace segments are written to
- Process traces
- Quick path:
sh go.sh <path-to-appletracedata>to merge and open Perfetto. - Manual path:
python3 merge.py -d <path>then drag<path>/trace.jsoninto ui.perfetto.dev. - Unified path:
python3 scripts/appletrace_cli.py open <path-to-appletracedata>.
- Quick path:
- Automated coverage (also run in CI on
macos-latest, see.github/workflows/python-tests.yml):python3 -m pytest tests— Python trace merge pipeline + binary fragment format.scripts/test_objc_msgsend_hook.sh— builds and runsTraceAllMsgDemoon an iOS Simulator, asserting theobjc_msgSendhook produces trace fragments without crashing.scripts/test_objc_msgsend_hook_experimental.sh— same harness with the experimental scenario, validatingsuperdispatch, cross-thread events, stack-passed/floating-point arguments, and small aggregate returns.scripts/test_batching_stress.sh— host build of the per-thread batched writer; runs the multithreaded workload in both text and binary (APPLETRACE_BINARY=1) modes and asserts no events are lost or duplicated.- The Simulator on Apple silicon runs the arm64 slice, so the hook ABI is exercised on a real arm64 target. The scripts assume a Python 3 with
pytestavailable (e.g. a venv) onPATH.
- Local-only (needs hardware, not run in CI):
scripts/test_objc_msgsend_hook_device.sh— builds, installs, and runsTraceAllMsgDemoon a connected, unlocked arm64 device viadevicectl, then pulls and verifies the trace in both text and binary modes. Requires a signing identity; setDEVICE_UDIDto target a specific device.
- Still manual:
- Instruments profiling of the batched writer (
docs/perf-batching-design.mdsection 9, step 3). - On-device validation of the loader / LLDB dylib-injection path on a real arm64 device.
- Instruments profiling of the batched writer (
- No dedicated Objective-C lint/format pipeline exists. Follow existing Objective-C/C/C++/Python conventions in the repo (clang/Xcode defaults, 4-space indentation in Python).
- Keep public headers tidy and update comments where behavior changes.
- Frameworks: Use
appletrace.xcodeprojtargets. Make sure exported headers remain inappletrace.framework. - Loader: After swapping in a rebuilt framework (
loader/AppleTraceLoader/Package/Library/Frameworks/appletrace.framework), runloader/resign.shto re-sign withldid. - Visualization: Traces open in Perfetto (
ui.perfetto.dev) directly; there is no bundled HTML exporter to keep in sync. - Deliverables: The
release/folder targets arm64; highlight this in release notes and README when publishing new binaries.
- Prefer concise Objective-C with explicit
APTBeginSectionmarkers; avoid introducing new macros unless necessary. - Keep Objective-C source under
appletrace/src; Python utilities now live both at repo root (merge.py) and underscripts/for higher-level workflows. - Use descriptive section names inside traces to keep Perfetto timelines meaningful.
- Use LLDB breakpoints around the rebinding/wrapper code (
appletrace/appletrace/src/objc/hook_objc_msgSend.m) when troubleshooting automatic tracing. - Inspect intermediate
trace.appletracefiles before merging to ensure data is written. - Open
trace.jsonin ui.perfetto.dev to verify event ordering and timing. - Compare against
sampledata/trace.jsonif output looks incorrect.
- Keep changes scoped: avoid mixing instrumentation updates with tooling refactors or documentation tweaks.
- Target arm64 only; other architectures (arm64e, x86_64) are out of scope. The
objc_msgSendhook hard-errors if built for arm64e. - Visualization is Perfetto-only — do not reintroduce a Catapult/Chrome HTML pipeline.
- Update README/AGENT/wiki when changing workflows, scripts, or dependencies.
- Never remove diagnostic scripts (
merge.py,go.sh) without providing replacements. - Preserve existing assets (images, sample traces) so documentation stays accurate.
- Build the relevant Xcode targets (framework, loader, and/or samples) and ensure they run on device or Simulator.
- Run
python3 -m pytest testswhen touching Python tooling. - Run
python3 merge.py -d <path>(orsh go.sh <path>) against fresh trace data to confirm the pipeline still works. - Update
README.md/AGENT.mdif instructions changed; keep images/links in sync. - Re-sign loader artifacts with
loader/resign.shif the embedded framework changed. - Document manual testing performed (devices, iOS versions, Simulator).
- Ensure no unrelated files or formatting-only changes are included.