datadog 2.30.0 → 2.45.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 (966) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +450 -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 +368 -151
  10. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +2 -6
  11. data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +0 -2
  12. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +29 -16
  13. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.h +5 -1
  14. data/ext/datadog_profiling_native_extension/collectors_stack.c +263 -170
  15. data/ext/datadog_profiling_native_extension/collectors_stack.h +24 -5
  16. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +909 -552
  17. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +25 -9
  18. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +5 -4
  19. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +44 -19
  20. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +7 -12
  21. data/ext/datadog_profiling_native_extension/extconf.rb +86 -104
  22. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +4 -43
  23. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +15 -47
  24. data/ext/datadog_profiling_native_extension/heap_recorder.c +393 -279
  25. data/ext/datadog_profiling_native_extension/heap_recorder.h +50 -31
  26. data/ext/datadog_profiling_native_extension/http_transport.c +10 -5
  27. data/ext/datadog_profiling_native_extension/macos_sampler_thread.h +55 -0
  28. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +3 -27
  29. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +847 -510
  30. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +38 -14
  31. data/ext/datadog_profiling_native_extension/profiling.c +65 -27
  32. data/ext/datadog_profiling_native_extension/ruby_helpers.c +37 -107
  33. data/ext/datadog_profiling_native_extension/ruby_helpers.h +4 -7
  34. data/ext/datadog_profiling_native_extension/stack_recorder.c +130 -93
  35. data/ext/datadog_profiling_native_extension/stack_recorder.h +12 -4
  36. data/ext/datadog_profiling_native_extension/time_helpers.c +2 -4
  37. data/ext/datadog_profiling_native_extension/time_helpers.h +25 -19
  38. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +4 -2
  39. data/ext/libdatadog_api/crashtracker.c +7 -8
  40. data/ext/libdatadog_api/datadog_ruby_common.c +44 -19
  41. data/ext/libdatadog_api/datadog_ruby_common.h +7 -12
  42. data/ext/libdatadog_api/di.c +218 -0
  43. data/ext/libdatadog_api/extconf.rb +40 -26
  44. data/ext/libdatadog_api/feature_flags.c +34 -0
  45. data/ext/libdatadog_api/init.c +13 -2
  46. data/ext/libdatadog_api/otel_thread_context.c +268 -0
  47. data/ext/libdatadog_api/otel_thread_context.h +5 -0
  48. data/ext/libdatadog_api/trace_exporter.c +1998 -0
  49. data/ext/libdatadog_api/trace_exporter.h +5 -0
  50. data/ext/libdatadog_extconf_helpers.rb +63 -13
  51. data/lib/datadog/ai_guard/autoload.rb +10 -0
  52. data/lib/datadog/ai_guard/component.rb +24 -13
  53. data/lib/datadog/ai_guard/configuration/ext.rb +2 -0
  54. data/lib/datadog/ai_guard/configuration.rb +119 -2
  55. data/lib/datadog/ai_guard/contrib/auto_instrument.rb +24 -0
  56. data/lib/datadog/ai_guard/contrib/rack/integration.rb +42 -0
  57. data/lib/datadog/ai_guard/contrib/rack/patcher.rb +26 -0
  58. data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +97 -0
  59. data/lib/datadog/ai_guard/contrib/rails/integration.rb +41 -0
  60. data/lib/datadog/ai_guard/contrib/rails/patcher.rb +97 -0
  61. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +54 -23
  62. data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +1 -1
  63. data/lib/datadog/ai_guard/contrib/ruby_llm/message_adapter.rb +76 -0
  64. data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +1 -0
  65. data/lib/datadog/ai_guard/contrib/ruby_llm/redaction_change_set.rb +135 -0
  66. data/lib/datadog/ai_guard/evaluation/client.rb +50 -0
  67. data/lib/datadog/ai_guard/evaluation/content_part.rb +48 -0
  68. data/lib/datadog/ai_guard/evaluation/message.rb +25 -7
  69. data/lib/datadog/ai_guard/evaluation/message_builder.rb +31 -0
  70. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +8 -6
  71. data/lib/datadog/ai_guard/evaluation/outcome.rb +24 -0
  72. data/lib/datadog/ai_guard/evaluation/request.rb +8 -59
  73. data/lib/datadog/ai_guard/evaluation/response.rb +33 -0
  74. data/lib/datadog/ai_guard/evaluation/result.rb +11 -16
  75. data/lib/datadog/ai_guard/evaluation/tool_call.rb +15 -2
  76. data/lib/datadog/ai_guard/evaluation.rb +75 -17
  77. data/lib/datadog/ai_guard/ext.rb +16 -0
  78. data/lib/datadog/ai_guard/{api_client.rb → http_client.rb} +15 -11
  79. data/lib/datadog/ai_guard/metrics/telemetry.rb +43 -0
  80. data/lib/datadog/ai_guard/redaction/replacements.rb +97 -0
  81. data/lib/datadog/ai_guard/redaction/result.rb +29 -0
  82. data/lib/datadog/ai_guard/redaction.rb +80 -0
  83. data/lib/datadog/ai_guard.rb +70 -43
  84. data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +22 -43
  85. data/lib/datadog/appsec/actions_handler.rb +6 -6
  86. data/lib/datadog/appsec/anonymizer.rb +1 -1
  87. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +2 -2
  88. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +6 -6
  89. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +1 -1
  90. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +2 -2
  91. data/lib/datadog/appsec/api_security/route_extractor.rb +26 -14
  92. data/lib/datadog/appsec/api_security/sampler.rb +5 -5
  93. data/lib/datadog/appsec/api_security.rb +1 -1
  94. data/lib/datadog/appsec/assets/blocked.html +1 -108
  95. data/lib/datadog/appsec/assets.rb +7 -7
  96. data/lib/datadog/appsec/autoload.rb +4 -4
  97. data/lib/datadog/appsec/component.rb +29 -22
  98. data/lib/datadog/appsec/compressed_json.rb +7 -7
  99. data/lib/datadog/appsec/configuration.rb +436 -1
  100. data/lib/datadog/appsec/context.rb +10 -8
  101. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +6 -6
  102. data/lib/datadog/appsec/contrib/active_record/integration.rb +4 -4
  103. data/lib/datadog/appsec/contrib/active_record/patcher.rb +4 -4
  104. data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +75 -0
  105. data/lib/datadog/appsec/contrib/aws_lambda/integration.rb +39 -0
  106. data/lib/datadog/appsec/contrib/aws_lambda/patcher.rb +30 -0
  107. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +146 -0
  108. data/lib/datadog/appsec/contrib/devise/configuration.rb +1 -1
  109. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +3 -3
  110. data/lib/datadog/appsec/contrib/devise/ext.rb +20 -20
  111. data/lib/datadog/appsec/contrib/devise/integration.rb +4 -4
  112. data/lib/datadog/appsec/contrib/devise/patcher.rb +8 -8
  113. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +14 -13
  114. data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +8 -8
  115. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +24 -9
  116. data/lib/datadog/appsec/contrib/excon/integration.rb +4 -4
  117. data/lib/datadog/appsec/contrib/excon/patcher.rb +1 -1
  118. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +76 -21
  119. data/lib/datadog/appsec/contrib/faraday/integration.rb +4 -4
  120. data/lib/datadog/appsec/contrib/faraday/patcher.rb +4 -4
  121. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +72 -18
  122. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +4 -4
  123. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +66 -21
  124. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +7 -7
  125. data/lib/datadog/appsec/contrib/graphql/integration.rb +9 -8
  126. data/lib/datadog/appsec/contrib/graphql/patcher.rb +3 -3
  127. data/lib/datadog/appsec/contrib/rack/buffered_input.rb +83 -0
  128. data/lib/datadog/appsec/contrib/rack/ext.rb +29 -29
  129. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +56 -17
  130. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +1 -1
  131. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +46 -30
  132. data/lib/datadog/appsec/contrib/rack/input_peeker.rb +79 -0
  133. data/lib/datadog/appsec/contrib/rack/integration.rb +6 -6
  134. data/lib/datadog/appsec/contrib/rack/patcher.rb +3 -3
  135. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +11 -8
  136. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +51 -61
  137. data/lib/datadog/appsec/contrib/rack/response_body.rb +1 -1
  138. data/lib/datadog/appsec/contrib/rails/gateway/request.rb +43 -4
  139. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +25 -9
  140. data/lib/datadog/appsec/contrib/rails/integration.rb +5 -5
  141. data/lib/datadog/appsec/contrib/rails/patcher.rb +17 -17
  142. data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.rb +2 -2
  143. data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +3 -3
  144. data/lib/datadog/appsec/contrib/rails/request.rb +3 -3
  145. data/lib/datadog/appsec/contrib/rails/request_middleware.rb +2 -1
  146. data/lib/datadog/appsec/contrib/rest_client/integration.rb +5 -5
  147. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +1 -1
  148. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +74 -21
  149. data/lib/datadog/appsec/contrib/sinatra/gateway/request.rb +1 -1
  150. data/lib/datadog/appsec/contrib/sinatra/gateway/route_params.rb +1 -1
  151. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +34 -13
  152. data/lib/datadog/appsec/contrib/sinatra/integration.rb +5 -5
  153. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +11 -11
  154. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +3 -3
  155. data/lib/datadog/appsec/contrib/sinatra/request_middleware.rb +2 -1
  156. data/lib/datadog/appsec/counter_sampler.rb +1 -1
  157. data/lib/datadog/appsec/default_header_tags.rb +52 -0
  158. data/lib/datadog/appsec/event.rb +14 -14
  159. data/lib/datadog/appsec/ext.rb +11 -10
  160. data/lib/datadog/appsec/extensions.rb +2 -2
  161. data/lib/datadog/appsec/instrumentation/gateway.rb +1 -14
  162. data/lib/datadog/appsec/metrics/collector.rb +18 -1
  163. data/lib/datadog/appsec/metrics/exporter.rb +17 -11
  164. data/lib/datadog/appsec/metrics/telemetry.rb +15 -3
  165. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +4 -4
  166. data/lib/datadog/appsec/metrics.rb +4 -4
  167. data/lib/datadog/appsec/monitor/gateway/watcher.rb +18 -12
  168. data/lib/datadog/appsec/monitor.rb +1 -1
  169. data/lib/datadog/appsec/processor/rule_loader.rb +32 -32
  170. data/lib/datadog/appsec/rate_limiter.rb +1 -1
  171. data/lib/datadog/appsec/remote.rb +15 -9
  172. data/lib/datadog/appsec/response.rb +22 -22
  173. data/lib/datadog/appsec/route_normalizer/rails_route_pattern.rb +176 -0
  174. data/lib/datadog/appsec/route_normalizer/route_pattern.rb +378 -0
  175. data/lib/datadog/appsec/route_normalizer/route_text.rb +57 -0
  176. data/lib/datadog/appsec/route_normalizer.rb +80 -0
  177. data/lib/datadog/appsec/security_engine/engine.rb +40 -40
  178. data/lib/datadog/appsec/security_engine/runner.rb +3 -3
  179. data/lib/datadog/appsec/security_event.rb +2 -2
  180. data/lib/datadog/appsec/trace_keeper.rb +18 -6
  181. data/lib/datadog/appsec/utils/http/body.rb +11 -8
  182. data/lib/datadog/appsec/utils/http/body_reader.rb +61 -0
  183. data/lib/datadog/appsec/utils/http/media_range.rb +15 -15
  184. data/lib/datadog/appsec/utils/http/media_type.rb +5 -6
  185. data/lib/datadog/appsec/utils/http/url_encoded.rb +79 -24
  186. data/lib/datadog/appsec.rb +21 -24
  187. data/lib/datadog/auto_instrument.rb +2 -2
  188. data/lib/datadog/core/buffer/cruby.rb +1 -1
  189. data/lib/datadog/core/buffer/thread_safe.rb +1 -1
  190. data/lib/datadog/core/configuration/agent_settings.rb +1 -1
  191. data/lib/datadog/core/configuration/agent_settings_resolver.rb +15 -15
  192. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +2 -2
  193. data/lib/datadog/core/configuration/base.rb +19 -7
  194. data/lib/datadog/core/configuration/components.rb +151 -37
  195. data/lib/datadog/core/configuration/components_state.rb +6 -1
  196. data/lib/datadog/core/configuration/config_helper.rb +12 -3
  197. data/lib/datadog/core/configuration/deprecations.rb +6 -6
  198. data/lib/datadog/core/configuration/ext.rb +11 -11
  199. data/lib/datadog/core/configuration/option.rb +46 -21
  200. data/lib/datadog/core/configuration/option_definition.rb +43 -17
  201. data/lib/datadog/core/configuration/options.rb +42 -8
  202. data/lib/datadog/core/configuration/settings.rb +133 -105
  203. data/lib/datadog/core/configuration/stable_config.rb +1 -1
  204. data/lib/datadog/core/configuration/supported_configurations.rb +33 -1
  205. data/lib/datadog/core/configuration.rb +9 -9
  206. data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
  207. data/lib/datadog/core/contrib/rails/utils.rb +7 -3
  208. data/lib/datadog/core/crashtracking/component.rb +17 -13
  209. data/lib/datadog/core/crashtracking/tag_builder.rb +5 -5
  210. data/lib/datadog/core/deprecations.rb +1 -1
  211. data/lib/datadog/core/diagnostics/environment_logger.rb +45 -8
  212. data/lib/datadog/core/diagnostics/health.rb +2 -2
  213. data/lib/datadog/core/encoding.rb +4 -4
  214. data/lib/datadog/core/environment/agent_info.rb +2 -2
  215. data/lib/datadog/core/environment/cgroup.rb +4 -4
  216. data/lib/datadog/core/environment/container.rb +11 -11
  217. data/lib/datadog/core/environment/execution.rb +3 -3
  218. data/lib/datadog/core/environment/ext.rb +21 -15
  219. data/lib/datadog/core/environment/gc.rb +1 -1
  220. data/lib/datadog/core/environment/git.rb +2 -2
  221. data/lib/datadog/core/environment/identity.rb +47 -11
  222. data/lib/datadog/core/environment/platform.rb +8 -8
  223. data/lib/datadog/core/environment/process.rb +54 -19
  224. data/lib/datadog/core/environment/socket.rb +15 -2
  225. data/lib/datadog/core/environment/variable_helpers.rb +1 -1
  226. data/lib/datadog/core/environment/vm_cache.rb +1 -1
  227. data/lib/datadog/core/environment/yjit.rb +2 -2
  228. data/lib/datadog/core/error.rb +5 -5
  229. data/lib/datadog/core/evp.rb +11 -0
  230. data/lib/datadog/core/extensions.rb +1 -1
  231. data/lib/datadog/core/feature_flags.rb +3 -3
  232. data/lib/datadog/core/git/ext.rb +4 -4
  233. data/lib/datadog/core/knuth_sampler.rb +3 -1
  234. data/lib/datadog/core/logger.rb +3 -3
  235. data/lib/datadog/core/logging/ext.rb +1 -1
  236. data/lib/datadog/core/metrics/client.rb +26 -26
  237. data/lib/datadog/core/metrics/ext.rb +5 -5
  238. data/lib/datadog/core/metrics/helpers.rb +1 -1
  239. data/lib/datadog/core/metrics/logging.rb +2 -2
  240. data/lib/datadog/core/metrics/options.rb +5 -5
  241. data/lib/datadog/core/pin.rb +1 -1
  242. data/lib/datadog/core/process_discovery.rb +8 -8
  243. data/lib/datadog/core/rate_limiter.rb +1 -1
  244. data/lib/datadog/core/remote/client/capabilities.rb +77 -19
  245. data/lib/datadog/core/remote/client.rb +18 -18
  246. data/lib/datadog/core/remote/component.rb +66 -34
  247. data/lib/datadog/core/remote/configuration/content.rb +3 -3
  248. data/lib/datadog/core/remote/configuration/digest.rb +1 -1
  249. data/lib/datadog/core/remote/configuration/path.rb +9 -9
  250. data/lib/datadog/core/remote/configuration/repository.rb +4 -4
  251. data/lib/datadog/core/remote/configuration/target.rb +9 -9
  252. data/lib/datadog/core/remote/configuration.rb +4 -4
  253. data/lib/datadog/core/remote/ext.rb +3 -3
  254. data/lib/datadog/core/remote/negotiation.rb +1 -1
  255. data/lib/datadog/core/remote/tie/tracing.rb +7 -7
  256. data/lib/datadog/core/remote/transport/config.rb +4 -4
  257. data/lib/datadog/core/remote/transport/http/config.rb +10 -10
  258. data/lib/datadog/core/remote/transport/http/negotiation.rb +4 -4
  259. data/lib/datadog/core/remote/transport/http.rb +10 -10
  260. data/lib/datadog/core/remote/transport/negotiation.rb +3 -3
  261. data/lib/datadog/core/remote/worker.rb +13 -5
  262. data/lib/datadog/core/runtime/ext.rb +22 -22
  263. data/lib/datadog/core/runtime/metrics.rb +10 -10
  264. data/lib/datadog/core/tag_builder.rb +18 -18
  265. data/lib/datadog/core/tag_normalizer.rb +7 -7
  266. data/lib/datadog/core/telemetry/component.rb +16 -13
  267. data/lib/datadog/core/telemetry/configuration_value.rb +38 -0
  268. data/lib/datadog/core/telemetry/emitter.rb +5 -5
  269. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +10 -9
  270. data/lib/datadog/core/telemetry/event/app_closing.rb +2 -2
  271. data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +2 -2
  272. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +3 -3
  273. data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
  274. data/lib/datadog/core/telemetry/event/app_heartbeat.rb +2 -2
  275. data/lib/datadog/core/telemetry/event/app_integrations_change.rb +2 -2
  276. data/lib/datadog/core/telemetry/event/app_started.rb +142 -193
  277. data/lib/datadog/core/telemetry/event/base.rb +1 -1
  278. data/lib/datadog/core/telemetry/event/distributions.rb +2 -2
  279. data/lib/datadog/core/telemetry/event/generate_metrics.rb +3 -3
  280. data/lib/datadog/core/telemetry/event/log.rb +6 -6
  281. data/lib/datadog/core/telemetry/event/message_batch.rb +1 -1
  282. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +3 -3
  283. data/lib/datadog/core/telemetry/event.rb +16 -22
  284. data/lib/datadog/core/telemetry/ext.rb +11 -10
  285. data/lib/datadog/core/telemetry/logger.rb +1 -1
  286. data/lib/datadog/core/telemetry/logging.rb +5 -5
  287. data/lib/datadog/core/telemetry/metric.rb +9 -9
  288. data/lib/datadog/core/telemetry/metrics_collection.rb +2 -2
  289. data/lib/datadog/core/telemetry/metrics_manager.rb +1 -1
  290. data/lib/datadog/core/telemetry/request.rb +6 -8
  291. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +16 -11
  292. data/lib/datadog/core/telemetry/transport/http.rb +7 -7
  293. data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -3
  294. data/lib/datadog/core/telemetry/worker.rb +26 -6
  295. data/lib/datadog/core/transport/ext.rb +14 -14
  296. data/lib/datadog/core/transport/http/adapters/net.rb +4 -4
  297. data/lib/datadog/core/transport/http/adapters/test.rb +1 -1
  298. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +4 -4
  299. data/lib/datadog/core/transport/http/api/endpoint.rb +1 -1
  300. data/lib/datadog/core/transport/http/api/map.rb +1 -1
  301. data/lib/datadog/core/transport/http/builder.rb +6 -6
  302. data/lib/datadog/core/transport/http/client.rb +2 -2
  303. data/lib/datadog/core/transport/http/response.rb +1 -1
  304. data/lib/datadog/core/transport/http.rb +9 -7
  305. data/lib/datadog/core/transport/response.rb +1 -1
  306. data/lib/datadog/core/transport/transport.rb +2 -2
  307. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +172 -20
  308. data/lib/datadog/core/utils/base64_codec.rb +23 -0
  309. data/lib/datadog/core/utils/duration.rb +1 -1
  310. data/lib/datadog/core/utils/{array.rb → enumerable_compat.rb} +2 -2
  311. data/lib/datadog/core/utils/forking.rb +3 -1
  312. data/lib/datadog/core/utils/hash.rb +0 -23
  313. data/lib/datadog/core/utils/lru_cache.rb +3 -3
  314. data/lib/datadog/core/utils/network.rb +11 -11
  315. data/lib/datadog/core/utils/only_once.rb +1 -1
  316. data/lib/datadog/core/utils/only_once_successful.rb +1 -1
  317. data/lib/datadog/core/utils/safe_dup.rb +2 -1
  318. data/lib/datadog/core/utils/sequence.rb +3 -6
  319. data/lib/datadog/core/utils/spawn_monkey_patch.rb +68 -0
  320. data/lib/datadog/core/utils/time.rb +18 -52
  321. data/lib/datadog/core/utils/url.rb +1 -1
  322. data/lib/datadog/core/utils.rb +5 -5
  323. data/lib/datadog/core/workers/async.rb +2 -2
  324. data/lib/datadog/core/workers/polling.rb +2 -2
  325. data/lib/datadog/core/workers/runtime_metrics.rb +4 -4
  326. data/lib/datadog/core.rb +7 -5
  327. data/lib/datadog/data_streams/configuration.rb +40 -1
  328. data/lib/datadog/data_streams/ext.rb +1 -1
  329. data/lib/datadog/data_streams/extensions.rb +2 -2
  330. data/lib/datadog/data_streams/pathway_context.rb +28 -30
  331. data/lib/datadog/data_streams/processor.rb +93 -62
  332. data/lib/datadog/data_streams/transport/http/stats.rb +5 -5
  333. data/lib/datadog/data_streams/transport/http.rb +5 -5
  334. data/lib/datadog/data_streams/transport/stats.rb +7 -7
  335. data/lib/datadog/data_streams.rb +5 -5
  336. data/lib/datadog/di/base.rb +16 -8
  337. data/lib/datadog/di/boot.rb +25 -24
  338. data/lib/datadog/di/capture_expression.rb +21 -0
  339. data/lib/datadog/di/capture_expression_evaluator.rb +71 -0
  340. data/lib/datadog/di/capture_limits.rb +41 -0
  341. data/lib/datadog/di/code_tracker.rb +218 -16
  342. data/lib/datadog/di/component.rb +123 -37
  343. data/lib/datadog/di/configuration.rb +241 -2
  344. data/lib/datadog/di/context.rb +9 -3
  345. data/lib/datadog/di/contrib/active_record.rb +6 -2
  346. data/lib/datadog/di/contrib/railtie.rb +1 -1
  347. data/lib/datadog/di/contrib.rb +5 -5
  348. data/lib/datadog/di/el/compiler.rb +114 -38
  349. data/lib/datadog/di/el/evaluator.rb +82 -2
  350. data/lib/datadog/di/el/expression.rb +13 -2
  351. data/lib/datadog/di/el.rb +3 -3
  352. data/lib/datadog/di/error.rb +10 -0
  353. data/lib/datadog/di/fatal_exceptions.rb +26 -0
  354. data/lib/datadog/di/instrumenter.rb +528 -184
  355. data/lib/datadog/di/logger.rb +3 -3
  356. data/lib/datadog/di/preload.rb +1 -1
  357. data/lib/datadog/di/probe.rb +69 -2
  358. data/lib/datadog/di/probe_builder.rb +98 -15
  359. data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
  360. data/lib/datadog/di/probe_file_loader.rb +14 -9
  361. data/lib/datadog/di/probe_manager.rb +83 -20
  362. data/lib/datadog/di/probe_notification_builder.rb +178 -25
  363. data/lib/datadog/di/probe_notifier_worker.rb +57 -37
  364. data/lib/datadog/di/probe_repository.rb +1 -1
  365. data/lib/datadog/di/proc_responder.rb +1 -1
  366. data/lib/datadog/di/redactor.rb +16 -1
  367. data/lib/datadog/di/remote.rb +187 -26
  368. data/lib/datadog/di/serializer.rb +79 -35
  369. data/lib/datadog/di/transport/diagnostics.rb +5 -5
  370. data/lib/datadog/di/transport/http/diagnostics.rb +3 -3
  371. data/lib/datadog/di/transport/http/input.rb +2 -2
  372. data/lib/datadog/di/transport/http.rb +10 -10
  373. data/lib/datadog/di/transport/input.rb +18 -14
  374. data/lib/datadog/di/utils.rb +43 -15
  375. data/lib/datadog/di.rb +147 -3
  376. data/lib/datadog/error_tracking/collector.rb +3 -2
  377. data/lib/datadog/error_tracking/component.rb +12 -12
  378. data/lib/datadog/error_tracking/configuration.rb +55 -2
  379. data/lib/datadog/error_tracking/ext.rb +7 -7
  380. data/lib/datadog/error_tracking/extensions.rb +2 -2
  381. data/lib/datadog/error_tracking/filters.rb +4 -4
  382. data/lib/datadog/error_tracking.rb +6 -6
  383. data/lib/datadog/kit/appsec/events/v2.rb +91 -33
  384. data/lib/datadog/kit/appsec/events.rb +25 -25
  385. data/lib/datadog/kit/enable_core_dumps.rb +3 -3
  386. data/lib/datadog/kit/identity.rb +18 -18
  387. data/lib/datadog/kit/tracing/method_tracer.rb +9 -6
  388. data/lib/datadog/kit.rb +1 -1
  389. data/lib/datadog/open_feature/agentless/configuration_source.rb +246 -0
  390. data/lib/datadog/open_feature/agentless/parser.rb +43 -0
  391. data/lib/datadog/open_feature/agentless/response.rb +19 -0
  392. data/lib/datadog/open_feature/agentless/transport.rb +103 -0
  393. data/lib/datadog/open_feature/component.rb +118 -13
  394. data/lib/datadog/open_feature/configuration/agentless_endpoint.rb +90 -0
  395. data/lib/datadog/open_feature/configuration/source/resolution.rb +22 -0
  396. data/lib/datadog/open_feature/configuration/source.rb +43 -0
  397. data/lib/datadog/open_feature/configuration.rb +140 -1
  398. data/lib/datadog/open_feature/evaluation_engine.rb +38 -12
  399. data/lib/datadog/open_feature/exposures/batch_builder.rb +1 -1
  400. data/lib/datadog/open_feature/exposures/buffer.rb +1 -1
  401. data/lib/datadog/open_feature/exposures/deduplicator.rb +1 -1
  402. data/lib/datadog/open_feature/exposures/event.rb +17 -10
  403. data/lib/datadog/open_feature/exposures/reporter.rb +3 -3
  404. data/lib/datadog/open_feature/exposures/worker.rb +6 -6
  405. data/lib/datadog/open_feature/ext.rb +32 -6
  406. data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +442 -0
  407. data/lib/datadog/open_feature/flag_evaluation/writer.rb +544 -0
  408. data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +105 -0
  409. data/lib/datadog/open_feature/hooks/flag_eval_metrics_hook.rb +51 -0
  410. data/lib/datadog/open_feature/hooks/span_enrichment_hook/codec.rb +58 -0
  411. data/lib/datadog/open_feature/hooks/span_enrichment_hook/span_enrichment_state.rb +96 -0
  412. data/lib/datadog/open_feature/hooks/span_enrichment_hook/span_enrichment_state_store.rb +49 -0
  413. data/lib/datadog/open_feature/hooks/span_enrichment_hook.rb +184 -0
  414. data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
  415. data/lib/datadog/open_feature/native_evaluator.rb +43 -7
  416. data/lib/datadog/open_feature/noop_evaluator.rb +8 -3
  417. data/lib/datadog/open_feature/provider.rb +115 -21
  418. data/lib/datadog/open_feature/remote.rb +8 -8
  419. data/lib/datadog/open_feature/resolution_details.rb +4 -2
  420. data/lib/datadog/open_feature/transport.rb +71 -12
  421. data/lib/datadog/open_feature.rb +2 -2
  422. data/lib/datadog/opentelemetry/api/baggage.rb +3 -3
  423. data/lib/datadog/opentelemetry/api/context.rb +4 -4
  424. data/lib/datadog/opentelemetry/configuration/settings.rb +95 -28
  425. data/lib/datadog/opentelemetry/ext.rb +9 -0
  426. data/lib/datadog/opentelemetry/logs.rb +98 -0
  427. data/lib/datadog/opentelemetry/metrics.rb +17 -44
  428. data/lib/datadog/opentelemetry/sdk/configurator.rb +46 -6
  429. data/lib/datadog/opentelemetry/sdk/id_generator.rb +16 -10
  430. data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +30 -0
  431. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +9 -16
  432. data/lib/datadog/opentelemetry/sdk/propagator.rb +17 -11
  433. data/lib/datadog/opentelemetry/sdk/span_processor.rb +18 -15
  434. data/lib/datadog/opentelemetry/sdk/trace/span.rb +32 -32
  435. data/lib/datadog/opentelemetry/sdk.rb +19 -0
  436. data/lib/datadog/opentelemetry/signal_configuration.rb +57 -0
  437. data/lib/datadog/opentelemetry/trace.rb +4 -1
  438. data/lib/datadog/opentelemetry.rb +8 -7
  439. data/lib/datadog/profiling/collectors/code_provenance.rb +36 -11
  440. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +45 -3
  441. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +10 -4
  442. data/lib/datadog/profiling/collectors/info.rb +19 -10
  443. data/lib/datadog/profiling/collectors/thread_context.rb +16 -22
  444. data/lib/datadog/profiling/component.rb +43 -37
  445. data/lib/datadog/profiling/exporter.rb +43 -16
  446. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +6 -5
  447. data/lib/datadog/profiling/ext.rb +0 -2
  448. data/lib/datadog/profiling/flush.rb +21 -12
  449. data/lib/datadog/profiling/http_transport.rb +13 -2
  450. data/lib/datadog/profiling/load_native_extension.rb +2 -2
  451. data/lib/datadog/profiling/profiler.rb +13 -5
  452. data/lib/datadog/profiling/scheduler.rb +2 -2
  453. data/lib/datadog/profiling/sequence_tracker.rb +1 -1
  454. data/lib/datadog/profiling/stack_recorder.rb +0 -8
  455. data/lib/datadog/profiling/tag_builder.rb +19 -19
  456. data/lib/datadog/profiling/tasks/exec.rb +9 -4
  457. data/lib/datadog/profiling/tasks/help.rb +1 -0
  458. data/lib/datadog/profiling/tasks/setup.rb +2 -2
  459. data/lib/datadog/profiling.rb +32 -29
  460. data/lib/datadog/ruby_version.rb +25 -0
  461. data/lib/datadog/single_step_instrument.rb +2 -2
  462. data/lib/datadog/symbol_database/component.rb +734 -0
  463. data/lib/datadog/symbol_database/configuration.rb +73 -0
  464. data/lib/datadog/symbol_database/extensions.rb +19 -0
  465. data/lib/datadog/symbol_database/extractor.rb +1092 -0
  466. data/lib/datadog/symbol_database/file_hash.rb +48 -0
  467. data/lib/datadog/symbol_database/logger.rb +43 -0
  468. data/lib/datadog/symbol_database/remote.rb +195 -0
  469. data/lib/datadog/symbol_database/scope.rb +102 -0
  470. data/lib/datadog/symbol_database/scope_batcher.rb +292 -0
  471. data/lib/datadog/symbol_database/service_version.rb +66 -0
  472. data/lib/datadog/symbol_database/symbol.rb +69 -0
  473. data/lib/datadog/symbol_database/transport/http/endpoint.rb +28 -0
  474. data/lib/datadog/symbol_database/transport/http.rb +45 -0
  475. data/lib/datadog/symbol_database/transport.rb +54 -0
  476. data/lib/datadog/symbol_database/uploader.rb +230 -0
  477. data/lib/datadog/symbol_database.rb +71 -0
  478. data/lib/datadog/tracing/analytics.rb +1 -1
  479. data/lib/datadog/tracing/buffer.rb +8 -8
  480. data/lib/datadog/tracing/client_ip.rb +4 -4
  481. data/lib/datadog/tracing/component.rb +41 -7
  482. data/lib/datadog/tracing/configuration/dynamic/option.rb +4 -0
  483. data/lib/datadog/tracing/configuration/dynamic.rb +13 -11
  484. data/lib/datadog/tracing/configuration/ext.rb +53 -38
  485. data/lib/datadog/tracing/configuration/http.rb +2 -2
  486. data/lib/datadog/tracing/configuration/settings.rb +58 -17
  487. data/lib/datadog/tracing/context.rb +25 -3
  488. data/lib/datadog/tracing/context_provider.rb +11 -7
  489. data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +2 -2
  490. data/lib/datadog/tracing/contrib/action_cable/event.rb +4 -4
  491. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +9 -6
  492. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +8 -5
  493. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +9 -6
  494. data/lib/datadog/tracing/contrib/action_cable/events.rb +5 -5
  495. data/lib/datadog/tracing/contrib/action_cable/ext.rb +18 -18
  496. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -5
  497. data/lib/datadog/tracing/contrib/action_cable/integration.rb +7 -7
  498. data/lib/datadog/tracing/contrib/action_cable/patcher.rb +4 -4
  499. data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +2 -2
  500. data/lib/datadog/tracing/contrib/action_mailer/event.rb +7 -4
  501. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +8 -8
  502. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +4 -4
  503. data/lib/datadog/tracing/contrib/action_mailer/events.rb +3 -3
  504. data/lib/datadog/tracing/contrib/action_mailer/ext.rb +18 -18
  505. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +7 -7
  506. data/lib/datadog/tracing/contrib/action_mailer/patcher.rb +3 -3
  507. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +15 -12
  508. data/lib/datadog/tracing/contrib/action_pack/action_controller/patcher.rb +2 -2
  509. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +31 -3
  510. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +5 -3
  511. data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +3 -3
  512. data/lib/datadog/tracing/contrib/action_pack/ext.rb +10 -10
  513. data/lib/datadog/tracing/contrib/action_pack/integration.rb +7 -7
  514. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +3 -3
  515. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -1
  516. data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +3 -3
  517. data/lib/datadog/tracing/contrib/action_view/event.rb +1 -1
  518. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +10 -7
  519. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +11 -8
  520. data/lib/datadog/tracing/contrib/action_view/events.rb +3 -3
  521. data/lib/datadog/tracing/contrib/action_view/ext.rb +10 -10
  522. data/lib/datadog/tracing/contrib/action_view/integration.rb +11 -11
  523. data/lib/datadog/tracing/contrib/action_view/patcher.rb +5 -5
  524. data/lib/datadog/tracing/contrib/action_view/utils.rb +2 -2
  525. data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +2 -2
  526. data/lib/datadog/tracing/contrib/active_job/data_streams.rb +62 -0
  527. data/lib/datadog/tracing/contrib/active_job/event.rb +3 -3
  528. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +10 -7
  529. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +10 -7
  530. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +10 -7
  531. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +10 -7
  532. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +10 -7
  533. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +10 -7
  534. data/lib/datadog/tracing/contrib/active_job/events.rb +6 -6
  535. data/lib/datadog/tracing/contrib/active_job/ext.rb +23 -23
  536. data/lib/datadog/tracing/contrib/active_job/integration.rb +7 -7
  537. data/lib/datadog/tracing/contrib/active_job/patcher.rb +17 -5
  538. data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +2 -2
  539. data/lib/datadog/tracing/contrib/active_model_serializers/event.rb +4 -4
  540. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +7 -7
  541. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +8 -9
  542. data/lib/datadog/tracing/contrib/active_model_serializers/events.rb +3 -3
  543. data/lib/datadog/tracing/contrib/active_model_serializers/ext.rb +10 -10
  544. data/lib/datadog/tracing/contrib/active_model_serializers/integration.rb +5 -5
  545. data/lib/datadog/tracing/contrib/active_model_serializers/patcher.rb +3 -3
  546. data/lib/datadog/tracing/contrib/active_record/configuration/makara_resolver.rb +1 -1
  547. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +9 -9
  548. data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +4 -4
  549. data/lib/datadog/tracing/contrib/active_record/event.rb +1 -1
  550. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +9 -9
  551. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +12 -14
  552. data/lib/datadog/tracing/contrib/active_record/events.rb +3 -3
  553. data/lib/datadog/tracing/contrib/active_record/ext.rb +15 -15
  554. data/lib/datadog/tracing/contrib/active_record/integration.rb +11 -11
  555. data/lib/datadog/tracing/contrib/active_record/patcher.rb +2 -2
  556. data/lib/datadog/tracing/contrib/active_record/utils.rb +4 -4
  557. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +1 -1
  558. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +14 -17
  559. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +1 -1
  560. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +5 -8
  561. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +5 -5
  562. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -4
  563. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +3 -3
  564. data/lib/datadog/tracing/contrib/active_support/ext.rb +16 -16
  565. data/lib/datadog/tracing/contrib/active_support/integration.rb +8 -8
  566. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +1 -1
  567. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +2 -2
  568. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +3 -3
  569. data/lib/datadog/tracing/contrib/active_support/patcher.rb +2 -2
  570. data/lib/datadog/tracing/contrib/analytics.rb +1 -1
  571. data/lib/datadog/tracing/contrib/auto_instrument.rb +4 -4
  572. data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +3 -3
  573. data/lib/datadog/tracing/contrib/aws/ext.rb +26 -26
  574. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +7 -11
  575. data/lib/datadog/tracing/contrib/aws/integration.rb +9 -9
  576. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +1 -1
  577. data/lib/datadog/tracing/contrib/aws/patcher.rb +5 -5
  578. data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +2 -2
  579. data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +2 -2
  580. data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +4 -4
  581. data/lib/datadog/tracing/contrib/aws/service/s3.rb +2 -2
  582. data/lib/datadog/tracing/contrib/aws/service/sns.rb +3 -3
  583. data/lib/datadog/tracing/contrib/aws/service/sqs.rb +3 -3
  584. data/lib/datadog/tracing/contrib/aws/service/states.rb +5 -5
  585. data/lib/datadog/tracing/contrib/aws/services.rb +14 -14
  586. data/lib/datadog/tracing/contrib/component.rb +1 -1
  587. data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +1 -1
  588. data/lib/datadog/tracing/contrib/concurrent_ruby/configuration/settings.rb +2 -2
  589. data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +1 -1
  590. data/lib/datadog/tracing/contrib/concurrent_ruby/ext.rb +2 -2
  591. data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +1 -1
  592. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +6 -6
  593. data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +4 -4
  594. data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +1 -1
  595. data/lib/datadog/tracing/contrib/configurable.rb +20 -5
  596. data/lib/datadog/tracing/contrib/configuration/resolver.rb +14 -4
  597. data/lib/datadog/tracing/contrib/configuration/resolvers/pattern_resolver.rb +1 -1
  598. data/lib/datadog/tracing/contrib/configuration/settings.rb +2 -2
  599. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +2 -2
  600. data/lib/datadog/tracing/contrib/dalli/ext.rb +13 -13
  601. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +6 -10
  602. data/lib/datadog/tracing/contrib/dalli/integration.rb +7 -7
  603. data/lib/datadog/tracing/contrib/dalli/patcher.rb +3 -3
  604. data/lib/datadog/tracing/contrib/dalli/quantize.rb +3 -3
  605. data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +2 -2
  606. data/lib/datadog/tracing/contrib/delayed_job/ext.rb +14 -14
  607. data/lib/datadog/tracing/contrib/delayed_job/integration.rb +5 -5
  608. data/lib/datadog/tracing/contrib/delayed_job/patcher.rb +3 -3
  609. data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +7 -5
  610. data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +1 -0
  611. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +2 -2
  612. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +15 -15
  613. data/lib/datadog/tracing/contrib/elasticsearch/integration.rb +6 -6
  614. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +12 -16
  615. data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +4 -4
  616. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +2 -2
  617. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +9 -13
  618. data/lib/datadog/tracing/contrib/ethon/ext.rb +13 -13
  619. data/lib/datadog/tracing/contrib/ethon/integration.rb +6 -6
  620. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +4 -11
  621. data/lib/datadog/tracing/contrib/ethon/patcher.rb +3 -3
  622. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +5 -5
  623. data/lib/datadog/tracing/contrib/excon/ext.rb +11 -11
  624. data/lib/datadog/tracing/contrib/excon/integration.rb +6 -6
  625. data/lib/datadog/tracing/contrib/excon/middleware.rb +10 -14
  626. data/lib/datadog/tracing/contrib/excon/patcher.rb +2 -2
  627. data/lib/datadog/tracing/contrib/ext.rb +20 -18
  628. data/lib/datadog/tracing/contrib/extensions.rb +11 -2
  629. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +4 -4
  630. data/lib/datadog/tracing/contrib/faraday/ext.rb +11 -11
  631. data/lib/datadog/tracing/contrib/faraday/integration.rb +6 -6
  632. data/lib/datadog/tracing/contrib/faraday/middleware.rb +10 -14
  633. data/lib/datadog/tracing/contrib/faraday/patcher.rb +6 -6
  634. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +4 -4
  635. data/lib/datadog/tracing/contrib/grape/endpoint.rb +59 -29
  636. data/lib/datadog/tracing/contrib/grape/ext.rb +15 -15
  637. data/lib/datadog/tracing/contrib/grape/instrumentation.rb +3 -3
  638. data/lib/datadog/tracing/contrib/grape/integration.rb +5 -5
  639. data/lib/datadog/tracing/contrib/grape/patcher.rb +5 -5
  640. data/lib/datadog/tracing/contrib/graphql/configuration/error_extension_env_parser.rb +1 -1
  641. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +3 -3
  642. data/lib/datadog/tracing/contrib/graphql/ext.rb +9 -9
  643. data/lib/datadog/tracing/contrib/graphql/integration.rb +9 -9
  644. data/lib/datadog/tracing/contrib/graphql/patcher.rb +7 -7
  645. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +38 -37
  646. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +2 -2
  647. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +2 -2
  648. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +11 -15
  649. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +11 -15
  650. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +6 -6
  651. data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +1 -1
  652. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +11 -9
  653. data/lib/datadog/tracing/contrib/grpc/ext.rb +14 -14
  654. data/lib/datadog/tracing/contrib/grpc/formatting.rb +7 -7
  655. data/lib/datadog/tracing/contrib/grpc/integration.rb +5 -5
  656. data/lib/datadog/tracing/contrib/grpc/intercept_with_datadog.rb +1 -1
  657. data/lib/datadog/tracing/contrib/grpc/patcher.rb +3 -3
  658. data/lib/datadog/tracing/contrib/grpc.rb +7 -6
  659. data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +4 -3
  660. data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +2 -2
  661. data/lib/datadog/tracing/contrib/hanami/ext.rb +8 -8
  662. data/lib/datadog/tracing/contrib/hanami/integration.rb +6 -6
  663. data/lib/datadog/tracing/contrib/hanami/patcher.rb +5 -5
  664. data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +4 -3
  665. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +6 -4
  666. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +1 -1
  667. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +4 -4
  668. data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +3 -3
  669. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +10 -8
  670. data/lib/datadog/tracing/contrib/http/ext.rb +11 -11
  671. data/lib/datadog/tracing/contrib/http/instrumentation.rb +10 -14
  672. data/lib/datadog/tracing/contrib/http/integration.rb +7 -7
  673. data/lib/datadog/tracing/contrib/http/patcher.rb +3 -3
  674. data/lib/datadog/tracing/contrib/http.rb +7 -6
  675. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +4 -4
  676. data/lib/datadog/tracing/contrib/httpclient/ext.rb +11 -11
  677. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +12 -12
  678. data/lib/datadog/tracing/contrib/httpclient/integration.rb +6 -6
  679. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +3 -3
  680. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +4 -4
  681. data/lib/datadog/tracing/contrib/httprb/ext.rb +11 -11
  682. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +10 -14
  683. data/lib/datadog/tracing/contrib/httprb/integration.rb +7 -7
  684. data/lib/datadog/tracing/contrib/httprb/patcher.rb +3 -3
  685. data/lib/datadog/tracing/contrib/integration.rb +3 -3
  686. data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +2 -2
  687. data/lib/datadog/tracing/contrib/kafka/event.rb +4 -3
  688. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +7 -3
  689. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +4 -4
  690. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +4 -4
  691. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +5 -5
  692. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/join_group.rb +5 -5
  693. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/leave_group.rb +5 -5
  694. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/sync_group.rb +5 -5
  695. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +10 -5
  696. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +10 -5
  697. data/lib/datadog/tracing/contrib/kafka/events.rb +10 -10
  698. data/lib/datadog/tracing/contrib/kafka/ext.rb +40 -40
  699. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +4 -4
  700. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +6 -6
  701. data/lib/datadog/tracing/contrib/kafka/integration.rb +6 -6
  702. data/lib/datadog/tracing/contrib/kafka/patcher.rb +5 -5
  703. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +2 -2
  704. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +11 -9
  705. data/lib/datadog/tracing/contrib/karafka/ext.rb +13 -13
  706. data/lib/datadog/tracing/contrib/karafka/integration.rb +5 -5
  707. data/lib/datadog/tracing/contrib/karafka/monitor.rb +14 -14
  708. data/lib/datadog/tracing/contrib/karafka/patcher.rb +9 -9
  709. data/lib/datadog/tracing/contrib/karafka.rb +7 -6
  710. data/lib/datadog/tracing/contrib/lograge/configuration/settings.rb +2 -2
  711. data/lib/datadog/tracing/contrib/lograge/ext.rb +1 -1
  712. data/lib/datadog/tracing/contrib/lograge/integration.rb +5 -5
  713. data/lib/datadog/tracing/contrib/lograge/patcher.rb +9 -9
  714. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +2 -2
  715. data/lib/datadog/tracing/contrib/mongodb/ext.rb +18 -18
  716. data/lib/datadog/tracing/contrib/mongodb/instrumentation.rb +3 -3
  717. data/lib/datadog/tracing/contrib/mongodb/integration.rb +7 -7
  718. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +9 -9
  719. data/lib/datadog/tracing/contrib/mongodb/patcher.rb +3 -3
  720. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +18 -21
  721. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +3 -3
  722. data/lib/datadog/tracing/contrib/mysql2/ext.rb +11 -11
  723. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +7 -11
  724. data/lib/datadog/tracing/contrib/mysql2/integration.rb +5 -5
  725. data/lib/datadog/tracing/contrib/mysql2/patcher.rb +2 -2
  726. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +2 -2
  727. data/lib/datadog/tracing/contrib/opensearch/ext.rb +23 -23
  728. data/lib/datadog/tracing/contrib/opensearch/integration.rb +6 -6
  729. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +15 -19
  730. data/lib/datadog/tracing/contrib/opensearch/quantize.rb +5 -5
  731. data/lib/datadog/tracing/contrib/patchable.rb +1 -1
  732. data/lib/datadog/tracing/contrib/patcher.rb +3 -3
  733. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +3 -3
  734. data/lib/datadog/tracing/contrib/pg/ext.rb +19 -19
  735. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +19 -23
  736. data/lib/datadog/tracing/contrib/pg/integration.rb +5 -5
  737. data/lib/datadog/tracing/contrib/pg/patcher.rb +2 -2
  738. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +2 -2
  739. data/lib/datadog/tracing/contrib/presto/ext.rb +21 -21
  740. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +8 -10
  741. data/lib/datadog/tracing/contrib/presto/integration.rb +6 -6
  742. data/lib/datadog/tracing/contrib/presto/patcher.rb +4 -4
  743. data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +2 -2
  744. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +19 -16
  745. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +3 -3
  746. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +5 -5
  747. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +2 -2
  748. data/lib/datadog/tracing/contrib/que/ext.rb +19 -19
  749. data/lib/datadog/tracing/contrib/que/integration.rb +6 -6
  750. data/lib/datadog/tracing/contrib/que/patcher.rb +1 -1
  751. data/lib/datadog/tracing/contrib/que/tracer.rb +3 -2
  752. data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +3 -3
  753. data/lib/datadog/tracing/contrib/racecar/event.rb +5 -9
  754. data/lib/datadog/tracing/contrib/racecar/events/batch.rb +3 -3
  755. data/lib/datadog/tracing/contrib/racecar/events/consume.rb +3 -3
  756. data/lib/datadog/tracing/contrib/racecar/events/message.rb +3 -3
  757. data/lib/datadog/tracing/contrib/racecar/events.rb +4 -4
  758. data/lib/datadog/tracing/contrib/racecar/ext.rb +18 -18
  759. data/lib/datadog/tracing/contrib/racecar/integration.rb +5 -5
  760. data/lib/datadog/tracing/contrib/racecar/patcher.rb +3 -3
  761. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +9 -3
  762. data/lib/datadog/tracing/contrib/rack/ext.rb +43 -16
  763. data/lib/datadog/tracing/contrib/rack/header_collection.rb +3 -3
  764. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +24 -1
  765. data/lib/datadog/tracing/contrib/rack/integration.rb +6 -6
  766. data/lib/datadog/tracing/contrib/rack/middlewares.rb +28 -25
  767. data/lib/datadog/tracing/contrib/rack/patcher.rb +5 -5
  768. data/lib/datadog/tracing/contrib/rack/request_queue.rb +4 -4
  769. data/lib/datadog/tracing/contrib/rack/route_inference.rb +12 -10
  770. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +124 -6
  771. data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +1 -1
  772. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +3 -3
  773. data/lib/datadog/tracing/contrib/rails/ext.rb +15 -15
  774. data/lib/datadog/tracing/contrib/rails/framework.rb +11 -11
  775. data/lib/datadog/tracing/contrib/rails/integration.rb +5 -5
  776. data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
  777. data/lib/datadog/tracing/contrib/rails/middlewares.rb +1 -1
  778. data/lib/datadog/tracing/contrib/rails/patcher.rb +8 -9
  779. data/lib/datadog/tracing/contrib/rails/railtie.rb +4 -4
  780. data/lib/datadog/tracing/contrib/rails/runner.rb +4 -2
  781. data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +3 -3
  782. data/lib/datadog/tracing/contrib/rake/ext.rb +12 -12
  783. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +9 -7
  784. data/lib/datadog/tracing/contrib/rake/integration.rb +5 -5
  785. data/lib/datadog/tracing/contrib/rake/patcher.rb +3 -3
  786. data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +3 -3
  787. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +2 -2
  788. data/lib/datadog/tracing/contrib/redis/ext.rb +16 -16
  789. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +6 -6
  790. data/lib/datadog/tracing/contrib/redis/integration.rb +7 -7
  791. data/lib/datadog/tracing/contrib/redis/patcher.rb +10 -10
  792. data/lib/datadog/tracing/contrib/redis/quantize.rb +40 -6
  793. data/lib/datadog/tracing/contrib/redis/tags.rb +6 -11
  794. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +16 -8
  795. data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +2 -2
  796. data/lib/datadog/tracing/contrib/resque/ext.rb +7 -7
  797. data/lib/datadog/tracing/contrib/resque/integration.rb +5 -5
  798. data/lib/datadog/tracing/contrib/resque/patcher.rb +3 -3
  799. data/lib/datadog/tracing/contrib/resque/resque_job.rb +6 -5
  800. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +2 -2
  801. data/lib/datadog/tracing/contrib/rest_client/ext.rb +10 -10
  802. data/lib/datadog/tracing/contrib/rest_client/integration.rb +6 -6
  803. data/lib/datadog/tracing/contrib/rest_client/patcher.rb +2 -2
  804. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +6 -10
  805. data/lib/datadog/tracing/contrib/roda/configuration/settings.rb +2 -2
  806. data/lib/datadog/tracing/contrib/roda/ext.rb +6 -5
  807. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +8 -5
  808. data/lib/datadog/tracing/contrib/roda/integration.rb +6 -6
  809. data/lib/datadog/tracing/contrib/roda/patcher.rb +3 -3
  810. data/lib/datadog/tracing/contrib/semantic_logger/configuration/settings.rb +2 -2
  811. data/lib/datadog/tracing/contrib/semantic_logger/ext.rb +1 -1
  812. data/lib/datadog/tracing/contrib/semantic_logger/integration.rb +5 -5
  813. data/lib/datadog/tracing/contrib/semantic_logger/patcher.rb +2 -2
  814. data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +2 -2
  815. data/lib/datadog/tracing/contrib/sequel/database.rb +7 -10
  816. data/lib/datadog/tracing/contrib/sequel/dataset.rb +6 -5
  817. data/lib/datadog/tracing/contrib/sequel/ext.rb +12 -8
  818. data/lib/datadog/tracing/contrib/sequel/integration.rb +5 -5
  819. data/lib/datadog/tracing/contrib/sequel/jdbc_connection_string.rb +140 -0
  820. data/lib/datadog/tracing/contrib/sequel/patcher.rb +3 -3
  821. data/lib/datadog/tracing/contrib/sequel/utils.rb +103 -16
  822. data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +2 -2
  823. data/lib/datadog/tracing/contrib/shoryuken/ext.rb +12 -12
  824. data/lib/datadog/tracing/contrib/shoryuken/integration.rb +6 -6
  825. data/lib/datadog/tracing/contrib/shoryuken/patcher.rb +1 -1
  826. data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +3 -2
  827. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +9 -8
  828. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +2 -2
  829. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +12 -10
  830. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +30 -30
  831. data/lib/datadog/tracing/contrib/sidekiq/integration.rb +7 -7
  832. data/lib/datadog/tracing/contrib/sidekiq/patcher.rb +8 -8
  833. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/heartbeat.rb +2 -0
  834. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/job_fetch.rb +1 -0
  835. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/redis_info.rb +1 -0
  836. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/scheduled_poller.rb +2 -0
  837. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/stop.rb +1 -0
  838. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +15 -14
  839. data/lib/datadog/tracing/contrib/sidekiq/utils.rb +10 -10
  840. data/lib/datadog/tracing/contrib/sidekiq.rb +7 -6
  841. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +3 -3
  842. data/lib/datadog/tracing/contrib/sinatra/env.rb +6 -6
  843. data/lib/datadog/tracing/contrib/sinatra/ext.rb +17 -17
  844. data/lib/datadog/tracing/contrib/sinatra/framework.rb +2 -2
  845. data/lib/datadog/tracing/contrib/sinatra/integration.rb +5 -5
  846. data/lib/datadog/tracing/contrib/sinatra/patcher.rb +5 -5
  847. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +11 -10
  848. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +9 -8
  849. data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +1 -1
  850. data/lib/datadog/tracing/contrib/sneakers/ext.rb +12 -12
  851. data/lib/datadog/tracing/contrib/sneakers/integration.rb +7 -7
  852. data/lib/datadog/tracing/contrib/sneakers/patcher.rb +2 -2
  853. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +5 -4
  854. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +2 -2
  855. data/lib/datadog/tracing/contrib/status_range_env_parser.rb +2 -2
  856. data/lib/datadog/tracing/contrib/status_range_matcher.rb +4 -0
  857. data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +2 -2
  858. data/lib/datadog/tracing/contrib/stripe/ext.rb +12 -12
  859. data/lib/datadog/tracing/contrib/stripe/integration.rb +5 -5
  860. data/lib/datadog/tracing/contrib/stripe/patcher.rb +2 -2
  861. data/lib/datadog/tracing/contrib/stripe/request.rb +4 -4
  862. data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +2 -2
  863. data/lib/datadog/tracing/contrib/sucker_punch/exception_handler.rb +1 -1
  864. data/lib/datadog/tracing/contrib/sucker_punch/ext.rb +13 -13
  865. data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +5 -4
  866. data/lib/datadog/tracing/contrib/sucker_punch/integration.rb +5 -5
  867. data/lib/datadog/tracing/contrib/sucker_punch/patcher.rb +4 -4
  868. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +3 -3
  869. data/lib/datadog/tracing/contrib/trilogy/ext.rb +11 -11
  870. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +7 -11
  871. data/lib/datadog/tracing/contrib/trilogy/integration.rb +5 -5
  872. data/lib/datadog/tracing/contrib/trilogy/patcher.rb +2 -2
  873. data/lib/datadog/tracing/contrib/utils/database.rb +5 -5
  874. data/lib/datadog/tracing/contrib/utils/quantization/{hash.rb → hash_formatter.rb} +3 -3
  875. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +16 -13
  876. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +2 -2
  877. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +11 -9
  878. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +4 -4
  879. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +5 -5
  880. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +2 -2
  881. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +7 -7
  882. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +3 -3
  883. data/lib/datadog/tracing/contrib/waterdrop.rb +7 -6
  884. data/lib/datadog/tracing/contrib.rb +62 -54
  885. data/lib/datadog/tracing/correlation.rb +12 -12
  886. data/lib/datadog/tracing/diagnostics/environment_logger.rb +15 -12
  887. data/lib/datadog/tracing/diagnostics/ext.rb +19 -19
  888. data/lib/datadog/tracing/diagnostics/health.rb +1 -1
  889. data/lib/datadog/tracing/distributed/b3_multi.rb +8 -8
  890. data/lib/datadog/tracing/distributed/b3_single.rb +4 -4
  891. data/lib/datadog/tracing/distributed/baggage.rb +81 -28
  892. data/lib/datadog/tracing/distributed/datadog.rb +34 -32
  893. data/lib/datadog/tracing/distributed/fetcher.rb +1 -1
  894. data/lib/datadog/tracing/distributed/helpers.rb +3 -3
  895. data/lib/datadog/tracing/distributed/propagation.rb +46 -11
  896. data/lib/datadog/tracing/distributed/propagation_policy.rb +5 -0
  897. data/lib/datadog/tracing/distributed/trace_context.rb +29 -263
  898. data/lib/datadog/tracing/distributed/trace_state/datadog.rb +233 -0
  899. data/lib/datadog/tracing/distributed/trace_state/ext.rb +44 -0
  900. data/lib/datadog/tracing/distributed/trace_state/open_telemetry.rb +156 -0
  901. data/lib/datadog/tracing/distributed/trace_state.rb +121 -0
  902. data/lib/datadog/tracing/event.rb +2 -2
  903. data/lib/datadog/tracing/ext.rb +9 -0
  904. data/lib/datadog/tracing/metadata/analytics.rb +2 -2
  905. data/lib/datadog/tracing/metadata/errors.rb +4 -4
  906. data/lib/datadog/tracing/metadata/ext.rb +79 -66
  907. data/lib/datadog/tracing/metadata/metastruct.rb +1 -1
  908. data/lib/datadog/tracing/metadata/metastruct_tagging.rb +1 -1
  909. data/lib/datadog/tracing/metadata/tagging.rb +5 -5
  910. data/lib/datadog/tracing/metadata.rb +4 -4
  911. data/lib/datadog/tracing/otel_thread_context.rb +99 -0
  912. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  913. data/lib/datadog/tracing/pipeline.rb +3 -3
  914. data/lib/datadog/tracing/remote.rb +227 -21
  915. data/lib/datadog/tracing/sampling/all_sampler.rb +1 -1
  916. data/lib/datadog/tracing/sampling/ext.rb +9 -7
  917. data/lib/datadog/tracing/sampling/matcher.rb +4 -4
  918. data/lib/datadog/tracing/sampling/priority_sampler.rb +17 -4
  919. data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +3 -3
  920. data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +2 -2
  921. data/lib/datadog/tracing/sampling/rate_sampler.rb +2 -2
  922. data/lib/datadog/tracing/sampling/rule.rb +5 -5
  923. data/lib/datadog/tracing/sampling/rule_sampler.rb +68 -37
  924. data/lib/datadog/tracing/sampling/sampler.rb +2 -2
  925. data/lib/datadog/tracing/sampling/span/ext.rb +3 -3
  926. data/lib/datadog/tracing/sampling/span/matcher.rb +2 -2
  927. data/lib/datadog/tracing/sampling/span/rule.rb +1 -1
  928. data/lib/datadog/tracing/sampling/span/rule_parser.rb +10 -10
  929. data/lib/datadog/tracing/span.rb +9 -9
  930. data/lib/datadog/tracing/span_event.rb +5 -5
  931. data/lib/datadog/tracing/span_operation.rb +39 -26
  932. data/lib/datadog/tracing/sync_writer.rb +12 -7
  933. data/lib/datadog/tracing/trace_digest.rb +29 -4
  934. data/lib/datadog/tracing/trace_operation.rb +112 -31
  935. data/lib/datadog/tracing/trace_segment.rb +10 -10
  936. data/lib/datadog/tracing/tracer.rb +83 -29
  937. data/lib/datadog/tracing/transport/http/client.rb +4 -4
  938. data/lib/datadog/tracing/transport/http/statistics.rb +2 -2
  939. data/lib/datadog/tracing/transport/http/traces.rb +8 -8
  940. data/lib/datadog/tracing/transport/http.rb +10 -10
  941. data/lib/datadog/tracing/transport/io/client.rb +3 -3
  942. data/lib/datadog/tracing/transport/io/response.rb +1 -1
  943. data/lib/datadog/tracing/transport/io/traces.rb +5 -5
  944. data/lib/datadog/tracing/transport/io.rb +3 -3
  945. data/lib/datadog/tracing/transport/native/response.rb +71 -0
  946. data/lib/datadog/tracing/transport/native.rb +380 -0
  947. data/lib/datadog/tracing/transport/serializable_trace.rb +19 -19
  948. data/lib/datadog/tracing/transport/span_events_negotiation.rb +41 -0
  949. data/lib/datadog/tracing/transport/statistics.rb +2 -2
  950. data/lib/datadog/tracing/transport/trace_formatter.rb +23 -13
  951. data/lib/datadog/tracing/transport/traces.rb +12 -32
  952. data/lib/datadog/tracing/utils.rb +2 -2
  953. data/lib/datadog/tracing/workers.rb +4 -3
  954. data/lib/datadog/tracing/writer.rb +22 -9
  955. data/lib/datadog/tracing.rb +3 -3
  956. data/lib/datadog/version.rb +4 -4
  957. data/lib/datadog.rb +15 -11
  958. metadata +109 -23
  959. data/ext/datadog_profiling_native_extension/clock_id_noop.c +0 -21
  960. data/lib/datadog/ai_guard/configuration/settings.rb +0 -113
  961. data/lib/datadog/appsec/configuration/settings.rb +0 -423
  962. data/lib/datadog/core/utils/base64.rb +0 -22
  963. data/lib/datadog/data_streams/configuration/settings.rb +0 -49
  964. data/lib/datadog/di/configuration/settings.rb +0 -243
  965. data/lib/datadog/error_tracking/configuration/settings.rb +0 -63
  966. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +0 -82
