[Debugger] Avoid resolving call tokens in exception replay IL scan#8815
Conversation
|
There was a problem hiding this comment.
Pull request overview
This PR updates the Exception Replay IL scanning logic in the Debugger to avoid runtime resolution (Module.ResolveMethod(token)) by switching to a metadata-only approach using System.Reflection.Metadata/vendored equivalents. This prevents IL scanning from triggering assembly loads or other side effects when inspecting call tokens in customer code.
Changes:
- Replaced call-token resolution with metadata-reader inspection of
MethodDefinition,MemberReference, andMethodSpecificationtokens. - Improved IL scanning alignment by skipping operands based on
OpCode.OperandType. - Added a regression test ensuring scanning does not resolve/load an unavailable dependency referenced by a
callvirttoken.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tracer/src/Datadog.Trace/Debugger/ExceptionAutoInstrumentation/ILAnalyzer.cs | Switches IL call-token detection from runtime resolution to metadata-only scanning; improves operand skipping logic. |
| tracer/test/Datadog.Trace.Tests/Debugger/ILAnalyzerTests.cs | Adds regression coverage for “unavailable dependency should not be resolved/loaded while scanning”. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
BenchmarksBenchmark execution time: 2026-06-23 11:05:46 Comparing candidate commit 8c2bb7c in PR branch Found 0 performance improvements and 1 performance regressions! Performance is the same for 71 metrics, 0 unstable metrics, 62 known flaky benchmarks, 64 flaky benchmarks without significant changes.
|
Execution-Time Benchmarks Report ⏱️Execution-time results for samples comparing This PR (8815) and master. ✅ No regressions detected - check the details below Full Metrics ComparisonFakeDbCommand
HttpMessageHandler
Comparison explanationExecution-time benchmarks measure the whole time it takes to execute a program, and are intended to measure the one-off costs. Cases where the execution time results for the PR are worse than latest master results are highlighted in **red**. The following thresholds were used for comparing the execution times:
Note that these results are based on a single point-in-time result for each branch. For full results, see the dashboard. Graphs show the p99 interval based on the mean and StdDev of the test run, as well as the mean value of the run (shown as a diamond below the graph). Duration chartsFakeDbCommand (.NET Framework 4.8)gantt
title Execution time (ms) FakeDbCommand (.NET Framework 4.8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8815) - mean (70ms) : 68, 73
master - mean (73ms) : 69, 76
section Bailout
This PR (8815) - mean (77ms) : 72, 81
master - mean (77ms) : 74, 79
section CallTarget+Inlining+NGEN
This PR (8815) - mean (1,081ms) : 1040, 1123
master - mean (1,079ms) : 1025, 1133
FakeDbCommand (.NET Core 3.1)gantt
title Execution time (ms) FakeDbCommand (.NET Core 3.1)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8815) - mean (109ms) : 106, 112
master - mean (113ms) : 108, 119
section Bailout
This PR (8815) - mean (115ms) : 108, 122
master - mean (112ms) : 108, 116
section CallTarget+Inlining+NGEN
This PR (8815) - mean (775ms) : 753, 796
master - mean (773ms) : 755, 791
FakeDbCommand (.NET 6)gantt
title Execution time (ms) FakeDbCommand (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8815) - mean (101ms) : 93, 108
master - mean (100ms) : 95, 105
section Bailout
This PR (8815) - mean (99ms) : 95, 103
master - mean (99ms) : 96, 102
section CallTarget+Inlining+NGEN
This PR (8815) - mean (938ms) : 894, 982
master - mean (936ms) : 897, 975
FakeDbCommand (.NET 8)gantt
title Execution time (ms) FakeDbCommand (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8815) - mean (96ms) : 93, 98
master - mean (95ms) : 92, 97
section Bailout
This PR (8815) - mean (100ms) : 95, 106
master - mean (98ms) : 94, 103
section CallTarget+Inlining+NGEN
This PR (8815) - mean (812ms) : 777, 846
master - mean (815ms) : 776, 855
HttpMessageHandler (.NET Framework 4.8)gantt
title Execution time (ms) HttpMessageHandler (.NET Framework 4.8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8815) - mean (201ms) : 195, 207
master - mean (203ms) : 198, 208
section Bailout
This PR (8815) - mean (207ms) : 201, 212
master - mean (207ms) : 201, 213
section CallTarget+Inlining+NGEN
This PR (8815) - mean (1,208ms) : 1172, 1244
master - mean (1,212ms) : 1166, 1257
HttpMessageHandler (.NET Core 3.1)gantt
title Execution time (ms) HttpMessageHandler (.NET Core 3.1)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8815) - mean (291ms) : 283, 299
master - mean (291ms) : 282, 300
section Bailout
This PR (8815) - mean (291ms) : 284, 297
master - mean (292ms) : 283, 301
section CallTarget+Inlining+NGEN
This PR (8815) - mean (971ms) : 950, 992
master - mean (973ms) : 950, 995
HttpMessageHandler (.NET 6)gantt
title Execution time (ms) HttpMessageHandler (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8815) - mean (286ms) : 280, 291
master - mean (286ms) : 276, 295
section Bailout
This PR (8815) - mean (284ms) : 279, 289
master - mean (288ms) : 283, 292
section CallTarget+Inlining+NGEN
This PR (8815) - mean (1,166ms) : 1127, 1204
master - mean (1,172ms) : 1129, 1214
HttpMessageHandler (.NET 8)gantt
title Execution time (ms) HttpMessageHandler (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8815) - mean (281ms) : 274, 288
master - mean (283ms) : 274, 292
section Bailout
This PR (8815) - mean (281ms) : 276, 286
master - mean (284ms) : 279, 288
section CallTarget+Inlining+NGEN
This PR (8815) - mean (1,044ms) : 1002, 1087
master - mean (1,045ms) : 998, 1092
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9258f47779
ℹ️ 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".
Scan exception replay call operands through metadata instead of Module.ResolveMethod so unavailable dependencies are not loaded while detecting ExceptionDispatchInfo.Throw frames. Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
9258f47 to
8c2bb7c
Compare
## Summary of changes - Add a single debugger safety boundaries guide covering reflection/type-loading risks and customer-code execution policy. - Make the safety guide visible from `AGENTS.md` so future agents and reviewers check it before changing debugger safety-sensitive paths. - Add focused snapshot tests for the current policy around default object getters, selected exception properties, and supported collection enumeration. ## Reason for change Define safety boundaries for debugger code running inside customer processes. Keeping the reflection audit, customer-code execution policy, and agent-facing guidance in one document gives reviewers one place to evaluate the current contract and the representative tests that lock it in. ## Implementation details This branch intentionally does not change capture behavior. The tests document that default object capture reads backing fields without invoking arbitrary getters, exception capture reads selected `System.Exception` properties, and supported collection serialization reads `Count` and enumerates under existing limits. The merged safety guide classifies related debugger reflection paths and defines when code may invoke getters, enumerators, exception overrides, attributes, or `ToString()`. ## Test coverage - `DebuggerSnapshotCreatorTests.Policy_` - `DebuggerSnapshotCreatorTests` Related follow-up PRs: - #8814: static member capture guard - #8815: metadata-only Exception Replay IL call scanning - #8816: metadata-only state-machine attribute checks --------- Co-authored-by: Cursor <[email protected]>
Summary of changes
Module.ResolveMethod(token)with metadata-reader inspection ofMethodDefinition,MemberReference, andMethodSpecificationtokens.OpCode.OperandType, so scanning remains aligned across non-call instructions.callvirttoken is not resolved or loaded while scanning.Reason for change
Module.ResolveMethod(token)can force runtime method/type resolution for arbitrary call operands in customer IL. Exception Replay only needs to detect direct calls such asExceptionDispatchInfo.Throw(), so this should be a metadata-only read and should not load unavailable dependencies or execute customer resolution side effects.Implementation details
The analyzer now opens the declaring module's PE image with a metadata reader and compares call operands by token metadata. It matches method names and declaring type metadata against the requested target type, including base types and interfaces to preserve the previous assignability behavior used by inherited calls such as
Exception.ToString()resolving toObject.ToString().The metadata reader is created once per analyzed method. In production this path is already cached by
MethodUniqueIdentifier.MightMisleadStacktrace, so the file metadata read happens once per method identity rather than per snapshot.Test coverage
ILAnalyzerTests