datadog 2.30.0 → 2.43.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 (942) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +382 -1
  3. data/README.md +3 -0
  4. data/bin/ddprofrb +4 -4
  5. data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +12 -24
  6. data/ext/datadog_profiling_native_extension/clock_id.h +9 -1
  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 +1 -1
  9. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +312 -99
  10. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
  11. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +18 -4
  12. data/ext/datadog_profiling_native_extension/collectors_stack.c +256 -163
  13. data/ext/datadog_profiling_native_extension/collectors_stack.h +23 -4
  14. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +833 -513
  15. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +24 -9
  16. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +5 -4
  17. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +44 -19
  18. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +7 -12
  19. data/ext/datadog_profiling_native_extension/extconf.rb +83 -105
  20. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +4 -43
  21. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +15 -47
  22. data/ext/datadog_profiling_native_extension/heap_recorder.c +391 -277
  23. data/ext/datadog_profiling_native_extension/heap_recorder.h +50 -31
  24. data/ext/datadog_profiling_native_extension/http_transport.c +10 -5
  25. data/ext/datadog_profiling_native_extension/macos_sampler_thread.h +55 -0
  26. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +3 -27
  27. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +750 -482
  28. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +32 -12
  29. data/ext/datadog_profiling_native_extension/profiling.c +43 -4
  30. data/ext/datadog_profiling_native_extension/ruby_helpers.c +34 -106
  31. data/ext/datadog_profiling_native_extension/ruby_helpers.h +0 -7
  32. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +2 -0
  33. data/ext/datadog_profiling_native_extension/stack_recorder.c +139 -84
  34. data/ext/datadog_profiling_native_extension/stack_recorder.h +13 -4
  35. data/ext/datadog_profiling_native_extension/time_helpers.h +15 -8
  36. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +4 -2
  37. data/ext/libdatadog_api/crashtracker.c +7 -8
  38. data/ext/libdatadog_api/datadog_ruby_common.c +44 -19
  39. data/ext/libdatadog_api/datadog_ruby_common.h +7 -12
  40. data/ext/libdatadog_api/di.c +213 -0
  41. data/ext/libdatadog_api/extconf.rb +40 -26
  42. data/ext/libdatadog_api/feature_flags.c +26 -0
  43. data/ext/libdatadog_api/init.c +11 -2
  44. data/ext/libdatadog_api/otel_thread_context.c +247 -0
  45. data/ext/libdatadog_api/otel_thread_context.h +5 -0
  46. data/ext/libdatadog_api/trace_exporter.c +1932 -0
  47. data/ext/libdatadog_api/trace_exporter.h +5 -0
  48. data/ext/libdatadog_extconf_helpers.rb +50 -5
  49. data/lib/datadog/ai_guard/api_client.rb +9 -7
  50. data/lib/datadog/ai_guard/autoload.rb +10 -0
  51. data/lib/datadog/ai_guard/component.rb +11 -9
  52. data/lib/datadog/ai_guard/configuration.rb +106 -2
  53. data/lib/datadog/ai_guard/contrib/auto_instrument.rb +24 -0
  54. data/lib/datadog/ai_guard/contrib/rack/integration.rb +42 -0
  55. data/lib/datadog/ai_guard/contrib/rack/patcher.rb +26 -0
  56. data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +97 -0
  57. data/lib/datadog/ai_guard/contrib/rails/integration.rb +41 -0
  58. data/lib/datadog/ai_guard/contrib/rails/patcher.rb +97 -0
  59. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +41 -3
  60. data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
  61. data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
  62. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +3 -1
  63. data/lib/datadog/ai_guard/evaluation/request.rb +22 -17
  64. data/lib/datadog/ai_guard/evaluation/result.rb +3 -1
  65. data/lib/datadog/ai_guard/evaluation.rb +51 -7
  66. data/lib/datadog/ai_guard/ext.rb +15 -0
  67. data/lib/datadog/ai_guard.rb +34 -8
  68. data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +22 -43
  69. data/lib/datadog/appsec/actions_handler.rb +6 -6
  70. data/lib/datadog/appsec/anonymizer.rb +1 -1
  71. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +2 -2
  72. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +6 -6
  73. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +1 -1
  74. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +2 -2
  75. data/lib/datadog/appsec/api_security/route_extractor.rb +26 -14
  76. data/lib/datadog/appsec/api_security/sampler.rb +5 -5
  77. data/lib/datadog/appsec/api_security.rb +1 -1
  78. data/lib/datadog/appsec/assets/blocked.html +1 -108
  79. data/lib/datadog/appsec/assets.rb +7 -7
  80. data/lib/datadog/appsec/autoload.rb +4 -4
  81. data/lib/datadog/appsec/component.rb +29 -22
  82. data/lib/datadog/appsec/compressed_json.rb +7 -7
  83. data/lib/datadog/appsec/configuration.rb +436 -1
  84. data/lib/datadog/appsec/context.rb +10 -8
  85. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +6 -6
  86. data/lib/datadog/appsec/contrib/active_record/integration.rb +4 -4
  87. data/lib/datadog/appsec/contrib/active_record/patcher.rb +4 -4
  88. data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +75 -0
  89. data/lib/datadog/appsec/contrib/aws_lambda/integration.rb +39 -0
  90. data/lib/datadog/appsec/contrib/aws_lambda/patcher.rb +30 -0
  91. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +146 -0
  92. data/lib/datadog/appsec/contrib/devise/configuration.rb +1 -1
  93. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +3 -3
  94. data/lib/datadog/appsec/contrib/devise/ext.rb +20 -20
  95. data/lib/datadog/appsec/contrib/devise/integration.rb +4 -4
  96. data/lib/datadog/appsec/contrib/devise/patcher.rb +8 -8
  97. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +14 -13
  98. data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +8 -8
  99. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +24 -9
  100. data/lib/datadog/appsec/contrib/excon/integration.rb +4 -4
  101. data/lib/datadog/appsec/contrib/excon/patcher.rb +1 -1
  102. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +76 -21
  103. data/lib/datadog/appsec/contrib/faraday/integration.rb +4 -4
  104. data/lib/datadog/appsec/contrib/faraday/patcher.rb +4 -4
  105. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +72 -18
  106. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +4 -4
  107. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +66 -21
  108. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +7 -7
  109. data/lib/datadog/appsec/contrib/graphql/integration.rb +9 -8
  110. data/lib/datadog/appsec/contrib/graphql/patcher.rb +3 -3
  111. data/lib/datadog/appsec/contrib/rack/buffered_input.rb +83 -0
  112. data/lib/datadog/appsec/contrib/rack/ext.rb +29 -29
  113. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +56 -17
  114. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +1 -1
  115. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +46 -30
  116. data/lib/datadog/appsec/contrib/rack/input_peeker.rb +79 -0
  117. data/lib/datadog/appsec/contrib/rack/integration.rb +6 -6
  118. data/lib/datadog/appsec/contrib/rack/patcher.rb +3 -3
  119. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +11 -8
  120. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +51 -61
  121. data/lib/datadog/appsec/contrib/rack/response_body.rb +1 -1
  122. data/lib/datadog/appsec/contrib/rails/gateway/request.rb +43 -4
  123. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +25 -9
  124. data/lib/datadog/appsec/contrib/rails/integration.rb +5 -5
  125. data/lib/datadog/appsec/contrib/rails/patcher.rb +17 -17
  126. data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.rb +2 -2
  127. data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +3 -3
  128. data/lib/datadog/appsec/contrib/rails/request.rb +3 -3
  129. data/lib/datadog/appsec/contrib/rails/request_middleware.rb +2 -1
  130. data/lib/datadog/appsec/contrib/rest_client/integration.rb +5 -5
  131. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +1 -1
  132. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +74 -21
  133. data/lib/datadog/appsec/contrib/sinatra/gateway/request.rb +1 -1
  134. data/lib/datadog/appsec/contrib/sinatra/gateway/route_params.rb +1 -1
  135. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +34 -13
  136. data/lib/datadog/appsec/contrib/sinatra/integration.rb +5 -5
  137. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +11 -11
  138. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +3 -3
  139. data/lib/datadog/appsec/contrib/sinatra/request_middleware.rb +2 -1
  140. data/lib/datadog/appsec/counter_sampler.rb +1 -1
  141. data/lib/datadog/appsec/default_header_tags.rb +52 -0
  142. data/lib/datadog/appsec/event.rb +14 -14
  143. data/lib/datadog/appsec/ext.rb +11 -10
  144. data/lib/datadog/appsec/extensions.rb +2 -2
  145. data/lib/datadog/appsec/instrumentation/gateway.rb +1 -14
  146. data/lib/datadog/appsec/metrics/collector.rb +18 -1
  147. data/lib/datadog/appsec/metrics/exporter.rb +17 -11
  148. data/lib/datadog/appsec/metrics/telemetry.rb +15 -3
  149. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +4 -4
  150. data/lib/datadog/appsec/metrics.rb +4 -4
  151. data/lib/datadog/appsec/monitor/gateway/watcher.rb +18 -12
  152. data/lib/datadog/appsec/monitor.rb +1 -1
  153. data/lib/datadog/appsec/processor/rule_loader.rb +32 -32
  154. data/lib/datadog/appsec/rate_limiter.rb +1 -1
  155. data/lib/datadog/appsec/remote.rb +15 -9
  156. data/lib/datadog/appsec/response.rb +22 -22
  157. data/lib/datadog/appsec/route_normalizer/rails_route_pattern.rb +176 -0
  158. data/lib/datadog/appsec/route_normalizer/route_pattern.rb +378 -0
  159. data/lib/datadog/appsec/route_normalizer/route_text.rb +57 -0
  160. data/lib/datadog/appsec/route_normalizer.rb +80 -0
  161. data/lib/datadog/appsec/security_engine/engine.rb +40 -40
  162. data/lib/datadog/appsec/security_engine/runner.rb +3 -3
  163. data/lib/datadog/appsec/security_event.rb +2 -2
  164. data/lib/datadog/appsec/trace_keeper.rb +18 -6
  165. data/lib/datadog/appsec/utils/http/body.rb +11 -8
  166. data/lib/datadog/appsec/utils/http/body_reader.rb +61 -0
  167. data/lib/datadog/appsec/utils/http/media_range.rb +15 -15
  168. data/lib/datadog/appsec/utils/http/media_type.rb +5 -6
  169. data/lib/datadog/appsec/utils/http/url_encoded.rb +79 -24
  170. data/lib/datadog/appsec.rb +21 -24
  171. data/lib/datadog/auto_instrument.rb +2 -2
  172. data/lib/datadog/core/buffer/cruby.rb +1 -1
  173. data/lib/datadog/core/buffer/thread_safe.rb +1 -1
  174. data/lib/datadog/core/configuration/agent_settings.rb +1 -1
  175. data/lib/datadog/core/configuration/agent_settings_resolver.rb +15 -15
  176. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +2 -2
  177. data/lib/datadog/core/configuration/base.rb +19 -7
  178. data/lib/datadog/core/configuration/components.rb +142 -36
  179. data/lib/datadog/core/configuration/components_state.rb +6 -1
  180. data/lib/datadog/core/configuration/config_helper.rb +12 -3
  181. data/lib/datadog/core/configuration/deprecations.rb +6 -6
  182. data/lib/datadog/core/configuration/ext.rb +11 -11
  183. data/lib/datadog/core/configuration/option.rb +46 -21
  184. data/lib/datadog/core/configuration/option_definition.rb +43 -17
  185. data/lib/datadog/core/configuration/options.rb +42 -8
  186. data/lib/datadog/core/configuration/settings.rb +129 -104
  187. data/lib/datadog/core/configuration/stable_config.rb +1 -1
  188. data/lib/datadog/core/configuration/supported_configurations.rb +25 -1
  189. data/lib/datadog/core/configuration.rb +9 -9
  190. data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
  191. data/lib/datadog/core/contrib/rails/utils.rb +7 -3
  192. data/lib/datadog/core/crashtracking/component.rb +17 -13
  193. data/lib/datadog/core/crashtracking/tag_builder.rb +5 -5
  194. data/lib/datadog/core/deprecations.rb +1 -1
  195. data/lib/datadog/core/diagnostics/environment_logger.rb +45 -8
  196. data/lib/datadog/core/diagnostics/health.rb +2 -2
  197. data/lib/datadog/core/encoding.rb +4 -4
  198. data/lib/datadog/core/environment/agent_info.rb +2 -2
  199. data/lib/datadog/core/environment/cgroup.rb +4 -4
  200. data/lib/datadog/core/environment/container.rb +11 -11
  201. data/lib/datadog/core/environment/execution.rb +3 -3
  202. data/lib/datadog/core/environment/ext.rb +21 -15
  203. data/lib/datadog/core/environment/gc.rb +1 -1
  204. data/lib/datadog/core/environment/git.rb +2 -2
  205. data/lib/datadog/core/environment/identity.rb +47 -11
  206. data/lib/datadog/core/environment/platform.rb +8 -8
  207. data/lib/datadog/core/environment/process.rb +54 -19
  208. data/lib/datadog/core/environment/socket.rb +15 -2
  209. data/lib/datadog/core/environment/variable_helpers.rb +1 -1
  210. data/lib/datadog/core/environment/vm_cache.rb +1 -1
  211. data/lib/datadog/core/environment/yjit.rb +2 -2
  212. data/lib/datadog/core/error.rb +5 -5
  213. data/lib/datadog/core/evp.rb +11 -0
  214. data/lib/datadog/core/extensions.rb +1 -1
  215. data/lib/datadog/core/feature_flags.rb +3 -3
  216. data/lib/datadog/core/git/ext.rb +4 -4
  217. data/lib/datadog/core/knuth_sampler.rb +3 -1
  218. data/lib/datadog/core/logger.rb +3 -3
  219. data/lib/datadog/core/logging/ext.rb +1 -1
  220. data/lib/datadog/core/metrics/client.rb +26 -26
  221. data/lib/datadog/core/metrics/ext.rb +5 -5
  222. data/lib/datadog/core/metrics/helpers.rb +1 -1
  223. data/lib/datadog/core/metrics/logging.rb +2 -2
  224. data/lib/datadog/core/metrics/options.rb +5 -5
  225. data/lib/datadog/core/pin.rb +1 -1
  226. data/lib/datadog/core/process_discovery.rb +8 -8
  227. data/lib/datadog/core/rate_limiter.rb +1 -1
  228. data/lib/datadog/core/remote/client/capabilities.rb +67 -15
  229. data/lib/datadog/core/remote/client.rb +18 -18
  230. data/lib/datadog/core/remote/component.rb +57 -32
  231. data/lib/datadog/core/remote/configuration/content.rb +3 -3
  232. data/lib/datadog/core/remote/configuration/digest.rb +1 -1
  233. data/lib/datadog/core/remote/configuration/path.rb +9 -9
  234. data/lib/datadog/core/remote/configuration/repository.rb +4 -4
  235. data/lib/datadog/core/remote/configuration/target.rb +9 -9
  236. data/lib/datadog/core/remote/configuration.rb +4 -4
  237. data/lib/datadog/core/remote/ext.rb +3 -3
  238. data/lib/datadog/core/remote/negotiation.rb +1 -1
  239. data/lib/datadog/core/remote/tie/tracing.rb +7 -7
  240. data/lib/datadog/core/remote/transport/config.rb +4 -4
  241. data/lib/datadog/core/remote/transport/http/config.rb +10 -10
  242. data/lib/datadog/core/remote/transport/http/negotiation.rb +4 -4
  243. data/lib/datadog/core/remote/transport/http.rb +10 -10
  244. data/lib/datadog/core/remote/transport/negotiation.rb +3 -3
  245. data/lib/datadog/core/remote/worker.rb +5 -5
  246. data/lib/datadog/core/runtime/ext.rb +22 -22
  247. data/lib/datadog/core/runtime/metrics.rb +10 -10
  248. data/lib/datadog/core/tag_builder.rb +18 -18
  249. data/lib/datadog/core/tag_normalizer.rb +7 -7
  250. data/lib/datadog/core/telemetry/component.rb +16 -13
  251. data/lib/datadog/core/telemetry/configuration_value.rb +38 -0
  252. data/lib/datadog/core/telemetry/emitter.rb +5 -5
  253. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +10 -9
  254. data/lib/datadog/core/telemetry/event/app_closing.rb +2 -2
  255. data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +2 -2
  256. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +3 -3
  257. data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
  258. data/lib/datadog/core/telemetry/event/app_heartbeat.rb +2 -2
  259. data/lib/datadog/core/telemetry/event/app_integrations_change.rb +2 -2
  260. data/lib/datadog/core/telemetry/event/app_started.rb +142 -193
  261. data/lib/datadog/core/telemetry/event/base.rb +1 -1
  262. data/lib/datadog/core/telemetry/event/distributions.rb +2 -2
  263. data/lib/datadog/core/telemetry/event/generate_metrics.rb +3 -3
  264. data/lib/datadog/core/telemetry/event/log.rb +6 -6
  265. data/lib/datadog/core/telemetry/event/message_batch.rb +1 -1
  266. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +3 -3
  267. data/lib/datadog/core/telemetry/event.rb +16 -22
  268. data/lib/datadog/core/telemetry/ext.rb +11 -10
  269. data/lib/datadog/core/telemetry/logger.rb +1 -1
  270. data/lib/datadog/core/telemetry/logging.rb +5 -5
  271. data/lib/datadog/core/telemetry/metric.rb +9 -9
  272. data/lib/datadog/core/telemetry/metrics_collection.rb +2 -2
  273. data/lib/datadog/core/telemetry/metrics_manager.rb +1 -1
  274. data/lib/datadog/core/telemetry/request.rb +6 -8
  275. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +16 -11
  276. data/lib/datadog/core/telemetry/transport/http.rb +7 -7
  277. data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -3
  278. data/lib/datadog/core/telemetry/worker.rb +26 -6
  279. data/lib/datadog/core/transport/ext.rb +14 -14
  280. data/lib/datadog/core/transport/http/adapters/net.rb +4 -4
  281. data/lib/datadog/core/transport/http/adapters/test.rb +1 -1
  282. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +4 -4
  283. data/lib/datadog/core/transport/http/api/endpoint.rb +1 -1
  284. data/lib/datadog/core/transport/http/api/map.rb +1 -1
  285. data/lib/datadog/core/transport/http/builder.rb +6 -6
  286. data/lib/datadog/core/transport/http/client.rb +2 -2
  287. data/lib/datadog/core/transport/http/response.rb +1 -1
  288. data/lib/datadog/core/transport/http.rb +9 -7
  289. data/lib/datadog/core/transport/response.rb +1 -1
  290. data/lib/datadog/core/transport/transport.rb +2 -2
  291. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +172 -20
  292. data/lib/datadog/core/utils/base64_codec.rb +23 -0
  293. data/lib/datadog/core/utils/duration.rb +1 -1
  294. data/lib/datadog/core/utils/{array.rb → enumerable_compat.rb} +2 -2
  295. data/lib/datadog/core/utils/forking.rb +3 -1
  296. data/lib/datadog/core/utils/hash.rb +0 -23
  297. data/lib/datadog/core/utils/lru_cache.rb +3 -3
  298. data/lib/datadog/core/utils/network.rb +11 -11
  299. data/lib/datadog/core/utils/only_once.rb +1 -1
  300. data/lib/datadog/core/utils/only_once_successful.rb +1 -1
  301. data/lib/datadog/core/utils/safe_dup.rb +2 -1
  302. data/lib/datadog/core/utils/sequence.rb +3 -6
  303. data/lib/datadog/core/utils/spawn_monkey_patch.rb +68 -0
  304. data/lib/datadog/core/utils/time.rb +18 -52
  305. data/lib/datadog/core/utils/url.rb +1 -1
  306. data/lib/datadog/core/utils.rb +5 -5
  307. data/lib/datadog/core/workers/async.rb +2 -2
  308. data/lib/datadog/core/workers/polling.rb +2 -2
  309. data/lib/datadog/core/workers/runtime_metrics.rb +4 -4
  310. data/lib/datadog/core.rb +7 -5
  311. data/lib/datadog/data_streams/configuration.rb +40 -1
  312. data/lib/datadog/data_streams/ext.rb +1 -1
  313. data/lib/datadog/data_streams/extensions.rb +2 -2
  314. data/lib/datadog/data_streams/pathway_context.rb +28 -30
  315. data/lib/datadog/data_streams/processor.rb +93 -62
  316. data/lib/datadog/data_streams/transport/http/stats.rb +5 -5
  317. data/lib/datadog/data_streams/transport/http.rb +5 -5
  318. data/lib/datadog/data_streams/transport/stats.rb +7 -7
  319. data/lib/datadog/data_streams.rb +5 -5
  320. data/lib/datadog/di/base.rb +16 -8
  321. data/lib/datadog/di/boot.rb +25 -24
  322. data/lib/datadog/di/capture_expression.rb +21 -0
  323. data/lib/datadog/di/capture_expression_evaluator.rb +71 -0
  324. data/lib/datadog/di/capture_limits.rb +41 -0
  325. data/lib/datadog/di/code_tracker.rb +218 -16
  326. data/lib/datadog/di/component.rb +123 -37
  327. data/lib/datadog/di/configuration.rb +241 -2
  328. data/lib/datadog/di/context.rb +9 -3
  329. data/lib/datadog/di/contrib/active_record.rb +6 -2
  330. data/lib/datadog/di/contrib/railtie.rb +1 -1
  331. data/lib/datadog/di/contrib.rb +5 -5
  332. data/lib/datadog/di/el/compiler.rb +114 -38
  333. data/lib/datadog/di/el/evaluator.rb +82 -2
  334. data/lib/datadog/di/el/expression.rb +13 -2
  335. data/lib/datadog/di/el.rb +3 -3
  336. data/lib/datadog/di/error.rb +10 -0
  337. data/lib/datadog/di/fatal_exceptions.rb +26 -0
  338. data/lib/datadog/di/instrumenter.rb +528 -184
  339. data/lib/datadog/di/logger.rb +3 -3
  340. data/lib/datadog/di/preload.rb +1 -1
  341. data/lib/datadog/di/probe.rb +69 -2
  342. data/lib/datadog/di/probe_builder.rb +98 -15
  343. data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
  344. data/lib/datadog/di/probe_file_loader.rb +14 -9
  345. data/lib/datadog/di/probe_manager.rb +73 -15
  346. data/lib/datadog/di/probe_notification_builder.rb +178 -25
  347. data/lib/datadog/di/probe_notifier_worker.rb +57 -37
  348. data/lib/datadog/di/probe_repository.rb +1 -1
  349. data/lib/datadog/di/redactor.rb +16 -1
  350. data/lib/datadog/di/remote.rb +187 -26
  351. data/lib/datadog/di/serializer.rb +79 -35
  352. data/lib/datadog/di/transport/diagnostics.rb +5 -5
  353. data/lib/datadog/di/transport/http/diagnostics.rb +3 -3
  354. data/lib/datadog/di/transport/http/input.rb +2 -2
  355. data/lib/datadog/di/transport/http.rb +10 -10
  356. data/lib/datadog/di/transport/input.rb +18 -14
  357. data/lib/datadog/di/utils.rb +43 -15
  358. data/lib/datadog/di.rb +147 -3
  359. data/lib/datadog/error_tracking/collector.rb +3 -2
  360. data/lib/datadog/error_tracking/component.rb +12 -12
  361. data/lib/datadog/error_tracking/configuration.rb +55 -2
  362. data/lib/datadog/error_tracking/ext.rb +7 -7
  363. data/lib/datadog/error_tracking/extensions.rb +2 -2
  364. data/lib/datadog/error_tracking/filters.rb +4 -4
  365. data/lib/datadog/error_tracking.rb +6 -6
  366. data/lib/datadog/kit/appsec/events/v2.rb +91 -33
  367. data/lib/datadog/kit/appsec/events.rb +25 -25
  368. data/lib/datadog/kit/enable_core_dumps.rb +3 -3
  369. data/lib/datadog/kit/identity.rb +18 -18
  370. data/lib/datadog/kit/tracing/method_tracer.rb +9 -6
  371. data/lib/datadog/kit.rb +1 -1
  372. data/lib/datadog/open_feature/component.rb +62 -13
  373. data/lib/datadog/open_feature/configuration.rb +22 -1
  374. data/lib/datadog/open_feature/evaluation_engine.rb +38 -12
  375. data/lib/datadog/open_feature/exposures/batch_builder.rb +1 -1
  376. data/lib/datadog/open_feature/exposures/buffer.rb +1 -1
  377. data/lib/datadog/open_feature/exposures/deduplicator.rb +1 -1
  378. data/lib/datadog/open_feature/exposures/event.rb +17 -10
  379. data/lib/datadog/open_feature/exposures/reporter.rb +3 -3
  380. data/lib/datadog/open_feature/exposures/worker.rb +6 -6
  381. data/lib/datadog/open_feature/ext.rb +32 -6
  382. data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +442 -0
  383. data/lib/datadog/open_feature/flag_evaluation/writer.rb +544 -0
  384. data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +105 -0
  385. data/lib/datadog/open_feature/hooks/flag_eval_metrics_hook.rb +51 -0
  386. data/lib/datadog/open_feature/hooks/span_enrichment_hook/codec.rb +58 -0
  387. data/lib/datadog/open_feature/hooks/span_enrichment_hook/span_enrichment_state.rb +96 -0
  388. data/lib/datadog/open_feature/hooks/span_enrichment_hook/span_enrichment_state_store.rb +49 -0
  389. data/lib/datadog/open_feature/hooks/span_enrichment_hook.rb +184 -0
  390. data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
  391. data/lib/datadog/open_feature/native_evaluator.rb +56 -7
  392. data/lib/datadog/open_feature/noop_evaluator.rb +8 -3
  393. data/lib/datadog/open_feature/provider.rb +115 -21
  394. data/lib/datadog/open_feature/remote.rb +3 -3
  395. data/lib/datadog/open_feature/resolution_details.rb +4 -2
  396. data/lib/datadog/open_feature/transport.rb +71 -12
  397. data/lib/datadog/open_feature.rb +2 -2
  398. data/lib/datadog/opentelemetry/api/baggage.rb +3 -3
  399. data/lib/datadog/opentelemetry/api/context.rb +4 -4
  400. data/lib/datadog/opentelemetry/configuration/settings.rb +95 -28
  401. data/lib/datadog/opentelemetry/ext.rb +9 -0
  402. data/lib/datadog/opentelemetry/logs.rb +98 -0
  403. data/lib/datadog/opentelemetry/metrics.rb +17 -44
  404. data/lib/datadog/opentelemetry/sdk/configurator.rb +46 -6
  405. data/lib/datadog/opentelemetry/sdk/id_generator.rb +16 -10
  406. data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +30 -0
  407. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +9 -16
  408. data/lib/datadog/opentelemetry/sdk/propagator.rb +17 -11
  409. data/lib/datadog/opentelemetry/sdk/span_processor.rb +18 -15
  410. data/lib/datadog/opentelemetry/sdk/trace/span.rb +32 -32
  411. data/lib/datadog/opentelemetry/sdk.rb +19 -0
  412. data/lib/datadog/opentelemetry/signal_configuration.rb +53 -0
  413. data/lib/datadog/opentelemetry/trace.rb +4 -1
  414. data/lib/datadog/opentelemetry.rb +8 -7
  415. data/lib/datadog/profiling/collectors/code_provenance.rb +36 -11
  416. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +40 -3
  417. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +12 -4
  418. data/lib/datadog/profiling/collectors/info.rb +19 -10
  419. data/lib/datadog/profiling/collectors/thread_context.rb +16 -22
  420. data/lib/datadog/profiling/component.rb +41 -34
  421. data/lib/datadog/profiling/exporter.rb +38 -13
  422. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +6 -5
  423. data/lib/datadog/profiling/ext.rb +0 -2
  424. data/lib/datadog/profiling/flush.rb +21 -12
  425. data/lib/datadog/profiling/http_transport.rb +13 -2
  426. data/lib/datadog/profiling/load_native_extension.rb +2 -2
  427. data/lib/datadog/profiling/profiler.rb +13 -5
  428. data/lib/datadog/profiling/scheduler.rb +2 -2
  429. data/lib/datadog/profiling/sequence_tracker.rb +1 -1
  430. data/lib/datadog/profiling/stack_recorder.rb +0 -8
  431. data/lib/datadog/profiling/tag_builder.rb +19 -19
  432. data/lib/datadog/profiling/tasks/exec.rb +9 -4
  433. data/lib/datadog/profiling/tasks/help.rb +1 -0
  434. data/lib/datadog/profiling/tasks/setup.rb +2 -2
  435. data/lib/datadog/profiling.rb +32 -29
  436. data/lib/datadog/ruby_version.rb +25 -0
  437. data/lib/datadog/single_step_instrument.rb +2 -2
  438. data/lib/datadog/symbol_database/component.rb +734 -0
  439. data/lib/datadog/symbol_database/configuration.rb +73 -0
  440. data/lib/datadog/symbol_database/extensions.rb +19 -0
  441. data/lib/datadog/symbol_database/extractor.rb +1110 -0
  442. data/lib/datadog/symbol_database/file_hash.rb +48 -0
  443. data/lib/datadog/symbol_database/logger.rb +43 -0
  444. data/lib/datadog/symbol_database/remote.rb +195 -0
  445. data/lib/datadog/symbol_database/scope.rb +102 -0
  446. data/lib/datadog/symbol_database/scope_batcher.rb +292 -0
  447. data/lib/datadog/symbol_database/service_version.rb +66 -0
  448. data/lib/datadog/symbol_database/symbol.rb +69 -0
  449. data/lib/datadog/symbol_database/transport/http/endpoint.rb +28 -0
  450. data/lib/datadog/symbol_database/transport/http.rb +45 -0
  451. data/lib/datadog/symbol_database/transport.rb +54 -0
  452. data/lib/datadog/symbol_database/uploader.rb +230 -0
  453. data/lib/datadog/symbol_database.rb +71 -0
  454. data/lib/datadog/tracing/analytics.rb +1 -1
  455. data/lib/datadog/tracing/buffer.rb +8 -8
  456. data/lib/datadog/tracing/client_ip.rb +4 -4
  457. data/lib/datadog/tracing/component.rb +39 -7
  458. data/lib/datadog/tracing/configuration/dynamic/option.rb +4 -0
  459. data/lib/datadog/tracing/configuration/dynamic.rb +13 -11
  460. data/lib/datadog/tracing/configuration/ext.rb +52 -38
  461. data/lib/datadog/tracing/configuration/http.rb +2 -2
  462. data/lib/datadog/tracing/configuration/settings.rb +51 -17
  463. data/lib/datadog/tracing/context.rb +1 -1
  464. data/lib/datadog/tracing/context_provider.rb +2 -2
  465. data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +2 -2
  466. data/lib/datadog/tracing/contrib/action_cable/event.rb +4 -4
  467. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +9 -6
  468. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +8 -5
  469. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +9 -6
  470. data/lib/datadog/tracing/contrib/action_cable/events.rb +5 -5
  471. data/lib/datadog/tracing/contrib/action_cable/ext.rb +18 -18
  472. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -5
  473. data/lib/datadog/tracing/contrib/action_cable/integration.rb +7 -7
  474. data/lib/datadog/tracing/contrib/action_cable/patcher.rb +4 -4
  475. data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +2 -2
  476. data/lib/datadog/tracing/contrib/action_mailer/event.rb +7 -4
  477. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +8 -8
  478. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +4 -4
  479. data/lib/datadog/tracing/contrib/action_mailer/events.rb +3 -3
  480. data/lib/datadog/tracing/contrib/action_mailer/ext.rb +18 -18
  481. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +7 -7
  482. data/lib/datadog/tracing/contrib/action_mailer/patcher.rb +3 -3
  483. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +15 -12
  484. data/lib/datadog/tracing/contrib/action_pack/action_controller/patcher.rb +2 -2
  485. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +31 -3
  486. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +5 -3
  487. data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +3 -3
  488. data/lib/datadog/tracing/contrib/action_pack/ext.rb +10 -10
  489. data/lib/datadog/tracing/contrib/action_pack/integration.rb +7 -7
  490. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +3 -3
  491. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -1
  492. data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +3 -3
  493. data/lib/datadog/tracing/contrib/action_view/event.rb +1 -1
  494. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +10 -7
  495. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +11 -8
  496. data/lib/datadog/tracing/contrib/action_view/events.rb +3 -3
  497. data/lib/datadog/tracing/contrib/action_view/ext.rb +10 -10
  498. data/lib/datadog/tracing/contrib/action_view/integration.rb +11 -11
  499. data/lib/datadog/tracing/contrib/action_view/patcher.rb +5 -5
  500. data/lib/datadog/tracing/contrib/action_view/utils.rb +2 -2
  501. data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +2 -2
  502. data/lib/datadog/tracing/contrib/active_job/data_streams.rb +62 -0
  503. data/lib/datadog/tracing/contrib/active_job/event.rb +3 -3
  504. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +10 -7
  505. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +10 -7
  506. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +10 -7
  507. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +10 -7
  508. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +10 -7
  509. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +10 -7
  510. data/lib/datadog/tracing/contrib/active_job/events.rb +6 -6
  511. data/lib/datadog/tracing/contrib/active_job/ext.rb +23 -23
  512. data/lib/datadog/tracing/contrib/active_job/integration.rb +7 -7
  513. data/lib/datadog/tracing/contrib/active_job/patcher.rb +17 -5
  514. data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +2 -2
  515. data/lib/datadog/tracing/contrib/active_model_serializers/event.rb +4 -4
  516. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +7 -7
  517. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +8 -9
  518. data/lib/datadog/tracing/contrib/active_model_serializers/events.rb +3 -3
  519. data/lib/datadog/tracing/contrib/active_model_serializers/ext.rb +10 -10
  520. data/lib/datadog/tracing/contrib/active_model_serializers/integration.rb +5 -5
  521. data/lib/datadog/tracing/contrib/active_model_serializers/patcher.rb +3 -3
  522. data/lib/datadog/tracing/contrib/active_record/configuration/makara_resolver.rb +1 -1
  523. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +9 -9
  524. data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +4 -4
  525. data/lib/datadog/tracing/contrib/active_record/event.rb +1 -1
  526. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +9 -9
  527. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +12 -14
  528. data/lib/datadog/tracing/contrib/active_record/events.rb +3 -3
  529. data/lib/datadog/tracing/contrib/active_record/ext.rb +15 -15
  530. data/lib/datadog/tracing/contrib/active_record/integration.rb +11 -11
  531. data/lib/datadog/tracing/contrib/active_record/patcher.rb +2 -2
  532. data/lib/datadog/tracing/contrib/active_record/utils.rb +4 -4
  533. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +1 -1
  534. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +14 -17
  535. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +1 -1
  536. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +5 -8
  537. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +5 -5
  538. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -4
  539. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +3 -3
  540. data/lib/datadog/tracing/contrib/active_support/ext.rb +16 -16
  541. data/lib/datadog/tracing/contrib/active_support/integration.rb +8 -8
  542. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +1 -1
  543. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +2 -2
  544. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +3 -3
  545. data/lib/datadog/tracing/contrib/active_support/patcher.rb +2 -2
  546. data/lib/datadog/tracing/contrib/analytics.rb +1 -1
  547. data/lib/datadog/tracing/contrib/auto_instrument.rb +4 -4
  548. data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +3 -3
  549. data/lib/datadog/tracing/contrib/aws/ext.rb +26 -26
  550. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +7 -11
  551. data/lib/datadog/tracing/contrib/aws/integration.rb +9 -9
  552. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +1 -1
  553. data/lib/datadog/tracing/contrib/aws/patcher.rb +5 -5
  554. data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +2 -2
  555. data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +2 -2
  556. data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +4 -4
  557. data/lib/datadog/tracing/contrib/aws/service/s3.rb +2 -2
  558. data/lib/datadog/tracing/contrib/aws/service/sns.rb +3 -3
  559. data/lib/datadog/tracing/contrib/aws/service/sqs.rb +3 -3
  560. data/lib/datadog/tracing/contrib/aws/service/states.rb +5 -5
  561. data/lib/datadog/tracing/contrib/aws/services.rb +14 -14
  562. data/lib/datadog/tracing/contrib/component.rb +1 -1
  563. data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +1 -1
  564. data/lib/datadog/tracing/contrib/concurrent_ruby/configuration/settings.rb +2 -2
  565. data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +1 -1
  566. data/lib/datadog/tracing/contrib/concurrent_ruby/ext.rb +2 -2
  567. data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +1 -1
  568. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +6 -6
  569. data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +4 -4
  570. data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +1 -1
  571. data/lib/datadog/tracing/contrib/configurable.rb +20 -5
  572. data/lib/datadog/tracing/contrib/configuration/resolver.rb +14 -4
  573. data/lib/datadog/tracing/contrib/configuration/resolvers/pattern_resolver.rb +1 -1
  574. data/lib/datadog/tracing/contrib/configuration/settings.rb +2 -2
  575. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +2 -2
  576. data/lib/datadog/tracing/contrib/dalli/ext.rb +13 -13
  577. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +6 -10
  578. data/lib/datadog/tracing/contrib/dalli/integration.rb +7 -7
  579. data/lib/datadog/tracing/contrib/dalli/patcher.rb +3 -3
  580. data/lib/datadog/tracing/contrib/dalli/quantize.rb +3 -3
  581. data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +2 -2
  582. data/lib/datadog/tracing/contrib/delayed_job/ext.rb +14 -14
  583. data/lib/datadog/tracing/contrib/delayed_job/integration.rb +5 -5
  584. data/lib/datadog/tracing/contrib/delayed_job/patcher.rb +3 -3
  585. data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +7 -5
  586. data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +1 -0
  587. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +2 -2
  588. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +15 -15
  589. data/lib/datadog/tracing/contrib/elasticsearch/integration.rb +6 -6
  590. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +12 -16
  591. data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +4 -4
  592. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +2 -2
  593. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +9 -13
  594. data/lib/datadog/tracing/contrib/ethon/ext.rb +13 -13
  595. data/lib/datadog/tracing/contrib/ethon/integration.rb +6 -6
  596. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +4 -11
  597. data/lib/datadog/tracing/contrib/ethon/patcher.rb +3 -3
  598. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +5 -5
  599. data/lib/datadog/tracing/contrib/excon/ext.rb +11 -11
  600. data/lib/datadog/tracing/contrib/excon/integration.rb +6 -6
  601. data/lib/datadog/tracing/contrib/excon/middleware.rb +10 -14
  602. data/lib/datadog/tracing/contrib/excon/patcher.rb +2 -2
  603. data/lib/datadog/tracing/contrib/ext.rb +20 -18
  604. data/lib/datadog/tracing/contrib/extensions.rb +11 -2
  605. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +4 -4
  606. data/lib/datadog/tracing/contrib/faraday/ext.rb +11 -11
  607. data/lib/datadog/tracing/contrib/faraday/integration.rb +6 -6
  608. data/lib/datadog/tracing/contrib/faraday/middleware.rb +10 -14
  609. data/lib/datadog/tracing/contrib/faraday/patcher.rb +6 -6
  610. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +4 -4
  611. data/lib/datadog/tracing/contrib/grape/endpoint.rb +28 -25
  612. data/lib/datadog/tracing/contrib/grape/ext.rb +15 -15
  613. data/lib/datadog/tracing/contrib/grape/instrumentation.rb +3 -3
  614. data/lib/datadog/tracing/contrib/grape/integration.rb +5 -5
  615. data/lib/datadog/tracing/contrib/grape/patcher.rb +5 -5
  616. data/lib/datadog/tracing/contrib/graphql/configuration/error_extension_env_parser.rb +1 -1
  617. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +3 -3
  618. data/lib/datadog/tracing/contrib/graphql/ext.rb +9 -9
  619. data/lib/datadog/tracing/contrib/graphql/integration.rb +9 -9
  620. data/lib/datadog/tracing/contrib/graphql/patcher.rb +7 -7
  621. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +38 -37
  622. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +2 -2
  623. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +2 -2
  624. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +11 -15
  625. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +11 -15
  626. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +6 -6
  627. data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +1 -1
  628. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +11 -9
  629. data/lib/datadog/tracing/contrib/grpc/ext.rb +14 -14
  630. data/lib/datadog/tracing/contrib/grpc/formatting.rb +7 -7
  631. data/lib/datadog/tracing/contrib/grpc/integration.rb +5 -5
  632. data/lib/datadog/tracing/contrib/grpc/intercept_with_datadog.rb +1 -1
  633. data/lib/datadog/tracing/contrib/grpc/patcher.rb +3 -3
  634. data/lib/datadog/tracing/contrib/grpc.rb +7 -6
  635. data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +4 -3
  636. data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +2 -2
  637. data/lib/datadog/tracing/contrib/hanami/ext.rb +8 -8
  638. data/lib/datadog/tracing/contrib/hanami/integration.rb +6 -6
  639. data/lib/datadog/tracing/contrib/hanami/patcher.rb +5 -5
  640. data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +4 -3
  641. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +6 -4
  642. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +1 -1
  643. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +4 -4
  644. data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +3 -3
  645. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +10 -8
  646. data/lib/datadog/tracing/contrib/http/ext.rb +11 -11
  647. data/lib/datadog/tracing/contrib/http/instrumentation.rb +10 -14
  648. data/lib/datadog/tracing/contrib/http/integration.rb +7 -7
  649. data/lib/datadog/tracing/contrib/http/patcher.rb +3 -3
  650. data/lib/datadog/tracing/contrib/http.rb +7 -6
  651. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +4 -4
  652. data/lib/datadog/tracing/contrib/httpclient/ext.rb +11 -11
  653. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +12 -12
  654. data/lib/datadog/tracing/contrib/httpclient/integration.rb +6 -6
  655. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +3 -3
  656. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +4 -4
  657. data/lib/datadog/tracing/contrib/httprb/ext.rb +11 -11
  658. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +10 -14
  659. data/lib/datadog/tracing/contrib/httprb/integration.rb +7 -7
  660. data/lib/datadog/tracing/contrib/httprb/patcher.rb +3 -3
  661. data/lib/datadog/tracing/contrib/integration.rb +3 -3
  662. data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +2 -2
  663. data/lib/datadog/tracing/contrib/kafka/event.rb +4 -3
  664. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +7 -3
  665. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +4 -4
  666. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +4 -4
  667. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +5 -5
  668. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/join_group.rb +5 -5
  669. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/leave_group.rb +5 -5
  670. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/sync_group.rb +5 -5
  671. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +10 -5
  672. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +10 -5
  673. data/lib/datadog/tracing/contrib/kafka/events.rb +10 -10
  674. data/lib/datadog/tracing/contrib/kafka/ext.rb +40 -40
  675. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +4 -4
  676. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +6 -6
  677. data/lib/datadog/tracing/contrib/kafka/integration.rb +6 -6
  678. data/lib/datadog/tracing/contrib/kafka/patcher.rb +5 -5
  679. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +2 -2
  680. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +11 -9
  681. data/lib/datadog/tracing/contrib/karafka/ext.rb +13 -13
  682. data/lib/datadog/tracing/contrib/karafka/integration.rb +5 -5
  683. data/lib/datadog/tracing/contrib/karafka/monitor.rb +14 -14
  684. data/lib/datadog/tracing/contrib/karafka/patcher.rb +9 -9
  685. data/lib/datadog/tracing/contrib/karafka.rb +7 -6
  686. data/lib/datadog/tracing/contrib/lograge/configuration/settings.rb +2 -2
  687. data/lib/datadog/tracing/contrib/lograge/ext.rb +1 -1
  688. data/lib/datadog/tracing/contrib/lograge/integration.rb +5 -5
  689. data/lib/datadog/tracing/contrib/lograge/patcher.rb +9 -9
  690. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +2 -2
  691. data/lib/datadog/tracing/contrib/mongodb/ext.rb +18 -18
  692. data/lib/datadog/tracing/contrib/mongodb/instrumentation.rb +3 -3
  693. data/lib/datadog/tracing/contrib/mongodb/integration.rb +7 -7
  694. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +9 -9
  695. data/lib/datadog/tracing/contrib/mongodb/patcher.rb +3 -3
  696. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +18 -21
  697. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +3 -3
  698. data/lib/datadog/tracing/contrib/mysql2/ext.rb +11 -11
  699. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +7 -11
  700. data/lib/datadog/tracing/contrib/mysql2/integration.rb +5 -5
  701. data/lib/datadog/tracing/contrib/mysql2/patcher.rb +2 -2
  702. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +2 -2
  703. data/lib/datadog/tracing/contrib/opensearch/ext.rb +23 -23
  704. data/lib/datadog/tracing/contrib/opensearch/integration.rb +6 -6
  705. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +15 -19
  706. data/lib/datadog/tracing/contrib/opensearch/quantize.rb +5 -5
  707. data/lib/datadog/tracing/contrib/patchable.rb +1 -1
  708. data/lib/datadog/tracing/contrib/patcher.rb +3 -3
  709. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +3 -3
  710. data/lib/datadog/tracing/contrib/pg/ext.rb +19 -19
  711. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +19 -23
  712. data/lib/datadog/tracing/contrib/pg/integration.rb +5 -5
  713. data/lib/datadog/tracing/contrib/pg/patcher.rb +2 -2
  714. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +2 -2
  715. data/lib/datadog/tracing/contrib/presto/ext.rb +21 -21
  716. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +8 -10
  717. data/lib/datadog/tracing/contrib/presto/integration.rb +6 -6
  718. data/lib/datadog/tracing/contrib/presto/patcher.rb +4 -4
  719. data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +2 -2
  720. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +19 -16
  721. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +3 -3
  722. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +5 -5
  723. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +2 -2
  724. data/lib/datadog/tracing/contrib/que/ext.rb +19 -19
  725. data/lib/datadog/tracing/contrib/que/integration.rb +6 -6
  726. data/lib/datadog/tracing/contrib/que/patcher.rb +1 -1
  727. data/lib/datadog/tracing/contrib/que/tracer.rb +3 -2
  728. data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +3 -3
  729. data/lib/datadog/tracing/contrib/racecar/event.rb +5 -9
  730. data/lib/datadog/tracing/contrib/racecar/events/batch.rb +3 -3
  731. data/lib/datadog/tracing/contrib/racecar/events/consume.rb +3 -3
  732. data/lib/datadog/tracing/contrib/racecar/events/message.rb +3 -3
  733. data/lib/datadog/tracing/contrib/racecar/events.rb +4 -4
  734. data/lib/datadog/tracing/contrib/racecar/ext.rb +18 -18
  735. data/lib/datadog/tracing/contrib/racecar/integration.rb +5 -5
  736. data/lib/datadog/tracing/contrib/racecar/patcher.rb +3 -3
  737. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +9 -3
  738. data/lib/datadog/tracing/contrib/rack/ext.rb +43 -16
  739. data/lib/datadog/tracing/contrib/rack/header_collection.rb +3 -3
  740. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +24 -1
  741. data/lib/datadog/tracing/contrib/rack/integration.rb +6 -6
  742. data/lib/datadog/tracing/contrib/rack/middlewares.rb +28 -25
  743. data/lib/datadog/tracing/contrib/rack/patcher.rb +5 -5
  744. data/lib/datadog/tracing/contrib/rack/request_queue.rb +4 -4
  745. data/lib/datadog/tracing/contrib/rack/route_inference.rb +12 -10
  746. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +124 -6
  747. data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +1 -1
  748. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +3 -3
  749. data/lib/datadog/tracing/contrib/rails/ext.rb +15 -15
  750. data/lib/datadog/tracing/contrib/rails/framework.rb +11 -11
  751. data/lib/datadog/tracing/contrib/rails/integration.rb +5 -5
  752. data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
  753. data/lib/datadog/tracing/contrib/rails/middlewares.rb +1 -1
  754. data/lib/datadog/tracing/contrib/rails/patcher.rb +8 -9
  755. data/lib/datadog/tracing/contrib/rails/railtie.rb +4 -4
  756. data/lib/datadog/tracing/contrib/rails/runner.rb +4 -2
  757. data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +3 -3
  758. data/lib/datadog/tracing/contrib/rake/ext.rb +12 -12
  759. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +9 -7
  760. data/lib/datadog/tracing/contrib/rake/integration.rb +5 -5
  761. data/lib/datadog/tracing/contrib/rake/patcher.rb +3 -3
  762. data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +3 -3
  763. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +2 -2
  764. data/lib/datadog/tracing/contrib/redis/ext.rb +16 -16
  765. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +6 -6
  766. data/lib/datadog/tracing/contrib/redis/integration.rb +7 -7
  767. data/lib/datadog/tracing/contrib/redis/patcher.rb +10 -10
  768. data/lib/datadog/tracing/contrib/redis/quantize.rb +40 -6
  769. data/lib/datadog/tracing/contrib/redis/tags.rb +6 -11
  770. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +16 -8
  771. data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +2 -2
  772. data/lib/datadog/tracing/contrib/resque/ext.rb +7 -7
  773. data/lib/datadog/tracing/contrib/resque/integration.rb +5 -5
  774. data/lib/datadog/tracing/contrib/resque/patcher.rb +3 -3
  775. data/lib/datadog/tracing/contrib/resque/resque_job.rb +6 -5
  776. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +2 -2
  777. data/lib/datadog/tracing/contrib/rest_client/ext.rb +10 -10
  778. data/lib/datadog/tracing/contrib/rest_client/integration.rb +6 -6
  779. data/lib/datadog/tracing/contrib/rest_client/patcher.rb +2 -2
  780. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +6 -10
  781. data/lib/datadog/tracing/contrib/roda/configuration/settings.rb +2 -2
  782. data/lib/datadog/tracing/contrib/roda/ext.rb +6 -5
  783. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +8 -5
  784. data/lib/datadog/tracing/contrib/roda/integration.rb +6 -6
  785. data/lib/datadog/tracing/contrib/roda/patcher.rb +3 -3
  786. data/lib/datadog/tracing/contrib/semantic_logger/configuration/settings.rb +2 -2
  787. data/lib/datadog/tracing/contrib/semantic_logger/ext.rb +1 -1
  788. data/lib/datadog/tracing/contrib/semantic_logger/integration.rb +5 -5
  789. data/lib/datadog/tracing/contrib/semantic_logger/patcher.rb +2 -2
  790. data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +2 -2
  791. data/lib/datadog/tracing/contrib/sequel/database.rb +7 -10
  792. data/lib/datadog/tracing/contrib/sequel/dataset.rb +6 -5
  793. data/lib/datadog/tracing/contrib/sequel/ext.rb +12 -8
  794. data/lib/datadog/tracing/contrib/sequel/integration.rb +5 -5
  795. data/lib/datadog/tracing/contrib/sequel/jdbc_connection_string.rb +140 -0
  796. data/lib/datadog/tracing/contrib/sequel/patcher.rb +3 -3
  797. data/lib/datadog/tracing/contrib/sequel/utils.rb +103 -16
  798. data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +2 -2
  799. data/lib/datadog/tracing/contrib/shoryuken/ext.rb +12 -12
  800. data/lib/datadog/tracing/contrib/shoryuken/integration.rb +6 -6
  801. data/lib/datadog/tracing/contrib/shoryuken/patcher.rb +1 -1
  802. data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +3 -2
  803. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +9 -8
  804. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +2 -2
  805. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +12 -10
  806. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +30 -30
  807. data/lib/datadog/tracing/contrib/sidekiq/integration.rb +7 -7
  808. data/lib/datadog/tracing/contrib/sidekiq/patcher.rb +8 -8
  809. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/heartbeat.rb +2 -0
  810. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/job_fetch.rb +1 -0
  811. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/redis_info.rb +1 -0
  812. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/scheduled_poller.rb +2 -0
  813. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/stop.rb +1 -0
  814. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +15 -14
  815. data/lib/datadog/tracing/contrib/sidekiq/utils.rb +10 -10
  816. data/lib/datadog/tracing/contrib/sidekiq.rb +7 -6
  817. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +3 -3
  818. data/lib/datadog/tracing/contrib/sinatra/env.rb +6 -6
  819. data/lib/datadog/tracing/contrib/sinatra/ext.rb +17 -17
  820. data/lib/datadog/tracing/contrib/sinatra/framework.rb +2 -2
  821. data/lib/datadog/tracing/contrib/sinatra/integration.rb +5 -5
  822. data/lib/datadog/tracing/contrib/sinatra/patcher.rb +5 -5
  823. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +11 -10
  824. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +9 -8
  825. data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +1 -1
  826. data/lib/datadog/tracing/contrib/sneakers/ext.rb +12 -12
  827. data/lib/datadog/tracing/contrib/sneakers/integration.rb +7 -7
  828. data/lib/datadog/tracing/contrib/sneakers/patcher.rb +2 -2
  829. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +5 -4
  830. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +2 -2
  831. data/lib/datadog/tracing/contrib/status_range_env_parser.rb +2 -2
  832. data/lib/datadog/tracing/contrib/status_range_matcher.rb +4 -0
  833. data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +2 -2
  834. data/lib/datadog/tracing/contrib/stripe/ext.rb +12 -12
  835. data/lib/datadog/tracing/contrib/stripe/integration.rb +5 -5
  836. data/lib/datadog/tracing/contrib/stripe/patcher.rb +2 -2
  837. data/lib/datadog/tracing/contrib/stripe/request.rb +4 -4
  838. data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +2 -2
  839. data/lib/datadog/tracing/contrib/sucker_punch/exception_handler.rb +1 -1
  840. data/lib/datadog/tracing/contrib/sucker_punch/ext.rb +13 -13
  841. data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +5 -4
  842. data/lib/datadog/tracing/contrib/sucker_punch/integration.rb +5 -5
  843. data/lib/datadog/tracing/contrib/sucker_punch/patcher.rb +4 -4
  844. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +3 -3
  845. data/lib/datadog/tracing/contrib/trilogy/ext.rb +11 -11
  846. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +7 -11
  847. data/lib/datadog/tracing/contrib/trilogy/integration.rb +5 -5
  848. data/lib/datadog/tracing/contrib/trilogy/patcher.rb +2 -2
  849. data/lib/datadog/tracing/contrib/utils/database.rb +5 -5
  850. data/lib/datadog/tracing/contrib/utils/quantization/{hash.rb → hash_formatter.rb} +3 -3
  851. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +10 -10
  852. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +2 -2
  853. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +11 -9
  854. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +4 -4
  855. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +5 -5
  856. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +2 -2
  857. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +7 -7
  858. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +3 -3
  859. data/lib/datadog/tracing/contrib/waterdrop.rb +7 -6
  860. data/lib/datadog/tracing/contrib.rb +62 -54
  861. data/lib/datadog/tracing/correlation.rb +12 -12
  862. data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -12
  863. data/lib/datadog/tracing/diagnostics/ext.rb +19 -19
  864. data/lib/datadog/tracing/diagnostics/health.rb +1 -1
  865. data/lib/datadog/tracing/distributed/b3_multi.rb +8 -8
  866. data/lib/datadog/tracing/distributed/b3_single.rb +4 -4
  867. data/lib/datadog/tracing/distributed/baggage.rb +81 -28
  868. data/lib/datadog/tracing/distributed/datadog.rb +34 -32
  869. data/lib/datadog/tracing/distributed/fetcher.rb +1 -1
  870. data/lib/datadog/tracing/distributed/helpers.rb +3 -3
  871. data/lib/datadog/tracing/distributed/propagation.rb +46 -11
  872. data/lib/datadog/tracing/distributed/propagation_policy.rb +5 -0
  873. data/lib/datadog/tracing/distributed/trace_context.rb +29 -263
  874. data/lib/datadog/tracing/distributed/trace_state/datadog.rb +233 -0
  875. data/lib/datadog/tracing/distributed/trace_state/ext.rb +44 -0
  876. data/lib/datadog/tracing/distributed/trace_state/open_telemetry.rb +156 -0
  877. data/lib/datadog/tracing/distributed/trace_state.rb +121 -0
  878. data/lib/datadog/tracing/event.rb +2 -2
  879. data/lib/datadog/tracing/ext.rb +9 -0
  880. data/lib/datadog/tracing/metadata/analytics.rb +2 -2
  881. data/lib/datadog/tracing/metadata/errors.rb +4 -4
  882. data/lib/datadog/tracing/metadata/ext.rb +76 -66
  883. data/lib/datadog/tracing/metadata/metastruct.rb +1 -1
  884. data/lib/datadog/tracing/metadata/metastruct_tagging.rb +1 -1
  885. data/lib/datadog/tracing/metadata/tagging.rb +5 -5
  886. data/lib/datadog/tracing/metadata.rb +4 -4
  887. data/lib/datadog/tracing/otel_thread_context.rb +34 -0
  888. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  889. data/lib/datadog/tracing/pipeline.rb +3 -3
  890. data/lib/datadog/tracing/remote.rb +227 -21
  891. data/lib/datadog/tracing/sampling/all_sampler.rb +1 -1
  892. data/lib/datadog/tracing/sampling/ext.rb +9 -7
  893. data/lib/datadog/tracing/sampling/matcher.rb +4 -4
  894. data/lib/datadog/tracing/sampling/priority_sampler.rb +17 -4
  895. data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +3 -3
  896. data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +2 -2
  897. data/lib/datadog/tracing/sampling/rate_sampler.rb +2 -2
  898. data/lib/datadog/tracing/sampling/rule.rb +5 -5
  899. data/lib/datadog/tracing/sampling/rule_sampler.rb +68 -37
  900. data/lib/datadog/tracing/sampling/sampler.rb +2 -2
  901. data/lib/datadog/tracing/sampling/span/ext.rb +3 -3
  902. data/lib/datadog/tracing/sampling/span/matcher.rb +2 -2
  903. data/lib/datadog/tracing/sampling/span/rule.rb +1 -1
  904. data/lib/datadog/tracing/sampling/span/rule_parser.rb +10 -10
  905. data/lib/datadog/tracing/span.rb +9 -9
  906. data/lib/datadog/tracing/span_event.rb +5 -5
  907. data/lib/datadog/tracing/span_operation.rb +39 -26
  908. data/lib/datadog/tracing/sync_writer.rb +12 -7
  909. data/lib/datadog/tracing/trace_digest.rb +29 -4
  910. data/lib/datadog/tracing/trace_operation.rb +112 -31
  911. data/lib/datadog/tracing/trace_segment.rb +10 -10
  912. data/lib/datadog/tracing/tracer.rb +68 -27
  913. data/lib/datadog/tracing/transport/http/client.rb +4 -4
  914. data/lib/datadog/tracing/transport/http/statistics.rb +2 -2
  915. data/lib/datadog/tracing/transport/http/traces.rb +8 -8
  916. data/lib/datadog/tracing/transport/http.rb +10 -10
  917. data/lib/datadog/tracing/transport/io/client.rb +3 -3
  918. data/lib/datadog/tracing/transport/io/response.rb +1 -1
  919. data/lib/datadog/tracing/transport/io/traces.rb +5 -5
  920. data/lib/datadog/tracing/transport/io.rb +3 -3
  921. data/lib/datadog/tracing/transport/native/response.rb +71 -0
  922. data/lib/datadog/tracing/transport/native.rb +379 -0
  923. data/lib/datadog/tracing/transport/serializable_trace.rb +19 -19
  924. data/lib/datadog/tracing/transport/span_events_negotiation.rb +41 -0
  925. data/lib/datadog/tracing/transport/statistics.rb +2 -2
  926. data/lib/datadog/tracing/transport/trace_formatter.rb +10 -10
  927. data/lib/datadog/tracing/transport/traces.rb +11 -31
  928. data/lib/datadog/tracing/utils.rb +2 -2
  929. data/lib/datadog/tracing/workers.rb +4 -3
  930. data/lib/datadog/tracing/writer.rb +22 -9
  931. data/lib/datadog/tracing.rb +3 -3
  932. data/lib/datadog/version.rb +4 -4
  933. data/lib/datadog.rb +15 -11
  934. metadata +92 -22
  935. data/ext/datadog_profiling_native_extension/clock_id_noop.c +0 -21
  936. data/lib/datadog/ai_guard/configuration/settings.rb +0 -113
  937. data/lib/datadog/appsec/configuration/settings.rb +0 -423
  938. data/lib/datadog/core/utils/base64.rb +0 -22
  939. data/lib/datadog/data_streams/configuration/settings.rb +0 -49
  940. data/lib/datadog/di/configuration/settings.rb +0 -243
  941. data/lib/datadog/error_tracking/configuration/settings.rb +0 -63
  942. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +0 -82
