datadog 2.36.0 → 2.40.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 (854) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +119 -1
  3. data/bin/ddprofrb +4 -4
  4. data/ext/datadog_profiling_native_extension/clock_id_from_mach.c +5 -12
  5. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +105 -18
  6. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +17 -3
  7. data/ext/datadog_profiling_native_extension/collectors_stack.c +228 -162
  8. data/ext/datadog_profiling_native_extension/collectors_stack.h +16 -3
  9. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +357 -217
  10. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +3 -0
  11. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +5 -4
  12. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +19 -11
  13. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +7 -0
  14. data/ext/datadog_profiling_native_extension/extconf.rb +24 -11
  15. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +261 -62
  16. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +17 -9
  17. data/ext/datadog_profiling_native_extension/ruby_helpers.c +7 -0
  18. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +2 -0
  19. data/ext/datadog_profiling_native_extension/time_helpers.h +12 -7
  20. data/ext/libdatadog_api/datadog_ruby_common.c +19 -11
  21. data/ext/libdatadog_api/datadog_ruby_common.h +7 -0
  22. data/ext/libdatadog_api/di.c +76 -0
  23. data/ext/libdatadog_api/extconf.rb +29 -23
  24. data/ext/libdatadog_api/feature_flags.c +26 -0
  25. data/ext/libdatadog_api/init.c +4 -0
  26. data/ext/libdatadog_api/trace_exporter.c +974 -0
  27. data/ext/libdatadog_api/trace_exporter.h +5 -0
  28. data/ext/libdatadog_extconf_helpers.rb +9 -9
  29. data/lib/datadog/ai_guard/api_client.rb +8 -6
  30. data/lib/datadog/ai_guard/component.rb +10 -10
  31. data/lib/datadog/ai_guard/evaluation.rb +1 -0
  32. data/lib/datadog/ai_guard/ext.rb +2 -0
  33. data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +16 -16
  34. data/lib/datadog/appsec/actions_handler.rb +6 -6
  35. data/lib/datadog/appsec/anonymizer.rb +1 -1
  36. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +1 -1
  37. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +6 -6
  38. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +1 -1
  39. data/lib/datadog/appsec/api_security/route_extractor.rb +18 -15
  40. data/lib/datadog/appsec/api_security/sampler.rb +5 -5
  41. data/lib/datadog/appsec/api_security.rb +1 -1
  42. data/lib/datadog/appsec/assets.rb +7 -7
  43. data/lib/datadog/appsec/autoload.rb +3 -3
  44. data/lib/datadog/appsec/component.rb +15 -15
  45. data/lib/datadog/appsec/compressed_json.rb +6 -6
  46. data/lib/datadog/appsec/configuration.rb +73 -58
  47. data/lib/datadog/appsec/context.rb +9 -7
  48. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +6 -6
  49. data/lib/datadog/appsec/contrib/active_record/integration.rb +4 -4
  50. data/lib/datadog/appsec/contrib/active_record/patcher.rb +4 -4
  51. data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +7 -7
  52. data/lib/datadog/appsec/contrib/aws_lambda/integration.rb +2 -2
  53. data/lib/datadog/appsec/contrib/aws_lambda/patcher.rb +2 -2
  54. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +36 -34
  55. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +3 -3
  56. data/lib/datadog/appsec/contrib/devise/ext.rb +20 -20
  57. data/lib/datadog/appsec/contrib/devise/integration.rb +4 -4
  58. data/lib/datadog/appsec/contrib/devise/patcher.rb +8 -8
  59. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +12 -12
  60. data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +8 -8
  61. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +24 -9
  62. data/lib/datadog/appsec/contrib/excon/integration.rb +4 -4
  63. data/lib/datadog/appsec/contrib/excon/patcher.rb +1 -1
  64. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +76 -21
  65. data/lib/datadog/appsec/contrib/faraday/integration.rb +4 -4
  66. data/lib/datadog/appsec/contrib/faraday/patcher.rb +4 -4
  67. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +72 -18
  68. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +4 -4
  69. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +3 -3
  70. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +7 -7
  71. data/lib/datadog/appsec/contrib/graphql/integration.rb +9 -9
  72. data/lib/datadog/appsec/contrib/graphql/patcher.rb +3 -3
  73. data/lib/datadog/appsec/contrib/rack/buffered_input.rb +1 -1
  74. data/lib/datadog/appsec/contrib/rack/ext.rb +29 -29
  75. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +18 -17
  76. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +1 -1
  77. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +25 -21
  78. data/lib/datadog/appsec/contrib/rack/input_peeker.rb +9 -22
  79. data/lib/datadog/appsec/contrib/rack/integration.rb +6 -6
  80. data/lib/datadog/appsec/contrib/rack/patcher.rb +3 -3
  81. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +11 -8
  82. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +45 -24
  83. data/lib/datadog/appsec/contrib/rack/response_body.rb +1 -1
  84. data/lib/datadog/appsec/contrib/rails/gateway/request.rb +14 -8
  85. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +10 -10
  86. data/lib/datadog/appsec/contrib/rails/integration.rb +5 -5
  87. data/lib/datadog/appsec/contrib/rails/patcher.rb +17 -17
  88. data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.rb +2 -2
  89. data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +3 -3
  90. data/lib/datadog/appsec/contrib/rails/request.rb +3 -3
  91. data/lib/datadog/appsec/contrib/rails/request_middleware.rb +2 -1
  92. data/lib/datadog/appsec/contrib/rest_client/integration.rb +5 -5
  93. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +1 -1
  94. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +74 -21
  95. data/lib/datadog/appsec/contrib/sinatra/gateway/request.rb +1 -1
  96. data/lib/datadog/appsec/contrib/sinatra/gateway/route_params.rb +1 -1
  97. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +16 -12
  98. data/lib/datadog/appsec/contrib/sinatra/integration.rb +5 -5
  99. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +11 -11
  100. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +3 -3
  101. data/lib/datadog/appsec/contrib/sinatra/request_middleware.rb +2 -1
  102. data/lib/datadog/appsec/counter_sampler.rb +1 -1
  103. data/lib/datadog/appsec/event.rb +14 -14
  104. data/lib/datadog/appsec/ext.rb +11 -10
  105. data/lib/datadog/appsec/extensions.rb +2 -2
  106. data/lib/datadog/appsec/instrumentation/gateway.rb +1 -1
  107. data/lib/datadog/appsec/metrics/collector.rb +18 -1
  108. data/lib/datadog/appsec/metrics/exporter.rb +17 -11
  109. data/lib/datadog/appsec/metrics/telemetry.rb +7 -7
  110. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +4 -4
  111. data/lib/datadog/appsec/metrics.rb +4 -4
  112. data/lib/datadog/appsec/monitor/gateway/watcher.rb +16 -12
  113. data/lib/datadog/appsec/monitor.rb +1 -1
  114. data/lib/datadog/appsec/processor/rule_loader.rb +26 -26
  115. data/lib/datadog/appsec/rate_limiter.rb +1 -1
  116. data/lib/datadog/appsec/remote.rb +15 -9
  117. data/lib/datadog/appsec/response.rb +21 -21
  118. data/lib/datadog/appsec/route_normalizer/rails_route_pattern.rb +176 -0
  119. data/lib/datadog/appsec/route_normalizer/route_pattern.rb +378 -0
  120. data/lib/datadog/appsec/route_normalizer/route_text.rb +57 -0
  121. data/lib/datadog/appsec/route_normalizer.rb +80 -0
  122. data/lib/datadog/appsec/security_engine/engine.rb +37 -37
  123. data/lib/datadog/appsec/security_engine/runner.rb +2 -2
  124. data/lib/datadog/appsec/security_event.rb +2 -2
  125. data/lib/datadog/appsec/utils/http/body.rb +11 -8
  126. data/lib/datadog/appsec/utils/http/body_reader.rb +61 -0
  127. data/lib/datadog/appsec/utils/http/media_range.rb +15 -15
  128. data/lib/datadog/appsec/utils/http/media_type.rb +4 -4
  129. data/lib/datadog/appsec/utils/http/url_encoded.rb +80 -25
  130. data/lib/datadog/appsec.rb +16 -16
  131. data/lib/datadog/auto_instrument.rb +2 -2
  132. data/lib/datadog/core/buffer/cruby.rb +1 -1
  133. data/lib/datadog/core/buffer/thread_safe.rb +1 -1
  134. data/lib/datadog/core/configuration/agent_settings.rb +1 -1
  135. data/lib/datadog/core/configuration/agent_settings_resolver.rb +15 -15
  136. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +2 -2
  137. data/lib/datadog/core/configuration/base.rb +2 -2
  138. data/lib/datadog/core/configuration/components.rb +115 -31
  139. data/lib/datadog/core/configuration/components_state.rb +6 -1
  140. data/lib/datadog/core/configuration/config_helper.rb +3 -3
  141. data/lib/datadog/core/configuration/deprecations.rb +6 -6
  142. data/lib/datadog/core/configuration/ext.rb +11 -11
  143. data/lib/datadog/core/configuration/option.rb +16 -16
  144. data/lib/datadog/core/configuration/option_definition.rb +4 -4
  145. data/lib/datadog/core/configuration/options.rb +1 -1
  146. data/lib/datadog/core/configuration/settings.rb +93 -104
  147. data/lib/datadog/core/configuration/stable_config.rb +1 -1
  148. data/lib/datadog/core/configuration/supported_configurations.rb +8 -1
  149. data/lib/datadog/core/configuration.rb +8 -8
  150. data/lib/datadog/core/contrib/rails/railtie.rb +3 -3
  151. data/lib/datadog/core/crashtracking/component.rb +9 -9
  152. data/lib/datadog/core/crashtracking/tag_builder.rb +5 -5
  153. data/lib/datadog/core/deprecations.rb +1 -1
  154. data/lib/datadog/core/diagnostics/environment_logger.rb +6 -6
  155. data/lib/datadog/core/diagnostics/health.rb +2 -2
  156. data/lib/datadog/core/encoding.rb +4 -4
  157. data/lib/datadog/core/environment/agent_info.rb +2 -2
  158. data/lib/datadog/core/environment/cgroup.rb +4 -4
  159. data/lib/datadog/core/environment/container.rb +9 -9
  160. data/lib/datadog/core/environment/execution.rb +3 -3
  161. data/lib/datadog/core/environment/ext.rb +18 -18
  162. data/lib/datadog/core/environment/gc.rb +1 -1
  163. data/lib/datadog/core/environment/git.rb +2 -2
  164. data/lib/datadog/core/environment/identity.rb +10 -10
  165. data/lib/datadog/core/environment/platform.rb +8 -8
  166. data/lib/datadog/core/environment/process.rb +3 -3
  167. data/lib/datadog/core/environment/socket.rb +2 -2
  168. data/lib/datadog/core/environment/variable_helpers.rb +1 -1
  169. data/lib/datadog/core/environment/vm_cache.rb +1 -1
  170. data/lib/datadog/core/environment/yjit.rb +2 -2
  171. data/lib/datadog/core/error.rb +5 -5
  172. data/lib/datadog/core/evp.rb +11 -0
  173. data/lib/datadog/core/extensions.rb +1 -1
  174. data/lib/datadog/core/feature_flags.rb +2 -2
  175. data/lib/datadog/core/git/ext.rb +4 -4
  176. data/lib/datadog/core/knuth_sampler.rb +3 -1
  177. data/lib/datadog/core/logger.rb +3 -3
  178. data/lib/datadog/core/logging/ext.rb +1 -1
  179. data/lib/datadog/core/metrics/client.rb +21 -21
  180. data/lib/datadog/core/metrics/ext.rb +5 -5
  181. data/lib/datadog/core/metrics/logging.rb +2 -2
  182. data/lib/datadog/core/metrics/options.rb +3 -3
  183. data/lib/datadog/core/pin.rb +1 -1
  184. data/lib/datadog/core/process_discovery.rb +8 -8
  185. data/lib/datadog/core/rate_limiter.rb +1 -1
  186. data/lib/datadog/core/remote/client/capabilities.rb +62 -19
  187. data/lib/datadog/core/remote/client.rb +16 -16
  188. data/lib/datadog/core/remote/component.rb +20 -12
  189. data/lib/datadog/core/remote/configuration/content.rb +3 -3
  190. data/lib/datadog/core/remote/configuration/digest.rb +1 -1
  191. data/lib/datadog/core/remote/configuration/path.rb +9 -9
  192. data/lib/datadog/core/remote/configuration/repository.rb +2 -2
  193. data/lib/datadog/core/remote/configuration/target.rb +9 -9
  194. data/lib/datadog/core/remote/configuration.rb +4 -4
  195. data/lib/datadog/core/remote/ext.rb +3 -3
  196. data/lib/datadog/core/remote/negotiation.rb +1 -1
  197. data/lib/datadog/core/remote/tie/tracing.rb +7 -7
  198. data/lib/datadog/core/remote/transport/config.rb +4 -4
  199. data/lib/datadog/core/remote/transport/http/config.rb +7 -7
  200. data/lib/datadog/core/remote/transport/http/negotiation.rb +4 -4
  201. data/lib/datadog/core/remote/transport/http.rb +10 -10
  202. data/lib/datadog/core/remote/transport/negotiation.rb +3 -3
  203. data/lib/datadog/core/remote/worker.rb +5 -5
  204. data/lib/datadog/core/runtime/ext.rb +22 -22
  205. data/lib/datadog/core/runtime/metrics.rb +9 -9
  206. data/lib/datadog/core/tag_builder.rb +18 -18
  207. data/lib/datadog/core/tag_normalizer.rb +7 -7
  208. data/lib/datadog/core/telemetry/component.rb +13 -13
  209. data/lib/datadog/core/telemetry/emitter.rb +4 -4
  210. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +5 -5
  211. data/lib/datadog/core/telemetry/event/app_closing.rb +2 -2
  212. data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +2 -2
  213. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +2 -2
  214. data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +2 -2
  215. data/lib/datadog/core/telemetry/event/app_heartbeat.rb +2 -2
  216. data/lib/datadog/core/telemetry/event/app_integrations_change.rb +2 -2
  217. data/lib/datadog/core/telemetry/event/app_started.rb +41 -34
  218. data/lib/datadog/core/telemetry/event/base.rb +1 -1
  219. data/lib/datadog/core/telemetry/event/distributions.rb +2 -2
  220. data/lib/datadog/core/telemetry/event/generate_metrics.rb +2 -2
  221. data/lib/datadog/core/telemetry/event/log.rb +4 -4
  222. data/lib/datadog/core/telemetry/event/message_batch.rb +1 -1
  223. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +3 -3
  224. data/lib/datadog/core/telemetry/event.rb +16 -16
  225. data/lib/datadog/core/telemetry/ext.rb +11 -11
  226. data/lib/datadog/core/telemetry/logger.rb +1 -1
  227. data/lib/datadog/core/telemetry/logging.rb +5 -5
  228. data/lib/datadog/core/telemetry/metric.rb +7 -7
  229. data/lib/datadog/core/telemetry/metrics_collection.rb +2 -2
  230. data/lib/datadog/core/telemetry/metrics_manager.rb +1 -1
  231. data/lib/datadog/core/telemetry/request.rb +4 -4
  232. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +14 -14
  233. data/lib/datadog/core/telemetry/transport/http.rb +7 -7
  234. data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -3
  235. data/lib/datadog/core/telemetry/worker.rb +6 -6
  236. data/lib/datadog/core/transport/ext.rb +14 -14
  237. data/lib/datadog/core/transport/http/adapters/net.rb +4 -4
  238. data/lib/datadog/core/transport/http/adapters/test.rb +1 -1
  239. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +4 -4
  240. data/lib/datadog/core/transport/http/api/endpoint.rb +1 -1
  241. data/lib/datadog/core/transport/http/api/map.rb +1 -1
  242. data/lib/datadog/core/transport/http/builder.rb +6 -6
  243. data/lib/datadog/core/transport/http/client.rb +2 -2
  244. data/lib/datadog/core/transport/http/response.rb +1 -1
  245. data/lib/datadog/core/transport/http.rb +8 -8
  246. data/lib/datadog/core/transport/transport.rb +1 -1
  247. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +83 -15
  248. data/lib/datadog/core/utils/base64_codec.rb +3 -3
  249. data/lib/datadog/core/utils/duration.rb +1 -1
  250. data/lib/datadog/core/utils/forking.rb +2 -2
  251. data/lib/datadog/core/utils/lru_cache.rb +3 -3
  252. data/lib/datadog/core/utils/network.rb +11 -11
  253. data/lib/datadog/core/utils/only_once_successful.rb +1 -1
  254. data/lib/datadog/core/utils/safe_dup.rb +2 -1
  255. data/lib/datadog/core/utils/sequence.rb +3 -6
  256. data/lib/datadog/core/utils/spawn_monkey_patch.rb +2 -2
  257. data/lib/datadog/core/utils/time.rb +18 -52
  258. data/lib/datadog/core/utils/url.rb +1 -1
  259. data/lib/datadog/core/utils.rb +4 -4
  260. data/lib/datadog/core/workers/async.rb +1 -1
  261. data/lib/datadog/core/workers/polling.rb +2 -2
  262. data/lib/datadog/core/workers/runtime_metrics.rb +4 -4
  263. data/lib/datadog/core.rb +4 -4
  264. data/lib/datadog/data_streams/configuration.rb +3 -3
  265. data/lib/datadog/data_streams/ext.rb +1 -1
  266. data/lib/datadog/data_streams/extensions.rb +2 -2
  267. data/lib/datadog/data_streams/pathway_context.rb +5 -5
  268. data/lib/datadog/data_streams/processor.rb +52 -52
  269. data/lib/datadog/data_streams/transport/http/stats.rb +5 -5
  270. data/lib/datadog/data_streams/transport/http.rb +5 -5
  271. data/lib/datadog/data_streams/transport/stats.rb +7 -7
  272. data/lib/datadog/data_streams.rb +5 -5
  273. data/lib/datadog/di/base.rb +6 -4
  274. data/lib/datadog/di/boot.rb +25 -22
  275. data/lib/datadog/di/capture_expression.rb +21 -0
  276. data/lib/datadog/di/capture_expression_evaluator.rb +71 -0
  277. data/lib/datadog/di/capture_limits.rb +41 -0
  278. data/lib/datadog/di/code_tracker.rb +10 -7
  279. data/lib/datadog/di/component.rb +122 -40
  280. data/lib/datadog/di/configuration.rb +7 -1
  281. data/lib/datadog/di/context.rb +9 -3
  282. data/lib/datadog/di/contrib/railtie.rb +1 -1
  283. data/lib/datadog/di/contrib.rb +5 -5
  284. data/lib/datadog/di/el/compiler.rb +91 -38
  285. data/lib/datadog/di/el/evaluator.rb +82 -2
  286. data/lib/datadog/di/el/expression.rb +10 -2
  287. data/lib/datadog/di/el.rb +3 -3
  288. data/lib/datadog/di/error.rb +10 -0
  289. data/lib/datadog/di/fatal_exceptions.rb +26 -0
  290. data/lib/datadog/di/instrumenter.rb +426 -169
  291. data/lib/datadog/di/logger.rb +1 -1
  292. data/lib/datadog/di/preload.rb +1 -1
  293. data/lib/datadog/di/probe.rb +61 -2
  294. data/lib/datadog/di/probe_builder.rb +96 -14
  295. data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
  296. data/lib/datadog/di/probe_file_loader.rb +12 -7
  297. data/lib/datadog/di/probe_manager.rb +67 -9
  298. data/lib/datadog/di/probe_notification_builder.rb +66 -22
  299. data/lib/datadog/di/probe_notifier_worker.rb +57 -37
  300. data/lib/datadog/di/probe_repository.rb +1 -1
  301. data/lib/datadog/di/redactor.rb +16 -1
  302. data/lib/datadog/di/remote.rb +178 -17
  303. data/lib/datadog/di/serializer.rb +47 -28
  304. data/lib/datadog/di/transport/diagnostics.rb +5 -5
  305. data/lib/datadog/di/transport/http/diagnostics.rb +3 -3
  306. data/lib/datadog/di/transport/http/input.rb +2 -2
  307. data/lib/datadog/di/transport/http.rb +10 -10
  308. data/lib/datadog/di/transport/input.rb +16 -12
  309. data/lib/datadog/di/utils.rb +4 -4
  310. data/lib/datadog/di.rb +66 -3
  311. data/lib/datadog/error_tracking/collector.rb +2 -2
  312. data/lib/datadog/error_tracking/component.rb +12 -12
  313. data/lib/datadog/error_tracking/configuration.rb +2 -2
  314. data/lib/datadog/error_tracking/ext.rb +7 -7
  315. data/lib/datadog/error_tracking/extensions.rb +2 -2
  316. data/lib/datadog/error_tracking/filters.rb +4 -4
  317. data/lib/datadog/error_tracking.rb +6 -6
  318. data/lib/datadog/kit/appsec/events/v2.rb +90 -32
  319. data/lib/datadog/kit/appsec/events.rb +25 -25
  320. data/lib/datadog/kit/enable_core_dumps.rb +2 -2
  321. data/lib/datadog/kit/identity.rb +18 -18
  322. data/lib/datadog/kit/tracing/method_tracer.rb +7 -7
  323. data/lib/datadog/kit.rb +1 -1
  324. data/lib/datadog/open_feature/component.rb +53 -21
  325. data/lib/datadog/open_feature/configuration.rb +22 -1
  326. data/lib/datadog/open_feature/evaluation_engine.rb +7 -7
  327. data/lib/datadog/open_feature/exposures/buffer.rb +1 -1
  328. data/lib/datadog/open_feature/exposures/deduplicator.rb +1 -1
  329. data/lib/datadog/open_feature/exposures/event.rb +2 -2
  330. data/lib/datadog/open_feature/exposures/reporter.rb +3 -3
  331. data/lib/datadog/open_feature/exposures/worker.rb +6 -6
  332. data/lib/datadog/open_feature/ext.rb +13 -6
  333. data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +286 -0
  334. data/lib/datadog/open_feature/flag_evaluation/writer.rb +433 -0
  335. data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +102 -0
  336. data/lib/datadog/open_feature/hooks/{flag_eval_hook.rb → flag_eval_metrics_hook.rb} +4 -2
  337. data/lib/datadog/open_feature/hooks/span_enrichment_hook/codec.rb +58 -0
  338. data/lib/datadog/open_feature/hooks/span_enrichment_hook/span_enrichment_state.rb +96 -0
  339. data/lib/datadog/open_feature/hooks/span_enrichment_hook/span_enrichment_state_store.rb +49 -0
  340. data/lib/datadog/open_feature/hooks/span_enrichment_hook.rb +184 -0
  341. data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +24 -24
  342. data/lib/datadog/open_feature/native_evaluator.rb +23 -1
  343. data/lib/datadog/open_feature/noop_evaluator.rb +3 -3
  344. data/lib/datadog/open_feature/provider.rb +97 -30
  345. data/lib/datadog/open_feature/remote.rb +2 -2
  346. data/lib/datadog/open_feature/resolution_details.rb +4 -2
  347. data/lib/datadog/open_feature/transport.rb +65 -11
  348. data/lib/datadog/open_feature.rb +2 -2
  349. data/lib/datadog/opentelemetry/api/baggage.rb +3 -3
  350. data/lib/datadog/opentelemetry/api/context.rb +4 -4
  351. data/lib/datadog/opentelemetry/configuration/settings.rb +41 -41
  352. data/lib/datadog/opentelemetry/ext.rb +1 -1
  353. data/lib/datadog/opentelemetry/logs.rb +5 -5
  354. data/lib/datadog/opentelemetry/metrics.rb +7 -7
  355. data/lib/datadog/opentelemetry/sdk/configurator.rb +8 -8
  356. data/lib/datadog/opentelemetry/sdk/id_generator.rb +2 -2
  357. data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +10 -17
  358. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +8 -15
  359. data/lib/datadog/opentelemetry/sdk/propagator.rb +7 -7
  360. data/lib/datadog/opentelemetry/sdk/span_processor.rb +15 -15
  361. data/lib/datadog/opentelemetry/sdk/trace/span.rb +32 -32
  362. data/lib/datadog/opentelemetry/sdk.rb +19 -0
  363. data/lib/datadog/opentelemetry/signal_configuration.rb +11 -11
  364. data/lib/datadog/opentelemetry/trace.rb +1 -1
  365. data/lib/datadog/opentelemetry.rb +8 -8
  366. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +6 -1
  367. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +5 -3
  368. data/lib/datadog/profiling/collectors/thread_context.rb +15 -14
  369. data/lib/datadog/profiling/component.rb +18 -0
  370. data/lib/datadog/profiling/exporter.rb +1 -1
  371. data/lib/datadog/profiling/http_transport.rb +1 -1
  372. data/lib/datadog/profiling/sequence_tracker.rb +1 -1
  373. data/lib/datadog/profiling/tag_builder.rb +19 -19
  374. data/lib/datadog/profiling.rb +31 -27
  375. data/lib/datadog/single_step_instrument.rb +1 -1
  376. data/lib/datadog/symbol_database/component.rb +177 -60
  377. data/lib/datadog/symbol_database/configuration.rb +15 -7
  378. data/lib/datadog/symbol_database/extensions.rb +19 -0
  379. data/lib/datadog/symbol_database/extractor.rb +167 -130
  380. data/lib/datadog/symbol_database/file_hash.rb +5 -3
  381. data/lib/datadog/symbol_database/logger.rb +1 -1
  382. data/lib/datadog/symbol_database/remote.rb +28 -8
  383. data/lib/datadog/symbol_database/scope.rb +2 -2
  384. data/lib/datadog/symbol_database/scope_batcher.rb +8 -4
  385. data/lib/datadog/symbol_database/service_version.rb +4 -4
  386. data/lib/datadog/symbol_database/symbol.rb +1 -1
  387. data/lib/datadog/symbol_database/transport/http/endpoint.rb +1 -1
  388. data/lib/datadog/symbol_database/transport/http.rb +6 -6
  389. data/lib/datadog/symbol_database/transport.rb +4 -4
  390. data/lib/datadog/symbol_database/uploader.rb +28 -21
  391. data/lib/datadog/symbol_database.rb +22 -0
  392. data/lib/datadog/tracing/analytics.rb +1 -1
  393. data/lib/datadog/tracing/buffer.rb +5 -5
  394. data/lib/datadog/tracing/client_ip.rb +4 -4
  395. data/lib/datadog/tracing/component.rb +28 -7
  396. data/lib/datadog/tracing/configuration/dynamic.rb +8 -8
  397. data/lib/datadog/tracing/configuration/ext.rb +43 -42
  398. data/lib/datadog/tracing/configuration/http.rb +2 -2
  399. data/lib/datadog/tracing/configuration/settings.rb +32 -16
  400. data/lib/datadog/tracing/context.rb +1 -1
  401. data/lib/datadog/tracing/context_provider.rb +2 -2
  402. data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +2 -2
  403. data/lib/datadog/tracing/contrib/action_cable/event.rb +4 -4
  404. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +5 -5
  405. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +4 -4
  406. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +5 -5
  407. data/lib/datadog/tracing/contrib/action_cable/events.rb +4 -4
  408. data/lib/datadog/tracing/contrib/action_cable/ext.rb +18 -18
  409. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +4 -4
  410. data/lib/datadog/tracing/contrib/action_cable/integration.rb +7 -7
  411. data/lib/datadog/tracing/contrib/action_cable/patcher.rb +4 -4
  412. data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +2 -2
  413. data/lib/datadog/tracing/contrib/action_mailer/event.rb +3 -3
  414. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +8 -8
  415. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +4 -4
  416. data/lib/datadog/tracing/contrib/action_mailer/events.rb +2 -2
  417. data/lib/datadog/tracing/contrib/action_mailer/ext.rb +18 -18
  418. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +7 -7
  419. data/lib/datadog/tracing/contrib/action_mailer/patcher.rb +3 -3
  420. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +8 -8
  421. data/lib/datadog/tracing/contrib/action_pack/action_controller/patcher.rb +2 -2
  422. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +4 -4
  423. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +4 -4
  424. data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +3 -3
  425. data/lib/datadog/tracing/contrib/action_pack/ext.rb +10 -10
  426. data/lib/datadog/tracing/contrib/action_pack/integration.rb +7 -7
  427. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +3 -3
  428. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -1
  429. data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +3 -3
  430. data/lib/datadog/tracing/contrib/action_view/event.rb +1 -1
  431. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +6 -6
  432. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +6 -6
  433. data/lib/datadog/tracing/contrib/action_view/events.rb +2 -2
  434. data/lib/datadog/tracing/contrib/action_view/ext.rb +10 -10
  435. data/lib/datadog/tracing/contrib/action_view/integration.rb +11 -11
  436. data/lib/datadog/tracing/contrib/action_view/patcher.rb +5 -5
  437. data/lib/datadog/tracing/contrib/action_view/utils.rb +2 -2
  438. data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +2 -2
  439. data/lib/datadog/tracing/contrib/active_job/data_streams.rb +62 -0
  440. data/lib/datadog/tracing/contrib/active_job/event.rb +3 -3
  441. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +5 -5
  442. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +5 -5
  443. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +5 -5
  444. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +5 -5
  445. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +5 -5
  446. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +5 -5
  447. data/lib/datadog/tracing/contrib/active_job/events.rb +6 -6
  448. data/lib/datadog/tracing/contrib/active_job/ext.rb +23 -23
  449. data/lib/datadog/tracing/contrib/active_job/integration.rb +7 -7
  450. data/lib/datadog/tracing/contrib/active_job/patcher.rb +17 -5
  451. data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +2 -2
  452. data/lib/datadog/tracing/contrib/active_model_serializers/event.rb +4 -4
  453. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +6 -6
  454. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +7 -8
  455. data/lib/datadog/tracing/contrib/active_model_serializers/events.rb +2 -2
  456. data/lib/datadog/tracing/contrib/active_model_serializers/ext.rb +10 -10
  457. data/lib/datadog/tracing/contrib/active_model_serializers/integration.rb +5 -5
  458. data/lib/datadog/tracing/contrib/active_model_serializers/patcher.rb +3 -3
  459. data/lib/datadog/tracing/contrib/active_record/configuration/makara_resolver.rb +1 -1
  460. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +6 -6
  461. data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +4 -4
  462. data/lib/datadog/tracing/contrib/active_record/event.rb +1 -1
  463. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +8 -8
  464. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +9 -9
  465. data/lib/datadog/tracing/contrib/active_record/events.rb +2 -2
  466. data/lib/datadog/tracing/contrib/active_record/ext.rb +15 -15
  467. data/lib/datadog/tracing/contrib/active_record/integration.rb +11 -11
  468. data/lib/datadog/tracing/contrib/active_record/patcher.rb +2 -2
  469. data/lib/datadog/tracing/contrib/active_record/utils.rb +3 -3
  470. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +1 -1
  471. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +11 -11
  472. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +1 -1
  473. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +4 -4
  474. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +5 -5
  475. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -4
  476. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +3 -3
  477. data/lib/datadog/tracing/contrib/active_support/ext.rb +16 -16
  478. data/lib/datadog/tracing/contrib/active_support/integration.rb +8 -8
  479. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +1 -1
  480. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +2 -2
  481. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +1 -1
  482. data/lib/datadog/tracing/contrib/active_support/patcher.rb +2 -2
  483. data/lib/datadog/tracing/contrib/analytics.rb +1 -1
  484. data/lib/datadog/tracing/contrib/auto_instrument.rb +4 -4
  485. data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +3 -3
  486. data/lib/datadog/tracing/contrib/aws/ext.rb +25 -25
  487. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +5 -5
  488. data/lib/datadog/tracing/contrib/aws/integration.rb +9 -9
  489. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +1 -1
  490. data/lib/datadog/tracing/contrib/aws/patcher.rb +5 -5
  491. data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +2 -2
  492. data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +2 -2
  493. data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +4 -4
  494. data/lib/datadog/tracing/contrib/aws/service/s3.rb +2 -2
  495. data/lib/datadog/tracing/contrib/aws/service/sns.rb +3 -3
  496. data/lib/datadog/tracing/contrib/aws/service/sqs.rb +3 -3
  497. data/lib/datadog/tracing/contrib/aws/service/states.rb +5 -5
  498. data/lib/datadog/tracing/contrib/aws/services.rb +14 -14
  499. data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +1 -1
  500. data/lib/datadog/tracing/contrib/concurrent_ruby/configuration/settings.rb +2 -2
  501. data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +1 -1
  502. data/lib/datadog/tracing/contrib/concurrent_ruby/ext.rb +2 -2
  503. data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +1 -1
  504. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +6 -6
  505. data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +4 -4
  506. data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +1 -1
  507. data/lib/datadog/tracing/contrib/configurable.rb +2 -2
  508. data/lib/datadog/tracing/contrib/configuration/resolvers/pattern_resolver.rb +1 -1
  509. data/lib/datadog/tracing/contrib/configuration/settings.rb +2 -2
  510. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +2 -2
  511. data/lib/datadog/tracing/contrib/dalli/ext.rb +13 -13
  512. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +5 -5
  513. data/lib/datadog/tracing/contrib/dalli/integration.rb +7 -7
  514. data/lib/datadog/tracing/contrib/dalli/patcher.rb +3 -3
  515. data/lib/datadog/tracing/contrib/dalli/quantize.rb +2 -2
  516. data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +2 -2
  517. data/lib/datadog/tracing/contrib/delayed_job/ext.rb +14 -14
  518. data/lib/datadog/tracing/contrib/delayed_job/integration.rb +5 -5
  519. data/lib/datadog/tracing/contrib/delayed_job/patcher.rb +3 -3
  520. data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +5 -5
  521. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +2 -2
  522. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +15 -15
  523. data/lib/datadog/tracing/contrib/elasticsearch/integration.rb +6 -6
  524. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +10 -10
  525. data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +2 -2
  526. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +2 -2
  527. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +8 -8
  528. data/lib/datadog/tracing/contrib/ethon/ext.rb +13 -13
  529. data/lib/datadog/tracing/contrib/ethon/integration.rb +6 -6
  530. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +3 -3
  531. data/lib/datadog/tracing/contrib/ethon/patcher.rb +3 -3
  532. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +5 -5
  533. data/lib/datadog/tracing/contrib/excon/ext.rb +11 -11
  534. data/lib/datadog/tracing/contrib/excon/integration.rb +6 -6
  535. data/lib/datadog/tracing/contrib/excon/middleware.rb +8 -8
  536. data/lib/datadog/tracing/contrib/excon/patcher.rb +2 -2
  537. data/lib/datadog/tracing/contrib/ext.rb +15 -15
  538. data/lib/datadog/tracing/contrib/extensions.rb +2 -2
  539. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +4 -4
  540. data/lib/datadog/tracing/contrib/faraday/ext.rb +11 -11
  541. data/lib/datadog/tracing/contrib/faraday/integration.rb +6 -6
  542. data/lib/datadog/tracing/contrib/faraday/middleware.rb +8 -8
  543. data/lib/datadog/tracing/contrib/faraday/patcher.rb +6 -6
  544. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +4 -4
  545. data/lib/datadog/tracing/contrib/grape/endpoint.rb +20 -20
  546. data/lib/datadog/tracing/contrib/grape/ext.rb +15 -15
  547. data/lib/datadog/tracing/contrib/grape/instrumentation.rb +3 -3
  548. data/lib/datadog/tracing/contrib/grape/integration.rb +5 -5
  549. data/lib/datadog/tracing/contrib/grape/patcher.rb +5 -5
  550. data/lib/datadog/tracing/contrib/graphql/configuration/error_extension_env_parser.rb +1 -1
  551. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +3 -3
  552. data/lib/datadog/tracing/contrib/graphql/ext.rb +9 -9
  553. data/lib/datadog/tracing/contrib/graphql/integration.rb +9 -9
  554. data/lib/datadog/tracing/contrib/graphql/patcher.rb +7 -7
  555. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +37 -37
  556. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +2 -2
  557. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +2 -2
  558. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +7 -7
  559. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +7 -7
  560. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +6 -6
  561. data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +1 -1
  562. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +8 -8
  563. data/lib/datadog/tracing/contrib/grpc/ext.rb +14 -14
  564. data/lib/datadog/tracing/contrib/grpc/formatting.rb +7 -7
  565. data/lib/datadog/tracing/contrib/grpc/integration.rb +5 -5
  566. data/lib/datadog/tracing/contrib/grpc/intercept_with_datadog.rb +1 -1
  567. data/lib/datadog/tracing/contrib/grpc/patcher.rb +3 -3
  568. data/lib/datadog/tracing/contrib/grpc.rb +6 -6
  569. data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +3 -3
  570. data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +2 -2
  571. data/lib/datadog/tracing/contrib/hanami/ext.rb +8 -8
  572. data/lib/datadog/tracing/contrib/hanami/integration.rb +6 -6
  573. data/lib/datadog/tracing/contrib/hanami/patcher.rb +5 -5
  574. data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +3 -3
  575. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +5 -4
  576. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +1 -1
  577. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +4 -4
  578. data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +3 -3
  579. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +8 -8
  580. data/lib/datadog/tracing/contrib/http/ext.rb +11 -11
  581. data/lib/datadog/tracing/contrib/http/instrumentation.rb +6 -6
  582. data/lib/datadog/tracing/contrib/http/integration.rb +7 -7
  583. data/lib/datadog/tracing/contrib/http/patcher.rb +3 -3
  584. data/lib/datadog/tracing/contrib/http.rb +6 -6
  585. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +4 -4
  586. data/lib/datadog/tracing/contrib/httpclient/ext.rb +11 -11
  587. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +5 -5
  588. data/lib/datadog/tracing/contrib/httpclient/integration.rb +6 -6
  589. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +3 -3
  590. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +4 -4
  591. data/lib/datadog/tracing/contrib/httprb/ext.rb +11 -11
  592. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +6 -6
  593. data/lib/datadog/tracing/contrib/httprb/integration.rb +7 -7
  594. data/lib/datadog/tracing/contrib/httprb/patcher.rb +3 -3
  595. data/lib/datadog/tracing/contrib/integration.rb +3 -3
  596. data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +2 -2
  597. data/lib/datadog/tracing/contrib/kafka/event.rb +3 -3
  598. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +7 -3
  599. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +4 -4
  600. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +4 -4
  601. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +5 -5
  602. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/join_group.rb +5 -5
  603. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/leave_group.rb +5 -5
  604. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/sync_group.rb +5 -5
  605. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +10 -5
  606. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +10 -5
  607. data/lib/datadog/tracing/contrib/kafka/events.rb +9 -9
  608. data/lib/datadog/tracing/contrib/kafka/ext.rb +40 -40
  609. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +2 -2
  610. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +4 -4
  611. data/lib/datadog/tracing/contrib/kafka/integration.rb +6 -6
  612. data/lib/datadog/tracing/contrib/kafka/patcher.rb +5 -5
  613. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +2 -2
  614. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +8 -8
  615. data/lib/datadog/tracing/contrib/karafka/ext.rb +13 -13
  616. data/lib/datadog/tracing/contrib/karafka/integration.rb +5 -5
  617. data/lib/datadog/tracing/contrib/karafka/monitor.rb +14 -14
  618. data/lib/datadog/tracing/contrib/karafka/patcher.rb +8 -8
  619. data/lib/datadog/tracing/contrib/karafka.rb +6 -6
  620. data/lib/datadog/tracing/contrib/lograge/configuration/settings.rb +2 -2
  621. data/lib/datadog/tracing/contrib/lograge/ext.rb +1 -1
  622. data/lib/datadog/tracing/contrib/lograge/integration.rb +5 -5
  623. data/lib/datadog/tracing/contrib/lograge/patcher.rb +9 -9
  624. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +2 -2
  625. data/lib/datadog/tracing/contrib/mongodb/ext.rb +18 -18
  626. data/lib/datadog/tracing/contrib/mongodb/instrumentation.rb +3 -3
  627. data/lib/datadog/tracing/contrib/mongodb/integration.rb +7 -7
  628. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +5 -5
  629. data/lib/datadog/tracing/contrib/mongodb/patcher.rb +3 -3
  630. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +13 -13
  631. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +3 -3
  632. data/lib/datadog/tracing/contrib/mysql2/ext.rb +11 -11
  633. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +6 -6
  634. data/lib/datadog/tracing/contrib/mysql2/integration.rb +5 -5
  635. data/lib/datadog/tracing/contrib/mysql2/patcher.rb +2 -2
  636. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +2 -2
  637. data/lib/datadog/tracing/contrib/opensearch/ext.rb +22 -22
  638. data/lib/datadog/tracing/contrib/opensearch/integration.rb +6 -6
  639. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +13 -13
  640. data/lib/datadog/tracing/contrib/opensearch/quantize.rb +3 -3
  641. data/lib/datadog/tracing/contrib/patcher.rb +2 -2
  642. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +3 -3
  643. data/lib/datadog/tracing/contrib/pg/ext.rb +19 -19
  644. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +18 -18
  645. data/lib/datadog/tracing/contrib/pg/integration.rb +5 -5
  646. data/lib/datadog/tracing/contrib/pg/patcher.rb +2 -2
  647. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +2 -2
  648. data/lib/datadog/tracing/contrib/presto/ext.rb +21 -21
  649. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +2 -2
  650. data/lib/datadog/tracing/contrib/presto/integration.rb +6 -6
  651. data/lib/datadog/tracing/contrib/presto/patcher.rb +4 -4
  652. data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +2 -2
  653. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +17 -17
  654. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +1 -1
  655. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +5 -5
  656. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +2 -2
  657. data/lib/datadog/tracing/contrib/que/ext.rb +19 -19
  658. data/lib/datadog/tracing/contrib/que/integration.rb +6 -6
  659. data/lib/datadog/tracing/contrib/que/patcher.rb +1 -1
  660. data/lib/datadog/tracing/contrib/que/tracer.rb +1 -1
  661. data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +3 -3
  662. data/lib/datadog/tracing/contrib/racecar/event.rb +4 -4
  663. data/lib/datadog/tracing/contrib/racecar/events/batch.rb +3 -3
  664. data/lib/datadog/tracing/contrib/racecar/events/consume.rb +3 -3
  665. data/lib/datadog/tracing/contrib/racecar/events/message.rb +3 -3
  666. data/lib/datadog/tracing/contrib/racecar/events.rb +3 -3
  667. data/lib/datadog/tracing/contrib/racecar/ext.rb +18 -18
  668. data/lib/datadog/tracing/contrib/racecar/integration.rb +5 -5
  669. data/lib/datadog/tracing/contrib/racecar/patcher.rb +3 -3
  670. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +2 -2
  671. data/lib/datadog/tracing/contrib/rack/ext.rb +33 -33
  672. data/lib/datadog/tracing/contrib/rack/header_collection.rb +3 -3
  673. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +4 -4
  674. data/lib/datadog/tracing/contrib/rack/integration.rb +6 -6
  675. data/lib/datadog/tracing/contrib/rack/middlewares.rb +24 -24
  676. data/lib/datadog/tracing/contrib/rack/patcher.rb +3 -3
  677. data/lib/datadog/tracing/contrib/rack/request_queue.rb +3 -3
  678. data/lib/datadog/tracing/contrib/rack/route_inference.rb +11 -11
  679. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +15 -15
  680. data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +1 -1
  681. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +3 -3
  682. data/lib/datadog/tracing/contrib/rails/ext.rb +15 -15
  683. data/lib/datadog/tracing/contrib/rails/framework.rb +11 -11
  684. data/lib/datadog/tracing/contrib/rails/integration.rb +5 -5
  685. data/lib/datadog/tracing/contrib/rails/middlewares.rb +1 -1
  686. data/lib/datadog/tracing/contrib/rails/patcher.rb +8 -8
  687. data/lib/datadog/tracing/contrib/rails/railtie.rb +4 -4
  688. data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
  689. data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +3 -3
  690. data/lib/datadog/tracing/contrib/rake/ext.rb +12 -12
  691. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +4 -4
  692. data/lib/datadog/tracing/contrib/rake/integration.rb +5 -5
  693. data/lib/datadog/tracing/contrib/rake/patcher.rb +3 -3
  694. data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +2 -2
  695. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +2 -2
  696. data/lib/datadog/tracing/contrib/redis/ext.rb +16 -16
  697. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +6 -6
  698. data/lib/datadog/tracing/contrib/redis/integration.rb +7 -7
  699. data/lib/datadog/tracing/contrib/redis/patcher.rb +10 -10
  700. data/lib/datadog/tracing/contrib/redis/quantize.rb +4 -4
  701. data/lib/datadog/tracing/contrib/redis/tags.rb +5 -5
  702. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +5 -5
  703. data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +2 -2
  704. data/lib/datadog/tracing/contrib/resque/ext.rb +7 -7
  705. data/lib/datadog/tracing/contrib/resque/integration.rb +5 -5
  706. data/lib/datadog/tracing/contrib/resque/patcher.rb +3 -3
  707. data/lib/datadog/tracing/contrib/resque/resque_job.rb +5 -5
  708. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +2 -2
  709. data/lib/datadog/tracing/contrib/rest_client/ext.rb +10 -10
  710. data/lib/datadog/tracing/contrib/rest_client/integration.rb +6 -6
  711. data/lib/datadog/tracing/contrib/rest_client/patcher.rb +2 -2
  712. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +5 -5
  713. data/lib/datadog/tracing/contrib/roda/configuration/settings.rb +2 -2
  714. data/lib/datadog/tracing/contrib/roda/ext.rb +6 -6
  715. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +4 -4
  716. data/lib/datadog/tracing/contrib/roda/integration.rb +6 -6
  717. data/lib/datadog/tracing/contrib/roda/patcher.rb +3 -3
  718. data/lib/datadog/tracing/contrib/semantic_logger/configuration/settings.rb +2 -2
  719. data/lib/datadog/tracing/contrib/semantic_logger/ext.rb +1 -1
  720. data/lib/datadog/tracing/contrib/semantic_logger/integration.rb +5 -5
  721. data/lib/datadog/tracing/contrib/semantic_logger/patcher.rb +2 -2
  722. data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +2 -2
  723. data/lib/datadog/tracing/contrib/sequel/database.rb +6 -10
  724. data/lib/datadog/tracing/contrib/sequel/dataset.rb +5 -5
  725. data/lib/datadog/tracing/contrib/sequel/ext.rb +12 -8
  726. data/lib/datadog/tracing/contrib/sequel/integration.rb +5 -5
  727. data/lib/datadog/tracing/contrib/sequel/patcher.rb +3 -3
  728. data/lib/datadog/tracing/contrib/sequel/utils.rb +115 -10
  729. data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +2 -2
  730. data/lib/datadog/tracing/contrib/shoryuken/ext.rb +12 -12
  731. data/lib/datadog/tracing/contrib/shoryuken/integration.rb +6 -6
  732. data/lib/datadog/tracing/contrib/shoryuken/patcher.rb +1 -1
  733. data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +2 -2
  734. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +8 -8
  735. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +2 -2
  736. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +9 -9
  737. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +30 -30
  738. data/lib/datadog/tracing/contrib/sidekiq/integration.rb +7 -7
  739. data/lib/datadog/tracing/contrib/sidekiq/patcher.rb +8 -8
  740. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +14 -14
  741. data/lib/datadog/tracing/contrib/sidekiq/utils.rb +9 -9
  742. data/lib/datadog/tracing/contrib/sidekiq.rb +6 -6
  743. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +2 -2
  744. data/lib/datadog/tracing/contrib/sinatra/env.rb +6 -6
  745. data/lib/datadog/tracing/contrib/sinatra/ext.rb +17 -17
  746. data/lib/datadog/tracing/contrib/sinatra/framework.rb +2 -2
  747. data/lib/datadog/tracing/contrib/sinatra/integration.rb +5 -5
  748. data/lib/datadog/tracing/contrib/sinatra/patcher.rb +5 -5
  749. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +10 -10
  750. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +8 -8
  751. data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +1 -1
  752. data/lib/datadog/tracing/contrib/sneakers/ext.rb +12 -12
  753. data/lib/datadog/tracing/contrib/sneakers/integration.rb +7 -7
  754. data/lib/datadog/tracing/contrib/sneakers/patcher.rb +2 -2
  755. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +3 -3
  756. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +2 -2
  757. data/lib/datadog/tracing/contrib/status_range_env_parser.rb +2 -2
  758. data/lib/datadog/tracing/contrib/status_range_matcher.rb +1 -1
  759. data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +2 -2
  760. data/lib/datadog/tracing/contrib/stripe/ext.rb +12 -12
  761. data/lib/datadog/tracing/contrib/stripe/integration.rb +5 -5
  762. data/lib/datadog/tracing/contrib/stripe/patcher.rb +2 -2
  763. data/lib/datadog/tracing/contrib/stripe/request.rb +3 -3
  764. data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +2 -2
  765. data/lib/datadog/tracing/contrib/sucker_punch/exception_handler.rb +1 -1
  766. data/lib/datadog/tracing/contrib/sucker_punch/ext.rb +13 -13
  767. data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +4 -4
  768. data/lib/datadog/tracing/contrib/sucker_punch/integration.rb +5 -5
  769. data/lib/datadog/tracing/contrib/sucker_punch/patcher.rb +4 -4
  770. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +3 -3
  771. data/lib/datadog/tracing/contrib/trilogy/ext.rb +11 -11
  772. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +6 -6
  773. data/lib/datadog/tracing/contrib/trilogy/integration.rb +5 -5
  774. data/lib/datadog/tracing/contrib/trilogy/patcher.rb +2 -2
  775. data/lib/datadog/tracing/contrib/utils/database.rb +5 -5
  776. data/lib/datadog/tracing/contrib/utils/quantization/hash_formatter.rb +1 -1
  777. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +10 -10
  778. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +2 -2
  779. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +8 -8
  780. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +4 -4
  781. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +5 -5
  782. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +2 -2
  783. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +7 -7
  784. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +3 -3
  785. data/lib/datadog/tracing/contrib/waterdrop.rb +6 -6
  786. data/lib/datadog/tracing/contrib.rb +54 -54
  787. data/lib/datadog/tracing/correlation.rb +10 -10
  788. data/lib/datadog/tracing/diagnostics/environment_logger.rb +9 -9
  789. data/lib/datadog/tracing/diagnostics/ext.rb +19 -19
  790. data/lib/datadog/tracing/diagnostics/health.rb +1 -1
  791. data/lib/datadog/tracing/distributed/b3_multi.rb +8 -8
  792. data/lib/datadog/tracing/distributed/b3_single.rb +4 -4
  793. data/lib/datadog/tracing/distributed/baggage.rb +28 -27
  794. data/lib/datadog/tracing/distributed/datadog.rb +21 -21
  795. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +3 -3
  796. data/lib/datadog/tracing/distributed/fetcher.rb +1 -1
  797. data/lib/datadog/tracing/distributed/helpers.rb +3 -3
  798. data/lib/datadog/tracing/distributed/propagation.rb +12 -12
  799. data/lib/datadog/tracing/distributed/propagation_policy.rb +5 -0
  800. data/lib/datadog/tracing/distributed/trace_context.rb +33 -33
  801. data/lib/datadog/tracing/event.rb +1 -1
  802. data/lib/datadog/tracing/ext.rb +9 -0
  803. data/lib/datadog/tracing/metadata/analytics.rb +2 -2
  804. data/lib/datadog/tracing/metadata/errors.rb +4 -4
  805. data/lib/datadog/tracing/metadata/ext.rb +69 -69
  806. data/lib/datadog/tracing/metadata/metastruct.rb +1 -1
  807. data/lib/datadog/tracing/metadata/metastruct_tagging.rb +1 -1
  808. data/lib/datadog/tracing/metadata/tagging.rb +2 -2
  809. data/lib/datadog/tracing/metadata.rb +4 -4
  810. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  811. data/lib/datadog/tracing/pipeline.rb +2 -2
  812. data/lib/datadog/tracing/remote.rb +45 -7
  813. data/lib/datadog/tracing/sampling/all_sampler.rb +1 -1
  814. data/lib/datadog/tracing/sampling/ext.rb +8 -8
  815. data/lib/datadog/tracing/sampling/matcher.rb +4 -4
  816. data/lib/datadog/tracing/sampling/priority_sampler.rb +4 -4
  817. data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +3 -3
  818. data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +2 -2
  819. data/lib/datadog/tracing/sampling/rate_sampler.rb +2 -2
  820. data/lib/datadog/tracing/sampling/rule.rb +4 -4
  821. data/lib/datadog/tracing/sampling/rule_sampler.rb +12 -12
  822. data/lib/datadog/tracing/sampling/sampler.rb +2 -2
  823. data/lib/datadog/tracing/sampling/span/ext.rb +3 -3
  824. data/lib/datadog/tracing/sampling/span/matcher.rb +2 -2
  825. data/lib/datadog/tracing/sampling/span/rule.rb +1 -1
  826. data/lib/datadog/tracing/sampling/span/rule_parser.rb +8 -8
  827. data/lib/datadog/tracing/span.rb +8 -8
  828. data/lib/datadog/tracing/span_event.rb +5 -5
  829. data/lib/datadog/tracing/span_operation.rb +21 -21
  830. data/lib/datadog/tracing/sync_writer.rb +12 -7
  831. data/lib/datadog/tracing/trace_operation.rb +11 -11
  832. data/lib/datadog/tracing/trace_segment.rb +10 -10
  833. data/lib/datadog/tracing/tracer.rb +23 -15
  834. data/lib/datadog/tracing/transport/http/client.rb +4 -4
  835. data/lib/datadog/tracing/transport/http/statistics.rb +2 -2
  836. data/lib/datadog/tracing/transport/http/traces.rb +8 -8
  837. data/lib/datadog/tracing/transport/http.rb +10 -10
  838. data/lib/datadog/tracing/transport/io/client.rb +2 -2
  839. data/lib/datadog/tracing/transport/io/response.rb +1 -1
  840. data/lib/datadog/tracing/transport/io/traces.rb +4 -4
  841. data/lib/datadog/tracing/transport/io.rb +3 -3
  842. data/lib/datadog/tracing/transport/native/response.rb +71 -0
  843. data/lib/datadog/tracing/transport/native.rb +363 -0
  844. data/lib/datadog/tracing/transport/serializable_trace.rb +19 -19
  845. data/lib/datadog/tracing/transport/statistics.rb +2 -2
  846. data/lib/datadog/tracing/transport/trace_formatter.rb +10 -10
  847. data/lib/datadog/tracing/transport/traces.rb +8 -8
  848. data/lib/datadog/tracing/utils.rb +2 -2
  849. data/lib/datadog/tracing/workers.rb +2 -2
  850. data/lib/datadog/tracing/writer.rb +21 -8
  851. data/lib/datadog/tracing.rb +3 -3
  852. data/lib/datadog/version.rb +4 -4
  853. data/lib/datadog.rb +13 -12
  854. metadata +33 -8
@@ -1,4 +1,5 @@
1
1
  #include <ruby.h>
2
+ #include <ruby/debug.h>
2
3
 
3
4
  #include "datadog_ruby_common.h"
4
5
  #include "collectors_thread_context.h"
@@ -42,11 +43,8 @@
42
43
  // When `thread_context_collector_on_gc_start` gets called, the current cpu and wall-time get recorded to the thread
43
44
  // context: `cpu_time_at_gc_start_ns` and `wall_time_at_gc_start_ns`.
44
45
  //
45
- // While `cpu_time_at_gc_start_ns` is set, regular samples (if any) do not account for cpu-time any time that passes
46
- // after this timestamp. The idea is that this cpu-time will be blamed separately on GC, and not on the user thread.
47
- // Wall-time accounting is not affected by this (e.g. we still record 60 seconds every 60 seconds).
48
- //
49
- // (Regular samples can still account for the cpu-time between the previous sample and the start of GC.)
46
+ // While `cpu_time_at_gc_start_ns` is set, we don't expect the thread to be sampled: the VM is doing GC
47
+ // on the thread holding the GVL so no other samples can/will be triggered until GC finishes.
50
48
  //
51
49
  // When `thread_context_collector_on_gc_finish` gets called, the cpu-time and wall-time spent during GC gets recorded
