datadog 2.8.0 → 2.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1035) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1009 -1
  3. data/README.md +0 -1
  4. data/bin/ddprofrb +4 -4
  5. data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
  6. data/ext/datadog_profiling_native_extension/clock_id.h +11 -3
  7. data/ext/datadog_profiling_native_extension/clock_id_from_mach.c +66 -0
  8. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +3 -2
  9. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +500 -188
  10. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +5 -5
  11. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +1 -1
  12. data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +3 -2
  13. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +34 -20
  14. data/ext/datadog_profiling_native_extension/collectors_stack.c +319 -101
  15. data/ext/datadog_profiling_native_extension/collectors_stack.h +27 -5
  16. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +985 -504
  17. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +19 -5
  18. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
  19. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +66 -5
  20. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +50 -1
  21. data/ext/datadog_profiling_native_extension/encoded_profile.c +80 -0
  22. data/ext/datadog_profiling_native_extension/encoded_profile.h +8 -0
  23. data/ext/datadog_profiling_native_extension/extconf.rb +53 -35
  24. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +6 -43
  25. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +15 -55
  26. data/ext/datadog_profiling_native_extension/heap_recorder.c +487 -497
  27. data/ext/datadog_profiling_native_extension/heap_recorder.h +17 -8
  28. data/ext/datadog_profiling_native_extension/http_transport.c +108 -144
  29. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +18 -15
  30. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +8 -10
  31. data/ext/datadog_profiling_native_extension/macos_sampler_thread.h +55 -0
  32. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +0 -13
  33. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +136 -60
  34. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +22 -5
  35. data/ext/datadog_profiling_native_extension/profiling.c +84 -27
  36. data/ext/datadog_profiling_native_extension/ruby_helpers.c +67 -62
  37. data/ext/datadog_profiling_native_extension/ruby_helpers.h +20 -16
  38. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +34 -10
  39. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +2 -1
  40. data/ext/datadog_profiling_native_extension/stack_recorder.c +326 -217
  41. data/ext/datadog_profiling_native_extension/stack_recorder.h +4 -2
  42. data/ext/datadog_profiling_native_extension/time_helpers.h +14 -8
  43. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +48 -0
  44. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +36 -0
  45. data/ext/libdatadog_api/crashtracker.c +27 -24
  46. data/ext/libdatadog_api/crashtracker.h +5 -0
  47. data/ext/libdatadog_api/crashtracker_report_exception.c +126 -0
  48. data/ext/libdatadog_api/datadog_ruby_common.c +66 -5
  49. data/ext/libdatadog_api/datadog_ruby_common.h +50 -1
  50. data/ext/libdatadog_api/ddsketch.c +102 -0
  51. data/ext/libdatadog_api/di.c +203 -0
  52. data/ext/libdatadog_api/extconf.rb +41 -45
  53. data/ext/libdatadog_api/feature_flags.c +580 -0
  54. data/ext/libdatadog_api/feature_flags.h +5 -0
  55. data/ext/libdatadog_api/helpers.h +27 -0
  56. data/ext/libdatadog_api/init.c +27 -0
  57. data/ext/libdatadog_api/library_config.c +173 -0
  58. data/ext/libdatadog_api/library_config.h +25 -0
  59. data/ext/libdatadog_api/process_discovery.c +118 -0
  60. data/ext/libdatadog_api/process_discovery.h +5 -0
  61. data/ext/libdatadog_extconf_helpers.rb +111 -18
  62. data/lib/datadog/ai_guard/api_client.rb +84 -0
  63. data/lib/datadog/ai_guard/autoload.rb +10 -0
  64. data/lib/datadog/ai_guard/component.rb +44 -0
  65. data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
  66. data/lib/datadog/ai_guard/configuration.rb +115 -0
  67. data/lib/datadog/ai_guard/contrib/auto_instrument.rb +24 -0
  68. data/lib/datadog/ai_guard/contrib/integration.rb +37 -0
  69. data/lib/datadog/ai_guard/contrib/rack/integration.rb +42 -0
  70. data/lib/datadog/ai_guard/contrib/rack/patcher.rb +26 -0
  71. data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +97 -0
  72. data/lib/datadog/ai_guard/contrib/rails/integration.rb +41 -0
  73. data/lib/datadog/ai_guard/contrib/rails/patcher.rb +97 -0
  74. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +80 -0
  75. data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +41 -0
  76. data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +30 -0
  77. data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
  78. data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
  79. data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
  80. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +36 -0
  81. data/lib/datadog/ai_guard/evaluation/request.rb +86 -0
  82. data/lib/datadog/ai_guard/evaluation/result.rb +45 -0
  83. data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
  84. data/lib/datadog/ai_guard/evaluation.rb +108 -0
  85. data/lib/datadog/ai_guard/ext.rb +29 -0
  86. data/lib/datadog/ai_guard.rb +181 -0
  87. data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +89 -0
  88. data/lib/datadog/appsec/actions_handler.rb +49 -0
  89. data/lib/datadog/appsec/anonymizer.rb +16 -0
  90. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
  91. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +66 -0
  92. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +36 -0
  93. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
  94. data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
  95. data/lib/datadog/appsec/api_security/route_extractor.rb +104 -0
  96. data/lib/datadog/appsec/api_security/sampler.rb +62 -0
  97. data/lib/datadog/appsec/api_security.rb +23 -0
  98. data/lib/datadog/appsec/assets/blocked.html +10 -1
  99. data/lib/datadog/appsec/assets/blocked.json +1 -1
  100. data/lib/datadog/appsec/assets/blocked.text +3 -1
  101. data/lib/datadog/appsec/assets/waf_rules/README.md +44 -5
  102. data/lib/datadog/appsec/assets/waf_rules/recommended.json +954 -229
  103. data/lib/datadog/appsec/assets/waf_rules/strict.json +109 -106
  104. data/lib/datadog/appsec/assets.rb +8 -8
  105. data/lib/datadog/appsec/autoload.rb +5 -5
  106. data/lib/datadog/appsec/component.rb +56 -68
  107. data/lib/datadog/appsec/compressed_json.rb +40 -0
  108. data/lib/datadog/appsec/configuration.rb +427 -1
  109. data/lib/datadog/appsec/context.rb +135 -0
  110. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +45 -24
  111. data/lib/datadog/appsec/contrib/active_record/integration.rb +6 -6
  112. data/lib/datadog/appsec/contrib/active_record/patcher.rb +67 -16
  113. data/lib/datadog/appsec/contrib/auto_instrument.rb +1 -1
  114. data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +75 -0
  115. data/lib/datadog/appsec/contrib/aws_lambda/integration.rb +39 -0
  116. data/lib/datadog/appsec/contrib/aws_lambda/patcher.rb +30 -0
  117. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +146 -0
  118. data/lib/datadog/appsec/contrib/devise/configuration.rb +52 -0
  119. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +78 -0
  120. data/lib/datadog/appsec/contrib/devise/ext.rb +22 -0
  121. data/lib/datadog/appsec/contrib/devise/integration.rb +5 -6
  122. data/lib/datadog/appsec/contrib/devise/patcher.rb +33 -25
  123. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +104 -0
  124. data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +70 -0
  125. data/lib/datadog/appsec/contrib/devise/{patcher/rememberable_patch.rb → patches/skip_signin_tracking_patch.rb} +3 -3
  126. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +121 -0
  127. data/lib/datadog/appsec/contrib/excon/integration.rb +41 -0
  128. data/lib/datadog/appsec/contrib/excon/patcher.rb +30 -0
  129. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +181 -0
  130. data/lib/datadog/appsec/contrib/faraday/connection_patch.rb +22 -0
  131. data/lib/datadog/appsec/contrib/faraday/integration.rb +42 -0
  132. data/lib/datadog/appsec/contrib/faraday/patcher.rb +53 -0
  133. data/lib/datadog/appsec/contrib/faraday/rack_builder_patch.rb +22 -0
  134. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +166 -0
  135. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +4 -10
  136. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +76 -26
  137. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +22 -33
  138. data/lib/datadog/appsec/contrib/graphql/integration.rb +10 -9
  139. data/lib/datadog/appsec/contrib/graphql/patcher.rb +3 -6
  140. data/lib/datadog/appsec/contrib/rack/buffered_input.rb +83 -0
  141. data/lib/datadog/appsec/contrib/rack/ext.rb +34 -0
  142. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +56 -21
  143. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +4 -4
  144. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +101 -100
  145. data/lib/datadog/appsec/contrib/rack/input_peeker.rb +79 -0
  146. data/lib/datadog/appsec/contrib/rack/integration.rb +7 -7
  147. data/lib/datadog/appsec/contrib/rack/patcher.rb +2 -5
  148. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +13 -14
  149. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +117 -115
  150. data/lib/datadog/appsec/contrib/rack/response_body.rb +36 -0
  151. data/lib/datadog/appsec/contrib/rails/gateway/request.rb +43 -4
  152. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +58 -33
  153. data/lib/datadog/appsec/contrib/rails/integration.rb +6 -6
  154. data/lib/datadog/appsec/contrib/rails/patcher.rb +82 -69
  155. data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.rb +29 -0
  156. data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +33 -0
  157. data/lib/datadog/appsec/contrib/rails/request.rb +3 -3
  158. data/lib/datadog/appsec/contrib/rest_client/integration.rb +45 -0
  159. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +30 -0
  160. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +193 -0
  161. data/lib/datadog/appsec/contrib/sinatra/gateway/request.rb +1 -1
  162. data/lib/datadog/appsec/contrib/sinatra/gateway/route_params.rb +1 -1
  163. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +83 -67
  164. data/lib/datadog/appsec/contrib/sinatra/integration.rb +6 -6
  165. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +25 -61
  166. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +31 -0
  167. data/lib/datadog/appsec/counter_sampler.rb +25 -0
  168. data/lib/datadog/appsec/default_header_tags.rb +52 -0
  169. data/lib/datadog/appsec/event.rb +80 -152
  170. data/lib/datadog/appsec/ext.rb +16 -4
  171. data/lib/datadog/appsec/extensions.rb +2 -2
  172. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +23 -2
  173. data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +23 -0
  174. data/lib/datadog/appsec/instrumentation/gateway.rb +6 -24
  175. data/lib/datadog/appsec/metrics/collector.rb +80 -0
  176. data/lib/datadog/appsec/metrics/exporter.rb +48 -0
  177. data/lib/datadog/appsec/metrics/telemetry.rb +40 -0
  178. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +47 -0
  179. data/lib/datadog/appsec/metrics.rb +14 -0
  180. data/lib/datadog/appsec/monitor/gateway/watcher.rb +60 -34
  181. data/lib/datadog/appsec/monitor.rb +1 -1
  182. data/lib/datadog/appsec/processor/rule_loader.rb +48 -54
  183. data/lib/datadog/appsec/rate_limiter.rb +1 -1
  184. data/lib/datadog/appsec/remote.rb +59 -68
  185. data/lib/datadog/appsec/response.rb +42 -90
  186. data/lib/datadog/appsec/route_normalizer/rails_route_pattern.rb +176 -0
  187. data/lib/datadog/appsec/route_normalizer/route_pattern.rb +378 -0
  188. data/lib/datadog/appsec/route_normalizer/route_text.rb +57 -0
  189. data/lib/datadog/appsec/route_normalizer.rb +80 -0
  190. data/lib/datadog/appsec/security_engine/engine.rb +197 -0
  191. data/lib/datadog/appsec/security_engine/result.rb +103 -0
  192. data/lib/datadog/appsec/security_engine/runner.rb +111 -0
  193. data/lib/datadog/appsec/security_engine.rb +9 -0
  194. data/lib/datadog/appsec/security_event.rb +37 -0
  195. data/lib/datadog/appsec/thread_safe_ref.rb +61 -0
  196. data/lib/datadog/appsec/trace_keeper.rb +36 -0
  197. data/lib/datadog/appsec/utils/hash_coercion.rb +23 -0
  198. data/lib/datadog/appsec/utils/http/body.rb +41 -0
  199. data/lib/datadog/appsec/utils/http/body_reader.rb +61 -0
  200. data/lib/datadog/appsec/utils/http/media_range.rb +17 -16
  201. data/lib/datadog/appsec/utils/http/media_type.rb +33 -27
  202. data/lib/datadog/appsec/utils/http/url_encoded.rb +107 -0
  203. data/lib/datadog/appsec/utils.rb +0 -2
  204. data/lib/datadog/appsec.rb +35 -30
  205. data/lib/datadog/auto_instrument.rb +5 -2
  206. data/lib/datadog/auto_instrument_base.rb +2 -1
  207. data/lib/datadog/core/buffer/cruby.rb +1 -1
  208. data/lib/datadog/core/buffer/random.rb +18 -2
  209. data/lib/datadog/core/buffer/thread_safe.rb +1 -1
  210. data/lib/datadog/core/configuration/agent_settings.rb +52 -0
  211. data/lib/datadog/core/configuration/agent_settings_resolver.rb +22 -36
  212. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +176 -0
  213. data/lib/datadog/core/configuration/base.rb +19 -7
  214. data/lib/datadog/core/configuration/components.rb +261 -57
  215. data/lib/datadog/core/configuration/components_state.rb +28 -0
  216. data/lib/datadog/core/configuration/config_helper.rb +109 -0
  217. data/lib/datadog/core/configuration/deprecations.rb +36 -0
  218. data/lib/datadog/core/configuration/ext.rb +15 -12
  219. data/lib/datadog/core/configuration/option.rb +150 -84
  220. data/lib/datadog/core/configuration/option_definition.rb +50 -29
  221. data/lib/datadog/core/configuration/options.rb +58 -19
  222. data/lib/datadog/core/configuration/settings.rb +287 -103
  223. data/lib/datadog/core/configuration/stable_config.rb +32 -0
  224. data/lib/datadog/core/configuration/supported_configurations.rb +415 -0
  225. data/lib/datadog/core/configuration.rb +47 -23
  226. data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
  227. data/lib/datadog/core/contrib/rails/utils.rb +28 -0
  228. data/lib/datadog/core/crashtracking/component.rb +81 -38
  229. data/lib/datadog/core/crashtracking/tag_builder.rb +11 -23
  230. data/lib/datadog/core/ddsketch.rb +19 -0
  231. data/lib/datadog/core/deprecations.rb +3 -3
  232. data/lib/datadog/core/diagnostics/environment_logger.rb +10 -8
  233. data/lib/datadog/core/diagnostics/health.rb +2 -2
  234. data/lib/datadog/core/encoding.rb +21 -5
  235. data/lib/datadog/core/environment/agent_info.rb +142 -0
  236. data/lib/datadog/core/environment/cgroup.rb +53 -28
  237. data/lib/datadog/core/environment/container.rb +143 -51
  238. data/lib/datadog/core/environment/execution.rb +3 -3
  239. data/lib/datadog/core/environment/ext.rb +32 -21
  240. data/lib/datadog/core/environment/git.rb +5 -4
  241. data/lib/datadog/core/environment/identity.rb +50 -14
  242. data/lib/datadog/core/environment/platform.rb +8 -8
  243. data/lib/datadog/core/environment/process.rb +122 -0
  244. data/lib/datadog/core/environment/socket.rb +15 -2
  245. data/lib/datadog/core/environment/variable_helpers.rb +4 -4
  246. data/lib/datadog/core/environment/yjit.rb +2 -1
  247. data/lib/datadog/core/error.rb +15 -13
  248. data/lib/datadog/core/evp.rb +11 -0
  249. data/lib/datadog/core/extensions.rb +1 -1
  250. data/lib/datadog/core/feature_flags.rb +61 -0
  251. data/lib/datadog/core/git/ext.rb +4 -4
  252. data/lib/datadog/core/knuth_sampler.rb +59 -0
  253. data/lib/datadog/core/logger.rb +5 -5
  254. data/lib/datadog/core/logging/ext.rb +1 -1
  255. data/lib/datadog/core/metrics/client.rb +49 -49
  256. data/lib/datadog/core/metrics/ext.rb +5 -5
  257. data/lib/datadog/core/metrics/logging.rb +8 -8
  258. data/lib/datadog/core/metrics/options.rb +3 -3
  259. data/lib/datadog/core/pin.rb +9 -9
  260. data/lib/datadog/core/process_discovery/tracer_memfd.rb +13 -0
  261. data/lib/datadog/core/process_discovery.rb +62 -0
  262. data/lib/datadog/core/rate_limiter.rb +16 -4
  263. data/lib/datadog/core/remote/client/capabilities.rb +72 -13
  264. data/lib/datadog/core/remote/client.rb +68 -51
  265. data/lib/datadog/core/remote/component.rb +90 -55
  266. data/lib/datadog/core/remote/configuration/content.rb +17 -4
  267. data/lib/datadog/core/remote/configuration/digest.rb +22 -15
  268. data/lib/datadog/core/remote/configuration/path.rb +9 -9
  269. data/lib/datadog/core/remote/configuration/repository.rb +17 -4
  270. data/lib/datadog/core/remote/configuration/target.rb +15 -8
  271. data/lib/datadog/core/remote/configuration.rb +4 -4
  272. data/lib/datadog/core/remote/ext.rb +3 -3
  273. data/lib/datadog/core/remote/negotiation.rb +10 -10
  274. data/lib/datadog/core/remote/tie/tracing.rb +7 -7
  275. data/lib/datadog/core/remote/transport/config.rb +11 -37
  276. data/lib/datadog/core/remote/transport/http/config.rb +39 -107
  277. data/lib/datadog/core/remote/transport/http/negotiation.rb +19 -92
  278. data/lib/datadog/core/remote/transport/http.rb +33 -111
  279. data/lib/datadog/core/remote/transport/negotiation.rb +9 -21
  280. data/lib/datadog/core/remote/worker.rb +33 -42
  281. data/lib/datadog/core/runtime/ext.rb +22 -23
  282. data/lib/datadog/core/runtime/metrics.rb +30 -14
  283. data/lib/datadog/core/semaphore.rb +1 -4
  284. data/lib/datadog/core/tag_builder.rb +52 -0
  285. data/lib/datadog/core/tag_normalizer.rb +84 -0
  286. data/lib/datadog/core/telemetry/component.rb +142 -65
  287. data/lib/datadog/core/telemetry/emitter.rb +26 -14
  288. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +65 -0
  289. data/lib/datadog/core/telemetry/event/app_closing.rb +18 -0
  290. data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +33 -0
  291. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
  292. data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
  293. data/lib/datadog/core/telemetry/event/app_heartbeat.rb +18 -0
  294. data/lib/datadog/core/telemetry/event/app_integrations_change.rb +58 -0
  295. data/lib/datadog/core/telemetry/event/app_started.rb +293 -0
  296. data/lib/datadog/core/telemetry/event/base.rb +40 -0
  297. data/lib/datadog/core/telemetry/event/distributions.rb +18 -0
  298. data/lib/datadog/core/telemetry/event/generate_metrics.rb +43 -0
  299. data/lib/datadog/core/telemetry/event/log.rb +76 -0
  300. data/lib/datadog/core/telemetry/event/message_batch.rb +42 -0
  301. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +66 -0
  302. data/lib/datadog/core/telemetry/event.rb +21 -392
  303. data/lib/datadog/core/telemetry/ext.rb +11 -10
  304. data/lib/datadog/core/telemetry/http/adapters/net.rb +12 -97
  305. data/lib/datadog/core/telemetry/logger.rb +6 -3
  306. data/lib/datadog/core/telemetry/logging.rb +27 -9
  307. data/lib/datadog/core/telemetry/metric.rb +35 -13
  308. data/lib/datadog/core/telemetry/metrics_collection.rb +2 -2
  309. data/lib/datadog/core/telemetry/metrics_manager.rb +10 -1
  310. data/lib/datadog/core/telemetry/request.rb +21 -9
  311. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +66 -0
  312. data/lib/datadog/core/telemetry/transport/http.rb +68 -0
  313. data/lib/datadog/core/telemetry/transport/telemetry.rb +42 -0
  314. data/lib/datadog/core/telemetry/worker.rb +214 -35
  315. data/lib/datadog/core/transport/ext.rb +14 -11
  316. data/lib/datadog/core/transport/http/adapters/net.rb +19 -4
  317. data/lib/datadog/core/transport/http/adapters/test.rb +3 -2
  318. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +5 -5
  319. data/lib/datadog/core/transport/http/api/endpoint.rb +10 -5
  320. data/lib/datadog/{tracing → core}/transport/http/api/instance.rb +5 -5
  321. data/lib/datadog/core/transport/http/api/map.rb +1 -1
  322. data/lib/datadog/{tracing → core}/transport/http/builder.rb +62 -36
  323. data/lib/datadog/core/transport/http/client.rb +80 -0
  324. data/lib/datadog/core/transport/http/env.rb +8 -0
  325. data/lib/datadog/core/transport/http/response.rb +5 -1
  326. data/lib/datadog/core/transport/http.rb +80 -0
  327. data/lib/datadog/core/transport/parcel.rb +61 -9
  328. data/lib/datadog/core/transport/response.rb +19 -1
  329. data/lib/datadog/core/transport/transport.rb +90 -0
  330. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +9 -9
  331. data/lib/datadog/core/utils/base64_codec.rb +23 -0
  332. data/lib/datadog/core/utils/duration.rb +33 -33
  333. data/lib/datadog/core/utils/enumerable_compat.rb +29 -0
  334. data/lib/datadog/core/utils/fnv.rb +26 -0
  335. data/lib/datadog/core/utils/forking.rb +5 -3
  336. data/lib/datadog/core/utils/hash.rb +0 -23
  337. data/lib/datadog/core/utils/lru_cache.rb +45 -0
  338. data/lib/datadog/core/utils/network.rb +34 -13
  339. data/lib/datadog/core/utils/only_once.rb +1 -1
  340. data/lib/datadog/core/utils/only_once_successful.rb +25 -8
  341. data/lib/datadog/core/utils/safe_dup.rb +4 -3
  342. data/lib/datadog/core/utils/sequence.rb +2 -0
  343. data/lib/datadog/core/utils/spawn_monkey_patch.rb +68 -0
  344. data/lib/datadog/core/utils/time.rb +26 -2
  345. data/lib/datadog/core/utils/truncation.rb +21 -0
  346. data/lib/datadog/core/utils/url.rb +1 -1
  347. data/lib/datadog/core/utils.rb +14 -5
  348. data/lib/datadog/core/vendor/multipart-post/multipart/post/composite_read_io.rb +1 -1
  349. data/lib/datadog/core/vendor/multipart-post/multipart/post/multipartable.rb +8 -8
  350. data/lib/datadog/core/vendor/multipart-post/multipart/post/parts.rb +7 -7
  351. data/lib/datadog/core/worker.rb +1 -1
  352. data/lib/datadog/core/workers/async.rb +40 -14
  353. data/lib/datadog/core/workers/interval_loop.rb +63 -4
  354. data/lib/datadog/core/workers/polling.rb +4 -2
  355. data/lib/datadog/core/workers/queue.rb +96 -1
  356. data/lib/datadog/core/workers/runtime_metrics.rb +14 -6
  357. data/lib/datadog/core.rb +20 -3
  358. data/lib/datadog/data_streams/configuration.rb +50 -0
  359. data/lib/datadog/data_streams/ext.rb +11 -0
  360. data/lib/datadog/data_streams/extensions.rb +16 -0
  361. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  362. data/lib/datadog/data_streams/processor.rb +511 -0
  363. data/lib/datadog/data_streams/transport/http/stats.rb +58 -0
  364. data/lib/datadog/data_streams/transport/http.rb +36 -0
  365. data/lib/datadog/data_streams/transport/stats.rb +39 -0
  366. data/lib/datadog/data_streams.rb +100 -0
  367. data/lib/datadog/di/base.rb +123 -0
  368. data/lib/datadog/di/boot.rb +47 -0
  369. data/lib/datadog/di/capture_expression.rb +21 -0
  370. data/lib/datadog/di/capture_expression_evaluator.rb +71 -0
  371. data/lib/datadog/di/capture_limits.rb +41 -0
  372. data/lib/datadog/di/code_tracker.rb +224 -12
  373. data/lib/datadog/di/component.rb +157 -49
  374. data/lib/datadog/di/configuration.rb +241 -2
  375. data/lib/datadog/di/context.rb +82 -0
  376. data/lib/datadog/di/contrib/active_record.rb +34 -5
  377. data/lib/datadog/di/contrib/railtie.rb +15 -0
  378. data/lib/datadog/di/contrib.rb +28 -0
  379. data/lib/datadog/di/el/compiler.rb +168 -0
  380. data/lib/datadog/di/el/evaluator.rb +159 -0
  381. data/lib/datadog/di/el/expression.rb +42 -0
  382. data/lib/datadog/di/el.rb +5 -0
  383. data/lib/datadog/di/error.rb +49 -0
  384. data/lib/datadog/di/fatal_exceptions.rb +26 -0
  385. data/lib/datadog/di/instrumenter.rb +672 -122
  386. data/lib/datadog/di/logger.rb +30 -0
  387. data/lib/datadog/di/{init.rb → preload.rb} +2 -4
  388. data/lib/datadog/di/probe.rb +125 -20
  389. data/lib/datadog/di/probe_builder.rb +126 -5
  390. data/lib/datadog/di/probe_file_loader/railtie.rb +15 -0
  391. data/lib/datadog/di/probe_file_loader.rb +87 -0
  392. data/lib/datadog/di/probe_manager.rb +198 -100
  393. data/lib/datadog/di/probe_notification_builder.rb +349 -103
  394. data/lib/datadog/di/probe_notifier_worker.rb +161 -51
  395. data/lib/datadog/di/probe_repository.rb +198 -0
  396. data/lib/datadog/di/proc_responder.rb +36 -0
  397. data/lib/datadog/di/redactor.rb +24 -3
  398. data/lib/datadog/di/remote.rb +259 -73
  399. data/lib/datadog/di/serializer.rb +319 -30
  400. data/lib/datadog/di/transport/diagnostics.rb +32 -0
  401. data/lib/datadog/di/transport/http/diagnostics.rb +35 -0
  402. data/lib/datadog/di/transport/http/input.rb +45 -0
  403. data/lib/datadog/di/transport/http.rb +81 -0
  404. data/lib/datadog/di/transport/input.rb +144 -0
  405. data/lib/datadog/di/utils.rb +133 -9
  406. data/lib/datadog/di.rb +188 -121
  407. data/lib/datadog/error_tracking/collector.rb +88 -0
  408. data/lib/datadog/error_tracking/component.rb +167 -0
  409. data/lib/datadog/error_tracking/configuration.rb +64 -0
  410. data/lib/datadog/error_tracking/ext.rb +18 -0
  411. data/lib/datadog/error_tracking/extensions.rb +16 -0
  412. data/lib/datadog/error_tracking/filters.rb +77 -0
  413. data/lib/datadog/error_tracking.rb +18 -0
  414. data/lib/datadog/kit/appsec/events/v2.rb +253 -0
  415. data/lib/datadog/kit/appsec/events.rb +39 -26
  416. data/lib/datadog/kit/enable_core_dumps.rb +3 -3
  417. data/lib/datadog/kit/identity.rb +34 -24
  418. data/lib/datadog/kit/tracing/method_tracer.rb +135 -0
  419. data/lib/datadog/kit.rb +1 -1
  420. data/lib/datadog/open_feature/component.rb +109 -0
  421. data/lib/datadog/open_feature/configuration.rb +50 -0
  422. data/lib/datadog/open_feature/evaluation_engine.rb +70 -0
  423. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  424. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  425. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  426. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  427. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  428. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  429. data/lib/datadog/open_feature/ext.rb +21 -0
  430. data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +286 -0
  431. data/lib/datadog/open_feature/flag_evaluation/writer.rb +433 -0
  432. data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +102 -0
  433. data/lib/datadog/open_feature/hooks/flag_eval_metrics_hook.rb +51 -0
  434. data/lib/datadog/open_feature/hooks/span_enrichment_hook/codec.rb +58 -0
  435. data/lib/datadog/open_feature/hooks/span_enrichment_hook/span_enrichment_state.rb +96 -0
  436. data/lib/datadog/open_feature/hooks/span_enrichment_hook/span_enrichment_state_store.rb +49 -0
  437. data/lib/datadog/open_feature/hooks/span_enrichment_hook.rb +184 -0
  438. data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
  439. data/lib/datadog/open_feature/native_evaluator.rb +60 -0
  440. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  441. data/lib/datadog/open_feature/provider.rb +226 -0
  442. data/lib/datadog/open_feature/remote.rb +67 -0
  443. data/lib/datadog/open_feature/resolution_details.rb +37 -0
  444. data/lib/datadog/open_feature/transport.rb +121 -0
  445. data/lib/datadog/open_feature.rb +19 -0
  446. data/lib/datadog/opentelemetry/api/baggage.rb +90 -0
  447. data/lib/datadog/opentelemetry/api/context.rb +25 -10
  448. data/lib/datadog/opentelemetry/configuration/settings.rb +226 -0
  449. data/lib/datadog/opentelemetry/ext.rb +9 -0
  450. data/lib/datadog/opentelemetry/logs.rb +98 -0
  451. data/lib/datadog/opentelemetry/metrics.rb +90 -0
  452. data/lib/datadog/opentelemetry/sdk/configurator.rb +69 -5
  453. data/lib/datadog/opentelemetry/sdk/id_generator.rb +16 -10
  454. data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +30 -0
  455. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +28 -0
  456. data/lib/datadog/opentelemetry/sdk/propagator.rb +20 -14
  457. data/lib/datadog/opentelemetry/sdk/span_processor.rb +24 -21
  458. data/lib/datadog/opentelemetry/sdk/trace/span.rb +43 -39
  459. data/lib/datadog/opentelemetry/sdk.rb +19 -0
  460. data/lib/datadog/opentelemetry/signal_configuration.rb +53 -0
  461. data/lib/datadog/opentelemetry/trace.rb +5 -5
  462. data/lib/datadog/opentelemetry.rb +11 -6
  463. data/lib/datadog/profiling/collectors/code_provenance.rb +84 -16
  464. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +57 -3
  465. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +13 -4
  466. data/lib/datadog/profiling/collectors/info.rb +62 -4
  467. data/lib/datadog/profiling/collectors/thread_context.rb +27 -7
  468. data/lib/datadog/profiling/component.rb +125 -88
  469. data/lib/datadog/profiling/encoded_profile.rb +11 -0
  470. data/lib/datadog/profiling/exporter.rb +51 -17
  471. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +24 -5
  472. data/lib/datadog/profiling/ext/exec_monkey_patch.rb +32 -0
  473. data/lib/datadog/profiling/ext.rb +2 -17
  474. data/lib/datadog/profiling/flush.rb +26 -17
  475. data/lib/datadog/profiling/http_transport.rb +30 -96
  476. data/lib/datadog/profiling/load_native_extension.rb +3 -35
  477. data/lib/datadog/profiling/profiler.rb +31 -12
  478. data/lib/datadog/profiling/scheduler.rb +19 -10
  479. data/lib/datadog/profiling/sequence_tracker.rb +44 -0
  480. data/lib/datadog/profiling/stack_recorder.rb +9 -17
  481. data/lib/datadog/profiling/tag_builder.rb +41 -41
  482. data/lib/datadog/profiling/tasks/exec.rb +10 -5
  483. data/lib/datadog/profiling/tasks/help.rb +1 -0
  484. data/lib/datadog/profiling/tasks/setup.rb +4 -2
  485. data/lib/datadog/profiling.rb +38 -37
  486. data/lib/datadog/ruby_version.rb +25 -0
  487. data/lib/datadog/single_step_instrument.rb +11 -2
  488. data/lib/datadog/symbol_database/component.rb +734 -0
  489. data/lib/datadog/symbol_database/configuration.rb +73 -0
  490. data/lib/datadog/symbol_database/extensions.rb +19 -0
  491. data/lib/datadog/symbol_database/extractor.rb +1110 -0
  492. data/lib/datadog/symbol_database/file_hash.rb +48 -0
  493. data/lib/datadog/symbol_database/logger.rb +43 -0
  494. data/lib/datadog/symbol_database/remote.rb +195 -0
  495. data/lib/datadog/symbol_database/scope.rb +102 -0
  496. data/lib/datadog/symbol_database/scope_batcher.rb +292 -0
  497. data/lib/datadog/symbol_database/service_version.rb +66 -0
  498. data/lib/datadog/symbol_database/symbol.rb +69 -0
  499. data/lib/datadog/symbol_database/transport/http/endpoint.rb +28 -0
  500. data/lib/datadog/symbol_database/transport/http.rb +45 -0
  501. data/lib/datadog/symbol_database/transport.rb +54 -0
  502. data/lib/datadog/symbol_database/uploader.rb +230 -0
  503. data/lib/datadog/symbol_database.rb +71 -0
  504. data/lib/datadog/tracing/analytics.rb +2 -2
  505. data/lib/datadog/tracing/buffer.rb +15 -15
  506. data/lib/datadog/tracing/client_ip.rb +4 -4
  507. data/lib/datadog/tracing/component.rb +39 -35
  508. data/lib/datadog/tracing/configuration/dynamic.rb +14 -16
  509. data/lib/datadog/tracing/configuration/ext.rb +60 -33
  510. data/lib/datadog/tracing/configuration/http.rb +2 -2
  511. data/lib/datadog/tracing/configuration/settings.rb +156 -26
  512. data/lib/datadog/tracing/context.rb +3 -3
  513. data/lib/datadog/tracing/context_provider.rb +3 -3
  514. data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +2 -2
  515. data/lib/datadog/tracing/contrib/action_cable/event.rb +5 -5
  516. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +9 -6
  517. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +8 -5
  518. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +9 -6
  519. data/lib/datadog/tracing/contrib/action_cable/events.rb +4 -4
  520. data/lib/datadog/tracing/contrib/action_cable/ext.rb +18 -18
  521. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -5
  522. data/lib/datadog/tracing/contrib/action_cable/integration.rb +10 -7
  523. data/lib/datadog/tracing/contrib/action_cable/patcher.rb +4 -4
  524. data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +2 -2
  525. data/lib/datadog/tracing/contrib/action_mailer/event.rb +7 -4
  526. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +8 -8
  527. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +4 -4
  528. data/lib/datadog/tracing/contrib/action_mailer/events.rb +2 -2
  529. data/lib/datadog/tracing/contrib/action_mailer/ext.rb +18 -18
  530. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +11 -7
  531. data/lib/datadog/tracing/contrib/action_mailer/patcher.rb +3 -3
  532. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +37 -19
  533. data/lib/datadog/tracing/contrib/action_pack/action_controller/patcher.rb +2 -2
  534. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +48 -13
  535. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +5 -3
  536. data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +3 -3
  537. data/lib/datadog/tracing/contrib/action_pack/ext.rb +10 -8
  538. data/lib/datadog/tracing/contrib/action_pack/integration.rb +10 -7
  539. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +3 -3
  540. data/lib/datadog/tracing/contrib/action_pack/utils.rb +2 -3
  541. data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +3 -3
  542. data/lib/datadog/tracing/contrib/action_view/event.rb +1 -1
  543. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +10 -7
  544. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +12 -9
  545. data/lib/datadog/tracing/contrib/action_view/events.rb +2 -2
  546. data/lib/datadog/tracing/contrib/action_view/ext.rb +10 -10
  547. data/lib/datadog/tracing/contrib/action_view/integration.rb +14 -11
  548. data/lib/datadog/tracing/contrib/action_view/patcher.rb +5 -5
  549. data/lib/datadog/tracing/contrib/action_view/utils.rb +2 -2
  550. data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +2 -2
  551. data/lib/datadog/tracing/contrib/active_job/data_streams.rb +62 -0
  552. data/lib/datadog/tracing/contrib/active_job/event.rb +11 -11
  553. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +11 -8
  554. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +11 -8
  555. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +11 -8
  556. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +11 -8
  557. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +11 -8
  558. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +11 -8
  559. data/lib/datadog/tracing/contrib/active_job/events.rb +6 -6
  560. data/lib/datadog/tracing/contrib/active_job/ext.rb +23 -23
  561. data/lib/datadog/tracing/contrib/active_job/integration.rb +10 -7
  562. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  563. data/lib/datadog/tracing/contrib/active_job/patcher.rb +21 -5
  564. data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +2 -2
  565. data/lib/datadog/tracing/contrib/active_model_serializers/event.rb +4 -4
  566. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +8 -8
  567. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +9 -10
  568. data/lib/datadog/tracing/contrib/active_model_serializers/events.rb +2 -2
  569. data/lib/datadog/tracing/contrib/active_model_serializers/ext.rb +10 -10
  570. data/lib/datadog/tracing/contrib/active_model_serializers/integration.rb +5 -6
  571. data/lib/datadog/tracing/contrib/active_model_serializers/patcher.rb +3 -3
  572. data/lib/datadog/tracing/contrib/active_record/configuration/makara_resolver.rb +1 -1
  573. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +8 -8
  574. data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +4 -4
  575. data/lib/datadog/tracing/contrib/active_record/event.rb +1 -1
  576. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +10 -10
  577. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +16 -19
  578. data/lib/datadog/tracing/contrib/active_record/events.rb +2 -2
  579. data/lib/datadog/tracing/contrib/active_record/ext.rb +15 -15
  580. data/lib/datadog/tracing/contrib/active_record/integration.rb +16 -12
  581. data/lib/datadog/tracing/contrib/active_record/patcher.rb +2 -2
  582. data/lib/datadog/tracing/contrib/active_record/utils.rb +19 -19
  583. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +1 -1
  584. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +28 -20
  585. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +1 -1
  586. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +40 -8
  587. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +8 -4
  588. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +5 -7
  589. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +26 -3
  590. data/lib/datadog/tracing/contrib/active_support/ext.rb +16 -16
  591. data/lib/datadog/tracing/contrib/active_support/integration.rb +11 -8
  592. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +3 -2
  593. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +2 -2
  594. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +9 -11
  595. data/lib/datadog/tracing/contrib/active_support/patcher.rb +2 -2
  596. data/lib/datadog/tracing/contrib/analytics.rb +1 -1
  597. data/lib/datadog/tracing/contrib/auto_instrument.rb +5 -5
  598. data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +3 -3
  599. data/lib/datadog/tracing/contrib/aws/ext.rb +26 -26
  600. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +21 -13
  601. data/lib/datadog/tracing/contrib/aws/integration.rb +11 -8
  602. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +9 -3
  603. data/lib/datadog/tracing/contrib/aws/patcher.rb +10 -6
  604. data/lib/datadog/tracing/contrib/aws/service/base.rb +2 -1
  605. data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +2 -2
  606. data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +2 -2
  607. data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +4 -4
  608. data/lib/datadog/tracing/contrib/aws/service/s3.rb +2 -2
  609. data/lib/datadog/tracing/contrib/aws/service/sns.rb +3 -3
  610. data/lib/datadog/tracing/contrib/aws/service/sqs.rb +3 -3
  611. data/lib/datadog/tracing/contrib/aws/service/states.rb +5 -5
  612. data/lib/datadog/tracing/contrib/aws/services.rb +14 -14
  613. data/lib/datadog/tracing/contrib/component.rb +3 -3
  614. data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +2 -2
  615. data/lib/datadog/tracing/contrib/concurrent_ruby/configuration/settings.rb +2 -2
  616. data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +2 -2
  617. data/lib/datadog/tracing/contrib/concurrent_ruby/ext.rb +2 -2
  618. data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +2 -2
  619. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +8 -5
  620. data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +4 -4
  621. data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +2 -2
  622. data/lib/datadog/tracing/contrib/configurable.rb +24 -9
  623. data/lib/datadog/tracing/contrib/configuration/resolver.rb +14 -4
  624. data/lib/datadog/tracing/contrib/configuration/resolvers/pattern_resolver.rb +5 -5
  625. data/lib/datadog/tracing/contrib/configuration/settings.rb +3 -3
  626. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +2 -2
  627. data/lib/datadog/tracing/contrib/dalli/ext.rb +16 -15
  628. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +6 -10
  629. data/lib/datadog/tracing/contrib/dalli/integration.rb +10 -7
  630. data/lib/datadog/tracing/contrib/dalli/patcher.rb +3 -3
  631. data/lib/datadog/tracing/contrib/dalli/quantize.rb +3 -3
  632. data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +2 -2
  633. data/lib/datadog/tracing/contrib/delayed_job/ext.rb +14 -14
  634. data/lib/datadog/tracing/contrib/delayed_job/integration.rb +5 -5
  635. data/lib/datadog/tracing/contrib/delayed_job/patcher.rb +3 -3
  636. data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +7 -5
  637. data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +1 -0
  638. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +2 -2
  639. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +18 -17
  640. data/lib/datadog/tracing/contrib/elasticsearch/integration.rb +8 -8
  641. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +57 -63
  642. data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +8 -8
  643. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +7 -3
  644. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +19 -21
  645. data/lib/datadog/tracing/contrib/ethon/ext.rb +16 -14
  646. data/lib/datadog/tracing/contrib/ethon/integration.rb +6 -6
  647. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +4 -11
  648. data/lib/datadog/tracing/contrib/ethon/patcher.rb +3 -3
  649. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +21 -10
  650. data/lib/datadog/tracing/contrib/excon/ext.rb +14 -12
  651. data/lib/datadog/tracing/contrib/excon/integration.rb +6 -6
  652. data/lib/datadog/tracing/contrib/excon/middleware.rb +17 -19
  653. data/lib/datadog/tracing/contrib/excon/patcher.rb +2 -2
  654. data/lib/datadog/tracing/contrib/ext.rb +20 -17
  655. data/lib/datadog/tracing/contrib/extensions.rb +57 -14
  656. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +20 -13
  657. data/lib/datadog/tracing/contrib/faraday/ext.rb +14 -12
  658. data/lib/datadog/tracing/contrib/faraday/integration.rb +6 -6
  659. data/lib/datadog/tracing/contrib/faraday/middleware.rb +19 -19
  660. data/lib/datadog/tracing/contrib/faraday/patcher.rb +6 -6
  661. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +11 -7
  662. data/lib/datadog/tracing/contrib/grape/endpoint.rb +37 -34
  663. data/lib/datadog/tracing/contrib/grape/ext.rb +15 -15
  664. data/lib/datadog/tracing/contrib/grape/instrumentation.rb +15 -10
  665. data/lib/datadog/tracing/contrib/grape/integration.rb +5 -5
  666. data/lib/datadog/tracing/contrib/grape/patcher.rb +10 -5
  667. data/lib/datadog/tracing/contrib/graphql/configuration/error_extension_env_parser.rb +21 -0
  668. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +20 -2
  669. data/lib/datadog/tracing/contrib/graphql/ext.rb +11 -5
  670. data/lib/datadog/tracing/contrib/graphql/integration.rb +9 -9
  671. data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -8
  672. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +174 -41
  673. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +2 -2
  674. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +7 -4
  675. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +25 -23
  676. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +13 -17
  677. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +6 -6
  678. data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +2 -2
  679. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +12 -7
  680. data/lib/datadog/tracing/contrib/grpc/ext.rb +14 -13
  681. data/lib/datadog/tracing/contrib/grpc/formatting.rb +7 -7
  682. data/lib/datadog/tracing/contrib/grpc/integration.rb +5 -5
  683. data/lib/datadog/tracing/contrib/grpc/intercept_with_datadog.rb +1 -1
  684. data/lib/datadog/tracing/contrib/grpc/patcher.rb +3 -3
  685. data/lib/datadog/tracing/contrib/grpc.rb +7 -6
  686. data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +4 -3
  687. data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +2 -2
  688. data/lib/datadog/tracing/contrib/hanami/ext.rb +8 -8
  689. data/lib/datadog/tracing/contrib/hanami/integration.rb +6 -6
  690. data/lib/datadog/tracing/contrib/hanami/patcher.rb +5 -5
  691. data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +5 -4
  692. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +12 -13
  693. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +1 -16
  694. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +20 -9
  695. data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +6 -6
  696. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +13 -8
  697. data/lib/datadog/tracing/contrib/http/ext.rb +14 -12
  698. data/lib/datadog/tracing/contrib/http/instrumentation.rb +20 -28
  699. data/lib/datadog/tracing/contrib/http/integration.rb +9 -8
  700. data/lib/datadog/tracing/contrib/http/patcher.rb +3 -3
  701. data/lib/datadog/tracing/contrib/http.rb +7 -6
  702. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +20 -9
  703. data/lib/datadog/tracing/contrib/httpclient/ext.rb +14 -12
  704. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +21 -31
  705. data/lib/datadog/tracing/contrib/httpclient/integration.rb +6 -6
  706. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +3 -3
  707. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +20 -9
  708. data/lib/datadog/tracing/contrib/httprb/ext.rb +14 -12
  709. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +20 -32
  710. data/lib/datadog/tracing/contrib/httprb/integration.rb +9 -6
  711. data/lib/datadog/tracing/contrib/httprb/patcher.rb +3 -3
  712. data/lib/datadog/tracing/contrib/integration.rb +3 -3
  713. data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +2 -2
  714. data/lib/datadog/tracing/contrib/kafka/event.rb +5 -4
  715. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +4 -4
  716. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +5 -5
  717. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +5 -5
  718. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +6 -6
  719. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/join_group.rb +6 -6
  720. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/leave_group.rb +6 -6
  721. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/sync_group.rb +6 -6
  722. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +4 -4
  723. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +4 -4
  724. data/lib/datadog/tracing/contrib/kafka/events.rb +9 -9
  725. data/lib/datadog/tracing/contrib/kafka/ext.rb +40 -40
  726. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  727. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  728. data/lib/datadog/tracing/contrib/kafka/integration.rb +8 -5
  729. data/lib/datadog/tracing/contrib/kafka/patcher.rb +17 -3
  730. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +31 -0
  731. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +50 -0
  732. data/lib/datadog/tracing/contrib/karafka/ext.rb +28 -0
  733. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  734. data/lib/datadog/tracing/contrib/karafka/integration.rb +45 -0
  735. data/lib/datadog/tracing/contrib/karafka/monitor.rb +77 -0
  736. data/lib/datadog/tracing/contrib/karafka/patcher.rb +102 -0
  737. data/lib/datadog/tracing/contrib/karafka.rb +38 -0
  738. data/lib/datadog/tracing/contrib/lograge/configuration/settings.rb +2 -2
  739. data/lib/datadog/tracing/contrib/lograge/ext.rb +1 -1
  740. data/lib/datadog/tracing/contrib/lograge/instrumentation.rb +1 -1
  741. data/lib/datadog/tracing/contrib/lograge/integration.rb +5 -5
  742. data/lib/datadog/tracing/contrib/lograge/patcher.rb +13 -11
  743. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +11 -3
  744. data/lib/datadog/tracing/contrib/mongodb/ext.rb +19 -18
  745. data/lib/datadog/tracing/contrib/mongodb/instrumentation.rb +3 -3
  746. data/lib/datadog/tracing/contrib/mongodb/integration.rb +9 -6
  747. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +10 -10
  748. data/lib/datadog/tracing/contrib/mongodb/patcher.rb +3 -3
  749. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +38 -24
  750. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +9 -3
  751. data/lib/datadog/tracing/contrib/mysql2/ext.rb +12 -12
  752. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +25 -18
  753. data/lib/datadog/tracing/contrib/mysql2/integration.rb +5 -5
  754. data/lib/datadog/tracing/contrib/mysql2/patcher.rb +2 -2
  755. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +19 -2
  756. data/lib/datadog/tracing/contrib/opensearch/ext.rb +31 -21
  757. data/lib/datadog/tracing/contrib/opensearch/integration.rb +8 -6
  758. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +75 -77
  759. data/lib/datadog/tracing/contrib/opensearch/quantize.rb +9 -9
  760. data/lib/datadog/tracing/contrib/patcher.rb +14 -13
  761. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +9 -3
  762. data/lib/datadog/tracing/contrib/pg/ext.rb +19 -19
  763. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +21 -24
  764. data/lib/datadog/tracing/contrib/pg/integration.rb +5 -5
  765. data/lib/datadog/tracing/contrib/pg/patcher.rb +2 -2
  766. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +2 -2
  767. data/lib/datadog/tracing/contrib/presto/ext.rb +22 -22
  768. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +11 -13
  769. data/lib/datadog/tracing/contrib/presto/integration.rb +8 -5
  770. data/lib/datadog/tracing/contrib/presto/patcher.rb +4 -4
  771. data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +2 -2
  772. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +26 -13
  773. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +7 -3
  774. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +29 -5
  775. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +7 -4
  776. data/lib/datadog/tracing/contrib/que/ext.rb +19 -18
  777. data/lib/datadog/tracing/contrib/que/integration.rb +6 -6
  778. data/lib/datadog/tracing/contrib/que/patcher.rb +1 -1
  779. data/lib/datadog/tracing/contrib/que/tracer.rb +2 -1
  780. data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +3 -3
  781. data/lib/datadog/tracing/contrib/racecar/event.rb +6 -10
  782. data/lib/datadog/tracing/contrib/racecar/events/batch.rb +5 -5
  783. data/lib/datadog/tracing/contrib/racecar/events/consume.rb +4 -4
  784. data/lib/datadog/tracing/contrib/racecar/events/message.rb +5 -5
  785. data/lib/datadog/tracing/contrib/racecar/events.rb +3 -3
  786. data/lib/datadog/tracing/contrib/racecar/ext.rb +18 -18
  787. data/lib/datadog/tracing/contrib/racecar/integration.rb +5 -5
  788. data/lib/datadog/tracing/contrib/racecar/patcher.rb +3 -3
  789. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +13 -3
  790. data/lib/datadog/tracing/contrib/rack/ext.rb +43 -15
  791. data/lib/datadog/tracing/contrib/rack/header_collection.rb +13 -3
  792. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +55 -32
  793. data/lib/datadog/tracing/contrib/rack/integration.rb +7 -7
  794. data/lib/datadog/tracing/contrib/rack/middlewares.rb +102 -63
  795. data/lib/datadog/tracing/contrib/rack/patcher.rb +5 -5
  796. data/lib/datadog/tracing/contrib/rack/request_queue.rb +8 -7
  797. data/lib/datadog/tracing/contrib/rack/route_inference.rb +67 -0
  798. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +130 -6
  799. data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +1 -1
  800. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +9 -6
  801. data/lib/datadog/tracing/contrib/rails/ext.rb +16 -14
  802. data/lib/datadog/tracing/contrib/rails/framework.rb +12 -12
  803. data/lib/datadog/tracing/contrib/rails/integration.rb +6 -6
  804. data/lib/datadog/tracing/contrib/rails/log_injection.rb +2 -2
  805. data/lib/datadog/tracing/contrib/rails/middlewares.rb +4 -4
  806. data/lib/datadog/tracing/contrib/rails/patcher.rb +11 -9
  807. data/lib/datadog/tracing/contrib/rails/railtie.rb +4 -4
  808. data/lib/datadog/tracing/contrib/rails/runner.rb +64 -40
  809. data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +3 -3
  810. data/lib/datadog/tracing/contrib/rake/ext.rb +12 -12
  811. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +13 -11
  812. data/lib/datadog/tracing/contrib/rake/integration.rb +5 -5
  813. data/lib/datadog/tracing/contrib/rake/patcher.rb +3 -3
  814. data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +4 -4
  815. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +2 -2
  816. data/lib/datadog/tracing/contrib/redis/ext.rb +19 -18
  817. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +6 -6
  818. data/lib/datadog/tracing/contrib/redis/integration.rb +7 -7
  819. data/lib/datadog/tracing/contrib/redis/patcher.rb +12 -12
  820. data/lib/datadog/tracing/contrib/redis/quantize.rb +6 -6
  821. data/lib/datadog/tracing/contrib/redis/tags.rb +7 -12
  822. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +11 -9
  823. data/lib/datadog/tracing/contrib/registerable.rb +11 -0
  824. data/lib/datadog/tracing/contrib/registry.rb +1 -1
  825. data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +2 -2
  826. data/lib/datadog/tracing/contrib/resque/ext.rb +7 -7
  827. data/lib/datadog/tracing/contrib/resque/integration.rb +5 -5
  828. data/lib/datadog/tracing/contrib/resque/patcher.rb +3 -3
  829. data/lib/datadog/tracing/contrib/resque/resque_job.rb +7 -6
  830. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +7 -4
  831. data/lib/datadog/tracing/contrib/rest_client/ext.rb +13 -11
  832. data/lib/datadog/tracing/contrib/rest_client/integration.rb +8 -5
  833. data/lib/datadog/tracing/contrib/rest_client/patcher.rb +2 -2
  834. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +18 -17
  835. data/lib/datadog/tracing/contrib/roda/configuration/settings.rb +2 -2
  836. data/lib/datadog/tracing/contrib/roda/ext.rb +6 -5
  837. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +12 -7
  838. data/lib/datadog/tracing/contrib/roda/integration.rb +6 -6
  839. data/lib/datadog/tracing/contrib/roda/patcher.rb +3 -3
  840. data/lib/datadog/tracing/contrib/semantic_logger/configuration/settings.rb +2 -2
  841. data/lib/datadog/tracing/contrib/semantic_logger/ext.rb +1 -1
  842. data/lib/datadog/tracing/contrib/semantic_logger/instrumentation.rb +1 -1
  843. data/lib/datadog/tracing/contrib/semantic_logger/integration.rb +5 -5
  844. data/lib/datadog/tracing/contrib/semantic_logger/patcher.rb +2 -2
  845. data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +2 -2
  846. data/lib/datadog/tracing/contrib/sequel/database.rb +12 -11
  847. data/lib/datadog/tracing/contrib/sequel/dataset.rb +7 -6
  848. data/lib/datadog/tracing/contrib/sequel/ext.rb +12 -8
  849. data/lib/datadog/tracing/contrib/sequel/integration.rb +5 -5
  850. data/lib/datadog/tracing/contrib/sequel/patcher.rb +3 -3
  851. data/lib/datadog/tracing/contrib/sequel/utils.rb +115 -15
  852. data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +2 -2
  853. data/lib/datadog/tracing/contrib/shoryuken/ext.rb +12 -12
  854. data/lib/datadog/tracing/contrib/shoryuken/integration.rb +6 -6
  855. data/lib/datadog/tracing/contrib/shoryuken/patcher.rb +1 -1
  856. data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +3 -2
  857. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +15 -9
  858. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +7 -3
  859. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +13 -8
  860. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +31 -29
  861. data/lib/datadog/tracing/contrib/sidekiq/integration.rb +7 -7
  862. data/lib/datadog/tracing/contrib/sidekiq/patcher.rb +8 -8
  863. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/heartbeat.rb +2 -0
  864. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/job_fetch.rb +1 -0
  865. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/redis_info.rb +1 -0
  866. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/scheduled_poller.rb +2 -0
  867. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/stop.rb +1 -0
  868. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +19 -15
  869. data/lib/datadog/tracing/contrib/sidekiq/utils.rb +10 -10
  870. data/lib/datadog/tracing/contrib/sidekiq.rb +7 -6
  871. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +7 -3
  872. data/lib/datadog/tracing/contrib/sinatra/env.rb +6 -6
  873. data/lib/datadog/tracing/contrib/sinatra/ext.rb +17 -16
  874. data/lib/datadog/tracing/contrib/sinatra/framework.rb +2 -2
  875. data/lib/datadog/tracing/contrib/sinatra/integration.rb +5 -5
  876. data/lib/datadog/tracing/contrib/sinatra/patcher.rb +5 -5
  877. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +11 -10
  878. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +46 -45
  879. data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +1 -1
  880. data/lib/datadog/tracing/contrib/sneakers/ext.rb +12 -12
  881. data/lib/datadog/tracing/contrib/sneakers/integration.rb +19 -8
  882. data/lib/datadog/tracing/contrib/sneakers/patcher.rb +2 -2
  883. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +4 -3
  884. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +9 -4
  885. data/lib/datadog/tracing/contrib/status_range_env_parser.rb +2 -2
  886. data/lib/datadog/tracing/contrib/status_range_matcher.rb +13 -1
  887. data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +2 -2
  888. data/lib/datadog/tracing/contrib/stripe/ext.rb +12 -12
  889. data/lib/datadog/tracing/contrib/stripe/integration.rb +5 -5
  890. data/lib/datadog/tracing/contrib/stripe/patcher.rb +2 -2
  891. data/lib/datadog/tracing/contrib/stripe/request.rb +5 -5
  892. data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +2 -2
  893. data/lib/datadog/tracing/contrib/sucker_punch/exception_handler.rb +1 -1
  894. data/lib/datadog/tracing/contrib/sucker_punch/ext.rb +13 -13
  895. data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +5 -4
  896. data/lib/datadog/tracing/contrib/sucker_punch/integration.rb +5 -5
  897. data/lib/datadog/tracing/contrib/sucker_punch/patcher.rb +4 -4
  898. data/lib/datadog/tracing/contrib/support.rb +28 -0
  899. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +9 -3
  900. data/lib/datadog/tracing/contrib/trilogy/ext.rb +12 -12
  901. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +10 -12
  902. data/lib/datadog/tracing/contrib/trilogy/integration.rb +5 -5
  903. data/lib/datadog/tracing/contrib/trilogy/patcher.rb +2 -2
  904. data/lib/datadog/tracing/contrib/utils/database.rb +5 -5
  905. data/lib/datadog/tracing/contrib/utils/quantization/{hash.rb → hash_formatter.rb} +16 -14
  906. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +16 -16
  907. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +31 -0
  908. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +50 -0
  909. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +18 -0
  910. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  911. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  912. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +49 -0
  913. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  914. data/lib/datadog/tracing/contrib/waterdrop.rb +42 -0
  915. data/lib/datadog/tracing/contrib.rb +62 -52
  916. data/lib/datadog/tracing/correlation.rb +19 -12
  917. data/lib/datadog/tracing/diagnostics/environment_logger.rb +21 -13
  918. data/lib/datadog/tracing/diagnostics/ext.rb +19 -19
  919. data/lib/datadog/tracing/diagnostics/health.rb +1 -1
  920. data/lib/datadog/tracing/distributed/b3_multi.rb +9 -9
  921. data/lib/datadog/tracing/distributed/b3_single.rb +6 -6
  922. data/lib/datadog/tracing/distributed/baggage.rb +252 -0
  923. data/lib/datadog/tracing/distributed/datadog.rb +39 -36
  924. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +12 -27
  925. data/lib/datadog/tracing/distributed/fetcher.rb +1 -1
  926. data/lib/datadog/tracing/distributed/helpers.rb +4 -4
  927. data/lib/datadog/tracing/distributed/none.rb +4 -2
  928. data/lib/datadog/tracing/distributed/propagation.rb +70 -14
  929. data/lib/datadog/tracing/distributed/propagation_policy.rb +42 -0
  930. data/lib/datadog/tracing/distributed/trace_context.rb +126 -97
  931. data/lib/datadog/tracing/event.rb +6 -8
  932. data/lib/datadog/tracing/ext.rb +9 -0
  933. data/lib/datadog/tracing/flush.rb +1 -1
  934. data/lib/datadog/tracing/metadata/analytics.rb +3 -3
  935. data/lib/datadog/tracing/metadata/errors.rb +8 -8
  936. data/lib/datadog/tracing/metadata/ext.rb +86 -59
  937. data/lib/datadog/tracing/metadata/metastruct.rb +36 -0
  938. data/lib/datadog/tracing/metadata/metastruct_tagging.rb +42 -0
  939. data/lib/datadog/tracing/metadata/tagging.rb +8 -8
  940. data/lib/datadog/tracing/metadata.rb +5 -3
  941. data/lib/datadog/tracing/pipeline/span_filter.rb +5 -3
  942. data/lib/datadog/tracing/pipeline/span_processor.rb +3 -1
  943. data/lib/datadog/tracing/pipeline.rb +4 -4
  944. data/lib/datadog/tracing/remote.rb +47 -17
  945. data/lib/datadog/tracing/sampling/all_sampler.rb +1 -1
  946. data/lib/datadog/tracing/sampling/ext.rb +9 -9
  947. data/lib/datadog/tracing/sampling/matcher.rb +4 -4
  948. data/lib/datadog/tracing/sampling/priority_sampler.rb +20 -5
  949. data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +3 -3
  950. data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +2 -2
  951. data/lib/datadog/tracing/sampling/rate_sampler.rb +9 -19
  952. data/lib/datadog/tracing/sampling/rule.rb +5 -5
  953. data/lib/datadog/tracing/sampling/rule_sampler.rb +88 -59
  954. data/lib/datadog/tracing/sampling/sampler.rb +2 -2
  955. data/lib/datadog/tracing/sampling/span/ext.rb +3 -3
  956. data/lib/datadog/tracing/sampling/span/matcher.rb +2 -2
  957. data/lib/datadog/tracing/sampling/span/rule.rb +1 -2
  958. data/lib/datadog/tracing/sampling/span/rule_parser.rb +11 -11
  959. data/lib/datadog/tracing/sampling/span/sampler.rb +0 -7
  960. data/lib/datadog/tracing/span.rb +28 -11
  961. data/lib/datadog/tracing/span_event.rb +125 -5
  962. data/lib/datadog/tracing/span_link.rb +12 -12
  963. data/lib/datadog/tracing/span_operation.rb +119 -39
  964. data/lib/datadog/tracing/sync_writer.rb +14 -11
  965. data/lib/datadog/tracing/trace_digest.rb +36 -24
  966. data/lib/datadog/tracing/trace_operation.rb +243 -112
  967. data/lib/datadog/tracing/trace_segment.rb +18 -16
  968. data/lib/datadog/tracing/tracer.rb +181 -72
  969. data/lib/datadog/tracing/transport/http/client.rb +17 -30
  970. data/lib/datadog/tracing/transport/http/statistics.rb +2 -2
  971. data/lib/datadog/tracing/transport/http/traces.rb +11 -90
  972. data/lib/datadog/tracing/transport/http.rb +26 -77
  973. data/lib/datadog/tracing/transport/io/client.rb +13 -16
  974. data/lib/datadog/tracing/transport/io/response.rb +1 -1
  975. data/lib/datadog/tracing/transport/io/traces.rb +32 -38
  976. data/lib/datadog/tracing/transport/io.rb +3 -3
  977. data/lib/datadog/tracing/transport/serializable_trace.rb +47 -23
  978. data/lib/datadog/tracing/transport/statistics.rb +3 -3
  979. data/lib/datadog/tracing/transport/trace_formatter.rb +36 -7
  980. data/lib/datadog/tracing/transport/traces.rb +56 -90
  981. data/lib/datadog/tracing/utils.rb +3 -3
  982. data/lib/datadog/tracing/workers.rb +11 -9
  983. data/lib/datadog/tracing/writer.rb +15 -11
  984. data/lib/datadog/tracing.rb +19 -6
  985. data/lib/datadog/version.rb +4 -4
  986. data/lib/datadog.rb +20 -7
  987. metadata +327 -69
  988. data/ext/datadog_profiling_loader/datadog_profiling_loader.c +0 -142
  989. data/ext/datadog_profiling_loader/extconf.rb +0 -60
  990. data/ext/datadog_profiling_native_extension/clock_id_noop.c +0 -21
  991. data/ext/libdatadog_api/macos_development.md +0 -26
  992. data/lib/datadog/appsec/assets/waf_rules/processors.json +0 -92
  993. data/lib/datadog/appsec/assets/waf_rules/scanners.json +0 -114
  994. data/lib/datadog/appsec/configuration/settings.rb +0 -215
  995. data/lib/datadog/appsec/contrib/devise/event.rb +0 -57
  996. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +0 -77
  997. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +0 -54
  998. data/lib/datadog/appsec/contrib/devise/resource.rb +0 -35
  999. data/lib/datadog/appsec/contrib/devise/tracking.rb +0 -57
  1000. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +0 -46
  1001. data/lib/datadog/appsec/contrib/patcher.rb +0 -12
  1002. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +0 -69
  1003. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +0 -47
  1004. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +0 -53
  1005. data/lib/datadog/appsec/contrib/rails/ext.rb +0 -13
  1006. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +0 -53
  1007. data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -14
  1008. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +0 -48
  1009. data/lib/datadog/appsec/monitor/reactive/set_user.rb +0 -45
  1010. data/lib/datadog/appsec/processor/context.rb +0 -107
  1011. data/lib/datadog/appsec/processor/rule_merger.rb +0 -170
  1012. data/lib/datadog/appsec/processor.rb +0 -106
  1013. data/lib/datadog/appsec/reactive/address_hash.rb +0 -22
  1014. data/lib/datadog/appsec/reactive/engine.rb +0 -47
  1015. data/lib/datadog/appsec/reactive/operation.rb +0 -68
  1016. data/lib/datadog/appsec/reactive/subscriber.rb +0 -19
  1017. data/lib/datadog/appsec/scope.rb +0 -58
  1018. data/lib/datadog/appsec/utils/trace_operation.rb +0 -15
  1019. data/lib/datadog/core/crashtracking/agent_base_url.rb +0 -21
  1020. data/lib/datadog/core/remote/transport/http/api/instance.rb +0 -39
  1021. data/lib/datadog/core/remote/transport/http/api/spec.rb +0 -21
  1022. data/lib/datadog/core/remote/transport/http/api.rb +0 -58
  1023. data/lib/datadog/core/remote/transport/http/builder.rb +0 -219
  1024. data/lib/datadog/core/remote/transport/http/client.rb +0 -48
  1025. data/lib/datadog/core/telemetry/http/env.rb +0 -20
  1026. data/lib/datadog/core/telemetry/http/ext.rb +0 -28
  1027. data/lib/datadog/core/telemetry/http/response.rb +0 -70
  1028. data/lib/datadog/core/telemetry/http/transport.rb +0 -90
  1029. data/lib/datadog/core/utils/base64.rb +0 -22
  1030. data/lib/datadog/di/configuration/settings.rb +0 -188
  1031. data/lib/datadog/di/transport.rb +0 -81
  1032. data/lib/datadog/tracing/contrib/rails/utils.rb +0 -26
  1033. data/lib/datadog/tracing/transport/http/api/spec.rb +0 -19
  1034. data/lib/datadog/tracing/transport/http/api.rb +0 -43
  1035. data/lib/datadog/tracing/workers/trace_writer.rb +0 -196