@@ -17,6 +17,10 @@
17
17
  #include "setup_signal_handler.h"
18
18
  #include "time_helpers.h"
19
19
 
20
+ #ifdef __APPLE__
21
+ #include "macos_sampler_thread.h"
22
+ #endif
23
+
20
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
  //
@@ -64,14 +68,24 @@
64
68
  // 4. The Ruby VM calls our `sample_from_postponed_job` from a thread holding the global VM lock. A sample is recorded by
65
69
  // calling `thread_context_collector_sample`.
66
70
  //
67
- // ### TracePoints and Forking
71
+ //
72
+ // ### Hooks and TracePoints
73
+ //
74
+ // This class uses various hooks:
75
+ // * A RUBY_INTERNAL_EVENT_GC_ENTER & RUBY_INTERNAL_EVENT_GC_EXIT TracePoint
76
+ // * A RUBY_INTERNAL_EVENT_NEWOBJ "event hook"/internal tracepoint
77
+ // * A GVL thread event hook
78
+ //
79
+ // We refer to those collectively as "hooks".
80
+ //
81
+ // ### Hooks and Forking
68
82
  //
69
83
  // When the Ruby VM forks, the CPU/Wall-time profiling stops naturally because it's triggered by a background thread
70
84
  // that doesn't get automatically restarted by the VM on the child process. (The profiler does trigger its restart at
