Trace Swift code: @Traced/@TraceAll macros (primary) + SwiftTrace bridge (secondary)#14
Merged
Conversation
Adds a SwiftPM package so AppleTrace can trace Swift, which the objc_msgSend hook can't reach (Swift's static / vtable / witness dispatch never goes through objc_msgSend). The primary route is source-level instrumentation via Swift macros, which sidesteps dispatch entirely — the begin/end calls are inserted into the function body at compile time, so they cover final classes, structs, and protocol methods alike. Package layout: - CAppleTrace: reuses the existing appletrace.mm core (single source of truth — included from the Xcode tree, not duplicated). - AppleTrace: idiomatic Swift API (withSpan, beginSection/endSection, traceInstant/Counter, asyncBegin/End, flush, setEnabled, traceDirectory) plus the macro declarations. - AppleTraceMacrosPlugin: SwiftSyntax compiler plugin implementing @Traced (body macro) and @traceall (member-attribute macro that stamps @Traced on every method with a body). Tests (all green on Swift 6.2.4, no experimental flags needed): - Macro expansion tests for @Traced and @traceall. - A usage test that actually applies the macros and asserts the function names land in the trace. - A runtime test that drives the C core through withSpan and verifies a fragment is written. docs/swift-tracing.md updated to record the chosen direction and status. The SwiftTrace-style runtime hook (secondary route) lands separately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds AppleTraceAuto, an optional product that bridges the proven johnno1962/SwiftTrace runtime hook into AppleTrace: a SwiftTrace.Decorated subclass forwards each traced method's entry/exit to APTBeginSection / APTEndSection, so zero-annotation auto-tracing of Swift class hierarchies lands in the same Perfetto trace. API: AppleTraceAuto.trace(aClass:) / traceClasses(matchingPattern:) / traceBundle(containing:) / stop(). Per SwiftTrace's design it can't see final / statically-dispatched methods — the @Traced / @traceall macros cover those, so the two routes complement each other. Verification: the SwiftTrace vtable patch can't be exercised from an XCTest bundle (its metadata scanning needs a normal executable / app image), so the bridge is verified by the runnable AppleTraceAutoExample target instead — `swift run AppleTraceAutoExample` traces a non-final class through a protocol and asserts the methods land in the trace (exits non-zero otherwise). The macro + runtime XCTests stay green. README / README_CN gain a "Tracing Swift" section; docs/swift-tracing.md records both routes as implemented. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds sample/AppleTraceSwiftDemo, a SwiftUI app that consumes the local SwiftPM package and demonstrates both Swift tracing routes in one guided app: - macros / withSpan: an "App Launch" span tree, an @Traced free function, a @traceall final class, async arcs, counters, and a 60-frame render loop across named worker threads; - AppleTraceAuto: zero-annotation hooking of a non-final ImageLoader reached through a protocol. Tap "Generate Trace" (or set APPLETRACE_AUTORUN=1) and the screen shows the trace directory plus the exact merge/Perfetto steps. Verified on the iPhone 17 Simulator — ~490 events across 5 named tracks, both routes present, no hang — and built + signed for a real device. Bridge hardening (Sources/AppleTraceAuto): subclass the lightweight SwiftTrace.Swizzle instead of Decorated and skip super. The trampoline does the real call and only uses onEntry/onExit as observers, so emitting begin/end is enough; Decorated's argument-reflection/logging path hung in the iOS app context. The AppleTraceAutoExample smoke check still passes. Docs: README / README_CN gain a samples table; AGENT.md and docs/swift-tracing.md describe the demo and the `-destination`-only build note (a stray `-sdk iphonesimulator` forces the macro plugin onto the wrong SDK). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ulator Two issues surfaced running AppleTraceSwiftDemo on a real iPhone 17 Pro: 1. dyld crash at launch — "Library not loaded: @rpath/SwiftTrace.framework". The embedded dynamic framework wasn't found because the project lacked LD_RUNPATH_SEARCH_PATHS; added @executable_path/Frameworks. (The Simulator happened to fall back to the on-disk build-products path, so it only failed on device.) 2. SwiftTrace patches pointer-authenticated vtable slots, which is unsafe on real devices. Gated AppleTraceAuto.trace(...) behind `#if targetEnvironment(simulator)`; the @Traced / @traceall macros are the on-device path and have no such limitation. Verified end-to-end: - Simulator: ~487 events, BOTH routes (macros + SwiftTrace-hooked ImageLoader.load/readBytes/resize). - Real device: ~463 events, macro route only (App Launch, Frame, Draw, warmCaches()/reload()/parse()/layout(), counters, async arcs across 5 named threads); SwiftTrace route correctly absent. Docs (README / README_CN / swift-tracing.md / AppleTraceAuto header) and the demo UI now state that AppleTraceAuto is Simulator/macOS-only and the macros are the on-device path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
AppleTrace's automatic mode rebinds
objc_msgSend, but Swift's static / vtable / witness dispatch never goes throughobjc_msgSend, so most Swift code is invisible to the auto-hook. This adds Swift tracing via a new SwiftPM package, following the two-route plan indocs/swift-tracing.md.What
A SwiftPM package at the repo root (reuses the existing
appletrace.mmcore — single source of truth, included from the Xcode tree, not duplicated).Primary route — Swift macros (
AppleTrace). Source-level instrumentation that sidesteps dispatch entirely (begin/end inserted into the body at compile time), so it coversfinalclasses, structs, and protocol methods alike:withSpan(_:_:)+beginSection/endSection/traceInstant/traceCounter/asyncBegin/asyncEnd/flushwrappers.@Traced(body macro) wraps a function body in a#function-named section.@TraceAll(member-attribute macro) stamps@Tracedon every method with a body.Secondary route — SwiftTrace bridge (
AppleTraceAuto, optional). Zero-annotation auto-tracing by bridging johnno1962/SwiftTrace: aSwiftTrace.Decoratedsubclass forwards each traced method's entry/exit toAPTBeginSection/APTEndSection.The two routes complement each other: SwiftTrace can't see
final/statically-dispatched methods (its documented blind spot) — the macros cover those.Verification
swift test— 6 tests green on Swift 6.2.4 with no experimental feature flags:@Traced/@TraceAllexpansion, a usage test that applies the macros and asserts the function names land in the trace, and a runtime test driving the C core throughwithSpan.swift run AppleTraceAutoExample— verifies the SwiftTrace bridge end-to-end (traces a non-final class through a protocol; exits non-zero if not captured). The bridge can't be exercised from an XCTest bundle because SwiftTrace's metadata scanning needs a normal executable / app image.AppleTraceandAppleTraceAutobuild for the iOS Simulator (xcodebuild).Swift TestsCI workflow runs all of the above onmacos-latest.Demo app
sample/AppleTraceSwiftDemo(SwiftUI) consumes the local package and demonstrates both routes in one guided app: tap Generate Trace to run a multi-threaded workload (@Traced/@TraceAll/withSpanspans, anAppleTraceAuto-hookedImageLoader, counters, async arcs, a 60-frame render loop). The screen shows the trace directory and the merge/Perfetto steps. Verified on the iPhone 17 Simulator (~490 events, 5 named tracks, both routes, no hang) and built+signed for device. CI also builds it.Build note: use
-destinationonly (no-sdk iphonesimulator, which forces the macro plugin onto the wrong SDK).Notes
objc_msgSendhook are unchanged.docs/swift-tracing.mdhas the full landscape survey, the rationale, and per-route limitations.🤖 Generated with Claude Code