@@ -1,12 +1,22 @@
1
1
  #include "heap_recorder.h"
2
- #include <pthread.h>
3
2
  #include "ruby/st.h"
4
3
  #include "ruby_helpers.h"
5
- #include <errno.h>
6
4
  #include "collectors_stack.h"
7
5
  #include "libdatadog_helpers.h"
8
6
  #include "time_helpers.h"
9
7
 
8
+ // note on calloc vs ruby_xcalloc use:
9
+ // * Whenever we're allocating memory after being called by the Ruby VM in a "regular" situation (e.g. initializer)
10
+ // we should use `ruby_xcalloc` to give the VM visibility into what we're doing + give it a chance to manage GC
11
+ // * BUT, when we're being called during a sample, being in the middle of an object allocation is a very special
12
+ // situation for the VM to be in, and we've found the hard way (e.g. https://bugs.ruby-lang.org/issues/20629 and
13
+ // https://github.com/DataDog/dd-trace-rb/pull/4240 ) that it can be easy to do things the VM didn't expect.
14
+ // * Thus, out of caution and to avoid future potential issues such as the ones above, whenever we allocate memory
15
+ // during **sampling** we use `calloc` instead of `ruby_xcalloc`. Note that we've never seen issues from using
16
+ // `ruby_xcalloc` at any time, so this is a **precaution** not a "we've seen it break". But it seems a harmless
17
+ // one to use.
18
+ // This applies to both heap_recorder.c and collectors_thread_context.c
19
+
10
20
  // Minimum age (in GC generations) of heap objects we want to include in heap