71
85
  // some point -- see `Profiling::Tasks::Setup` for details).
72
86
  //
73
- // But this doesn't apply to any `TracePoint`s this class may use, which will continue to be active. Thus, we need to
74
- // always remember consider this case of -- the worker thread may not be alive but the `TracePoint`s can continue to
87
+ // But this doesn't apply to any hooks this class may use, which will continue to be active. Thus, we need to
88
+ // always remember consider this case of -- the worker thread may not be alive but the hooks can continue to
75
89
  // trigger samples.
76
90
  //
77
91
  // ---
@@ -87,7 +101,7 @@ unsigned int MAX_ALLOC_WEIGHT = 10000;
87
101
  static rb_postponed_job_handle_t sample_from_postponed_job_handle;
88
102
  static rb_postponed_job_handle_t after_gc_from_postponed_job_handle;
89
103
  static rb_postponed_job_handle_t after_gvl_running_from_postponed_job_handle;
90
- static rb_postponed_job_handle_t after_allocation_from_postponed_job_handle;
104
+ static rb_postponed_job_handle_t commit_heap_recordings_from_postponed_job_may_lose_gvl_handle;
91
105
  #endif
92
106
 
93
107
  // Contains state for a single CpuAndWallTimeWorker instance
@@ -103,6 +117,8 @@ typedef struct {
103
117
  bool skip_idle_samples_for_testing;
104
118
  bool sighandler_sampling_enabled;
105
119
  uint32_t cpu_sampling_interval_ms;
120
+ // Minimum duration of a "Waiting for GVL" period to trigger a sample
121
+ uint32_t waiting_for_gvl_threshold_ns;
106
122
  VALUE self_instance;
107
123
  VALUE thread_context_collector_instance;
108
124
  VALUE idle_sampling_helper_instance;
@@ -123,9 +139,20 @@ typedef struct {
123
139
 
124
140
  // Others
125
141
 
126
- // Used to detect/avoid nested sampling, e.g. when on_newobj_event gets triggered by a memory allocation
127
- // that happens during another sample, or when the signal handler gets triggered while we're already in the middle of
128
- // sampling.
142
+ // Used to detect/avoid nested sampling, and intended to behave as a lock to ensure the profiler doesn't recurse on itself,
143
+ // e.g. when on_newobj_event gets triggered by a memory allocation that happens during another sample, or when the
144
+ // signal handler gets triggered while we're already in the middle of sampling.
145
+ //
146
+ // It's not an actual lock because we rely on the GVL for correct synchronization
147
+ // (and thus this flag is only valid when we know we have the GVL).
148
+ //
149
+ // Similar to a lock, it should not be held across long-running operations,
150
+ // **in particular it MUST NEVER be held during operations where we might lose the GVL**
151
+ // because effectively that would stop the profiler from working until control
152
+ // goes back to that special thread, which on a contended Ruby app, can take hundreds of ms (or more).
153
+ //
154
+ // Because what we want is "profiler doesn't recurse on itself" we want this to behave as a non-reentrant lock
155
+ // (FIXME: We should have checks for this)
129
156
  //
130
157
  // @ivoanjo: Right now we always sample inside `safely_call`; if that ever changes, this flag may need to become
131
158
  // volatile/atomic/have some barriers to ensure it's visible during e.g. signal handlers.
@@ -150,8 +177,6 @@ typedef struct {
150
177
  unsigned int signal_handler_enqueued_sample;
151
178
  // How many times we prepared a sample (sampled directly) from the signal handler
152
179
  unsigned int signal_handler_prepared_sample;
153
- // How many times the signal handler was called from the wrong thread
154
- unsigned int signal_handler_wrong_thread;
155
180
  // How many times we actually tried to interrupt a thread for sampling
156
181
  unsigned int interrupt_thread_attempts;
157
182
 
@@ -178,6 +203,11 @@ typedef struct {
178
203
  unsigned int allocations_during_sample;
179
204
 
180
205
  // # GVL profiling stats
206
+ // Note that this tracks two kind of samples from RESUMED:
207
+ // * samples when Waiting for GVL for more than the threshold
208
+ // * samples for the skip-samples-while-without-GVL optimization,
209
+ // which are needed to attribute the time without GVL to the correct Ruby stack
210
+
181
211
  // How many times we triggered the after_gvl_running sampling
182
212
  unsigned int after_gvl_running;
183
213
  // How many times we skipped the after_gvl_running sampling
@@ -186,6 +216,11 @@ typedef struct {
186
216
  uint64_t gvl_sampling_time_ns_min;
187
217
  uint64_t gvl_sampling_time_ns_max;
188
218
  uint64_t gvl_sampling_time_ns_total;
219
+
220
+ struct vm_metrics {
221
+ // Total time spent waiting for the GVL
222
+ uint64_t gvl_waiting_time_ns_total;
223
+ } vm_metrics;
189
224
  } stats;
190
225
  } cpu_and_wall_time_worker_state;
191
226
 
@@ -208,6 +243,7 @@ static VALUE _native_failure_exception_during_operation(DDTRACE_UNUSED VALUE sel
208
243
  static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
209
244
  static VALUE _native_install_testing_signal_handler(DDTRACE_UNUSED VALUE self);
210
245
  static VALUE _native_remove_testing_signal_handler(DDTRACE_UNUSED VALUE self);
246
+ static VALUE _native_install_sigprof_handler_on_altstack(DDTRACE_UNUSED VALUE self);
211
247
  static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self);
212
248
  static VALUE _native_gc_tracepoint(DDTRACE_UNUSED VALUE self, VALUE instance);
213
249
  static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused);
@@ -230,10 +266,10 @@ static void reset_stats_not_thread_safe(cpu_and_wall_time_worker_state *state);
230
266
  static void sleep_for(uint64_t time_ns);
231
267
  static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self);
232
268
  static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *unused2);
