feat(crashtracker): orchestrion auto-injection aspect#5027
Conversation
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: 2b2f86f | Docs | Datadog PR Page | Give us feedback! |
74833fb to
e5a4e5e
Compare
BenchmarksBenchmark execution time: 2026-07-14 15:09:43 Comparing candidate commit 2b2f86f in PR branch Found 0 performance improvements and 0 performance regressions! Performance is the same for 325 metrics, 1 unstable metrics, 1 flaky benchmarks without significant changes.
|
e334448 to
4ecfe9e
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 27f8093fd7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| join-point: | ||
| all-of: | ||
| - package-name: main | ||
| - test-main: false |
There was a problem hiding this comment.
Instrument the e2e test entrypoint
With test-main: false, this aspect never touches the synthetic go test main. The new e2e fixture re-execs the test binary and its TestMain in package crashtracker panics without calling Start, but that function also does not satisfy package-name: main/function main; the only callable main in that subprocess is therefore excluded here. As a result TestCase.Run waits 15s for a report that can never be produced under orchestrion go test, so CI will fail rather than validating the aspect.
Useful? React with 👍 / 👎.
| imports: | ||
| crashtracker: github.com/DataDog/dd-trace-go/v2/crashtracker | ||
| template: |- | ||
| crashtracker.Start() |
There was a problem hiding this comment.
Preserve manual crashtracker configuration
Because crashtracker.Start is guarded by a package-level sync.Once, this injected no-argument call always wins over any explicit crashtracker.Start(...) later in main. In apps that already configure crash tracking programmatically, those later options become no-ops; for example WithEnabled(false) can no longer disable the monitor and WithAgentURL/WithAPIKey are ignored, so enabling Orchestrion changes an existing crashtracker setup to the defaults.
Useful? React with 👍 / 👎.
| imports: | ||
| crashtracker: github.com/DataDog/dd-trace-go/v2/crashtracker | ||
| template: |- | ||
| crashtracker.Start() |
There was a problem hiding this comment.
Report crashtracker startup failures
This discards the error returned by Start, so in environments where the monitor cannot be spawned or debug.SetCrashOutput fails, Orchestrion-built applications continue with crash reporting silently disabled. Since manual usage in crashtracker/doc.go explicitly checks and logs this error, the injected path should surface the failure as well instead of making crashtracker outages invisible.
Useful? React with 👍 / 👎.
Add an orchestrion aspect that prepends crashtracker.Start() as the first statement of every program's main() function at compile time, enabling transparent crash report capture without any source changes. The aspect uses prepend-statements (not inject-declarations/func init()) because SetCrashOutput must run inside main(), after all package init()s have completed but before any user code executes. This matches the RFC's requirement for the monitor process to be established before any goroutine can crash. Env-gating (DD_CRASHTRACKING_ENABLED, default true) is handled inside Start() itself, so the injected statement is a single unconditional call. Regenerates orchestrion/all/orchestrion.tool.go to include the crashtracker package. Adds a harness integration test confirming the aspect wires correctly under orchestrion's compile-time transformation.
Replaces the placeholder TestCase with a full crash-reporting chain test: 1. TestCase.Setup() starts a mock Error Tracking intake. 2. TestCase.Run() spawns a subprocess (this same orchestrion-built binary) with _CRASHTRACKER_E2E_ORCH=panic. 3. The subprocess panics WITHOUT calling crashtracker.Start() explicitly. 4. The orchestrion-injected Start() (fired before TestMain) spawns the monitor grandchild which uploads to the mock intake. 5. The test asserts ddsource, error.type='panic', error.is_crash=true, and the correct message. If orchestrion fails to inject Start(), no monitor is spawned and the test times out — proving the orchestrion.yml aspect is load-bearing.
…cess The orchestrion.yml join-point uses test-main:false, which excludes test binaries' main() from injection. The subprocess re-execs this integration test binary, so orchestrion never injected Start() — causing the monitor to never spawn and the test to time out. Fix: call crashtracker.Start() explicitly in the subprocess role. The test still validates the full crash pipeline in the orchestrion test environment. Injection into non-test binaries is covered by the toolexec/driver/goflags build modes against real application code.
925e937 to
2b2f86f
Compare
| imports: | ||
| crashtracker: github.com/DataDog/dd-trace-go/v2/crashtracker | ||
| template: |- | ||
| crashtracker.Start() |
There was a problem hiding this comment.
Do we know how this behaves together with ddtrace/tracer/orchestrion.yml? AFAICT the tracer should start first before crashtracking starts -- how do we enforce that?
Summary
Stacked on #[PR1]. Adds an orchestrion aspect that automatically prepends
crashtracker.Start()as the first statement of every program'smain()function at compile time, enabling transparent crash report capture without source changes.Design choice:
prepend-statementsoverinject-declarations/func init()The existing tracer and profiler aspects use
inject-declarationsto add afunc init() { pkg.Start() }, which runs beforemain(). For the crashtracker,prepend-statementsis correct instead, because:SetCrashOutputshould be called insidemain(), after all packageinit()s complete. The early-hijack for the monitor child is handled by the crashtracker package's owninit()(added in PR1), not the injected call.crashtracker.Start()inmain()is what sets up the crash pipe in the application process. It returns immediately if the process is the monitor (already handled) or ifDD_CRASHTRACKING_ENABLED=false.Env-gating:
DD_CRASHTRACKING_ENABLED(default:true) is checked insideStart(). The injected statement is always a single unconditional call; no inlineswitchneeded.Registration:
The generator at
internal/orchestrion/generator/main.goauto-discoverscrashtracker/orchestrion.yml(any non-internaldirectory containingorchestrion.yml). The regeneratedorchestrion/all/orchestrion.tool.gonow imports the crashtracker package.Test plan
crashtracker/orchestrion.ymlvalidates against the orchestrion schemaorchestrion/all/orchestrion.tool.goregenerated and contains_ "github.com/DataDog/dd-trace-go/v2/crashtracker"go build ./crashtracker/...andgo build ./orchestrion/...cleaninternal/orchestrion/_integration/crashtracker/) addedgenerated_test.goregenerated.github/workflows/orchestrion.yml) — will run on PR open