@@ -2,42 +2,48 @@
2
2
 
3
3
  // This file exports functions used to access private Ruby VM APIs and internals.
4
4
  // To do this, it imports a few VM internal (private) headers.
5
+ // We rely on the datadog-ruby_core_source gem to get access to private VM headers; see
6
+ // https://github.com/DataDog/datadog-ruby_core_source for details.
5
7
  //
6
8
  // **Important Note**: Our medium/long-term plan is to stop relying on all private Ruby headers, and instead request and
7
9
  // contribute upstream changes so that they become official public VM APIs.
8
10
  //
9
11
  // In the meanwhile, be very careful when changing things here :)
10
12
 
11
- #ifdef RUBY_MJIT_HEADER
12
- // Pick up internal structures from the private Ruby MJIT header file
13
- #include RUBY_MJIT_HEADER
14
- #else
15
- // The MJIT header was introduced on 2.6 and removed on 3.3; for other Rubies we rely on
16
- // the datadog-ruby_core_source gem to get access to private VM headers.
13
+ #include <ruby/defines.h>
14
+
15
+ // We can't do anything about warnings in VM headers, so we just use this technique to suppress them.
16
+ // See https://nelkinda.com/blog/suppress-warnings-in-gcc-and-clang/#d11e364 for details.
17
+ #pragma GCC diagnostic push
18
+ #pragma GCC diagnostic ignored "-Wunused-parameter"
19
+ #pragma GCC diagnostic ignored "-Wattributes"
20
+ #pragma GCC diagnostic ignored "-Wpragmas"
21
+ #pragma GCC diagnostic ignored "-Wexpansion-to-defined"
22
+ #include <vm_core.h>
23
+ #pragma GCC diagnostic pop
24
+
25
+ #pragma GCC diagnostic push
26
+ #pragma GCC diagnostic ignored "-Wunused-parameter"
27
+ #include <iseq.h>
28
+ #ifdef HAVE_ZJIT_FRAME
29
+ #include <zjit.h>
30
+ #endif
31
+ #pragma GCC diagnostic pop
17
32
 
