Skip to content

DI: capture real positional parameter names in non-virtual method probes - #6114

Draft
p-datadog wants to merge 6 commits into
masterfrom
di-method-probe-real-arg-names
Draft

DI: capture real positional parameter names in non-virtual method probes#6114
p-datadog wants to merge 6 commits into
masterfrom
di-method-probe-real-arg-names

Conversation

@p-datadog

@p-datadog p-datadog commented Jul 28, 2026

Copy link
Copy Markdown
Member

What does this PR do?

Captures real positional parameter names in probes on explicitly defined methods (i.e. NOT method_missing / C code).

Motivation:

The arg1 etc. placeholders are not discoverable by customers.

Change log entry

Yes. Dynamic Instrumentation: report positional method
argument names for methods explicitly defined in Ruby

Additional Notes:

Virtual methods (method_missing) and C code still doesn't reveal the argument names. arg1 etc. placeholders remain for those.

How to test the change?

Unit and integration tests added

Method-probe snapshots keyed captured positional arguments as arg1,
arg2, ... because the prepend wrapper captures them as *args, losing the
declared names. Recover the names at hook time via UnboundMethod#parameters
(the same reflection the symbol database extractor uses), thread them
through run_method_probe into the serializer, and key each positional
argument by its real name. Arguments without an available name (generated
methods, values absorbed by a splat, virtual/C methods) keep the arg-N
label. Keyword arguments and self are unchanged.

This also lets capture expressions reference positional parameters by
their real names.
@p-datadog p-datadog added the AI Generated Largely based on code generated by an AI or LLM. This label is the same across all dd-trace-* repos label Jul 28, 2026
@dd-octo-sts dd-octo-sts Bot added the debugger Live Debugger (+Dynamic Instrumentation, +Symbol Database) label Jul 28, 2026
@dd-octo-sts

dd-octo-sts Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Typing analysis

Note: Ignored files are excluded from the next sections.

steep:ignore comments

This PR introduces 12 steep:ignore comments, and clears 12 steep:ignore comments.

steep:ignore comments (+12-12)Introduced:
lib/datadog/di/instrumenter.rb:197
lib/datadog/di/instrumenter.rb:202
lib/datadog/di/instrumenter.rb:206
lib/datadog/di/instrumenter.rb:211
lib/datadog/di/instrumenter.rb:215
lib/datadog/di/instrumenter.rb:234
lib/datadog/di/instrumenter.rb:295
lib/datadog/di/instrumenter.rb:297
lib/datadog/di/instrumenter.rb:940
lib/datadog/di/serializer.rb:284
lib/datadog/di/serializer.rb:416
lib/datadog/di/serializer.rb:531
Cleared:
lib/datadog/di/instrumenter.rb:196
lib/datadog/di/instrumenter.rb:201
lib/datadog/di/instrumenter.rb:205
lib/datadog/di/instrumenter.rb:210
lib/datadog/di/instrumenter.rb:214
lib/datadog/di/instrumenter.rb:233
lib/datadog/di/instrumenter.rb:294
lib/datadog/di/instrumenter.rb:296
lib/datadog/di/instrumenter.rb:903
lib/datadog/di/serializer.rb:257
lib/datadog/di/serializer.rb:389
lib/datadog/di/serializer.rb:504

Untyped methods

This PR introduces 5 partially typed methods, and clears 5 partially typed methods. It increases the percentage of typed methods from 69.55% to 69.56% (+0.01%).

