datadog 2.23.0 → 2.28.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 (215) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +132 -2
  3. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +2 -1
  4. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +100 -29
  5. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +2 -2
  6. data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +3 -2
  7. data/ext/datadog_profiling_native_extension/collectors_stack.c +23 -10
  8. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +16 -12
  9. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
  10. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +48 -1
  11. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +41 -0
  12. data/ext/datadog_profiling_native_extension/encoded_profile.c +2 -1
  13. data/ext/datadog_profiling_native_extension/extconf.rb +4 -1
  14. data/ext/datadog_profiling_native_extension/heap_recorder.c +24 -24
  15. data/ext/datadog_profiling_native_extension/http_transport.c +10 -4
  16. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +3 -22
  17. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +0 -5
  18. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +21 -8
  19. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +4 -0
  20. data/ext/datadog_profiling_native_extension/profiling.c +22 -15
  21. data/ext/datadog_profiling_native_extension/ruby_helpers.c +55 -44
  22. data/ext/datadog_profiling_native_extension/ruby_helpers.h +17 -5
  23. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +8 -2
  24. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +3 -0
  25. data/ext/datadog_profiling_native_extension/stack_recorder.c +16 -16
  26. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +2 -1
  27. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +5 -2
  28. data/ext/libdatadog_api/crashtracker.c +5 -8
  29. data/ext/libdatadog_api/datadog_ruby_common.c +48 -1
  30. data/ext/libdatadog_api/datadog_ruby_common.h +41 -0
  31. data/ext/libdatadog_api/ddsketch.c +4 -8
  32. data/ext/libdatadog_api/feature_flags.c +5 -5
  33. data/ext/libdatadog_api/helpers.h +27 -0
  34. data/ext/libdatadog_api/init.c +4 -0
  35. data/ext/libdatadog_extconf_helpers.rb +1 -1
  36. data/lib/datadog/ai_guard/api_client.rb +82 -0
  37. data/lib/datadog/ai_guard/component.rb +42 -0
  38. data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
  39. data/lib/datadog/ai_guard/configuration/settings.rb +110 -0
  40. data/lib/datadog/ai_guard/configuration.rb +11 -0
  41. data/lib/datadog/ai_guard/contrib/integration.rb +37 -0
  42. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +42 -0
  43. data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +41 -0
  44. data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +30 -0
  45. data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
  46. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +34 -0
  47. data/lib/datadog/ai_guard/evaluation/request.rb +81 -0
  48. data/lib/datadog/ai_guard/evaluation/result.rb +43 -0
  49. data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
  50. data/lib/datadog/ai_guard/evaluation.rb +72 -0
  51. data/lib/datadog/ai_guard/ext.rb +16 -0
  52. data/lib/datadog/ai_guard.rb +155 -0
  53. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +8 -1
  54. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +9 -2
  55. data/lib/datadog/appsec/component.rb +1 -1
  56. data/lib/datadog/appsec/context.rb +5 -4
  57. data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
  58. data/lib/datadog/appsec/contrib/active_record/patcher.rb +1 -1
  59. data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
  60. data/lib/datadog/appsec/contrib/excon/patcher.rb +1 -1
  61. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +47 -12
  62. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +32 -15
  63. data/lib/datadog/appsec/contrib/rails/patcher.rb +10 -2
  64. data/lib/datadog/appsec/contrib/rest_client/integration.rb +1 -1
  65. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +1 -1
  66. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +50 -14
  67. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +5 -4
  68. data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
  69. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +4 -4
  70. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +1 -1
  71. data/lib/datadog/appsec/ext.rb +2 -0
  72. data/lib/datadog/appsec/metrics/collector.rb +8 -3
  73. data/lib/datadog/appsec/metrics/exporter.rb +7 -0
  74. data/lib/datadog/appsec/metrics/telemetry.rb +7 -2
  75. data/lib/datadog/appsec/metrics.rb +5 -5
  76. data/lib/datadog/appsec/remote.rb +7 -14
  77. data/lib/datadog/appsec/security_engine/engine.rb +3 -3
  78. data/lib/datadog/appsec/security_engine/result.rb +2 -1
  79. data/lib/datadog/appsec/security_engine/runner.rb +2 -2
  80. data/lib/datadog/appsec/utils/http/media_type.rb +37 -23
  81. data/lib/datadog/appsec.rb +7 -1
  82. data/lib/datadog/core/configuration/components.rb +7 -0
  83. data/lib/datadog/core/configuration/config_helper.rb +1 -1
  84. data/lib/datadog/core/configuration/deprecations.rb +2 -2
  85. data/lib/datadog/core/configuration/option_definition.rb +4 -2
  86. data/lib/datadog/core/configuration/options.rb +8 -5
  87. data/lib/datadog/core/configuration/settings.rb +31 -3
  88. data/lib/datadog/core/configuration/supported_configurations.rb +10 -1
  89. data/lib/datadog/core/crashtracking/tag_builder.rb +6 -0
  90. data/lib/datadog/core/environment/cgroup.rb +52 -25
  91. data/lib/datadog/core/environment/container.rb +140 -46
  92. data/lib/datadog/core/environment/ext.rb +1 -0
  93. data/lib/datadog/core/environment/process.rb +9 -1
  94. data/lib/datadog/core/error.rb +6 -6
  95. data/lib/datadog/core/knuth_sampler.rb +57 -0
  96. data/lib/datadog/core/pin.rb +4 -0
  97. data/lib/datadog/core/rate_limiter.rb +9 -1
  98. data/lib/datadog/core/remote/client.rb +14 -6
  99. data/lib/datadog/core/remote/component.rb +6 -4
  100. data/lib/datadog/core/remote/configuration/content.rb +15 -2
  101. data/lib/datadog/core/remote/configuration/digest.rb +14 -7
  102. data/lib/datadog/core/remote/configuration/repository.rb +1 -1
  103. data/lib/datadog/core/remote/configuration/target.rb +13 -6
  104. data/lib/datadog/core/remote/transport/config.rb +3 -16
  105. data/lib/datadog/core/remote/transport/http/config.rb +4 -44
  106. data/lib/datadog/core/remote/transport/http/negotiation.rb +0 -39
  107. data/lib/datadog/core/remote/transport/http.rb +13 -24
  108. data/lib/datadog/core/remote/transport/negotiation.rb +7 -16
  109. data/lib/datadog/core/runtime/metrics.rb +11 -1
  110. data/lib/datadog/core/semaphore.rb +1 -4
  111. data/lib/datadog/core/telemetry/component.rb +52 -13
  112. data/lib/datadog/core/telemetry/event/app_started.rb +36 -1
  113. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  114. data/lib/datadog/core/telemetry/logger.rb +2 -0
  115. data/lib/datadog/core/telemetry/logging.rb +20 -2
  116. data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
  117. data/lib/datadog/core/telemetry/request.rb +17 -3
  118. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +2 -32
  119. data/lib/datadog/core/telemetry/transport/http.rb +21 -16
  120. data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -10
  121. data/lib/datadog/core/telemetry/worker.rb +88 -32
  122. data/lib/datadog/core/transport/ext.rb +2 -0
  123. data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
  124. data/lib/datadog/core/transport/http/api/instance.rb +4 -21
  125. data/lib/datadog/core/transport/http/builder.rb +9 -5
  126. data/lib/datadog/core/transport/http/client.rb +19 -8
  127. data/lib/datadog/core/transport/http.rb +22 -19
  128. data/lib/datadog/core/transport/response.rb +12 -1
  129. data/lib/datadog/core/transport/transport.rb +90 -0
  130. data/lib/datadog/core/utils/only_once_successful.rb +2 -0
  131. data/lib/datadog/core/utils/safe_dup.rb +2 -2
  132. data/lib/datadog/core/utils/sequence.rb +2 -0
  133. data/lib/datadog/core/utils/time.rb +1 -1
  134. data/lib/datadog/core/workers/async.rb +10 -1
  135. data/lib/datadog/core/workers/interval_loop.rb +44 -3
  136. data/lib/datadog/core/workers/polling.rb +2 -0
  137. data/lib/datadog/core/workers/queue.rb +100 -1
  138. data/lib/datadog/data_streams/processor.rb +1 -1
  139. data/lib/datadog/data_streams/transport/http/stats.rb +1 -36
  140. data/lib/datadog/data_streams/transport/http.rb +5 -6
  141. data/lib/datadog/data_streams/transport/stats.rb +3 -17
  142. data/lib/datadog/di/boot.rb +4 -2
  143. data/lib/datadog/di/configuration/settings.rb +22 -0
  144. data/lib/datadog/di/contrib/active_record.rb +30 -5
  145. data/lib/datadog/di/el/compiler.rb +8 -4
  146. data/lib/datadog/di/error.rb +5 -0
  147. data/lib/datadog/di/instrumenter.rb +26 -7
  148. data/lib/datadog/di/logger.rb +2 -2
  149. data/lib/datadog/di/probe_builder.rb +2 -1
  150. data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
  151. data/lib/datadog/di/probe_manager.rb +37 -31
  152. data/lib/datadog/di/probe_notification_builder.rb +15 -2
  153. data/lib/datadog/di/probe_notifier_worker.rb +5 -5
  154. data/lib/datadog/di/redactor.rb +8 -1
  155. data/lib/datadog/di/remote.rb +89 -84
  156. data/lib/datadog/di/transport/diagnostics.rb +7 -35
  157. data/lib/datadog/di/transport/http/diagnostics.rb +1 -31
  158. data/lib/datadog/di/transport/http/input.rb +1 -31
  159. data/lib/datadog/di/transport/http.rb +28 -17
  160. data/lib/datadog/di/transport/input.rb +7 -34
  161. data/lib/datadog/di.rb +61 -5
  162. data/lib/datadog/error_tracking/filters.rb +2 -2
  163. data/lib/datadog/kit/appsec/events/v2.rb +2 -3
  164. data/lib/datadog/open_feature/evaluation_engine.rb +2 -1
  165. data/lib/datadog/open_feature/remote.rb +3 -10
  166. data/lib/datadog/open_feature/transport.rb +9 -11
  167. data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
  168. data/lib/datadog/opentelemetry/configuration/settings.rb +2 -2
  169. data/lib/datadog/opentelemetry/metrics.rb +21 -14
  170. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +5 -8
  171. data/lib/datadog/profiling/collectors/code_provenance.rb +27 -2
  172. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +3 -2
  173. data/lib/datadog/profiling/collectors/info.rb +5 -4
  174. data/lib/datadog/profiling/component.rb +25 -11
  175. data/lib/datadog/profiling/exporter.rb +4 -0
  176. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +18 -0
  177. data/lib/datadog/profiling/ext/exec_monkey_patch.rb +32 -0
  178. data/lib/datadog/profiling/flush.rb +3 -0
  179. data/lib/datadog/profiling/http_transport.rb +4 -1
  180. data/lib/datadog/profiling/profiler.rb +3 -5
  181. data/lib/datadog/profiling/scheduler.rb +8 -7
  182. data/lib/datadog/profiling/tag_builder.rb +1 -0
  183. data/lib/datadog/tracing/contrib/extensions.rb +10 -2
  184. data/lib/datadog/tracing/contrib/karafka/patcher.rb +31 -32
  185. data/lib/datadog/tracing/contrib/status_range_matcher.rb +2 -1
  186. data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +3 -1
  187. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +6 -3
  188. data/lib/datadog/tracing/contrib/waterdrop.rb +4 -0
  189. data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
  190. data/lib/datadog/tracing/distributed/baggage.rb +3 -2
  191. data/lib/datadog/tracing/remote.rb +1 -9
  192. data/lib/datadog/tracing/sampling/priority_sampler.rb +3 -1
  193. data/lib/datadog/tracing/sampling/rate_sampler.rb +8 -19
  194. data/lib/datadog/tracing/span.rb +1 -1
  195. data/lib/datadog/tracing/span_event.rb +2 -2
  196. data/lib/datadog/tracing/span_operation.rb +20 -9
  197. data/lib/datadog/tracing/trace_operation.rb +44 -6
  198. data/lib/datadog/tracing/tracer.rb +42 -16
  199. data/lib/datadog/tracing/transport/http/traces.rb +2 -50
  200. data/lib/datadog/tracing/transport/http.rb +15 -9
  201. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  202. data/lib/datadog/tracing/transport/traces.rb +6 -66
  203. data/lib/datadog/tracing/workers/trace_writer.rb +5 -0
  204. data/lib/datadog/tracing/writer.rb +1 -0
  205. data/lib/datadog/version.rb +2 -2
  206. data/lib/datadog.rb +1 -0
  207. metadata +33 -19
  208. data/lib/datadog/core/remote/transport/http/api.rb +0 -53
  209. data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
  210. data/lib/datadog/core/transport/http/api/spec.rb +0 -36
  211. data/lib/datadog/data_streams/transport/http/api.rb +0 -33
  212. data/lib/datadog/data_streams/transport/http/client.rb +0 -21
  213. data/lib/datadog/di/transport/http/api.rb +0 -42
  214. data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
  215. data/lib/datadog/tracing/transport/http/api.rb +0 -44
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a168735b19c403a74cd23f49a301a74006d827ff24f62cc93255a353d0415359
4
- data.tar.gz: 7f6df03ec7834bf5709b36d8fa9a8213ad5c5f9bf04e299acb7d3c1e781760e7
3
+ metadata.gz: 91ec2ddb3720f61843ee7526cd4e3493a646ab7afaf3d6e4cce71019d1dfd582
4
+ data.tar.gz: d0996f8b1eb568aea751a1e781fa4d9dcc7e1d436911fd579c7aa43947340f32
5
5
  SHA512:
6
- metadata.gz: a933fb99fd374b1da93fd0a70b7cb33b2396977e8388ebfd5e12d968da7158ef8fc23a73672c308f8a72e433525c99ac1182285adc2df1eb244082efeef374bb
7
- data.tar.gz: 224bbcc48cde29a3a85c1eea9d5e1653ed26ad2d195389e599f2e4614158d63284c92fbf155cbd7bf584930195d4b30b7bf5a8a1fd6951ac798c1d3a1b35969b
6
+ metadata.gz: fb8da926cd9a09a500c781f60ad5fb35c72c9b3162acc067229f88d01281c63c6753c4230a2cf56f803fb0174994707b159e31f5d584e6682313a13e2187b61e
7
+ data.tar.gz: 8c93304f737b5798af5d6ed520f7006d7aa2e7644243fbfcd38994d66dde0aefb53084bca96024e61a7cbebcb72e60f958715fe4864c1e7fe90733103f9cb527
data/CHANGELOG.md CHANGED
@@ -2,6 +2,100 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [2.28.0] - 2026-02-04
6
+
7
+ ### Added
8
+
9
+ * AI Guard: Add instrumentation for `ruby_llm` gem. ([#5273][])
10
+
11
+ ### Changed
12
+
13
+ * Core: Bump minimum version of datadog-ruby_core_source to 3.5.2 ([#5278][])
14
+
15
+ ### Fixed
16
+
17
+ * AppSec: Fix exception in Rails contrib `:after_routes_loaded` hook when routes are reloaded. ([#5283][])
18
+
19
+ ## [2.27.0] - 2026-01-21
20
+
21
+ ### Added
22
+
23
+ * AppSec: Add analysis of the downstream requests ([#5206][])
24
+ * Telemetry: Add static error reporting for native extensions ([#5076][])
25
+
26
+ ### Changed
27
+
28
+ * SSI: Update injector to v1.2.1 ([#5254][])
29
+ * SSI: Prepare for expanded platform support ([#5254][])
30
+ * SSI: Improve remote resolution with expanded platform support via fallback to local gems ([#5254][])
31
+ * SSI: Introduce experimental fully local resolution support ([#5254][])
32
+ * Profiling: Telemetry-safe error reporting for native extensions ([#5076][])
33
+
34
+
35
+ ### Fixed
36
+
37
+ * Profiler: Fix interrupting new processes with the message `Profiling timer expired` during `exec` ([#5246][])
38
+ * Profiler: Fix rare race in profiler causing flaky spec on Ruby 2.7 ([#5247][])
39
+ * Appsec: Fix reporting of multi-method routes for Endpoint Collection ([#5240][])
40
+ * AppSec: Fix reporting of Rails routes that accept multiple request methods. ([#5240][])
41
+
42
+ ## [2.26.0] - 2026-01-16
43
+
44
+ ### Added
45
+
46
+ * Core: Add process tags to runtime metrics when `DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED` is enabled. ([#5210][])
47
+ * SSI: Add experimental dependency injection validation.
48
+
49
+ ### Changed
50
+
51
+ * Profiling: Improve profiler error reporting. ([#5237][])
52
+ * SSI: Improve injection debug error reporting. ([#5238][])
53
+
54
+ ## [2.25.0] - 2026-01-13
55
+
56
+ ### Added
57
+
58
+ AI Guard: Add SDK for evaluating the safety of user messages and assistant commands for LLM session ([#5144][])
59
+
60
+ ### Changed
61
+
62
+ Core: Bump minimum version of `datadog-ruby_core_source` dependency ([#5215][])
63
+
64
+ ### Fixed
65
+
66
+ AppSec: Fix processing of numeric data for WAF and RASP checks ([#5222][])
67
+
68
+ ## [2.24.0] - 2026-01-08
69
+
70
+ ### Added
71
+
72
+ * Core: Add support for installing the gem on Ruby 4.0.x stable ([#5157][])
73
+ * Tracing: Add origin detection using extra headers and the `DD_EXTERNAL_ENV` variable. ([#5028][])
74
+ * Dynamic Instrumentation: Add one-click enablement support ([#5150][])
75
+ * SSI: Add support for Bundler deployment mode ([#5053][])
76
+ * SSI: Report UI-oriented injection results ([#5053][])
77
+ * SSI: Guard against Bundler global force_ruby_platform ([#5053][])
78
+ * SSI: Guard against Bundler 4.0 and Bundler 2.7 in 4.0 mode ([#5053][])
79
+ * SSI: Guard against Ruby 3.5+ ([#5053][])
80
+
81
+ ### Changed
82
+
83
+ * Profiling: Remove profiler warning related to the Ractor issue ([#5194][])
84
+ * Profiling: Disable heap profiling on Ruby 4 due to incompatibility ([#5148][])
85
+ * Dynamic Instrumentation: Stop using customer-provided time provider for method duration calculation ([#5153][])
86
+ * Live Debugger / Dynamic Instrumentation: Improve probe instrumentation ([#5165][])
87
+ * Live Debugger / Dynamic Instrumentation: Improve instrumentation reliability for probes ([#5169][])
88
+
89
+ ### Fixed
90
+
91
+ * Core: Improve reliability of worker shutdown ([#5176][])
92
+ * Core: Fix RDoc error when installing the `datadog` gem ([#5145][])
93
+ * Tracing: Ensure `Tracing.continue_from!` keeps the active trace for the full block duration. ([#4941][])
94
+ * Profiling: Fix and refine profiler thread state categorization for Ruby 4 ([#5197][])
95
+ * Profiling: Fix profiler error triggering `Bundler::PermissionError` ([#5146][])
96
+ * Live Debugger / Dynamic Instrumentation: Fix Live Debugger and Dynamic Instrumentation UI for forking web servers ([#5159][])
97
+ * Live Debugger / Dynamic Instrumentation: Fix method probe leak when a referenced class loads after the probe reaches the application ([#5168][])
98
+
5
99
  ## [2.23.0] - 2025-12-11
6
100
 
7
101
  ### Added
@@ -3392,7 +3486,12 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
3392
3486
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3393
3487
 
3394
3488
 
3395
- [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.23.0...master
3489
+ [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.28.0...master
3490
+ [2.28.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.27.0...v2.28.0
3491
+ [2.27.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.26.0...v2.27.0
3492
+ [2.26.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.25.0...v2.26.0
3493
+ [2.25.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.24.0...v2.25.0
3494
+ [2.24.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.23.0...v2.24.0
3396
3495
  [2.23.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.22.0...v2.23.0
3397
3496
  [2.22.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.21.0...v2.22.0
3398
3497
  [2.21.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.20.0...v2.21.0
@@ -5010,6 +5109,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
5010
5109
  [#4914]: https://github.com/DataDog/dd-trace-rb/issues/4914
5011
5110
  [#4918]: https://github.com/DataDog/dd-trace-rb/issues/4918
5012
5111
  [#4919]: https://github.com/DataDog/dd-trace-rb/issues/4919
5112
+ [#4941]: https://github.com/DataDog/dd-trace-rb/issues/4941
5013
5113
  [#4957]: https://github.com/DataDog/dd-trace-rb/issues/4957
5014
5114
  [#4965]: https://github.com/DataDog/dd-trace-rb/issues/4965
5015
5115
  [#4969]: https://github.com/DataDog/dd-trace-rb/issues/4969
@@ -5024,18 +5124,48 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
5024
5124
  [#5021]: https://github.com/DataDog/dd-trace-rb/issues/5021
5025
5125
  [#5024]: https://github.com/DataDog/dd-trace-rb/issues/5024
5026
5126
  [#5025]: https://github.com/DataDog/dd-trace-rb/issues/5025
5127
+ [#5028]: https://github.com/DataDog/dd-trace-rb/issues/5028
5027
5128
  [#5031]: https://github.com/DataDog/dd-trace-rb/issues/5031
5028
5129
  [#5033]: https://github.com/DataDog/dd-trace-rb/issues/5033
5029
5130
  [#5042]: https://github.com/DataDog/dd-trace-rb/issues/5042
5030
5131
  [#5044]: https://github.com/DataDog/dd-trace-rb/issues/5044
5031
5132
  [#5045]: https://github.com/DataDog/dd-trace-rb/issues/5045
5032
5133
  [#5049]: https://github.com/DataDog/dd-trace-rb/issues/5049
5134
+ [#5053]: https://github.com/DataDog/dd-trace-rb/issues/5053
5033
5135
  [#5054]: https://github.com/DataDog/dd-trace-rb/issues/5054
5034
5136
  [#5058]: https://github.com/DataDog/dd-trace-rb/issues/5058
5035
5137
  [#5073]: https://github.com/DataDog/dd-trace-rb/issues/5073
5138
+ [#5076]: https://github.com/DataDog/dd-trace-rb/issues/5076
5036
5139
  [#5086]: https://github.com/DataDog/dd-trace-rb/issues/5086
5037
5140
  [#5091]: https://github.com/DataDog/dd-trace-rb/issues/5091
5038
5141
  [#5122]: https://github.com/DataDog/dd-trace-rb/issues/5122
5142
+ [#5144]: https://github.com/DataDog/dd-trace-rb/issues/5144
5143
+ [#5145]: https://github.com/DataDog/dd-trace-rb/issues/5145
5144
+ [#5146]: https://github.com/DataDog/dd-trace-rb/issues/5146
5145
+ [#5148]: https://github.com/DataDog/dd-trace-rb/issues/5148
5146
+ [#5150]: https://github.com/DataDog/dd-trace-rb/issues/5150
5147
+ [#5153]: https://github.com/DataDog/dd-trace-rb/issues/5153
5148
+ [#5157]: https://github.com/DataDog/dd-trace-rb/issues/5157
5149
+ [#5159]: https://github.com/DataDog/dd-trace-rb/issues/5159
5150
+ [#5165]: https://github.com/DataDog/dd-trace-rb/issues/5165
5151
+ [#5168]: https://github.com/DataDog/dd-trace-rb/issues/5168
5152
+ [#5169]: https://github.com/DataDog/dd-trace-rb/issues/5169
5153
+ [#5176]: https://github.com/DataDog/dd-trace-rb/issues/5176
5154
+ [#5194]: https://github.com/DataDog/dd-trace-rb/issues/5194
5155
+ [#5197]: https://github.com/DataDog/dd-trace-rb/issues/5197
5156
+ [#5206]: https://github.com/DataDog/dd-trace-rb/issues/5206
5157
+ [#5210]: https://github.com/DataDog/dd-trace-rb/issues/5210
5158
+ [#5215]: https://github.com/DataDog/dd-trace-rb/issues/5215
5159
+ [#5222]: https://github.com/DataDog/dd-trace-rb/issues/5222
5160
+ [#5237]: https://github.com/DataDog/dd-trace-rb/issues/5237
5161
+ [#5238]: https://github.com/DataDog/dd-trace-rb/issues/5238
5162
+ [#5240]: https://github.com/DataDog/dd-trace-rb/issues/5240
5163
+ [#5246]: https://github.com/DataDog/dd-trace-rb/issues/5246
5164
+ [#5247]: https://github.com/DataDog/dd-trace-rb/issues/5247
5165
+ [#5254]: https://github.com/DataDog/dd-trace-rb/issues/5254
5166
+ [#5273]: https://github.com/DataDog/dd-trace-rb/issues/5273
5167
+ [#5278]: https://github.com/DataDog/dd-trace-rb/issues/5278
5168
+ [#5283]: https://github.com/DataDog/dd-trace-rb/issues/5283
5039
5169
  [@AdrianLC]: https://github.com/AdrianLC
5040
5170
  [@Azure7111]: https://github.com/Azure7111
5041
5171
  [@BabyGroot]: https://github.com/BabyGroot
@@ -5190,4 +5320,4 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
5190
5320
  [@y-yagi]: https://github.com/y-yagi
5191
5321
  [@yujideveloper]: https://github.com/yujideveloper
5192
5322
  [@yukimurasawa]: https://github.com/yukimurasawa
5193
- [@zachmccormick]: https://github.com/zachmccormick
5323
+ [@zachmccormick]: https://github.com/zachmccormick
@@ -11,6 +11,7 @@
11
11
  #include "clock_id.h"
12
12
  #include "helpers.h"
13
13
  #include "private_vm_api_access.h"
14
+ #include "ruby_helpers.h"
14
15
  #include "time_helpers.h"
15
16
 
16
17
  // Validate that our home-cooked pthread_id_for() matches pthread_self() for the current thread
@@ -18,7 +19,7 @@ void self_test_clock_id(void) {
18
19
  rb_nativethread_id_t expected_pthread_id = pthread_self();
19
20
  rb_nativethread_id_t actual_pthread_id = pthread_id_for(rb_thread_current());
20
21
 
21
- if (expected_pthread_id != actual_pthread_id) rb_raise(rb_eRuntimeError, "pthread_id_for() self-test failed");
22
+ if (expected_pthread_id != actual_pthread_id) raise_error(rb_eRuntimeError, "pthread_id_for() self-test failed");
22
23
  }
23
24
 
24
25
  // Safety: This function is assumed never to raise exceptions by callers
@@ -76,8 +76,6 @@
76
76
  //
77
77
  // ---
78
78
 
79
- #define ERR_CLOCK_FAIL "failed to get clock time"
80
-
81
79
  // Maximum allowed value for an allocation weight. Attempts to use higher values will result in clamping.
82
80
  // See https://docs.google.com/document/d/1lWLB714wlLBBq6T4xZyAc4a5wtWhSmr4-hgiPKeErlA/edit#heading=h.ugp0zxcj5iqh
83
81
  // (Datadog-only link) for research backing the choice of this value.
@@ -117,6 +115,7 @@ typedef struct {
117
115
  // When something goes wrong during sampling, we record the Ruby exception here, so that it can be "re-raised" on
118
116
  // the CpuAndWallTimeWorker thread
119
117
  VALUE failure_exception;
118
+ const char *failure_exception_during_operation;
120
119
  // Used by `_native_stop` to flag the worker thread to start (see comment on `_native_sampling_loop`)
121
120
  VALUE stop_thread;
122
121
 
@@ -191,17 +190,17 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
191
190
  static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr);
192
191
  static VALUE _native_sampling_loop(VALUE self, VALUE instance);
193
192
  static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread);
194
- static VALUE stop(VALUE self_instance, VALUE optional_exception);
195
- static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception);
193
+ static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *optional_exception_during_operation);
194
+ static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception, const char *optional_operation_name);
196
195
  static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
197
196
  static void *run_sampling_trigger_loop(void *state_ptr);
198
197
  static void interrupt_sampling_trigger_loop(void *state_ptr);
199
198
  static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused);
200
199
  static VALUE rescued_sample_from_postponed_job(VALUE self_instance);
201
- static VALUE handle_sampling_failure(VALUE self_instance, VALUE exception);
202
200
  static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self);
203
201
  static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance);
204
202
  static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance);
203
+ static VALUE _native_failure_exception_during_operation(DDTRACE_UNUSED VALUE self, VALUE instance);
205
204
  static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
206
205
  static VALUE _native_install_testing_signal_handler(DDTRACE_UNUSED VALUE self);
207
206
  static VALUE _native_remove_testing_signal_handler(DDTRACE_UNUSED VALUE self);
@@ -209,7 +208,12 @@ static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self);
209
208
  static VALUE _native_gc_tracepoint(DDTRACE_UNUSED VALUE self, VALUE instance);
210
209
  static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused);
211
210
  static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused);
212
- static VALUE safely_call(VALUE (*function_to_call_safely)(VALUE), VALUE function_to_call_safely_arg, VALUE instance);
211
+ static VALUE safely_call(
212
+ VALUE (*function_to_call_safely)(VALUE),
213
+ VALUE function_to_call_safely_arg,
214
+ VALUE instance,
215
+ VALUE (*handle_sampling_failure)(VALUE, VALUE)
216
+ );
213
217
  static VALUE _native_simulate_handle_sampling_signal(DDTRACE_UNUSED VALUE self);
214
218
  static VALUE _native_simulate_sample_from_postponed_job(DDTRACE_UNUSED VALUE self);
215
219
  static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance);
@@ -226,6 +230,7 @@ static void disable_tracepoints(cpu_and_wall_time_worker_state *state);
226
230
  static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self);
227
231
  static VALUE rescued_sample_allocation(VALUE tracepoint_data);
228
232
  static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error);
233
+ static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state);
229
234
  static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
230
235
  static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self);
231
236
  static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);
@@ -235,6 +240,10 @@ static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
235
240
  #endif
236
241
  static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance);
237
242
  static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance);
243
+ static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception);
244
+ static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception);
245
+ 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);
238
247
  static inline void during_sample_enter(cpu_and_wall_time_worker_state* state);
239
248
  static inline void during_sample_exit(cpu_and_wall_time_worker_state* state);
240
249
 
@@ -262,6 +271,7 @@ static inline void during_sample_exit(cpu_and_wall_time_worker_state* state);
262
271
  // (e.g. signal handler) where it's impossible or just awkward to pass it as an argument.
263
272
  static VALUE active_sampler_instance = Qnil;
264
273
  static cpu_and_wall_time_worker_state *active_sampler_instance_state = NULL;
274
+ static VALUE clock_failure_exception_class = Qnil;
265
275
 
266
276
  // See handle_sampling_signal for details on what this does
267
277
  #ifdef NO_POSTPONED_TRIGGER
@@ -289,7 +299,7 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
289
299
  after_gc_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
290
300
  after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
291
301
  ) {
292
- rb_raise(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
302
+ raise_error(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
293
303
  }
294
304
  #else
295
305
  gc_finalize_deferred_workaround = objspace_ptr_for_gc_finalize_deferred_workaround();
@@ -299,6 +309,8 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
299
309
  VALUE collectors_cpu_and_wall_time_worker_class = rb_define_class_under(collectors_module, "CpuAndWallTimeWorker", rb_cObject);
300
310
  // Hosts methods used for testing the native code using RSpec
301
311
  VALUE testing_module = rb_define_module_under(collectors_cpu_and_wall_time_worker_class, "Testing");
312
+ clock_failure_exception_class = rb_define_class_under(collectors_cpu_and_wall_time_worker_class, "ClockFailure", rb_eRuntimeError);
313
+ rb_gc_register_mark_object(clock_failure_exception_class);
302
314
 
303
315
  // Instances of the CpuAndWallTimeWorker class are "TypedData" objects.
304
316
  // "TypedData" objects are special objects in the Ruby VM that can wrap C structs.
@@ -318,6 +330,7 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
318
330
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stats_reset_not_thread_safe", _native_stats_reset_not_thread_safe, 1);
319
331
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_allocation_count", _native_allocation_count, 0);
320
332
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_is_running?", _native_is_running, 1);
333
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_failure_exception_during_operation", _native_failure_exception_during_operation, 1);
321
334
  rb_define_singleton_method(testing_module, "_native_current_sigprof_signal_handler", _native_current_sigprof_signal_handler, 0);
322
335
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_hold_signals", _native_hold_signals, 0);
323
336
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_resume_signals", _native_resume_signals, 0);
@@ -370,6 +383,7 @@ static VALUE _native_new(VALUE klass) {
370
383
 
371
384
  atomic_init(&state->should_run, false);
372
385
  state->failure_exception = Qnil;
386
+ state->failure_exception_during_operation = NULL;
373
387
  state->stop_thread = Qnil;
374
388
 
375
389
  during_sample_exit(state);
@@ -472,10 +486,7 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
472
486
  cpu_and_wall_time_worker_state *old_state = active_sampler_instance_state;
473
487
  if (old_state != NULL) {
474
488
  if (is_thread_alive(old_state->owner_thread)) {
475
- rb_raise(
476
- rb_eRuntimeError,
477
- "Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread"
478
- );
489
+ raise_error(rb_eRuntimeError, "Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread");
479
490
  } else {
480
491
  // The previously active thread seems to have died without cleaning up after itself.
481
492
  // In this case, we can still go ahead and start the profiler BUT we make sure to disable any existing tracepoint
@@ -554,22 +565,24 @@ static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE
554
565
 
555
566
  state->stop_thread = worker_thread;
556
567
 
557
- return stop(self_instance, /* optional_exception: */ Qnil);
568
+ return stop(self_instance, Qnil, NULL);
558
569
  }
559
570
 
560
- static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception) {
571
+ // When providing an `optional_exception`, `optional_exception_during_operation` should be provided as well
572
+ static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception, const char *optional_exception_during_operation) {
561
573
  atomic_store(&state->should_run, false);
562
574
  state->failure_exception = optional_exception;
575
+ state->failure_exception_during_operation = optional_exception_during_operation;
563
576
 
564
577
  // Disable the tracepoints as soon as possible, so the VM doesn't keep on calling them
565
578
  disable_tracepoints(state);
566
579
  }
567
580
 
568
- static VALUE stop(VALUE self_instance, VALUE optional_exception) {
581
+ static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *optional_exception_during_operation) {
569
582
  cpu_and_wall_time_worker_state *state;
570
583
  TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
571
584
 
572
- stop_state(state, optional_exception);
585
+ stop_state(state, optional_exception, optional_exception_during_operation);
573
586
 
574
587
  return Qtrue;
575
588
  }
@@ -669,6 +682,10 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
669
682
  // we're doing this, so we may still not signal the correct thread from time to time, but our signal handler
670
683
  // includes a check to see if it got called in the right thread
671
684
  state->stats.interrupt_thread_attempts++;
685
+
686
+ // Pick up any last-minute attempts to stop before we send the signal
687
+ if (!atomic_load(&state->should_run)) return NULL;
688
+
672
689
  pthread_kill(owner.owner, SIGPROF);
673
690
  } else {
674
691
  if (state->skip_idle_samples_for_testing) {
@@ -726,7 +743,12 @@ static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused) {
726
743
  during_sample_enter(state);
727
744
 
728
745
  // Rescue against any exceptions that happen during sampling
729
- safely_call(rescued_sample_from_postponed_job, state->self_instance, state->self_instance);
746
+ safely_call(
747
+ rescued_sample_from_postponed_job,
748
+ state->self_instance,
749
+ state->self_instance,
750
+ handle_sampling_failure_rescued_sample_from_postponed_job
751
+ );
730
752
 
731
753
  during_sample_exit(state);
732
754
  }
@@ -763,11 +785,6 @@ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
763
785
  return Qnil;
764
786
  }
765
787
 
766
- static VALUE handle_sampling_failure(VALUE self_instance, VALUE exception) {
767
- stop(self_instance, exception);
768
- return Qnil;
769
- }
770
-
771
788
  // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
772
789
  // It SHOULD NOT be used for other purposes.
773
790
  static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self) {
@@ -821,7 +838,7 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
821
838
  NULL
822
839
  );
823
840
  #else
824
- rb_raise(rb_eArgError, "GVL profiling is not supported in this Ruby version");
841
+ raise_error(rb_eArgError, "GVL profiling is not supported in this Ruby version");
825
842
  #endif
826
843
  }
827
844
 
@@ -844,6 +861,15 @@ static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance) {
844
861
  return (state != NULL && is_thread_alive(state->owner_thread) && state->self_instance == instance) ? Qtrue : Qfalse;
845
862
  }
846
863
 
864
+ static VALUE _native_failure_exception_during_operation(DDTRACE_UNUSED VALUE self, VALUE instance) {
865
+ cpu_and_wall_time_worker_state *state;
866
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
867
+
868
+ if (state->failure_exception_during_operation == NULL) return Qnil;
869
+
870
+ return rb_str_new_cstr(state->failure_exception_during_operation);
871
+ }
872
+
847
873
  static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext) {
848
874
  /* Does nothing on purpose */
849
875
  }
@@ -936,14 +962,24 @@ static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused) {
936
962
 
937
963
  during_sample_enter(state);
938
964
 
939
- safely_call(thread_context_collector_sample_after_gc, state->thread_context_collector_instance, state->self_instance);
965
+ safely_call(
966
+ thread_context_collector_sample_after_gc,
967
+ state->thread_context_collector_instance,
968
+ state->self_instance,
969
+ handle_sampling_failure_thread_context_collector_sample_after_gc
970
+ );
940
971
 
941
972
  during_sample_exit(state);
942
973
  }
943
974
 
944
975
  // Equivalent to Ruby begin/rescue call, where we call a C function and jump to the exception handler if an
945
976
  // exception gets raised within
946
- static VALUE safely_call(VALUE (*function_to_call_safely)(VALUE), VALUE function_to_call_safely_arg, VALUE instance) {
977
+ static VALUE safely_call(
978
+ VALUE (*function_to_call_safely)(VALUE),
979
+ VALUE function_to_call_safely_arg,
980
+ VALUE instance,
981
+ VALUE (*handle_sampling_failure)(VALUE, VALUE)
982
+ ) {
947
983
  VALUE exception_handler_function_arg = instance;
948
984
  return rb_rescue2(
949
985
  function_to_call_safely,
@@ -1119,7 +1155,7 @@ static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
1119
1155
  #define HANDLE_CLOCK_FAILURE(call) ({ \
1120
1156
  long _result = (call); \
1121
1157
  if (_result == 0) { \
1122
- delayed_error(state, ERR_CLOCK_FAIL); \
1158
+ delayed_error_clock_failure(state); \
1123
1159
  return; \
1124
1160
  } \
1125
1161
  _result; \
@@ -1203,12 +1239,17 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
1203
1239
  during_sample_enter(state);
1204
1240
 
1205
1241
  // Rescue against any exceptions that happen during sampling
1206
- safely_call(rescued_sample_allocation, Qnil, state->self_instance);
1242
+ safely_call(
1243
+ rescued_sample_allocation,
1244
+ Qnil,
1245
+ state->self_instance,
1246
+ handle_sampling_failure_rescued_sample_allocation
1247
+ );
1207
1248
 
1208
1249
  if (state->dynamic_sampling_rate_enabled) {
1209
1250
  long now = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
1210
1251
  if (now == 0) {
1211
- delayed_error(state, ERR_CLOCK_FAIL);
1252
+ delayed_error_clock_failure(state);
1212
1253
  // NOTE: Not short-circuiting here to make sure cleanup happens
1213
1254
  }
1214
1255
  uint64_t sampling_time_ns = discrete_dynamic_sampler_after_sample(&state->allocation_sampler, now);
@@ -1284,7 +1325,12 @@ static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {
1284
1325
 
1285
1326
  static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error) {
1286
1327
  // If we can't raise an immediate exception at the calling site, use the asynchronous flow through the main worker loop.
1287
- stop_state(state, rb_exc_new_cstr(rb_eRuntimeError, error));
1328
+ stop_state(state, rb_exc_new_cstr(rb_eRuntimeError, error), "delayed_error");
1329
+ }
1330
+
1331
+ static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state) {
1332
+ // If we can't raise an immediate exception at the calling site, use the asynchronous flow through the main worker loop.
1333
+ stop_state(state, rb_exc_new_cstr(clock_failure_exception_class, "failed to get clock time"), "delayed_error_clock_failure");
1288
1334
  }
1289
1335
 
1290
1336
  static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg) {
@@ -1365,7 +1411,12 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
1365
1411
  during_sample_enter(state);
1366
1412
 
1367
1413
  // Rescue against any exceptions that happen during sampling
1368
- safely_call(rescued_after_gvl_running_from_postponed_job, state->self_instance, state->self_instance);
1414
+ safely_call(
1415
+ rescued_after_gvl_running_from_postponed_job,
1416
+ state->self_instance,
1417
+ state->self_instance,
1418
+ handle_sampling_failure_rescued_after_gvl_running_from_postponed_job
1419
+ );
1369
1420
 
1370
1421
  during_sample_exit(state);
1371
1422
  }
@@ -1404,6 +1455,26 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
1404
1455
  }
1405
1456
  #endif
1406
1457
 
1458
+ static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception) {
1459
+ stop(self_instance, exception, "rescued_sample_from_postponed_job");
1460
+ return Qnil;
1461
+ }
1462
+
1463
+ static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception) {
1464
+ stop(self_instance, exception, "thread_context_collector_sample_after_gc");
1465
+ return Qnil;
1466
+ }
1467
+
1468
+ static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception) {
1469
+ stop(self_instance, exception, "rescued_sample_allocation");
1470
+ return Qnil;
1471
+ }
1472
+
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");
1475
+ return Qnil;
1476
+ }
1477
+
1407
1478
  static inline void during_sample_enter(cpu_and_wall_time_worker_state* state) {
1408
1479
  // Tell the compiler it's not allowed to reorder the `during_sample` flag with anything that happens after.
1409
1480
  //
@@ -51,7 +51,7 @@ void discrete_dynamic_sampler_reset(discrete_dynamic_sampler *sampler, long now_
51
51
 
52
52
  void discrete_dynamic_sampler_set_overhead_target_percentage(discrete_dynamic_sampler *sampler, double target_overhead, long now_ns) {
53
53
  if (target_overhead <= 0 || target_overhead > 100) {
54
- rb_raise(rb_eArgError, "Target overhead must be a double between ]0,100] was %f", target_overhead);
54
+ raise_error(rb_eArgError, "Target overhead must be a double between ]0,100] was %f", target_overhead);
55
55
  }
56
56
  sampler->target_overhead = target_overhead;
57
57
  return discrete_dynamic_sampler_reset(sampler, now_ns);
@@ -369,7 +369,7 @@ static VALUE _native_new(VALUE klass) {
369
369
 
370
370
  long now_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
371
371
  if (now_ns == 0) {
372
- rb_raise(rb_eRuntimeError, "failed to get clock time");
372
+ raise_error(rb_eRuntimeError, "failed to get clock time");
373
373
  }
374
374
  discrete_dynamic_sampler_init(&state->sampler, "test sampler", now_ns);
375
375
 
@@ -2,6 +2,7 @@
2
2
  #include <datadog/profiling.h>
3
3
 
4
4
  #include "collectors_gc_profiling_helper.h"
5
+ #include "ruby_helpers.h"
5
6
 
6
7
  // This helper is used by the Datadog::Profiling::Collectors::ThreadContext to profile garbage collection.
7
8
  // It's tested through that class' interfaces.
@@ -71,7 +72,7 @@ uint8_t gc_profiling_set_metadata(ddog_prof_Label *labels, int labels_length) {
71
72
  1; // gc type
72
73
 
73
74
  if (max_label_count > labels_length) {
74
- rb_raise(rb_eArgError, "BUG: gc_profiling_set_metadata invalid labels_length (%d) < max_label_count (%d)", labels_length, max_label_count);
75
+ raise_error(rb_eArgError, "BUG: gc_profiling_set_metadata invalid labels_length (%d) < max_label_count (%d)", labels_length, max_label_count);
75
76
  }
76
77
 
77
78
  uint8_t label_pos = 0;
@@ -119,7 +120,7 @@ uint8_t gc_profiling_set_metadata(ddog_prof_Label *labels, int labels_length) {
119
120
  };
120
121
 
121
122
  if (label_pos > max_label_count) {
122
- rb_raise(rb_eRuntimeError, "BUG: gc_profiling_set_metadata unexpected label_pos (%d) > max_label_count (%d)", label_pos, max_label_count);
123
+ raise_error(rb_eRuntimeError, "BUG: gc_profiling_set_metadata unexpected label_pos (%d) > max_label_count (%d)", label_pos, max_label_count);
123
124
  }
124
125
 
125
126
  return label_pos;
@@ -17,6 +17,7 @@
17
17
 
18
18
  #include "datadog_ruby_common.h"
19
19
  #include "private_vm_api_access.h"
20
+ #include "ruby_helpers.h"
20
21
  #include "stack_recorder.h"
21
22
  #include "collectors_stack.h"
22
23
 
@@ -284,11 +285,11 @@ void sample_thread(
284
285
  // here, but >= 0 makes this easier to understand/debug.
285
286
  bool only_wall_time = cpu_or_wall_sample && values.cpu_time_ns == 0 && values.wall_time_ns >= 0;
286
287
 
287
- if (cpu_or_wall_sample && state_label == NULL) rb_raise(rb_eRuntimeError, "BUG: Unexpected missing state_label");
288
+ if (cpu_or_wall_sample && state_label == NULL) raise_error(rb_eRuntimeError, "BUG: Unexpected missing state_label");
288
289
 
289
290
  if (has_cpu_time) {
290
291
  state_label->str = DDOG_CHARSLICE_C("had cpu");
291
- if (labels.is_gvl_waiting_state) rb_raise(rb_eRuntimeError, "BUG: Unexpected combination of cpu-time with is_gvl_waiting");
292
+ if (labels.is_gvl_waiting_state) raise_error(rb_eRuntimeError, "BUG: Unexpected combination of cpu-time with is_gvl_waiting");
292
293
  }
293
294
 
294
295
  int top_of_stack_position = captured_frames - 1;
@@ -347,8 +348,10 @@ void sample_thread(
347
348
  } else if (CHARSLICE_EQUALS("select", name_slice)) { // Expected to be Kernel.select
348
349
  state_label->str = DDOG_CHARSLICE_C("waiting");
349
350
  } else if (
350
- CHARSLICE_EQUALS("synchronize", name_slice) || // Expected to be Monitor/Mutex#synchronize
351
- CHARSLICE_EQUALS("lock", name_slice) || // Expected to be Mutex#lock
351
+ CHARSLICE_EQUALS("synchronize", name_slice) || // Expected to be Monitor/Mutex#synchronize on Ruby 2 & 3, and Monitor#synchronize on 4 (Mutex becomes <internal:thread_sync>)
352
+ #ifdef NO_PRIMITIVE_MUTEX_AND_CONDITION_VARIABLE // Ruby < 4
353
+ CHARSLICE_EQUALS("lock", name_slice) || // Expected to be Mutex#lock
354
+ #endif
352
355
  CHARSLICE_EQUALS("join", name_slice) // Expected to be Thread#join
353
356
  ) {
354
357
  state_label->str = DDOG_CHARSLICE_C("blocked");
@@ -366,9 +369,19 @@ void sample_thread(
366
369
  #endif
367
370
  } else {
368
371
  #ifndef NO_PRIMITIVE_POP // Ruby >= 3.2
369
- // Unlike the above, Ruby actually treats this one specially and gives it a nice file name we can match on!
370
- if (CHARSLICE_EQUALS("pop", name_slice) && CHARSLICE_EQUALS("<internal:thread_sync>", filename_slice)) { // Expected to be Queue/SizedQueue#pop
371
- state_label->str = DDOG_CHARSLICE_C("waiting");
372
+ if (CHARSLICE_EQUALS("<internal:thread_sync>", filename_slice)) {
373
+ if (CHARSLICE_EQUALS("pop", name_slice)) { // Expected to be Queue/SizedQueue#pop
374
+ state_label->str = DDOG_CHARSLICE_C("waiting");
375
+ }
376
+ #ifndef NO_PRIMITIVE_MUTEX_AND_CONDITION_VARIABLE // Ruby >= 4
377
+ else if (CHARSLICE_EQUALS("synchronize", name_slice) || CHARSLICE_EQUALS("lock", name_slice)) { // Expected to be Mutex#lock/synchronize
378
+ state_label->str = DDOG_CHARSLICE_C("blocked");
379
+ } else if (CHARSLICE_EQUALS("sleep", name_slice)) { // Expected to be Mutex#sleep
380
+ state_label->str = DDOG_CHARSLICE_C("sleeping");
381
+ } else if (CHARSLICE_EQUALS("wait", name_slice)) { // Expected to be ConditionVariable#wait
382
+ state_label->str = DDOG_CHARSLICE_C("waiting");
383
+ }
384
+ #endif
372
385
  }
373
386
  #endif
374
387
  }
@@ -600,8 +613,8 @@ bool prepare_sample_thread(VALUE thread, sampling_buffer *buffer) {
600
613
  }
601
614
 
602
615
  uint16_t sampling_buffer_check_max_frames(int max_frames) {
603
- if (max_frames < 5) rb_raise(rb_eArgError, "Invalid max_frames: value must be >= 5");
604
- if (max_frames > MAX_FRAMES_LIMIT) rb_raise(rb_eArgError, "Invalid max_frames: value must be <= " MAX_FRAMES_LIMIT_AS_STRING);
616
+ if (max_frames < 5) raise_error(rb_eArgError, "Invalid max_frames: value must be >= 5");
617
+ if (max_frames > MAX_FRAMES_LIMIT) raise_error(rb_eArgError, "Invalid max_frames: value must be <= " MAX_FRAMES_LIMIT_AS_STRING);
605
618
  return max_frames;
606
619
  }
607
620
 
@@ -618,7 +631,7 @@ void sampling_buffer_initialize(sampling_buffer *buffer, uint16_t max_frames, dd
618
631
 
619
632
  void sampling_buffer_free(sampling_buffer *buffer) {
620
633
  if (buffer->max_frames == 0 || buffer->locations == NULL || buffer->stack_buffer == NULL) {
621
- rb_raise(rb_eArgError, "sampling_buffer_free called with invalid buffer");
634
+ raise_error(rb_eArgError, "sampling_buffer_free called with invalid buffer");
622
635
  }
623
636
 
624
637
  ruby_xfree(buffer->stack_buffer);