datadog 2.42.0 → 2.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +67 -2
  3. data/README.md +3 -0
  4. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +46 -5
  5. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +6 -7
  6. data/ext/datadog_profiling_native_extension/collectors_stack.c +6 -7
  7. data/ext/datadog_profiling_native_extension/collectors_stack.h +1 -1
  8. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +80 -36
  9. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +3 -2
  10. data/ext/datadog_profiling_native_extension/extconf.rb +4 -2
  11. data/ext/datadog_profiling_native_extension/heap_recorder.c +13 -0
  12. data/ext/datadog_profiling_native_extension/heap_recorder.h +4 -0
  13. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +34 -2
  14. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +1 -0
  15. data/ext/datadog_profiling_native_extension/profiling.c +1 -0
  16. data/ext/datadog_profiling_native_extension/stack_recorder.c +17 -4
  17. data/ext/datadog_profiling_native_extension/time_helpers.h +5 -4
  18. data/ext/libdatadog_api/di.c +8 -3
  19. data/ext/libdatadog_api/extconf.rb +1 -1
  20. data/ext/libdatadog_api/feature_flags.c +8 -0
  21. data/ext/libdatadog_api/init.c +3 -1
  22. data/ext/libdatadog_api/otel_thread_context.c +51 -15
  23. data/ext/libdatadog_api/trace_exporter.c +642 -22
  24. data/ext/libdatadog_extconf_helpers.rb +14 -9
  25. data/lib/datadog/ai_guard/component.rb +17 -8
  26. data/lib/datadog/ai_guard/configuration/ext.rb +2 -0
  27. data/lib/datadog/ai_guard/configuration.rb +13 -0
  28. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +48 -55
  29. data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +1 -1
  30. data/lib/datadog/ai_guard/contrib/ruby_llm/message_adapter.rb +76 -0
  31. data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +1 -0
  32. data/lib/datadog/ai_guard/contrib/ruby_llm/redaction_change_set.rb +135 -0
  33. data/lib/datadog/ai_guard/evaluation/client.rb +50 -0
  34. data/lib/datadog/ai_guard/evaluation/content_part.rb +12 -0
  35. data/lib/datadog/ai_guard/evaluation/message.rb +25 -7
  36. data/lib/datadog/ai_guard/evaluation/message_builder.rb +31 -0
  37. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +6 -6
  38. data/lib/datadog/ai_guard/evaluation/outcome.rb +24 -0
  39. data/lib/datadog/ai_guard/evaluation/request.rb +4 -60
  40. data/lib/datadog/ai_guard/evaluation/response.rb +33 -0
  41. data/lib/datadog/ai_guard/evaluation/result.rb +11 -18
  42. data/lib/datadog/ai_guard/evaluation/tool_call.rb +15 -2
  43. data/lib/datadog/ai_guard/evaluation.rb +25 -11
  44. data/lib/datadog/ai_guard/ext.rb +1 -0
  45. data/lib/datadog/ai_guard/{api_client.rb → http_client.rb} +6 -4
  46. data/lib/datadog/ai_guard/metrics/telemetry.rb +43 -0
  47. data/lib/datadog/ai_guard/redaction/replacements.rb +97 -0
  48. data/lib/datadog/ai_guard/redaction/result.rb +29 -0
  49. data/lib/datadog/ai_guard/redaction.rb +80 -0
  50. data/lib/datadog/ai_guard.rb +58 -57
  51. data/lib/datadog/core/configuration/components.rb +9 -1
  52. data/lib/datadog/core/configuration/settings.rb +5 -2
  53. data/lib/datadog/core/configuration/supported_configurations.rb +8 -0
  54. data/lib/datadog/core/remote/client/capabilities.rb +10 -4
  55. data/lib/datadog/core/remote/component.rb +9 -2
  56. data/lib/datadog/core/remote/worker.rb +8 -0
  57. data/lib/datadog/di/instrumenter.rb +1 -1
  58. data/lib/datadog/di/probe_manager.rb +10 -5
  59. data/lib/datadog/di/proc_responder.rb +1 -1
  60. data/lib/datadog/open_feature/agentless/configuration_source.rb +246 -0
  61. data/lib/datadog/open_feature/agentless/parser.rb +43 -0
  62. data/lib/datadog/open_feature/agentless/response.rb +19 -0
  63. data/lib/datadog/open_feature/agentless/transport.rb +103 -0
  64. data/lib/datadog/open_feature/component.rb +57 -1
  65. data/lib/datadog/open_feature/configuration/agentless_endpoint.rb +90 -0
  66. data/lib/datadog/open_feature/configuration/source/resolution.rb +22 -0
  67. data/lib/datadog/open_feature/configuration/source.rb +43 -0
  68. data/lib/datadog/open_feature/configuration.rb +118 -0
  69. data/lib/datadog/open_feature/native_evaluator.rb +1 -14
  70. data/lib/datadog/open_feature/remote.rb +5 -5
  71. data/lib/datadog/open_feature/transport.rb +5 -0
  72. data/lib/datadog/opentelemetry/signal_configuration.rb +13 -9
  73. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +2 -2
  74. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +5 -7
  75. data/lib/datadog/profiling/collectors/info.rb +2 -6
  76. data/lib/datadog/symbol_database/extractor.rb +84 -102
  77. data/lib/datadog/tracing/component.rb +2 -0
  78. data/lib/datadog/tracing/configuration/ext.rb +1 -0
  79. data/lib/datadog/tracing/configuration/settings.rb +6 -0
  80. data/lib/datadog/tracing/context.rb +24 -2
  81. data/lib/datadog/tracing/context_provider.rb +9 -5
  82. data/lib/datadog/tracing/contrib/grape/endpoint.rb +32 -5
  83. data/lib/datadog/tracing/contrib/sequel/jdbc_connection_string.rb +140 -0
  84. data/lib/datadog/tracing/contrib/sequel/utils.rb +49 -62
  85. data/lib/datadog/tracing/metadata/ext.rb +3 -0
  86. data/lib/datadog/tracing/otel_thread_context.rb +78 -9
  87. data/lib/datadog/tracing/tracer.rb +15 -2
  88. data/lib/datadog/tracing/transport/native.rb +18 -26
  89. data/lib/datadog/tracing/transport/span_events_negotiation.rb +41 -0
  90. data/lib/datadog/tracing/transport/trace_formatter.rb +13 -3
  91. data/lib/datadog/tracing/transport/traces.rb +3 -23
  92. data/lib/datadog/version.rb +1 -1
  93. metadata +25 -7
  94. data/lib/datadog/ai_guard/evaluation/content_builder.rb +0 -31
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dfc982d67dbb768eb43d510dd6322ee484bc0a0fdacf20deb40196aa13821e51
4
- data.tar.gz: be416fd9f34cbdf7ead90fa03d273f1739e90008cb82b6a0d22d5f62f17e1660
3
+ metadata.gz: c60f4ef57b9508827fac3387bd07b4233666ed9d22684c0d063187d05dcfc449
4
+ data.tar.gz: 6e795bd6f5fc8b5f59d040a4da62e636a15d12024ff4e6b5c2b10d0989c047ec
5
5
  SHA512:
6
- metadata.gz: 80d39181feb3e02e4f022d06f0c6bab26e993f48521c2fb5e526d3b81a5b4a68e7069d92af90638700f2e0a0af24efbd0df63de311c7561cb02c1270d63cbaaa
7
- data.tar.gz: 44932c7f87c3a52e1790b44ae2c4f13ae63006360a0846c240ec0b6ac3ffe4b1c98ff33426b19f27f51ce5105770ab263870601ebe5b33788381debd85a04b37
6
+ metadata.gz: 27f955e40d920907b2af84f36f8671f4e306235dce525be84da5221e2c66b852183974095afbbcc4acff4148e9202f585067e676d897b20da08c41e535846e9c
7
+ data.tar.gz: 29f4f51331feb1e0a6e70437490b8067b9e4a6f561b55c7f1b5fff58ae326964d92cf8e2dcf16c31466b5f654b851a25e8b683322795573e5ee3742fb395f519
data/CHANGELOG.md CHANGED
@@ -2,6 +2,50 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [2.44.0] - 2026-10-07
6
+
7
+ ### Added
8
+
9
+ * AI Guard: Add sensitive-data redaction to evaluated messages, enabled by default; configure it with `ai_guard.redaction_enabled` or `DD_AI_GUARD_REDACTION_ENABLED`. ([#6394][])
10
+ * Open Feature: Add agentless Feature Flags configuration delivery and settings for selecting agentless, Remote Configuration, or offline delivery. ([#6291][])
11
+
12
+ ### Changed
13
+
14
+ * AI Guard: Require `ruby_llm` 2.0 or later for the `RubyLLM` integration. ([#6394][])
15
+ * Dynamic Instrumentation: Condition evaluation error snapshots are now subject to the process-wide Dynamic Instrumentation snapshot rate limit in addition to the per-probe limit. ([#6250][])
16
+ * OpenTelemetry: Emit `deployment.environment.name` instead of `deployment.environment` for Logs and Metrics resources, and accept it as the Datadog environment while retaining `deployment.environment` as a fallback. ([#6261][])
17
+
18
+ ### Fixed
19
+
20
+ * Core: Fix native extension build failures when installing gems in a directory whose path contains spaces. ([#6371][])
21
+ * Core: Fix Remote Configuration polling remaining stopped in forked web workers when application warmup started it before the fork. ([#6427][])
22
+ * Tracing: Fix the `grape` integration creating no `grape.endpoint_run` span on Grape 4, which moved the API class, HTTP verb and route path off `Grape::Endpoint#options`. ([#6354][]) ([@dduugg][])
23
+ * Tracing: Fix an empty `http.route` tag on spans for 405 responses when running `grape` versions 2.3 through 3.0. ([#6358][])
24
+ * Profiling: Fix the profiler stopping with `BUG: Unexpected CPU time going backwards between samples`. ([#6364][])
25
+ * Profiling: Fix a sampling race condition during profiler shutdown. ([#6375][])
26
+ * Profiling: Fix a potential crash during garbage collection compaction when profiling is enabled. ([#6395][])
27
+ * Profiling: Stop profiling if `thread_name_for`/`rb_thread_t` changes. ([#6398][])
28
+ * Profiling: Fix native extension build breaking when trying to log %. ([#6407][])
29
+ * Dynamic Instrumentation: Fix methods disappearing from code search and source lookup when method logpoints are already installed. ([#6339][])
30
+ * Dynamic Instrumentation: Fix missing condition evaluation error snapshots for method probes with a `when` condition; errors raised while evaluating the condition are now reported to the backend. ([#6250][])
31
+
32
+ ## [2.43.0] - 2026-09-22
33
+
34
+ ### Added
35
+
36
+ * Core: Add an experimental `libdatadog`-backed native trace transport with full feature parity. Opt in with `DD_EXPERIMENTAL_NATIVE_TRANSPORT_ENABLED=true`. ([#5971][])
37
+ * SSI: Support `force_ruby_platform` during Ruby dependency resolution. ([#6316][])
38
+
39
+ ### Fixed
40
+
41
+ * SSI: Respect transitive application dependencies during Ruby injection. ([#6316][])
42
+ * SSI: Preserve checksums in injected Ruby lockfiles. ([#6320][])
43
+ * SSI: Preserve Bundler selection and `RUBYOPT` options during injection. ([#6316][])
44
+ * SSI: Support versioned and symlinked Bundler launchers. ([#6316][])
45
+ * SSI: Restore Ruby injection for deployment and vendored bundles. ([#6316][])
46
+ * Profiling: Fix a potential sampling race when Ruby is idle. ([#6286][])
47
+ * Profiling: Fix profiler not starting on Ruby 4 due to `undefined symbol: rb_current_box`. ([#6350][])
48
+
5
49
  ## [2.42.0] - 2026-08-31
6
50
 
7
51
  ### Added
@@ -3776,7 +3820,9 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
3776
3820
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3777
3821
 
3778
3822
 
3779
- [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.42.0...master
3823
+ [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.44.0...master
3824
+ [2.44.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.43.0...v2.44.0
3825
+ [2.43.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.42.0...v2.43.0
3780
3826
  [2.42.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.41.0...v2.42.0
3781
3827
  [2.41.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.40.0...v2.41.0
3782
3828
  [2.40.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.39.0...v2.40.0
@@ -5610,6 +5656,24 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
5610
5656
  [#6232]: https://github.com/DataDog/dd-trace-rb/issues/6232
5611
5657
  [#6234]: https://github.com/DataDog/dd-trace-rb/issues/6234
5612
5658
  [#6242]: https://github.com/DataDog/dd-trace-rb/issues/6242
5659
+ [#6250]: https://github.com/DataDog/dd-trace-rb/issues/6250
5660
+ [#6261]: https://github.com/DataDog/dd-trace-rb/issues/6261
5661
+ [#6286]: https://github.com/DataDog/dd-trace-rb/issues/6286
5662
+ [#6291]: https://github.com/DataDog/dd-trace-rb/issues/6291
5663
+ [#6316]: https://github.com/DataDog/dd-trace-rb/issues/6316
5664
+ [#6320]: https://github.com/DataDog/dd-trace-rb/issues/6320
5665
+ [#6339]: https://github.com/DataDog/dd-trace-rb/issues/6339
5666
+ [#6350]: https://github.com/DataDog/dd-trace-rb/issues/6350
5667
+ [#6354]: https://github.com/DataDog/dd-trace-rb/issues/6354
5668
+ [#6358]: https://github.com/DataDog/dd-trace-rb/issues/6358
5669
+ [#6364]: https://github.com/DataDog/dd-trace-rb/issues/6364
5670
+ [#6371]: https://github.com/DataDog/dd-trace-rb/issues/6371
5671
+ [#6375]: https://github.com/DataDog/dd-trace-rb/issues/6375
5672
+ [#6394]: https://github.com/DataDog/dd-trace-rb/issues/6394
5673
+ [#6395]: https://github.com/DataDog/dd-trace-rb/issues/6395
5674
+ [#6398]: https://github.com/DataDog/dd-trace-rb/issues/6398
5675
+ [#6407]: https://github.com/DataDog/dd-trace-rb/issues/6407
5676
+ [#6427]: https://github.com/DataDog/dd-trace-rb/issues/6427
5613
5677
  [@AdrianLC]: https://github.com/AdrianLC
5614
5678
  [@Azure7111]: https://github.com/Azure7111
5615
5679
  [@BabyGroot]: https://github.com/BabyGroot
@@ -5662,6 +5726,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
5662
5726
  [@cwoodcox]: https://github.com/cwoodcox
5663
5727
  [@danhodge]: https://github.com/danhodge
5664
5728
  [@dasch]: https://github.com/dasch
5729
+ [@dduugg]: https://github.com/dduugg
5665
5730
  [@dim]: https://github.com/dim
5666
5731
  [@dirk]: https://github.com/dirk
5667
5732
  [@djmb]: https://github.com/djmb
@@ -5765,4 +5830,4 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
5765
5830
  [@y-yagi]: https://github.com/y-yagi
5766
5831
  [@yujideveloper]: https://github.com/yujideveloper
5767
5832
  [@yukimurasawa]: https://github.com/yukimurasawa
5768
- [@zachmccormick]: https://github.com/zachmccormick
5833
+ [@zachmccormick]: https://github.com/zachmccormick
data/README.md CHANGED
@@ -17,8 +17,11 @@ For descriptions of terminology used in APM, take a look at the [APM Terms and C
17
17
 
18
18
  For contributing, checkout the [contribution guidelines][contribution docs] and [development guide][development docs].
19
19
 
20
+ To check that a downloaded gem file was published by this repository, see [how to verify gem provenance][provenance docs].
21
+
20
22
  [public docs]: https://docs.datadoghq.com/tracing/setup/ruby/
21
23
  [api docs]: https://datadog.github.io/dd-trace-rb/
22
24
  [APM glossary]: https://docs.datadoghq.com/tracing/glossary/
23
25
  [contribution docs]: https://github.com/DataDog/dd-trace-rb/blob/master/CONTRIBUTING.md
24
26
  [development docs]: https://github.com/DataDog/dd-trace-rb/blob/master/docs/DevelopmentGuide.md
27
+ [provenance docs]: https://github.com/DataDog/dd-trace-rb/blob/master/docs/Provenance.md
@@ -228,6 +228,7 @@ static VALUE _native_new(VALUE klass);
228
228
  static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
229
229
  static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr);
230
230
  static VALUE _native_sampling_loop(VALUE self, VALUE instance);
231
+ static VALUE _native_profiler_internal_thread_done(VALUE self_instance);
231
232
  static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread);
232
233
  static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *optional_exception_during_operation);
233
234
  static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception, const char *optional_operation_name);
@@ -289,6 +290,7 @@ static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instan
289
290
  static VALUE handle_sampling_failure_rescued_commit_heap_recordings(VALUE self_instance, VALUE exception);
290
291
  static inline void during_sample_enter(cpu_and_wall_time_worker_state* state);
291
292
  static inline void during_sample_exit(cpu_and_wall_time_worker_state* state);
293
+ static VALUE during_sample_exit_rescue(VALUE state_ptr);
292
294
  static void commit_heap_recordings_from_postponed_job_may_lose_gvl(DDTRACE_UNUSED void *_unused);
293
295
 
294
296
  // We're using `on_newobj_event` function with `rb_add_event_hook2`, which requires in its public signature a function
@@ -380,6 +382,7 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
380
382
 
381
383
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_initialize", _native_initialize, -1);
382
384
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_sampling_loop", _native_sampling_loop, 1);
385
+ rb_define_method(collectors_cpu_and_wall_time_worker_class, "_native_profiler_internal_thread_done", _native_profiler_internal_thread_done, 0);
383
386
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stop", _native_stop, 2);
384
387
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_reset_after_fork", _native_reset_after_fork, 1);
385
388
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stats", _native_stats, 1);
@@ -650,6 +653,17 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
650
653
  return Qnil;
651
654
  }
652
655
 
656
+ static VALUE _native_profiler_internal_thread_done(VALUE self_instance) {
657
+ cpu_and_wall_time_worker_state *state;
658
+ TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
659
+
660
+ during_sample_enter(state);
661
+ return rb_ensure(
662
+ thread_context_collector_profiler_internal_thread_done, state->thread_context_collector_instance,
663
+ during_sample_exit_rescue, (VALUE) state
664
+ );
665
+ }
666
+
653
667
  static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread) {
654
668
  cpu_and_wall_time_worker_state *state;
655
669
  TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
@@ -740,6 +754,8 @@ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED si
740
754
  // a) we get triggered using SIGPROF, and the docs state a second SIGPROF will not interrupt an existing one (see sigaction docs on sa_mask)
741
755
  // b) we validate we are in the thread that has the global VM lock; if a different thread gets a signal, it will return early
742
756
  // because it will not have the global VM lock
757
+ // c) `simulate_sampling_signal_delivery` calls us directly instead of via a real SIGPROF, and blocks SIGPROF delivery on its
758
+ // thread for the duration of that call, so it can't be nested into by a real SIGPROF either
743
759
 
744
760
  state->stats.signal_handler_enqueued_sample++;
745
761
 
@@ -749,11 +765,20 @@ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED si
749
765
  !state->during_sample;
750
766
 
751
767
  if (sample_from_signal_handler) {
752
- // Buffer current stack trace. Note that this will not actually record the sample, for that we still need to wait
753
- // until the postponed job below gets run.
754
- bool prepared = thread_context_collector_prepare_sample_inside_signal_handler();
768
+ if (rb_during_gc()) {
769
+ // During GC Ruby might be marking our buffer or compacting (moving) where iseqs live so we skip preparing the
770
+ // sample immediately, and instead ask on_gc_finish to do the job when GC is at its end.
771
+ //
772
+ // When GC profiling is disabled, we just fall back to sampling the stack after GC finishes, e.g. it's the
773
+ // equivalent of disabling `sample_from_signal_handler` for samples that happen during GC.
774
+ thread_context_collector_request_prepare_on_gc_finish();
775
+ } else {
776
+ // Buffer current stack trace. Note that this will not actually record the sample, for that we still need to wait
777
+ // until the postponed job below gets run.
778
+ bool prepared = thread_context_collector_prepare_sample_inside_signal_handler();
755
779
 
756
- if (prepared) state->stats.signal_handler_prepared_sample++;
780
+ if (prepared) state->stats.signal_handler_prepared_sample++;
781
+ }
757
782
  }
758
783
 
759
784
  #ifndef NO_POSTPONED_TRIGGER // Ruby 3.3+
@@ -1094,7 +1119,10 @@ static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused) {
1094
1119
  if (event == RUBY_INTERNAL_EVENT_GC_ENTER) {
1095
1120
  thread_context_collector_on_gc_start(state->thread_context_collector_instance);
1096
1121
  } else if (event == RUBY_INTERNAL_EVENT_GC_EXIT) {
1097
- bool should_flush = thread_context_collector_on_gc_finish(state->thread_context_collector_instance);
1122
+ bool allow_prepare_sample = !state->during_sample;
1123
+ if (allow_prepare_sample) during_sample_enter(state);
1124
+ bool should_flush = thread_context_collector_on_gc_finish(state->thread_context_collector_instance, allow_prepare_sample);
1125
+ if (allow_prepare_sample) during_sample_exit(state);
1098
1126
 
1099
1127
  // We use rb_postponed_job_register_one to ask Ruby to run thread_context_collector_sample_after_gc when the
1100
1128
  // thread collector flags it's time to flush.
@@ -1279,11 +1307,19 @@ void *simulate_sampling_signal_delivery(DDTRACE_UNUSED void *_unused) {
1279
1307
  // This can potentially happen if the CpuAndWallTimeWorker was stopped while the IdleSamplingHelper was trying to execute this action
1280
1308
  if (state == NULL) return NULL;
1281
1309
 
1310
+ // Since this is not a real signal firing, we need to block SIGPROF delivery on this thread to avoid an actual SIGPROF
1311
+ // signal coming in nested and interrupting us on this thread. Thus we respect the invariant of "no nesting" for `handle_sampling_signal`.
1312
+ //
1313
+ // Not needed when `no_signals_workaround_enabled` is set: no SIGPROFs are ever sent in that mode, so there's nothing to mask.
1314
+ if (!state->no_signals_workaround_enabled) block_sigprof_signal_handler_from_running_in_current_thread();
1315
+
1282
1316
  state->stats.simulated_signal_delivery++;
1283
1317
 
1284
1318
  // `handle_sampling_signal` does a few things extra on top of `sample_from_postponed_job` so that's why we don't shortcut here
1285
1319
  handle_sampling_signal(0, NULL, NULL);
1286
1320
 
1321
+ if (!state->no_signals_workaround_enabled) unblock_sigprof_signal_handler_from_running_in_current_thread();
1322
+
1287
1323
  return NULL; // Unused
1288
1324
  }
1289
1325
 
@@ -1765,3 +1801,8 @@ static inline void during_sample_exit(cpu_and_wall_time_worker_state* state) {
1765
1801
  atomic_signal_fence(memory_order_seq_cst);
1766
1802
  state->during_sample = false;
1767
1803
  }
1804
+
1805
+ static VALUE during_sample_exit_rescue(VALUE state_ptr) {
1806
+ during_sample_exit((cpu_and_wall_time_worker_state *) state_ptr);
1807
+ return Qnil;
1808
+ }
@@ -31,9 +31,9 @@ typedef struct {
31
31
 
32
32
  static VALUE _native_new(VALUE klass);
33
33
  static void reset_state(idle_sampling_loop_state *state);
34
- static VALUE _native_idle_sampling_loop(DDTRACE_UNUSED VALUE self, VALUE self_instance, VALUE thread_context_collector_instance);
34
+ static VALUE _native_idle_sampling_loop(DDTRACE_UNUSED VALUE self, VALUE self_instance, VALUE cpu_and_wall_time_worker_instance);
35
35
  static VALUE idle_sampling_loop_body(VALUE self_instance);
36
- static VALUE idle_sampling_loop_ensure(VALUE thread_context_collector_instance);
36
+ static VALUE idle_sampling_loop_ensure(VALUE cpu_and_wall_time_worker_instance);
37
37
  static VALUE _native_stop(DDTRACE_UNUSED VALUE self, VALUE self_instance);
38
38
  static void *run_idle_sampling_loop(void *state_ptr);
39
39
  static void interrupt_idle_sampling_loop(void *state_ptr);
@@ -112,8 +112,8 @@ static VALUE _native_reset(DDTRACE_UNUSED VALUE self, VALUE self_instance) {
112
112
  return Qtrue;
113
113
  }
114
114
 
115
- static VALUE _native_idle_sampling_loop(DDTRACE_UNUSED VALUE self, VALUE self_instance, VALUE thread_context_collector_instance) {
116
- return rb_ensure(idle_sampling_loop_body, self_instance, idle_sampling_loop_ensure, thread_context_collector_instance);
115
+ static VALUE _native_idle_sampling_loop(DDTRACE_UNUSED VALUE self, VALUE self_instance, VALUE cpu_and_wall_time_worker_instance) {
116
+ return rb_ensure(idle_sampling_loop_body, self_instance, idle_sampling_loop_ensure, cpu_and_wall_time_worker_instance);
117
117
  }
118
118
 
119
119
  static VALUE idle_sampling_loop_body(VALUE self_instance) {
@@ -128,9 +128,8 @@ static VALUE idle_sampling_loop_body(VALUE self_instance) {
128
128
  return Qtrue;
129
129
  }
130
130
 
131
- static VALUE idle_sampling_loop_ensure(VALUE thread_context_collector_instance) {
132
- thread_context_collector_profiler_internal_thread_done(thread_context_collector_instance);
133
- return Qnil;
131
+ static VALUE idle_sampling_loop_ensure(VALUE cpu_and_wall_time_worker_instance) {
132
+ return rb_funcall(cpu_and_wall_time_worker_instance, rb_intern("_native_profiler_internal_thread_done"), 0);
134
133
  }
135
134
 
136
135
  static void *run_idle_sampling_loop(void *state_ptr) {
@@ -642,8 +642,8 @@ void record_placeholder_stack(
642
642
  }
643
643
 
644
644
  bool prepare_sample_thread(VALUE thread, sampling_buffer *buffer) {
645
- // Since this can get called from inside a signal handler, we don't want to touch the buffer if
646
- // the thread was actually in the middle of marking it.
645
+ // While the signal handler checks if there's GC going on, it's actually possible for `sampling_buffer_mark` to
646
+ // still run outside of GC (see comments on that function) and thus we guard for it here.
647
647
  if (buffer->is_marking) return false;
648
648
 
649
649
  buffer->pending_sample = true;
@@ -705,12 +705,11 @@ void sampling_buffer_mark(sampling_buffer *buffer) {
705
705
  }
706
706
 
707
707
  buffer->is_marking = true;
708
- // Tell the compiler it's not allowed to reorder the `is_marking` flag with the iteration below.
709
- //
710
- // Specifically, in the middle of `sampling_buffer_mark` a signal handler may execute and call
711
- // `prepare_sample_thread` to add a new sample to the buffer. This flag is here to prevent that BUT we need to
712
- // make sure the signal handler actually sees the flag being set.
708
+ // The sampling buffer isn't supposed to be touched during GC. Yet, marking can happen outside of GC -- in particular
709
+ // ObjectSpace.dump/dump_all and reachable_objects_from invoke mark callbacks outside GC to enumerate references.
710
+ // Thus we keep this flag to tell the signal handler "hey you're still not allowed to touch the buffer at this time".
713
711
  //
712
+ // The fences ensure the compiler doesn't reorder the flags and a signal handler observes them
714
713
  // See https://github.com/ruby/ruby/pull/11036 for a similar change made to the Ruby VM with more context.
715
714
  atomic_signal_fence(memory_order_seq_cst);
716
715
 
@@ -13,7 +13,7 @@ typedef struct {
13
13
  uint16_t max_frames;
14
14
  frame_info *stack_buffer;
15
15
  bool pending_sample;
16
- bool is_marking; // Used to avoid recording a sample when marking
16
+ bool is_marking; // Used to avoid recording a sample when marking (marking can happen outside of GC as well)
17
17
  int pending_sample_result;
18
18
  } sampling_buffer;
19
19