233
- static void disable_tracepoints(cpu_and_wall_time_worker_state *state);
269
+ static void disable_hooks(cpu_and_wall_time_worker_state *state);
234
270
  static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self);
235
271
  static VALUE rescued_sample_allocation(VALUE tracepoint_data);
236
- static VALUE rescued_after_allocation(VALUE self_instance);
272
+ static VALUE rescued_commit_heap_recordings_may_lose_gvl(VALUE self_instance);
237
273
  static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error);
238
274
  static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state);
239
275
  static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
@@ -248,11 +284,12 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);
248
284
  static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance);
249
285
  static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception);
250
286
  static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception);
287
+ static VALUE handle_sampling_failure_thread_context_collector_heap_update(VALUE self_instance, VALUE exception);
251
288
  static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception);
252
- static VALUE handle_sampling_failure_rescued_after_allocation(VALUE self_instance, VALUE exception);
289
+ static VALUE handle_sampling_failure_rescued_commit_heap_recordings(VALUE self_instance, VALUE exception);
253
290
  static inline void during_sample_enter(cpu_and_wall_time_worker_state* state);
254
291
  static inline void during_sample_exit(cpu_and_wall_time_worker_state* state);
255
- static void after_allocation_from_postponed_job(DDTRACE_UNUSED void *_unused);
292
+ static void commit_heap_recordings_from_postponed_job_may_lose_gvl(DDTRACE_UNUSED void *_unused);
256
293
 