52
50
  // into the global gc_tracking structure, and further samples are not affected. (The `cpu_time_at_previous_sample_ns`
@@ -75,8 +73,6 @@
75
73
 
76
74
  #define THREAD_ID_LIMIT_CHARS 44 // Why 44? "#{2**64} (#{2**64})".size + 1 for \0
77
75
  #define THREAD_INVOKE_LOCATION_LIMIT_CHARS 512
78
- #define IS_WALL_TIME true
79
- #define IS_CPU_TIME false
80
76
  #define MISSING_TRACER_CONTEXT_KEY 0
81
77
  #define TIME_BETWEEN_GC_EVENTS_NS MILLIS_AS_NS(10)
82
78
  #define GVL_SUSPENDED ((uint64_t)1)
@@ -108,6 +104,20 @@ static ID server_id; // id of :server in Ruby
108
104
  static ID otel_context_storage_id; // id of :__opentelemetry_context_storage__ in Ruby
109
105
  static ID otel_fiber_context_storage_id; // id of :@opentelemetry_context in Ruby
110
106
 
107
+ // This is mutable and gets set last-writer-wins style by
108
+ // `thread_context_collector_reset_all_per_thread_contexts`, which is called whenever
109
+ // profiling is starting or restating.
110
+ //
111
+ // Note: We must be careful to not change this value while the profiler is still running,
112
+ // otherwise new threads can get the new value and cause profiling to stop with an
113
+ // exception due to the mismatched size.
114
+ //
115
+ // The initial value should be kept in sync with the default for DD_PROFILING_MAX_FRAMES
116
+ // in settings.rb. See `initialize_context` for details on why this is needed/used.
117
+ static uint16_t latest_max_frames = 400;
118
+
119
+ // Global tracepoint for RUBY_EVENT_THREAD_BEGIN. Created and enabled once when the first ThreadContext collector is initialized.
120
+ static VALUE thread_begin_tracepoint = Qnil;
111
121
 
