datadog 2.8.0 → 2.39.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 (1035) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1009 -1
  3. data/README.md +0 -1
  4. data/bin/ddprofrb +4 -4
  5. data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
  6. data/ext/datadog_profiling_native_extension/clock_id.h +11 -3
  7. data/ext/datadog_profiling_native_extension/clock_id_from_mach.c +66 -0
  8. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +3 -2
  9. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +500 -188
  10. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +5 -5
  11. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +1 -1
  12. data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +3 -2
  13. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +34 -20
  14. data/ext/datadog_profiling_native_extension/collectors_stack.c +319 -101
  15. data/ext/datadog_profiling_native_extension/collectors_stack.h +27 -5
  16. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +985 -504
  17. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +19 -5
  18. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
  19. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +66 -5
  20. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +50 -1
  21. data/ext/datadog_profiling_native_extension/encoded_profile.c +80 -0
  22. data/ext/datadog_profiling_native_extension/encoded_profile.h +8 -0
  23. data/ext/datadog_profiling_native_extension/extconf.rb +53 -35
  24. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +6 -43
  25. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +15 -55
  26. data/ext/datadog_profiling_native_extension/heap_recorder.c +487 -497
  27. data/ext/datadog_profiling_native_extension/heap_recorder.h +17 -8
  28. data/ext/datadog_profiling_native_extension/http_transport.c +108 -144
  29. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +18 -15
  30. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +8 -10
  31. data/ext/datadog_profiling_native_extension/macos_sampler_thread.h +55 -0
  32. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +0 -13
  33. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +136 -60
  34. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +22 -5
  35. data/ext/datadog_profiling_native_extension/profiling.c +84 -27
  36. data/ext/datadog_profiling_native_extension/ruby_helpers.c +67 -62
  37. data/ext/datadog_profiling_native_extension/ruby_helpers.h +20 -16
  38. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +34 -10
  39. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +2 -1
  40. data/ext/datadog_profiling_native_extension/stack_recorder.c +326 -217
  41. data/ext/datadog_profiling_native_extension/stack_recorder.h +4 -2
  42. data/ext/datadog_profiling_native_extension/time_helpers.h +14 -8
  43. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +48 -0
  44. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +36 -0
  45. data/ext/libdatadog_api/crashtracker.c +27 -24
  46. data/ext/libdatadog_api/crashtracker.h +5 -0
  47. data/ext/libdatadog_api/crashtracker_report_exception.c +126 -0
  48. data/ext/libdatadog_api/datadog_ruby_common.c +66 -5
  49. data/ext/libdatadog_api/datadog_ruby_common.h +50 -1
  50. data/ext/libdatadog_api/ddsketch.c +102 -0
  51. data/ext/libdatadog_api/di.c +203 -0
  52. data/ext/libdatadog_api/extconf.rb +41 -45
  53. data/ext/libdatadog_api/feature_flags.c +580 -0
  54. data/ext/libdatadog_api/feature_flags.h +5 -0
  55. data/ext/libdatadog_api/helpers.h +27 -0
  56. data/ext/libdatadog_api/init.c +27 -0
  57. data/ext/libdatadog_api/library_config.c +173 -0
  58. data/ext/libdatadog_api/library_config.h +25 -0
  59. data/ext/libdatadog_api/process_discovery.c +118 -0
  60. data/ext/libdatadog_api/process_discovery.h +5 -0
  61. data/ext/libdatadog_extconf_helpers.rb +111 -18
  62. data/lib/datadog/ai_guard/api_client.rb +84 -0
  63. data/lib/datadog/ai_guard/autoload.rb +10 -0
  64. data/lib/datadog/ai_guard/component.rb +44 -0
  65. data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
  66. data/lib/datadog/ai_guard/configuration.rb +115 -0
  67. data/lib/datadog/ai_guard/contrib/auto_instrument.rb +24 -0
  68. data/lib/datadog/ai_guard/contrib/integration.rb +37 -0
  69. data/lib/datadog/ai_guard/contrib/rack/integration.rb +42 -0
  70. data/lib/datadog/ai_guard/contrib/rack/patcher.rb +26 -0
  71. data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +97 -0
  72. data/lib/datadog/ai_guard/contrib/rails/integration.rb +41 -0
  73. data/lib/datadog/ai_guard/contrib/rails/patcher.rb +97 -0
  74. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +80 -0
  75. data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +41 -0
  76. data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +30 -0
  77. data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
  78. data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
  79. data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
  80. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +36 -0
  81. data/lib/datadog/ai_guard/evaluation/request.rb +86 -0
  82. data/lib/datadog/ai_guard/evaluation/result.rb +45 -0
  83. data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
  84. data/lib/datadog/ai_guard/evaluation.rb +108 -0
  85. data/lib/datadog/ai_guard/ext.rb +29 -0
  86. data/lib/datadog/ai_guard.rb +181 -0
  87. data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +89 -0
  88. data/lib/datadog/appsec/actions_handler.rb +49 -0
  89. data/lib/datadog/appsec/anonymizer.rb +16 -0
  90. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
  91. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +66 -0
  92. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +36 -0
  93. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
  94. data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
  95. data/lib/datadog/appsec/api_security/route_extractor.rb +104 -0
  96. data/lib/datadog/appsec/api_security/sampler.rb +62 -0
  97. data/lib/datadog/appsec/api_security.rb +23 -0
  98. data/lib/datadog/appsec/assets/blocked.html +10 -1
  99. data/lib/datadog/appsec/assets/blocked.json +1 -1
  100. data/lib/datadog/appsec/assets/blocked.text +3 -1
  101. data/lib/datadog/appsec/assets/waf_rules/README.md +44 -5
  102. data/lib/datadog/appsec/assets/waf_rules/recommended.json +954 -229
  103. data/lib/datadog/appsec/assets/waf_rules/strict.json +109 -106
  104. data/lib/datadog/appsec/assets.rb +8 -8
  105. data/lib/datadog/appsec/autoload.rb +5 -5
  106. data/lib/datadog/appsec/component.rb +56 -68
  107. data/lib/datadog/appsec/compressed_json.rb +40 -0
  108. data/lib/datadog/appsec/configuration.rb +427 -1
  109. data/lib/datadog/appsec/context.rb +135 -0
  110. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +45 -24
  111. data/lib/datadog/appsec/contrib/active_record/integration.rb +6 -6
  112. data/lib/datadog/appsec/contrib/active_record/patcher.rb +67 -16
  113. data/lib/datadog/appsec/contrib/auto_instrument.rb +1 -1
  114. data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +75 -0
  115. data/lib/datadog/appsec/contrib/aws_lambda/integration.rb +39 -0
  116. data/lib/datadog/appsec/contrib/aws_lambda/patcher.rb +30 -0
  117. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +146 -0
  118. data/lib/datadog/appsec/contrib/devise/configuration.rb +52 -0
  119. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +78 -0
  120. data/lib/datadog/appsec/contrib/devise/ext.rb +22 -0
  121. data/lib/datadog/appsec/contrib/devise/integration.rb +5 -6
  122. data/lib/datadog/appsec/contrib/devise/patcher.rb +33 -25
  123. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +104 -0
  124. data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +70 -0
  125. data/lib/datadog/appsec/contrib/devise/{patcher/rememberable_patch.rb → patches/skip_signin_tracking_patch.rb} +3 -3
  126. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +121 -0
  127. data/lib/datadog/appsec/contrib/excon/integration.rb +41 -0
  128. data/lib/datadog/appsec/contrib/excon/patcher.rb +30 -0
  129. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +181 -0
  130. data/lib/datadog/appsec/contrib/faraday/connection_patch.rb +22 -0
  131. data/lib/datadog/appsec/contrib/faraday/integration.rb +42 -0
  132. data/lib/datadog/appsec/contrib/faraday/patcher.rb +53 -0
  133. data/lib/datadog/appsec/contrib/faraday/rack_builder_patch.rb +22 -0
  134. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +166 -0
  135. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +4 -10
  136. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +76 -26
  137. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +22 -33
  138. data/lib/datadog/appsec/contrib/graphql/integration.rb +10 -9
  139. data/lib/datadog/appsec/contrib/graphql/patcher.rb +3 -6
  140. data/lib/datadog/appsec/contrib/rack/buffered_input.rb +83 -0
  141. data/lib/datadog/appsec/contrib/rack/ext.rb +34 -0
  142. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +56 -21
  143. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +4 -4
  144. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +101 -100
  145. data/lib/datadog/appsec/contrib/rack/input_peeker.rb +79 -0
  146. data/lib/datadog/appsec/contrib/rack/integration.rb +7 -7
  147. data/lib/datadog/appsec/contrib/rack/patcher.rb +2 -5
  148. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +13 -14
  149. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +117 -115
  150. data/lib/datadog/appsec/contrib/rack/response_body.rb +36 -0
  151. data/lib/datadog/appsec/contrib/rails/gateway/request.rb +43 -4
  152. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +58 -33
  153. data/lib/datadog/appsec/contrib/rails/integration.rb +6 -6
  154. data/lib/datadog/appsec/contrib/rails/patcher.rb +82 -69
  155. data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.rb +29 -0
  156. data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +33 -0
  157. data/lib/datadog/appsec/contrib/rails/request.rb +3 -3
  158. data/lib/datadog/appsec/contrib/rest_client/integration.rb +45 -0
  159. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +30 -0
  160. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +193 -0
  161. data/lib/datadog/appsec/contrib/sinatra/gateway/request.rb +1 -1
  162. data/lib/datadog/appsec/contrib/sinatra/gateway/route_params.rb +1 -1
  163. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +83 -67
  164. data/lib/datadog/appsec/contrib/sinatra/integration.rb +6 -6
  165. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +25 -61
  166. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +31 -0
  167. data/lib/datadog/appsec/counter_sampler.rb +25 -0
  168. data/lib/datadog/appsec/default_header_tags.rb +52 -0
  169. data/lib/datadog/appsec/event.rb +80 -152
  170. data/lib/datadog/appsec/ext.rb +16 -4
  171. data/lib/datadog/appsec/extensions.rb +2 -2
  172. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +23 -2
  173. data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +23 -0
  174. data/lib/datadog/appsec/instrumentation/gateway.rb +6 -24
  175. data/lib/datadog/appsec/metrics/collector.rb +80 -0
  176. data/lib/datadog/appsec/metrics/exporter.rb +48 -0
  177. data/lib/datadog/appsec/metrics/telemetry.rb +40 -0
  178. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +47 -0
  179. data/lib/datadog/appsec/metrics.rb +14 -0
  180. data/lib/datadog/appsec/monitor/gateway/watcher.rb +60 -34
  181. data/lib/datadog/appsec/monitor.rb +1 -1
  182. data/lib/datadog/appsec/processor/rule_loader.rb +48 -54
  183. data/lib/datadog/appsec/rate_limiter.rb +1 -1
  184. data/lib/datadog/appsec/remote.rb +59 -68
  185. data/lib/datadog/appsec/response.rb +42 -90
  186. data/lib/datadog/appsec/route_normalizer/rails_route_pattern.rb +176 -0
  187. data/lib/datadog/appsec/route_normalizer/route_pattern.rb +378 -0
  188. data/lib/datadog/appsec/route_normalizer/route_text.rb +57 -0
  189. data/lib/datadog/appsec/route_normalizer.rb +80 -0
  190. data/lib/datadog/appsec/security_engine/engine.rb +197 -0
  191. data/lib/datadog/appsec/security_engine/result.rb +103 -0
  192. data/lib/datadog/appsec/security_engine/runner.rb +111 -0
  193. data/lib/datadog/appsec/security_engine.rb +9 -0
  194. data/lib/datadog/appsec/security_event.rb +37 -0
  195. data/lib/datadog/appsec/thread_safe_ref.rb +61 -0
  196. data/lib/datadog/appsec/trace_keeper.rb +36 -0
  197. data/lib/datadog/appsec/utils/hash_coercion.rb +23 -0
  198. data/lib/datadog/appsec/utils/http/body.rb +41 -0
  199. data/lib/datadog/appsec/utils/http/body_reader.rb +61 -0
  200. data/lib/datadog/appsec/utils/http/media_range.rb +17 -16
  201. data/lib/datadog/appsec/utils/http/media_type.rb +33 -27
  202. data/lib/datadog/appsec/utils/http/url_encoded.rb +107 -0
  203. data/lib/datadog/appsec/utils.rb +0 -2
  204. data/lib/datadog/appsec.rb +35 -30
  205. data/lib/datadog/auto_instrument.rb +5 -2
  206. data/lib/datadog/auto_instrument_base.rb +2 -1
  207. data/lib/datadog/core/buffer/cruby.rb +1 -1
  208. data/lib/datadog/core/buffer/random.rb +18 -2
  209. data/lib/datadog/core/buffer/thread_safe.rb +1 -1
  210. data/lib/datadog/core/configuration/agent_settings.rb +52 -0
  211. data/lib/datadog/core/configuration/agent_settings_resolver.rb +22 -36
  212. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +176 -0
  213. data/lib/datadog/core/configuration/base.rb +19 -7
  214. data/lib/datadog/core/configuration/components.rb +261 -57
  215. data/lib/datadog/core/configuration/components_state.rb +28 -0
  216. data/lib/datadog/core/configuration/config_helper.rb +109 -0
  217. data/lib/datadog/core/configuration/deprecations.rb +36 -0
  218. data/lib/datadog/core/configuration/ext.rb +15 -12
  219. data/lib/datadog/core/configuration/option.rb +150 -84
  220. data/lib/datadog/core/configuration/option_definition.rb +50 -29
  221. data/lib/datadog/core/configuration/options.rb +58 -19
  222. data/lib/datadog/core/configuration/settings.rb +287 -103
  223. data/lib/datadog/core/configuration/stable_config.rb +32 -0
  224. data/lib/datadog/core/configuration/supported_configurations.rb +415 -0
  225. data/lib/datadog/core/configuration.rb +47 -23
  226. data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
  227. data/lib/datadog/core/contrib/rails/utils.rb +28 -0
  228. data/lib/datadog/core/crashtracking/component.rb +81 -38
  229. data/lib/datadog/core/crashtracking/tag_builder.rb +11 -23
  230. data/lib/datadog/core/ddsketch.rb +19 -0
  231. data/lib/datadog/core/deprecations.rb +3 -3
  232. data/lib/datadog/core/diagnostics/environment_logger.rb +10 -8
  233. data/lib/datadog/core/diagnostics/health.rb +2 -2
  234. data/lib/datadog/core/encoding.rb +21 -5
  235. data/lib/datadog/core/environment/agent_info.rb +142 -0
  236. data/lib/datadog/core/environment/cgroup.rb +53 -28
  237. data/lib/datadog/core/environment/container.rb +143 -51
  238. data/lib/datadog/core/environment/execution.rb +3 -3
  239. data/lib/datadog/core/environment/ext.rb +32 -21
  240. data/lib/datadog/core/environment/git.rb +5 -4
  241. data/lib/datadog/core/environment/identity.rb +50 -14
  242. data/lib/datadog/core/environment/platform.rb +8 -8
  243. data/lib/datadog/core/environment/process.rb +122 -0
  244. data/lib/datadog/core/environment/socket.rb +15 -2
  245. data/lib/datadog/core/environment/variable_helpers.rb +4 -4
  246. data/lib/datadog/core/environment/yjit.rb +2 -1
  247. data/lib/datadog/core/error.rb +15 -13
  248. data/lib/datadog/core/evp.rb +11 -0
  249. data/lib/datadog/core/extensions.rb +1 -1
  250. data/lib/datadog/core/feature_flags.rb +61 -0
  251. data/lib/datadog/core/git/ext.rb +4 -4
  252. data/lib/datadog/core/knuth_sampler.rb +59 -0
  253. data/lib/datadog/core/logger.rb +5 -5
  254. data/lib/datadog/core/logging/ext.rb +1 -1
  255. data/lib/datadog/core/metrics/client.rb +49 -49
  256. data/lib/datadog/core/metrics/ext.rb +5 -5
  257. data/lib/datadog/core/metrics/logging.rb +8 -8
  258. data/lib/datadog/core/metrics/options.rb +3 -3
  259. data/lib/datadog/core/pin.rb +9 -9
  260. data/lib/datadog/core/process_discovery/tracer_memfd.rb +13 -0
  261. data/lib/datadog/core/process_discovery.rb +62 -0
  262. data/lib/datadog/core/rate_limiter.rb +16 -4
  263. data/lib/datadog/core/remote/client/capabilities.rb +72 -13
  264. data/lib/datadog/core/remote/client.rb +68 -51
  265. data/lib/datadog/core/remote/component.rb +90 -55
  266. data/lib/datadog/core/remote/configuration/content.rb +17 -4
  267. data/lib/datadog/core/remote/configuration/digest.rb +22 -15
  268. data/lib/datadog/core/remote/configuration/path.rb +9 -9
  269. data/lib/datadog/core/remote/configuration/repository.rb +17 -4
  270. data/lib/datadog/core/remote/configuration/target.rb +15 -8
  271. data/lib/datadog/core/remote/configuration.rb +4 -4
  272. data/lib/datadog/core/remote/ext.rb +3 -3
  273. data/lib/datadog/core/remote/negotiation.rb +10 -10
  274. data/lib/datadog/core/remote/tie/tracing.rb +7 -7
  275. data/lib/datadog/core/remote/transport/config.rb +11 -37
  276. data/lib/datadog/core/remote/transport/http/config.rb +39 -107
  277. data/lib/datadog/core/remote/transport/http/negotiation.rb +19 -92
  278. data/lib/datadog/core/remote/transport/http.rb +33 -111
  279. data/lib/datadog/core/remote/transport/negotiation.rb +9 -21
  280. data/lib/datadog/core/remote/worker.rb +33 -42
  281. data/lib/datadog/core/runtime/ext.rb +22 -23
  282. data/lib/datadog/core/runtime/metrics.rb +30 -14
  283. data/lib/datadog/core/semaphore.rb +1 -4
  284. data/lib/datadog/core/tag_builder.rb +52 -0
  285. data/lib/datadog/core/tag_normalizer.rb +84 -0
  286. data/lib/datadog/core/telemetry/component.rb +142 -65
  287. data/lib/datadog/core/telemetry/emitter.rb +26 -14
  288. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +65 -0
  289. data/lib/datadog/core/telemetry/event/app_closing.rb +18 -0
  290. data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +33 -0
  291. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
  292. data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
  293. data/lib/datadog/core/telemetry/event/app_heartbeat.rb +18 -0
  294. data/lib/datadog/core/telemetry/event/app_integrations_change.rb +58 -0
  295. data/lib/datadog/core/telemetry/event/app_started.rb +293 -0
  296. data/lib/datadog/core/telemetry/event/base.rb +40 -0
  297. data/lib/datadog/core/telemetry/event/distributions.rb +18 -0
  298. data/lib/datadog/core/telemetry/event/generate_metrics.rb +43 -0
  299. data/lib/datadog/core/telemetry/event/log.rb +76 -0
  300. data/lib/datadog/core/telemetry/event/message_batch.rb +42 -0
  301. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +66 -0
  302. data/lib/datadog/core/telemetry/event.rb +21 -392
  303. data/lib/datadog/core/telemetry/ext.rb +11 -10
  304. data/lib/datadog/core/telemetry/http/adapters/net.rb +12 -97
  305. data/lib/datadog/core/telemetry/logger.rb +6 -3
  306. data/lib/datadog/core/telemetry/logging.rb +27 -9
  307. data/lib/datadog/core/telemetry/metric.rb +35 -13
  308. data/lib/datadog/core/telemetry/metrics_collection.rb +2 -2
  309. data/lib/datadog/core/telemetry/metrics_manager.rb +10 -1
  310. data/lib/datadog/core/telemetry/request.rb +21 -9
  311. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +66 -0
  312. data/lib/datadog/core/telemetry/transport/http.rb +68 -0
  313. data/lib/datadog/core/telemetry/transport/telemetry.rb +42 -0
  314. data/lib/datadog/core/telemetry/worker.rb +214 -35
  315. data/lib/datadog/core/transport/ext.rb +14 -11
  316. data/lib/datadog/core/transport/http/adapters/net.rb +19 -4
  317. data/lib/datadog/core/transport/http/adapters/test.rb +3 -2
  318. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +5 -5
  319. data/lib/datadog/core/transport/http/api/endpoint.rb +10 -5
  320. data/lib/datadog/{tracing → core}/transport/http/api/instance.rb +5 -5
  321. data/lib/datadog/core/transport/http/api/map.rb +1 -1
  322. data/lib/datadog/{tracing → core}/transport/http/builder.rb +62 -36
  323. data/lib/datadog/core/transport/http/client.rb +80 -0
  324. data/lib/datadog/core/transport/http/env.rb +8 -0
  325. data/lib/datadog/core/transport/http/response.rb +5 -1
  326. data/lib/datadog/core/transport/http.rb +80 -0
  327. data/lib/datadog/core/transport/parcel.rb +61 -9
  328. data/lib/datadog/core/transport/response.rb +19 -1
  329. data/lib/datadog/core/transport/transport.rb +90 -0
  330. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +9 -9
  331. data/lib/datadog/core/utils/base64_codec.rb +23 -0
  332. data/lib/datadog/core/utils/duration.rb +33 -33
  333. data/lib/datadog/core/utils/enumerable_compat.rb +29 -0
  334. data/lib/datadog/core/utils/fnv.rb +26 -0
  335. data/lib/datadog/core/utils/forking.rb +5 -3
  336. data/lib/datadog/core/utils/hash.rb +0 -23
  337. data/lib/datadog/core/utils/lru_cache.rb +45 -0
  338. data/lib/datadog/core/utils/network.rb +34 -13
  339. data/lib/datadog/core/utils/only_once.rb +1 -1
  340. data/lib/datadog/core/utils/only_once_successful.rb +25 -8
  341. data/lib/datadog/core/utils/safe_dup.rb +4 -3
  342. data/lib/datadog/core/utils/sequence.rb +2 -0
  343. data/lib/datadog/core/utils/spawn_monkey_patch.rb +68 -0
  344. data/lib/datadog/core/utils/time.rb +26 -2
  345. data/lib/datadog/core/utils/truncation.rb +21 -0
  346. data/lib/datadog/core/utils/url.rb +1 -1
  347. data/lib/datadog/core/utils.rb +14 -5
  348. data/lib/datadog/core/vendor/multipart-post/multipart/post/composite_read_io.rb +1 -1
  349. data/lib/datadog/core/vendor/multipart-post/multipart/post/multipartable.rb +8 -8
  350. data/lib/datadog/core/vendor/multipart-post/multipart/post/parts.rb +7 -7
  351. data/lib/datadog/core/worker.rb +1 -1
  352. data/lib/datadog/core/workers/async.rb +40 -14
  353. data/lib/datadog/core/workers/interval_loop.rb +63 -4
  354. data/lib/datadog/core/workers/polling.rb +4 -2
  355. data/lib/datadog/core/workers/queue.rb +96 -1
  356. data/lib/datadog/core/workers/runtime_metrics.rb +14 -6
  357. data/lib/datadog/core.rb +20 -3
  358. data/lib/datadog/data_streams/configuration.rb +50 -0
  359. data/lib/datadog/data_streams/ext.rb +11 -0
  360. data/lib/datadog/data_streams/extensions.rb +16 -0
  361. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  362. data/lib/datadog/data_streams/processor.rb +511 -0
  363. data/lib/datadog/data_streams/transport/http/stats.rb +58 -0
  364. data/lib/datadog/data_streams/transport/http.rb +36 -0
  365. data/lib/datadog/data_streams/transport/stats.rb +39 -0
  366. data/lib/datadog/data_streams.rb +100 -0
  367. data/lib/datadog/di/base.rb +123 -0
  368. data/lib/datadog/di/boot.rb +47 -0
  369. data/lib/datadog/di/capture_expression.rb +21 -0
  370. data/lib/datadog/di/capture_expression_evaluator.rb +71 -0
  371. data/lib/datadog/di/capture_limits.rb +41 -0
  372. data/lib/datadog/di/code_tracker.rb +224 -12
  373. data/lib/datadog/di/component.rb +157 -49
  374. data/lib/datadog/di/configuration.rb +241 -2
  375. data/lib/datadog/di/context.rb +82 -0
  376. data/lib/datadog/di/contrib/active_record.rb +34 -5
  377. data/lib/datadog/di/contrib/railtie.rb +15 -0
  378. data/lib/datadog/di/contrib.rb +28 -0
  379. data/lib/datadog/di/el/compiler.rb +168 -0
  380. data/lib/datadog/di/el/evaluator.rb +159 -0
  381. data/lib/datadog/di/el/expression.rb +42 -0
  382. data/lib/datadog/di/el.rb +5 -0
  383. data/lib/datadog/di/error.rb +49 -0
  384. data/lib/datadog/di/fatal_exceptions.rb +26 -0
  385. data/lib/datadog/di/instrumenter.rb +672 -122
  386. data/lib/datadog/di/logger.rb +30 -0
  387. data/lib/datadog/di/{init.rb → preload.rb} +2 -4
  388. data/lib/datadog/di/probe.rb +125 -20
  389. data/lib/datadog/di/probe_builder.rb +126 -5
  390. data/lib/datadog/di/probe_file_loader/railtie.rb +15 -0
  391. data/lib/datadog/di/probe_file_loader.rb +87 -0
  392. data/lib/datadog/di/probe_manager.rb +198 -100
  393. data/lib/datadog/di/probe_notification_builder.rb +349 -103
  394. data/lib/datadog/di/probe_notifier_worker.rb +161 -51
  395. data/lib/datadog/di/probe_repository.rb +198 -0
  396. data/lib/datadog/di/proc_responder.rb +36 -0
  397. data/lib/datadog/di/redactor.rb +24 -3
  398. data/lib/datadog/di/remote.rb +259 -73
  399. data/lib/datadog/di/serializer.rb +319 -30
  400. data/lib/datadog/di/transport/diagnostics.rb +32 -0
  401. data/lib/datadog/di/transport/http/diagnostics.rb +35 -0
  402. data/lib/datadog/di/transport/http/input.rb +45 -0
  403. data/lib/datadog/di/transport/http.rb +81 -0
  404. data/lib/datadog/di/transport/input.rb +144 -0
  405. data/lib/datadog/di/utils.rb +133 -9
  406. data/lib/datadog/di.rb +188 -121
  407. data/lib/datadog/error_tracking/collector.rb +88 -0
  408. data/lib/datadog/error_tracking/component.rb +167 -0
  409. data/lib/datadog/error_tracking/configuration.rb +64 -0
  410. data/lib/datadog/error_tracking/ext.rb +18 -0
  411. data/lib/datadog/error_tracking/extensions.rb +16 -0
  412. data/lib/datadog/error_tracking/filters.rb +77 -0
  413. data/lib/datadog/error_tracking.rb +18 -0
  414. data/lib/datadog/kit/appsec/events/v2.rb +253 -0
  415. data/lib/datadog/kit/appsec/events.rb +39 -26
  416. data/lib/datadog/kit/enable_core_dumps.rb +3 -3
  417. data/lib/datadog/kit/identity.rb +34 -24
  418. data/lib/datadog/kit/tracing/method_tracer.rb +135 -0
  419. data/lib/datadog/kit.rb +1 -1
  420. data/lib/datadog/open_feature/component.rb +109 -0
  421. data/lib/datadog/open_feature/configuration.rb +50 -0
  422. data/lib/datadog/open_feature/evaluation_engine.rb +70 -0
  423. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  424. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  425. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  426. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  427. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  428. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  429. data/lib/datadog/open_feature/ext.rb +21 -0
  430. data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +286 -0
  431. data/lib/datadog/open_feature/flag_evaluation/writer.rb +433 -0
  432. data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +102 -0
  433. data/lib/datadog/open_feature/hooks/flag_eval_metrics_hook.rb +51 -0
  434. data/lib/datadog/open_feature/hooks/span_enrichment_hook/codec.rb +58 -0
  435. data/lib/datadog/open_feature/hooks/span_enrichment_hook/span_enrichment_state.rb +96 -0
  436. data/lib/datadog/open_feature/hooks/span_enrichment_hook/span_enrichment_state_store.rb +49 -0
  437. data/lib/datadog/open_feature/hooks/span_enrichment_hook.rb +184 -0
  438. data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
  439. data/lib/datadog/open_feature/native_evaluator.rb +60 -0
  440. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  441. data/lib/datadog/open_feature/provider.rb +226 -0
  442. data/lib/datadog/open_feature/remote.rb +67 -0
  443. data/lib/datadog/open_feature/resolution_details.rb +37 -0
  444. data/lib/datadog/open_feature/transport.rb +121 -0
  445. data/lib/datadog/open_feature.rb +19 -0
  446. data/lib/datadog/opentelemetry/api/baggage.rb +90 -0
  447. data/lib/datadog/opentelemetry/api/context.rb +25 -10
  448. data/lib/datadog/opentelemetry/configuration/settings.rb +226 -0
  449. data/lib/datadog/opentelemetry/ext.rb +9 -0
  450. data/lib/datadog/opentelemetry/logs.rb +98 -0
  451. data/lib/datadog/opentelemetry/metrics.rb +90 -0
  452. data/lib/datadog/opentelemetry/sdk/configurator.rb +69 -5
  453. data/lib/datadog/opentelemetry/sdk/id_generator.rb +16 -10
  454. data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +30 -0
  455. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +28 -0
  456. data/lib/datadog/opentelemetry/sdk/propagator.rb +20 -14
  457. data/lib/datadog/opentelemetry/sdk/span_processor.rb +24 -21
  458. data/lib/datadog/opentelemetry/sdk/trace/span.rb +43 -39
  459. data/lib/datadog/opentelemetry/sdk.rb +19 -0
  460. data/lib/datadog/opentelemetry/signal_configuration.rb +53 -0
  461. data/lib/datadog/opentelemetry/trace.rb +5 -5
  462. data/lib/datadog/opentelemetry.rb +11 -6
  463. data/lib/datadog/profiling/collectors/code_provenance.rb +84 -16
  464. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +57 -3
  465. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +13 -4
  466. data/lib/datadog/profiling/collectors/info.rb +62 -4
  467. data/lib/datadog/profiling/collectors/thread_context.rb +27 -7
  468. data/lib/datadog/profiling/component.rb +125 -88
  469. data/lib/datadog/profiling/encoded_profile.rb +11 -0
  470. data/lib/datadog/profiling/exporter.rb +51 -17
  471. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +24 -5
  472. data/lib/datadog/profiling/ext/exec_monkey_patch.rb +32 -0
  473. data/lib/datadog/profiling/ext.rb +2 -17
  474. data/lib/datadog/profiling/flush.rb +26 -17
  475. data/lib/datadog/profiling/http_transport.rb +30 -96
  476. data/lib/datadog/profiling/load_native_extension.rb +3 -35
  477. data/lib/datadog/profiling/profiler.rb +31 -12
  478. data/lib/datadog/profiling/scheduler.rb +19 -10
  479. data/lib/datadog/profiling/sequence_tracker.rb +44 -0
  480. data/lib/datadog/profiling/stack_recorder.rb +9 -17
  481. data/lib/datadog/profiling/tag_builder.rb +41 -41
  482. data/lib/datadog/profiling/tasks/exec.rb +10 -5
  483. data/lib/datadog/profiling/tasks/help.rb +1 -0
  484. data/lib/datadog/profiling/tasks/setup.rb +4 -2
  485. data/lib/datadog/profiling.rb +38 -37
  486. data/lib/datadog/ruby_version.rb +25 -0
  487. data/lib/datadog/single_step_instrument.rb +11 -2
  488. data/lib/datadog/symbol_database/component.rb +734 -0
  489. data/lib/datadog/symbol_database/configuration.rb +73 -0
  490. data/lib/datadog/symbol_database/extensions.rb +19 -0
  491. data/lib/datadog/symbol_database/extractor.rb +1110 -0
  492. data/lib/datadog/symbol_database/file_hash.rb +48 -0
  493. data/lib/datadog/symbol_database/logger.rb +43 -0
  494. data/lib/datadog/symbol_database/remote.rb +195 -0
  495. data/lib/datadog/symbol_database/scope.rb +102 -0
  496. data/lib/datadog/symbol_database/scope_batcher.rb +292 -0
  497. data/lib/datadog/symbol_database/service_version.rb +66 -0
  498. data/lib/datadog/symbol_database/symbol.rb +69 -0
  499. data/lib/datadog/symbol_database/transport/http/endpoint.rb +28 -0
  500. data/lib/datadog/symbol_database/transport/http.rb +45 -0
  501. data/lib/datadog/symbol_database/transport.rb +54 -0
  502. data/lib/datadog/symbol_database/uploader.rb +230 -0
  503. data/lib/datadog/symbol_database.rb +71 -0
  504. data/lib/datadog/tracing/analytics.rb +2 -2
  505. data/lib/datadog/tracing/buffer.rb +15 -15
  506. data/lib/datadog/tracing/client_ip.rb +4 -4
  507. data/lib/datadog/tracing/component.rb +39 -35
  508. data/lib/datadog/tracing/configuration/dynamic.rb +14 -16
  509. data/lib/datadog/tracing/configuration/ext.rb +60 -33
  510. data/lib/datadog/tracing/configuration/http.rb +2 -2
  511. data/lib/datadog/tracing/configuration/settings.rb +156 -26
  512. data/lib/datadog/tracing/context.rb +3 -3
  513. data/lib/datadog/tracing/context_provider.rb +3 -3
  514. data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +2 -2
  515. data/lib/datadog/tracing/contrib/action_cable/event.rb +5 -5
  516. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +9 -6
  517. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +8 -5
  518. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +9 -6
  519. data/lib/datadog/tracing/contrib/action_cable/events.rb +4 -4
  520. data/lib/datadog/tracing/contrib/action_cable/ext.rb +18 -18
  521. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -5
  522. data/lib/datadog/tracing/contrib/action_cable/integration.rb +10 -7
  523. data/lib/datadog/tracing/contrib/action_cable/patcher.rb +4 -4
  524. data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +2 -2
  525. data/lib/datadog/tracing/contrib/action_mailer/event.rb +7 -4
  526. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +8 -8
  527. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +4 -4
  528. data/lib/datadog/tracing/contrib/action_mailer/events.rb +2 -2
  529. data/lib/datadog/tracing/contrib/action_mailer/ext.rb +18 -18
  530. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +11 -7
  531. data/lib/datadog/tracing/contrib/action_mailer/patcher.rb +3 -3
  532. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +37 -19
  533. data/lib/datadog/tracing/contrib/action_pack/action_controller/patcher.rb +2 -2
  534. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +48 -13
  535. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +5 -3
  536. data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +3 -3
  537. data/lib/datadog/tracing/contrib/action_pack/ext.rb +10 -8
  538. data/lib/datadog/tracing/contrib/action_pack/integration.rb +10 -7
  539. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +3 -3
  540. data/lib/datadog/tracing/contrib/action_pack/utils.rb +2 -3
  541. data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +3 -3
  542. data/lib/datadog/tracing/contrib/action_view/event.rb +1 -1
  543. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +10 -7
  544. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +12 -9
  545. data/lib/datadog/tracing/contrib/action_view/events.rb +2 -2
  546. data/lib/datadog/tracing/contrib/action_view/ext.rb +10 -10
  547. data/lib/datadog/tracing/contrib/action_view/integration.rb +14 -11
  548. data/lib/datadog/tracing/contrib/action_view/patcher.rb +5 -5
  549. data/lib/datadog/tracing/contrib/action_view/utils.rb +2 -2
  550. data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +2 -2
  551. data/lib/datadog/tracing/contrib/active_job/data_streams.rb +62 -0
  552. data/lib/datadog/tracing/contrib/active_job/event.rb +11 -11
  553. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +11 -8
  554. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +11 -8
  555. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +11 -8
  556. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +11 -8
  557. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +11 -8
  558. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +11 -8
  559. data/lib/datadog/tracing/contrib/active_job/events.rb +6 -6
  560. data/lib/datadog/tracing/contrib/active_job/ext.rb +23 -23
  561. data/lib/datadog/tracing/contrib/active_job/integration.rb +10 -7
  562. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  563. data/lib/datadog/tracing/contrib/active_job/patcher.rb +21 -5
  564. data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +2 -2
  565. data/lib/datadog/tracing/contrib/active_model_serializers/event.rb +4 -4
  566. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +8 -8
  567. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +9 -10
  568. data/lib/datadog/tracing/contrib/active_model_serializers/events.rb +2 -2
  569. data/lib/datadog/tracing/contrib/active_model_serializers/ext.rb +10 -10
  570. data/lib/datadog/tracing/contrib/active_model_serializers/integration.rb +5 -6
  571. data/lib/datadog/tracing/contrib/active_model_serializers/patcher.rb +3 -3
  572. data/lib/datadog/tracing/contrib/active_record/configuration/makara_resolver.rb +1 -1
  573. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +8 -8
  574. data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +4 -4
  575. data/lib/datadog/tracing/contrib/active_record/event.rb +1 -1
  576. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +10 -10
  577. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +16 -19
  578. data/lib/datadog/tracing/contrib/active_record/events.rb +2 -2
  579. data/lib/datadog/tracing/contrib/active_record/ext.rb +15 -15
  580. data/lib/datadog/tracing/contrib/active_record/integration.rb +16 -12
  581. data/lib/datadog/tracing/contrib/active_record/patcher.rb +2 -2
  582. data/lib/datadog/tracing/contrib/active_record/utils.rb +19 -19
  583. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +1 -1
  584. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +28 -20
  585. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +1 -1
  586. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +40 -8
  587. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +8 -4
  588. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +5 -7
  589. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +26 -3
  590. data/lib/datadog/tracing/contrib/active_support/ext.rb +16 -16
  591. data/lib/datadog/tracing/contrib/active_support/integration.rb +11 -8
  592. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +3 -2
  593. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +2 -2
  594. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +9 -11
  595. data/lib/datadog/tracing/contrib/active_support/patcher.rb +2 -2
  596. data/lib/datadog/tracing/contrib/analytics.rb +1 -1
  597. data/lib/datadog/tracing/contrib/auto_instrument.rb +5 -5
  598. data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +3 -3
  599. data/lib/datadog/tracing/contrib/aws/ext.rb +26 -26
  600. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +21 -13
  601. data/lib/datadog/tracing/contrib/aws/integration.rb +11 -8
  602. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +9 -3
  603. data/lib/datadog/tracing/contrib/aws/patcher.rb +10 -6
  604. data/lib/datadog/tracing/contrib/aws/service/base.rb +2 -1
  605. data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +2 -2
  606. data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +2 -2
  607. data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +4 -4
  608. data/lib/datadog/tracing/contrib/aws/service/s3.rb +2 -2
  609. data/lib/datadog/tracing/contrib/aws/service/sns.rb +3 -3
  610. data/lib/datadog/tracing/contrib/aws/service/sqs.rb +3 -3
  611. data/lib/datadog/tracing/contrib/aws/service/states.rb +5 -5
  612. data/lib/datadog/tracing/contrib/aws/services.rb +14 -14
  613. data/lib/datadog/tracing/contrib/component.rb +3 -3
  614. data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +2 -2
  615. data/lib/datadog/tracing/contrib/concurrent_ruby/configuration/settings.rb +2 -2
  616. data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +2 -2
  617. data/lib/datadog/tracing/contrib/concurrent_ruby/ext.rb +2 -2
  618. data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +2 -2
  619. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +8 -5
  620. data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +4 -4
  621. data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +2 -2
  622. data/lib/datadog/tracing/contrib/configurable.rb +24 -9
  623. data/lib/datadog/tracing/contrib/configuration/resolver.rb +14 -4
  624. data/lib/datadog/tracing/contrib/configuration/resolvers/pattern_resolver.rb +5 -5
  625. data/lib/datadog/tracing/contrib/configuration/settings.rb +3 -3
  626. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +2 -2
  627. data/lib/datadog/tracing/contrib/dalli/ext.rb +16 -15
  628. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +6 -10
  629. data/lib/datadog/tracing/contrib/dalli/integration.rb +10 -7
  630. data/lib/datadog/tracing/contrib/dalli/patcher.rb +3 -3
  631. data/lib/datadog/tracing/contrib/dalli/quantize.rb +3 -3
  632. data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +2 -2
  633. data/lib/datadog/tracing/contrib/delayed_job/ext.rb +14 -14
  634. data/lib/datadog/tracing/contrib/delayed_job/integration.rb +5 -5
  635. data/lib/datadog/tracing/contrib/delayed_job/patcher.rb +3 -3
  636. data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +7 -5
  637. data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +1 -0
  638. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +2 -2
  639. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +18 -17
  640. data/lib/datadog/tracing/contrib/elasticsearch/integration.rb +8 -8
  641. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +57 -63
  642. data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +8 -8
  643. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +7 -3
  644. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +19 -21
  645. data/lib/datadog/tracing/contrib/ethon/ext.rb +16 -14
  646. data/lib/datadog/tracing/contrib/ethon/integration.rb +6 -6
  647. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +4 -11
  648. data/lib/datadog/tracing/contrib/ethon/patcher.rb +3 -3
  649. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +21 -10
  650. data/lib/datadog/tracing/contrib/excon/ext.rb +14 -12
  651. data/lib/datadog/tracing/contrib/excon/integration.rb +6 -6
  652. data/lib/datadog/tracing/contrib/excon/middleware.rb +17 -19
  653. data/lib/datadog/tracing/contrib/excon/patcher.rb +2 -2
  654. data/lib/datadog/tracing/contrib/ext.rb +20 -17
  655. data/lib/datadog/tracing/contrib/extensions.rb +57 -14
  656. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +20 -13
  657. data/lib/datadog/tracing/contrib/faraday/ext.rb +14 -12
  658. data/lib/datadog/tracing/contrib/faraday/integration.rb +6 -6
  659. data/lib/datadog/tracing/contrib/faraday/middleware.rb +19 -19
  660. data/lib/datadog/tracing/contrib/faraday/patcher.rb +6 -6
  661. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +11 -7
  662. data/lib/datadog/tracing/contrib/grape/endpoint.rb +37 -34
  663. data/lib/datadog/tracing/contrib/grape/ext.rb +15 -15
  664. data/lib/datadog/tracing/contrib/grape/instrumentation.rb +15 -10
  665. data/lib/datadog/tracing/contrib/grape/integration.rb +5 -5
  666. data/lib/datadog/tracing/contrib/grape/patcher.rb +10 -5
  667. data/lib/datadog/tracing/contrib/graphql/configuration/error_extension_env_parser.rb +21 -0
  668. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +20 -2
  669. data/lib/datadog/tracing/contrib/graphql/ext.rb +11 -5
  670. data/lib/datadog/tracing/contrib/graphql/integration.rb +9 -9
  671. data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -8
  672. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +174 -41
  673. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +2 -2
  674. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +7 -4
  675. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +25 -23
  676. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +13 -17
  677. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +6 -6
  678. data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +2 -2
  679. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +12 -7
  680. data/lib/datadog/tracing/contrib/grpc/ext.rb +14 -13
  681. data/lib/datadog/tracing/contrib/grpc/formatting.rb +7 -7
  682. data/lib/datadog/tracing/contrib/grpc/integration.rb +5 -5
  683. data/lib/datadog/tracing/contrib/grpc/intercept_with_datadog.rb +1 -1
  684. data/lib/datadog/tracing/contrib/grpc/patcher.rb +3 -3
  685. data/lib/datadog/tracing/contrib/grpc.rb +7 -6
  686. data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +4 -3
  687. data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +2 -2
  688. data/lib/datadog/tracing/contrib/hanami/ext.rb +8 -8
  689. data/lib/datadog/tracing/contrib/hanami/integration.rb +6 -6
  690. data/lib/datadog/tracing/contrib/hanami/patcher.rb +5 -5
  691. data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +5 -4
  692. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +12 -13
  693. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +1 -16
  694. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +20 -9
  695. data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +6 -6
  696. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +13 -8
  697. data/lib/datadog/tracing/contrib/http/ext.rb +14 -12
  698. data/lib/datadog/tracing/contrib/http/instrumentation.rb +20 -28
  699. data/lib/datadog/tracing/contrib/http/integration.rb +9 -8
  700. data/lib/datadog/tracing/contrib/http/patcher.rb +3 -3
  701. data/lib/datadog/tracing/contrib/http.rb +7 -6
  702. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +20 -9
  703. data/lib/datadog/tracing/contrib/httpclient/ext.rb +14 -12
  704. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +21 -31
  705. data/lib/datadog/tracing/contrib/httpclient/integration.rb +6 -6
  706. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +3 -3
  707. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +20 -9
  708. data/lib/datadog/tracing/contrib/httprb/ext.rb +14 -12
  709. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +20 -32
  710. data/lib/datadog/tracing/contrib/httprb/integration.rb +9 -6
  711. data/lib/datadog/tracing/contrib/httprb/patcher.rb +3 -3
  712. data/lib/datadog/tracing/contrib/integration.rb +3 -3
  713. data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +2 -2
  714. data/lib/datadog/tracing/contrib/kafka/event.rb +5 -4
  715. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +4 -4
  716. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +5 -5
  717. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +5 -5
  718. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +6 -6
  719. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/join_group.rb +6 -6
  720. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/leave_group.rb +6 -6
  721. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/sync_group.rb +6 -6
  722. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +4 -4
  723. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +4 -4
  724. data/lib/datadog/tracing/contrib/kafka/events.rb +9 -9
  725. data/lib/datadog/tracing/contrib/kafka/ext.rb +40 -40
  726. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  727. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  728. data/lib/datadog/tracing/contrib/kafka/integration.rb +8 -5
  729. data/lib/datadog/tracing/contrib/kafka/patcher.rb +17 -3
  730. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +31 -0
  731. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +50 -0
  732. data/lib/datadog/tracing/contrib/karafka/ext.rb +28 -0
  733. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  734. data/lib/datadog/tracing/contrib/karafka/integration.rb +45 -0
  735. data/lib/datadog/tracing/contrib/karafka/monitor.rb +77 -0
  736. data/lib/datadog/tracing/contrib/karafka/patcher.rb +102 -0
  737. data/lib/datadog/tracing/contrib/karafka.rb +38 -0
  738. data/lib/datadog/tracing/contrib/lograge/configuration/settings.rb +2 -2
  739. data/lib/datadog/tracing/contrib/lograge/ext.rb +1 -1
  740. data/lib/datadog/tracing/contrib/lograge/instrumentation.rb +1 -1
  741. data/lib/datadog/tracing/contrib/lograge/integration.rb +5 -5
  742. data/lib/datadog/tracing/contrib/lograge/patcher.rb +13 -11
  743. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +11 -3
  744. data/lib/datadog/tracing/contrib/mongodb/ext.rb +19 -18
  745. data/lib/datadog/tracing/contrib/mongodb/instrumentation.rb +3 -3
  746. data/lib/datadog/tracing/contrib/mongodb/integration.rb +9 -6
  747. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +10 -10
  748. data/lib/datadog/tracing/contrib/mongodb/patcher.rb +3 -3
  749. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +38 -24
  750. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +9 -3
  751. data/lib/datadog/tracing/contrib/mysql2/ext.rb +12 -12
  752. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +25 -18
  753. data/lib/datadog/tracing/contrib/mysql2/integration.rb +5 -5
  754. data/lib/datadog/tracing/contrib/mysql2/patcher.rb +2 -2
  755. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +19 -2
  756. data/lib/datadog/tracing/contrib/opensearch/ext.rb +31 -21
  757. data/lib/datadog/tracing/contrib/opensearch/integration.rb +8 -6
  758. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +75 -77
  759. data/lib/datadog/tracing/contrib/opensearch/quantize.rb +9 -9
  760. data/lib/datadog/tracing/contrib/patcher.rb +14 -13
  761. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +9 -3
  762. data/lib/datadog/tracing/contrib/pg/ext.rb +19 -19
  763. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +21 -24
  764. data/lib/datadog/tracing/contrib/pg/integration.rb +5 -5
  765. data/lib/datadog/tracing/contrib/pg/patcher.rb +2 -2
  766. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +2 -2
  767. data/lib/datadog/tracing/contrib/presto/ext.rb +22 -22
  768. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +11 -13
  769. data/lib/datadog/tracing/contrib/presto/integration.rb +8 -5
  770. data/lib/datadog/tracing/contrib/presto/patcher.rb +4 -4
  771. data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +2 -2
  772. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +26 -13
  773. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +7 -3
  774. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +29 -5
  775. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +7 -4
  776. data/lib/datadog/tracing/contrib/que/ext.rb +19 -18
  777. data/lib/datadog/tracing/contrib/que/integration.rb +6 -6
  778. data/lib/datadog/tracing/contrib/que/patcher.rb +1 -1
  779. data/lib/datadog/tracing/contrib/que/tracer.rb +2 -1
  780. data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +3 -3
  781. data/lib/datadog/tracing/contrib/racecar/event.rb +6 -10
  782. data/lib/datadog/tracing/contrib/racecar/events/batch.rb +5 -5
  783. data/lib/datadog/tracing/contrib/racecar/events/consume.rb +4 -4
  784. data/lib/datadog/tracing/contrib/racecar/events/message.rb +5 -5
  785. data/lib/datadog/tracing/contrib/racecar/events.rb +3 -3
  786. data/lib/datadog/tracing/contrib/racecar/ext.rb +18 -18
  787. data/lib/datadog/tracing/contrib/racecar/integration.rb +5 -5
  788. data/lib/datadog/tracing/contrib/racecar/patcher.rb +3 -3
  789. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +13 -3
  790. data/lib/datadog/tracing/contrib/rack/ext.rb +43 -15
  791. data/lib/datadog/tracing/contrib/rack/header_collection.rb +13 -3
  792. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +55 -32
  793. data/lib/datadog/tracing/contrib/rack/integration.rb +7 -7
  794. data/lib/datadog/tracing/contrib/rack/middlewares.rb +102 -63
  795. data/lib/datadog/tracing/contrib/rack/patcher.rb +5 -5
  796. data/lib/datadog/tracing/contrib/rack/request_queue.rb +8 -7
  797. data/lib/datadog/tracing/contrib/rack/route_inference.rb +67 -0
  798. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +130 -6
  799. data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +1 -1
  800. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +9 -6
  801. data/lib/datadog/tracing/contrib/rails/ext.rb +16 -14
  802. data/lib/datadog/tracing/contrib/rails/framework.rb +12 -12
  803. data/lib/datadog/tracing/contrib/rails/integration.rb +6 -6
  804. data/lib/datadog/tracing/contrib/rails/log_injection.rb +2 -2
  805. data/lib/datadog/tracing/contrib/rails/middlewares.rb +4 -4
  806. data/lib/datadog/tracing/contrib/rails/patcher.rb +11 -9
  807. data/lib/datadog/tracing/contrib/rails/railtie.rb +4 -4
  808. data/lib/datadog/tracing/contrib/rails/runner.rb +64 -40
  809. data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +3 -3
  810. data/lib/datadog/tracing/contrib/rake/ext.rb +12 -12
  811. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +13 -11
  812. data/lib/datadog/tracing/contrib/rake/integration.rb +5 -5
  813. data/lib/datadog/tracing/contrib/rake/patcher.rb +3 -3
  814. data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +4 -4
  815. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +2 -2
  816. data/lib/datadog/tracing/contrib/redis/ext.rb +19 -18
  817. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +6 -6
  818. data/lib/datadog/tracing/contrib/redis/integration.rb +7 -7
  819. data/lib/datadog/tracing/contrib/redis/patcher.rb +12 -12
  820. data/lib/datadog/tracing/contrib/redis/quantize.rb +6 -6
  821. data/lib/datadog/tracing/contrib/redis/tags.rb +7 -12
  822. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +11 -9
  823. data/lib/datadog/tracing/contrib/registerable.rb +11 -0
  824. data/lib/datadog/tracing/contrib/registry.rb +1 -1
  825. data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +2 -2
  826. data/lib/datadog/tracing/contrib/resque/ext.rb +7 -7
  827. data/lib/datadog/tracing/contrib/resque/integration.rb +5 -5
  828. data/lib/datadog/tracing/contrib/resque/patcher.rb +3 -3
  829. data/lib/datadog/tracing/contrib/resque/resque_job.rb +7 -6
  830. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +7 -4
  831. data/lib/datadog/tracing/contrib/rest_client/ext.rb +13 -11
  832. data/lib/datadog/tracing/contrib/rest_client/integration.rb +8 -5
  833. data/lib/datadog/tracing/contrib/rest_client/patcher.rb +2 -2
  834. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +18 -17
  835. data/lib/datadog/tracing/contrib/roda/configuration/settings.rb +2 -2
  836. data/lib/datadog/tracing/contrib/roda/ext.rb +6 -5
  837. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +12 -7
  838. data/lib/datadog/tracing/contrib/roda/integration.rb +6 -6
  839. data/lib/datadog/tracing/contrib/roda/patcher.rb +3 -3
  840. data/lib/datadog/tracing/contrib/semantic_logger/configuration/settings.rb +2 -2
  841. data/lib/datadog/tracing/contrib/semantic_logger/ext.rb +1 -1
  842. data/lib/datadog/tracing/contrib/semantic_logger/instrumentation.rb +1 -1
  843. data/lib/datadog/tracing/contrib/semantic_logger/integration.rb +5 -5
  844. data/lib/datadog/tracing/contrib/semantic_logger/patcher.rb +2 -2
  845. data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +2 -2
  846. data/lib/datadog/tracing/contrib/sequel/database.rb +12 -11
  847. data/lib/datadog/tracing/contrib/sequel/dataset.rb +7 -6
  848. data/lib/datadog/tracing/contrib/sequel/ext.rb +12 -8
  849. data/lib/datadog/tracing/contrib/sequel/integration.rb +5 -5
  850. data/lib/datadog/tracing/contrib/sequel/patcher.rb +3 -3
  851. data/lib/datadog/tracing/contrib/sequel/utils.rb +115 -15
  852. data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +2 -2
  853. data/lib/datadog/tracing/contrib/shoryuken/ext.rb +12 -12
  854. data/lib/datadog/tracing/contrib/shoryuken/integration.rb +6 -6
  855. data/lib/datadog/tracing/contrib/shoryuken/patcher.rb +1 -1
  856. data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +3 -2
  857. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +15 -9
  858. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +7 -3
  859. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +13 -8
  860. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +31 -29
  861. data/lib/datadog/tracing/contrib/sidekiq/integration.rb +7 -7
  862. data/lib/datadog/tracing/contrib/sidekiq/patcher.rb +8 -8
  863. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/heartbeat.rb +2 -0
  864. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/job_fetch.rb +1 -0
  865. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/redis_info.rb +1 -0
  866. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/scheduled_poller.rb +2 -0
  867. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/stop.rb +1 -0
  868. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +19 -15
  869. data/lib/datadog/tracing/contrib/sidekiq/utils.rb +10 -10
  870. data/lib/datadog/tracing/contrib/sidekiq.rb +7 -6
  871. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +7 -3
  872. data/lib/datadog/tracing/contrib/sinatra/env.rb +6 -6
  873. data/lib/datadog/tracing/contrib/sinatra/ext.rb +17 -16
  874. data/lib/datadog/tracing/contrib/sinatra/framework.rb +2 -2
  875. data/lib/datadog/tracing/contrib/sinatra/integration.rb +5 -5
  876. data/lib/datadog/tracing/contrib/sinatra/patcher.rb +5 -5
  877. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +11 -10
  878. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +46 -45
  879. data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +1 -1
  880. data/lib/datadog/tracing/contrib/sneakers/ext.rb +12 -12
  881. data/lib/datadog/tracing/contrib/sneakers/integration.rb +19 -8
  882. data/lib/datadog/tracing/contrib/sneakers/patcher.rb +2 -2
  883. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +4 -3
  884. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +9 -4
  885. data/lib/datadog/tracing/contrib/status_range_env_parser.rb +2 -2
  886. data/lib/datadog/tracing/contrib/status_range_matcher.rb +13 -1
  887. data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +2 -2
  888. data/lib/datadog/tracing/contrib/stripe/ext.rb +12 -12
  889. data/lib/datadog/tracing/contrib/stripe/integration.rb +5 -5
  890. data/lib/datadog/tracing/contrib/stripe/patcher.rb +2 -2
  891. data/lib/datadog/tracing/contrib/stripe/request.rb +5 -5
  892. data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +2 -2
  893. data/lib/datadog/tracing/contrib/sucker_punch/exception_handler.rb +1 -1
  894. data/lib/datadog/tracing/contrib/sucker_punch/ext.rb +13 -13
  895. data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +5 -4
  896. data/lib/datadog/tracing/contrib/sucker_punch/integration.rb +5 -5
  897. data/lib/datadog/tracing/contrib/sucker_punch/patcher.rb +4 -4
  898. data/lib/datadog/tracing/contrib/support.rb +28 -0
  899. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +9 -3
  900. data/lib/datadog/tracing/contrib/trilogy/ext.rb +12 -12
  901. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +10 -12
  902. data/lib/datadog/tracing/contrib/trilogy/integration.rb +5 -5
  903. data/lib/datadog/tracing/contrib/trilogy/patcher.rb +2 -2
  904. data/lib/datadog/tracing/contrib/utils/database.rb +5 -5
  905. data/lib/datadog/tracing/contrib/utils/quantization/{hash.rb → hash_formatter.rb} +16 -14
  906. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +16 -16
  907. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +31 -0
  908. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +50 -0
  909. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +18 -0
  910. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  911. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  912. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +49 -0
  913. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  914. data/lib/datadog/tracing/contrib/waterdrop.rb +42 -0
  915. data/lib/datadog/tracing/contrib.rb +62 -52
  916. data/lib/datadog/tracing/correlation.rb +19 -12
  917. data/lib/datadog/tracing/diagnostics/environment_logger.rb +21 -13
  918. data/lib/datadog/tracing/diagnostics/ext.rb +19 -19
  919. data/lib/datadog/tracing/diagnostics/health.rb +1 -1
  920. data/lib/datadog/tracing/distributed/b3_multi.rb +9 -9
  921. data/lib/datadog/tracing/distributed/b3_single.rb +6 -6
  922. data/lib/datadog/tracing/distributed/baggage.rb +252 -0
  923. data/lib/datadog/tracing/distributed/datadog.rb +39 -36
  924. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +12 -27
  925. data/lib/datadog/tracing/distributed/fetcher.rb +1 -1
  926. data/lib/datadog/tracing/distributed/helpers.rb +4 -4
  927. data/lib/datadog/tracing/distributed/none.rb +4 -2
  928. data/lib/datadog/tracing/distributed/propagation.rb +70 -14
  929. data/lib/datadog/tracing/distributed/propagation_policy.rb +42 -0
  930. data/lib/datadog/tracing/distributed/trace_context.rb +126 -97
  931. data/lib/datadog/tracing/event.rb +6 -8
  932. data/lib/datadog/tracing/ext.rb +9 -0
  933. data/lib/datadog/tracing/flush.rb +1 -1
  934. data/lib/datadog/tracing/metadata/analytics.rb +3 -3
  935. data/lib/datadog/tracing/metadata/errors.rb +8 -8
  936. data/lib/datadog/tracing/metadata/ext.rb +86 -59
  937. data/lib/datadog/tracing/metadata/metastruct.rb +36 -0
  938. data/lib/datadog/tracing/metadata/metastruct_tagging.rb +42 -0
  939. data/lib/datadog/tracing/metadata/tagging.rb +8 -8
  940. data/lib/datadog/tracing/metadata.rb +5 -3
  941. data/lib/datadog/tracing/pipeline/span_filter.rb +5 -3
  942. data/lib/datadog/tracing/pipeline/span_processor.rb +3 -1
  943. data/lib/datadog/tracing/pipeline.rb +4 -4
  944. data/lib/datadog/tracing/remote.rb +47 -17
  945. data/lib/datadog/tracing/sampling/all_sampler.rb +1 -1
  946. data/lib/datadog/tracing/sampling/ext.rb +9 -9
  947. data/lib/datadog/tracing/sampling/matcher.rb +4 -4
  948. data/lib/datadog/tracing/sampling/priority_sampler.rb +20 -5
  949. data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +3 -3
  950. data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +2 -2
  951. data/lib/datadog/tracing/sampling/rate_sampler.rb +9 -19
  952. data/lib/datadog/tracing/sampling/rule.rb +5 -5
  953. data/lib/datadog/tracing/sampling/rule_sampler.rb +88 -59
  954. data/lib/datadog/tracing/sampling/sampler.rb +2 -2
  955. data/lib/datadog/tracing/sampling/span/ext.rb +3 -3
  956. data/lib/datadog/tracing/sampling/span/matcher.rb +2 -2
  957. data/lib/datadog/tracing/sampling/span/rule.rb +1 -2
  958. data/lib/datadog/tracing/sampling/span/rule_parser.rb +11 -11
  959. data/lib/datadog/tracing/sampling/span/sampler.rb +0 -7
  960. data/lib/datadog/tracing/span.rb +28 -11
  961. data/lib/datadog/tracing/span_event.rb +125 -5
  962. data/lib/datadog/tracing/span_link.rb +12 -12
  963. data/lib/datadog/tracing/span_operation.rb +119 -39
  964. data/lib/datadog/tracing/sync_writer.rb +14 -11
  965. data/lib/datadog/tracing/trace_digest.rb +36 -24
  966. data/lib/datadog/tracing/trace_operation.rb +243 -112
  967. data/lib/datadog/tracing/trace_segment.rb +18 -16
  968. data/lib/datadog/tracing/tracer.rb +181 -72
  969. data/lib/datadog/tracing/transport/http/client.rb +17 -30
  970. data/lib/datadog/tracing/transport/http/statistics.rb +2 -2
  971. data/lib/datadog/tracing/transport/http/traces.rb +11 -90
  972. data/lib/datadog/tracing/transport/http.rb +26 -77
  973. data/lib/datadog/tracing/transport/io/client.rb +13 -16
  974. data/lib/datadog/tracing/transport/io/response.rb +1 -1
  975. data/lib/datadog/tracing/transport/io/traces.rb +32 -38
  976. data/lib/datadog/tracing/transport/io.rb +3 -3
  977. data/lib/datadog/tracing/transport/serializable_trace.rb +47 -23
  978. data/lib/datadog/tracing/transport/statistics.rb +3 -3
  979. data/lib/datadog/tracing/transport/trace_formatter.rb +36 -7
  980. data/lib/datadog/tracing/transport/traces.rb +56 -90
  981. data/lib/datadog/tracing/utils.rb +3 -3
  982. data/lib/datadog/tracing/workers.rb +11 -9
  983. data/lib/datadog/tracing/writer.rb +15 -11
  984. data/lib/datadog/tracing.rb +19 -6
  985. data/lib/datadog/version.rb +4 -4
  986. data/lib/datadog.rb +20 -7
  987. metadata +327 -69
  988. data/ext/datadog_profiling_loader/datadog_profiling_loader.c +0 -142
  989. data/ext/datadog_profiling_loader/extconf.rb +0 -60
  990. data/ext/datadog_profiling_native_extension/clock_id_noop.c +0 -21
  991. data/ext/libdatadog_api/macos_development.md +0 -26
  992. data/lib/datadog/appsec/assets/waf_rules/processors.json +0 -92
  993. data/lib/datadog/appsec/assets/waf_rules/scanners.json +0 -114
  994. data/lib/datadog/appsec/configuration/settings.rb +0 -215
  995. data/lib/datadog/appsec/contrib/devise/event.rb +0 -57
  996. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +0 -77
  997. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +0 -54
  998. data/lib/datadog/appsec/contrib/devise/resource.rb +0 -35
  999. data/lib/datadog/appsec/contrib/devise/tracking.rb +0 -57
  1000. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +0 -46
  1001. data/lib/datadog/appsec/contrib/patcher.rb +0 -12
  1002. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +0 -69
  1003. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +0 -47
  1004. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +0 -53
  1005. data/lib/datadog/appsec/contrib/rails/ext.rb +0 -13
  1006. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +0 -53
  1007. data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -14
  1008. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +0 -48
  1009. data/lib/datadog/appsec/monitor/reactive/set_user.rb +0 -45
  1010. data/lib/datadog/appsec/processor/context.rb +0 -107
  1011. data/lib/datadog/appsec/processor/rule_merger.rb +0 -170
  1012. data/lib/datadog/appsec/processor.rb +0 -106
  1013. data/lib/datadog/appsec/reactive/address_hash.rb +0 -22
  1014. data/lib/datadog/appsec/reactive/engine.rb +0 -47
  1015. data/lib/datadog/appsec/reactive/operation.rb +0 -68
  1016. data/lib/datadog/appsec/reactive/subscriber.rb +0 -19
  1017. data/lib/datadog/appsec/scope.rb +0 -58
  1018. data/lib/datadog/appsec/utils/trace_operation.rb +0 -15
  1019. data/lib/datadog/core/crashtracking/agent_base_url.rb +0 -21
  1020. data/lib/datadog/core/remote/transport/http/api/instance.rb +0 -39
  1021. data/lib/datadog/core/remote/transport/http/api/spec.rb +0 -21
  1022. data/lib/datadog/core/remote/transport/http/api.rb +0 -58
  1023. data/lib/datadog/core/remote/transport/http/builder.rb +0 -219
  1024. data/lib/datadog/core/remote/transport/http/client.rb +0 -48
  1025. data/lib/datadog/core/telemetry/http/env.rb +0 -20
  1026. data/lib/datadog/core/telemetry/http/ext.rb +0 -28
  1027. data/lib/datadog/core/telemetry/http/response.rb +0 -70
  1028. data/lib/datadog/core/telemetry/http/transport.rb +0 -90
  1029. data/lib/datadog/core/utils/base64.rb +0 -22
  1030. data/lib/datadog/di/configuration/settings.rb +0 -188
  1031. data/lib/datadog/di/transport.rb +0 -81
  1032. data/lib/datadog/tracing/contrib/rails/utils.rb +0 -26
  1033. data/lib/datadog/tracing/transport/http/api/spec.rb +0 -19
  1034. data/lib/datadog/tracing/transport/http/api.rb +0 -43
  1035. data/lib/datadog/tracing/workers/trace_writer.rb +0 -196