18
- // We can't do anything about warnings in VM headers, so we just use this technique to suppress them.
19
- // See https://nelkinda.com/blog/suppress-warnings-in-gcc-and-clang/#d11e364 for details.
33
+ #ifndef NO_INTERNAL_CLASS_HEADER_INCLUDE
20
34
  #pragma GCC diagnostic push
21
35
  #pragma GCC diagnostic ignored "-Wunused-parameter"
22
- #pragma GCC diagnostic ignored "-Wattributes"
23
- #pragma GCC diagnostic ignored "-Wpragmas"
24
- #pragma GCC diagnostic ignored "-Wexpansion-to-defined"
25
- #include <vm_core.h>
36
+ #include <internal/class.h>
26
37
  #pragma GCC diagnostic pop
38
+ #endif
39
+
40
+ #include <ruby.h>
27
41
 
42
+ #ifndef NO_RACTOR_HEADER_INCLUDE
28
43
  #pragma GCC diagnostic push
29
44
  #pragma GCC diagnostic ignored "-Wunused-parameter"
30
- #include <iseq.h>
45
+ #include <ractor_core.h>
31
46
  #pragma GCC diagnostic pop
32
-
33
- #include <ruby.h>
34
-
35
- #ifndef NO_RACTOR_HEADER_INCLUDE
36
- #pragma GCC diagnostic push
37
- #pragma GCC diagnostic ignored "-Wunused-parameter"
38
- #include <ractor_core.h>
39
- #pragma GCC diagnostic pop
40
- #endif
41
47
  #endif
42
48
 
43
49
  // This file can't include datadog_ruby_common.h so we replicate this here
@@ -50,18 +56,45 @@
50
56
  #define PRIVATE_VM_API_ACCESS_SKIP_RUBY_INCLUDES
51
57
  #include "private_vm_api_access.h"
52
58
 
59
+ #ifdef HAVE_RUBY_RACTOR_H
60
+ typedef rb_ractor_t gvl_owner_context_t;
61
+ #else
62
+ typedef rb_vm_t gvl_owner_context_t;
63
+ #endif
64
+
65
+ static inline const rb_callable_method_entry_t* get_cfunc_method_entry(const rb_control_frame_t *cfp);
66
+ static const rb_callable_method_entry_t* safe_vm_frame_method_entry(const rb_control_frame_t *cfp);
67
+ static gvl_owner_context_t *get_gvl_owner_context(void);
68
+
69
+ typedef struct {
70
+ gvl_owner_context_t *main_ractor_gvl_owner_context;
71
+ const rb_data_type_t *thread_data_type;
72
+ } private_vm_api_global_t;
73
+
74
+ // Initialized once on the main ractor with the GVL held, before sampling starts; never rewritten.
75
+ // The native context stays at the same address across GC compaction and forks from the main ractor.
76
+ static private_vm_api_global_t private_vm_api_global;
77
+
78
+ void private_vm_api_access_init(void) {
79
+ private_vm_api_global.thread_data_type = RTYPEDDATA_TYPE(rb_thread_current());
80
+ private_vm_api_global.main_ractor_gvl_owner_context = get_gvl_owner_context();
81
+ }
82
+
83
+ void private_vm_api_access_self_test(void) {
84
+ if (private_vm_api_global.main_ractor_gvl_owner_context != get_gvl_owner_context()) {
85
+ rb_raise(rb_eRuntimeError, "BUG: main_ractor_gvl_owner_context changed unexpectedly");
86
+ }
87
+ }
88
+
53
89
  // MRI has a similar rb_thread_ptr() function which we can't call it directly
54
90
  // because Ruby does not expose the thread_data_type publicly.
55
- // Instead, we have our own version of that function, and we lazily initialize the thread_data_type pointer
91
+ // Instead, we have our own version of that function, and we initialize the thread_data_type pointer
56
92
  // from a known-correct object: the current thread.
57
93
  //
58
94
  // Note that beyond returning the rb_thread_struct*, rb_check_typeddata() raises an exception
59
95
  // if the argument passed in is not actually a `Thread` instance.
60
96
  static inline rb_thread_t *thread_struct_from_object(VALUE thread) {
61
- static const rb_data_type_t *thread_data_type = NULL;
62
- if (UNLIKELY(thread_data_type == NULL)) thread_data_type = RTYPEDDATA_TYPE(rb_thread_current());
63
-
64
- return (rb_thread_t *) rb_check_typeddata(thread, thread_data_type);
97
+ return (rb_thread_t *) rb_check_typeddata(thread, private_vm_api_global.thread_data_type);
65
98
  }
66
99
 