11
21
  // recorder iterations. Object with age 0 represent objects that have yet to undergo
12
22
  // a GC and, thus, may just be noise/trash at instant of iteration and are usually not
@@ -26,81 +36,37 @@
26
36
 
27
37
  // A compact representation of a stacktrace frame for a heap allocation.
28
38
  typedef struct {
29
- char *name;
30
- char *filename;
39
+ ddog_prof_ManagedStringId name;
40
+ ddog_prof_ManagedStringId filename;
31
41
  int32_t line;
32
42
  } heap_frame;
33
- static st_index_t heap_frame_hash(heap_frame*, st_index_t seed);
43
+
44
+ // We use memcmp/st_hash below to compare/hash an entire array of heap_frames, so want to make sure no padding is added
45
+ // We could define the structure to be packed, but that seems even weirder across compilers, and this seems more portable?
46
+ _Static_assert(
47
+ sizeof(heap_frame) == sizeof(ddog_prof_ManagedStringId) * 2 + sizeof(int32_t),
48
+ "Size of heap_frame does not match the sum of its members. Padding detected."
49
+ );
34
50
 
35
51
  // A compact representation of a stacktrace for a heap allocation.
36
- //
37
- // We could use a ddog_prof_Slice_Location instead but it has a lot of
38
- // unused fields. Because we have to keep these stacks around for at
39
- // least the lifetime of the objects allocated therein, we would be
40
- // incurring a non-negligible memory overhead for little purpose.
52
+ // Used to dedup heap allocation stacktraces across multiple objects sharing the same allocation location.
41
53
  typedef struct {
54
+ // How many objects are currently tracked in object_records recorder for this heap record.
55
+ uint32_t num_tracked_objects;
56
+
42
57
  uint16_t frames_len;
43
58
  heap_frame frames[];
44
- } heap_stack;
45
- static heap_stack* heap_stack_new(ddog_prof_Slice_Location);
46
- static void heap_stack_free(heap_stack*);
47
- static st_index_t heap_stack_hash(heap_stack*, st_index_t);
59
+ } heap_record;
60
+ static heap_record* heap_record_new(heap_recorder*, ddog_prof_Slice_Location);
61
+ static void heap_record_free(heap_recorder*, heap_record*, bool should_unintern);
48
62
 