@@ -17,7 +17,11 @@
17
17
  #include "setup_signal_handler.h"
18
18
  #include "time_helpers.h"
19
19
 
20
- // Used to trigger the execution of Collectors::ThreadState, which implements all of the sampling logic
20
+ #ifdef __APPLE__
21
+ #include "macos_sampler_thread.h"
22
+ #endif
23
+
24
+ // Used to trigger the execution of Collectors::ThreadContext, which implements all of the sampling logic
21
25
  // itself; this class only implements the "when to do it" part.
22
26
  //
23
27
  // This file implements the native bits of the Datadog::Profiling::Collectors::CpuAndWallTimeWorker class
@@ -33,7 +37,7 @@
33
37
  // Currently, sampling Ruby threads requires calling Ruby VM APIs that are only safe to call while holding on to the
34
38
  // global VM lock (and are not async-signal safe -- cannot be called from a signal handler).
35
39
  //
36
- // @ivoanjo: As a note, I don't think we should think of this constraint as set in stone. Since can reach into the Ruby
40
+ // @ivoanjo: As a note, I don't think we should think of this constraint as set in stone. Since we can reach inside the Ruby
37
41
  // internals, we may be able to figure out a way of overcoming it. But it's definitely going to be hard so for now
38
42
  // we're considering it as a given.
39
43
  //
@@ -76,8 +80,6 @@
76
80
  //
77
81
  // ---
78
82
 
79
- #define ERR_CLOCK_FAIL "failed to get clock time"
80
-
81
83
  // Maximum allowed value for an allocation weight. Attempts to use higher values will result in clamping.
82
84
  // See https://docs.google.com/document/d/1lWLB714wlLBBq6T4xZyAc4a5wtWhSmr4-hgiPKeErlA/edit#heading=h.ugp0zxcj5iqh
83
85
  // (Datadog-only link) for research backing the choice of this value.
@@ -89,10 +91,11 @@ unsigned int MAX_ALLOC_WEIGHT = 10000;
89
91
  static rb_postponed_job_handle_t sample_from_postponed_job_handle;
90
92
  static rb_postponed_job_handle_t after_gc_from_postponed_job_handle;
91
93
  static rb_postponed_job_handle_t after_gvl_running_from_postponed_job_handle;
94
+ static rb_postponed_job_handle_t after_allocation_from_postponed_job_handle;
92
95
  #endif
93
96
 
94
97
  // Contains state for a single CpuAndWallTimeWorker instance
95
- struct cpu_and_wall_time_worker_state {
98
+ typedef struct {
96
99
  // These are immutable after initialization
97
100
 
98
101
  bool gc_profiling_enabled;
@@ -102,6 +105,8 @@ struct cpu_and_wall_time_worker_state {
102
105
  bool allocation_counting_enabled;
103
106
  bool gvl_profiling_enabled;
104
107
  bool skip_idle_samples_for_testing;
108
+ bool sighandler_sampling_enabled;
109
+ uint32_t cpu_sampling_interval_ms;
105
110
  VALUE self_instance;
106
111
  VALUE thread_context_collector_instance;
107
112
  VALUE idle_sampling_helper_instance;
@@ -116,13 +121,18 @@ struct cpu_and_wall_time_worker_state {
116
121
  // When something goes wrong during sampling, we record the Ruby exception here, so that it can be "re-raised" on
117
122
  // the CpuAndWallTimeWorker thread
118
123
  VALUE failure_exception;
124
+ const char *failure_exception_during_operation;
119
125
  // Used by `_native_stop` to flag the worker thread to start (see comment on `_native_sampling_loop`)
120
126
  VALUE stop_thread;
121
127
 
122
128
  // Others
123
129
 
124
130
  // Used to detect/avoid nested sampling, e.g. when on_newobj_event gets triggered by a memory allocation
125
- // that happens during another sample.
131
+ // that happens during another sample, or when the signal handler gets triggered while we're already in the middle of
132
+ // sampling.
133
+ //
134
+ // @ivoanjo: Right now we always sample inside `safely_call`; if that ever changes, this flag may need to become
135
+ // volatile/atomic/have some barriers to ensure it's visible during e.g. signal handlers.
126
136
  bool during_sample;
127
137
 
128
138
  #ifndef NO_GVL_INSTRUMENTATION
@@ -134,27 +144,19 @@ struct cpu_and_wall_time_worker_state {
134
144
  // # Generic stats
135
145
  // How many times we tried to trigger a sample
136
146
  unsigned int trigger_sample_attempts;
147
+ // How many times extra sleep was triggered by dynamic sampling rate
148
+ unsigned int trigger_sample_extra_sleep;
137
149
  // How many times we tried to simulate signal delivery
138
150
  unsigned int trigger_simulated_signal_delivery_attempts;
139
151
  // How many times we actually simulated signal delivery
140
152
  unsigned int simulated_signal_delivery;
141
- // How many times we actually called rb_postponed_job_register_one from a signal handler
153
+ // How many times we actually called rb_postponed_job_register_one from the signal handler
142
154
  unsigned int signal_handler_enqueued_sample;
143
- // How many times the signal handler was called from the wrong thread
144
- unsigned int signal_handler_wrong_thread;
155
+ // How many times we prepared a sample (sampled directly) from the signal handler
156
+ unsigned int signal_handler_prepared_sample;
145
157
  // How many times we actually tried to interrupt a thread for sampling
146
158
  unsigned int interrupt_thread_attempts;
147
159
 
148
- // # Stats for the results of calling rb_postponed_job_register_one
149
- // The same function was already waiting to be executed
150
- unsigned int postponed_job_skipped_already_existed;
151
- // The function was added to the queue successfully
152
- unsigned int postponed_job_success;
153
- // The queue was full
154
- unsigned int postponed_job_full;
155
- // The function returned an unknown result code
156
- unsigned int postponed_job_unknown_result;
157
-
158
160
  // # CPU/Walltime sampling stats
159
161
  // How many times we actually CPU/wall sampled
160
162
  unsigned int cpu_sampled;
@@ -178,6 +180,11 @@ struct cpu_and_wall_time_worker_state {
178
180
  unsigned int allocations_during_sample;
179
181
 
180
182
  // # GVL profiling stats
183
+ // Note that this tracks two kind of samples from RESUMED:
184
+ // * samples when Waiting for GVL for more than the threshold
185
+ // * samples for the skip-samples-while-without-GVL optimization,
186
+ // which are needed to attribute the time without GVL to the correct Ruby stack
187
+
181
188
  // How many times we triggered the after_gvl_running sampling
182
189
  unsigned int after_gvl_running;
183
190
  // How many times we skipped the after_gvl_running sampling
@@ -186,33 +193,44 @@ struct cpu_and_wall_time_worker_state {
186
193
  uint64_t gvl_sampling_time_ns_min;
187
194
  uint64_t gvl_sampling_time_ns_max;
188
195
  uint64_t gvl_sampling_time_ns_total;
196
+
197
+ struct vm_metrics {
198
+ // Total time spent waiting for the GVL
199
+ uint64_t gvl_waiting_time_ns_total;
200
+ } vm_metrics;
189
201
  } stats;
190
- };
202
+ } cpu_and_wall_time_worker_state;
191
203
 
192
204
  static VALUE _native_new(VALUE klass);
193
205
  static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
194
206
  static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr);
195
207
  static VALUE _native_sampling_loop(VALUE self, VALUE instance);
196
208
  static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread);
197
- static VALUE stop(VALUE self_instance, VALUE optional_exception);
198
- static void stop_state(struct cpu_and_wall_time_worker_state *state, VALUE optional_exception);
209
+ static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *optional_exception_during_operation);
210
+ static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception, const char *optional_operation_name);
199
211
  static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
200
212
  static void *run_sampling_trigger_loop(void *state_ptr);
201
213
  static void interrupt_sampling_trigger_loop(void *state_ptr);
202
214
  static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused);
203
215
  static VALUE rescued_sample_from_postponed_job(VALUE self_instance);
204
- static VALUE handle_sampling_failure(VALUE self_instance, VALUE exception);
205
216
  static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self);
206
217
  static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance);
207
218
  static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance);
219
+ static VALUE _native_failure_exception_during_operation(DDTRACE_UNUSED VALUE self, VALUE instance);
208
220
  static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
209
221
  static VALUE _native_install_testing_signal_handler(DDTRACE_UNUSED VALUE self);
210
222
  static VALUE _native_remove_testing_signal_handler(DDTRACE_UNUSED VALUE self);
223
+ static VALUE _native_install_sigprof_handler_on_altstack(DDTRACE_UNUSED VALUE self);
211
224
  static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self);
212
225
  static VALUE _native_gc_tracepoint(DDTRACE_UNUSED VALUE self, VALUE instance);
213
226
  static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused);
214
227
  static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused);
215
- static VALUE safely_call(VALUE (*function_to_call_safely)(VALUE), VALUE function_to_call_safely_arg, VALUE instance);
228
+ static VALUE safely_call(
229
+ VALUE (*function_to_call_safely)(VALUE),
230
+ VALUE function_to_call_safely_arg,
231
+ VALUE instance,
232
+ VALUE (*handle_sampling_failure)(VALUE, VALUE)
233
+ );
216
234
  static VALUE _native_simulate_handle_sampling_signal(DDTRACE_UNUSED VALUE self);
217
235
  static VALUE _native_simulate_sample_from_postponed_job(DDTRACE_UNUSED VALUE self);
218
236
  static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance);
@@ -221,23 +239,33 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance);
221
239
  static VALUE _native_stats_reset_not_thread_safe(DDTRACE_UNUSED VALUE self, VALUE instance);
222
240
  void *simulate_sampling_signal_delivery(DDTRACE_UNUSED void *_unused);
223
241
  static void grab_gvl_and_sample(void);
224
- static void reset_stats_not_thread_safe(struct cpu_and_wall_time_worker_state *state);
242
+ static void reset_stats_not_thread_safe(cpu_and_wall_time_worker_state *state);
225
243
  static void sleep_for(uint64_t time_ns);
226
244
  static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self);
227
245
  static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *unused2);
228
- static void disable_tracepoints(struct cpu_and_wall_time_worker_state *state);
246
+ static void disable_tracepoints(cpu_and_wall_time_worker_state *state);
229
247
  static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self);
230
248
  static VALUE rescued_sample_allocation(VALUE tracepoint_data);
231
- static void delayed_error(struct cpu_and_wall_time_worker_state *state, const char *error);
249
+ static VALUE rescued_after_allocation(VALUE self_instance);
250
+ static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error);
251
+ static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state);
232
252
  static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
233
253
  static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self);
234
254
  static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);
235
255
  #ifndef NO_GVL_INSTRUMENTATION
236
- static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused);
237
- static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
256
+ static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused);
257
+ static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
258
+ static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance);
259
+ static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception);
238
260
  #endif
239
- static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance);
240
261
  static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance);
262
+ static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception);
263
+ static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception);
264
+ static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception);
265
+ static VALUE handle_sampling_failure_rescued_after_allocation(VALUE self_instance, VALUE exception);
266
+ static inline void during_sample_enter(cpu_and_wall_time_worker_state* state);
267
+ static inline void during_sample_exit(cpu_and_wall_time_worker_state* state);
268
+ static void after_allocation_from_postponed_job(DDTRACE_UNUSED void *_unused);
241
269
 
242
270
  // We're using `on_newobj_event` function with `rb_add_event_hook2`, which requires in its public signature a function
243
271
  // with signature `rb_event_hook_func_t` which doesn't match `on_newobj_event`.
@@ -262,7 +290,17 @@ static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE
262
290
  // This global state is needed because a bunch of functions on this file need to access it from situations
263
291
  // (e.g. signal handler) where it's impossible or just awkward to pass it as an argument.
264
292
  static VALUE active_sampler_instance = Qnil;
265
- static struct cpu_and_wall_time_worker_state *active_sampler_instance_state = NULL;
293
+ static cpu_and_wall_time_worker_state *active_sampler_instance_state = NULL;
294
+ static VALUE clock_failure_exception_class = Qnil;
295
+
296
+ // Stats that live outside of any particular `cpu_and_wall_time_worker_state`, so that they can always be safely
297
+ // touched, even when we're not sure it's safe to touch the state (e.g. signal handler, no GVL, etc).
298
+ typedef struct {
299
+ // How many times we skipped sampling because we were running on the alternate signal stack (e.g. nested inside
300
+ // another signal handler, presumably GC compaction).
301
+ unsigned int signal_handler_skipped_sample_on_altstack;
302
+ } global_stats_t;
303
+ static global_stats_t global_stats;
266
304
 
267
305
  // See handle_sampling_signal for details on what this does
268
306
  #ifdef NO_POSTPONED_TRIGGER
@@ -284,13 +322,15 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
284
322
  sample_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, sample_from_postponed_job, NULL);
285
323
  after_gc_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gc_from_postponed_job, NULL);
286
324
  after_gvl_running_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gvl_running_from_postponed_job, NULL);
325
+ after_allocation_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_allocation_from_postponed_job, NULL);
287
326
 
288
327
  if (
289
328
  sample_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
290
329
  after_gc_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
291
- after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
330
+ after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
331
+ after_allocation_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
292
332
  ) {
293
- rb_raise(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
333
+ raise_error(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
294
334
  }
295
335
  #else
296
336
  gc_finalize_deferred_workaround = objspace_ptr_for_gc_finalize_deferred_workaround();
@@ -300,6 +340,8 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
300
340
  VALUE collectors_cpu_and_wall_time_worker_class = rb_define_class_under(collectors_module, "CpuAndWallTimeWorker", rb_cObject);
301
341
  // Hosts methods used for testing the native code using RSpec
302
342
  VALUE testing_module = rb_define_module_under(collectors_cpu_and_wall_time_worker_class, "Testing");
343
+ clock_failure_exception_class = rb_define_class_under(collectors_cpu_and_wall_time_worker_class, "ClockFailure", rb_eRuntimeError);
344
+ rb_gc_register_mark_object(clock_failure_exception_class);
303
345
 
304
346
  // Instances of the CpuAndWallTimeWorker class are "TypedData" objects.
305
347
  // "TypedData" objects are special objects in the Ruby VM that can wrap C structs.
@@ -319,11 +361,13 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
319
361
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stats_reset_not_thread_safe", _native_stats_reset_not_thread_safe, 1);
320
362
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_allocation_count", _native_allocation_count, 0);
321
363
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_is_running?", _native_is_running, 1);
364
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_failure_exception_during_operation", _native_failure_exception_during_operation, 1);
322
365
  rb_define_singleton_method(testing_module, "_native_current_sigprof_signal_handler", _native_current_sigprof_signal_handler, 0);
323
366
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_hold_signals", _native_hold_signals, 0);
324
367
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_resume_signals", _native_resume_signals, 0);
325
368
  rb_define_singleton_method(testing_module, "_native_install_testing_signal_handler", _native_install_testing_signal_handler, 0);
326
369
  rb_define_singleton_method(testing_module, "_native_remove_testing_signal_handler", _native_remove_testing_signal_handler, 0);
370
+ rb_define_singleton_method(testing_module, "_native_install_sigprof_handler_on_altstack", _native_install_sigprof_handler_on_altstack, 0);
327
371
  rb_define_singleton_method(testing_module, "_native_trigger_sample", _native_trigger_sample, 0);
328
372
  rb_define_singleton_method(testing_module, "_native_gc_tracepoint", _native_gc_tracepoint, 1);
329
373
  rb_define_singleton_method(testing_module, "_native_simulate_handle_sampling_signal", _native_simulate_handle_sampling_signal, 0);
@@ -334,7 +378,7 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
334
378
  rb_define_singleton_method(testing_module, "_native_gvl_profiling_hook_active", _native_gvl_profiling_hook_active, 1);
335
379
  }
336
380
 
337
- // This structure is used to define a Ruby object that stores a pointer to a struct cpu_and_wall_time_worker_state
381
+ // This structure is used to define a Ruby object that stores a pointer to a cpu_and_wall_time_worker_state
338
382
  // See also https://github.com/ruby/ruby/blob/master/doc/extension.rdoc for how this works
339
383
  static const rb_data_type_t cpu_and_wall_time_worker_typed_data = {
340
384
  .wrap_struct_name = "Datadog::Profiling::Collectors::CpuAndWallTimeWorker",
@@ -350,7 +394,7 @@ static const rb_data_type_t cpu_and_wall_time_worker_typed_data = {
350
394
  static VALUE _native_new(VALUE klass) {
351
395
  long now = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
352
396
 
353
- struct cpu_and_wall_time_worker_state *state = ruby_xcalloc(1, sizeof(struct cpu_and_wall_time_worker_state));
397
+ cpu_and_wall_time_worker_state *state = ruby_xcalloc(1, sizeof(cpu_and_wall_time_worker_state));
354
398
 
355
399
  // Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
356
400
  // being leaked.
@@ -362,6 +406,8 @@ static VALUE _native_new(VALUE klass) {
362
406
  state->allocation_counting_enabled = false;
363
407
  state->gvl_profiling_enabled = false;
364
408
  state->skip_idle_samples_for_testing = false;
409
+ state->sighandler_sampling_enabled = false;
410
+ state->cpu_sampling_interval_ms = 10;
365
411
  state->thread_context_collector_instance = Qnil;
366
412
  state->idle_sampling_helper_instance = Qnil;
367
413
  state->owner_thread = Qnil;
@@ -370,9 +416,10 @@ static VALUE _native_new(VALUE klass) {
370
416
 
371
417
  atomic_init(&state->should_run, false);
372
418
  state->failure_exception = Qnil;
419
+ state->failure_exception_during_operation = NULL;
373
420
  state->stop_thread = Qnil;
374
421
 
375
- state->during_sample = false;
422
+ during_sample_exit(state);
376
423
 
377
424
  #ifndef NO_GVL_INSTRUMENTATION
378
425
  state->gvl_profiling_hook = NULL;
@@ -404,6 +451,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
404
451
  VALUE allocation_counting_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("allocation_counting_enabled")));
405
452
  VALUE gvl_profiling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("gvl_profiling_enabled")));
406
453
  VALUE skip_idle_samples_for_testing = rb_hash_fetch(options, ID2SYM(rb_intern("skip_idle_samples_for_testing")));
454
+ VALUE sighandler_sampling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("sighandler_sampling_enabled")));
455
+ VALUE cpu_sampling_interval_ms = rb_hash_fetch(options, ID2SYM(rb_intern("cpu_sampling_interval_ms")));
407
456
 
408
457
  ENFORCE_BOOLEAN(gc_profiling_enabled);
409
458
  ENFORCE_BOOLEAN(no_signals_workaround_enabled);
@@ -413,9 +462,11 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
413
462
  ENFORCE_BOOLEAN(allocation_counting_enabled);
414
463
  ENFORCE_BOOLEAN(gvl_profiling_enabled);
415
464
  ENFORCE_BOOLEAN(skip_idle_samples_for_testing)
465
+ ENFORCE_BOOLEAN(sighandler_sampling_enabled)
466
+ ENFORCE_TYPE(cpu_sampling_interval_ms, T_FIXNUM);
416
467
 