67
100
  rb_nativethread_id_t pthread_id_for(VALUE thread) {
@@ -69,123 +102,131 @@ rb_nativethread_id_t pthread_id_for(VALUE thread) {
69
102
  #ifndef NO_RB_NATIVE_THREAD
70
103
  struct rb_native_thread* native_thread = thread_struct_from_object(thread)->nt;
71
104
  // This can be NULL on Ruby 3.3 with MN threads (RUBY_MN_THREADS=1)
72
- if (native_thread == NULL) return 0;
105
+ if (native_thread == NULL) {
106
+ return 0;
107
+ }
73
108
  return native_thread->thread_id;
74
109
  #else
75
110
  return thread_struct_from_object(thread)->thread_id;
76
111
  #endif
77
112
  }
78
113
 
79
- size_t sizeof_rb_iseq_t(void) {
80
- return sizeof(rb_iseq_t);
81
- }
82
-
83
- VALUE ddtrace_iseq_base_label(const void *iseq) {
84
- return rb_iseq_base_label((const rb_iseq_t *)iseq);
85
- }
86
-
87
- VALUE ddtrace_iseq_path(const void *iseq) {
88
- return rb_iseq_path((const rb_iseq_t *)iseq);
89
- }
90
-
91
- // Queries if the current thread is the owner of the global VM lock.
114
+ // Combines checking for main ractor and GVL in one check that can be done safely from a signal handler, even
115
+ // if the thread is not a Ruby thread or if we're in the middle of GC.
92
116
  //
93
- // @ivoanjo: Ruby has a similarly-named `ruby_thread_has_gvl_p` but that API is insufficient for our needs because it can
94
- // still return `true` even when a thread DOES NOT HAVE the global VM lock.
95
- // In particular, looking at the implementation, that API assumes that if a thread is not in a "blocking region" then it
96
- // will have the GVL which is probably true for the situations that API was designed to be called from BUT this assumption
97
- // does not hold true when calling `ruby_thread_has_gvl_p` from a signal handler. (Because the thread may have lost the
98
- // GVL due to a scheduler decision, not because it decided to block.)
99
- // I have also submitted https://bugs.ruby-lang.org/issues/19172 to discuss this with upstream Ruby developers.
117
+ // Note that Ruby has `ruby_thread_has_gvl_p`, but unfortunately it can return true in a signal
118
+ // handler even when the thread doesn't hold the GVL. It assumes that a thread outside a "blocking region" holds
119
+ // the GVL, but the scheduler can also take it away (https://bugs.ruby-lang.org/issues/19172).
100
120
  //
101
- // Thus we need our own gvl-checking method which actually looks at the gvl structure to determine if it is the owner.
102
- bool is_current_thread_holding_the_gvl(void) {
103
- current_gvl_owner owner = gvl_owner();
121
+ // This must not touch Ruby objects, since GC compaction may have protected their heap pages.
122
+ bool is_current_thread_in_main_ractor_and_holding_the_gvl(void) {
123
+ current_gvl_owner owner = main_ractor_gvl_owner();
104
124
  return owner.valid && pthread_equal(pthread_self(), owner.owner);
105
125
  }
106
126
 
107
127
  #ifdef HAVE_RUBY_RACTOR_H
108
- static inline rb_ractor_t *ddtrace_get_ractor(void) {
109
- #ifndef USE_RACTOR_INTERNAL_APIS_DIRECTLY // Ruby >= 3.3
110
- return thread_struct_from_object(rb_thread_current())->ractor;
111
- #else
112
- return GET_RACTOR();
113
- #endif
114
- }
128
+ static inline rb_ractor_t *ddtrace_get_ractor(void) {
129
+ #ifndef USE_RACTOR_INTERNAL_APIS_DIRECTLY // Ruby >= 3.3
130
+ return thread_struct_from_object(rb_thread_current())->ractor;
131
+ #else
132
+ return GET_RACTOR();
133
+ #endif
134
+ }
115
135
  #endif
116
136
 
117
- #ifndef NO_GVL_OWNER // Ruby < 2.6 doesn't have the owner/running field
118
- // NOTE: Reading the owner in this is a racy read, because we're not grabbing the lock that Ruby uses to protect it.
119
- //
120
- // While we could potentially grab this lock, I (@ivoanjo) think we actually don't need it because:
121
- // * In the case where a thread owns the GVL and calls `gvl_owner`, it will always see the correct value. That's
122
- // because every thread sets itself as the owner when it grabs the GVL and unsets itself at the end.
123
- // That means that `is_current_thread_holding_the_gvl` is always accurate.
124
- // * In a case where we observe a different thread, then this may change by the time we do something with this value
125
- // anyway. So unless we want to prevent the Ruby scheduler from switching threads, we need to deal with races here.
126
- current_gvl_owner gvl_owner(void) {
127
- const rb_thread_t *current_owner =
128
- #ifndef NO_RB_THREAD_SCHED // Introduced in Ruby 3.2 as a replacement for struct rb_global_vm_lock_struct
129
- ddtrace_get_ractor()->threads.sched.running;
130
- #elif HAVE_RUBY_RACTOR_H
131
- ddtrace_get_ractor()->threads.gvl.owner;
132
- #else
133
- GET_VM()->gvl.owner;
134
- #endif
135
-
136
- if (current_owner == NULL) return (current_gvl_owner) {.valid = false};
137
-
138
- #ifndef NO_RB_NATIVE_THREAD
139
- struct rb_native_thread* current_owner_native_thread = current_owner->nt;
137
+ static gvl_owner_context_t *get_gvl_owner_context(void) {
138
+ if (!ddtrace_rb_ractor_main_p()) {
139
+ rb_raise(rb_eRuntimeError, "BUG: get_gvl_owner_context must be called from the main ractor");
140
+ }
140
141
 
141
- // This can be NULL on Ruby 3.3 with MN threads (RUBY_MN_THREADS=1)
142
- if (current_owner_native_thread == NULL) return (current_gvl_owner) {.valid = false};
142
+ #ifdef HAVE_RUBY_RACTOR_H
143
+ return ddtrace_get_ractor();
144
+ #else
145
+ return GET_VM();
146
+ #endif
147
+ }
143
148
 
144
- return (current_gvl_owner) {.valid = true, .owner = current_owner_native_thread->thread_id};
149
+ #ifndef NO_GVL_OWNER // Ruby < 2.6 doesn't have the owner/running field
150
+ // NOTE: Reading the owner in this is a racy read, because we're not grabbing the lock that Ruby uses to protect it.
151
+ //
152
+ // While we could potentially grab this lock, I (@ivoanjo) think we actually don't need it because:
153
+ // * In the case where a thread owns the GVL and calls `main_ractor_gvl_owner`, it will always see the correct value. That's
154
+ // because every thread sets itself as the owner when it grabs the GVL and unsets itself at the end.
155
+ // That means that `is_current_thread_in_main_ractor_and_holding_the_gvl` is always accurate.
156
+ // * In a case where we observe a different thread, then this may change by the time we do something with this value
157
+ // anyway. So unless we want to prevent the Ruby scheduler from switching threads, we need to deal with races here.
158
+ current_gvl_owner main_ractor_gvl_owner(void) {
159
+ if (private_vm_api_global.main_ractor_gvl_owner_context == NULL) return (current_gvl_owner) {.valid = false};
160
+
161
+ const rb_thread_t *current_owner =
162
+ #ifndef NO_RB_THREAD_SCHED // Introduced in Ruby 3.2 as a replacement for struct rb_global_vm_lock_struct
163
+ private_vm_api_global.main_ractor_gvl_owner_context->threads.sched.running;
164
+ #elif HAVE_RUBY_RACTOR_H
165
+ private_vm_api_global.main_ractor_gvl_owner_context->threads.gvl.owner;
145
166
  #else
146
- return (current_gvl_owner) {.valid = true, .owner = current_owner->thread_id};
167
+ private_vm_api_global.main_ractor_gvl_owner_context->gvl.owner;
147
168
  #endif
169
+
170
+ if (current_owner == NULL) {
171
+ return (current_gvl_owner) {.valid = false};
148
172
  }
149
- #else
150
- current_gvl_owner gvl_owner(void) {
151
- rb_vm_t *vm = GET_VM();
152
173
 
153
- // BIG Issue: Ruby < 2.6 did not have the owner field. The really nice thing about the owner field is that it's
154
- // "atomic" -- when a thread sets it, it "declares" two things in a single step
155
- // * Declaration 1: Someone has the GVL
156
- // * Declaration 2: That someone is the specific thread
157
- //
158
- // Observation 1: On older versions of Ruby, this ownership concept is actually split. Specifically, `gvl.acquired`
159
- // is a boolean that represents declaration 1 above, and `vm->running_thread` (or `ruby_current_thread`/
160
- // `ruby_current_execution_context_ptr`) represents declaration 2.
161
- //
162
- // Observation 2: In addition, when a thread releases the GVL, it only sets `gvl.acquired` back to 0 **BUT CRUCIALLY
163
- // DOES NOT CHANGE THE OTHER global variables**.
164
- //
165
- // Observation 1+2 above lead to the following possible race:
166
- // * Thread A grabs the GVL (`gvl.acquired == 1`)
167
- // * Thread A sets `running_thread` (`gvl.acquired == 1` + `running_thread == Thread A`)
168
- // * Thread A releases the GVL (`gvl.acquired == 0` + `running_thread == Thread A`)
169
- // * Thread B grabs the GVL (`gvl.acquired == 1` + `running_thread == Thread A`)
170
- // * Thread A calls gvl_owner. Due to the current state (`gvl.acquired == 1` + `running_thread == Thread A`), this
171
- // function returns an incorrect result.
172
- // * Thread B finally sets `running_thread` (`gvl.acquired == 1` + `running_thread == Thread B`)
173
- //
174
- // This is especially problematic because we use `gvl_owner` to implement `is_current_thread_holding_the_gvl` which
175
- // is called in a signal handler to decide "is it safe for me to call `rb_postponed_job_register_one` or not".
176
- // (See constraints in `collectors_cpu_and_wall_time_worker.c` comments for why).
177
- //
178
- // Thus an incorrect `is_current_thread_holding_the_gvl` result may lead to issues inside `rb_postponed_job_register_one`.
179
- //
180
- // For this reason we default to use the "no signals workaround" on Ruby 2.5 by default, and we print a
181
- // warning when customers force-enable it.
182
- bool gvl_acquired = vm->gvl.acquired != 0;
183
- rb_thread_t *current_owner = vm->running_thread;
174
+ #ifndef NO_RB_NATIVE_THREAD
175
+ struct rb_native_thread* current_owner_native_thread = current_owner->nt;
184
176
 
185
- if (!gvl_acquired || current_owner == NULL) return (current_gvl_owner) {.valid = false};
177
+ // This can be NULL on Ruby 3.3 with MN threads (RUBY_MN_THREADS=1)
178
+ if (current_owner_native_thread == NULL) {
179
+ return (current_gvl_owner) {.valid = false};
180
+ }
186
181
 
182
+ return (current_gvl_owner) {.valid = true, .owner = current_owner_native_thread->thread_id};
183
+ #else
187
184
  return (current_gvl_owner) {.valid = true, .owner = current_owner->thread_id};
185
+ #endif
186
+ }
187
+ #else
188
+ current_gvl_owner main_ractor_gvl_owner(void) {
189
+ rb_vm_t *vm = private_vm_api_global.main_ractor_gvl_owner_context;
190
+ if (vm == NULL) return (current_gvl_owner) {.valid = false};
191
+
192
+ // BIG Issue: Ruby < 2.6 did not have the owner field. The really nice thing about the owner field is that it's
193
+ // "atomic" -- when a thread sets it, it "declares" two things in a single step
194
+ // * Declaration 1: Someone has the GVL
195
+ // * Declaration 2: That someone is the specific thread
196
+ //
197
+ // Observation 1: On older versions of Ruby, this ownership concept is actually split. Specifically, `gvl.acquired`
198
+ // is a boolean that represents declaration 1 above, and `vm->running_thread` (or `ruby_current_thread`/
199
+ // `ruby_current_execution_context_ptr`) represents declaration 2.
200
+ //
201
+ // Observation 2: In addition, when a thread releases the GVL, it only sets `gvl.acquired` back to 0 **BUT CRUCIALLY
202
+ // DOES NOT CHANGE THE OTHER global variables**.
203
+ //
204
+ // Observation 1+2 above lead to the following possible race:
205
+ // * Thread A grabs the GVL (`gvl.acquired == 1`)
206
+ // * Thread A sets `running_thread` (`gvl.acquired == 1` + `running_thread == Thread A`)
207
+ // * Thread A releases the GVL (`gvl.acquired == 0` + `running_thread == Thread A`)
208
+ // * Thread B grabs the GVL (`gvl.acquired == 1` + `running_thread == Thread A`)
209
+ // * Thread A calls main_ractor_gvl_owner. Due to the current state (`gvl.acquired == 1` + `running_thread == Thread A`), this
210
+ // function returns an incorrect result.
211
+ // * Thread B finally sets `running_thread` (`gvl.acquired == 1` + `running_thread == Thread B`)
212
+ //
213
+ // This is especially problematic because we use `main_ractor_gvl_owner` to implement `is_current_thread_in_main_ractor_and_holding_the_gvl` which
214
+ // is called in a signal handler to decide "is it safe for me to call `rb_postponed_job_register_one` or not".
215
+ // (See constraints in `collectors_cpu_and_wall_time_worker.c` comments for why).
216
+ //
217
+ // Thus an incorrect `is_current_thread_in_main_ractor_and_holding_the_gvl` result may lead to issues inside `rb_postponed_job_register_one`.
218
+ //
219
+ // For this reason we default to use the "no signals workaround" on Ruby 2.5 by default, and we print a
220
+ // warning when customers force-enable it.
221
+ bool gvl_acquired = vm->gvl.acquired != 0;
222
+ rb_thread_t *current_owner = vm->running_thread;
223
+
224
+ if (!gvl_acquired || current_owner == NULL) {
225
+ return (current_gvl_owner) {.valid = false};
188
226
  }
227
+
228
+ return (current_gvl_owner) {.valid = true, .owner = current_owner->thread_id};
229
+ }
189
230
  #endif // NO_GVL_OWNER
190
231
 
191
232
  // Taken from upstream vm_core.h at commit d9cf0388599a3234b9f3c06ddd006cd59a58ab8b (November 2022, Ruby 3.2 trunk)
@@ -193,7 +234,7 @@ bool is_current_thread_holding_the_gvl(void) {
193
234
  // to support tid_for (see below)
194
235
  // Modifications: None
195
236
  #if defined(__linux__) || defined(__FreeBSD__)
196
- # define RB_THREAD_T_HAS_NATIVE_ID
237
+ #define RB_THREAD_T_HAS_NATIVE_ID
197
238
  #endif
198
239
 
199
240
  uint64_t native_thread_id_for(VALUE thread) {
@@ -201,7 +242,9 @@ uint64_t native_thread_id_for(VALUE thread) {
201
242
  #if !defined(NO_THREAD_TID) && defined(RB_THREAD_T_HAS_NATIVE_ID)
202
243
  #ifndef NO_RB_NATIVE_THREAD
203
244
  struct rb_native_thread* native_thread = thread_struct_from_object(thread)->nt;
204
- if (native_thread == NULL) return 0;
245
+ if (native_thread == NULL) {
246
+ return 0;
247
+ }
205
248
  return native_thread->tid;
206
249
  #else
207
250
  return thread_struct_from_object(thread)->tid;
@@ -213,7 +256,9 @@ uint64_t native_thread_id_for(VALUE thread) {
213
256
  uint64_t result;
214
257
  // On macOS, this gives us the same identifier that shows up in activity monitor
215
258
  int error = pthread_threadid_np(pthread_id, &result);
216
- if (error) rb_syserr_fail(error, "Unexpected failure in pthread_threadid_np");
259
+ if (error) {
260
+ rb_syserr_fail(error, "Unexpected failure in pthread_threadid_np");
261
+ }
217
262
  return result;
218
263
  #else
219
264
  // Fallback, when we have nothing better (e.g. on Ruby < 3.1 on Linux)
@@ -245,11 +290,12 @@ void ddtrace_thread_list(VALUE result_array) {
245
290
  // called from a different Ractor, but I'm not sure...
246
291
  #ifdef HAVE_RUBY_RACTOR_H
247
292
  rb_ractor_t *current_ractor = ddtrace_get_ractor();
248
- ccan_list_for_each(&current_ractor->threads.set, thread, lt_node) {
293
+ ccan_list_for_each(&current_ractor->threads.set, thread, lt_node)
249
294
  #else
250
295
  rb_vm_t *vm = GET_VM();
251
- list_for_each(&vm->living_threads, thread, vmlt_node) {
296
+ list_for_each(&vm->living_threads, thread, vmlt_node)
252
297
  #endif
298
+ {
253
299
  switch (thread->status) {
254
300
  case THREAD_RUNNABLE:
255
301
  case THREAD_STOPPED:
@@ -269,6 +315,23 @@ VALUE thread_name_for(VALUE thread) {
269
315
  return thread_struct_from_object(thread)->name;
270
316
  }
271
317
 
318
+ // This test doubles as a validation for the layout of the rb_thread_t structure. Because the `name` is almost at
319
+ // the end of the structure, if somehow we can't read the name, this probably means we're getting the structure wrong
320
+ // and we should stop immediately (this includes covering the use of `stat_insn_usage` on Ruby <= 3.2).
321
+ //
322
+ // One example where this happens is in patches such as
323
+ // https://github.com/gitlabhq/omnibus-gitlab/blob/master/config/patches/ruby/thread-memory-allocations-3.2.patch
324
+ // that add items to the structure and thus shift its position from where we expect.
325
+ void self_test_thread_name_for(void) {
326
+ VALUE thread = rb_eval_string("Thread.new { Thread.current.name = 'dd-prof-test' }.join");
327
+ VALUE expected_name = rb_funcall(thread, rb_intern("name"), 0);
328
+ if (thread_name_for(thread) != expected_name) {
329
+ rb_raise(rb_eRuntimeError,
330
+ "thread_name_for() self-test failed; this usually means the `rb_thread_t` on this Ruby doesn't match what the profiler expects"
331
+ );
332
+ }
333
+ }
334
+
272
335
  // -----------------------------------------------------------------------------
273
336
  // The sources below are modified versions of code extracted from the Ruby project.
274
337
  // Each function is annotated with its origin, why we imported it, and the changes made.
@@ -317,63 +380,71 @@ VALUE thread_name_for(VALUE thread) {
317
380
  #pragma GCC diagnostic push
318
381
  #pragma GCC diagnostic ignored "-Wunused-parameter"
319
382
  static inline int
320
- calc_pos(const rb_iseq_t *iseq, const VALUE *pc, int *lineno, int *node_id)
321
- {
322
- VM_ASSERT(iseq);
323
- VM_ASSERT(ISEQ_BODY(iseq));
324
- VM_ASSERT(ISEQ_BODY(iseq)->iseq_encoded);
325
- VM_ASSERT(ISEQ_BODY(iseq)->iseq_size);
326
- if (! pc) {
327
- if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_TOP) {
328
- VM_ASSERT(! ISEQ_BODY(iseq)->local_table);
329
- VM_ASSERT(! ISEQ_BODY(iseq)->local_table_size);
330
- return 0;
331
- }
332
- # ifndef NO_INT_FIRST_LINENO // Ruby 3.2+
333
- if (lineno) *lineno = ISEQ_BODY(iseq)->location.first_lineno;
334
- # else
335
- if (lineno) *lineno = FIX2INT(ISEQ_BODY(iseq)->location.first_lineno);
336
- #endif
383
+ calc_pos(const rb_iseq_t *iseq, const VALUE *pc, int *lineno, int *node_id) {
384
+ VM_ASSERT(iseq);
385
+ VM_ASSERT(ISEQ_BODY(iseq));
386
+ VM_ASSERT(ISEQ_BODY(iseq)->iseq_encoded);
387
+ VM_ASSERT(ISEQ_BODY(iseq)->iseq_size);
388
+ if (pc == NULL) {
389
+ if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_TOP) {
390
+ VM_ASSERT(! ISEQ_BODY(iseq)->local_table);
391
+ VM_ASSERT(! ISEQ_BODY(iseq)->local_table_size);
392
+ return 0;
393
+ }
394
+ #ifndef NO_INT_FIRST_LINENO // Ruby 3.2+
395
+ if (lineno) {
396
+ *lineno = ISEQ_BODY(iseq)->location.first_lineno;
397
+ }
398
+ #else
399
+ if (lineno) {
400
+ *lineno = FIX2INT(ISEQ_BODY(iseq)->location.first_lineno);
401
+ }
402
+ #endif
337
403
  #ifdef USE_ISEQ_NODE_ID
338
- if (node_id) *node_id = -1;
339
- #endif
340
- return 1;
404
+ if (node_id) {
405
+ *node_id = -1;
341
406
  }
342
- else {
343
- ptrdiff_t n = pc - ISEQ_BODY(iseq)->iseq_encoded;
344
- VM_ASSERT(n <= ISEQ_BODY(iseq)->iseq_size);
345
- VM_ASSERT(n >= 0);
346
- ASSUME(n >= 0);
347
- size_t pos = n; /* no overflow */
348
- if (LIKELY(pos)) {
349
- /* use pos-1 because PC points next instruction at the beginning of instruction */
350
- pos--;
351
- }
407
+ #endif
408
+ return 1;
409
+ } else {
410
+ ptrdiff_t n = pc - ISEQ_BODY(iseq)->iseq_encoded;
411
+ VM_ASSERT(n <= ISEQ_BODY(iseq)->iseq_size);
412
+ VM_ASSERT(n >= 0);
413
+ size_t pos = n; /* no overflow */
414
+ if (LIKELY(pos)) {
415
+ /* use pos-1 because PC points next instruction at the beginning of instruction */
416
+ pos--;
417
+ } else {
352
418
  #if VMDEBUG && defined(HAVE_BUILTIN___BUILTIN_TRAP)
353
- else {
354
- /* SDR() is not possible; that causes infinite loop. */
355
- rb_print_backtrace();
356
- __builtin_trap();
357
- }
419
+ /* SDR() is not possible; that causes infinite loop. */
420
+ rb_print_backtrace();
421
+ __builtin_trap();
358
422
  #endif
423
+ }
359
424
 
360
- // In PROF-11475 we spotted a crash when calling `rb_iseq_line_no` from this method.
361
- // We were only able to reproduce this issue on Ruby 2.6 and 2.7, not 2.5 or the 3.x series (tried 3.0, 3.2 and 3.4).
362
- // Note that going out of bounds doesn't crash every time, as usual with C we may just read garbage or get lucky.
363
- //
364
- // For those problematic Rubies, we observed that when we try to take a sample in the middle of processing the
365
- // VM `LEAVE` instruction, the value of `n` can violate the documented assumptions above and be
366
- // `n > ISEQ_BODY(iseq)->iseq_size)`.
367
- //
368
- // To work around this and any other potential issues, we validate here that the bytecode position is sane.
369
- if (RB_UNLIKELY(n < 0 || n > ISEQ_BODY(iseq)->iseq_size)) return 0;
370
-
371
- if (lineno) *lineno = rb_iseq_line_no(iseq, pos);
425
+ // In PROF-11475 we spotted a crash when calling `rb_iseq_line_no` from this method.
426
+ // We were only able to reproduce this issue on Ruby 2.6 and 2.7, not 2.5 or the 3.x series (tried 3.0, 3.2 and 3.4).
427
+ // Note that going out of bounds doesn't crash every time, as usual with C we may just read garbage or get lucky.
428
+ //
429
+ // For those problematic Rubies, we observed that when we try to take a sample in the middle of processing the
430
+ // VM `LEAVE` instruction, the value of `n` can violate the documented assumptions above and be
431
+ // `n > ISEQ_BODY(iseq)->iseq_size)`.
432
+ //
433
+ // To work around this and any other potential issues, we validate here that the bytecode position is sane.
434
+ if (RB_UNLIKELY(n < 0 || n > ISEQ_BODY(iseq)->iseq_size)) {
435
+ return 0;
436
+ }
437
+
438
+ if (lineno) {
439
+ *lineno = rb_iseq_line_no(iseq, pos);
440
+ }
372
441
  #ifdef USE_ISEQ_NODE_ID
373
- if (node_id) *node_id = rb_iseq_node_id(iseq, pos);
374
- #endif
375
- return 1;
442
+ if (node_id) {
443
+ *node_id = rb_iseq_node_id(iseq, pos);
376
444
  }
445
+ #endif
446
+ return 1;
447
+ }
377
448
  }
378
449
  #pragma GCC diagnostic pop
379
450
 
@@ -382,11 +453,12 @@ calc_pos(const rb_iseq_t *iseq, const VALUE *pc, int *lineno, int *node_id)
382
453
  // to support our custom rb_profile_frames (see below)
383
454
  // Modifications: None
384
455
  static inline int
385
- calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
386
- {
387
- int lineno;
388
- if (calc_pos(iseq, pc, &lineno, NULL)) return lineno;
389
- return 0;
456
+ calc_lineno(const rb_iseq_t *iseq, const VALUE *pc) {
457
+ int lineno;
458
+ if (calc_pos(iseq, pc, &lineno, NULL)) {
459
+ return lineno;
460
+ }
461
+ return 0;
390
462
  }
391
463
 
392
464
  // Taken from upstream vm_backtrace.c at commit 5f10bd634fb6ae8f74a4ea730176233b0ca96954 (March 2022, Ruby 3.2 trunk)
@@ -445,183 +517,211 @@ calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
445
517
  // disagree, and quite a few of them seem oversights/bugs (speculation from my part) rather than deliberate
446
518
  // decisions.
447
519
  int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, frame_info *stack_buffer) {
448
- int i;
449
- // Modified from upstream: Instead of using `GET_EC` to collect info from the current thread,
450
- // support sampling any thread (including the current) passed as an argument
451
- rb_thread_t *th = thread_struct_from_object(thread);
452
- const rb_execution_context_t *ec = th->ec;
520
+ int i;
521
+ // Modified from upstream: Instead of using `GET_EC` to collect info from the current thread,
522
+ // support sampling any thread (including the current) passed as an argument
523
+ rb_thread_t *th = thread_struct_from_object(thread);
524
+ const rb_execution_context_t *ec = th->ec;
525
+
526
+ // As of this writing, we don't support profiling with MN enabled, and this only happens in that mode, but as we
527
+ // probably want to experiment with it in the future, I've decided to import https://github.com/ruby/ruby/pull/9310
528
+ // here.
529
+ if (ec == NULL) {
530
+ return 0;
531
+ }
532
+
533
+ // Avoid sampling dead threads
534
+ if (th->status == THREAD_KILLED) {
535
+ return 0;
536
+ }
537
+
538
+ const rb_control_frame_t *cfp = ec->cfp;
539
+
540
+ // This happens on newly-created threads (we even had a flaky test because of it)
541
+ if (cfp == NULL) {
542
+ return PLACEHOLDER_STACK_IN_NATIVE_CODE;
543
+ }
453
544
 
454
- // As of this writing, we don't support profiling with MN enabled, and this only happens in that mode, but as we
455
- // probably want to experiment with it in the future, I've decided to import https://github.com/ruby/ruby/pull/9310
456
- // here.
457
- if (ec == NULL) return 0;
545
+ // I suspect this won't happen for ddtrace, but just-in-case we've imported a potential fix for
546
+ // https://github.com/ruby/ruby/pull/13643 by assuming that these can be NULL/zero with the cfp being non-NULL yet.
547
+ if (ec->vm_stack == NULL || ec->vm_stack_size == 0) {
548
+ return 0;
549
+ }
458
550
 
459
- // Avoid sampling dead threads
460
- if (th->status == THREAD_KILLED) return 0;
551
+ const rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
552
+ #ifndef NO_JIT_RETURN
553
+ const rb_control_frame_t *top = cfp;
554
+ #endif
555
+ const rb_callable_method_entry_t *cme;
461
556
 
462
- const rb_control_frame_t *cfp = ec->cfp;
557
+ // `vm_backtrace.c` includes this check in several methods. This happens on newly-created threads, and may
558
+ // also (not entirely sure) happen on dead threads
559
+ if (end_cfp == NULL) {
560
+ return PLACEHOLDER_STACK_IN_NATIVE_CODE;
561
+ }
463
562
 
464
- // This happens on newly-created threads (we even had a flaky test because of it)
465
- if (cfp == NULL) return PLACEHOLDER_STACK_IN_NATIVE_CODE;
563
+ // Fix: Skip dummy frame that shows up in main thread.
564
+ //
565
+ // According to a comment in `backtrace_each` (`vm_backtrace.c`), there's two dummy frames that we should ignore
566
+ // at the base of every thread's stack.
567
+ // (see https://github.com/ruby/ruby/blob/4bd38e8120f2fdfdd47a34211720e048502377f1/vm_backtrace.c#L890-L914 )
568
+ //
569
+ // One is being pointed to by `RUBY_VM_END_CONTROL_FRAME(ec)`, and so we need to advance to the next one, and
570
+ // reaching it will be used as a condition to break out of the loop below.
571
+ //
572
+ // Note that in `backtrace_each` there's two calls to `RUBY_VM_NEXT_CONTROL_FRAME`, but the loop bounds there
573
+ // are computed in a different way, so the two calls really are equivalent to one here.
574
+ end_cfp = RUBY_VM_NEXT_CONTROL_FRAME(end_cfp);
466
575
 
467
- // I suspect this won't happen for ddtrace, but just-in-case we've imported a potential fix for
468
- // https://github.com/ruby/ruby/pull/13643 by assuming that these can be NULL/zero with the cfp being non-NULL yet.
469
- if (ec->vm_stack == NULL || ec->vm_stack_size == 0) return 0;
576
+ // See comment on `record_placeholder_stack_in_native_code` for a full explanation of what this means (and why we don't just return 0)
577
+ if (end_cfp <= cfp) {
578
+ return PLACEHOLDER_STACK_IN_NATIVE_CODE;
579
+ }
470
580
 
471
- const rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
472
- #ifndef NO_JIT_RETURN
473
- const rb_control_frame_t *top = cfp;
581
+ // This is the position just after the top of the stack -- e.g. where a new frame pushed on the stack would end up.
582
+ const rb_control_frame_t *top_sentinel = RUBY_VM_NEXT_CONTROL_FRAME(cfp);
583
+
584
+ // We iterate the stack from bottom (beginning of thread) to the top (currently-active frame). This is different
585
+ // from upstream rb_profile_frames, but actually matches what `backtrace_each` does (yes, different Ruby VM APIs
586
+ // iterate in different directions).
587
+ // We do this to better take advantage of the `same_frame` caching mechanism: By starting from the bottom of the
588
+ // stack towards the top, we can usually keep most of the stack intact when the code is only going up and down
589
+ // a few methods at the top. Before this change, the cache was really only useful if between samples the app had
590
+ // not moved from the current stack, as adding or removing one frame would invalidate the existing cache (because
591
+ // every position would shift).
592
+ cfp = RUBY_VM_NEXT_CONTROL_FRAME(end_cfp);
593
+
594
+ for (i=0; i<limit && cfp != top_sentinel; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
595
+ const VALUE *pc = cfp->pc;
596
+ #ifdef HAVE_ZJIT_FRAME
597
+ const rb_iseq_t *iseq = cfp->_iseq;
598
+ #else
599
+ const rb_iseq_t *iseq = cfp->iseq;
474
600
  #endif
475
- const rb_callable_method_entry_t *cme;
476
601
 
477
- // `vm_backtrace.c` includes this check in several methods. This happens on newly-created threads, and may
478
- // also (not entirely sure) happen on dead threads
479
- if (end_cfp == NULL) return PLACEHOLDER_STACK_IN_NATIVE_CODE;
602
+ #if defined(HAVE_ZJIT_FRAME) && USE_ZJIT
603
+ // Based on CFP_ZJIT_FRAME, which we currently can't use directly.
604
+ if (CFP_ZJIT_FRAME_P(cfp)) {
605
+ if ((VALUE)cfp->jit_return == ZJIT_JIT_RETURN_C_FRAME) {
606
+ // ZJIT uses this sentinel for C method frames, which have no Ruby ISEQ or bytecode PC.
607
+ // Set both to NULL explicitly because ZJIT leaves those fields untouched, so they may contain stale data.
608
+ iseq = NULL;
609
+ pc = NULL;
610
+ } else {
611
+ const zjit_jit_frame_t *jit_frame = (const zjit_jit_frame_t *)((VALUE *)cfp->jit_return)[-1];
612
+ iseq = jit_frame->iseq;
613
+ pc = jit_frame->pc;
614
+ }
615
+ }
616
+ #endif
480
617
 
481
- // Fix: Skip dummy frame that shows up in main thread.
482
- //
483
- // According to a comment in `backtrace_each` (`vm_backtrace.c`), there's two dummy frames that we should ignore
484
- // at the base of every thread's stack.
485
- // (see https://github.com/ruby/ruby/blob/4bd38e8120f2fdfdd47a34211720e048502377f1/vm_backtrace.c#L890-L914 )
486
- //
487
- // One is being pointed to by `RUBY_VM_END_CONTROL_FRAME(ec)`, and so we need to advance to the next one, and
488
- // reaching it will be used as a condition to break out of the loop below.
489
- //
490
- // Note that in `backtrace_each` there's two calls to `RUBY_VM_NEXT_CONTROL_FRAME`, but the loop bounds there
491
- // are computed in a different way, so the two calls really are equivalent to one here.
492
- end_cfp = RUBY_VM_NEXT_CONTROL_FRAME(end_cfp);
493
-
494
- // See comment on `record_placeholder_stack_in_native_code` for a full explanation of what this means (and why we don't just return 0)
495
- if (end_cfp <= cfp) return PLACEHOLDER_STACK_IN_NATIVE_CODE;
496
-
497
- // This is the position just after the top of the stack -- e.g. where a new frame pushed on the stack would end up.
498
- const rb_control_frame_t *top_sentinel = RUBY_VM_NEXT_CONTROL_FRAME(cfp);
499
-
500
- // We iterate the stack from bottom (beginning of thread) to the top (currently-active frame). This is different
501
- // from upstream rb_profile_frames, but actually matches what `backtrace_each` does (yes, different Ruby VM APIs
502
- // iterate in different directions).
503
- // We do this to better take advantage of the `same_frame` caching mechanism: By starting from the bottom of the
504
- // stack towards the top, we can usually keep most of the stack intact when the code is only going up and down
505
- // a few methods at the top. Before this change, the cache was really only useful if between samples the app had
506
- // not moved from the current stack, as adding or removing one frame would invalidate the existing cache (because
507
- // every position would shift).
508
- cfp = RUBY_VM_NEXT_CONTROL_FRAME(end_cfp);
509
-
510
- for (i=0; i<limit && cfp != top_sentinel; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
511
- if (cfp->iseq && !cfp->pc) {
512
- // Fix: Do nothing -- this frame should not be used
513
- //
514
- // rb_profile_frames does not do this check, but `backtrace_each` (`vm_backtrace.c`) does. This frame is not
515
- // exposed by the Ruby backtrace APIs and for now we want to match its behavior 1:1
618
+ #ifndef NO_T_MOVED
619
+ if (iseq && RB_TYPE_P((VALUE) iseq, T_MOVED)) {
620
+ // The profiler is not supposed to sample during GC compaction, so T_MOVED is not expected here.
621
+ // Yet, crash tracking also uses this walker and may run at any time. For now, we choose to skip these frames.
622
+ continue;
623
+ }
624
+ #endif
625
+
626
+ if (iseq && !pc) {
627
+ // Fix: Do nothing -- this frame should not be used
628
+ //
629
+ // rb_profile_frames does not do this check, but `backtrace_each` (`vm_backtrace.c`) does. This frame is not
630
+ // exposed by the Ruby backtrace APIs and for now we want to match its behavior 1:1
631
+ } else if (cfp->ep == NULL) {
632
+ // Do nothing -- this frame should not be used
633
+ //
634
+ // We're not sure this can ever happen, but we've seen a crash inside `VM_FRAME_RUBYFRAME_P` below (which
635
+ // dereferences `cfp->ep`), so "just in case" we're adding this extra sanity check to avoid crashing on a
636
+ // NULL `ep`.
637
+ } else if (VM_FRAME_RUBYFRAME_P(cfp)) {
638
+ if (start > 0) {
639
+ start--;
640
+ continue;
641
+ }
642
+
643
+ cme = safe_vm_frame_method_entry(cfp);
644
+
645
+ // Upstream (Ruby 4.0) does:
646
+ // if (cme && cme->def->type == VM_METHOD_TYPE_ISEQ) {
647
+ // buff[i] = (VALUE)cme;
648
+ // } else {
649
+ // buff[i] = (VALUE)cfp->iseq;
650
+ // }
651
+ // We get both the iseq and CME because we need both to format like Ruby backtraces
652
+
653
+ stack_buffer[i].same_frame =
654
+ stack_buffer[i].is_ruby_frame &&
655
+ stack_buffer[i].as.ruby_frame.iseq == iseq &&
656
+ stack_buffer[i].as.ruby_frame.caching_pc == pc &&
657
+ stack_buffer[i].cme == cme;
658
+
659
+ if (stack_buffer[i].same_frame) { // Nothing to do, buffer already contains this frame
660
+ i++;
661
+ continue;
662
+ }
663
+
664
+ stack_buffer[i].as.ruby_frame.iseq = iseq;
665
+ stack_buffer[i].as.ruby_frame.caching_pc = (void *) pc;
666
+ stack_buffer[i].cme = cme;
667
+
668
+ // The topmost frame may not have an updated PC because the JIT
669
+ // may not have set one. The JIT compiler will update the PC
670
+ // before entering a new function (so that `caller` will work),
671
+ // so only the topmost frame could possibly have an out of date PC
672
+ #ifndef NO_JIT_RETURN
673
+ if (cfp == top && cfp->jit_return) {
674
+ stack_buffer[i].as.ruby_frame.line = 0;
675
+ } else {
676
+ stack_buffer[i].as.ruby_frame.line = calc_lineno(iseq, pc);
516
677
  }
517
- else if (VM_FRAME_RUBYFRAME_P(cfp)) {
518
- if (start > 0) {
519
- start--;
520
- continue;
521
- }
522
-
523
- stack_buffer[i].same_frame =
524
- stack_buffer[i].is_ruby_frame &&
525
- stack_buffer[i].as.ruby_frame.iseq == (VALUE) cfp->iseq &&
526
- stack_buffer[i].as.ruby_frame.caching_pc == cfp->pc;
527
-
528
- if (stack_buffer[i].same_frame) { // Nothing to do, buffer already contains this frame
529
- i++;
530
- continue;
531
- }
532
-
533
- // dd-trace-rb NOTE:
534
- // Upstream Ruby has code here to retrieve the rb_callable_method_entry_t (cme) and in some cases to use it
535
- // instead of the iseq.
536
- // In practice, they are usually the same; the difference is that when you have e.g. block, one gets you a
537
- // reference to the block, and the other to the method containing the block.
538
- // This would be important if we used `rb_profile_frame_label` and wanted the "block in foo" label instead
539
- // of just "foo". But we're currently using `rb_profile_frame_base_label` which I believe is always the same
540
- // between the rb_callable_method_entry_t and the iseq. Thus, to simplify a bit our logic and reduce a bit
541
- // the overhead, we always use the iseq here.
542
- //
543
- // @ivoanjo: I've left the upstream Ruby code commented out below for reference, so it's more obvious that
544
- // we're diverging, and we can easily compare and experiment with the upstream version in the future.
545
- //
546
- // cme = rb_vm_frame_method_entry(cfp);
547
-
548
- // if (cme && cme->def->type == VM_METHOD_TYPE_ISEQ &&
549
- // // Fix: Do not use callable method entry when iseq is for an eval.
550
- // // TL;DR: This fix is needed for us to match the Ruby reference API information in the
551
- // // "when sampling an eval/instance eval inside an object" spec.
552
- // cfp->iseq->body->type != ISEQ_TYPE_EVAL) {
553
- // buff[i] = (VALUE)cme;
554
- // }
555
- // else {
556
- stack_buffer[i].as.ruby_frame.iseq = (VALUE)cfp->iseq;
557
- stack_buffer[i].as.ruby_frame.caching_pc = (void *) cfp->pc;
558
- // }
559
-
560
- // The topmost frame may not have an updated PC because the JIT
561
- // may not have set one. The JIT compiler will update the PC
562
- // before entering a new function (so that `caller` will work),
563
- // so only the topmost frame could possibly have an out of date PC
564
- #ifndef NO_JIT_RETURN
565
- if (cfp == top && cfp->jit_return) {
566
- stack_buffer[i].as.ruby_frame.line = 0;
567
- } else {
568
- stack_buffer[i].as.ruby_frame.line = calc_lineno(cfp->iseq, cfp->pc);
569
- }
570
- #else // Ruby < 3.1
571
- stack_buffer[i].as.ruby_frame.line = calc_lineno(cfp->iseq, cfp->pc);
572
- #endif
573
-
574
- stack_buffer[i].is_ruby_frame = true;
575
- i++;
678
+ #else // Ruby < 3.1
679
+ stack_buffer[i].as.ruby_frame.line = calc_lineno(iseq, pc);
680
+ #endif
681
+
682
+ stack_buffer[i].is_ruby_frame = true;
683
+ i++;
684
+ } else {
685
+ cme = get_cfunc_method_entry(cfp);
686
+ if (cme && cme->def->type == VM_METHOD_TYPE_CFUNC) {
687
+ if (start > 0) {
688
+ start--;
689
+ continue;
576
690
  }
577
- else {
578
- cme = rb_vm_frame_method_entry(cfp);
579
- if (cme && cme->def->type == VM_METHOD_TYPE_CFUNC) {
580
- if (start > 0) {
581
- start--;
582
- continue;
583
- }
584
-
585
- stack_buffer[i].same_frame =
586
- !stack_buffer[i].is_ruby_frame &&
587
- stack_buffer[i].as.native_frame.caching_cme == (VALUE) cme;
588
-
589
- if (stack_buffer[i].same_frame) { // Nothing to do, buffer already contains this frame
590
- i++;
591
- continue;
592
- }
593
-
594
- stack_buffer[i].as.native_frame.caching_cme = (VALUE)cme;
595
- stack_buffer[i].as.native_frame.method_id = cme->def->original_id;
596
- stack_buffer[i].as.native_frame.function = cme->def->body.cfunc.func;
597
- stack_buffer[i].is_ruby_frame = false;
598
- i++;
599
- }
691
+
692
+ stack_buffer[i].same_frame =
693
+ !stack_buffer[i].is_ruby_frame &&
694
+ stack_buffer[i].cme == cme;
695
+
696
+ if (stack_buffer[i].same_frame) { // Nothing to do, buffer already contains this frame
697
+ i++;
698
+ continue;
600
699
  }
700
+
701
+ stack_buffer[i].cme = cme;
702
+ stack_buffer[i].is_ruby_frame = false;
703
+ i++;
704
+ }
601
705
  }
706
+ }
602
707
 
603
- return i;
708
+ return i;
604
709
  }
605
710
 
606
- // Support code for older Rubies that cannot use the MJIT header
607
- #ifndef RUBY_MJIT_HEADER
608
-
609
- #define MJIT_STATIC // No-op on older Rubies
610
-
611
711
  // Taken from upstream include/ruby/backward/2/bool.h at commit 5f10bd634fb6ae8f74a4ea730176233b0ca96954 (March 2022, Ruby 3.2 trunk)
612
712
  // Copyright (C) Ruby developers <ruby-core@ruby-lang.org>
613
713
  // to support our custom rb_profile_frames (see above)
614
714
  // Modifications: None
615
715
  #ifndef FALSE
616
- # define FALSE false
716
+ #define FALSE false
617
717
  #elif FALSE
618
- # error FALSE must be false
718
+ #error FALSE must be false
619
719
  #endif
620
720
 
621
721
  #ifndef TRUE
622
- # define TRUE true
722
+ #define TRUE true
623
723
  #elif ! TRUE
624
- # error TRUE must be true
724
+ #error TRUE must be true
625
725
  #endif
626
726
 
627
727
  // Taken from upstream vm_insnhelper.c at commit 5f10bd634fb6ae8f74a4ea730176233b0ca96954 (March 2022, Ruby 3.2 trunk)
@@ -629,87 +729,104 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, frame_info *st
629
729
  // to support our custom rb_profile_frames (see above)
630
730
  // Modifications:
631
731
  // * Removed debug checks (they were ifdef'd out anyway)
732
+ // * Optimized to avoid recursion
632
733
  static rb_callable_method_entry_t *
633
- check_method_entry(VALUE obj, int can_be_svar)
634
- {
635
- if (obj == Qfalse) return NULL;
636
-
637
- switch (imemo_type(obj)) {
638
- case imemo_ment:
639
- return (rb_callable_method_entry_t *)obj;
640
- case imemo_cref:
641
- return NULL;
642
- case imemo_svar:
643
- if (can_be_svar) {
644
- return check_method_entry(((struct vm_svar *)obj)->cref_or_me, FALSE);
645
- }
646
- // fallthrough
647
- default:
648
- return NULL;
734
+ check_method_entry(VALUE obj, int can_be_svar) {
735
+ if (obj == Qfalse) {
736
+ return NULL;
737
+ }
738
+
739
+ enum imemo_type type = imemo_type(obj);
740
+ if (type == imemo_ment) {
741
+ return (rb_callable_method_entry_t *)obj;
742
+ } else if (can_be_svar && type == imemo_svar) {
743
+ VALUE cref_or_me = ((struct vm_svar *)obj)->cref_or_me;
744
+ if (imemo_type(cref_or_me) == imemo_ment) {
745
+ return (rb_callable_method_entry_t *)cref_or_me;
649
746
  }
747
+ }
748
+
749
+ return NULL;
650
750
  }
651
751
 
652
- // Taken from upstream vm_insnhelper.c at commit 5f10bd634fb6ae8f74a4ea730176233b0ca96954 (March 2022, Ruby 3.2 trunk)
653
- // Copyright (C) 2007 Koichi Sasada
654
- // to support our custom rb_profile_frames (see above)
752
+ // Identical to upstream rb_vm_frame_method_entry (vm_insnhelper.c) with two additions:
753
+ // 1. FIXNUM_P check on ep[FLAGS] before each iteration to detect torn EPs
754
+ // 2. NULL check on ep after VM_ENV_PREV_EP
655
755
  //
656
- // While older Rubies may have this function, the symbol is not exported which leads to dynamic loader issues, e.g.
657
- // `dyld: lazy symbol binding failed: Symbol not found: _rb_vm_frame_method_entry`.
658
- //
659
- // Modifications: None
660
- MJIT_STATIC const rb_callable_method_entry_t *
661
- rb_vm_frame_method_entry(const rb_control_frame_t *cfp)
662
- {
663
- const VALUE *ep = cfp->ep;
664
- rb_callable_method_entry_t *me;
665
-
666
- while (!VM_ENV_LOCAL_P(ep)) {
667
- if ((me = check_method_entry(ep[VM_ENV_DATA_INDEX_ME_CREF], FALSE)) != NULL) return me;
668
- ep = VM_ENV_PREV_EP(ep);
756
+ // When the profiler's signal handler interrupts vm_make_env_each (vm.c) mid-escape,
757
+ // a child frame's SPECVAL can still point to the parent's old stack EP whose flags
758
+ // slot has been overwritten with (VALUE)env for GC marking.
759
+ static const rb_callable_method_entry_t *
760
+ safe_vm_frame_method_entry(const rb_control_frame_t *cfp) {
761
+ const VALUE *ep = cfp->ep;
762
+ rb_callable_method_entry_t *me;
763
+
764
+ // Torn-EP check before VM_ENV_LOCAL_P, check_method_entry, and VM_ENV_PREV_EP
765
+ // dereference ep
766
+ while (FIXNUM_P(ep[VM_ENV_DATA_INDEX_FLAGS]) && !VM_ENV_LOCAL_P(ep)) {
767
+ if ((me = check_method_entry(ep[VM_ENV_DATA_INDEX_ME_CREF], FALSE)) != NULL) {
768
+ return me;
669
769
  }
770
+ ep = VM_ENV_PREV_EP(ep);
771
+ if (ep == NULL) {
772
+ return NULL;
773
+ }
774
+ }
670
775
 
671
- return check_method_entry(ep[VM_ENV_DATA_INDEX_ME_CREF], TRUE);
776
+ // If we exited because of a torn EP (failed FIXNUM_P), bail out
777
+ if (!FIXNUM_P(ep[VM_ENV_DATA_INDEX_FLAGS])) {
778
+ return NULL;
779
+ }
780
+
781
+ return check_method_entry(ep[VM_ENV_DATA_INDEX_ME_CREF], TRUE);
782
+ }
783
+
784
+ // Optimized version of rb_vm_frame_method_entry() for cfunc frames.
785
+ // Cfunc frames always have VM_ENV_FLAG_LOCAL set, so ep[-2] is the cme directly
786
+ // with no EP chain walk needed. The only complication is cfuncs that set $~ or $_
787
+ // (e.g. Regexp#match, String#gsub) which replace ep[-2] with an imemo_svar wrapping
788
+ // the cme - in that case svar->cref_or_me is always the cme (never a cref).
789
+ static inline const rb_callable_method_entry_t *
790
+ get_cfunc_method_entry(const rb_control_frame_t *cfp) {
791
+ return check_method_entry(cfp->ep[VM_ENV_DATA_INDEX_ME_CREF], TRUE);
672
792
  }
673
- #endif // RUBY_MJIT_HEADER
674
793
 
675
794
  #ifndef NO_RACTORS
676
- // This API and definition are exported as a public symbol by the VM BUT the function header is not defined in any public header, so we
677
- // repeat it here to be able to use in our code.
678
- #ifndef USE_RACTOR_INTERNAL_APIS_DIRECTLY
679
- // Disable fast path for detecting multiple Ractors. Unfortunately this symbol is no longer visible on modern Ruby
680
- // versions, so we need to do a bit more work.
681
- struct rb_ractor_struct *ruby_single_main_ractor = NULL;
682
-
683
- // Alternative implementation of rb_ractor_main_p_ that avoids relying on non-public symbols
684
- bool rb_ractor_main_p_(void) {
685
- // We need to get the main ractor in a bit of a roundabout way, since Ruby >= 3.3 hid `GET_VM()`
686
- return ddtrace_get_ractor() == thread_struct_from_object(rb_thread_current())->vm->ractor.main_ractor;
687
- }
688
- #else
689
- // Directly access Ruby internal fast path for detecting multiple Ractors.
690
- extern struct rb_ractor_struct *ruby_single_main_ractor;
795
+ // This API and definition are exported as a public symbol by the VM BUT the function header is not defined in any public header, so we
796
+ // repeat it here to be able to use in our code.
797
+ #ifndef USE_RACTOR_INTERNAL_APIS_DIRECTLY
798
+ // Disable fast path for detecting multiple Ractors. Unfortunately this symbol is no longer visible on modern Ruby
799
+ // versions, so we need to do a bit more work.
800
+ struct rb_ractor_struct *ruby_single_main_ractor = NULL;
801
+
802
+ // Alternative implementation of rb_ractor_main_p_ that avoids relying on non-public symbols
803
+ bool rb_ractor_main_p_(void) {
804
+ // We need to get the main ractor in a bit of a roundabout way, since Ruby >= 3.3 hid `GET_VM()`
805
+ return ddtrace_get_ractor() == thread_struct_from_object(rb_thread_current())->vm->ractor.main_ractor;
806
+ }
807
+ #else
808
+ // Directly access Ruby internal fast path for detecting multiple Ractors.
809
+ extern struct rb_ractor_struct *ruby_single_main_ractor;
691
810
 
692
- // Ruby 3.0 to 3.2 directly expose this symbol, we just need to tell the compiler it exists.
693
- bool rb_ractor_main_p_(void);
694
- #endif
811
+ // Ruby 3.0 to 3.2 directly expose this symbol, we just need to tell the compiler it exists.
812
+ bool rb_ractor_main_p_(void);
813
+ #endif
695
814
 
696
- // Taken from upstream ractor_core.h at commit d9cf0388599a3234b9f3c06ddd006cd59a58ab8b (November 2022, Ruby 3.2 trunk)
697
- // to allow us to ensure that we're always operating on the main ractor (if Ruby has ractors)
698
- // Modifications:
699
- // * None
700
- bool ddtrace_rb_ractor_main_p(void)
701
- {
702
- if (ruby_single_main_ractor) {
703
- return true;
704
- }
705
- else {
706
- return rb_ractor_main_p_();
707
- }
815
+ // Taken from upstream ractor_core.h at commit d9cf0388599a3234b9f3c06ddd006cd59a58ab8b (November 2022, Ruby 3.2 trunk)
816
+ // to allow us to ensure that we're always operating on the main ractor (if Ruby has ractors)
817
+ // Modifications:
818
+ // * None
819
+ bool ddtrace_rb_ractor_main_p(void) {
820
+ if (ruby_single_main_ractor) {
821
+ return true;
822
+ } else {
823
+ return rb_ractor_main_p_();
708
824
  }
825
+ }
709
826
  #else
710
- // Simplify callers on older Rubies, instead of having them probe if the VM supports Ractors we just tell them that yes
711
- // they're always on the main Ractor
712
- bool ddtrace_rb_ractor_main_p(void) { return true; }
827
+ // Simplify callers on older Rubies, instead of having them probe if the VM supports Ractors we just tell them that yes
828
+ // they're always on the main Ractor
829
+ bool ddtrace_rb_ractor_main_p(void) { return true; }
713
830
  #endif // NO_RACTORS
714
831
 
715
832
  // This is a tweaked and inlined version of
@@ -721,17 +838,23 @@ static const rb_iseq_t *maybe_thread_invoke_proc_iseq(VALUE thread_value) {
721
838
  rb_thread_t *thread = thread_struct_from_object(thread_value);
722
839
 
723
840
  #ifndef NO_THREAD_INVOKE_ARG // Ruby 2.6+
724
- if (thread->invoke_type != thread_invoke_type_proc) return NULL;
841
+ if (thread->invoke_type != thread_invoke_type_proc) {
842
+ return NULL;
843
+ }
725
844
 
726
845
  VALUE proc = thread->invoke_arg.proc.proc;
727
846
  #else
728
- if (thread->first_func || !thread->first_proc) return NULL;
847
+ if (thread->first_func || !thread->first_proc) {
848
+ return NULL;
849
+ }
729
850
 
730
851
  VALUE proc = thread->first_proc;
731
852
  #endif
732
853
 
733
854
  const rb_iseq_t *iseq = rb_proc_get_iseq(proc, 0);
734
- if (iseq == NULL) return NULL;
855
+ if (iseq == NULL) {
856
+ return NULL;
857
+ }
735
858
 
736
859
  rb_iseq_check(iseq);
737
860
  return iseq;
@@ -740,10 +863,12 @@ static const rb_iseq_t *maybe_thread_invoke_proc_iseq(VALUE thread_value) {
740
863
  VALUE invoke_location_for(VALUE thread, int *line_location) {
741
864
  const rb_iseq_t *iseq = maybe_thread_invoke_proc_iseq(thread);
742
865
 
743
- if (iseq == NULL) return Qnil;
866
+ if (iseq == NULL) {
867
+ return Qnil;
868
+ }
744
869
 
745
870
  *line_location = NUM2INT(rb_iseq_first_lineno(iseq));
746
- return rb_iseq_path(iseq);
871
+ return ddtrace_iseq_path(iseq);
747
872
  }
748
873
 
749
874
  void self_test_mn_enabled(void) {
@@ -756,139 +881,351 @@ void self_test_mn_enabled(void) {
756
881
  #endif
757
882
  }
758
883
 
759
- // Taken from upstream imemo.h at commit 6ebcf25de2859b5b6402b7e8b181066c32d0e0bf (November 2023, master branch)
760
- // (See the Ruby project copyright and license above)
761
- // to enable calling rb_imemo_name
762
- //
763
- // Modifications:
764
- // * Added IMEMO_MASK define
765
- // * Changed return type to int to avoid having to define `enum imemo_type`
766
- static inline int ddtrace_imemo_type(VALUE imemo) {
767
- // This mask is the same between Ruby 2.5 and 3.3-preview3. Furthermore, the intention of this method is to be used
768
- // to call `rb_imemo_name` which correctly handles invalid numbers so even if the mask changes in the future, at most
769
- // we'll get incorrect results (and never a VM crash)
770
- #define IMEMO_MASK 0x0f
771
- return (RBASIC(imemo)->flags >> FL_USHIFT) & IMEMO_MASK;
772
- }
773
-
774
884
  // Safety: This function assumes the object passed in is of the imemo type. But in the worst case, you'll just get
775
885
  // a string that doesn't make any sense.
776
886
  #ifndef NO_IMEMO_NAME
777
- const char *imemo_kind(VALUE imemo) {
778
- return rb_imemo_name(ddtrace_imemo_type(imemo));
779
- }
887
+ const char *imemo_kind(VALUE imemo) {
888
+ return rb_imemo_name(imemo_type(imemo));
889
+ }
780
890
  #else
781
- const char *imemo_kind(__attribute__((unused)) VALUE imemo) {
782
- return NULL;
783
- }
891
+ const char *imemo_kind(__attribute__((unused)) VALUE imemo) {
892
+ return NULL;
893
+ }
784
894
  #endif
785
895
 
786
896
  // This is used to workaround a VM bug. See "handle_sampling_signal" in "collectors_cpu_and_wall_time_worker" for details.
787
897
  #ifdef NO_POSTPONED_TRIGGER
788
- void *objspace_ptr_for_gc_finalize_deferred_workaround(void) {
789
- return GET_VM()->objspace;
898
+ void *objspace_ptr_for_gc_finalize_deferred_workaround(void) {
899
+ return GET_VM()->objspace;
900
+ }
901
+ #endif
902
+
903
+ #ifndef HAVE_RUBY_THREAD_STORAGE_API
904
+ #include "gvl_profiling_helper.h"
905
+
906
+ // Hack: In Ruby 3.3+ we attach gvl profiling state to Ruby threads using the
907
+ // rb_internal_thread_specific_* APIs. These APIs did not exist on Ruby <= 3.2. On Ruby <= 3.2 we instead store the
908
+ // needed data inside the `rb_thread_t` structure, specifically in `stat_insn_usage` as a Ruby FIXNUM.
909
+ //
910
+ // Why `stat_insn_usage`? We needed some per-thread storage, and while looking at the Ruby VM sources I noticed
911
+ // that `stat_insn_usage` has been in `rb_thread_t` for a long time, but is not used anywhere in the VM
912
+ // code. There's a comment attached to it "/* statistics data for profiler */" but other than marking this
913
+ // field for GC, I could not find any place in the VM commit history or on GitHub where this has ever been used.
914
+ //
915
+ // Thus, since this hack is only for Ruby <= 3.2, which presumably will never see this field either removed or used
916
+ // we... kinda take it for our own usage. It's ugly, I know...
917
+ //
918
+ // 64-bit pointers actually use 48-bit virtual addresses (https://muxup.com/2023q4/storing-data-in-pointers),
919
+ // so we are sure the addresses fit in Fixnums.
920
+ per_thread_context *get_per_thread_context(VALUE thread) {
921
+ VALUE current_value = thread_struct_from_object(thread)->stat_insn_usage;
922
+ return RB_FIXNUM_P(current_value) ? (per_thread_context *) FIX2LONG(current_value) : NULL;
923
+ }
924
+
925
+ void set_per_thread_context(VALUE thread, per_thread_context *value) {
926
+ if (!RB_FIXABLE((intptr_t) value)) {
927
+ rb_bug("per_thread_context pointer does not fit in a Fixnum: %p", value);
790
928
  }
929
+ thread_struct_from_object(thread)->stat_insn_usage = value ? LONG2FIX((intptr_t) value) : Qfalse;
930
+ }
791
931
  #endif
792
932
 
793
- #ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS // Ruby 3.2
794
- #include "gvl_profiling_helper.h"
933
+ // Is the VM smack in the middle of raising an exception?
934
+ bool is_raised_flag_set(VALUE thread) { return thread_struct_from_object(thread)->ec->raised_flag > 0; }
795
935
 
796
- gvl_profiling_thread thread_from_thread_object(VALUE thread) {
797
- return (gvl_profiling_thread) {.thread = thread_struct_from_object(thread)};
936
+ #ifndef NO_CURRENT_FIBER_FOR
937
+ // The following three declarations are all
938
+ // taken from upstream cont.c at commit d97884a58be32e829fd03a80cd521f4733d65c79 (February 2025, master branch)
939
+ // (See the Ruby project copyright and license above)
940
+ // to enable building `current_fiber_for`.
941
+ //
942
+ // We needed to copy them because they aren't otherwise exposed in any VM APIs or headers.
943
+ // @ivoanjo: I manually checked the Ruby 3.1, 3.2, 3.3 and 3.4 branches + master, and the parts we care about in these
944
+ // structures have not changed in many years (in fact, last change I spotted was for 2.7).
945
+ enum context_type {
946
+ CONTINUATION_CONTEXT = 0,
947
+ FIBER_CONTEXT = 1
948
+ };
949
+
950
+ typedef struct rb_context_struct { // This declaration is incomplete -- only contains up to `self` which is the part we care about
951
+ enum context_type type;
952
+ int argc;
953
+ int kw_splat;
954
+ VALUE self;
955
+ } rb_context_t;
956
+
957
+ struct rb_fiber_struct { // This declaration is incomplete -- only contains the first entry which is the part we care about
958
+ rb_context_t cont;
959
+ };
960
+
961
+ VALUE current_fiber_for(VALUE thread) {
962
+ VALUE self = thread_struct_from_object(thread)->ec->fiber_ptr->cont.self;
963
+ return self == 0 ? Qnil : self;
964
+ }
965
+
966
+ void self_test_current_fiber_for(void) {
967
+ VALUE expected_current_fiber = current_fiber_for(rb_thread_current());
968
+ VALUE actual_current_fiber = rb_fiber_current();
969
+
970
+ if (expected_current_fiber == Qnil) {
971
+ // On purpose above we tried reading before calling `rb_fiber_current()` so the fiber may have not existed yet.
972
+ // But now it should be there.
973
+ expected_current_fiber = current_fiber_for(rb_thread_current());
798
974
  }
799
975
 
800
- // Hack: In Ruby 3.3+ we attach gvl profiling state to Ruby threads using the
801
- // rb_internal_thread_specific_* APIs. These APIs did not exist on Ruby 3.2. On Ruby 3.2 we instead store the
802
- // needed data inside the `rb_thread_t` structure, specifically in `stat_insn_usage` as a Ruby FIXNUM.
803
- //
804
- // Why `stat_insn_usage`? We needed some per-thread storage, and while looking at the Ruby VM sources I noticed
805
- // that `stat_insn_usage` has been in `rb_thread_t` for a long time, but is not used anywhere in the VM
806
- // code. There's a comment attached to it "/* statistics data for profiler */" but other than marking this
807
- // field for GC, I could not find any place in the VM commit history or on GitHub where this has ever been used.
808
- //
809
- // Thus, since this hack is only for 3.2, which presumably will never see this field either removed or used
810
- // during its remaining maintenance release period we... kinda take it for our own usage. It's ugly, I know...
811
- intptr_t gvl_profiling_state_get(gvl_profiling_thread thread) {
812
- if (thread.thread == NULL) return 0;
976
+ if (expected_current_fiber != actual_current_fiber) {
977
+ rb_raise(rb_eRuntimeError, "current_fiber_for() self-test failed");
978
+ }
979
+ }
980
+ #else
981
+ NORETURN(VALUE current_fiber_for(DDTRACE_UNUSED VALUE thread));
982
+
983
+ VALUE current_fiber_for(DDTRACE_UNUSED VALUE thread) { rb_raise(rb_eRuntimeError, "Not implemented for Ruby < 3.1"); }
984
+ void self_test_current_fiber_for(void) { } // Nothing to do
985
+ #endif
813
986
 
814
- VALUE current_value = ((rb_thread_t *)thread.thread)->stat_insn_usage;
815
- intptr_t result = current_value == Qnil ? 0 : FIX2LONG(current_value);
816
- return result;
987
+ // Variant of functions related to Thread::Backtrace::Location#label in Ruby 4.0
988
+ // (location_label(), rb_gen_method_name(), calculate_iseq_label()),
989
+ // but formatting using a C buffer instead of allocating Ruby Strings.
990
+ // Also, those functions are static functions in vm_backtrace.c so not reusable.
991
+ // There is also AFAIK no reasonable way to create a rb_backtrace_location_t from outside that file.
992
+
993
+ // Return true if a given location is a C method or supposed to behave like one.
994
+ static bool location_cfunc_p(const rb_callable_method_entry_t *cme) {
995
+ if (!cme) {
996
+ return false;
817
997
  }
818
998
 
819
- void gvl_profiling_state_set(gvl_profiling_thread thread, intptr_t value) {
820
- if (thread.thread == NULL) return;
821
- ((rb_thread_t *)thread.thread)->stat_insn_usage = LONG2FIX(value);
999
+ switch (cme->def->type) {
1000
+ case VM_METHOD_TYPE_CFUNC:
1001
+ return true;
1002
+ // Upstream, but we don't want that behavior:
1003
+ // case VM_METHOD_TYPE_ISEQ:
1004
+ // return is_internal_location(loc->cme->def->body.iseq.iseqptr);
1005
+ default:
1006
+ return false;
822
1007
  }
1008
+ }
823
1009
 
824
- // Because Ruby 3.2 does not give us the current thread when calling the RUBY_INTERNAL_THREAD_EVENT_READY and
825
- // RUBY_INTERNAL_THREAD_EVENT_RESUMED APIs, we need to figure out this info ourselves.
826
- //
827
- // Specifically, this method was created to be called from a RUBY_INTERNAL_THREAD_EVENT_RESUMED callback --
828
- // when it's triggered, we know the thread the code gets executed on is holding the GVL, so we use this
829
- // opportunity to initialize our thread-local value.
830
- gvl_profiling_thread gvl_profiling_state_maybe_initialize(void) {
831
- gvl_profiling_thread current_thread = gvl_waiting_tls;
832
-
833
- if (current_thread.thread == NULL) {
834
- // threads.sched.running is the thread currently holding the GVL, which when this gets executed is the
835
- // current thread!
836
- current_thread = (gvl_profiling_thread) {.thread = (void *) rb_current_ractor()->threads.sched.running};
837
- gvl_waiting_tls = current_thread;
1010
+ // RCLASS_SINGLETON_P() is only defined on Ruby 3.4+, so we have a copy for older versions
1011
+ static bool ddtrace_RCLASS_SINGLETON_P(VALUE klass) {
1012
+ return RB_TYPE_P(klass, T_CLASS) && FL_TEST_RAW(klass, FL_SINGLETON);
1013
+ }
1014
+
1015
+ static VALUE get_class_attached_object(VALUE klass) {
1016
+ #ifndef NO_CLASS_ATTACHED_OBJECT
1017
+ return rb_class_attached_object(klass);
1018
+ #else
1019
+ return rb_ivar_get(klass, rb_intern("__attached__"));
1020
+ #endif
1021
+ }
1022
+
1023
+ static bool is_metaclass(VALUE mod, VALUE* attached) {
1024
+ if (ddtrace_RCLASS_SINGLETON_P(mod)) {
1025
+ VALUE attached_object = get_class_attached_object(mod);
1026
+ if (RB_TYPE_P(attached_object, T_CLASS) || RB_TYPE_P(attached_object, T_MODULE)) {
1027
+ *attached = attached_object;
1028
+ return true;
838
1029
  }
1030
+ }
1031
+ return false;
1032
+ }
839
1033
 
840
- return current_thread;
1034
+ VALUE ddtrace_alloc_free_rb_mod_name(VALUE mod) {
1035
+ #ifdef NO_ALLOC_FREE_MOD_NAME
1036
+ VALUE name = rb_attr_get(mod, rb_intern("__classpath__"));
1037
+ #else
1038
+ VALUE name = rb_mod_name(mod);
1039
+ #endif
1040
+ // While Module#const_set rejects empty strings,
1041
+ // an empty String is possible if `rb_const_set(mod, "", val)` was used
1042
+ // but that's not understandable so consider those anonymous too.
1043
+ if (name == Qnil || RSTRING_LEN(name) == 0) {
1044
+ return Qnil;
1045
+ } else {
1046
+ return name;
841
1047
  }
1048
+ }
1049
+
1050
+ // Ruby 3.3+ has a `permanent_classpath` flag on rb_classext_struct.
1051
+ // Checking for '#' in the name is equivalent on older Rubies:
1052
+ // non-permanent names contain '#' from `#<Module:0x...>` or `#<Class:0x...>` prefixes.
1053
+ static bool has_permanent_classpath(DDTRACE_UNUSED VALUE mod, DDTRACE_UNUSED VALUE mod_name) {
1054
+ #if defined(RCLASS_EXT_PRIME) // 4.0+
1055
+ // We can't actually use RCLASS_PERMANENT_CLASSPATH_P(mod) because that uses
1056
+ // RCLASS_EXT_READABLE() which uses rb_current_box(), which is a private symbol:
1057
+ // https://github.com/DataDog/dd-trace-rb/issues/6338
1058
+ // Instead we use `RCLASS_EXT_PRIME(mod)->permanent_classpath`, which is equivalent for our usage:
1059
+ // the two can only differ for boxable (= core) modules with no permanent name, i.e. metaclasses like
1060
+ // String.singleton_class, and is_metaclass() already replaced those with the attached module, so we display
1061
+ // `String.foo` and not `#<Class:String>#foo`. Same for the name in ddtrace_alloc_free_rb_mod_name().
1062
+ return RCLASS_EXT_PRIME(mod)->permanent_classpath;
1063
+ #elif defined(HAVE_PERMANENT_CLASSPATH) // 3.3 - 3.4
1064
+ return RCLASS_EXT(mod)->permanent_classpath;
1065
+ #else // 3.2 and older
1066
+ return memchr(RSTRING_PTR(mod_name), '#', RSTRING_LEN(mod_name)) == NULL;
842
1067
  #endif
1068
+ }
843
1069
 
844
- // Is the VM smack in the middle of raising an exception?
845
- bool is_raised_flag_set(VALUE thread) { return thread_struct_from_object(thread)->ec->raised_flag > 0; }
1070
+ VALUE ddtrace_permanent_mod_name(VALUE mod) {
1071
+ VALUE name = ddtrace_alloc_free_rb_mod_name(mod);
846
1072
 
847
- #ifndef NO_CURRENT_FIBER_FOR
848
- // The following three declarations are all
849
- // taken from upstream cont.c at commit d97884a58be32e829fd03a80cd521f4733d65c79 (February 2025, master branch)
850
- // (See the Ruby project copyright and license above)
851
- // to enable building `current_fiber_for`.
852
- //
853
- // We needed to copy them because they aren't otherwise exposed in any VM APIs or headers.
854
- // @ivoanjo: I manually checked the Ruby 3.1, 3.2, 3.3 and 3.4 branches + master, and the parts we care about in these
855
- // structures have not changed in many years (in fact, last change I spotted was for 2.7).
856
- enum context_type {
857
- CONTINUATION_CONTEXT = 0,
858
- FIBER_CONTEXT = 1
859
- };
860
-
861
- typedef struct rb_context_struct { // This declaration is incomplete -- only contains up to `self` which is the part we care about
862
- enum context_type type;
863
- int argc;
864
- int kw_splat;
865
- VALUE self;
866
- } rb_context_t;
867
-
868
- struct rb_fiber_struct { // This declaration is incomplete -- only contains the first entry which is the part we care about
869
- rb_context_t cont;
870
- };
871
-
872
- VALUE current_fiber_for(VALUE thread) {
873
- VALUE self = thread_struct_from_object(thread)->ec->fiber_ptr->cont.self;
874
- return self == 0 ? Qnil : self;
875
- }
876
-
877
- void self_test_current_fiber_for(void) {
878
- VALUE expected_current_fiber = current_fiber_for(rb_thread_current());
879
- VALUE actual_current_fiber = rb_fiber_current();
880
-
881
- if (expected_current_fiber == Qnil) {
882
- // On purpose above we tried reading before calling `rb_fiber_current()` so the fiber may have not existed yet.
883
- // But now it should be there.
884
- expected_current_fiber = current_fiber_for(rb_thread_current());
885
- }
1073
+ if (NIL_P(name) || !has_permanent_classpath(mod, name)) {
1074
+ return Qnil;
1075
+ } else {
1076
+ return name;
1077
+ }
1078
+ }
1079
+
1080
+ #define ONLY_METHOD_NAME ((ssize_t) -1)
1081
+ #define BUFFER_OUT_OF_SPACE ((ssize_t) -2)
1082
+ #define NO_METHOD_NAME ((ssize_t) -3)
886
1083
 
887
- if (expected_current_fiber != actual_current_fiber) rb_raise(rb_eRuntimeError, "current_fiber_for() self-test failed");
1084
+ static ssize_t rb_gen_method_name(VALUE owner, VALUE method_name, char *buf, size_t buf_size) {
1085
+ if (!(RB_TYPE_P(owner, T_CLASS) || RB_TYPE_P(owner, T_MODULE))) {
1086
+ return ONLY_METHOD_NAME;
888
1087
  }
889
- #else
890
- NORETURN(VALUE current_fiber_for(DDTRACE_UNUSED VALUE thread));
891
1088
 
892
- VALUE current_fiber_for(DDTRACE_UNUSED VALUE thread) { rb_raise(rb_eRuntimeError, "Not implemented for Ruby < 3.1"); }
893
- void self_test_current_fiber_for(void) { } // Nothing to do
1089
+ VALUE mod = owner;
1090
+ char separator = '#';
1091
+ if (is_metaclass(owner, &mod)) {
1092
+ separator = '.';
1093
+ }
1094
+
1095
+ VALUE mod_name = ddtrace_permanent_mod_name(mod);
1096
+
1097
+ // Exclude non-permanent names (e.g. `#<Module:0x0123>::Foo`) which break flamegraph aggregation
1098
+ // since they contain addresses that differ across processes/runs.
1099
+ if (NIL_P(mod_name)) {
1100
+ return ONLY_METHOD_NAME;
1101
+ }
1102
+
1103
+ size_t mod_name_len = RSTRING_LEN(mod_name);
1104
+ size_t method_name_len = RSTRING_LEN(method_name);
1105
+ size_t total = mod_name_len + 1 + method_name_len;
1106
+ if (total <= buf_size) {
1107
+ memcpy(buf, RSTRING_PTR(mod_name), mod_name_len);
1108
+ buf[mod_name_len] = separator;
1109
+ memcpy(buf + mod_name_len + 1, RSTRING_PTR(method_name), method_name_len);
1110
+ return total;
1111
+ } else {
1112
+ return BUFFER_OUT_OF_SPACE;
1113
+ }
1114
+ }
1115
+
1116
+ static ssize_t calculate_iseq_label(VALUE owner, const rb_iseq_t *iseq, char *buf, size_t buf_size) {
1117
+ switch (ISEQ_BODY(iseq)->type) {
1118
+ case ISEQ_TYPE_TOP:
1119
+ case ISEQ_TYPE_CLASS:
1120
+ case ISEQ_TYPE_MAIN:
1121
+ // Just the method name, not `Object#<top (required)>`
1122
+ return ONLY_METHOD_NAME;
1123
+
1124
+ case ISEQ_TYPE_METHOD:
1125
+ return rb_gen_method_name(owner, ISEQ_BODY(iseq)->location.label, buf, buf_size);
1126
+
1127
+ #ifdef ISEQ_TYPE_PLAIN /* 2.5 does not have it */
1128
+ case ISEQ_TYPE_PLAIN:
894
1129
  #endif
1130
+ case ISEQ_TYPE_BLOCK: {
1131
+ // CRuby uses "block in Foo#bar" and ""block (N levels) in Foo#bar" in backtraces.
1132
+ // We use "Foo#bar (block)" because it's much faster and looks better in the UI.
1133
+ const rb_iseq_t* method_iseq = ISEQ_BODY(iseq)->local_iseq;
1134
+
1135
+ static const char suffix[] = " (block)";
1136
+ size_t suffix_len = sizeof(suffix) - 1;
1137
+
1138
+ // Only use the result if there is enough space for both parts
1139
+ ssize_t written = calculate_iseq_label(owner, method_iseq, buf, buf_size);
1140
+ if (written > 0 && (written + suffix_len) <= buf_size) {
1141
+ memcpy(buf + written, suffix, suffix_len);
1142
+ return written + suffix_len;
1143
+ } else if (written == ONLY_METHOD_NAME) {
1144
+ VALUE method_name = rb_iseq_base_label(method_iseq);
1145
+ size_t method_name_len = RSTRING_LEN(method_name);
1146
+ if ((method_name_len + suffix_len) <= buf_size) {
1147
+ memcpy(buf, RSTRING_PTR(method_name), method_name_len);
1148
+ memcpy(buf + method_name_len, suffix, suffix_len);
1149
+ return method_name_len + suffix_len;
1150
+ }
1151
+ }
1152
+
1153
+ return BUFFER_OUT_OF_SPACE;
1154
+ }
1155
+
1156
+ case ISEQ_TYPE_RESCUE:
1157
+ case ISEQ_TYPE_ENSURE:
1158
+ case ISEQ_TYPE_EVAL:
1159
+ iseq = ISEQ_BODY(iseq)->parent_iseq;
1160
+ return calculate_iseq_label(owner, iseq, buf, buf_size);
1161
+
1162
+ default:
1163
+ // Upstream: rb_bug("calculate_iseq_label: unreachable");
1164
+ return ONLY_METHOD_NAME;
1165
+ }
1166
+ }
1167
+
1168
+ // MRI: location_label()
1169
+ // C methods don't have an iseq, only Ruby methods have
1170
+ ssize_t ddtrace_location_label(const rb_callable_method_entry_t *cme, const rb_iseq_t *iseq, char *buf, size_t buf_size) {
1171
+ if (location_cfunc_p(cme)) {
1172
+ VALUE method_name = rb_id2str(cme->def->original_id);
1173
+ if (method_name == Qfalse) {
1174
+ return NO_METHOD_NAME;
1175
+ }
1176
+ return rb_gen_method_name(cme->owner, method_name, buf, buf_size);
1177
+ } else {
1178
+ VALUE owner = cme ? cme->owner : Qnil;
1179
+ return calculate_iseq_label(owner, iseq, buf, buf_size);
1180
+ }
1181
+ }
1182
+
1183
+ // Returns a String or Qfalse (like rb_id2str())
1184
+ VALUE ddtrace_location_base_label(const rb_callable_method_entry_t *cme, const rb_iseq_t *iseq) {
1185
+ if (location_cfunc_p(cme)) {
1186
+ return rb_id2str(cme->def->original_id);
1187
+ } else {
1188
+ return rb_iseq_base_label(iseq);
1189
+ }
1190
+ }
1191
+
1192
+ // Always returns a String
1193
+ VALUE ddtrace_iseq_base_label(const rb_iseq_t *iseq) {
1194
+ return rb_iseq_base_label(iseq);
1195
+ }
1196
+
1197
+ // Always returns a String
1198
+ // See https://github.com/ruby/ruby/blob/75aeb225b8558ff908ea78bf608dfbba09bdc2f9/iseq.c#L564-L565
1199
+ VALUE ddtrace_iseq_path(const rb_iseq_t *iseq) {
1200
+ return rb_iseq_path(iseq);
1201
+ }
1202
+
1203
+ size_t sizeof_rb_iseq_t(void) {
1204
+ return sizeof(rb_iseq_t);
1205
+ }
1206
+
1207
+ size_t sizeof_rb_callable_method_entry_t(void) {
1208
+ return sizeof(rb_callable_method_entry_t);
1209
+ }
1210
+
1211
+ void* ddtrace_cme_cfunc_func(const rb_callable_method_entry_t *cme) {
1212
+ return cme->def->body.cfunc.func;
1213
+ }
1214
+
1215
+ const char *ddtrace_cme_original_method_name(const rb_callable_method_entry_t *cme) {
1216
+ return rb_id2name(cme->def->original_id);
1217
+ }
1218
+
1219
+ // This function is not present in the VM headers, but is a public symbol that can be invoked.
1220
+ int rb_objspace_internal_object_p(VALUE obj);
1221
+
1222
+ bool ddtrace_is_internal_object_p(VALUE obj) {
1223
+ if (RB_SPECIAL_CONST_P(obj)) {
1224
+ // Ruby special constants are not internal, except Qundef.
1225
+ // See enum ruby_special_consts in CRuby.
1226
+ return obj == Qundef;
1227
+ } else {
1228
+ // rb_objspace_internal_object_p() assumes non-immediate, so check that first above
1229
+ return rb_objspace_internal_object_p(obj);
1230
+ }
1231
+ }