[Debugger] Guard static member capture#8814
Conversation
|
@codex review |
Snapshots difference summaryThe following differences have been observed in committed snapshots. It is meant to help the reviewer. 4 occurrences of : - "type": "String",
- "value": "Static Field"
+ "notCapturedReason": "typeInitializer",
+ "type": "String"
[...]
- "type": "String",
- "value": "Static Property"
+ "notCapturedReason": "typeInitializer",
+ "type": "String"
2 occurrences of : - "type": "String",
- "value": "Static Field"
+ "notCapturedReason": "typeInitializer",
+ "type": "String"
[...]
- "type": "String",
- "value": "Static Property"
+ "notCapturedReason": "typeInitializer",
+ "type": "String"
2 occurrences of : - "elements": [
- {
- "fields": {
- "Id": {
- "type": "String",
- "value": "Foo"
- },
- "Status": {
- "type": "RoomStatus",
- "value": "Maintenance"
- }
- },
- "type": "Room"
- },
- {
- "fields": {
- "Id": {
- "type": "String",
- "value": "Bar"
- },
- "Status": {
- "type": "RoomStatus",
- "value": "Ready"
- }
- },
- "type": "Room"
- }
- ],
- "size": 2,
+ "notCapturedReason": "typeInitializer",
2 occurrences of : - "elements": [
- {
- "fields": {
- "Id": {
- "type": "String",
- "value": "Foo"
- },
- "Status": {
- "type": "RoomStatus",
- "value": "Ready"
- }
- },
- "type": "Room"
- },
- {
- "fields": {
- "Id": {
- "type": "String",
- "value": "Bar"
- },
- "Status": {
- "type": "RoomStatus",
- "value": "Ready"
- }
- },
- "type": "Room"
- }
- ],
- "size": 2,
+ "notCapturedReason": "typeInitializer",
2 occurrences of : - "elements": [
- {
- "elements": [
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- }
- ],
- "size": 10,
- "type": "State[]"
- },
- {
- "elements": [
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- },
- {
- "type": "State",
- "value": "Error"
- }
- ],
- "size": 10,
- "type": "State[]"
- }
- ],
- "size": 2,
+ "notCapturedReason": "typeInitializer",
2 occurrences of : - SpanDecorationError: Instance=Datadog.Trace.Debugger.Expressions.UndefinedValue,
+ SpanDecorationError: UndefinedValue,
2 occurrences of : - SpanDecorationSameTagsSecondError: Instance=Datadog.Trace.Debugger.Expressions.UndefinedValue,
+ SpanDecorationSameTagsSecondError: UndefinedValue,
1 occurrences of : - SpanDecorationTwoSegments: Instance=Datadog.Trace.Debugger.Expressions.UndefinedValueRun,
+ SpanDecorationTwoSegments: UndefinedValueRun,
|
There was a problem hiding this comment.
Pull request overview
This PR hardens debugger snapshot serialization and probe expression evaluation to avoid triggering customer type initializers (.cctor) when reading static fields/properties via reflection, while still capturing metadata-only constants safely.
Changes:
- Introduces
StaticMemberSafetyto centralize the “safe to read static member” decision and to read literal constants viaGetRawConstantValue(including preserving enum types). - Updates snapshot serialization and probe expression evaluation/dump paths to skip unsafe statics (types with
TypeInitializer != null) and to use the new literal-constant handling. - Adds/updates regression tests and approval baselines to cover safe statics, unsafe statics, literals, and enum constants.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tracer/src/Datadog.Trace/Debugger/Helpers/StaticMemberSafety.cs | Adds centralized static-member safety checks and raw constant extraction. |
| tracer/src/Datadog.Trace/Debugger/Snapshots/DebuggerSnapshotSerializer.cs | Prevents snapshot static reads from triggering type initializers; preserves literal constants. |
| tracer/src/Datadog.Trace/Debugger/Expressions/ProbeExpressionParser.General.cs | Guards static member access in expressions; emits undefined + error for unsafe statics; preserves literal constants. |
| tracer/src/Datadog.Trace/Debugger/Expressions/ProbeExpressionParser.Dump.cs | Filters unsafe static fields during dumps and safely retrieves literal field values. |
| tracer/test/Datadog.Trace.Tests/Debugger/DebuggerSnapshotCreatorTests.cs | Adds unit coverage for capturing cctor-free statics and skipping unsafe statics while preserving constants. |
| tracer/test/Datadog.Trace.Tests/Debugger/DebuggerExpressionLanguageTests.cs | Adds expression evaluation coverage for safe vs unsafe static access and constant/enum constant handling. |
| tracer/test/Datadog.Trace.Tests/Debugger/ProbeExpressionsResources/Approvals/DebuggerExpressionLanguageTests.ToStringNotSupported.verified.txt | Updates approval baseline to reflect filtered static field dumps. |
| tracer/test/Datadog.Trace.Tests/Debugger/ProbeExpressionsResources/Approvals/DebuggerExpressionLanguageTests.ParentStaticProtectedMember.verified.txt | Updates approval baseline for skipped unsafe static member access (undefined + error). |
| tracer/test/Datadog.Trace.Tests/Debugger/ProbeExpressionsResources/Approvals/DebuggerExpressionLanguageTests.CustomArrayAtIndex.verified.txt | Updates approval baseline to reflect filtered static field dumps. |
| tracer/test/Datadog.Trace.Tests/Debugger/ProbeExpressionsResources/Approvals/DebuggerExpressionLanguageTests.AccessNullableStructNotNull.verified.txt | Updates approval baseline to reflect filtered static field dumps. |
| tracer/test/Datadog.Trace.Tests/Debugger/ProbeExpressionsResources/Approvals/DebuggerExpressionLanguageTests.AccessNullableStruct.verified.txt | Updates approval baseline to reflect filtered static field dumps. |
| tracer/test/Datadog.Trace.Debugger.IntegrationTests/Approvals/snapshots/ProbeTests.StaticType.verified.txt | Updates integration approval baseline for removal of unsafe static captures. |
| tracer/test/Datadog.Trace.Debugger.IntegrationTests/Approvals/snapshots/ProbeTests.AsyncStaticType.verified.txt | Updates integration approval baseline for removal of unsafe static captures. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Codex Review: Didn't find any major issues. Chef's kiss. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
BenchmarksBenchmark execution time: 2026-06-25 11:28:46 Comparing candidate commit 5eaf36e in PR branch Found 0 performance improvements and 0 performance regressions! Performance is the same for 72 metrics, 0 unstable metrics, 60 known flaky benchmarks, 66 flaky benchmarks without significant changes.
|
Execution-Time Benchmarks Report ⏱️Execution-time results for samples comparing This PR (8814) and master.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Metric | Master (Mean ± 95% CI) | Current (Mean ± 95% CI) | Change | Status |
|---|---|---|---|---|
| .NET Framework 4.8 - Baseline | ||||
| duration | 70.52 ± (70.52 - 70.93) ms | 78.48 ± (78.50 - 79.24) ms | +11.3% | ❌⬆️ |
Full Metrics Comparison
FakeDbCommand
| Metric | Master (Mean ± 95% CI) | Current (Mean ± 95% CI) | Change | Status |
|---|---|---|---|---|
| .NET Framework 4.8 - Baseline | ||||
| duration | 70.52 ± (70.52 - 70.93) ms | 78.48 ± (78.50 - 79.24) ms | +11.3% | ❌⬆️ |
| .NET Framework 4.8 - Bailout | ||||
| duration | 77.32 ± (77.19 - 77.64) ms | 81.85 ± (81.54 - 82.07) ms | +5.9% | ✅⬆️ |
| .NET Framework 4.8 - CallTarget+Inlining+NGEN | ||||
| duration | 1078.34 ± (1077.97 - 1083.89) ms | 1081.66 ± (1093.10 - 1107.58) ms | +0.3% | ✅⬆️ |
| .NET Core 3.1 - Baseline | ||||
| process.internal_duration_ms | 22.16 ± (22.12 - 22.20) ms | 22.35 ± (22.31 - 22.40) ms | +0.9% | ✅⬆️ |
| process.time_to_main_ms | 81.70 ± (81.43 - 81.97) ms | 82.55 ± (82.27 - 82.84) ms | +1.0% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 10.93 ± (10.92 - 10.93) MB | 10.95 ± (10.95 - 10.95) MB | +0.2% | ✅⬆️ |
| runtime.dotnet.threads.count | 12 ± (12 - 12) | 12 ± (12 - 12) | +0.0% | ✅ |
| .NET Core 3.1 - Bailout | ||||
| process.internal_duration_ms | 22.21 ± (22.18 - 22.24) ms | 22.24 ± (22.20 - 22.27) ms | +0.1% | ✅⬆️ |
| process.time_to_main_ms | 83.47 ± (83.25 - 83.68) ms | 83.62 ± (83.42 - 83.83) ms | +0.2% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 10.96 ± (10.96 - 10.97) MB | 10.98 ± (10.97 - 10.98) MB | +0.1% | ✅⬆️ |
| runtime.dotnet.threads.count | 13 ± (13 - 13) | 13 ± (13 - 13) | +0.0% | ✅ |
| .NET Core 3.1 - CallTarget+Inlining+NGEN | ||||
| process.internal_duration_ms | 208.18 ± (207.20 - 209.17) ms | 210.33 ± (209.49 - 211.16) ms | +1.0% | ✅⬆️ |
| process.time_to_main_ms | 527.87 ± (526.80 - 528.95) ms | 527.89 ± (526.68 - 529.10) ms | +0.0% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 48.67 ± (48.63 - 48.71) MB | 48.68 ± (48.63 - 48.73) MB | +0.0% | ✅⬆️ |
| runtime.dotnet.threads.count | 28 ± (28 - 28) | 28 ± (28 - 28) | -0.1% | ✅ |
| .NET 6 - Baseline | ||||
| process.internal_duration_ms | 21.08 ± (21.04 - 21.12) ms | 21.15 ± (21.10 - 21.20) ms | +0.3% | ✅⬆️ |
| process.time_to_main_ms | 71.37 ± (71.15 - 71.59) ms | 72.82 ± (72.56 - 73.08) ms | +2.0% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 10.61 ± (10.61 - 10.62) MB | 10.65 ± (10.65 - 10.65) MB | +0.3% | ✅⬆️ |
| runtime.dotnet.threads.count | 10 ± (10 - 10) | 10 ± (10 - 10) | +0.0% | ✅ |
| .NET 6 - Bailout | ||||
| process.internal_duration_ms | 20.68 ± (20.65 - 20.71) ms | 20.82 ± (20.79 - 20.84) ms | +0.7% | ✅⬆️ |
| process.time_to_main_ms | 70.93 ± (70.83 - 71.03) ms | 71.71 ± (71.57 - 71.85) ms | +1.1% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 10.72 ± (10.72 - 10.73) MB | 10.76 ± (10.75 - 10.76) MB | +0.3% | ✅⬆️ |
| runtime.dotnet.threads.count | 11 ± (11 - 11) | 11 ± (11 - 11) | +0.0% | ✅ |
| .NET 6 - CallTarget+Inlining+NGEN | ||||
| process.internal_duration_ms | 370.66 ± (368.47 - 372.85) ms | 371.18 ± (368.81 - 373.54) ms | +0.1% | ✅⬆️ |
| process.time_to_main_ms | 533.42 ± (532.22 - 534.61) ms | 536.85 ± (535.56 - 538.13) ms | +0.6% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 50.16 ± (50.14 - 50.19) MB | 50.16 ± (50.14 - 50.18) MB | -0.0% | ✅ |
| runtime.dotnet.threads.count | 28 ± (28 - 28) | 28 ± (28 - 28) | -0.0% | ✅ |
| .NET 8 - Baseline | ||||
| process.internal_duration_ms | 19.09 ± (19.06 - 19.11) ms | 19.01 ± (18.98 - 19.03) ms | -0.4% | ✅ |
| process.time_to_main_ms | 69.33 ± (69.19 - 69.48) ms | 68.69 ± (68.57 - 68.81) ms | -0.9% | ✅ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 7.66 ± (7.65 - 7.67) MB | 7.67 ± (7.67 - 7.68) MB | +0.1% | ✅⬆️ |
| runtime.dotnet.threads.count | 10 ± (10 - 10) | 10 ± (10 - 10) | +0.0% | ✅ |
| .NET 8 - Bailout | ||||
| process.internal_duration_ms | 19.28 ± (19.22 - 19.34) ms | 19.22 ± (19.17 - 19.26) ms | -0.3% | ✅ |
| process.time_to_main_ms | 72.96 ± (72.65 - 73.27) ms | 72.06 ± (71.82 - 72.30) ms | -1.2% | ✅ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 7.70 ± (7.70 - 7.71) MB | 7.74 ± (7.73 - 7.74) MB | +0.4% | ✅⬆️ |
| runtime.dotnet.threads.count | 11 ± (11 - 11) | 11 ± (11 - 11) | +0.0% | ✅ |
| .NET 8 - CallTarget+Inlining+NGEN | ||||
| process.internal_duration_ms | 296.66 ± (294.16 - 299.17) ms | 299.95 ± (297.58 - 302.33) ms | +1.1% | ✅⬆️ |
| process.time_to_main_ms | 484.31 ± (483.33 - 485.29) ms | 483.02 ± (482.07 - 483.98) ms | -0.3% | ✅ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 37.13 ± (37.10 - 37.16) MB | 37.16 ± (37.13 - 37.19) MB | +0.1% | ✅⬆️ |
| runtime.dotnet.threads.count | 27 ± (27 - 27) | 27 ± (27 - 27) | -0.3% | ✅ |
HttpMessageHandler
| Metric | Master (Mean ± 95% CI) | Current (Mean ± 95% CI) | Change | Status |
|---|---|---|---|---|
| .NET Framework 4.8 - Baseline | ||||
| duration | 204.27 ± (204.12 - 205.09) ms | 202.59 ± (202.34 - 203.35) ms | -0.8% | ✅ |
| .NET Framework 4.8 - Bailout | ||||
| duration | 208.62 ± (208.46 - 209.02) ms | 207.08 ± (206.64 - 207.31) ms | -0.7% | ✅ |
| .NET Framework 4.8 - CallTarget+Inlining+NGEN | ||||
| duration | 1208.71 ± (1208.11 - 1214.13) ms | 1208.31 ± (1208.03 - 1213.77) ms | -0.0% | ✅ |
| .NET Core 3.1 - Baseline | ||||
| process.internal_duration_ms | 196.21 ± (195.76 - 196.66) ms | 196.86 ± (196.49 - 197.24) ms | +0.3% | ✅⬆️ |
| process.time_to_main_ms | 85.08 ± (84.79 - 85.37) ms | 85.80 ± (85.53 - 86.07) ms | +0.8% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 3 ± (3 - 3) | 3 ± (3 - 3) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 15.96 ± (15.94 - 15.98) MB | 16.02 ± (16.00 - 16.05) MB | +0.4% | ✅⬆️ |
| runtime.dotnet.threads.count | 20 ± (20 - 20) | 20 ± (20 - 20) | +1.1% | ✅⬆️ |
| .NET Core 3.1 - Bailout | ||||
| process.internal_duration_ms | 195.70 ± (195.31 - 196.09) ms | 196.24 ± (195.82 - 196.66) ms | +0.3% | ✅⬆️ |
| process.time_to_main_ms | 86.36 ± (86.07 - 86.65) ms | 86.68 ± (86.43 - 86.92) ms | +0.4% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 3 ± (3 - 3) | 3 ± (3 - 3) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 16.05 ± (16.03 - 16.08) MB | 16.09 ± (16.07 - 16.12) MB | +0.3% | ✅⬆️ |
| runtime.dotnet.threads.count | 21 ± (21 - 21) | 21 ± (20 - 21) | -1.2% | ✅ |
| .NET Core 3.1 - CallTarget+Inlining+NGEN | ||||
| process.internal_duration_ms | 392.42 ± (391.01 - 393.84) ms | 390.44 ± (388.94 - 391.94) ms | -0.5% | ✅ |
| process.time_to_main_ms | 545.48 ± (543.95 - 547.01) ms | 543.33 ± (542.27 - 544.39) ms | -0.4% | ✅ |
| runtime.dotnet.exceptions.count | 3 ± (3 - 3) | 3 ± (3 - 3) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 58.54 ± (58.31 - 58.76) MB | 58.68 ± (58.45 - 58.90) MB | +0.2% | ✅⬆️ |
| runtime.dotnet.threads.count | 30 ± (30 - 30) | 30 ± (30 - 30) | +0.2% | ✅⬆️ |
| .NET 6 - Baseline | ||||
| process.internal_duration_ms | 201.78 ± (201.31 - 202.26) ms | 202.21 ± (201.80 - 202.63) ms | +0.2% | ✅⬆️ |
| process.time_to_main_ms | 74.34 ± (74.10 - 74.58) ms | 74.78 ± (74.52 - 75.05) ms | +0.6% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 4 ± (4 - 4) | 4 ± (4 - 4) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 16.32 ± (16.30 - 16.35) MB | 16.33 ± (16.30 - 16.36) MB | +0.0% | ✅⬆️ |
| runtime.dotnet.threads.count | 19 ± (19 - 19) | 19 ± (19 - 19) | +0.2% | ✅⬆️ |
| .NET 6 - Bailout | ||||
| process.internal_duration_ms | 199.46 ± (199.03 - 199.90) ms | 201.47 ± (201.09 - 201.85) ms | +1.0% | ✅⬆️ |
| process.time_to_main_ms | 74.44 ± (74.22 - 74.65) ms | 75.32 ± (75.16 - 75.49) ms | +1.2% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 4 ± (4 - 4) | 4 ± (4 - 4) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 16.40 ± (16.37 - 16.43) MB | 16.42 ± (16.39 - 16.44) MB | +0.1% | ✅⬆️ |
| runtime.dotnet.threads.count | 20 ± (20 - 20) | 20 ± (20 - 20) | -0.6% | ✅ |
| .NET 6 - CallTarget+Inlining+NGEN | ||||
| process.internal_duration_ms | 586.73 ± (584.26 - 589.20) ms | 585.14 ± (583.00 - 587.27) ms | -0.3% | ✅ |
| process.time_to_main_ms | 556.27 ± (555.09 - 557.45) ms | 555.90 ± (554.64 - 557.16) ms | -0.1% | ✅ |
| runtime.dotnet.exceptions.count | 4 ± (4 - 4) | 4 ± (4 - 4) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 61.51 ± (61.41 - 61.61) MB | 61.23 ± (61.15 - 61.30) MB | -0.5% | ✅ |
| runtime.dotnet.threads.count | 31 ± (31 - 31) | 31 ± (31 - 31) | +0.1% | ✅⬆️ |
| .NET 8 - Baseline | ||||
| process.internal_duration_ms | 199.56 ± (199.11 - 200.02) ms | 198.73 ± (198.35 - 199.11) ms | -0.4% | ✅ |
| process.time_to_main_ms | 73.86 ± (73.55 - 74.16) ms | 73.53 ± (73.25 - 73.81) ms | -0.4% | ✅ |
| runtime.dotnet.exceptions.count | 4 ± (4 - 4) | 4 ± (4 - 4) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 11.77 ± (11.74 - 11.79) MB | 11.67 ± (11.65 - 11.69) MB | -0.8% | ✅ |
| runtime.dotnet.threads.count | 19 ± (18 - 19) | 18 ± (18 - 18) | -1.0% | ✅ |
| .NET 8 - Bailout | ||||
| process.internal_duration_ms | 198.26 ± (197.93 - 198.59) ms | 197.05 ± (196.74 - 197.37) ms | -0.6% | ✅ |
| process.time_to_main_ms | 74.65 ± (74.48 - 74.82) ms | 73.93 ± (73.76 - 74.09) ms | -1.0% | ✅ |
| runtime.dotnet.exceptions.count | 4 ± (4 - 4) | 4 ± (4 - 4) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 11.81 ± (11.79 - 11.83) MB | 11.77 ± (11.75 - 11.79) MB | -0.3% | ✅ |
| runtime.dotnet.threads.count | 19 ± (19 - 19) | 19 ± (19 - 19) | -0.1% | ✅ |
| .NET 8 - CallTarget+Inlining+NGEN | ||||
| process.internal_duration_ms | 512.05 ± (509.23 - 514.86) ms | 514.69 ± (511.54 - 517.83) ms | +0.5% | ✅⬆️ |
| process.time_to_main_ms | 503.01 ± (502.07 - 503.94) ms | 502.69 ± (501.81 - 503.56) ms | -0.1% | ✅ |
| runtime.dotnet.exceptions.count | 4 ± (4 - 4) | 4 ± (4 - 4) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 50.85 ± (50.81 - 50.90) MB | 50.97 ± (50.93 - 51.00) MB | +0.2% | ✅⬆️ |
| runtime.dotnet.threads.count | 30 ± (30 - 30) | 30 ± (29 - 30) | -1.1% | ✅ |
Comparison explanation
Execution-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:
- Welch test with statistical test for significance of 5%
- Only results indicating a difference greater than 5% and 5 ms are considered.
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 charts
FakeDbCommand (.NET Framework 4.8)
gantt
title Execution time (ms) FakeDbCommand (.NET Framework 4.8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8814) - mean (79ms) : 73, 84
master - mean (71ms) : 68, 74
section Bailout
This PR (8814) - mean (82ms) : 78, 86
master - mean (77ms) : 74, 81
section CallTarget+Inlining+NGEN
This PR (8814) - mean (1,100ms) : 996, 1205
master - mean (1,081ms) : 1038, 1124
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 (8814) - mean (112ms) : 106, 118
master - mean (111ms) : 105, 116
section Bailout
This PR (8814) - mean (113ms) : 108, 119
master - mean (113ms) : 108, 118
section CallTarget+Inlining+NGEN
This PR (8814) - mean (775ms) : 755, 796
master - mean (773ms) : 752, 794
FakeDbCommand (.NET 6)
gantt
title Execution time (ms) FakeDbCommand (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8814) - mean (101ms) : 95, 106
master - mean (99ms) : 94, 104
section Bailout
This PR (8814) - mean (99ms) : 95, 102
master - mean (98ms) : 96, 99
section CallTarget+Inlining+NGEN
This PR (8814) - mean (938ms) : 901, 975
master - mean (937ms) : 898, 976
FakeDbCommand (.NET 8)
gantt
title Execution time (ms) FakeDbCommand (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8814) - mean (94ms) : 91, 97
master - mean (95ms) : 92, 99
section Bailout
This PR (8814) - mean (99ms) : 93, 105
master - mean (100ms) : 95, 105
section CallTarget+Inlining+NGEN
This PR (8814) - mean (813ms) : 774, 851
master - mean (811ms) : 776, 845
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 (8814) - mean (203ms) : 197, 209
master - mean (205ms) : 199, 210
section Bailout
This PR (8814) - mean (207ms) : 204, 210
master - mean (209ms) : 206, 211
section CallTarget+Inlining+NGEN
This PR (8814) - mean (1,211ms) : 1170, 1252
master - mean (1,211ms) : 1167, 1256
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 (8814) - mean (292ms) : 286, 298
master - mean (292ms) : 285, 298
section Bailout
This PR (8814) - mean (292ms) : 287, 298
master - mean (292ms) : 287, 297
section CallTarget+Inlining+NGEN
This PR (8814) - mean (974ms) : 948, 999
master - mean (981ms) : 956, 1006
HttpMessageHandler (.NET 6)
gantt
title Execution time (ms) HttpMessageHandler (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8814) - mean (287ms) : 281, 292
master - mean (286ms) : 279, 293
section Bailout
This PR (8814) - mean (286ms) : 282, 290
master - mean (283ms) : 277, 290
section CallTarget+Inlining+NGEN
This PR (8814) - mean (1,172ms) : 1139, 1204
master - mean (1,174ms) : 1131, 1217
HttpMessageHandler (.NET 8)
gantt
title Execution time (ms) HttpMessageHandler (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8814) - mean (283ms) : 276, 289
master - mean (284ms) : 277, 290
section Bailout
This PR (8814) - mean (281ms) : 277, 285
master - mean (283ms) : 279, 287
section CallTarget+Inlining+NGEN
This PR (8814) - mean (1,049ms) : 993, 1105
master - mean (1,047ms) : 1003, 1091
c4e441b to
3171ba7
Compare
This comment has been minimized.
This comment has been minimized.
3171ba7 to
5eaf36e
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
GetRawConstantValue, including enum constants with their enum type.Reason for change
Reading static members through reflection can trigger a customer type initializer (
.cctor) during debugger snapshot or expression evaluation. This narrows static capture to cases that are metadata-safe while preserving existing behavior for constants and cctor-free types.Implementation details
Static member safety is centralized in
StaticMemberSafety. Static fields/properties are read only when their declaring type has noTypeInitializer; literal constants use metadata-only raw constant reads. Expression dumps filter unsafe static fields beforeGetValueand use the same literal handling.Implementation details
Static member safety is centralized in
StaticMemberSafety.A static member is still captured when:
const/FieldInfo.IsLiteral), read viaGetRawConstantValueTypeInitializer, so there is no.cctorto triggerA static member is skipped when its declaring type has a
TypeInitializer. It can be emitted either from an explicitstatic TypeName()constructor or from static field/property initializers such aspublic static string Field = "value"orpublic static string Property { get; } = "value".The guard intentionally does not special-case
beforefieldinit.beforefieldinitchanges when the runtime is allowed to run the type initializer, but it does not give us a public, non-triggering way to know whether the initializer has already run. Because a reflective static read can still be the operation that causes customer code to execute, any member on a type withTypeInitializer != nullis treated as unsafe unless it is a literal constant.Test coverage
DebuggerSnapshotCreatorTests|DebuggerExpressionLanguageTests|DebuggerintegrationTests