417
- struct cpu_and_wall_time_worker_state *state;
418
- TypedData_Get_Struct(self_instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
468
+ cpu_and_wall_time_worker_state *state;
469
+ TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
419
470
 
420
471
  state->gc_profiling_enabled = (gc_profiling_enabled == Qtrue);
421
472
  state->no_signals_workaround_enabled = (no_signals_workaround_enabled == Qtrue);
@@ -424,6 +475,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
424
475
  state->allocation_counting_enabled = (allocation_counting_enabled == Qtrue);
425
476
  state->gvl_profiling_enabled = (gvl_profiling_enabled == Qtrue);
426
477
  state->skip_idle_samples_for_testing = (skip_idle_samples_for_testing == Qtrue);
478
+ state->sighandler_sampling_enabled = (sighandler_sampling_enabled == Qtrue);
479
+ state->cpu_sampling_interval_ms = NUM2INT(cpu_sampling_interval_ms);
427
480
 
428
481
  double total_overhead_target_percentage = NUM2DBL(dynamic_sampling_rate_overhead_target_percentage);
429
482
  if (!state->allocation_profiling_enabled) {
@@ -445,7 +498,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
445
498
 
446
499
  // Since our state contains references to Ruby objects, we need to tell the Ruby GC about them
447
500
  static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr) {
448
- struct cpu_and_wall_time_worker_state *state = (struct cpu_and_wall_time_worker_state *) state_ptr;
501
+ cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
449
502
 
450
503
  rb_gc_mark(state->thread_context_collector_instance);
451
504
  rb_gc_mark(state->idle_sampling_helper_instance);
@@ -457,8 +510,8 @@ static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr) {
457
510
 
458
511
  // Called in a background thread created in CpuAndWallTimeWorker#start
459
512
  static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
460
- struct cpu_and_wall_time_worker_state *state;
461
- TypedData_Get_Struct(instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
513
+ cpu_and_wall_time_worker_state *state;
514
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
462
515
 
463
516
  // If we already got a delayed exception registered even before starting, raise before starting
464
517
  if (state->failure_exception != Qnil) {
@@ -466,13 +519,10 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
466
519
  rb_exc_raise(state->failure_exception);
467
520
  }
468
521
 
469
- struct cpu_and_wall_time_worker_state *old_state = active_sampler_instance_state;
522
+ cpu_and_wall_time_worker_state *old_state = active_sampler_instance_state;
470
523
  if (old_state != NULL) {
471
524
  if (is_thread_alive(old_state->owner_thread)) {
472
- rb_raise(
473
- rb_eRuntimeError,
474
- "Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread"
475
- );
525
+ raise_error(rb_eRuntimeError, "Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread");
476
526
  } else {
477
527
  // The previously active thread seems to have died without cleaning up after itself.
478
528
  // In this case, we can still go ahead and start the profiler BUT we make sure to disable any existing tracepoint
@@ -489,11 +539,20 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
489
539
  // situation we stop immediately and never even start the sampling trigger loop.
490
540
  if (state->stop_thread == rb_thread_current()) return Qnil;
491
541
 
542
+ thread_context_collector_profiler_internal_thread_started();
543
+
492
544
  // Reset the dynamic sampling rate state, if any (reminder: the monotonic clock reference may change after a fork)
493
545
  dynamic_sampling_rate_reset(&state->cpu_dynamic_sampling_rate);
494
546
  long now = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
495
547
  discrete_dynamic_sampler_reset(&state->allocation_sampler, now);
496
548
 
549
+ // Reset per-thread state, if any. This ensures there's no leftover state from a previous profiler run that would
550
+ // affect or be included in samples taken by this profiler about to run.
551
+ //
552
+ // NOTE: This needs to be called before we enable any tracepoints or anything that could trigger samples (e.g.
553
+ // reset cannot be concurrent with any sampling activity)
554
+ thread_context_collector_reset_all_per_thread_contexts(state->thread_context_collector_instance);
555
+
497
556
  // This write to a global is thread-safe BECAUSE we're still holding on to the global VM lock at this point
498
557
  active_sampler_instance_state = state;
499
558
  active_sampler_instance = instance;
@@ -503,6 +562,14 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
503
562
 
504
563
  block_sigprof_signal_handler_from_running_in_current_thread(); // We want to interrupt the thread with the global VM lock, never this one
505
564
 
565
+ #ifdef __APPLE__
566
+ // Promote the native thread that will run the sampling loop to a realtime scheduling policy so
567
+ // its periodic wake-ups are not subject to the default ~1-2ms timeshare wake latency. The matching
568
+ // demote happens below in this same function, alongside the SIGPROF unblock dance, so both per-thread
569
+ // policy changes are reverted before Ruby reuses this native thread for an unrelated Ruby thread.
570
+ promote_sampler_thread_to_realtime(MILLIS_AS_NS(state->cpu_sampling_interval_ms));
571
+ #endif
572
+
506
573
  // Release GVL, get to the actual work!
507
574
  int exception_state;
508
575
  rb_protect(release_gvl_and_run_sampling_trigger_loop, instance, &exception_state);
@@ -524,6 +591,14 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
524
591
  // had SIGPROF delivery blocked. :hide_the_pain_harold:
525
592
  unblock_sigprof_signal_handler_from_running_in_current_thread();
526
593
 
594
+ #ifdef __APPLE__
595
+ // Pairs with promote_sampler_thread_to_realtime above. Same reasoning as the SIGPROF unblock:
596
+ // Ruby may reuse this native thread for an unrelated Ruby thread that would otherwise inherit
597
+ // our scheduler policy. Doing it here also covers the exception path, since grab_gvl_and_sample
598
+ // can raise and unwind out of the sampling loop.
599
+ demote_sampler_thread_from_realtime();
600
+ #endif
601
+
527
602
  // Why replace and not use remove the signal handler? We do this because when a process receives a SIGPROF without
528
603
  // having an explicit signal handler set up, the process will instantly terminate with a confusing
529
604
  // "Profiling timer expired" message left behind. (This message doesn't come from us -- it's the default message for
@@ -534,8 +609,9 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
534
609
  // @ivoanjo: I suspect this will never happen, but the cost of getting it wrong is really high (VM terminates) so this
535
610
  // is a just-in-case situation.
536
611
  //
537
- // Note 2: This can raise exceptions as well, so make sure that all cleanups are done by the time we get here.
538
- replace_sigprof_signal_handler_with_empty_handler(handle_sampling_signal);
612
+ // Note 2: If exception_state is set, we have a pending exception that we'll re-raise below.
613
+ // In that case, we don't want replace_sigprof_signal_handler_with_empty_handler to raise another exception.
614
+ replace_sigprof_signal_handler_with_empty_handler(handle_sampling_signal, !exception_state);
539
615
 
540
616
  // Ensure that instance is not garbage collected while the native sampling loop is running; this is probably not needed, but just in case
541
617
  RB_GC_GUARD(instance);
@@ -546,68 +622,115 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
546
622
  }
547
623
 
548
624
  static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread) {
549
- struct cpu_and_wall_time_worker_state *state;
550
- TypedData_Get_Struct(self_instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
625
+ cpu_and_wall_time_worker_state *state;
626
+ TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
551
627
 
552
628
  state->stop_thread = worker_thread;
553
629
 
554
- return stop(self_instance, /* optional_exception: */ Qnil);
630
+ return stop(self_instance, Qnil, NULL);
555
631
  }
556
632
 
557
- static void stop_state(struct cpu_and_wall_time_worker_state *state, VALUE optional_exception) {
633
+ // When providing an `optional_exception`, `optional_exception_during_operation` should be provided as well
634
+ static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception, const char *optional_exception_during_operation) {
558
635
  atomic_store(&state->should_run, false);
559
636
  state->failure_exception = optional_exception;
637
+ state->failure_exception_during_operation = optional_exception_during_operation;
560
638
 
561
639
  // Disable the tracepoints as soon as possible, so the VM doesn't keep on calling them
562
640
  disable_tracepoints(state);
563
641
  }
564
642
 
565
- static VALUE stop(VALUE self_instance, VALUE optional_exception) {
566
- struct cpu_and_wall_time_worker_state *state;
567
- TypedData_Get_Struct(self_instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
643
+ static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *optional_exception_during_operation) {
644
+ cpu_and_wall_time_worker_state *state;
645
+ TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
568
646
 
569
- stop_state(state, optional_exception);
647
+ stop_state(state, optional_exception, optional_exception_during_operation);
570
648
 
571
649
  return Qtrue;
572
650
  }
573
651
 
652
+ // Our SIGPROF handler is not installed with SA_ONSTACK (see `install_sigprof_signal_handler`).
653
+ // If we detect our signal handler is running on the alt stack, it means we interrupted another signal handler
654
+ // that IS running on the alt stack -- in practice, Ruby's GC compaction read-barrier handler.
655
+ #ifdef __APPLE__
656
+ // On macOS, `ucontext->uc_stack.ss_sp` reports the (live) stack pointer near the top of the alt stack rather than the
657
+ // alt-stack base like Linux does, and `ss_size` is the full alt-stack size. Thus the linux version of the check
658
+ // didn't work for macOS.
659
+ // As an alternative, we query `sigaltstack()`; we save/restore errno since `sigaltstack()` may clobber it.
660
+ static bool is_running_on_alternate_signal_stack(DDTRACE_UNUSED void *ucontext) {
661
+ int old_errno = errno;
662
+ stack_t current_alt_stack;
663
+ bool on_alt_stack = sigaltstack(NULL, &current_alt_stack) == 0 && (current_alt_stack.ss_flags & SS_ONSTACK) != 0;
664
+ errno = old_errno;
665
+ return on_alt_stack;
666
+ }
667
+ #else
668
+ // NOTE: On Linux we use the alt-stack bounds from the `ucontext` rather than `sigaltstack()`, allowing us to avoid one syscall.
669
+ static bool is_running_on_alternate_signal_stack(void *ucontext) {
670
+ if (ucontext == NULL) return false;
671
+
672
+ stack_t alt_stack = ((ucontext_t *) ucontext)->uc_stack;
673
+ if (alt_stack.ss_size == 0) return false; // No alternate signal stack registered on this thread
674
+
675
+ char stack_local;
676
+ uintptr_t current_sp = (uintptr_t) &stack_local;
677
+ uintptr_t alt_stack_start = (uintptr_t) alt_stack.ss_sp;
678
+
679
+ return current_sp >= alt_stack_start && current_sp < alt_stack_start + alt_stack.ss_size;
680
+ }
681
+ #endif
682
+
574
683
  // NOTE: Remember that this will run in the thread and within the scope of user code, including user C code.
575
684
  // We need to be careful not to change any state that may be observed OR to restore it if we do. For instance, if anything
576
685
  // we do here can set `errno`, then we must be careful to restore the old `errno` after the fact.
577
- static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext) {
578
- struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
579
-
580
- // This can potentially happen if the CpuAndWallTimeWorker was stopped while the signal delivery was happening; nothing to do
581
- if (state == NULL) return;
686
+ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, void *ucontext) {
687
+ // If we're running on the alternate signal stack, we've interrupted another signal handler that's running
688
+ // there -- in practice, Ruby's GC compaction read-barrier handler.
689
+ // During GC compaction, Ruby protects pages containing Ruby objects, so many of the checks we do below are unsafe
690
+ // since they can touch those pages, and thus we just bail out here in this situation to avoid crashes.
691
+ if (is_running_on_alternate_signal_stack(ucontext)) {
692
+ global_stats.signal_handler_skipped_sample_on_altstack++;
693
+ return;
694
+ }
582
695
 
696
+ // We must first check that we landed on the correct thread and can proceed.
697
+ // We must never touch the state before we confirm that we have landed on the thread that is holding the GVL on the main
698
+ // ractor as otherwise we may be concurrent with the profiler shutting down and removing its state.
583
699
  if (
584
700
  !ruby_native_thread_p() || // Not a Ruby thread
585
701
  !is_current_thread_holding_the_gvl() || // Not safe to enqueue a sample from this thread
586
702
  !ddtrace_rb_ractor_main_p() // We're not on the main Ractor; we currently don't support profiling non-main Ractors
587
- ) {
588
- state->stats.signal_handler_wrong_thread++;
589
- return;
590
- }
703
+ ) return;
704
+
705
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
591
706
 
592
- // We implicitly assume there can be no concurrent nor nested calls to handle_sampling_signal because
593
- // a) we get triggered using SIGPROF, and the docs state a second SIGPROF will not interrupt an existing one
707
+ // This can potentially happen if the CpuAndWallTimeWorker was stopped while the signal delivery was happening; nothing to do
708
+ if (state == NULL) return;
709
+
710
+ // We assume there can be no concurrent nor nested calls to handle_sampling_signal because
711
+ // a) we get triggered using SIGPROF, and the docs state a second SIGPROF will not interrupt an existing one (see sigaction docs on sa_mask)
594
712
  // b) we validate we are in the thread that has the global VM lock; if a different thread gets a signal, it will return early
595
713
  // because it will not have the global VM lock
596
714
 
597
- // Note: rb_postponed_job_register_one ensures that if there's a previous sample_from_postponed_job queued for execution
598
- // then we will not queue a second one. It does this by doing a linear scan on the existing jobs; in the future we
599
- // may want to implement that check ourselves.
600
-
601
715
  state->stats.signal_handler_enqueued_sample++;
602
716
 
603
- // Note: If we ever want to get rid of rb_postponed_job_register_one, remember not to clobber Ruby exceptions, as
604
- // this function does this helpful job for us now -- https://github.com/ruby/ruby/commit/a98e343d39c4d7bf1e2190b076720f32d9f298b3.
717
+ bool sample_from_signal_handler =
718
+ state->sighandler_sampling_enabled &&
719
+ // Don't sample if we're already in the middle of processing a sample
720
+ !state->during_sample;
721
+
722
+ if (sample_from_signal_handler) {
723
+ // Buffer current stack trace. Note that this will not actually record the sample, for that we still need to wait
724
+ // until the postponed job below gets run.
725
+ bool prepared = thread_context_collector_prepare_sample_inside_signal_handler();
726
+
727
+ if (prepared) state->stats.signal_handler_prepared_sample++;
728
+ }
729
+
605
730
  #ifndef NO_POSTPONED_TRIGGER // Ruby 3.3+
606
731
  rb_postponed_job_trigger(sample_from_postponed_job_handle);
607
- state->stats.postponed_job_success++; // Always succeeds
608
732
  #else
609
-
610
- // This is a workaround for https://bugs.ruby-lang.org/issues/19991 (for Ruby < 3.3)
733
+ // Passing in `gc_finalize_deferred_workaround` is a workaround for https://bugs.ruby-lang.org/issues/19991 (for Ruby < 3.3)
611
734
  //
612
735
  // TL;DR the `rb_postponed_job_register_one` API is not atomic (which is why it got replaced by `rb_postponed_job_trigger`)
613
736
  // and in rare cases can cause VM crashes.
@@ -631,28 +754,15 @@ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED si
631
754
  //
632
755
  // Thus, our workaround is simple: we pass in objspace as our argument, just in case the clobbering happens.
633
756
  // In the happy path, we never use this argument so it makes no difference. In the buggy path, we avoid crashing the VM.
634
- int result = rb_postponed_job_register(0, sample_from_postponed_job, gc_finalize_deferred_workaround /* instead of NULL */);
635
-
636
- // Officially, the result of rb_postponed_job_register_one is documented as being opaque, but in practice it does not
637
- // seem to have changed between Ruby 2.3 and 3.2, and so we track it as a debugging mechanism
638
- switch (result) {
639
- case 0:
640
- state->stats.postponed_job_full++; break;
641
- case 1:
642
- state->stats.postponed_job_success++; break;
643
- case 2:
644
- state->stats.postponed_job_skipped_already_existed++; break;
645
- default:
646
- state->stats.postponed_job_unknown_result++;
647
- }
757
+ rb_postponed_job_register(0, sample_from_postponed_job, gc_finalize_deferred_workaround /* instead of NULL */);
648
758
  #endif
649
759
  }
650
760
 
651
761
  // The actual sampling trigger loop always runs **without** the global vm lock.
652
762
  static void *run_sampling_trigger_loop(void *state_ptr) {
653
- struct cpu_and_wall_time_worker_state *state = (struct cpu_and_wall_time_worker_state *) state_ptr;
763
+ cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
654
764
 
655
- uint64_t minimum_time_between_signals = MILLIS_AS_NS(10);
765
+ uint64_t minimum_time_between_signals = MILLIS_AS_NS(state->cpu_sampling_interval_ms);
656
766
 
657
767
  while (atomic_load(&state->should_run)) {
658
768
  state->stats.trigger_sample_attempts++;
@@ -674,6 +784,10 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
674
784
  // we're doing this, so we may still not signal the correct thread from time to time, but our signal handler
675
785
  // includes a check to see if it got called in the right thread
676
786
  state->stats.interrupt_thread_attempts++;
787
+
788
+ // Pick up any last-minute attempts to stop before we send the signal
789
+ if (!atomic_load(&state->should_run)) return NULL;
790
+
677
791
  pthread_kill(owner.owner, SIGPROF);
678
792
  } else {
679
793
  if (state->skip_idle_samples_for_testing) {
@@ -701,7 +815,10 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
701
815
  // `dynamic_sampling_rate_get_sleep` may have changed while the above sleep was ongoing.
702
816
  uint64_t extra_sleep =
703
817
  dynamic_sampling_rate_get_sleep(&state->cpu_dynamic_sampling_rate, monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE));
704
- if (state->dynamic_sampling_rate_enabled && extra_sleep > 0) sleep_for(extra_sleep);
818
+ if (state->dynamic_sampling_rate_enabled && extra_sleep > 0) {
819
+ state->stats.trigger_sample_extra_sleep++;
820
+ sleep_for(extra_sleep);
821
+ }
705
822
  }
706
823
 
707
824
  return NULL; // Unused
@@ -709,13 +826,15 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
709
826
 
710
827
  // This is called by the Ruby VM when it wants to shut down the background thread
711
828
  static void interrupt_sampling_trigger_loop(void *state_ptr) {
712
- struct cpu_and_wall_time_worker_state *state = (struct cpu_and_wall_time_worker_state *) state_ptr;
829
+ cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
713
830
 
714
831
  atomic_store(&state->should_run, false);
715
832
  }
716
833
 
834
+ // Note: If we ever want to get rid of the postponed job execution, remember not to clobber Ruby exceptions, as
835
+ // this function does this helpful job for us now -- https://github.com/ruby/ruby/commit/a98e343d39c4d7bf1e2190b076720f32d9f298b3.
717
836
  static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused) {
718
- struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
837
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
719
838
 
720
839
  // This can potentially happen if the CpuAndWallTimeWorker was stopped while the postponed job was waiting to be executed; nothing to do
721
840
  if (state == NULL) return;
@@ -726,17 +845,22 @@ static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused) {
726
845
  return; // We're not on the main Ractor; we currently don't support profiling non-main Ractors
727
846
  }
728
847
 
729
- state->during_sample = true;
848
+ during_sample_enter(state);
730
849
 
731
850
  // Rescue against any exceptions that happen during sampling
732
- safely_call(rescued_sample_from_postponed_job, state->self_instance, state->self_instance);
851
+ safely_call(
852
+ rescued_sample_from_postponed_job,
853
+ state->self_instance,
854
+ state->self_instance,
855
+ handle_sampling_failure_rescued_sample_from_postponed_job
856
+ );
733
857
 
734
- state->during_sample = false;
858
+ during_sample_exit(state);
735
859
  }
736
860
 
737
861
  static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
738
- struct cpu_and_wall_time_worker_state *state;
739
- TypedData_Get_Struct(self_instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
862
+ cpu_and_wall_time_worker_state *state;
863
+ TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
740
864
 
741
865
  long wall_time_ns_before_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
742
866
 
@@ -747,8 +871,7 @@ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
747
871
 
748
872
  state->stats.cpu_sampled++;
749
873
 
750
- VALUE profiler_overhead_stack_thread = state->owner_thread; // Used to attribute profiler overhead to a different stack
751
- thread_context_collector_sample(state->thread_context_collector_instance, wall_time_ns_before_sample, profiler_overhead_stack_thread);
874
+ thread_context_collector_sample(state->thread_context_collector_instance, wall_time_ns_before_sample);
752
875
 
753
876
  long wall_time_ns_after_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
754
877
  long delta_ns = wall_time_ns_after_sample - wall_time_ns_before_sample;
@@ -766,11 +889,6 @@ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
766
889
  return Qnil;
767
890
  }
768
891
 
769
- static VALUE handle_sampling_failure(VALUE self_instance, VALUE exception) {
770
- stop(self_instance, exception);
771
- return Qnil;
772
- }
773
-
774
892
  // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
775
893
  // It SHOULD NOT be used for other purposes.
776
894
  static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self) {
@@ -791,8 +909,8 @@ static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self) {
791
909
  }
792
910
 
793
911
  static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
794
- struct cpu_and_wall_time_worker_state *state;
795
- TypedData_Get_Struct(instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
912
+ cpu_and_wall_time_worker_state *state;
913
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
796
914
 
797
915
  // Final preparations: Setup signal handler and enable tracepoints. We run these here and not in `_native_sampling_loop`
798
916
  // because they may raise exceptions.
@@ -809,22 +927,19 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
809
927
 
810
928
  if (state->gvl_profiling_enabled) {
811
929
  #ifndef NO_GVL_INSTRUMENTATION
812
- #ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS
813
- gvl_profiling_state_thread_tracking_workaround();
814
- #endif
815
-
816
930
  state->gvl_profiling_hook = rb_internal_thread_add_event_hook(
817
931
  on_gvl_event,
818
932
  (
819
933
  // For now we're only asking for these events, even though there's more
820
934
  // (e.g. check docs or gvl-tracing gem)
935
+ RUBY_INTERNAL_THREAD_EVENT_SUSPENDED | /* released gvl */
821
936
  RUBY_INTERNAL_THREAD_EVENT_READY /* waiting for gvl */ |
822
937
  RUBY_INTERNAL_THREAD_EVENT_RESUMED /* running/runnable */
823
938
  ),
824
939
  NULL
825
940
  );
826
941
  #else
827
- rb_raise(rb_eArgError, "GVL profiling is not supported in this Ruby version");
942
+ raise_error(rb_eArgError, "GVL profiling is not supported in this Ruby version");
828
943
  #endif
829
944
  }
830
945
 
@@ -836,17 +951,28 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
836
951
  // If we stopped sampling due to an exception, re-raise it (now in the worker thread)
837
952
  if (state->failure_exception != Qnil) rb_exc_raise(state->failure_exception);
838
953
 
954
+ thread_context_collector_profiler_internal_thread_done(state->thread_context_collector_instance);
955
+
839
956
  return Qnil;
840
957
  }
841
958
 
842
959
  // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
843
960
  // It SHOULD NOT be used for other purposes.
844
961
  static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance) {
845
- struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
962
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
846
963
 
847
964
  return (state != NULL && is_thread_alive(state->owner_thread) && state->self_instance == instance) ? Qtrue : Qfalse;
848
965
  }