49
63
  #if MAX_FRAMES_LIMIT > UINT16_MAX
50
64
  #error Frames len type not compatible with MAX_FRAMES_LIMIT
51
65
  #endif
52
66
 
53
- enum heap_record_key_type {
54
- HEAP_STACK,
55
- LOCATION_SLICE
56
- };
57
- // This struct allows us to use two different types of stacks when
58
- // interacting with a heap_record hash.
59
- //
60
- // The idea is that we'll always want to use heap_stack-keys when
61
- // adding new entries to the hash since that's the compact stack
62
- // representation we rely on internally.
63
- //
64
- // However, when querying for an existing heap record, we'd save a
65
- // lot of allocations if we could query with the
66
- // ddog_prof_Slice_Location we receive in our external API.
67
- //
68
- // To allow this interchange, we need a union and need to ensure
69
- // that whatever shape of the union, the heap_record_key_cmp_st
70
- // and heap_record_hash_st functions return the same results for
71
- // equivalent stacktraces.
72
- typedef struct {
73
- enum heap_record_key_type type;
74
- union {
75
- // key never owns this if set
76
- heap_stack *heap_stack;
77
- // key never owns this if set
78
- ddog_prof_Slice_Location *location_slice;
79
- };
80
- } heap_record_key;
81
- static heap_record_key* heap_record_key_new(heap_stack*);
82
- static void heap_record_key_free(heap_record_key*);
83
- static int heap_record_key_cmp_st(st_data_t, st_data_t);
84
- static st_index_t heap_record_key_hash_st(st_data_t);
85
- static const struct st_hash_type st_hash_type_heap_record_key = {
86
- heap_record_key_cmp_st,
87
- heap_record_key_hash_st,
88
- };
89
-
90
- // Need to implement these functions to support the location-slice based keys
91
- static st_index_t ddog_location_hash(ddog_prof_Location, st_index_t seed);
92
- static st_index_t ddog_location_slice_hash(ddog_prof_Slice_Location, st_index_t seed);
93
-
94
- // A heap record is used for deduping heap allocation stacktraces across multiple
95
- // objects sharing the same allocation location.
96
- typedef struct {
97
- // How many objects are currently tracked by the heap recorder for this heap record.
98
- uint32_t num_tracked_objects;
99
- // stack is owned by the associated record and gets cleaned up alongside it
100
- heap_stack *stack;
101
- } heap_record;
102
- static heap_record* heap_record_new(heap_stack*);
103
- static void heap_record_free(heap_record*);
67
+ static int heap_record_cmp_st(st_data_t, st_data_t);
68
+ static st_index_t heap_record_hash_st(st_data_t);
69
+ static const struct st_hash_type st_hash_type_heap_record = { .compare = heap_record_cmp_st, .hash = heap_record_hash_st };
104
70
 
105
71
  // An object record is used for storing data about currently tracked live objects