257
294
  // We're using `on_newobj_event` function with `rb_add_event_hook2`, which requires in its public signature a function
258
295
  // with signature `rb_event_hook_func_t` which doesn't match `on_newobj_event`.
@@ -280,6 +317,15 @@ static VALUE active_sampler_instance = Qnil;
280
317
  static cpu_and_wall_time_worker_state *active_sampler_instance_state = NULL;
281
318
  static VALUE clock_failure_exception_class = Qnil;
282
319
 
320
+ // Stats that live outside of any particular `cpu_and_wall_time_worker_state`, so that they can always be safely
321
+ // touched, even when we're not sure it's safe to touch the state (e.g. signal handler, no GVL, etc).
322
+ typedef struct {
323
+ // How many times we skipped sampling because we were running on the alternate signal stack (e.g. nested inside
324
+ // another signal handler, presumably GC compaction).
325
+ unsigned int signal_handler_skipped_sample_on_altstack;
326
+ } global_stats_t;
327
+ static global_stats_t global_stats;
328
+
283
329
  // See handle_sampling_signal for details on what this does
284
330
  #ifdef NO_POSTPONED_TRIGGER
285
331
  static void *gc_finalize_deferred_workaround;
@@ -300,13 +346,14 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
300
346
  sample_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, sample_from_postponed_job, NULL);
301
347
  after_gc_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gc_from_postponed_job, NULL);
302
348
  after_gvl_running_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gvl_running_from_postponed_job, NULL);
303
- after_allocation_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_allocation_from_postponed_job, NULL);
349
+ commit_heap_recordings_from_postponed_job_may_lose_gvl_handle =
350
+ rb_postponed_job_preregister(unused_flags, commit_heap_recordings_from_postponed_job_may_lose_gvl, NULL);
304
351
 
305
352
  if (
306
353
  sample_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
307
354
  after_gc_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
308
355
  after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
309
- after_allocation_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
356
+ commit_heap_recordings_from_postponed_job_may_lose_gvl_handle == POSTPONED_JOB_HANDLE_INVALID
310
357
  ) {
311
358
  raise_error(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
312
359
  }
@@ -345,6 +392,7 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
345
392
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_resume_signals", _native_resume_signals, 0);
346
393
  rb_define_singleton_method(testing_module, "_native_install_testing_signal_handler", _native_install_testing_signal_handler, 0);
347
394
  rb_define_singleton_method(testing_module, "_native_remove_testing_signal_handler", _native_remove_testing_signal_handler, 0);
395
+ rb_define_singleton_method(testing_module, "_native_install_sigprof_handler_on_altstack", _native_install_sigprof_handler_on_altstack, 0);
348
396
  rb_define_singleton_method(testing_module, "_native_trigger_sample", _native_trigger_sample, 0);
349
397
  rb_define_singleton_method(testing_module, "_native_gc_tracepoint", _native_gc_tracepoint, 1);
350
398
  rb_define_singleton_method(testing_module, "_native_simulate_handle_sampling_signal", _native_simulate_handle_sampling_signal, 0);
@@ -385,6 +433,7 @@ static VALUE _native_new(VALUE klass) {
385
433
  state->skip_idle_samples_for_testing = false;
386
434
  state->sighandler_sampling_enabled = false;
387
435
  state->cpu_sampling_interval_ms = 10;
436
+ state->waiting_for_gvl_threshold_ns = 10 * 1000 * 1000;
388
437
  state->thread_context_collector_instance = Qnil;
389
438
  state->idle_sampling_helper_instance = Qnil;
390
439
  state->owner_thread = Qnil;
@@ -430,6 +479,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
430
479
  VALUE skip_idle_samples_for_testing = rb_hash_fetch(options, ID2SYM(rb_intern("skip_idle_samples_for_testing")));
431
480
  VALUE sighandler_sampling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("sighandler_sampling_enabled")));
432
481
  VALUE cpu_sampling_interval_ms = rb_hash_fetch(options, ID2SYM(rb_intern("cpu_sampling_interval_ms")));
482
+ VALUE waiting_for_gvl_threshold_ns = rb_hash_fetch(options, ID2SYM(rb_intern("waiting_for_gvl_threshold_ns")));
433
483
 
434
484
  ENFORCE_BOOLEAN(gc_profiling_enabled);
435
485
  ENFORCE_BOOLEAN(no_signals_workaround_enabled);
@@ -441,6 +491,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
441
491
  ENFORCE_BOOLEAN(skip_idle_samples_for_testing)
442
492
  ENFORCE_BOOLEAN(sighandler_sampling_enabled)
443
493
  ENFORCE_TYPE(cpu_sampling_interval_ms, T_FIXNUM);
494
+ ENFORCE_TYPE(waiting_for_gvl_threshold_ns, T_FIXNUM);
444
495
 
445
496
  cpu_and_wall_time_worker_state *state;
446
497
  TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
@@ -454,6 +505,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
454
505
  state->skip_idle_samples_for_testing = (skip_idle_samples_for_testing == Qtrue);
455
506
  state->sighandler_sampling_enabled = (sighandler_sampling_enabled == Qtrue);
456
507
  state->cpu_sampling_interval_ms = NUM2INT(cpu_sampling_interval_ms);
508
+ state->waiting_for_gvl_threshold_ns = NUM2UINT(waiting_for_gvl_threshold_ns);
457
509
 
458
510
  double total_overhead_target_percentage = NUM2DBL(dynamic_sampling_rate_overhead_target_percentage);
459
511
  if (!state->allocation_profiling_enabled) {
@@ -492,7 +544,7 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
492
544
 
493
545
  // If we already got a delayed exception registered even before starting, raise before starting
494
546
  if (state->failure_exception != Qnil) {
495
- disable_tracepoints(state);
547
+ disable_hooks(state);
496
548
  rb_exc_raise(state->failure_exception);
497
549
  }
498
550
 
@@ -502,13 +554,13 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
502
554
  raise_error(rb_eRuntimeError, "Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread");
503
555
  } else {
504
556
  // The previously active thread seems to have died without cleaning up after itself.
505
- // In this case, we can still go ahead and start the profiler BUT we make sure to disable any existing tracepoint
557
+ // In this case, we can still go ahead and start the profiler BUT we make sure to disable any existing hooks
506
558
  // first as:
507
- // a) If this is a new instance of the CpuAndWallTimeWorker, we don't want the tracepoint from the old instance
559
+ // a) If this is a new instance of the CpuAndWallTimeWorker, we don't want the hooks from the old instance
508
560
  // being kept around
509
561
  // b) If this is the same instance of the CpuAndWallTimeWorker if we call enable on a tracepoint that is already
510
562
  // enabled, it will start firing more than once, see https://bugs.ruby-lang.org/issues/19114 for details.
511
- disable_tracepoints(old_state);
563
+ disable_hooks(old_state);
512
564
  }
513
565
  }
514
566
 
@@ -516,11 +568,20 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
516
568
  // situation we stop immediately and never even start the sampling trigger loop.
517
569
  if (state->stop_thread == rb_thread_current()) return Qnil;
518
570
 
571
+ thread_context_collector_profiler_internal_thread_started();
572
+
519
573
  // Reset the dynamic sampling rate state, if any (reminder: the monotonic clock reference may change after a fork)
520
574
  dynamic_sampling_rate_reset(&state->cpu_dynamic_sampling_rate);
521
575
  long now = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
522
576
  discrete_dynamic_sampler_reset(&state->allocation_sampler, now);
523
577
 
578
+ // Reset per-thread state, if any. This ensures there's no leftover state from a previous profiler run that would
579
+ // affect or be included in samples taken by this profiler about to run.
580
+ //
581
+ // NOTE: This needs to be called before we enable any hooks or anything that could trigger samples (e.g.
582
+ // reset cannot be concurrent with any sampling activity)
583
+ thread_context_collector_reset_all_per_thread_contexts(state->thread_context_collector_instance);
584
+
524
585
  // This write to a global is thread-safe BECAUSE we're still holding on to the global VM lock at this point
525
586
  active_sampler_instance_state = state;
526
587
  active_sampler_instance = instance;
@@ -530,13 +591,21 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
530
591
 
531
592
  block_sigprof_signal_handler_from_running_in_current_thread(); // We want to interrupt the thread with the global VM lock, never this one
532
593
 
594
+ #ifdef __APPLE__
595
+ // Promote the native thread that will run the sampling loop to a realtime scheduling policy so
596
+ // its periodic wake-ups are not subject to the default ~1-2ms timeshare wake latency. The matching
597
+ // demote happens below in this same function, alongside the SIGPROF unblock dance, so both per-thread
598
+ // policy changes are reverted before Ruby reuses this native thread for an unrelated Ruby thread.
599
+ promote_sampler_thread_to_realtime(MILLIS_AS_NS(state->cpu_sampling_interval_ms));
600
+ #endif
601
+
533
602
  // Release GVL, get to the actual work!
534
603
  int exception_state;
535
604
  rb_protect(release_gvl_and_run_sampling_trigger_loop, instance, &exception_state);
536
605
 
537
606
  // The sample trigger loop finished (either cleanly or with an error); let's clean up
538
607
 
539
- disable_tracepoints(state);
608
+ disable_hooks(state);
540
609
 
541
610
  active_sampler_instance_state = NULL;
542
611
  active_sampler_instance = Qnil;
@@ -551,6 +620,14 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
551
620
  // had SIGPROF delivery blocked. :hide_the_pain_harold:
552
621
  unblock_sigprof_signal_handler_from_running_in_current_thread();
553
622
 
623
+ #ifdef __APPLE__
624
+ // Pairs with promote_sampler_thread_to_realtime above. Same reasoning as the SIGPROF unblock:
625
+ // Ruby may reuse this native thread for an unrelated Ruby thread that would otherwise inherit
626
+ // our scheduler policy. Doing it here also covers the exception path, since grab_gvl_and_sample
627
+ // can raise and unwind out of the sampling loop.
628
+ demote_sampler_thread_from_realtime();
629
+ #endif
630
+
554
631
  // Why replace and not use remove the signal handler? We do this because when a process receives a SIGPROF without
555
632
  // having an explicit signal handler set up, the process will instantly terminate with a confusing
556
633
  // "Profiling timer expired" message left behind. (This message doesn't come from us -- it's the default message for
@@ -588,8 +665,8 @@ static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exc
588
665
  state->failure_exception = optional_exception;
589
666
  state->failure_exception_during_operation = optional_exception_during_operation;
590
667
 
591
- // Disable the tracepoints as soon as possible, so the VM doesn't keep on calling them
592
- disable_tracepoints(state);
668
+ // Disable the hooks as soon as possible, so the VM doesn't keep on calling them
669
+ disable_hooks(state);
593
670
  }
594
671
 
595
672
  static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *optional_exception_during_operation) {
@@ -601,28 +678,70 @@ static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *opt
601
678
  return Qtrue;
602
679
  }
603
680
 
681
+ // Our SIGPROF handler is not installed with SA_ONSTACK (see `install_sigprof_signal_handler`).
682
+ // If we detect our signal handler is running on the alt stack, it means we interrupted another signal handler
683
+ // that IS running on the alt stack -- in practice, Ruby's GC compaction read-barrier handler.
684
+ #ifdef __APPLE__
685
+ // On macOS, `ucontext->uc_stack.ss_sp` reports the (live) stack pointer near the top of the alt stack rather than the
686
+ // alt-stack base like Linux does, and `ss_size` is the full alt-stack size. Thus the linux version of the check
687
+ // didn't work for macOS.
688
+ // As an alternative, we query `sigaltstack()`; we save/restore errno since `sigaltstack()` may clobber it.
689
+ static bool is_running_on_alternate_signal_stack(DDTRACE_UNUSED void *ucontext) {
690
+ int old_errno = errno;
691
+ stack_t current_alt_stack;
692
+ bool on_alt_stack = sigaltstack(NULL, &current_alt_stack) == 0 && (current_alt_stack.ss_flags & SS_ONSTACK) != 0;
693
+ errno = old_errno;
694
+ return on_alt_stack;
695
+ }
696
+ #else
697
+ // NOTE: On Linux we use the alt-stack bounds from the `ucontext` rather than `sigaltstack()`, allowing us to avoid one syscall.
698
+ static bool is_running_on_alternate_signal_stack(void *ucontext) {
699
+ if (ucontext == NULL) return false;
700
+
701
+ stack_t alt_stack = ((ucontext_t *) ucontext)->uc_stack;
702
+ if (alt_stack.ss_size == 0) return false; // No alternate signal stack registered on this thread
703
+
704
+ char stack_local;
705
+ uintptr_t current_sp = (uintptr_t) &stack_local;
706
+ uintptr_t alt_stack_start = (uintptr_t) alt_stack.ss_sp;
707
+
708
+ return current_sp >= alt_stack_start && current_sp < alt_stack_start + alt_stack.ss_size;
709
+ }
710
+ #endif
711
+
604
712
  // NOTE: Remember that this will run in the thread and within the scope of user code, including user C code.
605
713
  // 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
606
714
  // we do here can set `errno`, then we must be careful to restore the old `errno` after the fact.
607
- static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext) {
608
- cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
609
-
610
- // This can potentially happen if the CpuAndWallTimeWorker was stopped while the signal delivery was happening; nothing to do
611
- if (state == NULL) return;
715
+ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, void *ucontext) {
716
+ // If we're running on the alternate signal stack, we've interrupted another signal handler that's running
717
+ // there -- in practice, Ruby's GC compaction read-barrier handler.
718
+ // During GC compaction, Ruby protects pages containing Ruby objects, so many of the checks we do below are unsafe
719
+ // since they can touch those pages, and thus we just bail out here in this situation to avoid crashes.
720
+ if (is_running_on_alternate_signal_stack(ucontext)) {
721
+ global_stats.signal_handler_skipped_sample_on_altstack++;
722
+ return;
723
+ }
612
724
 
725
+ // We must first check that we landed on the correct thread and can proceed.
726
+ // We must never touch the state before we confirm that we have landed on the thread that is holding the GVL on the main
727
+ // ractor as otherwise we may be concurrent with the profiler shutting down and removing its state.
613
728
  if (
614
729
  !ruby_native_thread_p() || // Not a Ruby thread
615
730
  !is_current_thread_holding_the_gvl() || // Not safe to enqueue a sample from this thread
616
731
  !ddtrace_rb_ractor_main_p() // We're not on the main Ractor; we currently don't support profiling non-main Ractors
617
- ) {
618
- state->stats.signal_handler_wrong_thread++;
619
- return;
620
- }
732
+ ) return;
733
+
734
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
735
+
736
+ // This can potentially happen if the CpuAndWallTimeWorker was stopped while the signal delivery was happening; nothing to do
737
+ if (state == NULL) return;
621
738
 
622
739
  // We assume there can be no concurrent nor nested calls to handle_sampling_signal because
623
740
  // 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)
624
741
  // 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
625
742
  // because it will not have the global VM lock
743
+ // c) `simulate_sampling_signal_delivery` calls us directly instead of via a real SIGPROF, and blocks SIGPROF delivery on its
744
+ // thread for the duration of that call, so it can't be nested into by a real SIGPROF either
626
745
 
627
746
  state->stats.signal_handler_enqueued_sample++;
628
747
 
@@ -634,7 +753,7 @@ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED si
634
753
  if (sample_from_signal_handler) {
635
754
  // Buffer current stack trace. Note that this will not actually record the sample, for that we still need to wait
636
755
  // until the postponed job below gets run.
637
- bool prepared = thread_context_collector_prepare_sample_inside_signal_handler(state->thread_context_collector_instance);
756
+ bool prepared = thread_context_collector_prepare_sample_inside_signal_handler();
638
757
 
639
758
  if (prepared) state->stats.signal_handler_prepared_sample++;
640
759
  }
@@ -760,7 +879,7 @@ static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused) {
760
879
  during_sample_enter(state);
761
880
 
762
881
  // Rescue against any exceptions that happen during sampling
763
- safely_call(
882
+ VALUE needs_otel_span_key = safely_call(
764
883
  rescued_sample_from_postponed_job,
765
884
  state->self_instance,
766
885
  state->self_instance,
@@ -768,6 +887,12 @@ static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused) {
768
887
  );
769
888
 
770
889
  during_sample_exit(state);
890
+
891
+ // Extracting the otel span key can lose the GVL, so we move it outside `during_sample`
892
+ // (It can't raise: it rescues its own exceptions)
893
+ if (needs_otel_span_key == Qtrue) {
894
+ thread_context_collector_resolve_otel_span_key_may_lose_gvl(state->thread_context_collector_instance);
895
+ }
771
896
  }
772
897
 
773
898
  static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
@@ -778,13 +903,13 @@ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
778
903
 
779
904
  if (state->dynamic_sampling_rate_enabled && !dynamic_sampling_rate_should_sample(&state->cpu_dynamic_sampling_rate, wall_time_ns_before_sample)) {
780
905
  state->stats.cpu_skipped++;
781
- return Qnil;
906
+ return Qfalse;
782
907
  }
783
908
 
784
909
  state->stats.cpu_sampled++;
785
910
 
786
- VALUE profiler_overhead_stack_thread = state->owner_thread; // Used to attribute profiler overhead to a different stack
787
- thread_context_collector_sample(state->thread_context_collector_instance, wall_time_ns_before_sample, profiler_overhead_stack_thread);
911
+ bool needs_otel_span_key =
912
+ thread_context_collector_sample(state->thread_context_collector_instance, wall_time_ns_before_sample);
788
913
 
789
914
  long wall_time_ns_after_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
790
915
  long delta_ns = wall_time_ns_after_sample - wall_time_ns_before_sample;
@@ -798,8 +923,7 @@ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
798
923
 
799
924
  dynamic_sampling_rate_after_sample(&state->cpu_dynamic_sampling_rate, wall_time_ns_after_sample, sampling_time_ns);
800
925
 
801
- // Return a dummy VALUE because we're called from rb_rescue2 which requires it
802
- return Qnil;
926
+ return needs_otel_span_key ? Qtrue : Qfalse;
803
927
  }
804
928
 
805
929
  // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
@@ -825,7 +949,7 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
825
949
  cpu_and_wall_time_worker_state *state;
826
950
  TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
827
951
 
828
- // Final preparations: Setup signal handler and enable tracepoints. We run these here and not in `_native_sampling_loop`
952
+ // Final preparations: Setup signal handler and enable hooks. We run these here and not in `_native_sampling_loop`
829
953
  // because they may raise exceptions.
830
954
  install_sigprof_signal_handler(handle_sampling_signal, "handle_sampling_signal");
831
955
  if (state->gc_profiling_enabled) rb_tracepoint_enable(state->gc_tracepoint);
@@ -840,15 +964,12 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
840
964
 
841
965
  if (state->gvl_profiling_enabled) {
842
966
  #ifndef NO_GVL_INSTRUMENTATION
843
- #ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS
844
- gvl_profiling_state_thread_tracking_workaround();
845
- #endif
846
-
847
967
  state->gvl_profiling_hook = rb_internal_thread_add_event_hook(
848
968
  on_gvl_event,
849
969
  (
850
970
  // For now we're only asking for these events, even though there's more
851
971
  // (e.g. check docs or gvl-tracing gem)
972
+ RUBY_INTERNAL_THREAD_EVENT_SUSPENDED | /* released gvl */
852
973
  RUBY_INTERNAL_THREAD_EVENT_READY /* waiting for gvl */ |
853
974
  RUBY_INTERNAL_THREAD_EVENT_RESUMED /* running/runnable */
854
975
  ),
@@ -867,6 +988,8 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
867
988
  // If we stopped sampling due to an exception, re-raise it (now in the worker thread)
868
989
  if (state->failure_exception != Qnil) rb_exc_raise(state->failure_exception);
869
990
 
991
+ thread_context_collector_profiler_internal_thread_done(state->thread_context_collector_instance);
992
+
870
993
  return Qnil;
871
994
  }
872
995
 
@@ -905,6 +1028,28 @@ static VALUE _native_remove_testing_signal_handler(DDTRACE_UNUSED VALUE self) {
905
1028
  return Qtrue;
906
1029
  }
907
1030
 
1031
+ // Reproduces a profiler sample being taken in the middle of GC compaction: during compaction a SIGPROF can be
1032
+ // delivered while nested inside Ruby's read-barrier signal handler, which runs on the alternate signal stack. We
1033
+ // simulate that here by re-installing the production `handle_sampling_signal` with the `SA_ONSTACK` flag, so it runs
1034
+ // on the alternate signal stack just like it would in that scenario.
1035
+ //
1036
+ // The `SA_ONSTACK` will be undone by any next `replace_sigprof_signal_handler_with_empty_handler` /
1037
+ // `install_sigprof_signal_handler_internal` / `remove_sigprof_signal_handle` so this doesn't need a specific, symmetric
1038
+ // `uninstall` to undo this.
1039
+ static VALUE _native_install_sigprof_handler_on_altstack(DDTRACE_UNUSED VALUE self) {
1040
+ struct sigaction signal_handler_config = {
1041
+ .sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK,
1042
+ .sa_sigaction = handle_sampling_signal,
1043
+ };
1044
+ sigemptyset(&signal_handler_config.sa_mask);
1045
+
1046
+ if (sigaction(SIGPROF, &signal_handler_config, NULL) != 0) {
1047
+ rb_sys_fail("Failed to install SA_ONSTACK SIGPROF signal handler for testing");
1048
+ }
1049
+
1050
+ return Qtrue;
1051
+ }
1052
+
908
1053
  // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
909
1054
  // It SHOULD NOT be used for other purposes.
910
1055
  static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self) {
@@ -987,6 +1132,15 @@ static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused) {
987
1132
  );
988
1133
 
989
1134
  during_sample_exit(state);
1135
+
1136
+ // This part runs separately from above because it may lose the GVL and we don't want `during_sample` to be set in
1137
+ // such a situation
1138
+ safely_call(
1139
+ thread_context_collector_heap_update_may_lose_gvl,
1140
+ state->thread_context_collector_instance,
1141
+ state->self_instance,
1142
+ handle_sampling_failure_thread_context_collector_heap_update
1143
+ );
990
1144
  }
991
1145
 
992
1146
  // Equivalent to Ruby begin/rescue call, where we call a C function and jump to the exception handler if an
@@ -1024,18 +1178,21 @@ static VALUE _native_simulate_sample_from_postponed_job(DDTRACE_UNUSED VALUE sel
1024
1178
 
1025
1179
  // After the Ruby VM forks, this method gets called in the child process to clean up any leftover state from the parent.
1026
1180
  //
1027
- // Assumption: This method gets called BEFORE restarting profiling. Note that profiling-related tracepoints may still
1181
+ // Assumption: This method gets called BEFORE restarting profiling. Note that profiling-related hooks may still
1028
1182
  // be active, so we make sure to disable them before calling into anything else, so that there are no components
1029
1183
  // attempting to trigger samples at the same time as the reset is done.
1030
1184
  //
1031
- // In the future, if we add more other components with tracepoints, we will need to coordinate stopping all such
1032
- // tracepoints before doing the other cleaning steps.
1185
+ // In the future, if we add more other components with hooks, we will need to coordinate stopping all such
1186
+ // hooks before doing the other cleaning steps.
1187
+ //
1188
+ // Note that tests call this method directly in the same process without forking,
1189
+ // and in such a case non-current Threads keep running.
1033
1190
  static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance) {
1034
1191
  cpu_and_wall_time_worker_state *state;
1035
1192
  TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1036
1193
 
1037
- // Disable all tracepoints, so that there are no more attempts to mutate the profile
1038
- disable_tracepoints(state);
1194
+ // Disable all hooks, so that there are no more attempts to mutate the profile
1195
+ disable_hooks(state);
1039
1196
 
1040
1197
  reset_stats_not_thread_safe(state);
1041
1198
 
@@ -1071,7 +1228,7 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
1071
1228
  ID2SYM(rb_intern("simulated_signal_delivery")), /* => */ UINT2NUM(state->stats.simulated_signal_delivery),
1072
1229
  ID2SYM(rb_intern("signal_handler_enqueued_sample")), /* => */ UINT2NUM(state->stats.signal_handler_enqueued_sample),
1073
1230
  ID2SYM(rb_intern("signal_handler_prepared_sample")), /* => */ UINT2NUM(state->stats.signal_handler_prepared_sample),
1074
- ID2SYM(rb_intern("signal_handler_wrong_thread")), /* => */ UINT2NUM(state->stats.signal_handler_wrong_thread),
1231
+ ID2SYM(rb_intern("signal_handler_skipped_sample_on_altstack")), /* => */ UINT2NUM(global_stats.signal_handler_skipped_sample_on_altstack),
1075
1232
  ID2SYM(rb_intern("interrupt_thread_attempts")), /* => */ UINT2NUM(state->stats.interrupt_thread_attempts),
1076
1233
 
1077
1234
  // CPU Stats
@@ -1101,8 +1258,12 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
1101
1258
  ID2SYM(rb_intern("gvl_sampling_time_ns_max")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_max, > 0, ULL2NUM),
1102
1259
  ID2SYM(rb_intern("gvl_sampling_time_ns_total")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_total, > 0, ULL2NUM),
1103
1260
  ID2SYM(rb_intern("gvl_sampling_time_ns_avg")), /* => */ RUBY_AVG_OR_NIL(state->stats.gvl_sampling_time_ns_total, state->stats.after_gvl_running),
1261
+ ID2SYM(rb_intern("gvl_waiting_time_ns_total")), /* => */ state->gvl_profiling_enabled ? ULL2NUM(state->stats.vm_metrics.gvl_waiting_time_ns_total) : Qnil,
1104
1262
  };
1105
1263
  for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(stats_as_hash, arguments[i], arguments[i+1]);
1264
+
1265
+ thread_context_collector_stats(state->thread_context_collector_instance, stats_as_hash);
1266
+
1106
1267
  return stats_as_hash;
1107
1268
  }
1108
1269
 
@@ -1110,6 +1271,7 @@ static VALUE _native_stats_reset_not_thread_safe(DDTRACE_UNUSED VALUE self, VALU
1110
1271
  cpu_and_wall_time_worker_state *state;
1111
1272
  TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1112
1273
  reset_stats_not_thread_safe(state);
1274
+ thread_context_collector_stats_reset_not_thread_safe(state->thread_context_collector_instance);
1113
1275
  return Qnil;
1114
1276
  }
1115
1277
 
@@ -1119,11 +1281,19 @@ void *simulate_sampling_signal_delivery(DDTRACE_UNUSED void *_unused) {
1119
1281
  // This can potentially happen if the CpuAndWallTimeWorker was stopped while the IdleSamplingHelper was trying to execute this action
1120
1282
  if (state == NULL) return NULL;
1121
1283
 
1284
+ // Since this is not a real signal firing, we need to block SIGPROF delivery on this thread to avoid an actual SIGPROF
1285
+ // signal coming in nested and interrupting us on this thread. Thus we respect the invariant of "no nesting" for `handle_sampling_signal`.
1286
+ //
1287
+ // Not needed when `no_signals_workaround_enabled` is set: no SIGPROFs are ever sent in that mode, so there's nothing to mask.
1288
+ if (!state->no_signals_workaround_enabled) block_sigprof_signal_handler_from_running_in_current_thread();
1289
+
1122
1290
  state->stats.simulated_signal_delivery++;
1123
1291
 
1124
1292
  // `handle_sampling_signal` does a few things extra on top of `sample_from_postponed_job` so that's why we don't shortcut here
1125
1293
  handle_sampling_signal(0, NULL, NULL);
1126
1294
 
1295
+ if (!state->no_signals_workaround_enabled) unblock_sigprof_signal_handler_from_running_in_current_thread();
1296
+
1127
1297
  return NULL; // Unused
1128
1298
  }
1129
1299
 
@@ -1136,12 +1306,15 @@ static void reset_stats_not_thread_safe(cpu_and_wall_time_worker_state *state) {
1136
1306
  // * Included in the following stats window (writes after stats retrieval and reset).
1137
1307
  // Given the expected infrequency of resetting (~once per 60s profile) and the auxiliary/non-critical nature of these stats
1138
1308
  // this momentary loss of accuracy is deemed acceptable to keep overhead to a minimum.
1309
+
1139
1310
  state->stats = (struct stats) {
1140
1311
  // All these values are initialized to their highest value possible since we always take the min between existing and latest sample
1141
1312
  .cpu_sampling_time_ns_min = UINT64_MAX,
1142
1313
  .allocation_sampling_time_ns_min = UINT64_MAX,
1143
1314
  .gvl_sampling_time_ns_min = UINT64_MAX,
1315
+ // Other fields are reset to 0
1144
1316
  };
1317
+ global_stats = (global_stats_t) {0};
1145
1318
  }
1146
1319
 
1147
1320
  static void sleep_for(uint64_t time_ns) {
@@ -1182,6 +1355,9 @@ static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
1182
1355
  // Implements memory-related profiling events. This function is called by Ruby via the `rb_add_event_hook2`
1183
1356
  // when the RUBY_INTERNAL_EVENT_NEWOBJ event is triggered.
1184
1357
  //
1358
+ // This function is called from the RUBY_INTERNAL_EVENT_NEWOBJ tracepoint so it should neither allocate in the
1359
+ // Ruby heap nor release the GVL (https://github.com/DataDog/dd-trace-rb/pull/4240).
1360
+ //
1185
1361
  // When allocation sampling is enabled, this function gets called for almost all* objects allocated by the Ruby VM.
1186
1362
  // (*In some weird cases the VM may skip this tracepoint.)
1187
1363
  //
@@ -1218,13 +1394,22 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
1218
1394
  return;
1219
1395
  }
1220
1396
 
1397
+ VALUE current_thread = rb_thread_current();
1398
+
1221
1399
  // If Ruby is in the middle of raising an exception, we don't want to try to sample. This is because if we accidentally
1222
1400
  // trigger an exception inside the profiler code, bad things will happen (specifically, Ruby will try to kill off the
1223
1401
  // thread even though we may try to catch the exception).
1224
1402
  //
1225
1403
  // Note that "in the middle of raising an exception" means the exception itself has already been allocated.
1226
1404
  // What's getting allocated now is probably the backtrace objects (@ivoanjo or at least that's what I've observed)
1227
- if (is_raised_flag_set(rb_thread_current())) {
1405
+ if (is_raised_flag_set(current_thread)) {
1406
+ return;
1407
+ }
1408
+
1409
+ per_thread_context *thread_context = get_per_thread_context(current_thread);
1410
+ if (!thread_context) {
1411
+ // Context is created eagerly via on_thread_begin_event, so this should not normally be NULL.
1412
+ // We keep the guard since we can't allocate here (inside on_newobj_event).
1228
1413
  return;
1229
1414
  }
1230
1415
 
@@ -1259,7 +1444,7 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
1259
1444
  // Rescue against any exceptions that happen during sampling
1260
1445
  safely_call(
1261
1446
  rescued_sample_allocation,
1262
- Qnil,
1447
+ (VALUE) thread_context,
1263
1448
  state->self_instance,
1264
1449
  handle_sampling_failure_rescued_sample_allocation
1265
1450
  );
@@ -1283,7 +1468,7 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
1283
1468
  during_sample_exit(state);
1284
1469
  }
1285
1470
 
1286
- static void disable_tracepoints(cpu_and_wall_time_worker_state *state) {
1471
+ static void disable_hooks(cpu_and_wall_time_worker_state *state) {
1287
1472
  if (state->gc_tracepoint != Qnil) {
1288
1473
  rb_tracepoint_disable(state->gc_tracepoint);
1289
1474
  }
@@ -1311,7 +1496,8 @@ static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self) {
1311
1496
  }
1312
1497
  }
1313
1498
 
1314
- static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {
1499
+ static VALUE rescued_sample_allocation(VALUE arg) {
1500
+ per_thread_context *thread_context = (per_thread_context*) arg;
1315
1501
  cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1316
1502
 
1317
1503
  // This should not happen in a normal situation because on_newobj_event already checked for this, but just in case...
@@ -1330,18 +1516,18 @@ static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {
1330
1516
  // To control bias from sampling, we clamp the maximum weight attributed to a single allocation sample. This avoids
1331
1517
  // assigning a very large number to a sample, if for instance the dynamic sampling mechanism chose a really big interval.
1332
1518
  unsigned int weight = allocations_since_last_sample > MAX_ALLOC_WEIGHT ? MAX_ALLOC_WEIGHT : (unsigned int) allocations_since_last_sample;
1333
- bool needs_after_allocation = thread_context_collector_sample_allocation(state->thread_context_collector_instance, weight, new_object);
1519
+ bool needs_commit = thread_context_collector_sample_allocation(state->thread_context_collector_instance, thread_context, weight, new_object);
1334
1520
  // ...but we still represent the skipped samples in the profile, thus the data will account for all allocations.
1335
1521
  if (weight < allocations_since_last_sample) {
1336
1522
  uint32_t skipped_samples = (uint32_t) uint64_min_of(allocations_since_last_sample - weight, UINT32_MAX);
1337
1523
  thread_context_collector_sample_skipped_allocation_samples(state->thread_context_collector_instance, skipped_samples);
1338
1524
  }
1339
1525
 
1340
- if (needs_after_allocation) {
1526
+ if (needs_commit) {
1341
1527
  #ifndef NO_POSTPONED_TRIGGER
1342
- rb_postponed_job_trigger(after_allocation_from_postponed_job_handle);
1528
+ rb_postponed_job_trigger(commit_heap_recordings_from_postponed_job_may_lose_gvl_handle);
1343
1529
  #else
1344
- // Not needed on legacy rubies
1530
+ rb_postponed_job_register_one(0, commit_heap_recordings_from_postponed_job_may_lose_gvl, NULL);
1345
1531
  #endif
1346
1532
  }
1347
1533
 
@@ -1387,44 +1573,71 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
1387
1573
  #ifndef NO_GVL_INSTRUMENTATION
1388
1574
  static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused) {
1389
1575
  // Be very careful about touching the `state` here or doing anything at all:
1390
- // This function gets called without the GVL, and potentially from background Ractors!
1576
+ // This function gets called without the GVL, and potentially from non-main Ractors!
1391
1577
  //
1392
- // In fact, the `target_thread` that this event is about may not even be the current thread. (So be careful with thread locals that
1393
- // are not directly tied to the `target_thread` object and the like)
1394
- gvl_profiling_thread target_thread = thread_from_event(event_data);
1578
+ // Note, even though these events can get called without the GVL, they synchronize-with enabling/disabling of
1579
+ // the hook that calls us using a read-write lock. Thus, disabling the hook cannot be concurrent with calling this function,
1580
+ // and once the disable finishes there can't be "late" calls into this function.
1581
+
1582
+ // The thread that this event is about may not be the current thread
1583
+ // (as documented on rb_internal_thread_add_event_hook(), and this is notably the case for READY on Ruby 4.0),
1584
+ // so be careful with native thread locals that are not directly tied to the thread object and the like.
1585
+
1586
+ #ifdef HAVE_RUBY_THREAD_STORAGE_API
1587
+ VALUE target_thread = event_data->thread;
1588
+ #else
1589
+ // On Ruby 3.2 the event does not carry the thread, but all events fire on the thread itself.
1590
+ // However, during early thread startup rb_thread_current() can crash because the execution context (Fiber) isn't
1591
+ // stored in TLS yet; ruby_native_thread_p() guards against this.
1592
+ if (!ruby_native_thread_p()) return;
1593
+ VALUE target_thread = rb_thread_current();
1594
+ #endif
1395
1595
 
1396
- if (event_id == RUBY_INTERNAL_THREAD_EVENT_READY) { /* waiting for gvl */
1397
- thread_context_collector_on_gvl_waiting(target_thread);
1596
+ per_thread_context* thread_context = get_per_thread_context(target_thread);
1597
+ if (!thread_context) return;
1598
+ // If non-NULL the thread is profiled and from the main Ractor
1599
+
1600
+ if (event_id == RUBY_INTERNAL_THREAD_EVENT_SUSPENDED) { /* released gvl */
1601
+ thread_context_collector_on_gvl_released(thread_context);
1602
+ } else if (event_id == RUBY_INTERNAL_THREAD_EVENT_READY) { /* waiting for gvl */
1603
+ thread_context_collector_on_gvl_waiting(thread_context);
1398
1604
  } else if (event_id == RUBY_INTERNAL_THREAD_EVENT_RESUMED) { /* running/runnable */
1399
- // Interesting note: A RUBY_INTERNAL_THREAD_EVENT_RESUMED is guaranteed to be called with the GVL being acquired.
1400
- // (And... I think target_thread will be == rb_thread_current()?)
1605
+ // We must only use async-signal-safe functions here and not call arbitrary Ruby APIs and not allocate!
1606
+ // One might assume RUBY_INTERNAL_THREAD_EVENT_RESUMED means having the GVL and running that thread.
1607
+ // However, the reality is more complicated (https://bugs.ruby-lang.org/issues/22098),
1608
+ // it only "sort of" has the GVL but not fully, and it's called while holding the scheduler lock,
1609
+ // so we do as little work as possible here, and perform the sample in a postponed_job.
1610
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1611
+ if (state == NULL) return; // This should not happen, but just in case...
1612
+
1613
+ // on_gvl_running prepares a stack sample so we need to gate it with `during_sample_enter` to avoid a
1614
+ // SIGPROF signal coming in during that function and potentially causing a corrupted final state.
1401
1615
  //
1402
- // But we're not sure if we're on the main Ractor yet. The thread context collector actually can actually help here:
1403
- // it tags threads it's tracking, so if a thread is tagged then by definition we know that thread belongs to the main
1404
- // Ractor. Thus, if we get a ON_GVL_RUNNING_UNKNOWN result we shouldn't touch any state, but otherwise we're good to go.
1616
+ // TODO: Currently, the sample prepared in on_gvl_running can still be clobbered if the signal handler runs
1617
+ // after `during_sample_exit` but before `after_gvl_running_from_postponed_job` gets to run. We'll need to fix
1618
+ // that next.
1619
+ during_sample_enter(state);
1405
1620
 
1406
- #ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS
1407
- target_thread = gvl_profiling_state_maybe_initialize();
1408
- #endif
1621
+ on_gvl_running_result result =
1622
+ thread_context_collector_on_gvl_running(target_thread, thread_context, state->waiting_for_gvl_threshold_ns);
1623
+
1624
+ during_sample_exit(state);
1409
1625
 
1410
- on_gvl_running_result result = thread_context_collector_on_gvl_running(target_thread);
1626
+ if (result.waiting_for_gvl_duration_ns > 0) {
1627
+ state->stats.vm_metrics.gvl_waiting_time_ns_total += (uint64_t) result.waiting_for_gvl_duration_ns;
1628
+ }
1411
1629
 
1412
- if (result == ON_GVL_RUNNING_SAMPLE) {
1630
+ if (result.action == ON_GVL_RUNNING_SAMPLE) {
1413
1631
  #ifndef NO_POSTPONED_TRIGGER
1414
1632
  rb_postponed_job_trigger(after_gvl_running_from_postponed_job_handle);
1415
1633
  #else
1416
1634
  rb_postponed_job_register_one(0, after_gvl_running_from_postponed_job, NULL);
1417
1635
  #endif
1418
- } else if (result == ON_GVL_RUNNING_DONT_SAMPLE) {
1419
- cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1420
-
1421
- if (state == NULL) return; // This should not happen, but just in case...
1422
-
1636
+ } else if (result.action == ON_GVL_RUNNING_DONT_SAMPLE) {
1423
1637
  state->stats.gvl_dont_sample++;
1424
1638
  }
1425
1639
  } else {
1426
- // This is a very delicate time and it's hard for us to raise an exception so let's at least complain to stderr
1427
- fprintf(stderr, "[ddtrace] Unexpected value in on_gvl_event (%d)\n", event_id);
1640
+ rb_bug("[ddtrace] Unexpected value in on_gvl_event (%d)\n", event_id);
1428
1641
  }
1429
1642
  }
1430
1643
 
@@ -1496,54 +1709,54 @@ static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VA
1496
1709
  return Qnil;
1497
1710
  }
1498
1711
 
1712
+ static VALUE handle_sampling_failure_thread_context_collector_heap_update(VALUE self_instance, VALUE exception) {
1713
+ stop(self_instance, exception, "thread_context_collector_heap_update_may_lose_gvl");
1714
+ return Qnil;
1715
+ }
1716
+
1499
1717
  static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception) {
1500
1718
  stop(self_instance, exception, "rescued_sample_allocation");
1501
1719
  return Qnil;
1502
1720
  }
1503
1721
 
1504
- static VALUE handle_sampling_failure_rescued_after_allocation(VALUE self_instance, VALUE exception) {
1505
- stop(self_instance, exception, "rescued_after_allocation");
1722
+ static VALUE handle_sampling_failure_rescued_commit_heap_recordings(VALUE self_instance, VALUE exception) {
1723
+ stop(self_instance, exception, "rescued_commit_heap_recordings_may_lose_gvl");
1506
1724
  return Qnil;
1507
1725
  }
1508
1726
 
1509
- static VALUE rescued_after_allocation(VALUE self_instance) {
1727
+ static VALUE rescued_commit_heap_recordings_may_lose_gvl(VALUE self_instance) {
1510
1728
  cpu_and_wall_time_worker_state *state;
1511
1729
  TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1512
1730
 
1513
- thread_context_collector_after_allocation(state->thread_context_collector_instance);
1731
+ thread_context_collector_commit_heap_recordings_may_lose_gvl(state->thread_context_collector_instance);
1514
1732
 
1515
1733
  // Return a dummy VALUE because we're called from rb_rescue2 which requires it
1516
1734
  return Qnil;
1517
1735
  }
1518
1736
 
1519
- // This postponed job callback is used to finalize heap allocation recordings on Ruby 4+.
1520
- // During on_newobj_event, calling rb_obj_id() is unsafe because it mutates the object.
1521
- // So we defer getting the object_id until after the event completes.
1522
- #pragma GCC diagnostic push
1523
- #pragma GCC diagnostic ignored "-Wunused-function" // This is only used for some Rubies, but we want to build on all to make it easier to dev
1524
- static void after_allocation_from_postponed_job(DDTRACE_UNUSED void *_unused) {
1737
+ // This postponed job callback is used to commit heap allocation recordings.
1738
+ // During on_newobj_event, we can't take the weak reference the heap recorder needs to track the object, so we defer
1739
+ // that until after the event completes.
1740
+ static void commit_heap_recordings_from_postponed_job_may_lose_gvl(DDTRACE_UNUSED void *_unused) {
1525
1741
  cpu_and_wall_time_worker_state *state = active_sampler_instance_state;
1526
1742
 
1527
1743
  if (state == NULL || !ddtrace_rb_ractor_main_p()) return;
1528
1744
 
1529
- // Protect against nested operations
1530
- if (state->during_sample) return;
1531
-
1532
- during_sample_enter(state);
1745
+ if (state->during_sample) {
1746
+ delayed_error(state, "commit_heap_recordings_from_postponed_job_may_lose_gvl called during_sample");
1747
+ return;
1748
+ }
1533
1749
 
1534
1750
  // NOTE: We're not updating the allocation_sampler here.
1535
1751
  // This means work done in this function isn't accounted for as profiler overhead.
1536
1752
  // This is acceptable as the amount of work done here is expected to be small.
1537
1753
  safely_call(
1538
- rescued_after_allocation,
1754
+ rescued_commit_heap_recordings_may_lose_gvl,
1539
1755
  state->self_instance,
1540
1756
  state->self_instance,
1541
- handle_sampling_failure_rescued_after_allocation
1757
+ handle_sampling_failure_rescued_commit_heap_recordings
1542
1758
  );
1543
-
1544
- during_sample_exit(state);
1545
1759
  }
1546
- #pragma GCC diagnostic pop
1547
1760
 
1548
1761
  static inline void during_sample_enter(cpu_and_wall_time_worker_state* state) {
1549
1762
  // Tell the compiler it's not allowed to reorder the `during_sample` flag with anything that happens after.