849
966
 
967
+ static VALUE _native_failure_exception_during_operation(DDTRACE_UNUSED VALUE self, VALUE instance) {
968
+ cpu_and_wall_time_worker_state *state;
969
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
970
+
971
+ if (state->failure_exception_during_operation == NULL) return Qnil;
972
+
973
+ return rb_str_new_cstr(state->failure_exception_during_operation);
974
+ }
975
+
850
976
  static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext) {
851
977
  /* Does nothing on purpose */
852
978
  }
@@ -865,6 +991,28 @@ static VALUE _native_remove_testing_signal_handler(DDTRACE_UNUSED VALUE self) {
865
991
  return Qtrue;
866
992
  }
867
993
 
994
+ // Reproduces a profiler sample being taken in the middle of GC compaction: during compaction a SIGPROF can be
995
+ // delivered while nested inside Ruby's read-barrier signal handler, which runs on the alternate signal stack. We
996
+ // simulate that here by re-installing the production `handle_sampling_signal` with the `SA_ONSTACK` flag, so it runs
997
+ // on the alternate signal stack just like it would in that scenario.
998
+ //
999
+ // The `SA_ONSTACK` will be undone by any next `replace_sigprof_signal_handler_with_empty_handler` /
1000
+ // `install_sigprof_signal_handler_internal` / `remove_sigprof_signal_handle` so this doesn't need a specific, symmetric
1001
+ // `uninstall` to undo this.
1002
+ static VALUE _native_install_sigprof_handler_on_altstack(DDTRACE_UNUSED VALUE self) {
1003
+ struct sigaction signal_handler_config = {
1004
+ .sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK,
1005
+ .sa_sigaction = handle_sampling_signal,
1006
+ };
1007
+ sigemptyset(&signal_handler_config.sa_mask);
1008
+
1009
+ if (sigaction(SIGPROF, &signal_handler_config, NULL) != 0) {
1010
+ rb_sys_fail("Failed to install SA_ONSTACK SIGPROF signal handler for testing");
1011
+ }
1012
+
1013
+ return Qtrue;
1014
+ }
1015
+
868
1016
  // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
869
1017
  // It SHOULD NOT be used for other purposes.
870
1018
  static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self) {
@@ -875,8 +1023,8 @@ static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self) {
875
1023
  // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
876
1024
  // It SHOULD NOT be used for other purposes.
877
1025
  static VALUE _native_gc_tracepoint(DDTRACE_UNUSED VALUE self, VALUE instance) {
878
- struct cpu_and_wall_time_worker_state *state;
879
- TypedData_Get_Struct(instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1026
+ cpu_and_wall_time_worker_state *state;
1027
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
880
1028
 
881
1029
  return state->gc_tracepoint;
882
1030
  }
@@ -902,7 +1050,7 @@ static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused) {
902
1050
  int event = rb_tracearg_event_flag(rb_tracearg_from_tracepoint(tracepoint_data));
903
1051
  if (event != RUBY_INTERNAL_EVENT_GC_ENTER && event != RUBY_INTERNAL_EVENT_GC_EXIT) return; // Unknown event
904
1052
 
905
- struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1053
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
906
1054
 
907
1055
  // This should not happen in a normal situation because the tracepoint is always enabled after the instance is set
908
1056
  // and disabled before it is cleared, but just in case...
@@ -926,7 +1074,7 @@ static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused) {
926
1074
  }
927
1075
 
928
1076
  static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused) {
929
- struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1077
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
930
1078
 
931
1079
  // This can potentially happen if the CpuAndWallTimeWorker was stopped while the postponed job was waiting to be executed; nothing to do
932
1080
  if (state == NULL) return;
@@ -937,16 +1085,26 @@ static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused) {
937
1085
  return; // We're not on the main Ractor; we currently don't support profiling non-main Ractors
938
1086
  }
939
1087
 
940
- state->during_sample = true;
1088
+ during_sample_enter(state);
941
1089
 
942
- safely_call(thread_context_collector_sample_after_gc, state->thread_context_collector_instance, state->self_instance);
1090
+ safely_call(
1091
+ thread_context_collector_sample_after_gc,
1092
+ state->thread_context_collector_instance,
1093
+ state->self_instance,
1094
+ handle_sampling_failure_thread_context_collector_sample_after_gc
1095
+ );
943
1096
 
944
- state->during_sample = false;
1097
+ during_sample_exit(state);
945
1098
  }
946
1099
 
947
1100
  // Equivalent to Ruby begin/rescue call, where we call a C function and jump to the exception handler if an
948
1101
  // exception gets raised within
