[NO-TICKET] Profiling: Clean up logic for tracking time spent in GC#6006
Conversation
**What does this PR do?** This PR refactors the `update_time_since_previous_sample` to remove the weird logic we had where cpu-time wasn't allowed to advance if we tried to sample between `on_gc_start` and `on_gc_finish`. This logic was a leftover of past versions of the code where part of the time accounting for GC was split between `on_gc_finish` and `sample_after_gc`. Because we can't stop Ruby from GCing as many times as it wants before we're able to run `sample_after_gc`, this "freezing the clock" made sure we never lost GC time. Yet, this became unneeded since #3313 as `on_gc_finish` immediately records all of the needed information. Thus, we no longer need to deal with the above corner case, and we can massively simplify this logic. **Motivation:** Clean up unneeded and confusing logic. **Additional Notes:** N/A **How to test the change?** I've added a test that an exception gets raised if somehow a sample happens between `on_gc_start` and `on_gc_finish`. The rest of the logic is unchanged, so existing test coverage is enough.
We were passing ten million arguments yet only support cpu and wall-time so let's clean this up.
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: a062690 | Docs | Datadog PR Page | Give us feedback! |
eregon
left a comment
There was a problem hiding this comment.
Amazing cleanup!
Now I trust update_time_since_previous_sample does the right thing and not sometimes something fishy for the overhead :)
|
Maybe we can cleanup some usages & checks of INVALID_TIME after this, I'll try to take a look after this PR is merged. |
…xt.c Co-authored-by: Benoit Daloze <[email protected]>
Yup... The thought crossed my mind as well. The thing that I was unsure about was how to deal with syscall failures when dealing with the clocks -- in theory they are sources of |
BenchmarksBenchmark execution time: 2026-07-08 13:42:24 Comparing candidate commit a062690 in PR branch Found 0 performance improvements and 0 performance regressions! Performance is the same for 48 metrics, 1 unstable metrics.
|
…try 2 **What does this PR do?** This PR is a second attempt at #6006 ; the first time got reverted in #6012 as we were seeing flaky failures. From DataDog/ruby-guild#314: ``` W, [2026-07-08T16:58:22.863694 #11835] WARN -- datadog: [datadog] CpuAndWallTimeWorker thread error. Operation: "rescued_sample_from_postponed_job" Cause: RuntimeError: BUG: Unexpected sample during GC (thread_id=11835 (10200), gc_tracking.cpu_time_at_start_ns=2802579244, gc_tracking.wall_time_at_start_ns=729604100602, monotonic_wall_time_now_ns=729890337067) Location: /__w/dd-trace-rb/dd-trace-rb/lib/datadog/profiling/collectors/idle_sampling_helper.rb:42:in 'Datadog::Profiling::Collectors::IdleSamplingHelper._native_idle_sampling_loop' W, [2026-07-08T16:58:22.863821 #11835] WARN -- datadog: [datadog] Failed to send telemetry before components initialization or within components lifecycle records garbage collection cycles (FAILED - 3) marks the new thread as fork-safe does not create a second thread if start is called again with allocation profiling enabled /__w/dd-trace-rb/dd-trace-rb/spec/datadog/profiling/collectors/cpu_and_wall_time_worker_spec.rb:43: [BUG] Datadog Ruby profiler detected callback nested inside sample. Please report this at https://github.com/datadog/dd-trace-rb/blob/master/CONTRIBUTING.md#found-a-bug ruby 4.0.1 (2026-01-13 revision e04267a14b) +PRISM [x86_64-linux] ``` (The "callback nested inside sample" happens because some of our tests have strict assertions that things like object allocations can't happen, and they were failing when trying to raise that `RunntimeError: BUG: Unexpected sample during GC` as well) The problem we were seeing was due to the `#on_gc_start` specs never clearing the "thread is in GC" state and thus this state was leaking to other tests which then would trigger the assertion we added. The fix is to make sure we don't leave such leftover state; in the future #5960 is a more holistic solution to this whole "leftover state between profiler instances" problem. I recommend reviewing this PR commit-by-commit. **Motivation:** The original cleanup is useful so we definitely want to keep it! **Additional Notes:** ``` bundle exec rspec './spec/datadog/profiling/collectors/cpu_and_wall_time_worker_spec.rb[1:2:12:1,1:2:12:2,1:2:13,1:2:15:1,1:2:16,1:2:18:1,1:2:18:2:1,1:2:20:1,1:7:1]' './spec/datadog/profiling/collectors/thread_context_spec.rb[1:3:2:1]' --seed 32922 ``` is one way of reproducing the issue. **How to test the change?** We should see a green CI and no flakiness.
What does this PR do?
This PR refactors the
update_time_since_previous_sampleto remove the weird logic we had where cpu-time wasn't allowed to advance if we tried to sample betweenon_gc_startandon_gc_finish.This logic was a leftover of past versions of the code where part of the time accounting for GC was split between
on_gc_finishandsample_after_gc. Because we can't stop Ruby from GCing as many times as it wants before we're able to runsample_after_gc, this "freezing the clock" made sure we never lost GC time.Yet, this became unneeded since #3313 as
on_gc_finishimmediately records all of the needed information.Thus, we no longer need to deal with the above corner case, and we can massively simplify this logic.
Motivation:
Clean up unneeded and confusing logic.
Change log entry
None. (The whole point of this change is that this logic wasn't actually used in practice anymore)
Additional Notes:
I also took the opportunity to simplify the callers of
update_time_since_previous_sampleusing helpers, since we were passing in a million arguments but only need to support two different cases.How to test the change?
I've added a test that an exception gets raised if somehow a sample happens between
on_gc_startandon_gc_finish.The rest of the logic is unchanged, so existing test coverage is enough.