112
122
  typedef enum { OTEL_CONTEXT_ENABLED_FALSE, OTEL_CONTEXT_ENABLED_ONLY, OTEL_CONTEXT_ENABLED_BOTH } otel_context_enabled;
113
123
  typedef enum { OTEL_CONTEXT_SOURCE_UNKNOWN, OTEL_CONTEXT_SOURCE_FIBER_IVAR, OTEL_CONTEXT_SOURCE_FIBER_LOCAL } otel_context_source;
@@ -143,6 +153,8 @@ typedef struct {
143
153
  VALUE otel_current_span_key;
144
154
  // Used to enable native filenames in stack traces
145
155
  bool native_filenames_enabled;
156
+ // Used to include class/module names in method names (e.g. "Foo#bar" instead of "bar")
157
+ bool show_classes;
146
158
  // Used to cache native filename lookup results (Map[void *function_pointer, char *filename])
147
159
  st_table *native_filenames_cache;
148
160
  // Used to attribute overhead during sampling to this component
@@ -160,6 +172,7 @@ typedef struct {
160
172
  // How many per-thread samples were skipped because the thread has been continuously suspended
161
173
  // (no GVL) since its previous sample, so its Ruby stack cannot have changed.
162
174
  unsigned int inactive_thread_samples_skipped;
175
+ unsigned int profiler_thread_samples_skipped;
163
176
  } stats;
164
177
 
165
178
  struct {
@@ -171,7 +184,21 @@ typedef struct {
171
184
  } gc_tracking;
172
185
  } thread_context_collector_state;
173
186
 
174
- // Tracks per-thread state
187
+ // Tracks per-thread state.
188
+ // This state is global and lives forever on the Ruby Thread (until the Thread is GC'd).
189
+ // The state is created early on for all threads on the main Ractor
190
+ // (enabling a TracePoint only enables it for the current Ractor).
191
+ // The state is either created when the Thread starts running (via the RUBY_EVENT_THREAD_BEGIN TracePoint),
192
+ // or via `thread_context_collector_reset_all_per_thread_contexts` when the profiler starts.
193
+ //
194
+ // Unfortunately that RUBY_EVENT_THREAD_BEGIN TracePoint still fires after some other events:
195
+ // * RUBY_INTERNAL_THREAD_EVENT_RESUMED for the Thread acquiring the GVL for the first time
196
+ // * an early SIGPROF calling handle_sampling_signal()
197
+ // * Ruby might check for interrupts and run postponed jobs (e.g. thread_context_collector_sample)
198
+ // * another RUBY_EVENT_THREAD_BEGIN TracePoint which might run before ours
199
+ // * etc
200
+ // For those cases we have to ignore those events and we cannot assume the state is always set,
201
+ // however this only matters for a very short period when a thread starts.
175
202
  struct per_thread_context {
176
203
  sampling_buffer sampling_buffer;
177
204
  char thread_id[THREAD_ID_LIMIT_CHARS];
@@ -179,8 +206,8 @@ struct per_thread_context {
179
206
  char thread_invoke_location[THREAD_INVOKE_LOCATION_LIMIT_CHARS];
180
207
  ddog_CharSlice thread_invoke_location_char_slice;
181
208
  thread_cpu_time_id thread_cpu_time_id;
182
- long cpu_time_at_previous_sample_ns; // Can be INVALID_TIME until initialized or if getting it fails for another reason
183
- long wall_time_at_previous_sample_ns; // Can be INVALID_TIME until initialized
209
+ long cpu_time_at_previous_sample_ns;
210
+ long wall_time_at_previous_sample_ns;
184
211
 
185
212
  // There are 3 possible states for the GVL (per thread), and 3 transitions for which we receive GVL events:
186
213
  // Thread holds the GVL
@@ -233,6 +260,14 @@ struct per_thread_context {
233
260
  // but this is deemed worth it for this optimization. In any case we don't know exactly
234
261
  // at what time a thread was doing CPU work (unless it's on CPU 100% of the time).
235
262
  bool was_skipped_at_last_sample;
263
+ // Set as true for CpuAndWallTimeWorker and IdleSamplingHelper threads.
264
+ // When true, per-tick samples are skipped entirely; the thread is sampled only once per
265
+ // reporting period during the on_serialize flush.
266
+ //
267
+ // These threads are always in native code so their stacks aren't interesting;
268
+ // the Profiling::Scheduler thread on the other hand does a lot of different
269
+ // things using a mix of Ruby and native code, so that one isn't considered internal.
270
+ bool is_profiler_internal_thread;
236
271
 
237
272
  struct {
238
273
  // Both of these fields are set by on_gc_start and kept until on_gc_finish is called.
@@ -270,7 +305,6 @@ static void update_metrics_and_sample(
270
305
  thread_context_collector_state *state,
271
306
  VALUE thread_being_sampled,
272
307
  per_thread_context *thread_context,
273
- sampling_buffer* sampling_buffer,
274
308
  long current_cpu_time_ns,
275
309
  long current_monotonic_wall_time_ns,
276
310
  bool force_sample
@@ -279,7 +313,6 @@ static void trigger_sample_for_thread(
279
313
  thread_context_collector_state *state,
280
314
  VALUE thread_being_sampled,
281
315
  per_thread_context *thread_context,
282
- sampling_buffer* sampling_buffer,
283
316
  sample_values values,
284
317
  long current_monotonic_wall_time_ns,
285
318
  ddog_CharSlice *ruby_vm_type,
@@ -288,14 +321,16 @@ static void trigger_sample_for_thread(
288
321
  bool is_safe_to_allocate_objects
289
322
  );
290
323
  static VALUE _native_thread_list(VALUE self);
291
- static per_thread_context *get_or_create_context_for(VALUE thread, thread_context_collector_state *state);
292
- static void initialize_context(VALUE thread, per_thread_context *thread_context, thread_context_collector_state *state);
324
+ static void check_frozen_thread(VALUE thread);
325
+ static per_thread_context *get_or_create_context_for(VALUE thread);
326
+ static void initialize_context(VALUE thread, per_thread_context *thread_context);
293
327
  static VALUE _native_inspect(VALUE self, VALUE collector_instance);
294
328
  static VALUE per_thread_context_to_ruby_hash(per_thread_context *thread_context);
295
329
  static VALUE stats_to_ruby_hash(thread_context_collector_state *state, VALUE hash);
296
330
  static VALUE gc_tracking_as_ruby_hash(thread_context_collector_state *state);
297
331
  static VALUE _native_per_thread_context(VALUE self, VALUE collector_instance);
298
- static long update_time_since_previous_sample(long *time_at_previous_sample_ns, long current_time_ns, long gc_start_time_ns, bool is_wall_time);
332
+ static long update_cpu_time_since_previous_sample(per_thread_context *thread_context, long current_cpu_time_ns);
333
+ static long update_wall_time_since_previous_sample(per_thread_context *thread_context, long current_wall_time_ns);
299
334
  static long cpu_time_now_ns(per_thread_context *thread_context);
300
335
  static long thread_id_for(VALUE thread);
301
336
  static VALUE _native_stats(VALUE self, VALUE collector_instance);
@@ -326,17 +361,16 @@ static bool handle_gvl_waiting(
326
361
  thread_context_collector_state *state,
327
362
  VALUE thread_being_sampled,
328
363
  per_thread_context *thread_context,
329
- sampling_buffer* sampling_buffer,
330
364
  long current_cpu_time_ns
331
365
  );
366
+ static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread);
367
+ static VALUE _native_on_gvl_released(DDTRACE_UNUSED VALUE self, VALUE thread);
332
368
  #ifndef NO_GVL_INSTRUMENTATION
333
- static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread);
334
369
  static VALUE _native_gvl_waiting_at_for(DDTRACE_UNUSED VALUE self, VALUE thread);
335
370
  static VALUE _native_on_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread);
336
- static VALUE _native_on_gvl_released(DDTRACE_UNUSED VALUE self, VALUE thread);
337
371
  static VALUE _native_sample_after_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread, VALUE allow_exception);
338
- static VALUE _native_apply_delta_to_cpu_time_at_previous_sample_ns(DDTRACE_UNUSED VALUE self, VALUE thread, VALUE delta_ns);
339
372
  #endif
373
+ static VALUE _native_apply_delta_to_cpu_time_at_previous_sample_ns(DDTRACE_UNUSED VALUE self, VALUE thread, VALUE delta_ns);
340
374
  static void otel_without_ddtrace_trace_identifiers_for(
341
375
  thread_context_collector_state *state,
342
376
  VALUE thread,
@@ -348,12 +382,16 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id);
348
382
  static VALUE safely_lookup_hash_without_going_into_ruby_code(VALUE hash, VALUE key);
349
383
  static VALUE _native_system_epoch_time_now_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
350
384
  static VALUE _native_prepare_sample_inside_signal_handler(DDTRACE_UNUSED VALUE self);
351
- static VALUE _native_clear_per_thread_context_for(DDTRACE_UNUSED VALUE self, VALUE thread);
352
- static bool skip_sample(thread_context_collector_state *state, per_thread_context *thread_context, bool is_gvl_waiting_state, bool force_sample_suspended);
385
+ static VALUE _native_mark_thread_as_profiler_internal(DDTRACE_UNUSED VALUE self, VALUE thread);
386
+ static VALUE _native_remove_per_thread_context_for(DDTRACE_UNUSED VALUE self, VALUE thread);
387
+ static VALUE _native_global_reset_per_thread_context(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
388
+ static bool skip_sample(thread_context_collector_state *state, per_thread_context *thread_context, bool is_gvl_waiting_state, bool force_sample);
389
+ static void on_thread_begin_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused);
353
390
 
354
391
  void collectors_thread_context_init(VALUE profiling_module) {
355
392
  VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
356
393
  VALUE collectors_thread_context_class = rb_define_class_under(collectors_module, "ThreadContext", rb_cObject);
394
+
357
395
  // Hosts methods used for testing the native code using RSpec
358
396
  VALUE testing_module = rb_define_module_under(collectors_thread_context_class, "Testing");
359
397
 
@@ -383,15 +421,17 @@ void collectors_thread_context_init(VALUE profiling_module) {
383
421
  rb_define_singleton_method(testing_module, "_native_sample_skipped_allocation_samples", _native_sample_skipped_allocation_samples, 2);
384
422
  rb_define_singleton_method(testing_module, "_native_system_epoch_time_now_ns", _native_system_epoch_time_now_ns, 1);
385
423
  rb_define_singleton_method(testing_module, "_native_prepare_sample_inside_signal_handler", _native_prepare_sample_inside_signal_handler, 0);
386
- rb_define_singleton_method(testing_module, "_native_clear_per_thread_context_for", _native_clear_per_thread_context_for, 1);
424
+ rb_define_singleton_method(testing_module, "_native_remove_per_thread_context_for", _native_remove_per_thread_context_for, 1);
425
+ rb_define_singleton_method(testing_module, "_native_global_reset_per_thread_context", _native_global_reset_per_thread_context, 1);
426
+ rb_define_singleton_method(testing_module, "_native_mark_thread_as_profiler_internal", _native_mark_thread_as_profiler_internal, 1);
427
+ rb_define_singleton_method(testing_module, "_native_on_gvl_waiting", _native_on_gvl_waiting, 1);
428
+ rb_define_singleton_method(testing_module, "_native_on_gvl_released", _native_on_gvl_released, 1);
387
429
  #ifndef NO_GVL_INSTRUMENTATION
388
- rb_define_singleton_method(testing_module, "_native_on_gvl_waiting", _native_on_gvl_waiting, 1);
389
430
  rb_define_singleton_method(testing_module, "_native_gvl_waiting_at_for", _native_gvl_waiting_at_for, 1);
390
431
  rb_define_singleton_method(testing_module, "_native_on_gvl_running", _native_on_gvl_running, 2);
391
- rb_define_singleton_method(testing_module, "_native_on_gvl_released", _native_on_gvl_released, 1);
392
432
  rb_define_singleton_method(testing_module, "_native_sample_after_gvl_running", _native_sample_after_gvl_running, 3);
393
- rb_define_singleton_method(testing_module, "_native_apply_delta_to_cpu_time_at_previous_sample_ns", _native_apply_delta_to_cpu_time_at_previous_sample_ns, 2);
394
433
  #endif
434
+ rb_define_singleton_method(testing_module, "_native_apply_delta_to_cpu_time_at_previous_sample_ns", _native_apply_delta_to_cpu_time_at_previous_sample_ns, 2);
395
435
 
396
436
  at_active_span_id = rb_intern_const("@active_span");
397
437
  at_active_trace_id = rb_intern_const("@active_trace");
@@ -417,6 +457,8 @@ void collectors_thread_context_init(VALUE profiling_module) {
417
457
  // This will raise if Ruby already ran out of thread-local keys
418
458
  per_thread_context_tls_init();
419
459
 
460
+ rb_global_variable(&thread_begin_tracepoint);
461
+
420
462
  gc_profiling_init();
421
463
  }
422
464
 
@@ -453,7 +495,7 @@ static void thread_context_collector_typed_data_free(void *state_ptr) {
453
495
 
454
496
  // Important: Remember that we're only guaranteed to see here what's been set in _native_new, aka
455
497
  // pointers that have been set NULL there may still be NULL here.
456
- if (state->locations.ptr != NULL) ruby_xfree(state->locations.ptr);
498
+ sample_locations_free(&state->locations);
457
499
 
458
500
  st_free_table(state->native_filenames_cache);
459
501
 
@@ -485,17 +527,24 @@ static void per_thread_context_typed_data_free(void *ctx_ptr) {
485
527
  free(ctx);
486
528
  }
487
529
 
488
- static VALUE _native_clear_per_thread_context_for(DDTRACE_UNUSED VALUE self, VALUE thread) {
530
+ // Only for testing: removes the per-thread context without recreating it, so the thread has no context.
531
+ // This simulates situations where we observe a thread running before our RUBY_EVENT_THREAD_BEGIN tracepoint fires,
532
+ // so that we can test our `get_per_thread_context(...) => null` code paths.
533
+ static VALUE _native_remove_per_thread_context_for(DDTRACE_UNUSED VALUE self, VALUE thread) {
534
+ check_frozen_thread(thread);
489
535
  per_thread_context *ctx = get_per_thread_context(thread);
490
536
  if (ctx != NULL) {
491
537
  set_per_thread_context(thread, NULL);
492
- if (!RB_OBJ_FROZEN(thread)) {
493
- rb_ivar_set(thread, dd_per_thread_context_id, Qnil);
494
- }
538
+ rb_ivar_set(thread, dd_per_thread_context_id, Qnil);
495
539
  }
496
540
  return Qnil;
497
541
  }
498
542
 
543
+ static VALUE _native_global_reset_per_thread_context(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
544
+ thread_context_collector_reset_all_per_thread_contexts(collector_instance);
545
+ return Qnil;
546
+ }
547
+
499
548
  static VALUE _native_new(VALUE klass) {
500
549
  thread_context_collector_state *state = ruby_xcalloc(1, sizeof(thread_context_collector_state));
501
550
 
@@ -503,14 +552,13 @@ static VALUE _native_new(VALUE klass) {
503
552
  // being leaked.
504
553
 
505
554
  // Update this when modifying state struct
506
- state->locations.ptr = NULL;
507
- state->locations.len = 0;
508
555
  state->recorder_instance = Qnil;
509
556
  state->tracer_context_key = MISSING_TRACER_CONTEXT_KEY;
510
557
  VALUE thread_list_buffer = rb_ary_new();
511
558
  state->thread_list_buffer = thread_list_buffer;
512
559
  state->endpoint_collection_enabled = true;
513
560
  state->native_filenames_enabled = false;
561
+ state->show_classes = false;
514
562
  state->native_filenames_cache = st_init_numtable();
515
563
  state->otel_context_enabled = OTEL_CONTEXT_ENABLED_FALSE;
516
564
  state->otel_context_source = OTEL_CONTEXT_SOURCE_UNKNOWN;
@@ -546,24 +594,28 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
546
594
  VALUE waiting_for_gvl_threshold_ns = rb_hash_fetch(options, ID2SYM(rb_intern("waiting_for_gvl_threshold_ns")));
547
595
  VALUE otel_context_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("otel_context_enabled")));
548
596
  VALUE native_filenames_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("native_filenames_enabled")));
597
+ VALUE show_classes = rb_hash_fetch(options, ID2SYM(rb_intern("show_classes")));
549
598
  VALUE overhead_filename = rb_hash_fetch(options, ID2SYM(rb_intern("overhead_filename")));
550
599
 
551
600
  ENFORCE_TYPE(max_frames, T_FIXNUM);
552
601
  ENFORCE_BOOLEAN(endpoint_collection_enabled);
553
602
  ENFORCE_TYPE(waiting_for_gvl_threshold_ns, T_FIXNUM);
554
603
  ENFORCE_BOOLEAN(native_filenames_enabled);
604
+ ENFORCE_BOOLEAN(show_classes);
555
605
  ENFORCE_TYPE(overhead_filename, T_STRING);
556
606
 
607
+ uint16_t max_frame_int = sampling_buffer_check_max_frames(NUM2INT(max_frames));
608
+
557
609
  thread_context_collector_state *state;
558
610
  TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
559
611
 
560
612
  // Update this when modifying state struct
561
- state->locations.len = sampling_buffer_check_max_frames(NUM2INT(max_frames));
562
- state->locations.ptr = ruby_xcalloc(state->locations.len, sizeof(ddog_prof_Location));
613
+ sample_locations_initialize(&state->locations, max_frame_int, show_classes == Qtrue);
563
614
  state->recorder_instance = enforce_recorder_instance(recorder_instance);
564
615
  recorder_install_on_serialize(recorder_instance, self_instance);
565
616
  state->endpoint_collection_enabled = (endpoint_collection_enabled == Qtrue);
566
617
  state->native_filenames_enabled = (native_filenames_enabled == Qtrue);
618
+ state->show_classes = (show_classes == Qtrue);
567
619
  state->overhead_filename = overhead_filename;
568
620
  if (otel_context_enabled == Qfalse || otel_context_enabled == Qnil) {
569
621
  state->otel_context_enabled = OTEL_CONTEXT_ENABLED_FALSE;
@@ -585,6 +637,11 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
585
637
  state->tracer_context_key = rb_to_id(tracer_context_key);
586
638
  }
587
639
 
640
+ if (thread_begin_tracepoint == Qnil) {
641
+ thread_begin_tracepoint = rb_tracepoint_new(Qnil, RUBY_EVENT_THREAD_BEGIN, on_thread_begin_event, NULL);
642
+ rb_tracepoint_enable(thread_begin_tracepoint);
643
+ }
644
+
588
645
  return Qtrue;
589
646
  }
590
647
 
@@ -643,17 +700,8 @@ static void record_sampling_overhead(thread_context_collector_state *state, per_
643
700
  long wall_time_after_sampling = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
644
701
  long cpu_time_after_sampling = cpu_time_now_ns(current_thread_context);
645
702
 
646
- long overhead_cpu_time_ns = update_time_since_previous_sample(
647
- &current_thread_context->cpu_time_at_previous_sample_ns,
648
- cpu_time_after_sampling,
649
- current_thread_context->gc_tracking.cpu_time_at_start_ns,
650
- IS_CPU_TIME);
651
-
652
- long overhead_wall_time_ns = update_time_since_previous_sample(
653
- &current_thread_context->wall_time_at_previous_sample_ns,
654
- wall_time_after_sampling,
655
- INVALID_TIME,
656
- IS_WALL_TIME);
703
+ long overhead_cpu_time_ns = update_cpu_time_since_previous_sample(current_thread_context, cpu_time_after_sampling);
704
+ long overhead_wall_time_ns = update_wall_time_since_previous_sample(current_thread_context, wall_time_after_sampling);
657
705
 
658
706
  ddog_prof_Label overhead_labels[] = {
659
707
  {.key = DDOG_CHARSLICE_C("thread id"), .str = DDOG_CHARSLICE_C("0"), .num = 0},
@@ -698,7 +746,7 @@ void thread_context_collector_sample(VALUE self_instance, long current_monotonic
698
746
  TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
699
747
 
700
748
  VALUE current_thread = rb_thread_current();
701
- per_thread_context *current_thread_context = get_or_create_context_for(current_thread, state);
749
+ per_thread_context *current_thread_context = get_or_create_context_for(current_thread);
702
750
  long cpu_time_at_sample_start_for_current_thread = cpu_time_now_ns(current_thread_context);
703
751
 
704
752
  VALUE threads = thread_list(state);
@@ -706,7 +754,7 @@ void thread_context_collector_sample(VALUE self_instance, long current_monotonic
706
754
  const long thread_count = RARRAY_LEN(threads);
707
755
  for (long i = 0; i < thread_count; i++) {
708
756
  VALUE thread = RARRAY_AREF(threads, i);
709
- per_thread_context *thread_context = get_or_create_context_for(thread, state);
757
+ per_thread_context *thread_context = get_or_create_context_for(thread);
710
758
 
711
759
  // We account for cpu-time for the current thread in a different way: we use the cpu-time at sampling start,
712
760
  // to avoid blaming the time the profiler took on whatever is currently running on the thread,
@@ -717,44 +765,41 @@ void thread_context_collector_sample(VALUE self_instance, long current_monotonic
717
765
  state,
718
766
  thread,
719
767
  thread_context,
720
- &thread_context->sampling_buffer,
721
768
  current_cpu_time_ns,
722
769
  current_monotonic_wall_time_ns,
723
770
  false);
724
771
  }
725
772
 
726
773
  state->stats.sample_count++;
727
- record_sampling_overhead(state, current_thread_context);
774
+
775
+ // If the current thread is a profiler-internal thread, we don't use record_sampling_overhead()
776
+ // and accept the sampling overhead is attributed to the profiler-internal thread
777
+ // (both are under the same datadog group in the flamegraph).
778
+ // The reason we need to do this is profiler-internal threads are skipped during per-tick sampling
779
+ // so their timestamps are not updated. record_sampling_overhead() would see the stale previous-sample timestamps and
780
+ // attribute the entire sleep interval incorrectly as overhead instead of just the sampling work.
781
+ if (!current_thread_context->is_profiler_internal_thread) {
782
+ record_sampling_overhead(state, current_thread_context);
783
+ }
728
784
  }
729
785
 
730
786
  static void update_metrics_and_sample(
731
787
  thread_context_collector_state *state,
732
788
  VALUE thread_being_sampled,
733
789
  per_thread_context *thread_context,
734
- sampling_buffer* sampling_buffer,
735
790
  long current_cpu_time_ns,
736
791
  long current_monotonic_wall_time_ns,
737
- bool force_sample_suspended
792
+ bool force_sample
738
793
  ) {
739
794
  bool is_gvl_waiting_state =
740
- handle_gvl_waiting(state, thread_being_sampled, thread_context, sampling_buffer, current_cpu_time_ns);
795
+ handle_gvl_waiting(state, thread_being_sampled, thread_context, current_cpu_time_ns);
741
796
 
742
- if (skip_sample(state, thread_context, is_gvl_waiting_state, force_sample_suspended)) return;
797
+ if (skip_sample(state, thread_context, is_gvl_waiting_state, force_sample)) return;
743
798
 
744
799
  // Don't assign/update cpu during "Waiting for GVL"
745
- long cpu_time_elapsed_ns = is_gvl_waiting_state ? 0 : update_time_since_previous_sample(
746
- &thread_context->cpu_time_at_previous_sample_ns,
747
- current_cpu_time_ns,
748
- thread_context->gc_tracking.cpu_time_at_start_ns,
749
- IS_CPU_TIME
750
- );
800
+ long cpu_time_elapsed_ns = is_gvl_waiting_state ? 0 : update_cpu_time_since_previous_sample(thread_context, current_cpu_time_ns);
751
801
 
752
- long wall_time_elapsed_ns = update_time_since_previous_sample(
753
- &thread_context->wall_time_at_previous_sample_ns,
754
- current_monotonic_wall_time_ns,
755
- INVALID_TIME,
756
- IS_WALL_TIME
757
- );
802
+ long wall_time_elapsed_ns = update_wall_time_since_previous_sample(thread_context, current_monotonic_wall_time_ns);
758
803
 
759
804
  // A thread enters "Waiting for GVL", well, as the name implies, without the GVL.
760
805
  //
@@ -775,7 +820,6 @@ static void update_metrics_and_sample(
775
820
  state,
776
821
  thread_being_sampled,
777
822
  thread_context,
778
- sampling_buffer,
779
823
  (sample_values) {.cpu_time_ns = cpu_time_elapsed_ns, .cpu_or_wall_samples = 1, .wall_time_ns = wall_time_elapsed_ns},
780
824
  current_monotonic_wall_time_ns,
781
825
  NULL,
@@ -785,7 +829,12 @@ static void update_metrics_and_sample(
785
829
  );
786
830
  }
787
831
 
788
- static bool skip_sample(thread_context_collector_state *state, per_thread_context *thread_context, bool is_gvl_waiting_state, bool force_sample_suspended) {
832
+ static bool skip_sample(thread_context_collector_state *state, per_thread_context *thread_context, bool is_gvl_waiting_state, bool force_sample) {
833
+ if (!force_sample && thread_context->is_profiler_internal_thread) {
834
+ state->stats.profiler_thread_samples_skipped++;
835
+ return true;
836
+ }
837
+
789
838
  // Racy read but harmless, can only cause an extra sample
790
839
  uint64_t gvl_state_change_count = thread_context->gvl_state_change_count;
791
840
 
@@ -797,7 +846,7 @@ static bool skip_sample(thread_context_collector_state *state, per_thread_contex
797
846
  // in handle_gvl_waiting (situation 1 extra sample, situation 2 regular sample) keeps running.
798
847
  // TODO: we could probably also skip while "Waiting for GVL"
799
848
  if (!is_gvl_waiting_state &&
800
- !force_sample_suspended &&
849
+ !force_sample &&
801
850
  (gvl_state_change_count & GVL_SUSPENDED) &&
802
851
  gvl_state_change_count == thread_context->gvl_state_change_count_at_previous_sample) {
803
852
  state->stats.inactive_thread_samples_skipped++;
@@ -829,10 +878,8 @@ void thread_context_collector_on_gc_start(VALUE self_instance) {
829
878
 
830
879
  per_thread_context *thread_context = get_per_thread_context(rb_thread_current());
831
880
 
832
- // If there was no previously-existing context for this thread, we won't allocate one (see safety). For now we just drop
833
- // the GC sample, under the assumption that "a thread that is so new that we never sampled it even once before it triggers
834
- // GC" is a rare enough case that we can just ignore it.
835
- // We can always improve this later if we find that this happens often (and we have the counter to help us figure that out)!
881
+ // Context is created eagerly via on_thread_begin_event, so this should not normally be NULL.
882
+ // We can't get_or_create_context_for() here since we can't allocate (GC context).
836
883
  if (thread_context == NULL) {
837
884
  state->stats.gc_samples_missed_due_to_missing_context++;
838
885
  return;
@@ -862,8 +909,7 @@ bool thread_context_collector_on_gc_finish(VALUE self_instance) {
862
909
 
863
910
  per_thread_context *thread_context = get_per_thread_context(rb_thread_current());
864
911
 
865
- // If there was no previously-existing context for this thread, we won't allocate one (see safety). We keep a metric for
866
- // how often this happens -- see on_gc_start.
912
+ // Context is created eagerly, so this should not normally be NULL (see on_gc_start).
867
913
  if (thread_context == NULL) return false;
868
914
 
869
915
  long cpu_time_at_start_ns = thread_context->gc_tracking.cpu_time_at_start_ns;
@@ -904,9 +950,9 @@ bool thread_context_collector_on_gc_finish(VALUE self_instance) {
904
950
  state->gc_tracking.accumulated_wall_time_ns += gc_wall_time_elapsed_ns;
905
951
  state->gc_tracking.wall_time_at_previous_gc_ns = wall_time_at_finish_ns;
906
952
 
907
- // Update cpu-time accounting so it doesn't include the cpu-time spent in GC during the next sample
908
- // We don't update the wall-time because we don't subtract the wall-time spent in GC (see call to
909
- // `update_time_since_previous_sample` for wall-time in `update_metrics_and_sample`).
953
+ // Update cpu-time accounting so it doesn't include the cpu-time spent in GC during the next sample.
954
+ // We don't do the same for wall-time, because GC is just like any other reason a thread didn't make
955
+ // progress -- time always goes forward regardless of the thread making progress on what it wanted.
910
956
  if (thread_context->cpu_time_at_previous_sample_ns != INVALID_TIME) {
911
957
  thread_context->cpu_time_at_previous_sample_ns += gc_cpu_time_elapsed_ns;
912
958
  }
@@ -982,7 +1028,6 @@ static void trigger_sample_for_thread(
982
1028
  thread_context_collector_state *state,
983
1029
  VALUE thread_being_sampled,
984
1030
  per_thread_context *thread_context,
985
- sampling_buffer* sampling_buffer,
986
1031
  sample_values values,
987
1032
  long current_monotonic_wall_time_ns,
988
1033
  // These two labels are only used for allocation profiling; @ivoanjo: may want to refactor this at some point?
@@ -1104,7 +1149,7 @@ static void trigger_sample_for_thread(
1104
1149
 
1105
1150
  sample_thread(
1106
1151
  thread_being_sampled,
1107
- sampling_buffer,
1152
+ &thread_context->sampling_buffer,
1108
1153
  state->locations,
1109
1154
  state->recorder_instance,
1110
1155
  values,
@@ -1115,7 +1160,8 @@ static void trigger_sample_for_thread(
1115
1160
  .is_gvl_waiting_state = is_gvl_waiting_state,
1116
1161
  },
1117
1162
  state->native_filenames_enabled,
1118
- state->native_filenames_cache
1163
+ state->native_filenames_cache,
1164
+ state->show_classes
1119
1165
  );
1120
1166
  }
1121
1167
 
@@ -1133,17 +1179,22 @@ static VALUE _native_thread_list(DDTRACE_UNUSED VALUE _self) {
1133
1179
  return result;
1134
1180
  }
1135
1181
 
1182
+ static void check_frozen_thread(VALUE thread) {
1183
+ if (RB_OBJ_FROZEN(thread)) {
1184
+ raise_error(rb_eFrozenError, "Cannot setup profiler state for Thread %"PRIsVALUE" because it is frozen. Please avoid freezing Thread instances and/or report the issue to dd-trace-rb", thread);
1185
+ }
1186
+ }
1187
+
1188
+ // See the docs on struct per_thread_context.
1136
1189
  // This allocates a Ruby object and therefore needs the GVL and is not safe to call from RUBY_INTERNAL_EVENT_* hooks.
1137
- static per_thread_context *get_or_create_context_for(VALUE thread, thread_context_collector_state *state) {
1190
+ static per_thread_context *get_or_create_context_for(VALUE thread) {
1138
1191
  per_thread_context *thread_context = get_per_thread_context(thread);
1139
1192
  if (thread_context != NULL) return thread_context;
1140
1193
 
1141
- if (RB_OBJ_FROZEN(thread)) {
1142
- raise_error(rb_eFrozenError, "Cannot setup profiler state for Thread %"PRIsVALUE" because it is frozen. Please avoid freezing Thread instances and/or report the issue to dd-trace-rb", thread);
1143
- }
1194
+ check_frozen_thread(thread);
1144
1195
 
1145
1196
  thread_context = calloc(1, sizeof(per_thread_context)); // See "note on calloc vs ruby_xcalloc use" in heap_recorder.c
1146
- initialize_context(thread, thread_context, state);
1197
+ initialize_context(thread, thread_context);
1147
1198
 
1148
1199
  VALUE wrapper = TypedData_Wrap_Struct(rb_cObject, &per_thread_context_typed_data, thread_context);
1149
1200
  rb_ivar_set(thread, dd_per_thread_context_id, wrapper);
@@ -1152,6 +1203,14 @@ static per_thread_context *get_or_create_context_for(VALUE thread, thread_contex
1152
1203
  return thread_context;
1153
1204
  }
1154
1205
 
1206
+ static void on_thread_begin_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused) {
1207
+ if (!ddtrace_rb_ractor_main_p()) return;
1208
+
1209
+ VALUE thread = rb_tracearg_self(rb_tracearg_from_tracepoint(tracepoint_data));
1210
+ ENFORCE_THREAD(thread);
1211
+ get_or_create_context_for(thread);
1212
+ }
1213
+
1155
1214
  #define LOGGING_GEM_PATH "/lib/logging/diagnostic_context.rb"
1156
1215
 
1157
1216
  // The `logging` gem monkey patches thread creation, which makes the `invoke_location_for` useless, since every thread
@@ -1173,8 +1232,11 @@ static bool is_logging_gem_monkey_patch(VALUE invoke_file_location) {
1173
1232
  return strncmp(invoke_file + invoke_file_len - logging_gem_path_len, LOGGING_GEM_PATH, logging_gem_path_len) == 0;
1174
1233
  }
1175
1234
 
1176
- static void initialize_context(VALUE thread, per_thread_context *thread_context, thread_context_collector_state *state) {
1177
- sampling_buffer_initialize(&thread_context->sampling_buffer, state->locations.len);
1235
+ static void initialize_context(VALUE thread, per_thread_context *thread_context) {
1236
+ // We always create per_thread_context's with latest_max_frames; that value is kept in sync with the
1237
+ // active profiler's max_frames by the global reset that runs when profiling starts
1238
+ // so we expect to always see here the latest correct value to be used.
1239
+ sampling_buffer_initialize(&thread_context->sampling_buffer, latest_max_frames);
1178
1240
 
1179
1241
  snprintf(thread_context->thread_id, THREAD_ID_LIMIT_CHARS, "%"PRIu64" (%lu)", native_thread_id_for(thread), (unsigned long) thread_id_for(thread));
1180
1242
  thread_context->thread_id_char_slice = (ddog_CharSlice) {.ptr = thread_context->thread_id, .len = strlen(thread_context->thread_id)};
@@ -1193,7 +1255,7 @@ static void initialize_context(VALUE thread, per_thread_context *thread_context,
1193
1255
  } else {
1194
1256
  snprintf(thread_context->thread_invoke_location, THREAD_INVOKE_LOCATION_LIMIT_CHARS, "%s", "(Unnamed thread)");
1195
1257
  }
1196
- } else if (thread != state->main_thread) {
1258
+ } else if (thread != rb_thread_main()) {
1197
1259
  // If the first function of a thread is native code, there won't be an invoke location, so we use this fallback.
1198
1260
  // NOTE: In the future, I wonder if we could take the pointer to the native function, and try to see if there's a native
1199
1261
  // symbol attached to it.
@@ -1207,9 +1269,8 @@ static void initialize_context(VALUE thread, per_thread_context *thread_context,
1207
1269
 
1208
1270
  thread_context->thread_cpu_time_id = thread_cpu_time_id_for(thread);
1209
1271
 
1210
- // These will get initialized during actual sampling
1211
- thread_context->cpu_time_at_previous_sample_ns = INVALID_TIME;
1212
- thread_context->wall_time_at_previous_sample_ns = INVALID_TIME;
1272
+ thread_context->wall_time_at_previous_sample_ns = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
1273
+ thread_context->cpu_time_at_previous_sample_ns = cpu_time_now_ns(thread_context);
1213
1274
 
1214
1275
  // These will only be used during a GC operation
1215
1276
  thread_context->gc_tracking.cpu_time_at_start_ns = INVALID_TIME;
@@ -1219,6 +1280,45 @@ static void initialize_context(VALUE thread, per_thread_context *thread_context,
1219
1280
  thread_context->gvl_state_change_count = 0;
1220
1281
  }
1221
1282
 
1283
+ // This MUST be called before profiling starts, so that a new profiler session starts from a fresh state and never
1284
+ // observes or includes any leftover stale state from a previous session.
1285
+ // Such a call MUST happen while the CpuAndWallTimeWorker is stopped (e.g. no tracepoints active, no signals
1286
+ // triggering samples, no gvl hooks, etc).
1287
+ //
1288
+ // It updates the global `latest_max_frames` from the given (latest) ThreadContext and (re)creates every per-thread
1289
+ // context's sampling buffer sized accordingly, so the buffers always match the collector that's about to start
1290
+ // sampling -- even if a previous session used a different max_frames.
1291
+ void thread_context_collector_reset_all_per_thread_contexts(VALUE self_instance) {
1292
+ thread_context_collector_state *state;
1293
+ TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
1294
+
1295
+ // Update global max frames to be used when allocating sampling buffers
1296
+ if (state->locations.len == 0) {
1297
+ raise_error(rb_eRuntimeError, "BUG: Unexpected locations.len == 0. Is this ThreadContext not initialized?");
1298
+ }
1299
+
1300
+ latest_max_frames = state->locations.len;
1301
+
1302
+ VALUE threads = thread_list(state);
1303
+ const long thread_count = RARRAY_LEN(threads);
1304
+ for (long i = 0; i < thread_count; i++) {
1305
+ VALUE thread = rb_ary_entry(threads, i);
1306
+ per_thread_context *thread_context = get_per_thread_context(thread);
1307
+ if (thread_context != NULL) {
1308
+ bool is_profiler_internal_thread = thread_context->is_profiler_internal_thread;
1309
+
1310
+ sampling_buffer_free(&thread_context->sampling_buffer);
1311
+ memset(thread_context, 0, sizeof(per_thread_context));
1312
+ initialize_context(thread, thread_context);
1313
+
1314
+ thread_context->is_profiler_internal_thread = is_profiler_internal_thread;
1315
+ } else {
1316
+ // If thread didn't have a context, let's trigger its creation
1317
+ get_or_create_context_for(thread);
1318
+ }
1319
+ }
1320
+ }
1321
+
1222
1322
  static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
1223
1323
  thread_context_collector_state *state;
1224
1324
  TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
@@ -1233,6 +1333,7 @@ static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instanc
1233
1333
  rb_str_concat(result, rb_sprintf(" stats=%"PRIsVALUE, stats_to_ruby_hash(state, rb_hash_new())));
1234
1334
  rb_str_concat(result, rb_sprintf(" endpoint_collection_enabled=%"PRIsVALUE, state->endpoint_collection_enabled ? Qtrue : Qfalse));
1235
1335
  rb_str_concat(result, rb_sprintf(" native_filenames_enabled=%"PRIsVALUE, state->native_filenames_enabled ? Qtrue : Qfalse));
1336
+ rb_str_concat(result, rb_sprintf(" show_classes=%"PRIsVALUE, state->show_classes ? Qtrue : Qfalse));
1236
1337
  // Note: `st_table_size()` is available from Ruby 3.2+ but not before
1237
1338
  rb_str_concat(result, rb_sprintf(" native_filenames_cache_size=%zu", state->native_filenames_cache->num_entries));
1238
1339
  rb_str_concat(result, rb_sprintf(" otel_context_enabled=%d", state->otel_context_enabled));
@@ -1271,6 +1372,7 @@ static VALUE per_thread_context_to_ruby_hash(per_thread_context *thread_context)
1271
1372
  ID2SYM(rb_intern("gvl_state_change_count")), /* => */ ULL2NUM(thread_context->gvl_state_change_count),
1272
1373
  ID2SYM(rb_intern("gvl_state_change_count_at_previous_sample")), /* => */ ULL2NUM(thread_context->gvl_state_change_count_at_previous_sample),
1273
1374
  ID2SYM(rb_intern("was_skipped_at_last_sample")), /* => */ thread_context->was_skipped_at_last_sample ? Qtrue : Qfalse,
1375
+ ID2SYM(rb_intern("is_profiler_internal_thread")), /* => */ thread_context->is_profiler_internal_thread ? Qtrue : Qfalse,
1274
1376
  };
1275
1377
  for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(context_as_hash, arguments[i], arguments[i+1]);
1276
1378
 
@@ -1284,6 +1386,7 @@ static VALUE stats_to_ruby_hash(thread_context_collector_state *state, VALUE has
1284
1386
  ID2SYM(rb_intern("gc_samples")), /* => */ UINT2NUM(state->stats.gc_samples),
1285
1387
  ID2SYM(rb_intern("gc_samples_missed_due_to_missing_context")), /* => */ UINT2NUM(state->stats.gc_samples_missed_due_to_missing_context),
1286
1388
  ID2SYM(rb_intern("inactive_thread_samples_skipped")), /* => */ UINT2NUM(state->stats.inactive_thread_samples_skipped),
1389
+ ID2SYM(rb_intern("profiler_thread_samples_skipped")), /* => */ UINT2NUM(state->stats.profiler_thread_samples_skipped),
1287
1390
  };
1288
1391
  for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(hash, arguments[i], arguments[i+1]);
1289
1392
  return hash;
@@ -1323,48 +1426,59 @@ static VALUE _native_per_thread_context(DDTRACE_UNUSED VALUE _self, VALUE collec
1323
1426
  return result;
1324
1427
  }
1325
1428
 
1326
- // gc_start_time_ns should only be passed if IS_CPU_TIME
1327
- static long update_time_since_previous_sample(long *time_at_previous_sample_ns, long current_time_ns, long gc_start_time_ns, bool is_wall_time) {
1429
+ static long update_time_since_previous_sample(long *time_at_previous_sample_ns, long current_time_ns, per_thread_context *thread_context) {
1328
1430
  // If we didn't have a time for the previous sample, we use the current one
1329
1431
  if (*time_at_previous_sample_ns == INVALID_TIME) *time_at_previous_sample_ns = current_time_ns;
1330
1432
 
1331
- // We don't want wall-time accounting to change during GC.
1332
- // E.g. if 60 seconds pass in the real world, 60 seconds of wall-time are recorded, regardless of the thread doing GC or not.
1333
- bool is_thread_doing_gc = !is_wall_time && gc_start_time_ns != INVALID_TIME;
1334
- long elapsed_time_ns = -1;
1433
+ // We don't expect to be sampling a thread (and thus updating these counters) while Ruby is doing GC (between
1434
+ // `thread_context_collector_on_gc_start` and `thread_context_collector_on_gc_finish`)
1435
+ if (thread_context->gc_tracking.cpu_time_at_start_ns != INVALID_TIME) {
1436
+ raise_error(
1437
+ rb_eRuntimeError,
1438
+ "BUG: Unexpected sample during GC (thread_id=%s, gc_tracking.cpu_time_at_start_ns=%ld, "
1439
+ "gc_tracking.wall_time_at_start_ns=%ld, monotonic_wall_time_now_ns=%ld)",
1440
+ thread_context->thread_id,
1441
+ thread_context->gc_tracking.cpu_time_at_start_ns,
1442
+ thread_context->gc_tracking.wall_time_at_start_ns,
1443
+ monotonic_wall_time_now_ns(RAISE_ON_FAILURE)
1444
+ );
1445
+ }
1335
1446
 
1336
- if (is_thread_doing_gc) {
1337
- bool previous_sample_was_during_gc = gc_start_time_ns <= *time_at_previous_sample_ns;
1447
+ long elapsed_time_ns = current_time_ns - *time_at_previous_sample_ns; // Capture all time since previous sample
1448
+ *time_at_previous_sample_ns = current_time_ns;
1338
1449
 
1339
- if (previous_sample_was_during_gc) {
1340
- elapsed_time_ns = 0; // No time to account for -- any time since the last sample is going to get assigned to GC separately
1341
- } else {
1342
- elapsed_time_ns = gc_start_time_ns - *time_at_previous_sample_ns; // Capture time between previous sample and start of GC only
1343
- }
1450
+ return elapsed_time_ns;
1451
+ }
1344
1452
 
1345
- // Remaining time (from gc_start_time to current_time_ns) will be accounted for inside `sample_after_gc`
1346
- *time_at_previous_sample_ns = gc_start_time_ns;
1347
- } else {
1348
- elapsed_time_ns = current_time_ns - *time_at_previous_sample_ns; // Capture all time since previous sample
1349
- *time_at_previous_sample_ns = current_time_ns;
1350
- }
1453
+ static long update_cpu_time_since_previous_sample(per_thread_context *thread_context, long current_cpu_time_ns) {
1454
+ long elapsed_time_ns = update_time_since_previous_sample(
1455
+ &thread_context->cpu_time_at_previous_sample_ns,
1456
+ current_cpu_time_ns,
1457
+ thread_context
1458
+ );
1351
1459
 
1460
+ // We don't expect cpu-time to go backwards, so let's flag this as a bug
1352
1461
  if (elapsed_time_ns < 0) {
1353
- if (is_wall_time) {
1354
- // Wall-time can actually go backwards (e.g. when the system clock gets set) so we can't assume time going backwards
1355
- // was a bug.
1356
- // @ivoanjo: I've also observed time going backwards spuriously on macOS, see discussion on
1357
- // https://github.com/DataDog/dd-trace-rb/pull/2336.
1358
- elapsed_time_ns = 0;
1359
- } else {
1360
- // We don't expect non-wall time to go backwards, so let's flag this as a bug
1361
- raise_error(rb_eRuntimeError, "BUG: Unexpected negative elapsed_time_ns between samples");
1362
- }
1462
+ raise_error(rb_eRuntimeError, "BUG: Unexpected CPU time going backwards between samples");
1363
1463
  }
1364
1464
 
1365
1465
  return elapsed_time_ns;
1366
1466
  }
1367
1467
 
1468
+ static long update_wall_time_since_previous_sample(per_thread_context *thread_context, long current_wall_time_ns) {
1469
+ long elapsed_time_ns = update_time_since_previous_sample(
1470
+ &thread_context->wall_time_at_previous_sample_ns,
1471
+ current_wall_time_ns,
1472
+ thread_context
1473
+ );
1474
+
1475
+ // Wall-time can actually go backwards (e.g. when the system clock gets set) so we can't assume time going backwards
1476
+ // was a bug.
1477
+ // @ivoanjo: I've also observed time going backwards spuriously on macOS, see discussion on
1478
+ // https://github.com/DataDog/dd-trace-rb/pull/2336.
1479
+ return long_max_of(elapsed_time_ns, 0);
1480
+ }
1481
+
1368
1482
  // Safety: This function is assumed never to raise exceptions by callers
1369
1483
  static long cpu_time_now_ns(per_thread_context *thread_context) {
1370
1484
  thread_cpu_time cpu_time = thread_cpu_time_for(thread_context->thread_cpu_time_id);
@@ -1521,8 +1635,8 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector
1521
1635
 
1522
1636
  state->stats = (struct stats) {}; // Resets all stats back to zero
1523
1637
 
1524
- // Clear any leftover state from parent process in the current thread; all other threads are assumed dead
1525
- _native_clear_per_thread_context_for(Qnil, rb_thread_current());
1638
+ // No need to clean-up the per-thread context because the CpuAndWallTimeWorker always cleans
1639
+ // it up unconditionally on every start/restart and that includes after a fork.
1526
1640
 
1527
1641
  rb_funcall(state->recorder_instance, rb_intern("reset_after_fork"), 0);
1528
1642
 
@@ -1634,11 +1748,10 @@ bool thread_context_collector_sample_allocation(VALUE self_instance, per_thread_
1634
1748
  state,
1635
1749
  current_thread,
1636
1750
  thread_context,
1637
- &thread_context->sampling_buffer,
1638
1751
  (sample_values) {.alloc_samples = sample_weight, .alloc_samples_unscaled = 1, .heap_sample = true},
1639
1752
  INVALID_TIME, // For now we're not collecting timestamps for allocation events, as per profiling team internal discussions
1640
1753
  &ruby_vm_type,
1641
- &class_name,
1754
+ &class_name,
1642
1755
  /* is_gvl_waiting_state: */ false,
1643
1756
  /* is_safe_to_allocate_objects: */ false // Not safe to allocate further inside the NEWOBJ tracepoint
1644
1757
  );
@@ -1649,9 +1762,10 @@ bool thread_context_collector_sample_allocation(VALUE self_instance, per_thread_
1649
1762
  // This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
1650
1763
  // It SHOULD NOT be used for other purposes.
1651
1764
  static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object) {
1652
- thread_context_collector_state *state;
1653
- TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
1654
- per_thread_context *thread_context = get_or_create_context_for(rb_thread_current(), state);
1765
+ per_thread_context *thread_context = get_per_thread_context(rb_thread_current());
1766
+ if (thread_context == NULL) {
1767
+ rb_raise(rb_eRuntimeError, "Missing per_thread_context for current thread in _native_sample_allocation");
1768
+ }
1655
1769
 
1656
1770
  debug_enter_unsafe_context();
1657
1771
 
@@ -1972,49 +2086,115 @@ void thread_context_collector_stats_reset_not_thread_safe(VALUE self_instance) {
1972
2086
  state->stats = (struct stats) {};
1973
2087
  }
1974
2088
 
1975
- #ifndef NO_GVL_INSTRUMENTATION
1976
- void thread_context_collector_on_gvl_released(per_thread_context *thread_context) {
1977
- thread_context->gvl_state_change_count |= GVL_SUSPENDED;
2089
+ static void mark_thread_as_profiler_internal(per_thread_context *ctx) {
2090
+ ctx->is_profiler_internal_thread = true;
2091
+ }
2092
+
2093
+ void thread_context_collector_profiler_internal_thread_started(void) {
2094
+ per_thread_context *ctx = get_or_create_context_for(rb_thread_current());
2095
+ mark_thread_as_profiler_internal(ctx);
2096
+ }
2097
+
2098
+ static VALUE _native_mark_thread_as_profiler_internal(DDTRACE_UNUSED VALUE self, VALUE thread) {
2099
+ per_thread_context *ctx = get_or_create_context_for(thread);
2100
+ mark_thread_as_profiler_internal(ctx);
2101
+ return Qnil;
2102
+ }
2103
+
2104
+ // Called via rb_ensure when a profiler-internal thread (worker or idle helper) is about to exit.
2105
+ // Records a final sample so the thread's accumulated cpu/wall time since the last on_serialize
2106
+ // flush is not lost. on_serialize (below) also flushes profiler-internal threads during periodic
2107
+ // serialization, but it can't help at shutdown: by the time the final serialize runs, these
2108
+ // threads are already dead and absent from thread_list.
2109
+ void thread_context_collector_profiler_internal_thread_done(VALUE self_instance) {
2110
+ thread_context_collector_state *state;
2111
+ TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
2112
+
2113
+ VALUE current_thread = rb_thread_current();
2114
+ per_thread_context *thread_context = get_or_create_context_for(current_thread);
2115
+ if (!thread_context->is_profiler_internal_thread) {
2116
+ rb_raise(rb_eRuntimeError, "current thread %"PRIsVALUE" is not profiler-internal thread", current_thread);
1978
2117
  }
1979
2118
 
1980
- // Called by the stack recorder at the start of _native_serialize, so that threads whose last
1981
- // per-tick sample was skipped by the SUSPENDED-skip optimization still get their accumulated
1982
- // time recorded in this reporting period. Without this, a thread that sleeps across the whole
1983
- // period would not be reported at all.
1984
- void thread_context_collector_on_serialize(VALUE self_instance) {
1985
- thread_context_collector_state *state;
1986
- TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
2119
+ long current_cpu_time_ns = cpu_time_now_ns(thread_context);
2120
+ long current_monotonic_wall_time_ns = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
1987
2121
 
1988
- long current_monotonic_wall_time_ns = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
1989
- VALUE threads = thread_list(state);
1990
- const long thread_count = RARRAY_LEN(threads);
1991
-
1992
- for (long i = 0; i < thread_count; i++) {
1993
- VALUE thread = RARRAY_AREF(threads, i);
1994
- per_thread_context *thread_context = get_per_thread_context(thread);
1995
-
1996
- if (thread_context != NULL && thread_context->was_skipped_at_last_sample) {
1997
- long current_cpu_time_ns = cpu_time_now_ns(thread_context);
1998
- // We need to force_sample_suspended=true otherwise this sample would be skipped too
1999
- update_metrics_and_sample(
2000
- state,
2001
- thread,
2002
- thread_context,
2003
- &thread_context->sampling_buffer,
2004
- current_cpu_time_ns,
2005
- current_monotonic_wall_time_ns,
2006
- true);
2007
- }
2122
+ update_metrics_and_sample(
2123
+ state,
2124
+ current_thread,
2125
+ thread_context,
2126
+ current_cpu_time_ns,
2127
+ current_monotonic_wall_time_ns,
2128
+ true);
2129
+ }
2130
+
2131
+ // Flushes threads whose last per-tick sample was skipped (either by the SUSPENDED-skip
2132
+ // optimization, or by is_profiler_internal_thread) so their accumulated time is recorded.
2133
+ // Called by the stack recorder at the start of _native_serialize (regular periodic flush).
2134
+ void thread_context_collector_on_serialize(VALUE self_instance) {
2135
+ thread_context_collector_state *state;
2136
+ TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
2137
+
2138
+ long current_monotonic_wall_time_ns = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
2139
+ VALUE threads = thread_list(state);
2140
+ const long thread_count = RARRAY_LEN(threads);
2141
+
2142
+ for (long i = 0; i < thread_count; i++) {
2143
+ VALUE thread = RARRAY_AREF(threads, i);
2144
+ per_thread_context *thread_context = get_per_thread_context(thread);
2145
+
2146
+ if (thread_context != NULL && (thread_context->was_skipped_at_last_sample || thread_context->is_profiler_internal_thread)) {
2147
+ long current_cpu_time_ns = cpu_time_now_ns(thread_context);
2148
+ // We need to force_sample=true otherwise this sample would be skipped too
2149
+ update_metrics_and_sample(
2150
+ state,
2151
+ thread,
2152
+ thread_context,
2153
+ current_cpu_time_ns,
2154
+ current_monotonic_wall_time_ns,
2155
+ true);
2008
2156
  }
2009
2157
  }
2158
+ }
2010
2159
 
2011
- void thread_context_collector_on_gvl_waiting(per_thread_context *thread_context) {
2012
- long current_monotonic_wall_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
2013
- if (current_monotonic_wall_time_ns <= 0) return;
2160
+ void thread_context_collector_on_gvl_released(per_thread_context *thread_context) {
2161
+ thread_context->gvl_state_change_count |= GVL_SUSPENDED;
2162
+ }
2014
2163
 
2015
- thread_context->gvl_waiting_at = current_monotonic_wall_time_ns;
2016
- }
2164
+ void thread_context_collector_on_gvl_waiting(per_thread_context *thread_context) {
2165
+ long current_monotonic_wall_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
2166
+ if (current_monotonic_wall_time_ns <= 0) return;
2167
+
2168
+ thread_context->gvl_waiting_at = current_monotonic_wall_time_ns;
2169
+ }
2170
+
2171
+ static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread) {
2172
+ ENFORCE_THREAD(thread);
2173
+
2174
+ debug_enter_unsafe_context();
2175
+
2176
+ per_thread_context *thread_context = get_per_thread_context(thread);
2177
+ if (thread_context) thread_context_collector_on_gvl_waiting(thread_context);
2178
+
2179
+ debug_leave_unsafe_context();
2180
+
2181
+ return Qnil;
2182
+ }
2183
+
2184
+ static VALUE _native_on_gvl_released(DDTRACE_UNUSED VALUE self, VALUE thread) {
2185
+ ENFORCE_THREAD(thread);
2186
+
2187
+ debug_enter_unsafe_context();
2188
+
2189
+ per_thread_context *thread_context = get_per_thread_context(thread);
2190
+ if (thread_context) thread_context_collector_on_gvl_released(thread_context);
2017
2191
 
2192
+ debug_leave_unsafe_context();
2193
+
2194
+ return Qnil;
2195
+ }
2196
+
2197
+ #ifndef NO_GVL_INSTRUMENTATION
2018
2198
  // This function runs on the passed thread and has the GVL because it gets called just after the Ruby thread acquired the GVL
2019
2199
  __attribute__((warn_unused_result))
2020
2200
  on_gvl_running_result thread_context_collector_on_gvl_running(VALUE self_instance, VALUE thread, per_thread_context *thread_context) {
@@ -2103,7 +2283,7 @@ void thread_context_collector_stats_reset_not_thread_safe(VALUE self_instance) {
2103
2283
  thread_context_collector_state *state;
2104
2284
  TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
2105
2285
 
2106
- per_thread_context *thread_context = get_or_create_context_for(current_thread, state);
2286
+ per_thread_context *thread_context = get_or_create_context_for(current_thread);
2107
2287
 
2108
2288
  long gvl_waiting_at = thread_context->gvl_waiting_at;
2109
2289
 
@@ -2126,7 +2306,6 @@ void thread_context_collector_stats_reset_not_thread_safe(VALUE self_instance) {
2126
2306
  state,
2127
2307
  current_thread,
2128
2308
  thread_context,
2129
- &thread_context->sampling_buffer,
2130
2309
  cpu_time_for_thread,
2131
2310
  current_monotonic_wall_time_ns,
2132
2311
  false);
@@ -2141,7 +2320,6 @@ void thread_context_collector_stats_reset_not_thread_safe(VALUE self_instance) {
2141
2320
  thread_context_collector_state *state,
2142
2321
  VALUE thread_being_sampled,
2143
2322
  per_thread_context *thread_context,
2144
- sampling_buffer* sampling_buffer,
2145
2323
  long current_cpu_time_ns
2146
2324
  ) {
2147
2325
  long gvl_waiting_at = thread_context->gvl_waiting_at;
@@ -2200,26 +2378,16 @@ void thread_context_collector_stats_reset_not_thread_safe(VALUE self_instance) {
2200
2378
  long gvl_waiting_started_wall_time_ns = labs(gvl_waiting_at);
2201
2379
 
2202
2380
  if (thread_context->wall_time_at_previous_sample_ns < gvl_waiting_started_wall_time_ns) { // situation 1 above
2203
- long cpu_time_elapsed_ns = update_time_since_previous_sample(
2204
- &thread_context->cpu_time_at_previous_sample_ns,
2205
- current_cpu_time_ns,
2206
- thread_context->gc_tracking.cpu_time_at_start_ns,
2207
- IS_CPU_TIME
2208
- );
2381
+ long cpu_time_elapsed_ns = update_cpu_time_since_previous_sample(thread_context, current_cpu_time_ns);
2209
2382
 
2210
- long duration_until_start_of_gvl_waiting_ns = update_time_since_previous_sample(
2211
- &thread_context->wall_time_at_previous_sample_ns,
2212
- gvl_waiting_started_wall_time_ns,
2213
- INVALID_TIME,
2214
- IS_WALL_TIME
2215
- );
2383
+ long duration_until_start_of_gvl_waiting_ns =
2384
+ update_wall_time_since_previous_sample(thread_context, gvl_waiting_started_wall_time_ns);
2216
2385
 
2217
2386
  // Push extra sample
2218
2387
  trigger_sample_for_thread(
2219
2388
  state,
2220
2389
  thread_being_sampled,
2221
2390
  thread_context,
2222
- sampling_buffer,
2223
2391
  (sample_values) {.cpu_time_ns = cpu_time_elapsed_ns, .cpu_or_wall_samples = 1, .wall_time_ns = duration_until_start_of_gvl_waiting_ns},
2224
2392
  gvl_waiting_started_wall_time_ns,
2225
2393
  NULL,
@@ -2232,19 +2400,6 @@ void thread_context_collector_stats_reset_not_thread_safe(VALUE self_instance) {
2232
2400
  return true;
2233
2401
  }
2234
2402
 
2235
- static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread) {
2236
- ENFORCE_THREAD(thread);
2237
-
2238
- debug_enter_unsafe_context();
2239
-
2240
- per_thread_context *thread_context = get_per_thread_context(thread);
2241
- if (thread_context) thread_context_collector_on_gvl_waiting(thread_context);
2242
-
2243
- debug_leave_unsafe_context();
2244
-
2245
- return Qnil;
2246
- }
2247
-
2248
2403
  static VALUE _native_gvl_waiting_at_for(DDTRACE_UNUSED VALUE self, VALUE thread) {
2249
2404
  ENFORCE_THREAD(thread);
2250
2405
 
@@ -2276,19 +2431,6 @@ void thread_context_collector_stats_reset_not_thread_safe(VALUE self_instance) {
2276
2431
  return result;
2277
2432
  }
2278
2433
 
2279
- static VALUE _native_on_gvl_released(DDTRACE_UNUSED VALUE self, VALUE thread) {
2280
- ENFORCE_THREAD(thread);
2281
-
2282
- debug_enter_unsafe_context();
2283
-
2284
- per_thread_context *thread_context = get_per_thread_context(thread);
2285
- if (thread_context) thread_context_collector_on_gvl_released(thread_context);
2286
-
2287
- debug_leave_unsafe_context();
2288
-
2289
- return Qnil;
2290
- }
2291
-
2292
2434
  static VALUE _native_sample_after_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread, VALUE allow_exception) {
2293
2435
  ENFORCE_THREAD(thread);
2294
2436
  ENFORCE_BOOLEAN(allow_exception);
@@ -2306,29 +2448,27 @@ void thread_context_collector_stats_reset_not_thread_safe(VALUE self_instance) {
2306
2448
  return result;
2307
2449
  }
2308
2450
 
2309
- static VALUE _native_apply_delta_to_cpu_time_at_previous_sample_ns(DDTRACE_UNUSED VALUE self, VALUE thread, VALUE delta_ns) {
2310
- ENFORCE_THREAD(thread);
2311
-
2312
- per_thread_context *thread_context = get_per_thread_context(thread);
2313
- if (thread_context == NULL) raise_error(rb_eArgError, "Unexpected: This method cannot be used unless the per-thread context for the thread already exists");
2314
-
2315
- thread_context->cpu_time_at_previous_sample_ns += NUM2LONG(delta_ns);
2316
-
2317
- return Qtrue;
2318
- }
2319
-
2320
2451
  #else
2321
2452
  static bool handle_gvl_waiting(
2322
2453
  DDTRACE_UNUSED thread_context_collector_state *state,
2323
2454
  DDTRACE_UNUSED VALUE thread_being_sampled,
2324
2455
  DDTRACE_UNUSED per_thread_context *thread_context,
2325
- DDTRACE_UNUSED sampling_buffer* sampling_buffer,
2326
2456
  DDTRACE_UNUSED long current_cpu_time_ns
2327
2457
  ) { return false; }
2328
2458
 
2329
- void thread_context_collector_on_serialize(DDTRACE_UNUSED VALUE self_instance) { }
2330
2459
  #endif // NO_GVL_INSTRUMENTATION
2331
2460
 
2461
+ static VALUE _native_apply_delta_to_cpu_time_at_previous_sample_ns(DDTRACE_UNUSED VALUE self, VALUE thread, VALUE delta_ns) {
2462
+ ENFORCE_THREAD(thread);
2463
+
2464
+ per_thread_context *thread_context = get_per_thread_context(thread);
2465
+ if (thread_context == NULL) raise_error(rb_eArgError, "Unexpected: This method cannot be used unless the per-thread context for the thread already exists");
2466
+
2467
+ thread_context->cpu_time_at_previous_sample_ns += NUM2LONG(delta_ns);
2468
+
2469
+ return Qtrue;
2470
+ }
2471
+
2332
2472
  #define MAX_SAFE_LOOKUP_SIZE 16
2333
2473
 
2334
2474
  typedef struct { VALUE lookup_key; VALUE result; } safe_lookup_hash_state;