106
72
  typedef struct {
@@ -109,17 +75,19 @@ typedef struct {
109
75
  live_object_data object_data;
110
76
  } object_record;
111
77
  static object_record* object_record_new(long, heap_record*, live_object_data);
112
- static void object_record_free(object_record*);
113
- static VALUE object_record_inspect(object_record*);
78
+ static void object_record_free(heap_recorder*, object_record*, bool should_unintern);
79
+ static VALUE object_record_inspect(heap_recorder*, object_record*);
114
80
  static object_record SKIPPED_RECORD = {0};
115
81
 
116
- // A wrapper around an object record that is in the process of being recorded and was not
117
- // yet committed.
82
+ // A pending recording is used to defer the object_id call on Ruby 4+
83
+ // where calling rb_obj_id during on_newobj_event is unsafe.
118
84
  typedef struct {
119
- // Pointer to the (potentially partial) object_record containing metadata about an ongoing recording.
120
- // When NULL, this symbolizes an unstarted/invalid recording.
121
- object_record *object_record;
122
- } recording;
85
+ VALUE object_ref;
86
+ heap_record *heap_record;
87
+ live_object_data object_data;
88
+ } pending_recording;
89
+
90
+ #define MAX_PENDING_RECORDINGS 256
123
91
 
124
92
  struct heap_recorder {
125
93
  // Config
@@ -127,12 +95,15 @@ struct heap_recorder {
127
95
  bool size_enabled;
128
96
  uint sample_rate;
129
97
 
130
- // Map[key: heap_record_key*, record: heap_record*]
131
- // NOTE: We always use heap_record_key.type == HEAP_STACK for storage but support lookups
132
- // via heap_record_key.type == LOCATION_SLICE to allow for allocation-free fast-paths.
98
+ // Map[key: heap_record*, record: nothing] (This is a set, basically)
133
99
  // NOTE: This table is currently only protected by the GVL since we never interact with it
134
100
  // outside the GVL.
135
- // NOTE: This table has ownership of both its heap_record_keys and heap_records.
101
+ // NOTE: This table has ownership of its heap_records.
102
+ //
103
+ // This is a cpu/memory trade-off: Maintaining the "heap_records" map means we spend extra CPU when sampling as we need
104
+ // to do de-duplication, but we reduce the memory footprint of the heap profiler.
105
+ // In the future, it may be worth revisiting if we can move this inside libdatadog: if libdatadog was able to track
106
+ // entire stacks for us, then we wouldn't need to do it on the Ruby side.
136
107
  st_table *heap_records;
137
108
 
138
109
  // Map[obj_id: long, record: object_record*]
@@ -140,6 +111,11 @@ struct heap_recorder {
140
111
  // outside the GVL.
141
112
  // NOTE: This table has ownership of its object_records. The keys are longs and so are
142
113
  // passed as values.
114
+ //
115
+ // TODO: @ivoanjo We've evolved to actually never need to look up on object_records (we only insert and iterate),
116
+ // so right now this seems to be just a really really fancy self-resizing list/set.
117
+ // If we replace this with a list, we could record the latest id and compare it when inserting to make sure our
118
+ // assumption of ids never reused + always increasing always holds. (This as an alternative to checking for duplicates)
143
119
  st_table *object_records;
144
120
 
145
121
  // Map[obj_id: long, record: object_record*]
@@ -162,14 +138,30 @@ struct heap_recorder {
162
138
  long last_update_ns;
163
139
 
164
140
  // Data for a heap recording that was started but not yet ended
165
- recording active_recording;
166
-
167
- // Reusable location array, implementing a flyweight pattern for things like iteration.
141
+ object_record *active_recording;
142
+
143
+ // Pending recordings that need to be finalized after on_newobj_event completes.
144
+ // On Ruby 4+, we can't call rb_obj_id during the newobj event, so we store the
145
+ // VALUE reference here and finalize it via a postponed job.
146
+ pending_recording pending_recordings[MAX_PENDING_RECORDINGS];
147
+ // Temporary storage for the recording in progress, used between start and end
148
+ VALUE active_deferred_object;
149
+ live_object_data active_deferred_object_data;
150
+ uint16_t pending_recordings_count;
151
+
152
+ // Reusable arrays, implementing a flyweight pattern for things like iteration
153
+ #define REUSABLE_LOCATIONS_SIZE MAX_FRAMES_LIMIT
168
154
  ddog_prof_Location *reusable_locations;
169
155
 
156
+ #define REUSABLE_FRAME_DETAILS_SIZE (2 * MAX_FRAMES_LIMIT) // because it'll be used for both function names AND file names)
157
+ ddog_prof_ManagedStringId *reusable_ids;
158
+ ddog_CharSlice *reusable_char_slices;
159
+
170
160
  // Sampling state
171
161
  uint num_recordings_skipped;
172
162
 
163
+ ddog_prof_ManagedStringStorage string_storage;
164
+
173
165
  struct stats_last_update {
174
166
  size_t objects_alive;
175
167
  size_t objects_dead;
@@ -190,27 +182,35 @@ struct heap_recorder {
190
182
  double ewma_objects_alive;
191
183
  double ewma_objects_dead;
192
184
  double ewma_objects_skipped;
185
+
186
+ unsigned long deferred_recordings_skipped_buffer_full;
187
+ unsigned long deferred_recordings_finalized;
193
188
  } stats_lifetime;
194
189
  };
195
190
 
196
- struct end_heap_allocation_args {
197
- struct heap_recorder *heap_recorder;
191
+ typedef struct {
192
+ heap_recorder *heap_recorder;
198
193
  ddog_prof_Slice_Location locations;
199
- };
194
+ } end_heap_allocation_args;
200
195
 
201
196
  static heap_record* get_or_create_heap_record(heap_recorder*, ddog_prof_Slice_Location);
202
197
  static void cleanup_heap_record_if_unused(heap_recorder*, heap_record*);
203
198
  static void on_committed_object_record_cleanup(heap_recorder *heap_recorder, object_record *record);
204
- static int st_heap_record_entry_free(st_data_t, st_data_t, st_data_t);
205
- static int st_object_record_entry_free(st_data_t, st_data_t, st_data_t);
199
+ static int st_heap_record_entry_free_no_unintern(st_data_t, st_data_t, st_data_t);
200
+ static int st_object_record_entry_free_no_unintern(st_data_t, st_data_t, st_data_t);
206
201
  static int st_object_record_update(st_data_t, st_data_t, st_data_t);
207
202
  static int st_object_records_iterate(st_data_t, st_data_t, st_data_t);
208
203
  static int st_object_records_debug(st_data_t key, st_data_t value, st_data_t extra);
209
204
  static int update_object_record_entry(st_data_t*, st_data_t*, st_data_t, int);
210
- static void commit_recording(heap_recorder*, heap_record*, recording);
205
+ static void inc_tracked_objects_or_fail(heap_record *heap_record);
206
+ static void commit_recording(heap_recorder *, heap_record *, object_record *active_recording);
211
207
  static VALUE end_heap_allocation_recording(VALUE end_heap_allocation_args);
212
208
  static void heap_recorder_update(heap_recorder *heap_recorder, bool full_update);
213
209
  static inline double ewma_stat(double previous, double current);
210
+ static void unintern_or_raise(heap_recorder *, ddog_prof_ManagedStringId);
211
+ static void unintern_all_or_raise(heap_recorder *recorder, ddog_prof_Slice_ManagedStringId ids);
212
+ static VALUE get_ruby_string_or_raise(heap_recorder*, ddog_prof_ManagedStringId);
213
+ static long obj_id_or_fail(VALUE obj);
214
214
 
215
215
  // ==========================
216
216
  // Heap Recorder External API
@@ -221,16 +221,20 @@ static inline double ewma_stat(double previous, double current);
221
221
  // happens under the GVL.
222
222
  //
223
223
  // ==========================
224
- heap_recorder* heap_recorder_new(void) {
224
+ heap_recorder* heap_recorder_new(ddog_prof_ManagedStringStorage string_storage) {
225
225
  heap_recorder *recorder = ruby_xcalloc(1, sizeof(heap_recorder));
226
226
 
227
- recorder->heap_records = st_init_table(&st_hash_type_heap_record_key);
227
+ recorder->heap_records = st_init_table(&st_hash_type_heap_record);
228
228
  recorder->object_records = st_init_numtable();
229
229
  recorder->object_records_snapshot = NULL;
230
- recorder->reusable_locations = ruby_xcalloc(MAX_FRAMES_LIMIT, sizeof(ddog_prof_Location));
231
- recorder->active_recording = (recording) {0};
230
+ recorder->reusable_locations = ruby_xcalloc(REUSABLE_LOCATIONS_SIZE, sizeof(ddog_prof_Location));
231
+ recorder->reusable_ids = ruby_xcalloc(REUSABLE_FRAME_DETAILS_SIZE, sizeof(ddog_prof_ManagedStringId));
232
+ recorder->reusable_char_slices = ruby_xcalloc(REUSABLE_FRAME_DETAILS_SIZE, sizeof(ddog_CharSlice));
233
+ recorder->active_recording = NULL;
232
234
  recorder->size_enabled = true;
233
235
  recorder->sample_rate = 1; // By default do no sampling on top of what allocation profiling already does
236
+ recorder->string_storage = string_storage;
237
+ recorder->active_deferred_object = Qnil;
234
238
 
235
239
  return recorder;
236
240
  }
@@ -246,20 +250,29 @@ void heap_recorder_free(heap_recorder *heap_recorder) {
246
250
  heap_recorder_finish_iteration(heap_recorder);
247
251
  }
248
252
 
253
+ // NOTE: We don't unintern the strings referenced by the records we're about to free, thus we use
254
+ // `..._no_unintern` and `should_unintern: false`. This is intentional: `heap_recorder_free` is only ever called as part of
255
+ // tearing down the entire stack recorder, and the caller drops the whole managed string storage right after
256
+ // (see `stack_recorder_typed_data_free`), so there's no need to spend effort updating the managed string table.
257
+ // Crucially, this also keeps us from crashing: this code runs from the stack recorder's GC free callback, and
258
+ // because uninterning can fail, we can't raise exceptions in the middle of a dfree.
259
+
249
260
  // Clean-up all object records
250
- st_foreach(heap_recorder->object_records, st_object_record_entry_free, 0);
261
+ st_foreach(heap_recorder->object_records, st_object_record_entry_free_no_unintern, (st_data_t) heap_recorder);
251
262
  st_free_table(heap_recorder->object_records);
252
263
 
253
264
  // Clean-up all heap records (this includes those only referred to by queued_samples)
254
- st_foreach(heap_recorder->heap_records, st_heap_record_entry_free, 0);
265
+ st_foreach(heap_recorder->heap_records, st_heap_record_entry_free_no_unintern, (st_data_t) heap_recorder);
255
266
  st_free_table(heap_recorder->heap_records);
256
267
 
257
- if (heap_recorder->active_recording.object_record != NULL && heap_recorder->active_recording.object_record != &SKIPPED_RECORD) {
268
+ if (heap_recorder->active_recording != NULL && heap_recorder->active_recording != &SKIPPED_RECORD) {
258
269
  // If there's a partial object record, clean it up as well
259
- object_record_free(heap_recorder->active_recording.object_record);
270
+ object_record_free(heap_recorder, heap_recorder->active_recording, false);
260
271
  }
261
272
 
262
273
  ruby_xfree(heap_recorder->reusable_locations);
274
+ ruby_xfree(heap_recorder->reusable_ids);
275
+ ruby_xfree(heap_recorder->reusable_char_slices);
263
276
 
264
277
  ruby_xfree(heap_recorder);
265
278
  }
@@ -278,7 +291,7 @@ void heap_recorder_set_sample_rate(heap_recorder *heap_recorder, int sample_rate
278
291
  }
279
292
 
280
293
  if (sample_rate <= 0) {
281
- rb_raise(rb_eArgError, "Heap sample rate must be a positive integer value but was %d", sample_rate);
294
+ raise_error(rb_eArgError, "Heap sample rate must be a positive integer value but was %d", sample_rate);
282
295
  }
283
296
 
284
297
  heap_recorder->sample_rate = sample_rate;
@@ -300,8 +313,8 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder) {
300
313
  // simply be noticed on next heap_recorder_prepare_iteration.
301
314
  //
302
315
  // There is one small caveat though: fork only preserves one thread and in a Ruby app, that
303
- // will be the thread holding on to the GVL. Since we support iteration on the heap recorder
304
- // outside of the GVL, any state specific to that interaction may be incosistent after fork
316
+ // will be the thread holding the GVL. Since we support iteration on the heap recorder
317
+ // outside of the GVL, any state specific to that interaction may be inconsistent after fork
305
318
  // (e.g. an acquired lock for thread safety). Iteration operates on object_records_snapshot
306
319
  // though and that one will be updated on next heap_recorder_prepare_iteration so we really
307
320
  // only need to finish any iteration that might have been left unfinished.
@@ -313,101 +326,220 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder) {
313
326
  heap_recorder->stats_lifetime = (struct stats_lifetime) {0};
314
327
  }
315
328
 
316
- void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice *alloc_class) {
329
+ bool start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice alloc_class) {
317
330
  if (heap_recorder == NULL) {
318
- return;
331
+ return false;
319
332
  }
320
333
 
321
- if (heap_recorder->active_recording.object_record != NULL) {
322
- rb_raise(rb_eRuntimeError, "Detected consecutive heap allocation recording starts without end.");
334
+ if (heap_recorder->active_recording != NULL) {
335
+ raise_error(rb_eRuntimeError, "Detected consecutive heap allocation recording starts without end.");
323
336
  }
324
337
 
325
- if (heap_recorder->num_recordings_skipped + 1 < heap_recorder->sample_rate) {
326
- heap_recorder->active_recording.object_record = &SKIPPED_RECORD;
327
- heap_recorder->num_recordings_skipped++;
328
- return;
338
+ if (++heap_recorder->num_recordings_skipped < heap_recorder->sample_rate ||
339
+ #ifdef NO_IMEMO_OBJECT_ID
340
+ // On Ruby 4, we can't ask the object_id from IMEMOs (https://github.com/ruby/ruby/pull/13347)
341
+ RB_BUILTIN_TYPE(new_obj) == RUBY_T_IMEMO
342
+ #else
343
+ false
344
+ #endif
345
+ // If we got really unlucky and an allocation showed up during an update (because it triggered an allocation
346
+ // directly OR because the GVL got released in the middle of an update), let's skip this sample as well.
347
+ // See notes on `heap_recorder_update` for details.
348
+ || heap_recorder->updating
349
+ ) {
350
+ heap_recorder->active_recording = &SKIPPED_RECORD;
351
+ return false;
329
352
  }
330
353
 
331
- heap_recorder->num_recordings_skipped = 0;
354
+ bool needs_after_allocation = false;
332
355
 
333
- VALUE ruby_obj_id = rb_obj_id(new_obj);
334
- if (!FIXNUM_P(ruby_obj_id)) {
335
- rb_raise(rb_eRuntimeError, "Detected a bignum object id. These are not supported by heap profiling.");
356
+ #ifdef USE_DEFERRED_HEAP_ALLOCATION_RECORDING
357
+ // Skip if we've hit the pending recordings limit or if there's already a deferred object being recorded
358
+ if (heap_recorder->pending_recordings_count >= MAX_PENDING_RECORDINGS) {
359
+ heap_recorder->stats_lifetime.deferred_recordings_skipped_buffer_full++;
360
+ heap_recorder->active_recording = &SKIPPED_RECORD;
361
+ return true; // If the buffer is full, we keep asking for a callback (see `needs_after_allocation` below)
362
+ } else {
363
+ // The intuition here is: We start by asking for an `after_allocation` callback when the buffer is about to go
364
+ // from empty -> non-empty, because this is going to be mapped onto a postponed job, so after it gets queued once
365
+ // it doesn't seem worth it to keep spamming requests.
366
+ //
367
+ // Yet, if for some reason the postponed job doesn't flush the pending list (or if e.g. it ran with `during_sample == true` and thus
368
+ // was skipped) we need to have some mechanism to recover -- and so if the buffer starts accumulating too much we
369
+ // start always requesting the callback to happen so that we eventually flush the buffer.
370
+ needs_after_allocation =
371
+ heap_recorder->pending_recordings_count == 0 || heap_recorder->pending_recordings_count >= (MAX_PENDING_RECORDINGS / 2);
336
372
  }
373
+ #endif
374
+
375
+ heap_recorder->num_recordings_skipped = 0;
337
376
 
338
- heap_recorder->active_recording = (recording) {
339
- .object_record = object_record_new(FIX2LONG(ruby_obj_id), NULL, (live_object_data) {
340
- .weight = weight * heap_recorder->sample_rate,
341
- .class = alloc_class != NULL ? string_from_char_slice(*alloc_class) : NULL,
342
- .alloc_gen = rb_gc_count(),
343
- }),
377
+ live_object_data object_data = (live_object_data) {
378
+ .weight = weight * heap_recorder->sample_rate,
379
+ .class = intern_or_raise(heap_recorder->string_storage, alloc_class),
380
+ .alloc_gen = rb_gc_count(),
344
381
  };
382
+
383
+ #ifdef USE_DEFERRED_HEAP_ALLOCATION_RECORDING
384
+ // On Ruby 4+, we can't call rb_obj_id during on_newobj_event as it mutates the object.
385
+ // Instead, we store the VALUE reference and will get the object_id later via a postponed job.
386
+ // active_deferred_object != Qnil indicates we're in deferred mode.
387
+ heap_recorder->active_deferred_object = new_obj;
388
+ heap_recorder->active_deferred_object_data = object_data;
389
+ #else
390
+ heap_recorder->active_recording = object_record_new(obj_id_or_fail(new_obj), NULL, object_data);
391
+ #endif
392
+
393
+ return needs_after_allocation;
345
394
  }
346
395
 
347
396
  // end_heap_allocation_recording_with_rb_protect gets called while the stack_recorder is holding one of the profile
348
397
  // locks. To enable us to correctly unlock the profile on exception, we wrap the call to end_heap_allocation_recording
349
398
  // with an rb_protect.
350
399
  __attribute__((warn_unused_result))
351
- int end_heap_allocation_recording_with_rb_protect(struct heap_recorder *heap_recorder, ddog_prof_Slice_Location locations) {
400
+ int end_heap_allocation_recording_with_rb_protect(heap_recorder *heap_recorder, ddog_prof_Slice_Location locations) {
401
+ if (heap_recorder == NULL) {
402
+ return 0;
403
+ }
404
+ if (heap_recorder->active_recording == &SKIPPED_RECORD) {
405
+ // Short circuit, in this case there's nothing to be done
406
+ heap_recorder->active_recording = NULL;
407
+ return 0;
408
+ }
409
+
352
410
  int exception_state;
353
- struct end_heap_allocation_args end_heap_allocation_args = {
411
+ end_heap_allocation_args args = {
354
412
  .heap_recorder = heap_recorder,
355
413
  .locations = locations,
356
414
  };
357
- rb_protect(end_heap_allocation_recording, (VALUE) &end_heap_allocation_args, &exception_state);
415
+ rb_protect(end_heap_allocation_recording, (VALUE) &args, &exception_state);
358
416
  return exception_state;
359
417
  }
360
418
 
361
- static VALUE end_heap_allocation_recording(VALUE end_heap_allocation_args) {
362
- struct end_heap_allocation_args *args = (struct end_heap_allocation_args *) end_heap_allocation_args;
419
+ static VALUE end_heap_allocation_recording(VALUE protect_args) {
420
+ end_heap_allocation_args *args = (end_heap_allocation_args *) protect_args;
363
421
 
364
- struct heap_recorder *heap_recorder = args->heap_recorder;
422
+ heap_recorder *heap_recorder = args->heap_recorder;
365
423
  ddog_prof_Slice_Location locations = args->locations;
366
424
 
425
+ #ifdef USE_DEFERRED_HEAP_ALLOCATION_RECORDING
426
+ if (heap_recorder->active_deferred_object == Qnil) {
427
+ // Recording ended without having been started?
428
+ raise_error(rb_eRuntimeError, "Ended a heap recording that was not started");
429
+ }
430
+ #else
431
+ object_record *active_recording = heap_recorder->active_recording;
432
+
433
+ if (active_recording == NULL) {
434
+ // Recording ended without having been started?
435
+ raise_error(rb_eRuntimeError, "Ended a heap recording that was not started");
436
+ }
437
+ // From now on, mark the global active recording as invalid so we can short-circuit at any point
438
+ // and not end up with a still active recording. the local active_recording still holds the
439
+ // data required for committing though.
440
+ heap_recorder->active_recording = NULL;
441
+
442
+ if (active_recording == &SKIPPED_RECORD) {
443
+ raise_error(
444
+ rb_eRuntimeError,
445
+ "BUG: end_heap_allocation_recording should never observe SKIPPED_RECORDING because " \
446
+ "end_heap_allocation_recording_with_rb_protect is supposed to test for it directly"
447
+ );
448
+ }
449
+ #endif
450
+
451
+ heap_record *heap_record = get_or_create_heap_record(heap_recorder, locations);
452
+ inc_tracked_objects_or_fail(heap_record);
453
+
454
+ #ifdef USE_DEFERRED_HEAP_ALLOCATION_RECORDING
455
+ // Commit is delayed, so we need to record all we'll need for it
456
+ pending_recording *pending = &heap_recorder->pending_recordings[heap_recorder->pending_recordings_count++];
457
+ pending->object_ref = heap_recorder->active_deferred_object;
458
+ pending->heap_record = heap_record;
459
+ pending->object_data = heap_recorder->active_deferred_object_data;
460
+
461
+ heap_recorder->active_deferred_object = Qnil;
462
+ heap_recorder->active_deferred_object_data = (live_object_data) {0};
463
+ #else
464
+ // And then commit the new allocation
465
+ commit_recording(heap_recorder, heap_record, active_recording);
466
+ #endif
467
+
468
+ return Qnil;
469
+ }
470
+
471
+ void heap_recorder_update_young_objects(heap_recorder *heap_recorder) {
367
472
  if (heap_recorder == NULL) {
368
- return Qnil;
473
+ return;
369
474
  }
370
475
 
371
- recording active_recording = heap_recorder->active_recording;
476
+ heap_recorder_update(heap_recorder, /* full_update: */ false);
477
+ }
372
478
 
373
- if (active_recording.object_record == NULL) {
374
- // Recording ended without having been started?
375
- rb_raise(rb_eRuntimeError, "Ended a heap recording that was not started");
479
+ void heap_recorder_finalize_pending_recordings(heap_recorder *heap_recorder) {
480
+ if (heap_recorder == NULL) {
481
+ return; // Nothing to do
376
482
  }
377
- // From now on, mark the global active recording as invalid so we can short-circuit at any point
378
- // and not end up with a still active recording. the local active_recording still holds the
379
- // data required for committing though.
380
- heap_recorder->active_recording = (recording) {0};
381
483
 
382
- if (active_recording.object_record == &SKIPPED_RECORD) { // special marker when we decided to skip due to sampling
383
- return Qnil;
484
+ uint count = heap_recorder->pending_recordings_count;
485
+ if (count == 0) {
486
+ return; // Nothing to do
384
487
  }
385
488
 
386
- heap_record *heap_record = get_or_create_heap_record(heap_recorder, locations);
489
+ heap_recorder->stats_lifetime.deferred_recordings_finalized += count;
387
490
 
388
- // And then commit the new allocation.
389
- commit_recording(heap_recorder, heap_record, active_recording);
491
+ for (uint i = 0; i < count; i++) {
492
+ pending_recording *pending = &heap_recorder->pending_recordings[i];
390
493
 
391
- return Qnil;
494
+ // This is the step we couldn't do during the original sample call -- we're now expected to be called in a context
495
+ // where it's finally safe to call this
496
+ long obj_id = obj_id_or_fail(pending->object_ref);
497
+
498
+ // Create the object record now that we have the object_id
499
+ object_record *record = object_record_new(obj_id, pending->heap_record, pending->object_data);
500
+
501
+ commit_recording(heap_recorder, pending->heap_record, record);
502
+ }
503
+
504
+ heap_recorder->pending_recordings_count = 0;
392
505
  }
393
506
 
394
- void heap_recorder_update_young_objects(heap_recorder *heap_recorder) {
507
+ // Mark pending recordings to prevent GC from collecting the objects
508
+ // while they're waiting to be finalized
509
+ void heap_recorder_mark_pending_recordings(heap_recorder *heap_recorder) {
395
510
  if (heap_recorder == NULL) {
396
511
  return;
397
512
  }
398
513
 
399
- heap_recorder_update(heap_recorder, /* full_update: */ false);
514
+ for (uint i = 0; i < heap_recorder->pending_recordings_count; i++) {
515
+ rb_gc_mark(heap_recorder->pending_recordings[i].object_ref);
516
+ }
517
+
518
+ rb_gc_mark(heap_recorder->active_deferred_object);
400
519
  }
401
520
 
521
+ // NOTE: This function needs and assumes it gets called with the GVL being held.
522
+ // But importantly **some of the operations inside `st_object_record_update` may cause a thread switch**,
523
+ // so we can't assume a single update happens in a single "atomic" step -- other threads may get some running time
524
+ // in the meanwhile.
402
525
  static void heap_recorder_update(heap_recorder *heap_recorder, bool full_update) {
403
526
  if (heap_recorder->updating) {
404
- if (full_update) rb_raise(rb_eRuntimeError, "BUG: full_update should not be triggered during another update");
405
-
406
- // If we try to update while another update is still running, short-circuit.
407
- // NOTE: This runs while holding the GVL. But since updates may be triggered from GC activity, there's still
408
- // a chance for updates to be attempted concurrently if scheduling gods so determine.
409
- heap_recorder->stats_lifetime.updates_skipped_concurrent++;
410
- return;
527
+ if (full_update) {
528
+ // There's another thread that's already doing an update :(
529
+ //
530
+ // Because there's a lock on the `StackRecorder` (see @no_concurrent_serialize_mutex) then it's not possible that
531
+ // the other update is a full update.
532
+ // Thus we expect is happening is that the GVL got released by the other thread in the middle of a non-full update
533
+ // and the scheduler thread decided now was a great time to serialize the profile.
534
+ //
535
+ // So, let's yield the time on the current thread until Ruby goes back to the other thread doing the update and
536
+ // it finishes cleanly.
537
+ while (heap_recorder->updating) { rb_thread_schedule(); }
538
+ } else {
539
+ // Non-full updates are optional, so let's walk away
540
+ heap_recorder->stats_lifetime.updates_skipped_concurrent++;
541
+ return;
542
+ }
411
543
  }
412
544
 
413
545
  if (heap_recorder->object_records_snapshot != NULL) {
@@ -474,14 +606,14 @@ void heap_recorder_prepare_iteration(heap_recorder *heap_recorder) {
474
606
 
475
607
  if (heap_recorder->object_records_snapshot != NULL) {
476
608
  // we could trivially handle this but we raise to highlight and catch unexpected usages.
477
- rb_raise(rb_eRuntimeError, "New heap recorder iteration prepared without the previous one having been finished.");
609
+ raise_error(rb_eRuntimeError, "New heap recorder iteration prepared without the previous one having been finished.");
478
610
  }
479
611
 
480
612
  heap_recorder_update(heap_recorder, /* full_update: */ true);
481
613
 
482
614
  heap_recorder->object_records_snapshot = st_copy(heap_recorder->object_records);
483
615
  if (heap_recorder->object_records_snapshot == NULL) {
484
- rb_raise(rb_eRuntimeError, "Failed to create heap snapshot.");
616
+ raise_error(rb_eRuntimeError, "Failed to create heap snapshot.");
485
617
  }
486
618
  }
487
619
 
@@ -492,7 +624,7 @@ void heap_recorder_finish_iteration(heap_recorder *heap_recorder) {
492
624
 
493
625
  if (heap_recorder->object_records_snapshot == NULL) {
494
626
  // we could trivially handle this but we raise to highlight and catch unexpected usages.
495
- rb_raise(rb_eRuntimeError, "Heap recorder iteration finished without having been prepared.");
627
+ raise_error(rb_eRuntimeError, "Heap recorder iteration finished without having been prepared.");
496
628
  }
497
629
 
498
630
  st_free_table(heap_recorder->object_records_snapshot);
@@ -534,20 +666,21 @@ bool heap_recorder_for_each_live_object(
534
666
 
535
667
  VALUE heap_recorder_state_snapshot(heap_recorder *heap_recorder) {
536
668
  VALUE arguments[] = {
537
- ID2SYM(rb_intern("num_object_records")), /* => */ LONG2NUM(heap_recorder->object_records->num_entries),
538
- ID2SYM(rb_intern("num_heap_records")), /* => */ LONG2NUM(heap_recorder->heap_records->num_entries),
669
+ ID2SYM(rb_intern("num_object_records")), /* => */ ULONG2NUM(heap_recorder->object_records->num_entries),
670
+ ID2SYM(rb_intern("num_heap_records")), /* => */ ULONG2NUM(heap_recorder->heap_records->num_entries),
671
+ ID2SYM(rb_intern("pending_recordings_count")), /* => */ ULONG2NUM(heap_recorder->pending_recordings_count),
539
672
 
540
673
  // Stats as of last update
541
- ID2SYM(rb_intern("last_update_objects_alive")), /* => */ LONG2NUM(heap_recorder->stats_last_update.objects_alive),
542
- ID2SYM(rb_intern("last_update_objects_dead")), /* => */ LONG2NUM(heap_recorder->stats_last_update.objects_dead),
543
- ID2SYM(rb_intern("last_update_objects_skipped")), /* => */ LONG2NUM(heap_recorder->stats_last_update.objects_skipped),
544
- ID2SYM(rb_intern("last_update_objects_frozen")), /* => */ LONG2NUM(heap_recorder->stats_last_update.objects_frozen),
674
+ ID2SYM(rb_intern("last_update_objects_alive")), /* => */ ULONG2NUM(heap_recorder->stats_last_update.objects_alive),
675
+ ID2SYM(rb_intern("last_update_objects_dead")), /* => */ ULONG2NUM(heap_recorder->stats_last_update.objects_dead),
676
+ ID2SYM(rb_intern("last_update_objects_skipped")), /* => */ ULONG2NUM(heap_recorder->stats_last_update.objects_skipped),
677
+ ID2SYM(rb_intern("last_update_objects_frozen")), /* => */ ULONG2NUM(heap_recorder->stats_last_update.objects_frozen),
545
678
 
546
679
  // Lifetime stats
547
- ID2SYM(rb_intern("lifetime_updates_successful")), /* => */ LONG2NUM(heap_recorder->stats_lifetime.updates_successful),
548
- ID2SYM(rb_intern("lifetime_updates_skipped_concurrent")), /* => */ LONG2NUM(heap_recorder->stats_lifetime.updates_skipped_concurrent),
549
- ID2SYM(rb_intern("lifetime_updates_skipped_gcgen")), /* => */ LONG2NUM(heap_recorder->stats_lifetime.updates_skipped_gcgen),
550
- ID2SYM(rb_intern("lifetime_updates_skipped_time")), /* => */ LONG2NUM(heap_recorder->stats_lifetime.updates_skipped_time),
680
+ ID2SYM(rb_intern("lifetime_updates_successful")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.updates_successful),
681
+ ID2SYM(rb_intern("lifetime_updates_skipped_concurrent")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.updates_skipped_concurrent),
682
+ ID2SYM(rb_intern("lifetime_updates_skipped_gcgen")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.updates_skipped_gcgen),
683
+ ID2SYM(rb_intern("lifetime_updates_skipped_time")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.updates_skipped_time),
551
684
  ID2SYM(rb_intern("lifetime_ewma_young_objects_alive")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_young_objects_alive),
552
685
  ID2SYM(rb_intern("lifetime_ewma_young_objects_dead")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_young_objects_dead),
553
686
  // Note: Here "young" refers to the young update; objects skipped includes non-young objects
@@ -555,61 +688,54 @@ VALUE heap_recorder_state_snapshot(heap_recorder *heap_recorder) {
555
688
  ID2SYM(rb_intern("lifetime_ewma_objects_alive")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_objects_alive),
556
689
  ID2SYM(rb_intern("lifetime_ewma_objects_dead")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_objects_dead),
557
690
  ID2SYM(rb_intern("lifetime_ewma_objects_skipped")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_objects_skipped),
691
+
692
+ ID2SYM(rb_intern("lifetime_deferred_recordings_skipped_buffer_full")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.deferred_recordings_skipped_buffer_full),
693
+ ID2SYM(rb_intern("lifetime_deferred_recordings_finalized")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.deferred_recordings_finalized),
558
694
  };
559
695
  VALUE hash = rb_hash_new();
560
696
  for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(hash, arguments[i], arguments[i+1]);
697
+
561
698
  return hash;
562
699
  }
563
700
 
564
- void heap_recorder_testonly_assert_hash_matches(ddog_prof_Slice_Location locations) {
565
- heap_stack *stack = heap_stack_new(locations);
566
- heap_record_key stack_based_key = (heap_record_key) {
567
- .type = HEAP_STACK,
568
- .heap_stack = stack,
569
- };
570
- heap_record_key location_based_key = (heap_record_key) {
571
- .type = LOCATION_SLICE,
572
- .location_slice = &locations,
573
- };
574
-
575
- st_index_t stack_hash = heap_record_key_hash_st((st_data_t) &stack_based_key);
576
- st_index_t location_hash = heap_record_key_hash_st((st_data_t) &location_based_key);
577
-
578
- heap_stack_free(stack);
579
-
580
- if (stack_hash != location_hash) {
581
- rb_raise(rb_eRuntimeError, "Heap record key hashes built from the same locations differ. stack_based_hash=%"PRI_VALUE_PREFIX"u location_based_hash=%"PRI_VALUE_PREFIX"u", stack_hash, location_hash);
582
- }
583
- }
701
+ typedef struct {
702
+ heap_recorder *recorder;
703
+ VALUE debug_ary;
704
+ } debug_context;
584
705
 
585
706
  VALUE heap_recorder_testonly_debug(heap_recorder *heap_recorder) {
586
707
  if (heap_recorder == NULL) {
587
- rb_raise(rb_eArgError, "heap_recorder is NULL");
708
+ raise_error(rb_eArgError, "heap_recorder is NULL");
588
709
  }
589
710
 
590
- VALUE debug_str = rb_str_new2("object records:\n");
591
- st_foreach(heap_recorder->object_records, st_object_records_debug, (st_data_t) debug_str);
592
-
593
- rb_str_catf(debug_str, "state snapshot: %"PRIsVALUE"\n------\n", heap_recorder_state_snapshot(heap_recorder));
711
+ VALUE debug_ary = rb_ary_new();
712
+ debug_context context = (debug_context) {.recorder = heap_recorder, .debug_ary = debug_ary};
713
+ st_foreach(heap_recorder->object_records, st_object_records_debug, (st_data_t) &context);
594
714
 
595
- return debug_str;
715
+ return rb_ary_new_from_args(2,
716
+ rb_ary_new_from_args(2, ID2SYM(rb_intern("records")), debug_ary),
717
+ rb_ary_new_from_args(2, ID2SYM(rb_intern("state")), heap_recorder_state_snapshot(heap_recorder))
718
+ );
596
719
  }
597
720
 
598
721
  // ==========================
599
722
  // Heap Recorder Internal API
600
723
  // ==========================
601
- static int st_heap_record_entry_free(st_data_t key, st_data_t value, DDTRACE_UNUSED st_data_t extra_arg) {
602
- heap_record_key *record_key = (heap_record_key*) key;
603
- heap_record_key_free(record_key);
604
- heap_record_free((heap_record *) value);
724
+ // NOTE: Only expected to be used from heap_recorder_free, which will separately destroy the string table
725
+ static int st_heap_record_entry_free_no_unintern(st_data_t key, DDTRACE_UNUSED st_data_t value, st_data_t extra_arg) {
726
+ heap_recorder *recorder = (heap_recorder *) extra_arg;
727
+ heap_record_free(recorder, (heap_record *) key, false);
605
728
  return ST_DELETE;
606
729
  }
607
730
 
608
- static int st_object_record_entry_free(DDTRACE_UNUSED st_data_t key, st_data_t value, DDTRACE_UNUSED st_data_t extra_arg) {
609
- object_record_free((object_record *) value);
731
+ // NOTE: Only expected to be used from heap_recorder_free, which will separately destroy the string table
732
+ static int st_object_record_entry_free_no_unintern(DDTRACE_UNUSED st_data_t key, st_data_t value, st_data_t extra_arg) {
733
+ heap_recorder *recorder = (heap_recorder *) extra_arg;
734
+ object_record_free(recorder, (object_record *) value, false);
610
735
  return ST_DELETE;
611
736
  }
612
737
 
738
+ // NOTE: Some operations inside this function can cause the GVL to be released! Plan accordingly.
613
739
  static int st_object_record_update(st_data_t key, st_data_t value, st_data_t extra_arg) {
614
740
  long obj_id = (long) key;
615
741
  object_record *record = (object_record*) value;
@@ -635,7 +761,7 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
635
761
  return ST_CONTINUE;
636
762
  }
637
763
 
638
- if (!ruby_ref_from_id(LONG2NUM(obj_id), &ref)) {
764
+ if (!ruby_ref_from_id(LONG2NUM(obj_id), &ref)) { // Note: This function call can cause the GVL to be released
639
765
  // Id no longer associated with a valid ref. Need to delete this object record!
640
766
  on_committed_object_record_cleanup(recorder, record);
641
767
  recorder->stats_last_update.objects_dead++;
@@ -651,7 +777,8 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
651
777
  ) {
652
778
  // if we were asked to update sizes and this object was not already seen as being frozen,
653
779
  // update size again.
654
- record->object_data.size = ruby_obj_memsize_of(ref);
780
+ record->object_data.size = ruby_obj_memsize_of(ref); // Note: This function call can cause the GVL to be released... maybe?
781
+ // (With T_DATA for instance, since it can be a custom method supplied by extensions)
655
782
  // Check if it's now frozen so we skip a size update next time
656
783
  record->object_data.is_frozen = RB_OBJ_FROZEN(ref);
657
784
  }
@@ -671,7 +798,7 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
671
798
  // WARN: This can get called outside the GVL. NO HEAP ALLOCATIONS OR EXCEPTIONS ARE ALLOWED.
672
799
  static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t value, st_data_t extra) {
673
800
  object_record *record = (object_record*) value;
674
- const heap_stack *stack = record->heap_record->stack;
801
+ const heap_record *stack = record->heap_record;
675
802
  iteration_context *context = (iteration_context*) extra;
676
803
 
677
804
  const heap_recorder *recorder = context->heap_recorder;
@@ -685,10 +812,12 @@ static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t val
685
812
  for (uint16_t i = 0; i < stack->frames_len; i++) {
686
813
  const heap_frame *frame = &stack->frames[i];
687
814
  locations[i] = (ddog_prof_Location) {
688
- .mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C("")},
815
+ .mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C(""), .build_id_id = {}},
689
816
  .function = {
690
- .name = {.ptr = frame->name, .len = strlen(frame->name)},
691
- .filename = {.ptr = frame->filename, .len = strlen(frame->filename)},
817
+ .name = DDOG_CHARSLICE_C(""),
818
+ .name_id = frame->name,
819
+ .filename = DDOG_CHARSLICE_C(""),
820
+ .filename_id = frame->filename,
692
821
  },
693
822
  .line = frame->line,
694
823
  };
@@ -698,6 +827,7 @@ static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t val
698
827
  iteration_data.object_data = record->object_data;
699
828
  iteration_data.locations = (ddog_prof_Slice_Location) {.ptr = locations, .len = stack->frames_len};
700
829
 
830
+ // This is expected to be StackRecorder's add_heap_sample_to_active_profile_without_gvl
701
831
  if (!context->for_each_callback(iteration_data, context->for_each_callback_extra_arg)) {
702
832
  return ST_STOP;
703
833
  }
@@ -706,107 +836,71 @@ static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t val
706
836
  }
707
837
 
708
838
  static int st_object_records_debug(DDTRACE_UNUSED st_data_t key, st_data_t value, st_data_t extra) {
709
- VALUE debug_str = (VALUE) extra;
839
+ debug_context *context = (debug_context*) extra;
710
840
 
711
841
  object_record *record = (object_record*) value;
712
842
 
713
- rb_str_catf(debug_str, "%"PRIsVALUE"\n", object_record_inspect(record));
843
+ rb_ary_push(context->debug_ary, object_record_inspect(context->recorder, record));
714
844
 
715
845
  return ST_CONTINUE;
716
846
  }
717
847
 
718
- // Struct holding data required for an update operation on heap_records
719
- typedef struct {
720
- // [in] The recording containing the new object record we want to add.
721
- // NOTE: Transfer of ownership of the contained object record is assumed, do not re-use it after call to ::update_object_record_entry
722
- recording recording;
723
-
724
- // [in] The heap recorder where the update is happening.
725
- heap_recorder *heap_recorder;
726
- } object_record_update_data;
727
-
728
- static int update_object_record_entry(DDTRACE_UNUSED st_data_t *key, st_data_t *value, st_data_t data, int existing) {
729
- object_record_update_data *update_data = (object_record_update_data*) data;
730
- recording recording = update_data->recording;
731
- object_record *new_object_record = recording.object_record;
732
- if (existing) {
733
- object_record *existing_record = (object_record*) (*value);
734
-
735
- // This is not supposed to happen, raising...
736
- VALUE existing_inspect = object_record_inspect(existing_record);
737
- VALUE new_inspect = object_record_inspect(new_object_record);
738
- rb_raise(rb_eRuntimeError, "Object ids are supposed to be unique. We got 2 allocation recordings with "
739
- "the same id. previous=%"PRIsVALUE" new=%"PRIsVALUE, existing_inspect, new_inspect);
848
+ static int update_object_record_entry(DDTRACE_UNUSED st_data_t *key, st_data_t *value, st_data_t new_object_record, int existing) {
849
+ if (!existing) {
850
+ (*value) = (st_data_t) new_object_record; // Expected to be a `object_record *`
851
+ } else {
852
+ // If key already existed, we don't touch the existing value, so it can be used for diagnostics
740
853
  }
741
- // Always carry on with the update, we want the new record to be there at the end
742
- (*value) = (st_data_t) new_object_record;
743
854
  return ST_CONTINUE;
744
855
  }
745
856
 
746
- static void commit_recording(heap_recorder *heap_recorder, heap_record *heap_record, recording recording) {
747
- // Link the object record with the corresponding heap record. This was the last remaining thing we
748
- // needed to fully build the object_record.
749
- recording.object_record->heap_record = heap_record;
857
+ static void inc_tracked_objects_or_fail(heap_record *heap_record) {
750
858
  if (heap_record->num_tracked_objects == UINT32_MAX) {
751
- rb_raise(rb_eRuntimeError, "Reached maximum number of tracked objects for heap record");
859
+ raise_error(rb_eRuntimeError, "Reached maximum number of tracked objects for heap record");
752
860
  }
753
861
  heap_record->num_tracked_objects++;
754
-
755
- // Update object_records with the data for this new recording
756
- object_record_update_data update_data = (object_record_update_data) {
757
- .heap_recorder = heap_recorder,
758
- .recording = recording,
759
- };
760
- st_update(heap_recorder->object_records, recording.object_record->obj_id, update_object_record_entry, (st_data_t) &update_data);
761
862
  }
762
863
 
763
- // Struct holding data required for an update operation on heap_records
764
- typedef struct {
765
- // [in] The locations we did this update with
766
- ddog_prof_Slice_Location locations;
767
- // [out] Pointer that will be updated to the updated heap record to prevent having to do
768
- // another lookup to access the updated heap record.
769
- heap_record **record;
770
- } heap_record_update_data;
864
+ static void commit_recording(heap_recorder *heap_recorder, heap_record *heap_record, object_record *active_recording) {
865
+ // Link the object record with the corresponding heap record. This was the last remaining thing we
866
+ // needed to fully build the object_record.
867
+ active_recording->heap_record = heap_record;
868
+
869
+ int existing_error = st_update(heap_recorder->object_records, active_recording->obj_id, update_object_record_entry, (st_data_t) active_recording);
870
+ if (existing_error) {
871
+ object_record *existing_record = NULL;
872
+ st_lookup(heap_recorder->object_records, active_recording->obj_id, (st_data_t *) &existing_record);
873
+ if (existing_record == NULL) raise_error(rb_eRuntimeError, "Unexpected NULL when reading existing record");
874
+
875
+ VALUE existing_inspect = object_record_inspect(heap_recorder, existing_record);
876
+ VALUE new_inspect = object_record_inspect(heap_recorder, active_recording);
877
+ raise_error(rb_eRuntimeError, "Object ids are supposed to be unique. We got 2 allocation recordings with "
878
+ "the same id. previous={%"PRIsVALUE"} new={%"PRIsVALUE"}", existing_inspect, new_inspect);
879
+ }
880
+ }
771
881
 
772
- // This function assumes ownership of stack_data is passed on to it so it'll either transfer ownership or clean-up.
773
882
  static int update_heap_record_entry_with_new_allocation(st_data_t *key, st_data_t *value, st_data_t data, int existing) {
774
- heap_record_update_data *update_data = (heap_record_update_data*) data;
883
+ heap_record **new_or_existing_record = (heap_record **) data;
884
+ (*new_or_existing_record) = (heap_record *) (*key);
775
885
 
776
886
  if (!existing) {
777
- // there was no matching heap record so lets create a new one...
778
- // we need to initialize a heap_record_key with a new stack and use that for the key storage. We can't use the
779
- // locations-based key we used for the update call because we don't own its lifecycle. So we create a new
780
- // heap stack and will pass ownership of it to the heap_record.
781
- heap_stack *stack = heap_stack_new(update_data->locations);
782
- (*key) = (st_data_t) heap_record_key_new(stack);
783
- (*value) = (st_data_t) heap_record_new(stack);
887
+ (*value) = (st_data_t) true; // We're only using this hash as a set
784
888
  }
785
889
 
786
- heap_record *record = (heap_record*) (*value);
787
- (*update_data->record) = record;
788
-
789
890
  return ST_CONTINUE;
790
891
  }
791
892
 
792
893
  static heap_record* get_or_create_heap_record(heap_recorder *heap_recorder, ddog_prof_Slice_Location locations) {
793
- // For performance reasons we use a stack-allocated location-slice based key. This allows us
794
- // to do allocation-free lookups and reuse of a matching existing heap record.
795
- // NOTE: If we end up creating a new record, we'll create a heap-allocated key we own and use that for storage
796
- // instead of this one.
797
- heap_record_key lookup_key = (heap_record_key) {
798
- .type = LOCATION_SLICE,
799
- .location_slice = &locations,
800
- };
894
+ // See note on "heap_records" definition for why we keep this map.
895
+ heap_record *stack = heap_record_new(heap_recorder, locations);
801
896
 
802
- heap_record *heap_record = NULL;
803
- heap_record_update_data update_data = (heap_record_update_data) {
804
- .locations = locations,
805
- .record = &heap_record,
806
- };
807
- st_update(heap_recorder->heap_records, (st_data_t) &lookup_key, update_heap_record_entry_with_new_allocation, (st_data_t) &update_data);
897
+ heap_record *new_or_existing_record = NULL; // Will be set inside update_heap_record_entry_with_new_allocation
898
+ bool existing = st_update(heap_recorder->heap_records, (st_data_t) stack, update_heap_record_entry_with_new_allocation, (st_data_t) &new_or_existing_record);
899
+ if (existing) {
900
+ heap_record_free(heap_recorder, stack, true);
901
+ }
808
902
 
809
- return heap_record;
903
+ return new_or_existing_record;
810
904
  }
811
905
 
812
906
  static void cleanup_heap_record_if_unused(heap_recorder *heap_recorder, heap_record *heap_record) {
@@ -815,18 +909,10 @@ static void cleanup_heap_record_if_unused(heap_recorder *heap_recorder, heap_rec
815
909
  return;
816
910
  }
817
911
 
818
- heap_record_key heap_key = (heap_record_key) {
819
- .type = HEAP_STACK,
820
- .heap_stack = heap_record->stack,
821
- };
822
- // We need to access the deleted key to free it since we gave ownership of the keys to the hash.
823
- // st_delete will change this pointer to point to the removed key if one is found.
824
- heap_record_key *deleted_key = &heap_key;
825
- if (!st_delete(heap_recorder->heap_records, (st_data_t*) &deleted_key, NULL)) {
826
- rb_raise(rb_eRuntimeError, "Attempted to cleanup an untracked heap_record");
912
+ if (!st_delete(heap_recorder->heap_records, (st_data_t*) &heap_record, NULL)) {
913
+ raise_error(rb_eRuntimeError, "Attempted to cleanup an untracked heap_record");
827
914
  };
828
- heap_record_key_free(deleted_key);
829
- heap_record_free(heap_record);
915
+ heap_record_free(heap_recorder, heap_record, true);
830
916
  }
831
917
 
832
918
  static void on_committed_object_record_cleanup(heap_recorder *heap_recorder, object_record *record) {
@@ -834,68 +920,56 @@ static void on_committed_object_record_cleanup(heap_recorder *heap_recorder, obj
834
920
  // (See PROF-10656 Datadog-internal for details). Just in case, I've sprinkled a bunch of NULL tests in this function for now.
835
921
  // Once we figure out the issue we can get rid of them again.
836
922
 
837
- if (heap_recorder == NULL) rb_raise(rb_eRuntimeError, "heap_recorder was NULL in on_committed_object_record_cleanup");
838
- if (heap_recorder->heap_records == NULL) rb_raise(rb_eRuntimeError, "heap_recorder->heap_records was NULL in on_committed_object_record_cleanup");
839
- if (record == NULL) rb_raise(rb_eRuntimeError, "record was NULL in on_committed_object_record_cleanup");
923
+ if (heap_recorder == NULL) raise_error(rb_eRuntimeError, "heap_recorder was NULL in on_committed_object_record_cleanup");
924
+ if (heap_recorder->heap_records == NULL) raise_error(rb_eRuntimeError, "heap_recorder->heap_records was NULL in on_committed_object_record_cleanup");
925
+ if (record == NULL) raise_error(rb_eRuntimeError, "record was NULL in on_committed_object_record_cleanup");
840
926
 
841
927
  // Starting with the associated heap record. There will now be one less tracked object pointing to it
842
928
  heap_record *heap_record = record->heap_record;
843
929
 
844
- if (heap_record == NULL) rb_raise(rb_eRuntimeError, "heap_record was NULL in on_committed_object_record_cleanup");
845
- if (heap_record->stack == NULL) rb_raise(rb_eRuntimeError, "heap_record->stack was NULL in on_committed_object_record_cleanup");
930
+ if (heap_record == NULL) raise_error(rb_eRuntimeError, "heap_record was NULL in on_committed_object_record_cleanup");
846
931
 
847
932
  heap_record->num_tracked_objects--;
848
933
 
849
934
  // One less object using this heap record, it may have become unused...
850
935
  cleanup_heap_record_if_unused(heap_recorder, heap_record);
851
936
 
852
- object_record_free(record);
853
- }
854
-
855
- // ===============
856
- // Heap Record API
857
- // ===============
858
- heap_record* heap_record_new(heap_stack *stack) {
859
- heap_record *record = ruby_xcalloc(1, sizeof(heap_record));
860
- record->num_tracked_objects = 0;
861
- record->stack = stack;
862
- return record;
863
- }
864
-
865
- void heap_record_free(heap_record *record) {
866
- heap_stack_free(record->stack);
867
- ruby_xfree(record);
937
+ object_record_free(heap_recorder, record, true);
868
938
  }
869
939
 
870
-
871
940
  // =================
872
941
  // Object Record API
873
942
  // =================
874
943
  object_record* object_record_new(long obj_id, heap_record *heap_record, live_object_data object_data) {
875
- object_record *record = ruby_xcalloc(1, sizeof(object_record));
944
+ object_record *record = calloc(1, sizeof(object_record)); // See "note on calloc vs ruby_xcalloc use" above
876
945
  record->obj_id = obj_id;
877
946
  record->heap_record = heap_record;
878
947
  record->object_data = object_data;
879
948
  return record;
880
949
  }
881
950
 
882
- void object_record_free(object_record *record) {
883
- if (record->object_data.class != NULL) {
884
- ruby_xfree(record->object_data.class);
885
- }
886
- ruby_xfree(record);
951
+ void object_record_free(heap_recorder *recorder, object_record *record, bool should_unintern) {
952
+ // When tearing down the whole recorder state, we skip uninterning as it's not needed (the managed
953
+ // string table is going to be destroyed anyway) and if there's any failures we can't raise
954
+ // in the middle of a dfree callback.
955
+ if (should_unintern) unintern_or_raise(recorder, record->object_data.class);
956
+
957
+ free(record); // See "note on calloc vs ruby_xcalloc use" above
887
958
  }
888
959
 
889
- VALUE object_record_inspect(object_record *record) {
890
- heap_frame top_frame = record->heap_record->stack->frames[0];
960
+ VALUE object_record_inspect(heap_recorder *recorder, object_record *record) {
961
+ heap_frame top_frame = record->heap_record->frames[0];
962
+ VALUE filename = get_ruby_string_or_raise(recorder, top_frame.filename);
891
963
  live_object_data object_data = record->object_data;
892
- VALUE inspect = rb_sprintf("obj_id=%ld weight=%d size=%zu location=%s:%d alloc_gen=%zu gen_age=%zu frozen=%d ",
893
- record->obj_id, object_data.weight, object_data.size, top_frame.filename,
964
+
965
+ VALUE inspect = rb_sprintf("obj_id=%ld weight=%d size=%zu location=%"PRIsVALUE":%d alloc_gen=%zu gen_age=%zu frozen=%d ",
966
+ record->obj_id, object_data.weight, object_data.size, filename,
894
967
  (int) top_frame.line, object_data.alloc_gen, object_data.gen_age, object_data.is_frozen);
895
968
 
896
- const char *class = record->object_data.class;
897
- if (class != NULL) {
898
- rb_str_catf(inspect, "class=%s ", class);
969
+ if (record->object_data.class.value > 0) {
970
+ VALUE class = get_ruby_string_or_raise(recorder, record->object_data.class);
971
+
972
+ rb_str_catf(inspect, "class=%"PRIsVALUE" ", class);
899
973
  }
900
974
  VALUE ref;
901
975
 
@@ -915,223 +989,119 @@ VALUE object_record_inspect(object_record *record) {
915
989
  }
916
990
 
917
991
  // ==============
918
- // Heap Frame API
919
- // ==============
920
- int heap_frame_cmp(heap_frame *f1, heap_frame *f2) {
921
- int line_diff = (int) (f1->line - f2->line);
922
- if (line_diff != 0) {
923
- return line_diff;
924
- }
925
- int cmp = strcmp(f1->name, f2->name);
926
- if (cmp != 0) {
927
- return cmp;
928
- }
929
- return strcmp(f1->filename, f2->filename);
930
- }
931
-
932
- // TODO: Research potential performance improvements around hashing stuff here
933
- // once we have a benchmarking suite.
934
- // Example: Each call to st_hash is calling murmur_finish and we may want
935
- // to only finish once per structure, not per field?
936
- // Example: There may be a more efficient hashing for line that is not the
937
- // generic st_hash algorithm?
938
-
939
- // WARN: Must be kept in-sync with ::char_slice_hash
940
- st_index_t string_hash(char *str, st_index_t seed) {
941
- return st_hash(str, strlen(str), seed);
942
- }
943
-
944
- // WARN: Must be kept in-sync with ::string_hash
945
- st_index_t char_slice_hash(ddog_CharSlice char_slice, st_index_t seed) {
946
- return st_hash(char_slice.ptr, char_slice.len, seed);
947
- }
948
-
949
- // WARN: Must be kept in-sync with ::ddog_location_hash
950
- st_index_t heap_frame_hash(heap_frame *frame, st_index_t seed) {
951
- st_index_t hash = string_hash(frame->name, seed);
952
- hash = string_hash(frame->filename, hash);
953
- hash = st_hash(&frame->line, sizeof(frame->line), hash);
954
- return hash;
955
- }
956
-
957
- // WARN: Must be kept in-sync with ::heap_frame_hash
958
- st_index_t ddog_location_hash(ddog_prof_Location location, st_index_t seed) {
959
- st_index_t hash = char_slice_hash(location.function.name, seed);
960
- hash = char_slice_hash(location.function.filename, hash);
961
- // Convert ddog_prof line type to the same type we use for our heap_frames to
962
- // ensure we have compatible hashes
963
- int32_t line_as_int32 = (int32_t) location.line;
964
- hash = st_hash(&line_as_int32, sizeof(line_as_int32), hash);
965
- return hash;
966
- }
967
-
968
- // ==============
969
- // Heap Stack API
992
+ // Heap Record API
970
993
  // ==============
971
- heap_stack* heap_stack_new(ddog_prof_Slice_Location locations) {
994
+ heap_record* heap_record_new(heap_recorder *recorder, ddog_prof_Slice_Location locations) {
972
995
  uint16_t frames_len = locations.len;
973
996
  if (frames_len > MAX_FRAMES_LIMIT) {
974
- // This should not be happening anyway since MAX_FRAMES_LIMIT should be shared with
975
- // the stacktrace construction mechanism. If it happens, lets just raise. This should
976
- // be safe since only allocate with the GVL anyway.
977
- rb_raise(rb_eRuntimeError, "Found stack with more than %d frames (%d)", MAX_FRAMES_LIMIT, frames_len);
997
+ // This is not expected as MAX_FRAMES_LIMIT is shared with the stacktrace construction mechanism
998
+ raise_error(rb_eRuntimeError, "Found stack with more than %d frames (%d)", MAX_FRAMES_LIMIT, frames_len);
978
999
  }
979
- heap_stack *stack = ruby_xcalloc(1, sizeof(heap_stack) + frames_len * sizeof(heap_frame));
1000
+ heap_record *stack = calloc(1, sizeof(heap_record) + frames_len * sizeof(heap_frame)); // See "note on calloc vs ruby_xcalloc use" above
1001
+ stack->num_tracked_objects = 0;
980
1002
  stack->frames_len = frames_len;
1003
+
1004
+ // Intern all these strings...
1005
+ ddog_CharSlice *strings = recorder->reusable_char_slices;
1006
+ // Put all the char slices in the same array; we'll pull them out in the same order from the ids array
981
1007
  for (uint16_t i = 0; i < stack->frames_len; i++) {
982
1008
  const ddog_prof_Location *location = &locations.ptr[i];
1009
+ strings[i] = location->function.filename;
1010
+ strings[i + stack->frames_len] = location->function.name;
1011
+ }
1012
+ intern_all_or_raise(recorder->string_storage, (ddog_prof_Slice_CharSlice) { .ptr = strings, .len = stack->frames_len * 2 }, recorder->reusable_ids, stack->frames_len * 2);
1013
+
1014
+ // ...and record them for later use
1015
+ for (uint16_t i = 0; i < stack->frames_len; i++) {
983
1016
  stack->frames[i] = (heap_frame) {
984
- .name = string_from_char_slice(location->function.name),
985
- .filename = string_from_char_slice(location->function.filename),
1017
+ .filename = recorder->reusable_ids[i],
1018
+ .name = recorder->reusable_ids[i + stack->frames_len],
986
1019
  // ddog_prof_Location is a int64_t. We don't expect to have to profile files with more than
987
1020
  // 2M lines so this cast should be fairly safe?
988
- .line = (int32_t) location->line,
1021
+ .line = (int32_t) locations.ptr[i].line,
989
1022
  };
990
1023
  }
991
- return stack;
992
- }
993
-
994
- void heap_stack_free(heap_stack *stack) {
995
- for (uint64_t i = 0; i < stack->frames_len; i++) {
996
- heap_frame *frame = &stack->frames[i];
997
- ruby_xfree(frame->name);
998
- ruby_xfree(frame->filename);
999
- }
1000
- ruby_xfree(stack);
1001
- }
1002
1024
 
1003
- // WARN: Must be kept in-sync with ::ddog_location_slice_hash
1004
- st_index_t heap_stack_hash(heap_stack *stack, st_index_t seed) {
1005
- st_index_t hash = seed;
1006
- for (uint64_t i = 0; i < stack->frames_len; i++) {
1007
- hash = heap_frame_hash(&stack->frames[i], hash);
1008
- }
1009
- return hash;
1025
+ return stack;
1010
1026
  }
1011
1027
 
1012
- // WARN: Must be kept in-sync with ::heap_stack_hash
1013
- st_index_t ddog_location_slice_hash(ddog_prof_Slice_Location locations, st_index_t seed) {
1014
- st_index_t hash = seed;
1015
- for (uint64_t i = 0; i < locations.len; i++) {
1016
- hash = ddog_location_hash(locations.ptr[i], hash);
1028
+ void heap_record_free(heap_recorder *recorder, heap_record *stack, bool should_unintern) {
1029
+ // When tearing down the whole recorder state, we skip uninterning as it's not needed (the managed
1030
+ // string table is going to be destroyed anyway) and if there's any failures we can't raise
1031
+ // in the middle of a dfree callback.
1032
+ if (should_unintern) {
1033
+ ddog_prof_ManagedStringId *ids = recorder->reusable_ids;
1034
+
1035
+ // Put all the ids in the same array; doesn't really matter the order
1036
+ for (u_int16_t i = 0; i < stack->frames_len; i++) {
1037
+ ids[i] = stack->frames[i].filename;
1038
+ ids[i + stack->frames_len] = stack->frames[i].name;
1039
+ }
1040
+ unintern_all_or_raise(recorder, (ddog_prof_Slice_ManagedStringId) { .ptr = ids, .len = stack->frames_len * 2 });
1017
1041
  }
1018
- return hash;
1019
- }
1020
1042
 
1021
- // ===================
1022
- // Heap Record Key API
1023
- // ===================
1024
- heap_record_key* heap_record_key_new(heap_stack *stack) {
1025
- heap_record_key *key = ruby_xmalloc(sizeof(heap_record_key));
1026
- key->type = HEAP_STACK;
1027
- key->heap_stack = stack;
1028
- return key;
1043
+ free(stack); // See "note on calloc vs ruby_xcalloc use" above
1029
1044
  }
1030
1045
 
1031
- void heap_record_key_free(heap_record_key *key) {
1032
- ruby_xfree(key);
1033
- }
1046
+ // The entire stack is represented by ids (name, filename) and lines (integers) so we can treat is as just
1047
+ // a big string of bytes and compare it all in one go.
1048
+ int heap_record_cmp_st(st_data_t key1, st_data_t key2) {
1049
+ heap_record *stack1 = (heap_record*) key1;
1050
+ heap_record *stack2 = (heap_record*) key2;
1034
1051
 
1035
- static inline size_t heap_record_key_len(heap_record_key *key) {
1036
- if (key->type == HEAP_STACK) {
1037
- return key->heap_stack->frames_len;
1052
+ if (stack1->frames_len != stack2->frames_len) {
1053
+ return ((int) stack1->frames_len) - ((int) stack2->frames_len);
1038
1054
  } else {
1039
- return key->location_slice->len;
1055
+ return memcmp(stack1->frames, stack2->frames, stack1->frames_len * sizeof(heap_frame));
1040
1056
  }
1041
1057
  }
1042
1058
 
1043
- static inline int64_t heap_record_key_entry_line(heap_record_key *key, size_t entry_i) {
1044
- if (key->type == HEAP_STACK) {
1045
- return key->heap_stack->frames[entry_i].line;
1046
- } else {
1047
- return key->location_slice->ptr[entry_i].line;
1048
- }
1049
- }
1059
+ // Initial seed for hash function, same as Ruby uses
1060
+ #define FNV1_32A_INIT 0x811c9dc5
1050
1061
 
1051
- static inline size_t heap_record_key_entry_name(heap_record_key *key, size_t entry_i, const char **name_ptr) {
1052
- if (key->type == HEAP_STACK) {
1053
- char *name = key->heap_stack->frames[entry_i].name;
1054
- (*name_ptr) = name;
1055
- return strlen(name);
1056
- } else {
1057
- ddog_CharSlice name = key->location_slice->ptr[entry_i].function.name;
1058
- (*name_ptr) = name.ptr;
1059
- return name.len;
1060
- }
1062
+ // The entire stack is represented by ids (name, filename) and lines (integers) so we can treat is as just
1063
+ // a big string of bytes and hash it all in one go.
1064
+ st_index_t heap_record_hash_st(st_data_t key) {
1065
+ heap_record *stack = (heap_record*) key;
1066
+ return st_hash(stack->frames, stack->frames_len * sizeof(heap_frame), FNV1_32A_INIT);
1061
1067
  }
1062
1068
 
1063
- static inline size_t heap_record_key_entry_filename(heap_record_key *key, size_t entry_i, const char **filename_ptr) {
1064
- if (key->type == HEAP_STACK) {
1065
- char *filename = key->heap_stack->frames[entry_i].filename;
1066
- (*filename_ptr) = filename;
1067
- return strlen(filename);
1068
- } else {
1069
- ddog_CharSlice filename = key->location_slice->ptr[entry_i].function.filename;
1070
- (*filename_ptr) = filename.ptr;
1071
- return filename.len;
1069
+ static void unintern_or_raise(heap_recorder *recorder, ddog_prof_ManagedStringId id) {
1070
+ if (id.value == 0) return; // Empty string, nothing to do
1071
+
1072
+ ddog_prof_MaybeError result = ddog_prof_ManagedStringStorage_unintern(recorder->string_storage, id);
1073
+ if (result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
1074
+ raise_error(rb_eRuntimeError, "Failed to unintern id: %"PRIsVALUE, get_error_details_and_drop(&result.some));
1072
1075
  }
1073
1076
  }
1074
1077
 
1075
- int heap_record_key_cmp_st(st_data_t key1, st_data_t key2) {
1076
- heap_record_key *key_record1 = (heap_record_key*) key1;
1077
- heap_record_key *key_record2 = (heap_record_key*) key2;
1078
-
1079
- // Fast path, check if lengths differ
1080
- size_t key_record1_len = heap_record_key_len(key_record1);
1081
- size_t key_record2_len = heap_record_key_len(key_record2);
1082
-
1083
- if (key_record1_len != key_record2_len) {
1084
- return ((int) key_record1_len) - ((int) key_record2_len);
1078
+ static void unintern_all_or_raise(heap_recorder *recorder, ddog_prof_Slice_ManagedStringId ids) {
1079
+ ddog_prof_MaybeError result = ddog_prof_ManagedStringStorage_unintern_all(recorder->string_storage, ids);
1080
+ if (result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
1081
+ raise_error(rb_eRuntimeError, "Failed to unintern_all: %"PRIsVALUE, get_error_details_and_drop(&result.some));
1085
1082
  }
1083
+ }
1086
1084
 
1087
- // If we got this far, we have same lengths so need to check item-by-item
1088
- for (size_t i = 0; i < key_record1_len; i++) {
1089
- // Lines are faster to compare, lets do that first
1090
- size_t line1 = heap_record_key_entry_line(key_record1, i);
1091
- size_t line2 = heap_record_key_entry_line(key_record2, i);
1092
- if (line1 != line2) {
1093
- return ((int) line1) - ((int)line2);
1094
- }
1095
-
1096
- // Then come names, they are usually smaller than filenames
1097
- const char *name1, *name2;
1098
- size_t name1_len = heap_record_key_entry_name(key_record1, i, &name1);
1099
- size_t name2_len = heap_record_key_entry_name(key_record2, i, &name2);
1100
- if (name1_len != name2_len) {
1101
- return ((int) name1_len) - ((int) name2_len);
1102
- }
1103
- int name_cmp_result = strncmp(name1, name2, name1_len);
1104
- if (name_cmp_result != 0) {
1105
- return name_cmp_result;
1106
- }
1107
-
1108
- // Then come filenames
1109
- const char *filename1, *filename2;
1110
- int64_t filename1_len = heap_record_key_entry_filename(key_record1, i, &filename1);
1111
- int64_t filename2_len = heap_record_key_entry_filename(key_record2, i, &filename2);
1112
- if (filename1_len != filename2_len) {
1113
- return ((int) filename1_len) - ((int) filename2_len);
1114
- }
1115
- int filename_cmp_result = strncmp(filename1, filename2, filename1_len);
1116
- if (filename_cmp_result != 0) {
1117
- return filename_cmp_result;
1118
- }
1085
+ static VALUE get_ruby_string_or_raise(heap_recorder *recorder, ddog_prof_ManagedStringId id) {
1086
+ ddog_StringWrapperResult get_string_result = ddog_prof_ManagedStringStorage_get_string(recorder->string_storage, id);
1087
+ if (get_string_result.tag == DDOG_STRING_WRAPPER_RESULT_ERR) {
1088
+ raise_error(rb_eRuntimeError, "Failed to get string: %"PRIsVALUE, get_error_details_and_drop(&get_string_result.err));
1119
1089
  }
1090
+ VALUE ruby_string = ruby_string_from_vec_u8(get_string_result.ok.message);
1091
+ ddog_StringWrapper_drop((ddog_StringWrapper *) &get_string_result.ok);
1120
1092
 
1121
- // If we survived the above for, then everything matched
1122
- return 0;
1093
+ return ruby_string;
1123
1094
  }
1124
1095
 
1125
- // Initial seed for hash functions
1126
- #define FNV1_32A_INIT 0x811c9dc5
1127
-
1128
- st_index_t heap_record_key_hash_st(st_data_t key) {
1129
- heap_record_key *record_key = (heap_record_key*) key;
1130
- if (record_key->type == HEAP_STACK) {
1131
- return heap_stack_hash(record_key->heap_stack, FNV1_32A_INIT);
1132
- } else {
1133
- return ddog_location_slice_hash(*record_key->location_slice, FNV1_32A_INIT);
1096
+ static long obj_id_or_fail(VALUE obj) {
1097
+ VALUE ruby_obj_id = rb_obj_id(obj);
1098
+ if (!FIXNUM_P(ruby_obj_id)) {
1099
+ // Bignum object ids indicate the fixnum range is exhausted - all future IDs will also be bignums.
1100
+ // Heap profiling cannot continue.
1101
+ raise_error(rb_eRuntimeError, "Heap profiling: bignum object id detected. Heap profiling cannot continue.");
1134
1102
  }
1103
+
1104
+ return FIX2LONG(ruby_obj_id);
1135
1105
  }
1136
1106
 
1137
1107
  static inline double ewma_stat(double previous, double current) {
@@ -1141,7 +1111,7 @@ static inline double ewma_stat(double previous, double current) {
1141
1111
 
1142
1112
  VALUE heap_recorder_testonly_is_object_recorded(heap_recorder *heap_recorder, VALUE obj_id) {
1143
1113
  if (heap_recorder == NULL) {
1144
- rb_raise(rb_eArgError, "heap_recorder is NULL");
1114
+ raise_error(rb_eArgError, "heap_recorder is NULL");
1145
1115
  }
1146
1116
 
1147
1117
  // Check if object records contains an object with this object_id
@@ -1150,8 +1120,28 @@ VALUE heap_recorder_testonly_is_object_recorded(heap_recorder *heap_recorder, VA
1150
1120
 
1151
1121
  void heap_recorder_testonly_reset_last_update(heap_recorder *heap_recorder) {
1152
1122
  if (heap_recorder == NULL) {
1153
- rb_raise(rb_eArgError, "heap_recorder is NULL");
1123
+ raise_error(rb_eArgError, "heap_recorder is NULL");
1154
1124
  }
1155
1125
 
1156
1126
  heap_recorder->last_update_ns = 0;
1157
1127
  }
1128
+
1129
+ void heap_recorder_testonly_benchmark_intern(heap_recorder *heap_recorder, ddog_CharSlice string, int times, bool use_all) {
1130
+ if (heap_recorder == NULL) raise_error(rb_eArgError, "heap profiling must be enabled");
1131
+ if (times > REUSABLE_FRAME_DETAILS_SIZE) raise_error(rb_eArgError, "times cannot be > than REUSABLE_FRAME_DETAILS_SIZE");
1132
+
1133
+ if (use_all) {
1134
+ ddog_CharSlice *strings = heap_recorder->reusable_char_slices;
1135
+
1136
+ for (int i = 0; i < times; i++) strings[i] = string;
1137
+
1138
+ intern_all_or_raise(
1139
+ heap_recorder->string_storage,
1140
+ (ddog_prof_Slice_CharSlice) { .ptr = strings, .len = times },
1141
+ heap_recorder->reusable_ids,
1142
+ times
1143
+ );
1144
+ } else {
1145
+ for (int i = 0; i < times; i++) intern_or_raise(heap_recorder->string_storage, string);
1146
+ }
1147
+ }