Partially typed methods (+5-5)Introduced:
sig/datadog/di/instrumenter.rbs:63
└── def run_method_probe: (::Array[untyped] args, ::Hash[::Symbol, untyped] kwargs, ::Proc? target_block, Object target_self, Probe probe, untyped responder, [::String, ::Integer]? loc, ::String method_name, ?::Array[::Symbol?]? positional_param_names) { () -> untyped } -> untyped
sig/datadog/di/instrumenter.rbs:75
└── def line_trace_point_callback: (Probe probe, RubyVM::InstructionSequence? iseq, untyped responder, TracePoint tp) -> void
sig/datadog/di/instrumenter.rbs:79
└── def check_and_disable_if_exceeded: (Probe probe, untyped responder, Float di_start_time, ?Float accumulated_duration) -> void
sig/datadog/di/serializer.rbs:24
└── def combine_args: (Array[untyped] args, Hash[Symbol, untyped] kwargs, Object target_self, ?Array[Symbol?]? param_names) -> Hash[Symbol, untyped]
sig/datadog/di/serializer.rbs:25
└── def serialize_args: (Array[untyped] args, Hash[Symbol, untyped] kwargs, untyped instance_vars, ?Array[Symbol?]? param_names, ?depth: Integer, ?attribute_count: Integer?, ?length: Integer?, ?collection_size: Integer?) -> Hash[Symbol, untyped]
Cleared:
sig/datadog/di/instrumenter.rbs:63
└── def run_method_probe: (::Array[untyped] args, ::Hash[::Symbol, untyped] kwargs, ::Proc? target_block, Object target_self, Probe probe, untyped responder, [::String, ::Integer]? loc, ::String method_name) { () -> untyped } -> untyped
sig/datadog/di/instrumenter.rbs:73
└── def line_trace_point_callback: (Probe probe, RubyVM::InstructionSequence? iseq, untyped responder, TracePoint tp) -> void
sig/datadog/di/instrumenter.rbs:77
└── def check_and_disable_if_exceeded: (Probe probe, untyped responder, Float di_start_time, ?Float accumulated_duration) -> void
sig/datadog/di/serializer.rbs:24
└── def combine_args: (Array[untyped] args, Hash[Symbol, untyped] kwargs, Object target_self) -> Hash[Symbol, untyped]
sig/datadog/di/serializer.rbs:25
└── def serialize_args: (Array[untyped] args, Hash[Symbol, untyped] kwargs, untyped instance_vars, ?depth: Integer, ?attribute_count: Integer?, ?length: Integer?, ?collection_size: Integer?) -> Hash[Symbol, untyped]

If you believe a method or an attribute is rightfully untyped or partially typed, you can add # untyped:accept on the line before the definition to remove it from the stats.

@datadog-official

datadog-official Bot commented Jul 28, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 13.64%
Overall Coverage: 90.00% (-0.03%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 2aaa948 | Docs | Datadog PR Page | Give us feedback!

p-ddsign and others added 2 commits July 28, 2026 13:40
Omit the explicit StandardError class in the positional-parameter-name
extraction rescue (rescue => e), fixing the standard/lint CI failure
(bundle exec rubocop -D).

Verified under gemfiles/ruby-4.0.gemfile: `bundle exec rubocop -D`
reports no offenses and `bundle exec rake standard` passes.

Co-Authored-By: Claude <noreply@anthropic.com>
…cs, RBS prefix

- extract_positional_param_names: rescue Exception + reraise_if_fatal + telemetry
  (matches the DI catch-all convention used elsewhere in the file)
- combine_args / serialize_args: full YARD param/return docs after signature change
- instrumenter.rbs: ::UnboundMethod prefix
@p-datadog p-datadog changed the title DI: label method-probe positional args with real parameter names DI: capture real positional parameter names in non-virtual method probes Jul 28, 2026
@pr-commenter

pr-commenter Bot commented Jul 28, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-07-28 18:10:44

Comparing candidate commit 2aaa948 in PR branch di-method-probe-real-arg-names with baseline commit e1e1961 in branch master.

📊 Benchmarking dashboard

Found 0 performance improvements and 0 performance regressions! Performance is the same for 48 metrics, 1 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

Unstable benchmarks

These benchmarks have a confidence interval too wide to call a change; treat them as noise rather than signal.

scenario:tracing - trace.to_digest - Continue

  • unstable throughput [-1458.943op/s; +1549.838op/s] or [-4.986%; +5.296%]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Generated Largely based on code generated by an AI or LLM. This label is the same across all dd-trace-* repos debugger Live Debugger (+Dynamic Instrumentation, +Symbol Database)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants