datadog 2.28.0 → 2.30.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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -1
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +82 -12
  4. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +32 -11
  5. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +3 -1
  6. data/ext/datadog_profiling_native_extension/extconf.rb +9 -24
  7. data/ext/datadog_profiling_native_extension/heap_recorder.c +186 -55
  8. data/ext/datadog_profiling_native_extension/heap_recorder.h +12 -1
  9. data/ext/datadog_profiling_native_extension/http_transport.c +51 -64
  10. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +0 -13
  11. data/ext/datadog_profiling_native_extension/profiling.c +3 -1
  12. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +24 -8
  13. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -3
  14. data/ext/datadog_profiling_native_extension/stack_recorder.c +63 -48
  15. data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -1
  16. data/ext/libdatadog_api/crashtracker.c +5 -0
  17. data/ext/libdatadog_api/crashtracker_report_exception.c +126 -0
  18. data/ext/libdatadog_api/extconf.rb +4 -21
  19. data/ext/libdatadog_extconf_helpers.rb +49 -11
  20. data/lib/datadog/ai_guard/configuration/settings.rb +3 -0
  21. data/lib/datadog/appsec/assets/blocked.html +2 -1
  22. data/lib/datadog/appsec/configuration/settings.rb +14 -0
  23. data/lib/datadog/appsec/context.rb +44 -9
  24. data/lib/datadog/appsec/contrib/active_record/patcher.rb +3 -0
  25. data/lib/datadog/appsec/contrib/devise/integration.rb +1 -1
  26. data/lib/datadog/appsec/contrib/excon/patcher.rb +2 -0
  27. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +55 -6
  28. data/lib/datadog/appsec/contrib/faraday/integration.rb +1 -1
  29. data/lib/datadog/appsec/contrib/faraday/patcher.rb +1 -1
  30. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +60 -7
  31. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +11 -6
  32. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +1 -1
  33. data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -1
  34. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +6 -10
  35. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +4 -4
  36. data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
  37. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +26 -5
  38. data/lib/datadog/appsec/contrib/rack/response_body.rb +36 -0
  39. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +2 -2
  40. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
  41. data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.rb +2 -0
  42. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +2 -0
  43. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +72 -7
  44. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +5 -3
  45. data/lib/datadog/appsec/counter_sampler.rb +25 -0
  46. data/lib/datadog/appsec/event.rb +1 -17
  47. data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +2 -3
  48. data/lib/datadog/appsec/instrumentation/gateway.rb +2 -2
  49. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +18 -0
  50. data/lib/datadog/appsec/monitor/gateway/watcher.rb +2 -2
  51. data/lib/datadog/appsec/security_engine/engine.rb +23 -2
  52. data/lib/datadog/appsec/utils/http/body.rb +38 -0
  53. data/lib/datadog/appsec/utils/http/media_range.rb +2 -1
  54. data/lib/datadog/appsec/utils/http/media_type.rb +28 -35
  55. data/lib/datadog/appsec/utils/http/url_encoded.rb +52 -0
  56. data/lib/datadog/core/configuration/components.rb +29 -4
  57. data/lib/datadog/core/configuration/option.rb +2 -1
  58. data/lib/datadog/core/configuration/options.rb +1 -1
  59. data/lib/datadog/core/configuration/settings.rb +27 -3
  60. data/lib/datadog/core/configuration/supported_configurations.rb +19 -0
  61. data/lib/datadog/core/configuration.rb +2 -2
  62. data/lib/datadog/core/crashtracking/component.rb +71 -19
  63. data/lib/datadog/core/environment/agent_info.rb +65 -1
  64. data/lib/datadog/core/logger.rb +1 -1
  65. data/lib/datadog/core/metrics/logging.rb +1 -1
  66. data/lib/datadog/core/process_discovery.rb +20 -19
  67. data/lib/datadog/core/rate_limiter.rb +2 -0
  68. data/lib/datadog/core/remote/component.rb +16 -5
  69. data/lib/datadog/core/remote/transport/config.rb +5 -11
  70. data/lib/datadog/core/runtime/metrics.rb +1 -2
  71. data/lib/datadog/core/telemetry/component.rb +0 -13
  72. data/lib/datadog/core/telemetry/transport/telemetry.rb +5 -6
  73. data/lib/datadog/core/transport/ext.rb +1 -0
  74. data/lib/datadog/core/transport/http/response.rb +4 -0
  75. data/lib/datadog/core/transport/parcel.rb +61 -9
  76. data/lib/datadog/core/utils/base64.rb +1 -1
  77. data/lib/datadog/core/utils/fnv.rb +26 -0
  78. data/lib/datadog/core/workers/interval_loop.rb +13 -6
  79. data/lib/datadog/core/workers/queue.rb +0 -4
  80. data/lib/datadog/core/workers/runtime_metrics.rb +9 -1
  81. data/lib/datadog/core.rb +6 -1
  82. data/lib/datadog/data_streams/processor.rb +35 -33
  83. data/lib/datadog/data_streams/transport/http/stats.rb +6 -0
  84. data/lib/datadog/data_streams/transport/http.rb +0 -4
  85. data/lib/datadog/data_streams/transport/stats.rb +5 -12
  86. data/lib/datadog/di/boot.rb +1 -0
  87. data/lib/datadog/di/component.rb +17 -5
  88. data/lib/datadog/di/configuration/settings.rb +9 -0
  89. data/lib/datadog/di/context.rb +6 -0
  90. data/lib/datadog/di/instrumenter.rb +183 -134
  91. data/lib/datadog/di/probe.rb +10 -1
  92. data/lib/datadog/di/probe_file_loader.rb +2 -2
  93. data/lib/datadog/di/probe_manager.rb +86 -64
  94. data/lib/datadog/di/probe_notification_builder.rb +46 -18
  95. data/lib/datadog/di/probe_notifier_worker.rb +65 -9
  96. data/lib/datadog/di/probe_repository.rb +198 -0
  97. data/lib/datadog/di/proc_responder.rb +4 -0
  98. data/lib/datadog/di/remote.rb +6 -7
  99. data/lib/datadog/di/serializer.rb +127 -9
  100. data/lib/datadog/di/transport/diagnostics.rb +5 -7
  101. data/lib/datadog/di/transport/http/diagnostics.rb +3 -1
  102. data/lib/datadog/di/transport/http/input.rb +1 -1
  103. data/lib/datadog/di/transport/http.rb +12 -3
  104. data/lib/datadog/di/transport/input.rb +51 -14
  105. data/lib/datadog/kit/tracing/method_tracer.rb +132 -0
  106. data/lib/datadog/open_feature/configuration.rb +2 -0
  107. data/lib/datadog/open_feature/transport.rb +8 -11
  108. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +13 -0
  109. data/lib/datadog/profiling/component.rb +20 -6
  110. data/lib/datadog/profiling/http_transport.rb +5 -6
  111. data/lib/datadog/profiling/profiler.rb +15 -8
  112. data/lib/datadog/tracing/contrib/dalli/integration.rb +4 -1
  113. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +5 -1
  114. data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
  115. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +5 -2
  116. data/lib/datadog/tracing/contrib/excon/ext.rb +1 -0
  117. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +5 -2
  118. data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
  119. data/lib/datadog/tracing/contrib/grape/endpoint.rb +2 -2
  120. data/lib/datadog/tracing/contrib/grape/instrumentation.rb +13 -8
  121. data/lib/datadog/tracing/contrib/grape/patcher.rb +6 -1
  122. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +5 -2
  123. data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
  124. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +5 -2
  125. data/lib/datadog/tracing/contrib/http/ext.rb +1 -0
  126. data/lib/datadog/tracing/contrib/http/integration.rb +0 -2
  127. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +5 -2
  128. data/lib/datadog/tracing/contrib/httpclient/ext.rb +1 -0
  129. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +5 -2
  130. data/lib/datadog/tracing/contrib/httprb/ext.rb +1 -0
  131. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +5 -1
  132. data/lib/datadog/tracing/contrib/karafka/ext.rb +1 -0
  133. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +6 -0
  134. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +2 -1
  135. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +6 -0
  136. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +2 -1
  137. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +10 -0
  138. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
  139. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +24 -0
  140. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +5 -2
  141. data/lib/datadog/tracing/contrib/que/ext.rb +1 -0
  142. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +5 -1
  143. data/lib/datadog/tracing/contrib/rack/ext.rb +1 -0
  144. data/lib/datadog/tracing/contrib/rack/route_inference.rb +18 -6
  145. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +5 -2
  146. data/lib/datadog/tracing/contrib/rails/ext.rb +1 -0
  147. data/lib/datadog/tracing/contrib/registerable.rb +11 -0
  148. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +5 -2
  149. data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
  150. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +5 -1
  151. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
  152. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +5 -1
  153. data/lib/datadog/tracing/contrib/sinatra/ext.rb +1 -0
  154. data/lib/datadog/tracing/contrib/sneakers/integration.rb +15 -4
  155. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +6 -0
  156. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +3 -1
  157. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +5 -1
  158. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +1 -0
  159. data/lib/datadog/tracing/metadata/ext.rb +4 -0
  160. data/lib/datadog/tracing/sync_writer.rb +0 -1
  161. data/lib/datadog/tracing/transport/io/client.rb +5 -8
  162. data/lib/datadog/tracing/transport/io/traces.rb +28 -34
  163. data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
  164. data/lib/datadog/tracing/transport/traces.rb +4 -10
  165. data/lib/datadog/tracing/writer.rb +0 -1
  166. data/lib/datadog/version.rb +1 -1
  167. metadata +14 -8
  168. data/lib/datadog/appsec/contrib/rails/ext.rb +0 -13
  169. data/lib/datadog/tracing/workers/trace_writer.rb +0 -204
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 91ec2ddb3720f61843ee7526cd4e3493a646ab7afaf3d6e4cce71019d1dfd582
4
- data.tar.gz: d0996f8b1eb568aea751a1e781fa4d9dcc7e1d436911fd579c7aa43947340f32
3
+ metadata.gz: f2983471f43d0c9b3473b3c65d0ca656514c6cf8cd7a32e35ec26826193fe533
4
+ data.tar.gz: 687d088d90ce4ee0ae5af66d9e604135f058bd7609641f4c7963f1ba32a2bb37
5
5
  SHA512:
6
- metadata.gz: fb8da926cd9a09a500c781f60ad5fb35c72c9b3162acc067229f88d01281c63c6753c4230a2cf56f803fb0174994707b159e31f5d584e6682313a13e2187b61e
7
- data.tar.gz: 8c93304f737b5798af5d6ed520f7006d7aa2e7644243fbfcd38994d66dde0aefb53084bca96024e61a7cbebcb72e60f958715fe4864c1e7fe90733103f9cb527
6
+ metadata.gz: 96f3744eadab0292666e18beac7deab2a6b7ef61b0da216ab4490e7621b0cec7839d18a519079b5b0563e2a260388b63ef59b5243a65c3d6ba997f3c7981986c
7
+ data.tar.gz: a82f01f50d499f1a08309f23144e5453cb9cf79bdc5b0c7daa9b3c9c373e3a41983babe3f86dc027f2dd2e9f056bc4b7645a49f799531e40eae9455ab7e00a6d
data/CHANGELOG.md CHANGED
@@ -2,6 +2,59 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [2.30.0] - 2026-03-19
6
+
7
+ ### Added
8
+
9
+ * Core: Enable `libdatadog`-based features and tests on macOS ([#5351][])
10
+ * Tracing: Add `_dd.p.ksr` propagated tag to transmit Knuth sampling rate for backend resampling ([#5436][])
11
+ * Tracing: Integrations: Add `DD_TRACE_<INTEGRATION>_DISTRIBUTED_TRACING` environment variables to control distributed tracing per integration ([#5396][])
12
+ * Profiling: Add profiler setting `experimental_use_system_dns` to use system DNS resolver (defaults to true) ([#5425][], [#5449][])
13
+ * Profiling: Allow lowering CPU profiling sampling interval with `experimental_cpu_sampling_interval_ms` setting ([#5424][])
14
+
15
+ ### Changed
16
+
17
+ * Core: Enable process tags by default by setting `DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED`, expanding tag collection for Tracing, Database Monitoring, Data Streams Monitoring, Profiling, Runtime Metrics, Process Discovery, Remote Configuration, Telemetry, Dynamic Instrumentation, and CrashTracking ([#5432][])
18
+ * Core: Upgrade `libdatadog` dependency to version 29.0.0 ([#5274][], [#5461][])
19
+ * Core: Remove dependency on `pkg-config` system tool for native extension builds ([#5469][])
20
+ * Profiling: Improve profiling error message when another profiler is present ([#5375][])
21
+
22
+ ### Fixed
23
+
24
+ * Tracing: Integrations: Fix `endpoint_render.grape` ActiveSupport notification instrumentation for `grape` 3.x ([#5414][])
25
+ * Tracing: Integrations: Fix compatibility with `dalli` version 5.x and later ([#5435][])
26
+ * Dynamic Instrumentation: Fix JSON serialization failures when snapshots contain binary data and invalid UTF-8 strings ([#5434][])
27
+ * Dynamic Instrumentation: Show ERROR probe status when custom serializers produce non-JSON-encodable data instead of silent failures ([#5448][])
28
+ * Data Streams: Fix Data Streams Monitoring to correctly report the configured environment instead of showing `env:none` ([#5427][])
29
+
30
+ ## [2.29.0] - 2026-02-20
31
+
32
+
33
+ ### Added
34
+
35
+ * AppSec: Add analysis for downstream request with redirects ([#5347][])
36
+ * AppSec: Add downstream request body analysis. ([#5320][])
37
+ * SSI: Add support for Bundler vendored mode (`BUNDLE_PATH`) ([#5368][])
38
+ * SSI: Default to local dependency resolution ([#5368][])
39
+ * Dynamic Instrumentation: Added circuit breaker to automatically disable probes consuming excessive CPU time ([#5335][])
40
+ * Crashtracking: Add reporting of unhandled exceptions ([#5321][])
41
+ * Tracing: Add support for the `kicks` gem ([#5305][])
42
+ * Profiling: Add heap profiling for ruby 4.x ([#5201][])
43
+ * Tracing: Add simple method tracing API ([#5294][])
44
+ * Tracing: Add `trace_singleton_class_method` for tracing singleton class methods. ([#5334][])
45
+
46
+ ### Changed
47
+
48
+ * Dynamic Instrumentation: Improve error reporting when instrumentation fails or is removed due to circuit breaker ([#5371][])
49
+ * SSI: Reduce SSI package size ([#5352][])
50
+ * Core: Change default logger output from stdout to stderr ([#5342][])
51
+ * AppSec: Make AppSec blocking page more friendly for vulnerability scanners ([#5341][])
52
+ * Core: Add process tags and container id to process discovery payloads when the experimental setting `DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED=true` is enabled. ([#5336][])
53
+
54
+ ### Fixed
55
+
56
+ * Dynamic Instrumentation: Fix Live Debugger UI for forking web servers with more than one worker process ([#5304][])
57
+
5
58
  ## [2.28.0] - 2026-02-04
6
59
 
7
60
  ### Added
@@ -3486,7 +3539,9 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
3486
3539
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3487
3540
 
3488
3541
 
3489
- [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.28.0...master
3542
+ [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.30.0...master
3543
+ [2.30.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.29.0...v2.30.0
3544
+ [2.29.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.28.0...v2.29.0
3490
3545
  [2.28.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.27.0...v2.28.0
3491
3546
  [2.27.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.26.0...v2.27.0
3492
3547
  [2.26.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.25.0...v2.26.0
@@ -5153,6 +5208,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
5153
5208
  [#5176]: https://github.com/DataDog/dd-trace-rb/issues/5176
5154
5209
  [#5194]: https://github.com/DataDog/dd-trace-rb/issues/5194
5155
5210
  [#5197]: https://github.com/DataDog/dd-trace-rb/issues/5197
5211
+ [#5201]: https://github.com/DataDog/dd-trace-rb/issues/5201
5156
5212
  [#5206]: https://github.com/DataDog/dd-trace-rb/issues/5206
5157
5213
  [#5210]: https://github.com/DataDog/dd-trace-rb/issues/5210
5158
5214
  [#5215]: https://github.com/DataDog/dd-trace-rb/issues/5215
@@ -5164,8 +5220,38 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
5164
5220
  [#5247]: https://github.com/DataDog/dd-trace-rb/issues/5247
5165
5221
  [#5254]: https://github.com/DataDog/dd-trace-rb/issues/5254
5166
5222
  [#5273]: https://github.com/DataDog/dd-trace-rb/issues/5273
5223
+ [#5274]: https://github.com/DataDog/dd-trace-rb/issues/5274
5167
5224
  [#5278]: https://github.com/DataDog/dd-trace-rb/issues/5278
5168
5225
  [#5283]: https://github.com/DataDog/dd-trace-rb/issues/5283
5226
+ [#5294]: https://github.com/DataDog/dd-trace-rb/issues/5294
5227
+ [#5304]: https://github.com/DataDog/dd-trace-rb/issues/5304
5228
+ [#5305]: https://github.com/DataDog/dd-trace-rb/issues/5305
5229
+ [#5320]: https://github.com/DataDog/dd-trace-rb/issues/5320
5230
+ [#5321]: https://github.com/DataDog/dd-trace-rb/issues/5321
5231
+ [#5334]: https://github.com/DataDog/dd-trace-rb/issues/5334
5232
+ [#5335]: https://github.com/DataDog/dd-trace-rb/issues/5335
5233
+ [#5336]: https://github.com/DataDog/dd-trace-rb/issues/5336
5234
+ [#5341]: https://github.com/DataDog/dd-trace-rb/issues/5341
5235
+ [#5342]: https://github.com/DataDog/dd-trace-rb/issues/5342
5236
+ [#5347]: https://github.com/DataDog/dd-trace-rb/issues/5347
5237
+ [#5351]: https://github.com/DataDog/dd-trace-rb/issues/5351
5238
+ [#5352]: https://github.com/DataDog/dd-trace-rb/issues/5352
5239
+ [#5368]: https://github.com/DataDog/dd-trace-rb/issues/5368
5240
+ [#5371]: https://github.com/DataDog/dd-trace-rb/issues/5371
5241
+ [#5375]: https://github.com/DataDog/dd-trace-rb/issues/5375
5242
+ [#5396]: https://github.com/DataDog/dd-trace-rb/issues/5396
5243
+ [#5414]: https://github.com/DataDog/dd-trace-rb/issues/5414
5244
+ [#5424]: https://github.com/DataDog/dd-trace-rb/issues/5424
5245
+ [#5425]: https://github.com/DataDog/dd-trace-rb/issues/5425
5246
+ [#5427]: https://github.com/DataDog/dd-trace-rb/issues/5427
5247
+ [#5432]: https://github.com/DataDog/dd-trace-rb/issues/5432
5248
+ [#5434]: https://github.com/DataDog/dd-trace-rb/issues/5434
5249
+ [#5435]: https://github.com/DataDog/dd-trace-rb/issues/5435
5250
+ [#5436]: https://github.com/DataDog/dd-trace-rb/issues/5436
5251
+ [#5448]: https://github.com/DataDog/dd-trace-rb/issues/5448
5252
+ [#5449]: https://github.com/DataDog/dd-trace-rb/issues/5449
5253
+ [#5461]: https://github.com/DataDog/dd-trace-rb/issues/5461
5254
+ [#5469]: https://github.com/DataDog/dd-trace-rb/issues/5469
5169
5255
  [@AdrianLC]: https://github.com/AdrianLC
5170
5256
  [@Azure7111]: https://github.com/Azure7111
5171
5257
  [@BabyGroot]: https://github.com/BabyGroot
@@ -87,6 +87,7 @@ unsigned int MAX_ALLOC_WEIGHT = 10000;
87
87
  static rb_postponed_job_handle_t sample_from_postponed_job_handle;
88
88
  static rb_postponed_job_handle_t after_gc_from_postponed_job_handle;
89
89
  static rb_postponed_job_handle_t after_gvl_running_from_postponed_job_handle;
90
+ static rb_postponed_job_handle_t after_allocation_from_postponed_job_handle;
90
91
  #endif
91
92
 
92
93
  // Contains state for a single CpuAndWallTimeWorker instance
@@ -101,6 +102,7 @@ typedef struct {
101
102
  bool gvl_profiling_enabled;
102
103
  bool skip_idle_samples_for_testing;
103
104
  bool sighandler_sampling_enabled;
105
+ uint32_t cpu_sampling_interval_ms;
104
106
  VALUE self_instance;
105
107
  VALUE thread_context_collector_instance;
106
108
  VALUE idle_sampling_helper_instance;
@@ -138,6 +140,8 @@ typedef struct {
138
140
  // # Generic stats
139
141
  // How many times we tried to trigger a sample
140
142
  unsigned int trigger_sample_attempts;
143
+ // How many times extra sleep was triggered by dynamic sampling rate
144
+ unsigned int trigger_sample_extra_sleep;
141
145
  // How many times we tried to simulate signal delivery
142
146
  unsigned int trigger_simulated_signal_delivery_attempts;
143
147
  // How many times we actually simulated signal delivery
@@ -229,23 +233,26 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
229
233
  static void disable_tracepoints(cpu_and_wall_time_worker_state *state);
230
234
  static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self);
231
235
  static VALUE rescued_sample_allocation(VALUE tracepoint_data);
236
+ static VALUE rescued_after_allocation(VALUE self_instance);
232
237
  static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error);
233
238
  static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state);
234
239
  static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
235
240
  static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self);
236
241
  static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);
237
242
  #ifndef NO_GVL_INSTRUMENTATION
238
- static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused);
239
- static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
243
+ static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused);
244
+ static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
245
+ static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance);
246
+ static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception);
240
247
  #endif
241
- static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance);
242
248
  static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance);
243
249
  static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception);
244
250
  static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception);
245
251
  static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception);
246
- static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception);
252
+ static VALUE handle_sampling_failure_rescued_after_allocation(VALUE self_instance, VALUE exception);
247
253
  static inline void during_sample_enter(cpu_and_wall_time_worker_state* state);
248
254
  static inline void during_sample_exit(cpu_and_wall_time_worker_state* state);
255
+ static void after_allocation_from_postponed_job(DDTRACE_UNUSED void *_unused);
249
256
 
250
257
  // We're using `on_newobj_event` function with `rb_add_event_hook2`, which requires in its public signature a function
251
258
  // with signature `rb_event_hook_func_t` which doesn't match `on_newobj_event`.
@@ -293,11 +300,13 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
293
300
  sample_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, sample_from_postponed_job, NULL);
294
301
  after_gc_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gc_from_postponed_job, NULL);
295
302
  after_gvl_running_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gvl_running_from_postponed_job, NULL);
303
+ after_allocation_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_allocation_from_postponed_job, NULL);
296
304
 
297
305
  if (
298
306
  sample_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
299
307
  after_gc_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
300
- after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
308
+ after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
309
+ after_allocation_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
301
310
  ) {
302
311
  raise_error(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
303
312
  }
@@ -375,6 +384,7 @@ static VALUE _native_new(VALUE klass) {
375
384
  state->gvl_profiling_enabled = false;
376
385
  state->skip_idle_samples_for_testing = false;
377
386
  state->sighandler_sampling_enabled = false;
387
+ state->cpu_sampling_interval_ms = 10;
378
388
  state->thread_context_collector_instance = Qnil;
379
389
  state->idle_sampling_helper_instance = Qnil;
380
390
  state->owner_thread = Qnil;
@@ -419,6 +429,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
419
429
  VALUE gvl_profiling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("gvl_profiling_enabled")));
420
430
  VALUE skip_idle_samples_for_testing = rb_hash_fetch(options, ID2SYM(rb_intern("skip_idle_samples_for_testing")));
421
431
  VALUE sighandler_sampling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("sighandler_sampling_enabled")));
432
+ VALUE cpu_sampling_interval_ms = rb_hash_fetch(options, ID2SYM(rb_intern("cpu_sampling_interval_ms")));
422
433
 
423
434
  ENFORCE_BOOLEAN(gc_profiling_enabled);
424
435
  ENFORCE_BOOLEAN(no_signals_workaround_enabled);
@@ -429,6 +440,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
429
440
  ENFORCE_BOOLEAN(gvl_profiling_enabled);
430
441
  ENFORCE_BOOLEAN(skip_idle_samples_for_testing)
431
442
  ENFORCE_BOOLEAN(sighandler_sampling_enabled)
443
+ ENFORCE_TYPE(cpu_sampling_interval_ms, T_FIXNUM);
432
444
 
433
445
  cpu_and_wall_time_worker_state *state;
434
446
  TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
@@ -441,6 +453,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
441
453
  state->gvl_profiling_enabled = (gvl_profiling_enabled == Qtrue);
442
454
  state->skip_idle_samples_for_testing = (skip_idle_samples_for_testing == Qtrue);
443
455
  state->sighandler_sampling_enabled = (sighandler_sampling_enabled == Qtrue);
456
+ state->cpu_sampling_interval_ms = NUM2INT(cpu_sampling_interval_ms);
444
457
 
445
458
  double total_overhead_target_percentage = NUM2DBL(dynamic_sampling_rate_overhead_target_percentage);
446
459
  if (!state->allocation_profiling_enabled) {
@@ -548,8 +561,9 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
548
561
  // @ivoanjo: I suspect this will never happen, but the cost of getting it wrong is really high (VM terminates) so this
549
562
  // is a just-in-case situation.
550
563
  //
551
- // Note 2: This can raise exceptions as well, so make sure that all cleanups are done by the time we get here.
552
- replace_sigprof_signal_handler_with_empty_handler(handle_sampling_signal);
564
+ // Note 2: If exception_state is set, we have a pending exception that we'll re-raise below.
565
+ // In that case, we don't want replace_sigprof_signal_handler_with_empty_handler to raise another exception.
566
+ replace_sigprof_signal_handler_with_empty_handler(handle_sampling_signal, !exception_state);
553
567
 
554
568
  // Ensure that instance is not garbage collected while the native sampling loop is running; this is probably not needed, but just in case
555
569
  RB_GC_GUARD(instance);
@@ -660,7 +674,7 @@ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED si
660
674
  static void *run_sampling_trigger_loop(void *state_ptr) {
661
675
  cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
662
676
 
663
- uint64_t minimum_time_between_signals = MILLIS_AS_NS(10);
677
+ uint64_t minimum_time_between_signals = MILLIS_AS_NS(state->cpu_sampling_interval_ms);
664
678
 
665
679
  while (atomic_load(&state->should_run)) {
666
680
  state->stats.trigger_sample_attempts++;
@@ -713,7 +727,10 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
713
727
  // `dynamic_sampling_rate_get_sleep` may have changed while the above sleep was ongoing.
714
728
  uint64_t extra_sleep =
715
729
  dynamic_sampling_rate_get_sleep(&state->cpu_dynamic_sampling_rate, monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE));
716
- if (state->dynamic_sampling_rate_enabled && extra_sleep > 0) sleep_for(extra_sleep);
730
+ if (state->dynamic_sampling_rate_enabled && extra_sleep > 0) {
731
+ state->stats.trigger_sample_extra_sleep++;
732
+ sleep_for(extra_sleep);
733
+ }
717
734
  }
718
735
 
719
736
  return NULL; // Unused
@@ -1049,6 +1066,7 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
1049
1066
  VALUE stats_as_hash = rb_hash_new();
1050
1067
  VALUE arguments[] = {
1051
1068
  ID2SYM(rb_intern("trigger_sample_attempts")), /* => */ UINT2NUM(state->stats.trigger_sample_attempts),
1069
+ ID2SYM(rb_intern("trigger_sample_extra_sleep")), /* => */ UINT2NUM(state->stats.trigger_sample_extra_sleep),
1052
1070
  ID2SYM(rb_intern("trigger_simulated_signal_delivery_attempts")), /* => */ UINT2NUM(state->stats.trigger_simulated_signal_delivery_attempts),
1053
1071
  ID2SYM(rb_intern("simulated_signal_delivery")), /* => */ UINT2NUM(state->stats.simulated_signal_delivery),
1054
1072
  ID2SYM(rb_intern("signal_handler_enqueued_sample")), /* => */ UINT2NUM(state->stats.signal_handler_enqueued_sample),
@@ -1312,13 +1330,21 @@ static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {
1312
1330
  // To control bias from sampling, we clamp the maximum weight attributed to a single allocation sample. This avoids
1313
1331
  // assigning a very large number to a sample, if for instance the dynamic sampling mechanism chose a really big interval.
1314
1332
  unsigned int weight = allocations_since_last_sample > MAX_ALLOC_WEIGHT ? MAX_ALLOC_WEIGHT : (unsigned int) allocations_since_last_sample;
1315
- thread_context_collector_sample_allocation(state->thread_context_collector_instance, weight, new_object);
1333
+ bool needs_after_allocation = thread_context_collector_sample_allocation(state->thread_context_collector_instance, weight, new_object);
1316
1334
  // ...but we still represent the skipped samples in the profile, thus the data will account for all allocations.
1317
1335
  if (weight < allocations_since_last_sample) {
1318
1336
  uint32_t skipped_samples = (uint32_t) uint64_min_of(allocations_since_last_sample - weight, UINT32_MAX);
1319
1337
  thread_context_collector_sample_skipped_allocation_samples(state->thread_context_collector_instance, skipped_samples);
1320
1338
  }
1321
1339
 
1340
+ if (needs_after_allocation) {
1341
+ #ifndef NO_POSTPONED_TRIGGER
1342
+ rb_postponed_job_trigger(after_allocation_from_postponed_job_handle);
1343
+ #else
1344
+ // Not needed on legacy rubies
1345
+ #endif
1346
+ }
1347
+
1322
1348
  // Return a dummy VALUE because we're called from rb_rescue2 which requires it
1323
1349
  return Qnil;
1324
1350
  }
@@ -1449,6 +1475,11 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
1449
1475
 
1450
1476
  return state->gvl_profiling_hook != NULL ? Qtrue : Qfalse;
1451
1477
  }
1478
+
1479
+ static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception) {
1480
+ stop(self_instance, exception, "rescued_after_gvl_running_from_postponed_job");
1481
+ return Qnil;
1482
+ }
1452
1483
  #else
1453
1484
  static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, DDTRACE_UNUSED VALUE instance) {
1454
1485
  return Qfalse;
@@ -1470,11 +1501,50 @@ static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instan
1470
1501
  return Qnil;
1471
1502
  }
1472
1503
 
1473
- static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception) {
1474
- stop(self_instance, exception, "rescued_after_gvl_running_from_postponed_job");
1504
+ static VALUE handle_sampling_failure_rescued_after_allocation(VALUE self_instance, VALUE exception) {
1505
+ stop(self_instance, exception, "rescued_after_allocation");
1506
+ return Qnil;
1507
+ }
1508
+
1509
+ static VALUE rescued_after_allocation(VALUE self_instance) {
1510
+ cpu_and_wall_time_worker_state *state;
1511
+ TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1512
+
1513
+ thread_context_collector_after_allocation(state->thread_context_collector_instance);
1514
+
1515
+ // Return a dummy VALUE because we're called from rb_rescue2 which requires it
1475
1516
  return Qnil;
1476
1517
  }
1477
1518
 
1519
+ // This postponed job callback is used to finalize heap allocation recordings on Ruby 4+.
1520
+ // During on_newobj_event, calling rb_obj_id() is unsafe because it mutates the object.
1521
+ // So we defer getting the object_id until after the event completes.
1522
+ #pragma GCC diagnostic push
1523
+ #pragma GCC diagnostic ignored "-Wunused-function" // This is only used for some Rubies, but we want to build on all to make it easier to dev
1524
+ static void after_allocation_from_postponed_job(DDTRACE_UNUSED void *_unused) {
1525
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state;
1526
+
1527
+ if (state == NULL || !ddtrace_rb_ractor_main_p()) return;
1528
+
1529
+ // Protect against nested operations
1530
+ if (state->during_sample) return;
1531
+
1532
+ during_sample_enter(state);
1533
+
1534
+ // NOTE: We're not updating the allocation_sampler here.
1535
+ // This means work done in this function isn't accounted for as profiler overhead.
1536
+ // This is acceptable as the amount of work done here is expected to be small.
1537
+ safely_call(
1538
+ rescued_after_allocation,
1539
+ state->self_instance,
1540
+ state->self_instance,
1541
+ handle_sampling_failure_rescued_after_allocation
1542
+ );
1543
+
1544
+ during_sample_exit(state);
1545
+ }
1546
+ #pragma GCC diagnostic pop
1547
+
1478
1548
  static inline void during_sample_enter(cpu_and_wall_time_worker_state* state) {
1479
1549
  // Tell the compiler it's not allowed to reorder the `during_sample` flag with anything that happens after.
1480
1550
  //
@@ -290,11 +290,13 @@ static bool handle_gvl_waiting(
290
290
  sampling_buffer* sampling_buffer,
291
291
  long current_cpu_time_ns
292
292
  );
293
- static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread);
294
- static VALUE _native_gvl_waiting_at_for(DDTRACE_UNUSED VALUE self, VALUE thread);
295
- static VALUE _native_on_gvl_running(DDTRACE_UNUSED VALUE self, VALUE thread);
296
- static VALUE _native_sample_after_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread, VALUE allow_exception);
297
- static VALUE _native_apply_delta_to_cpu_time_at_previous_sample_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread, VALUE delta_ns);
293
+ #ifndef NO_GVL_INSTRUMENTATION
294
+ static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread);
295
+ static VALUE _native_gvl_waiting_at_for(DDTRACE_UNUSED VALUE self, VALUE thread);
296
+ static VALUE _native_on_gvl_running(DDTRACE_UNUSED VALUE self, VALUE thread);
297
+ static VALUE _native_sample_after_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread, VALUE allow_exception);
298
+ static VALUE _native_apply_delta_to_cpu_time_at_previous_sample_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread, VALUE delta_ns);
299
+ #endif
298
300
  static void otel_without_ddtrace_trace_identifiers_for(
299
301
  thread_context_collector_state *state,
300
302
  VALUE thread,
@@ -1339,6 +1341,16 @@ VALUE enforce_thread_context_collector_instance(VALUE object) {
1339
1341
  return object;
1340
1342
  }
1341
1343
 
1344
+ // Finalize any pending heap allocation recordings.
1345
+ // On Ruby 4+, heap allocations are recorded in two phases: during on_newobj_event we capture
1346
+ // the object reference, then later we safely call rb_obj_id() to get the object ID.
1347
+ void thread_context_collector_after_allocation(VALUE self_instance) {
1348
+ thread_context_collector_state *state;
1349
+ TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
1350
+
1351
+ recorder_after_sample(state->recorder_instance);
1352
+ }
1353
+
1342
1354
  // This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
1343
1355
  // It SHOULD NOT be used for other purposes.
1344
1356
  static VALUE _native_stats(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
@@ -1483,7 +1495,12 @@ bool thread_context_collector_prepare_sample_inside_signal_handler(VALUE self_in
1483
1495
  return prepare_sample_thread(current_thread, &thread_context->sampling_buffer);
1484
1496
  }
1485
1497
 
1486
- void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object) {
1498
+ // This method gets called from inside the RUBY_INTERNAL_EVENT_NEWOBJ tracepoint so it should never allocate in the
1499
+ // Ruby heap.
1500
+ //
1501
+ // Returns true if the after_allocation needs to be called (to do work that can't be done from inside the
1502
+ // tracepoint, such as allocate new objects), and false if it doesn't
1503
+ bool thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object) {
1487
1504
  thread_context_collector_state *state;
1488
1505
  TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
1489
1506
 
@@ -1553,7 +1570,7 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
1553
1570
  class_name = ruby_vm_type; // For other weird internal things we just use the VM type
1554
1571
  }
1555
1572
 
1556
- track_object(state->recorder_instance, new_object, sample_weight, class_name);
1573
+ bool needs_after_allocation = track_object(state->recorder_instance, new_object, sample_weight, class_name);
1557
1574
 
1558
1575
  per_thread_context *thread_context = get_or_create_context_for(current_thread, state);
1559
1576
 
@@ -1570,6 +1587,8 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
1570
1587
  /* is_gvl_waiting_state: */ false,
1571
1588
  /* is_safe_to_allocate_objects: */ false // Not safe to allocate further inside the NEWOBJ tracepoint
1572
1589
  );
1590
+
1591
+ return needs_after_allocation;
1573
1592
  }
1574
1593
 
1575
1594
  // This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
@@ -1577,11 +1596,13 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
1577
1596
  static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object) {
1578
1597
  debug_enter_unsafe_context();
1579
1598
 
1580
- thread_context_collector_sample_allocation(collector_instance, NUM2UINT(sample_weight), new_object);
1599
+ bool needs_after_allocation = thread_context_collector_sample_allocation(collector_instance, NUM2UINT(sample_weight), new_object);
1581
1600
 
1582
1601
  debug_leave_unsafe_context();
1583
1602
 
1584
- return Qtrue;
1603
+ // We could instead choose to automatically trigger the after allocation here; yet, it seems kinda nice to keep it manual for
1604
+ // the tests so we can pull on each lever separately and observe "the sausage being made" in steps
1605
+ return needs_after_allocation ? Qtrue : Qfalse;
1585
1606
  }
1586
1607
 
1587
1608
  static VALUE new_empty_thread_inner(DDTRACE_UNUSED void *arg) { return Qnil; }
@@ -1799,7 +1820,7 @@ static void otel_without_ddtrace_trace_identifiers_for(
1799
1820
  VALUE otel_current_span_key = get_otel_current_span_key(state, is_safe_to_allocate_objects);
1800
1821
  if (otel_current_span_key == Qnil) return;
1801
1822
 
1802
- int active_context_index = RARRAY_LEN(context_storage) - 1;
1823
+ long active_context_index = RARRAY_LEN(context_storage) - 1;
1803
1824
  if (active_context_index < 0) return;
1804
1825
 
1805
1826
  otel_span active_span = otel_span_from(rb_ary_entry(context_storage, active_context_index), otel_current_span_key);
@@ -1808,7 +1829,7 @@ static void otel_without_ddtrace_trace_identifiers_for(
1808
1829
  otel_span local_root_span = active_span;
1809
1830
 
1810
1831
  // Now find the oldest span starting from the active span that still has the same trace id as the active span
1811
- for (int i = active_context_index - 1; i >= 0; i--) {
1832
+ for (long i = active_context_index - 1; i >= 0; i--) {
1812
1833
  otel_span checking_span = otel_span_from(rb_ary_entry(context_storage, i), otel_current_span_key);
1813
1834
  if (checking_span.span == Qnil) return;
1814
1835
 
@@ -11,13 +11,15 @@ void thread_context_collector_sample(
11
11
  VALUE profiler_overhead_stack_thread
12
12
  );
13
13
  __attribute__((warn_unused_result)) bool thread_context_collector_prepare_sample_inside_signal_handler(VALUE self_instance);
14
- void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object);
14
+ __attribute__((warn_unused_result)) bool thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object);
15
+ void thread_context_collector_after_allocation(VALUE self_instance);
15
16
  void thread_context_collector_sample_skipped_allocation_samples(VALUE self_instance, unsigned int skipped_samples);
16
17
  VALUE thread_context_collector_sample_after_gc(VALUE self_instance);
17
18
  void thread_context_collector_on_gc_start(VALUE self_instance);
18
19
  __attribute__((warn_unused_result)) bool thread_context_collector_on_gc_finish(VALUE self_instance);
19
20
  VALUE enforce_thread_context_collector_instance(VALUE object);
20
21
 
22
+
21
23
  #ifndef NO_GVL_INSTRUMENTATION
22
24
  typedef enum {
23
25
  ON_GVL_RUNNING_UNKNOWN, // Thread is not known, it may not even be from the current Ractor
@@ -82,7 +82,8 @@ append_cflags "-Werror" if ENV["DATADOG_GEM_CI"] == "true"
82
82
  # * by upstream Ruby -- search for gnu99 in the codebase
83
83
  # * by msgpack, another datadog gem dependency
84
84
  # (https://github.com/msgpack/msgpack-ruby/blob/18ce08f6d612fe973843c366ac9a0b74c4e50599/ext/msgpack/extconf.rb#L8)
85
- append_cflags "-std=gnu99"
85
+ # @ivoanjo: We could probably start using C11/gnu11 for non macOS-too but it's somewhat hard to validate so I chickened out for now
86
+ append_cflags RUBY_PLATFORM.include?('darwin') ? '-std=gnu11' : '-std=gnu99'
86
87
 
87
88
  # Gets really noisy when we include the MJIT header, let's omit it (TODO: Use #pragma GCC diagnostic instead?)
88
89
  append_cflags "-Wno-unused-function"
@@ -144,6 +145,11 @@ $defs << "-DNO_PRIMITIVE_MUTEX_AND_CONDITION_VARIABLE" if RUBY_VERSION < "4"
144
145
  # On Ruby 4, we can't ask the object_id from IMEMOs (https://github.com/ruby/ruby/pull/13347)
145
146
  $defs << "-DNO_IMEMO_OBJECT_ID" unless RUBY_VERSION < "4"
146
147
 
148
+ # On Ruby 4, we need to defer calling rb_obj_id during heap allocation recording
149
+ # because it's not safe to mutate objects during the newobj tracepoint
150
+ # (see https://bugs.ruby-lang.org/issues/21710)
151
+ $defs << "-DUSE_DEFERRED_HEAP_ALLOCATION_RECORDING" unless RUBY_VERSION < "4"
152
+
147
153
  # This symbol is exclusively visible on certain Ruby versions: 2.6 to 3.2, as well as 3.4 (but not 4.0+)
148
154
  # It's only used to get extra information about an object when a failure happens, so it's a "very nice to have" but not
149
155
  # actually required for correct behavior of the profiler.
@@ -205,37 +211,16 @@ $defs << "-DNO_GVL_OWNER" if RUBY_VERSION < "2.6"
205
211
  $defs << "-DNO_THREAD_INVOKE_ARG" if RUBY_VERSION < "2.6"
206
212
 
207
213
  # If we got here, libdatadog is available and loaded
208
- ENV["PKG_CONFIG_PATH"] = "#{ENV["PKG_CONFIG_PATH"]}:#{Libdatadog.pkgconfig_folder}"
209
- Logging.message("[datadog] PKG_CONFIG_PATH set to #{ENV["PKG_CONFIG_PATH"].inspect}\n")
210
214
  $stderr.puts("Using libdatadog #{Libdatadog::VERSION} from #{Libdatadog.pkgconfig_folder}")
211
215
 
212
- unless pkg_config("datadog_profiling_with_rpath")
213
- Logging.message("[datadog] Ruby detected the pkg-config command is #{$PKGCONFIG.inspect}\n")
214
-
215
- skip_building_extension!(
216
- if Datadog::LibdatadogExtconfHelpers.pkg_config_missing?
217
- Datadog::Profiling::NativeExtensionHelpers::Supported::PKG_CONFIG_IS_MISSING
218
- else
219
- # Less specific error message
220
- Datadog::Profiling::NativeExtensionHelpers::Supported::FAILED_TO_CONFIGURE_LIBDATADOG
221
- end
222
- )
216
+ unless Datadog::LibdatadogExtconfHelpers.configure_libdatadog(extconf_folder: __dir__)
217
+ skip_building_extension!(Datadog::Profiling::NativeExtensionHelpers::Supported::FAILED_TO_CONFIGURE_LIBDATADOG)
223
218
  end
224
219
 
225
220
  unless have_type("atomic_int", ["stdatomic.h"])
226
221
  skip_building_extension!(Datadog::Profiling::NativeExtensionHelpers::Supported::COMPILER_ATOMIC_MISSING)
227
222
  end
228
223
 
229
- # See comments on the helper methods being used for why we need to additionally set this.
230
- # The extremely excessive escaping around ORIGIN below seems to be correct and was determined after a lot of
231
- # experimentation. We need to get these special characters across a lot of tools untouched...
232
- extra_relative_rpaths = [
233
- Datadog::LibdatadogExtconfHelpers.libdatadog_folder_relative_to_native_lib_folder(current_folder: __dir__),
234
- *Datadog::LibdatadogExtconfHelpers.libdatadog_folder_relative_to_ruby_extensions_folders,
235
- ]
236
- extra_relative_rpaths.each { |folder| $LDFLAGS += " -Wl,-rpath,$$$\\\\{ORIGIN\\}/#{folder.to_str}" }
237
- Logging.message("[datadog] After pkg-config $LDFLAGS were set to: #{$LDFLAGS.inspect}\n")
238
-
239
224
  # Tag the native extension library with the Ruby version and Ruby platform.
240
225
  # This makes it easier for development (avoids "oops I forgot to rebuild when I switched my Ruby") and ensures that
241
226
  # the wrong library is never loaded.