949
- static VALUE safely_call(VALUE (*function_to_call_safely)(VALUE), VALUE function_to_call_safely_arg, VALUE instance) {
1102
+ static VALUE safely_call(
1103
+ VALUE (*function_to_call_safely)(VALUE),
1104
+ VALUE function_to_call_safely_arg,
1105
+ VALUE instance,
1106
+ VALUE (*handle_sampling_failure)(VALUE, VALUE)
1107
+ ) {
950
1108
  VALUE exception_handler_function_arg = instance;
951
1109
  return rb_rescue2(
952
1110
  function_to_call_safely,
@@ -980,9 +1138,12 @@ static VALUE _native_simulate_sample_from_postponed_job(DDTRACE_UNUSED VALUE sel
980
1138
  //
981
1139
  // In the future, if we add more other components with tracepoints, we will need to coordinate stopping all such
982
1140
  // tracepoints before doing the other cleaning steps.
1141
+ //
1142
+ // Note that tests call this method directly in the same process without forking,
1143
+ // and in such a case non-current Threads keep running.
983
1144
  static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance) {
984
- struct cpu_and_wall_time_worker_state *state;
985
- TypedData_Get_Struct(instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1145
+ cpu_and_wall_time_worker_state *state;
1146
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
986
1147
 
987
1148
  // Disable all tracepoints, so that there are no more attempts to mutate the profile
988
1149
  disable_tracepoints(state);
@@ -1000,8 +1161,8 @@ static VALUE _native_is_sigprof_blocked_in_current_thread(DDTRACE_UNUSED VALUE s
1000
1161
  }
1001
1162
 
1002
1163
  static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
1003
- struct cpu_and_wall_time_worker_state *state;
1004
- TypedData_Get_Struct(instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1164
+ cpu_and_wall_time_worker_state *state;
1165
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1005
1166
 
1006
1167
  unsigned long total_cpu_samples_attempted = state->stats.cpu_sampled + state->stats.cpu_skipped;
1007
1168
  VALUE effective_cpu_sample_rate =
@@ -1016,14 +1177,12 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
1016
1177
  VALUE stats_as_hash = rb_hash_new();
1017
1178
  VALUE arguments[] = {
1018
1179
  ID2SYM(rb_intern("trigger_sample_attempts")), /* => */ UINT2NUM(state->stats.trigger_sample_attempts),
1180
+ ID2SYM(rb_intern("trigger_sample_extra_sleep")), /* => */ UINT2NUM(state->stats.trigger_sample_extra_sleep),
1019
1181
  ID2SYM(rb_intern("trigger_simulated_signal_delivery_attempts")), /* => */ UINT2NUM(state->stats.trigger_simulated_signal_delivery_attempts),
1020
1182
  ID2SYM(rb_intern("simulated_signal_delivery")), /* => */ UINT2NUM(state->stats.simulated_signal_delivery),
1021
1183
  ID2SYM(rb_intern("signal_handler_enqueued_sample")), /* => */ UINT2NUM(state->stats.signal_handler_enqueued_sample),
1022
- ID2SYM(rb_intern("signal_handler_wrong_thread")), /* => */ UINT2NUM(state->stats.signal_handler_wrong_thread),
1023
- ID2SYM(rb_intern("postponed_job_skipped_already_existed")), /* => */ UINT2NUM(state->stats.postponed_job_skipped_already_existed),
1024
- ID2SYM(rb_intern("postponed_job_success")), /* => */ UINT2NUM(state->stats.postponed_job_success),
1025
- ID2SYM(rb_intern("postponed_job_full")), /* => */ UINT2NUM(state->stats.postponed_job_full),
1026
- ID2SYM(rb_intern("postponed_job_unknown_result")), /* => */ UINT2NUM(state->stats.postponed_job_unknown_result),
1184
+ ID2SYM(rb_intern("signal_handler_prepared_sample")), /* => */ UINT2NUM(state->stats.signal_handler_prepared_sample),
1185
+ ID2SYM(rb_intern("signal_handler_skipped_sample_on_altstack")), /* => */ UINT2NUM(global_stats.signal_handler_skipped_sample_on_altstack),
1027
1186
  ID2SYM(rb_intern("interrupt_thread_attempts")), /* => */ UINT2NUM(state->stats.interrupt_thread_attempts),
1028
1187
 
1029
1188
  // CPU Stats
@@ -1053,28 +1212,32 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
1053
1212
  ID2SYM(rb_intern("gvl_sampling_time_ns_max")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_max, > 0, ULL2NUM),
1054
1213
  ID2SYM(rb_intern("gvl_sampling_time_ns_total")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_total, > 0, ULL2NUM),
1055
1214
  ID2SYM(rb_intern("gvl_sampling_time_ns_avg")), /* => */ RUBY_AVG_OR_NIL(state->stats.gvl_sampling_time_ns_total, state->stats.after_gvl_running),
1215
+ ID2SYM(rb_intern("gvl_waiting_time_ns_total")), /* => */ state->gvl_profiling_enabled ? ULL2NUM(state->stats.vm_metrics.gvl_waiting_time_ns_total) : Qnil,
1056
1216
  };
1057
1217
  for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(stats_as_hash, arguments[i], arguments[i+1]);
1218
+
1219
+ thread_context_collector_stats(state->thread_context_collector_instance, stats_as_hash);
1220
+
1058
1221
  return stats_as_hash;
1059
1222
  }
1060
1223
 
1061
1224
  static VALUE _native_stats_reset_not_thread_safe(DDTRACE_UNUSED VALUE self, VALUE instance) {
1062
- struct cpu_and_wall_time_worker_state *state;
1063
- TypedData_Get_Struct(instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1225
+ cpu_and_wall_time_worker_state *state;
1226
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1064
1227
  reset_stats_not_thread_safe(state);
1228
+ thread_context_collector_stats_reset_not_thread_safe(state->thread_context_collector_instance);
1065
1229
  return Qnil;
1066
1230
  }
1067
1231
 
1068
1232
  void *simulate_sampling_signal_delivery(DDTRACE_UNUSED void *_unused) {
1069
- struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1233
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1070
1234
 
1071
1235
  // This can potentially happen if the CpuAndWallTimeWorker was stopped while the IdleSamplingHelper was trying to execute this action
1072
1236
  if (state == NULL) return NULL;
1073
1237
 
1074
1238
  state->stats.simulated_signal_delivery++;
1075
1239
 
1076
- // @ivoanjo: We could instead directly call sample_from_postponed_job, but I chose to go through the signal handler
1077
- // so that the simulated case is as close to the original one as well (including any metrics increases, etc).
1240
+ // `handle_sampling_signal` does a few things extra on top of `sample_from_postponed_job` so that's why we don't shortcut here
1078
1241
  handle_sampling_signal(0, NULL, NULL);
1079
1242
 
1080
1243
  return NULL; // Unused
@@ -1082,19 +1245,22 @@ void *simulate_sampling_signal_delivery(DDTRACE_UNUSED void *_unused) {
1082
1245
 
1083
1246
  static void grab_gvl_and_sample(void) { rb_thread_call_with_gvl(simulate_sampling_signal_delivery, NULL); }
1084
1247
 
1085
- static void reset_stats_not_thread_safe(struct cpu_and_wall_time_worker_state *state) {
1248
+ static void reset_stats_not_thread_safe(cpu_and_wall_time_worker_state *state) {
1086
1249
  // NOTE: This is not really thread safe so ongoing sampling operations that are concurrent with a reset can have their stats:
1087
1250
  // * Lost (writes after stats retrieval but before reset).
1088
1251
  // * Included in the previous stats window (writes before stats retrieval and reset).
1089
1252
  // * Included in the following stats window (writes after stats retrieval and reset).
1090
1253
  // Given the expected infrequency of resetting (~once per 60s profile) and the auxiliary/non-critical nature of these stats
1091
1254
  // this momentary loss of accuracy is deemed acceptable to keep overhead to a minimum.
1255
+
1092
1256
  state->stats = (struct stats) {
1093
1257
  // All these values are initialized to their highest value possible since we always take the min between existing and latest sample
1094
1258
  .cpu_sampling_time_ns_min = UINT64_MAX,
1095
1259
  .allocation_sampling_time_ns_min = UINT64_MAX,
1096
1260
  .gvl_sampling_time_ns_min = UINT64_MAX,
1261
+ // Other fields are reset to 0
1097
1262
  };
1263
+ global_stats = (global_stats_t) {0};
1098
1264
  }
1099
1265
 
1100
1266
  static void sleep_for(uint64_t time_ns) {
@@ -1116,7 +1282,7 @@ static void sleep_for(uint64_t time_ns) {
1116
1282
  }
1117
1283
 
1118
1284
  static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
1119
- struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state;
1285
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state;
1120
1286
 
1121
1287
  bool are_allocations_being_tracked = state != NULL && state->allocation_profiling_enabled && state->allocation_counting_enabled;
1122
1288
 
@@ -1126,7 +1292,7 @@ static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
1126
1292
  #define HANDLE_CLOCK_FAILURE(call) ({ \
1127
1293
  long _result = (call); \
1128
1294
  if (_result == 0) { \
1129
- delayed_error(state, ERR_CLOCK_FAIL); \
1295
+ delayed_error_clock_failure(state); \
1130
1296
  return; \
1131
1297
  } \
1132
1298
  _result; \
@@ -1149,7 +1315,7 @@ static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
1149
1315
  // call `rb_tracearg_from_tracepoint(anything)` anywhere during this function or its callees to get the data, so that's
1150
1316
  // why it's not being passed as an argument.
1151
1317
  static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *unused2) {
1152
- struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1318
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1153
1319
 
1154
1320
  // This should not happen in a normal situation because the tracepoint is always enabled after the instance is set
1155
1321
  // and disabled before it is cleared, but just in case...
@@ -1171,6 +1337,25 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
1171
1337
  return;
1172
1338
  }
1173
1339
 
1340
+ VALUE current_thread = rb_thread_current();
1341
+
1342
+ // If Ruby is in the middle of raising an exception, we don't want to try to sample. This is because if we accidentally
1343
+ // trigger an exception inside the profiler code, bad things will happen (specifically, Ruby will try to kill off the
1344
+ // thread even though we may try to catch the exception).
1345
+ //
1346
+ // Note that "in the middle of raising an exception" means the exception itself has already been allocated.
1347
+ // What's getting allocated now is probably the backtrace objects (@ivoanjo or at least that's what I've observed)
1348
+ if (is_raised_flag_set(current_thread)) {
1349
+ return;
1350
+ }
1351
+
1352
+ per_thread_context *thread_context = get_per_thread_context(current_thread);
1353
+ if (!thread_context) {
1354
+ // Context is created eagerly via on_thread_begin_event, so this should not normally be NULL.
1355
+ // We keep the guard since we can't allocate here (inside on_newobj_event).
1356
+ return;
1357
+ }
1358
+
1174
1359
  // Hot path: Dynamic sampling rate is usually enabled and the sampling decision is usually false
1175
1360
  if (RB_LIKELY(state->dynamic_sampling_rate_enabled && !discrete_dynamic_sampler_should_sample(&state->allocation_sampler))) {
1176
1361
  state->stats.allocation_skipped++;
@@ -1197,19 +1382,20 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
1197
1382
  &state->allocation_sampler, HANDLE_CLOCK_FAILURE(monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE))
1198
1383
  );
1199
1384
 
1200
- // @ivoanjo: Strictly speaking, this is not needed because Ruby should not call the same tracepoint while a previous
1201
- // invocation is still pending, (e.g. it wouldn't call `on_newobj_event` while it's already running), but I decided
1202
- // to keep this here for consistency -- every call to the thread context (other than the special gc calls which are
1203
- // defined as not being able to allocate) sets this.
1204
- state->during_sample = true;
1385
+ during_sample_enter(state);
1205
1386
 
1206
1387
  // Rescue against any exceptions that happen during sampling
1207
- safely_call(rescued_sample_allocation, Qnil, state->self_instance);
1388
+ safely_call(
1389
+ rescued_sample_allocation,
1390
+ (VALUE) thread_context,
1391
+ state->self_instance,
1392
+ handle_sampling_failure_rescued_sample_allocation
1393
+ );
1208
1394
 
1209
1395
  if (state->dynamic_sampling_rate_enabled) {
1210
1396
  long now = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
1211
1397
  if (now == 0) {
1212
- delayed_error(state, ERR_CLOCK_FAIL);
1398
+ delayed_error_clock_failure(state);
1213
1399
  // NOTE: Not short-circuiting here to make sure cleanup happens
1214
1400
  }
1215
1401
  uint64_t sampling_time_ns = discrete_dynamic_sampler_after_sample(&state->allocation_sampler, now);
@@ -1222,10 +1408,10 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
1222
1408
 
1223
1409
  state->stats.allocation_sampled++;
1224
1410
 
1225
- state->during_sample = false;
1411
+ during_sample_exit(state);
1226
1412
  }
1227
1413
 
1228
- static void disable_tracepoints(struct cpu_and_wall_time_worker_state *state) {
1414
+ static void disable_tracepoints(cpu_and_wall_time_worker_state *state) {
1229
1415
  if (state->gc_tracepoint != Qnil) {
1230
1416
  rb_tracepoint_disable(state->gc_tracepoint);
1231
1417
  }
@@ -1253,8 +1439,9 @@ static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self) {
1253
1439
  }
1254
1440
  }
1255
1441
 
1256
- static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {
1257
- struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1442
+ static VALUE rescued_sample_allocation(VALUE arg) {
1443
+ per_thread_context *thread_context = (per_thread_context*) arg;
1444
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1258
1445
 
1259
1446
  // This should not happen in a normal situation because on_newobj_event already checked for this, but just in case...
1260
1447
  if (state == NULL) return Qnil;
@@ -1272,27 +1459,40 @@ static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {
1272
1459
  // To control bias from sampling, we clamp the maximum weight attributed to a single allocation sample. This avoids
1273
1460
  // assigning a very large number to a sample, if for instance the dynamic sampling mechanism chose a really big interval.
1274
1461
  unsigned int weight = allocations_since_last_sample > MAX_ALLOC_WEIGHT ? MAX_ALLOC_WEIGHT : (unsigned int) allocations_since_last_sample;
1275
- thread_context_collector_sample_allocation(state->thread_context_collector_instance, weight, new_object);
1462
+ bool needs_after_allocation = thread_context_collector_sample_allocation(state->thread_context_collector_instance, thread_context, weight, new_object);
1276
1463
  // ...but we still represent the skipped samples in the profile, thus the data will account for all allocations.
1277
1464
  if (weight < allocations_since_last_sample) {
1278
1465
  uint32_t skipped_samples = (uint32_t) uint64_min_of(allocations_since_last_sample - weight, UINT32_MAX);
1279
1466
  thread_context_collector_sample_skipped_allocation_samples(state->thread_context_collector_instance, skipped_samples);
1280
1467
  }
1281
1468
 
1469
+ if (needs_after_allocation) {
1470
+ #ifndef NO_POSTPONED_TRIGGER
1471
+ rb_postponed_job_trigger(after_allocation_from_postponed_job_handle);
1472
+ #else
1473
+ // Not needed on legacy rubies
1474
+ #endif
1475
+ }
1476
+
1282
1477
  // Return a dummy VALUE because we're called from rb_rescue2 which requires it
1283
1478
  return Qnil;
1284
1479
  }
1285
1480
 
1286
- static void delayed_error(struct cpu_and_wall_time_worker_state *state, const char *error) {
1481
+ static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error) {
1287
1482
  // If we can't raise an immediate exception at the calling site, use the asynchronous flow through the main worker loop.
1288
- stop_state(state, rb_exc_new_cstr(rb_eRuntimeError, error));
1483
+ stop_state(state, rb_exc_new_cstr(rb_eRuntimeError, error), "delayed_error");
1484
+ }
1485
+
1486
+ static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state) {
1487
+ // If we can't raise an immediate exception at the calling site, use the asynchronous flow through the main worker loop.
1488
+ stop_state(state, rb_exc_new_cstr(clock_failure_exception_class, "failed to get clock time"), "delayed_error_clock_failure");
1289
1489
  }
1290
1490
 
1291
1491
  static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg) {
1292
1492
  ENFORCE_TYPE(error_msg, T_STRING);
1293
1493
 
1294
- struct cpu_and_wall_time_worker_state *state;
1295
- TypedData_Get_Struct(instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1494
+ cpu_and_wall_time_worker_state *state;
1495
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1296
1496
 
1297
1497
  delayed_error(state, rb_string_value_cstr(&error_msg));
1298
1498
 
@@ -1316,64 +1516,94 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
1316
1516
  #ifndef NO_GVL_INSTRUMENTATION
1317
1517
  static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused) {
1318
1518
  // Be very careful about touching the `state` here or doing anything at all:
1319
- // This function gets called without the GVL, and potentially from background Ractors!
1519
+ // This function gets called without the GVL, and potentially from non-main Ractors!
1320
1520
  //
1321
- // In fact, the `target_thread` that this event is about may not even be the current thread. (So be careful with thread locals that
1322
- // are not directly tied to the `target_thread` object and the like)
1323
- gvl_profiling_thread target_thread = thread_from_event(event_data);
1521
+ // Note, even though these events can get called without the GVL, they synchronize-with enabling/disabling of
1522
+ // the hook that calls us using a read-write lock. Thus, disabling the hook cannot be concurrent with calling this function,
1523
+ // and once the disable finishes there can't be "late" calls into this function.
1324
1524
 
1325
- if (event_id == RUBY_INTERNAL_THREAD_EVENT_READY) { /* waiting for gvl */
1326
- thread_context_collector_on_gvl_waiting(target_thread);
1525
+ // The thread that this event is about may not be the current thread
1526
+ // (as documented on rb_internal_thread_add_event_hook(), and this is notably the case for READY on Ruby 4.0),
1527
+ // so be careful with native thread locals that are not directly tied to the thread object and the like.
1528
+
1529
+ #ifdef HAVE_RUBY_THREAD_STORAGE_API
1530
+ VALUE target_thread = event_data->thread;
1531
+ #else
1532
+ // On Ruby 3.2 the event does not carry the thread, but all events fire on the thread itself.
1533
+ // However, during early thread startup rb_thread_current() can crash because the execution context (Fiber) isn't
1534
+ // stored in TLS yet; ruby_native_thread_p() guards against this.
1535
+ if (!ruby_native_thread_p()) return;
1536
+ VALUE target_thread = rb_thread_current();
1537
+ #endif
1538
+
1539
+ per_thread_context* thread_context = get_per_thread_context(target_thread);
1540
+ if (!thread_context) return;
1541
+ // If non-NULL the thread is profiled and from the main Ractor
1542
+
1543
+ if (event_id == RUBY_INTERNAL_THREAD_EVENT_SUSPENDED) { /* released gvl */
1544
+ thread_context_collector_on_gvl_released(thread_context);
1545
+ } else if (event_id == RUBY_INTERNAL_THREAD_EVENT_READY) { /* waiting for gvl */
1546
+ thread_context_collector_on_gvl_waiting(thread_context);
1327
1547
  } else if (event_id == RUBY_INTERNAL_THREAD_EVENT_RESUMED) { /* running/runnable */
1328
- // Interesting note: A RUBY_INTERNAL_THREAD_EVENT_RESUMED is guaranteed to be called with the GVL being acquired.
1329
- // (And... I think target_thread will be == rb_thread_current()?)
1548
+ // Interesting note: A RUBY_INTERNAL_THREAD_EVENT_RESUMED is guaranteed to be called with the GVL being acquired
1549
+ // and on the event thread.
1550
+ // However, on_gvl_event() is called while holding the scheduler lock, so we do as little work as possible here,
1551
+ // and perform the sample in a postponed_job.
1552
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1553
+ if (state == NULL) return; // This should not happen, but just in case...
1554
+
1555
+ // on_gvl_running prepares a stack sample so we need to gate it with `during_sample_enter` to avoid a
1556
+ // SIGPROF signal coming in during that function and potentially causing a corrupted final state.
1330
1557
  //
1331
- // But we're not sure if we're on the main Ractor yet. The thread context collector actually can actually help here:
1332
- // it tags threads it's tracking, so if a thread is tagged then by definition we know that thread belongs to the main
1333
- // Ractor. Thus, if we get a ON_GVL_RUNNING_UNKNOWN result we shouldn't touch any state, but otherwise we're good to go.
1558
+ // TODO: Currently, the sample prepared in on_gvl_running can still be clobbered if the signal handler runs
1559
+ // after `during_sample_exit` but before `after_gvl_running_from_postponed_job` gets to run. We'll need to fix
1560
+ // that next.
1561
+ during_sample_enter(state);
1334
1562
 
1335
- #ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS
1336
- target_thread = gvl_profiling_state_maybe_initialize();
1337
- #endif
1563
+ on_gvl_running_result result = thread_context_collector_on_gvl_running(state->thread_context_collector_instance, target_thread, thread_context);
1338
1564
 
1339
- on_gvl_running_result result = thread_context_collector_on_gvl_running(target_thread);
1565
+ during_sample_exit(state);
1340
1566
 
1341
- if (result == ON_GVL_RUNNING_SAMPLE) {
1567
+ if (result.waiting_for_gvl_duration_ns > 0) {
1568
+ state->stats.vm_metrics.gvl_waiting_time_ns_total += (uint64_t) result.waiting_for_gvl_duration_ns;
1569
+ }
1570
+
1571
+ if (result.action == ON_GVL_RUNNING_SAMPLE) {
1342
1572
  #ifndef NO_POSTPONED_TRIGGER
1343
1573
  rb_postponed_job_trigger(after_gvl_running_from_postponed_job_handle);
1344
1574
  #else
1345
1575
  rb_postponed_job_register_one(0, after_gvl_running_from_postponed_job, NULL);
1346
1576
  #endif
1347
- } else if (result == ON_GVL_RUNNING_DONT_SAMPLE) {
1348
- struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1349
-
1350
- if (state == NULL) return; // This should not happen, but just in case...
1351
-
1577
+ } else if (result.action == ON_GVL_RUNNING_DONT_SAMPLE) {
1352
1578
  state->stats.gvl_dont_sample++;
1353
1579
  }
1354
1580
  } else {
1355
- // This is a very delicate time and it's hard for us to raise an exception so let's at least complain to stderr
1356
- fprintf(stderr, "[ddtrace] Unexpected value in on_gvl_event (%d)\n", event_id);
1581
+ rb_bug("[ddtrace] Unexpected value in on_gvl_event (%d)\n", event_id);
1357
1582
  }
1358
1583
  }
1359
1584
 
1360
1585
  static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused) {
1361
- struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1586
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1362
1587
 
1363
1588
  // This can potentially happen if the CpuAndWallTimeWorker was stopped while the postponed job was waiting to be executed; nothing to do
1364
1589
  if (state == NULL) return;
1365
1590
 
1366
- state->during_sample = true;
1591
+ during_sample_enter(state);
1367
1592
 
1368
1593
  // Rescue against any exceptions that happen during sampling
1369
- safely_call(rescued_after_gvl_running_from_postponed_job, state->self_instance, state->self_instance);
1594
+ safely_call(
1595
+ rescued_after_gvl_running_from_postponed_job,
1596
+ state->self_instance,
1597
+ state->self_instance,
1598
+ handle_sampling_failure_rescued_after_gvl_running_from_postponed_job
1599
+ );
1370
1600
 
1371
- state->during_sample = false;
1601
+ during_sample_exit(state);
1372
1602
  }
1373
1603
 
1374
1604
  static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance) {
1375
- struct cpu_and_wall_time_worker_state *state;
1376
- TypedData_Get_Struct(self_instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1605
+ cpu_and_wall_time_worker_state *state;
1606
+ TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1377
1607
 
1378
1608
  long wall_time_ns_before_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
1379
1609
  thread_context_collector_sample_after_gvl_running(state->thread_context_collector_instance, rb_thread_current(), wall_time_ns_before_sample);
@@ -1394,13 +1624,95 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
1394
1624
  }
1395
1625
 
1396
1626
  static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance) {
1397
- struct cpu_and_wall_time_worker_state *state;
1398
- TypedData_Get_Struct(instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1627
+ cpu_and_wall_time_worker_state *state;
1628
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1399
1629
 
1400
1630
  return state->gvl_profiling_hook != NULL ? Qtrue : Qfalse;
1401
1631
  }
1632
+
1633
+ static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception) {
1634
+ stop(self_instance, exception, "rescued_after_gvl_running_from_postponed_job");
1635
+ return Qnil;
1636
+ }
1402
1637
  #else
1403
1638
  static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, DDTRACE_UNUSED VALUE instance) {
1404
1639
  return Qfalse;
1405
1640
  }
1406
1641
  #endif
1642
+
1643
+ static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception) {
1644
+ stop(self_instance, exception, "rescued_sample_from_postponed_job");
1645
+ return Qnil;
1646
+ }
1647
+
1648
+ static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception) {
1649
+ stop(self_instance, exception, "thread_context_collector_sample_after_gc");
1650
+ return Qnil;
1651
+ }
1652
+
1653
+ static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception) {
1654
+ stop(self_instance, exception, "rescued_sample_allocation");
1655
+ return Qnil;
1656
+ }
1657
+
1658
+ static VALUE handle_sampling_failure_rescued_after_allocation(VALUE self_instance, VALUE exception) {
1659
+ stop(self_instance, exception, "rescued_after_allocation");
1660
+ return Qnil;
1661
+ }
1662
+
1663
+ static VALUE rescued_after_allocation(VALUE self_instance) {
1664
+ cpu_and_wall_time_worker_state *state;
1665
+ TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1666
+
1667
+ thread_context_collector_after_allocation(state->thread_context_collector_instance);
1668
+
1669
+ // Return a dummy VALUE because we're called from rb_rescue2 which requires it
1670
+ return Qnil;
1671
+ }
1672
+
1673
+ // This postponed job callback is used to finalize heap allocation recordings on Ruby 4+.
1674
+ // During on_newobj_event, calling rb_obj_id() is unsafe because it mutates the object.
1675
+ // So we defer getting the object_id until after the event completes.
1676
+ #pragma GCC diagnostic push
1677
+ #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
1678
+ static void after_allocation_from_postponed_job(DDTRACE_UNUSED void *_unused) {
1679
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state;
1680
+
1681
+ if (state == NULL || !ddtrace_rb_ractor_main_p()) return;
1682
+
1683
+ // Protect against nested operations
1684
+ if (state->during_sample) return;
1685
+
1686
+ during_sample_enter(state);
1687
+
1688
+ // NOTE: We're not updating the allocation_sampler here.
1689
+ // This means work done in this function isn't accounted for as profiler overhead.
1690
+ // This is acceptable as the amount of work done here is expected to be small.
1691
+ safely_call(
1692
+ rescued_after_allocation,
1693
+ state->self_instance,
1694
+ state->self_instance,
1695
+ handle_sampling_failure_rescued_after_allocation
1696
+ );
1697
+
1698
+ during_sample_exit(state);
1699
+ }
1700
+ #pragma GCC diagnostic pop
1701
+
1702
+ static inline void during_sample_enter(cpu_and_wall_time_worker_state* state) {
1703
+ // Tell the compiler it's not allowed to reorder the `during_sample` flag with anything that happens after.
1704
+ //
1705
+ // In a few cases, we may be checking this flag from a signal handler, so we need to make sure the compiler didn't
1706
+ // get clever and reordered things in such a way that makes us miss the flag update.
1707
+ //
1708
+ // See https://github.com/ruby/ruby/pull/11036 for a similar change made to the Ruby VM with more context.
1709
+ state->during_sample = true;
1710
+ atomic_signal_fence(memory_order_seq_cst);
1711
+ }
1712
+
1713
+ static inline void during_sample_exit(cpu_and_wall_time_worker_state* state) {
1714
+ // See `during_sample_enter` for more context; in this case we set the fence before to make sure anything that
1715
+ // happens before the fence is not reordered with the flag update.
1716
+ atomic_signal_fence(memory_order_seq_cst);
1717
+ state->during_sample = false;
1718
+ }