datadog 2.7.1 → 2.17.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 (417) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +310 -1
  3. data/ext/datadog_profiling_native_extension/clock_id.h +2 -2
  4. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +66 -56
  5. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
  6. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +1 -1
  7. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +16 -16
  8. data/ext/datadog_profiling_native_extension/collectors_stack.c +10 -10
  9. data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
  10. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +314 -145
  11. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +1 -4
  12. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +10 -0
  13. data/ext/datadog_profiling_native_extension/encoded_profile.c +79 -0
  14. data/ext/datadog_profiling_native_extension/encoded_profile.h +8 -0
  15. data/ext/datadog_profiling_native_extension/extconf.rb +7 -8
  16. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +2 -0
  17. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +0 -8
  18. data/ext/datadog_profiling_native_extension/heap_recorder.c +61 -174
  19. data/ext/datadog_profiling_native_extension/heap_recorder.h +2 -2
  20. data/ext/datadog_profiling_native_extension/http_transport.c +64 -98
  21. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +68 -1
  22. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +10 -1
  23. data/ext/datadog_profiling_native_extension/profiling.c +19 -8
  24. data/ext/datadog_profiling_native_extension/ruby_helpers.c +8 -8
  25. data/ext/datadog_profiling_native_extension/stack_recorder.c +84 -131
  26. data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -2
  27. data/ext/datadog_profiling_native_extension/time_helpers.h +1 -1
  28. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +47 -0
  29. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +31 -0
  30. data/ext/libdatadog_api/crashtracker.c +17 -15
  31. data/ext/libdatadog_api/crashtracker.h +5 -0
  32. data/ext/libdatadog_api/datadog_ruby_common.c +1 -4
  33. data/ext/libdatadog_api/datadog_ruby_common.h +10 -0
  34. data/ext/libdatadog_api/init.c +15 -0
  35. data/ext/libdatadog_api/library_config.c +122 -0
  36. data/ext/libdatadog_api/library_config.h +19 -0
  37. data/ext/libdatadog_api/macos_development.md +3 -3
  38. data/ext/libdatadog_api/process_discovery.c +117 -0
  39. data/ext/libdatadog_api/process_discovery.h +5 -0
  40. data/ext/libdatadog_extconf_helpers.rb +1 -1
  41. data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +89 -0
  42. data/lib/datadog/appsec/actions_handler.rb +49 -0
  43. data/lib/datadog/appsec/anonymizer.rb +16 -0
  44. data/lib/datadog/appsec/api_security/lru_cache.rb +49 -0
  45. data/lib/datadog/appsec/api_security.rb +9 -0
  46. data/lib/datadog/appsec/assets/waf_rules/README.md +50 -5
  47. data/lib/datadog/appsec/assets/waf_rules/processors.json +239 -10
  48. data/lib/datadog/appsec/assets/waf_rules/recommended.json +355 -157
  49. data/lib/datadog/appsec/assets/waf_rules/scanners.json +926 -17
  50. data/lib/datadog/appsec/assets/waf_rules/strict.json +62 -32
  51. data/lib/datadog/appsec/autoload.rb +1 -1
  52. data/lib/datadog/appsec/component.rb +41 -33
  53. data/lib/datadog/appsec/compressed_json.rb +40 -0
  54. data/lib/datadog/appsec/configuration/settings.rb +152 -25
  55. data/lib/datadog/appsec/context.rb +74 -0
  56. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +92 -0
  57. data/lib/datadog/appsec/contrib/active_record/integration.rb +41 -0
  58. data/lib/datadog/appsec/contrib/active_record/patcher.rb +101 -0
  59. data/lib/datadog/appsec/contrib/auto_instrument.rb +1 -1
  60. data/lib/datadog/appsec/contrib/devise/configuration.rb +52 -0
  61. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +78 -0
  62. data/lib/datadog/appsec/contrib/devise/ext.rb +22 -0
  63. data/lib/datadog/appsec/contrib/devise/integration.rb +1 -2
  64. data/lib/datadog/appsec/contrib/devise/patcher.rb +33 -25
  65. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +102 -0
  66. data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +69 -0
  67. data/lib/datadog/appsec/contrib/devise/{patcher/rememberable_patch.rb → patches/skip_signin_tracking_patch.rb} +3 -3
  68. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +106 -0
  69. data/lib/datadog/appsec/contrib/excon/integration.rb +41 -0
  70. data/lib/datadog/appsec/contrib/excon/patcher.rb +28 -0
  71. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +42 -0
  72. data/lib/datadog/appsec/contrib/faraday/connection_patch.rb +22 -0
  73. data/lib/datadog/appsec/contrib/faraday/integration.rb +42 -0
  74. data/lib/datadog/appsec/contrib/faraday/patcher.rb +53 -0
  75. data/lib/datadog/appsec/contrib/faraday/rack_builder_patch.rb +22 -0
  76. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +41 -0
  77. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +1 -7
  78. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +17 -30
  79. data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -1
  80. data/lib/datadog/appsec/contrib/graphql/patcher.rb +0 -3
  81. data/lib/datadog/appsec/contrib/rack/ext.rb +34 -0
  82. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
  83. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +78 -98
  84. data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
  85. data/lib/datadog/appsec/contrib/rack/patcher.rb +0 -3
  86. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +10 -11
  87. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +52 -68
  88. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +16 -33
  89. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
  90. data/lib/datadog/appsec/contrib/rails/patcher.rb +25 -38
  91. data/lib/datadog/appsec/contrib/rest_client/integration.rb +45 -0
  92. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +28 -0
  93. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +38 -0
  94. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +31 -68
  95. data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
  96. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +5 -31
  97. data/lib/datadog/appsec/event.rb +96 -135
  98. data/lib/datadog/appsec/ext.rb +12 -3
  99. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +7 -2
  100. data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +24 -0
  101. data/lib/datadog/appsec/instrumentation/gateway.rb +17 -22
  102. data/lib/datadog/appsec/metrics/collector.rb +38 -0
  103. data/lib/datadog/appsec/metrics/exporter.rb +35 -0
  104. data/lib/datadog/appsec/metrics/telemetry.rb +23 -0
  105. data/lib/datadog/appsec/metrics.rb +13 -0
  106. data/lib/datadog/appsec/monitor/gateway/watcher.rb +52 -32
  107. data/lib/datadog/appsec/processor/rule_loader.rb +26 -31
  108. data/lib/datadog/appsec/processor/rule_merger.rb +7 -6
  109. data/lib/datadog/appsec/processor.rb +5 -4
  110. data/lib/datadog/appsec/remote.rb +26 -12
  111. data/lib/datadog/appsec/response.rb +19 -85
  112. data/lib/datadog/appsec/security_engine/result.rb +67 -0
  113. data/lib/datadog/appsec/security_engine/runner.rb +88 -0
  114. data/lib/datadog/appsec/security_engine.rb +9 -0
  115. data/lib/datadog/appsec/security_event.rb +39 -0
  116. data/lib/datadog/appsec/utils.rb +0 -2
  117. data/lib/datadog/appsec.rb +23 -10
  118. data/lib/datadog/auto_instrument.rb +3 -0
  119. data/lib/datadog/core/buffer/random.rb +18 -2
  120. data/lib/datadog/core/configuration/agent_settings_resolver.rb +42 -14
  121. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +176 -0
  122. data/lib/datadog/core/configuration/components.rb +76 -32
  123. data/lib/datadog/core/configuration/components_state.rb +23 -0
  124. data/lib/datadog/core/configuration/ext.rb +5 -1
  125. data/lib/datadog/core/configuration/option.rb +79 -43
  126. data/lib/datadog/core/configuration/option_definition.rb +6 -4
  127. data/lib/datadog/core/configuration/options.rb +3 -3
  128. data/lib/datadog/core/configuration/settings.rb +100 -41
  129. data/lib/datadog/core/configuration/stable_config.rb +23 -0
  130. data/lib/datadog/core/configuration.rb +43 -11
  131. data/lib/datadog/{tracing → core}/contrib/rails/utils.rb +1 -3
  132. data/lib/datadog/core/crashtracking/component.rb +4 -13
  133. data/lib/datadog/core/diagnostics/environment_logger.rb +1 -1
  134. data/lib/datadog/core/encoding.rb +17 -1
  135. data/lib/datadog/core/environment/agent_info.rb +78 -0
  136. data/lib/datadog/core/environment/cgroup.rb +10 -12
  137. data/lib/datadog/core/environment/container.rb +38 -40
  138. data/lib/datadog/core/environment/ext.rb +6 -6
  139. data/lib/datadog/core/environment/git.rb +1 -0
  140. data/lib/datadog/core/environment/identity.rb +3 -3
  141. data/lib/datadog/core/environment/platform.rb +3 -3
  142. data/lib/datadog/core/environment/variable_helpers.rb +1 -1
  143. data/lib/datadog/core/error.rb +11 -9
  144. data/lib/datadog/core/logger.rb +2 -2
  145. data/lib/datadog/core/metrics/client.rb +27 -27
  146. data/lib/datadog/core/metrics/logging.rb +5 -5
  147. data/lib/datadog/core/process_discovery.rb +32 -0
  148. data/lib/datadog/core/rate_limiter.rb +4 -2
  149. data/lib/datadog/core/remote/client/capabilities.rb +6 -0
  150. data/lib/datadog/core/remote/client.rb +107 -92
  151. data/lib/datadog/core/remote/component.rb +18 -19
  152. data/lib/datadog/core/remote/configuration/digest.rb +7 -7
  153. data/lib/datadog/core/remote/configuration/path.rb +1 -1
  154. data/lib/datadog/core/remote/configuration/repository.rb +2 -1
  155. data/lib/datadog/core/remote/negotiation.rb +9 -9
  156. data/lib/datadog/core/remote/transport/config.rb +4 -3
  157. data/lib/datadog/core/remote/transport/http/api.rb +13 -18
  158. data/lib/datadog/core/remote/transport/http/client.rb +5 -4
  159. data/lib/datadog/core/remote/transport/http/config.rb +27 -55
  160. data/lib/datadog/core/remote/transport/http/negotiation.rb +8 -51
  161. data/lib/datadog/core/remote/transport/http.rb +25 -94
  162. data/lib/datadog/core/remote/transport/negotiation.rb +17 -4
  163. data/lib/datadog/core/remote/worker.rb +10 -7
  164. data/lib/datadog/core/runtime/metrics.rb +12 -5
  165. data/lib/datadog/core/telemetry/component.rb +84 -49
  166. data/lib/datadog/core/telemetry/emitter.rb +23 -11
  167. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +65 -0
  168. data/lib/datadog/core/telemetry/event/app_closing.rb +18 -0
  169. data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +33 -0
  170. data/lib/datadog/core/telemetry/event/app_heartbeat.rb +18 -0
  171. data/lib/datadog/core/telemetry/event/app_integrations_change.rb +58 -0
  172. data/lib/datadog/core/telemetry/event/app_started.rb +179 -0
  173. data/lib/datadog/core/telemetry/event/base.rb +40 -0
  174. data/lib/datadog/core/telemetry/event/distributions.rb +18 -0
  175. data/lib/datadog/core/telemetry/event/generate_metrics.rb +43 -0
  176. data/lib/datadog/core/telemetry/event/log.rb +76 -0
  177. data/lib/datadog/core/telemetry/event/message_batch.rb +42 -0
  178. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +43 -0
  179. data/lib/datadog/core/telemetry/event.rb +17 -383
  180. data/lib/datadog/core/telemetry/ext.rb +1 -0
  181. data/lib/datadog/core/telemetry/http/adapters/net.rb +12 -97
  182. data/lib/datadog/core/telemetry/logger.rb +1 -1
  183. data/lib/datadog/core/telemetry/logging.rb +2 -2
  184. data/lib/datadog/core/telemetry/metric.rb +28 -6
  185. data/lib/datadog/core/telemetry/request.rb +4 -4
  186. data/lib/datadog/core/telemetry/transport/http/api.rb +43 -0
  187. data/lib/datadog/core/telemetry/transport/http/client.rb +49 -0
  188. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +92 -0
  189. data/lib/datadog/core/telemetry/transport/http.rb +63 -0
  190. data/lib/datadog/core/telemetry/transport/telemetry.rb +51 -0
  191. data/lib/datadog/core/telemetry/worker.rb +128 -25
  192. data/lib/datadog/core/transport/http/adapters/test.rb +2 -1
  193. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +1 -1
  194. data/lib/datadog/{tracing → core}/transport/http/api/instance.rb +18 -1
  195. data/lib/datadog/core/transport/http/api/spec.rb +36 -0
  196. data/lib/datadog/{tracing → core}/transport/http/builder.rb +53 -31
  197. data/lib/datadog/core/transport/http.rb +75 -0
  198. data/lib/datadog/core/transport/response.rb +4 -0
  199. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +6 -6
  200. data/lib/datadog/core/utils/duration.rb +32 -32
  201. data/lib/datadog/core/utils/forking.rb +2 -2
  202. data/lib/datadog/core/utils/network.rb +6 -6
  203. data/lib/datadog/core/utils/only_once_successful.rb +16 -5
  204. data/lib/datadog/core/utils/time.rb +20 -0
  205. data/lib/datadog/core/utils/truncation.rb +21 -0
  206. data/lib/datadog/core/vendor/multipart-post/multipart/post/composite_read_io.rb +1 -1
  207. data/lib/datadog/core/vendor/multipart-post/multipart/post/multipartable.rb +8 -8
  208. data/lib/datadog/core/vendor/multipart-post/multipart/post/parts.rb +7 -7
  209. data/lib/datadog/core/worker.rb +1 -1
  210. data/lib/datadog/core/workers/async.rb +29 -12
  211. data/lib/datadog/core/workers/interval_loop.rb +12 -1
  212. data/lib/datadog/core/workers/runtime_metrics.rb +2 -2
  213. data/lib/datadog/core.rb +8 -0
  214. data/lib/datadog/di/base.rb +115 -0
  215. data/lib/datadog/di/boot.rb +34 -0
  216. data/lib/datadog/di/code_tracker.rb +26 -15
  217. data/lib/datadog/di/component.rb +23 -14
  218. data/lib/datadog/di/configuration/settings.rb +25 -1
  219. data/lib/datadog/di/contrib/active_record.rb +1 -0
  220. data/lib/datadog/di/contrib/railtie.rb +15 -0
  221. data/lib/datadog/di/contrib.rb +28 -0
  222. data/lib/datadog/di/error.rb +5 -0
  223. data/lib/datadog/di/instrumenter.rb +111 -20
  224. data/lib/datadog/di/logger.rb +30 -0
  225. data/lib/datadog/di/preload.rb +18 -0
  226. data/lib/datadog/di/probe.rb +14 -7
  227. data/lib/datadog/di/probe_builder.rb +1 -0
  228. data/lib/datadog/di/probe_manager.rb +11 -5
  229. data/lib/datadog/di/probe_notification_builder.rb +34 -8
  230. data/lib/datadog/di/probe_notifier_worker.rb +52 -26
  231. data/lib/datadog/di/redactor.rb +0 -1
  232. data/lib/datadog/di/remote.rb +147 -0
  233. data/lib/datadog/di/serializer.rb +14 -7
  234. data/lib/datadog/di/transport/diagnostics.rb +62 -0
  235. data/lib/datadog/di/transport/http/api.rb +42 -0
  236. data/lib/datadog/di/transport/http/client.rb +47 -0
  237. data/lib/datadog/di/transport/http/diagnostics.rb +65 -0
  238. data/lib/datadog/di/transport/http/input.rb +67 -0
  239. data/lib/datadog/di/transport/http.rb +57 -0
  240. data/lib/datadog/di/transport/input.rb +62 -0
  241. data/lib/datadog/di/utils.rb +103 -0
  242. data/lib/datadog/di.rb +14 -76
  243. data/lib/datadog/error_tracking/collector.rb +87 -0
  244. data/lib/datadog/error_tracking/component.rb +167 -0
  245. data/lib/datadog/error_tracking/configuration/settings.rb +63 -0
  246. data/lib/datadog/error_tracking/configuration.rb +11 -0
  247. data/lib/datadog/error_tracking/ext.rb +18 -0
  248. data/lib/datadog/error_tracking/extensions.rb +16 -0
  249. data/lib/datadog/error_tracking/filters.rb +77 -0
  250. data/lib/datadog/error_tracking.rb +18 -0
  251. data/lib/datadog/kit/appsec/events.rb +15 -3
  252. data/lib/datadog/kit/identity.rb +9 -5
  253. data/lib/datadog/opentelemetry/api/baggage.rb +90 -0
  254. data/lib/datadog/opentelemetry/api/baggage.rbs +26 -0
  255. data/lib/datadog/opentelemetry/api/context.rb +16 -2
  256. data/lib/datadog/opentelemetry/sdk/trace/span.rb +1 -1
  257. data/lib/datadog/opentelemetry.rb +2 -1
  258. data/lib/datadog/profiling/collectors/code_provenance.rb +1 -1
  259. data/lib/datadog/profiling/collectors/info.rb +3 -0
  260. data/lib/datadog/profiling/collectors/thread_context.rb +1 -1
  261. data/lib/datadog/profiling/component.rb +60 -76
  262. data/lib/datadog/profiling/encoded_profile.rb +11 -0
  263. data/lib/datadog/profiling/exporter.rb +3 -4
  264. data/lib/datadog/profiling/ext.rb +0 -2
  265. data/lib/datadog/profiling/flush.rb +5 -8
  266. data/lib/datadog/profiling/http_transport.rb +6 -85
  267. data/lib/datadog/profiling/load_native_extension.rb +1 -33
  268. data/lib/datadog/profiling/scheduler.rb +8 -1
  269. data/lib/datadog/profiling/stack_recorder.rb +4 -4
  270. data/lib/datadog/profiling/tag_builder.rb +1 -5
  271. data/lib/datadog/profiling.rb +6 -2
  272. data/lib/datadog/tracing/analytics.rb +1 -1
  273. data/lib/datadog/tracing/component.rb +16 -12
  274. data/lib/datadog/tracing/configuration/ext.rb +8 -1
  275. data/lib/datadog/tracing/configuration/settings.rb +22 -10
  276. data/lib/datadog/tracing/context_provider.rb +1 -1
  277. data/lib/datadog/tracing/contrib/action_cable/integration.rb +5 -2
  278. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +6 -2
  279. data/lib/datadog/tracing/contrib/action_pack/integration.rb +5 -2
  280. data/lib/datadog/tracing/contrib/action_view/integration.rb +5 -2
  281. data/lib/datadog/tracing/contrib/active_job/integration.rb +5 -2
  282. data/lib/datadog/tracing/contrib/active_record/integration.rb +7 -3
  283. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +7 -2
  284. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +36 -1
  285. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +4 -0
  286. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +14 -4
  287. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +10 -0
  288. data/lib/datadog/tracing/contrib/active_support/integration.rb +5 -2
  289. data/lib/datadog/tracing/contrib/auto_instrument.rb +2 -2
  290. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +10 -0
  291. data/lib/datadog/tracing/contrib/aws/integration.rb +3 -0
  292. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +5 -1
  293. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +3 -0
  294. data/lib/datadog/tracing/contrib/configuration/settings.rb +1 -1
  295. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +4 -0
  296. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
  297. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -5
  298. data/lib/datadog/tracing/contrib/excon/middleware.rb +5 -3
  299. data/lib/datadog/tracing/contrib/ext.rb +1 -0
  300. data/lib/datadog/tracing/contrib/extensions.rb +29 -3
  301. data/lib/datadog/tracing/contrib/faraday/middleware.rb +5 -3
  302. data/lib/datadog/tracing/contrib/graphql/configuration/error_extension_env_parser.rb +21 -0
  303. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +11 -0
  304. data/lib/datadog/tracing/contrib/graphql/ext.rb +5 -0
  305. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +102 -11
  306. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +7 -1
  307. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +3 -0
  308. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +0 -15
  309. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +4 -1
  310. data/lib/datadog/tracing/contrib/http/instrumentation.rb +6 -10
  311. data/lib/datadog/tracing/contrib/http/integration.rb +3 -0
  312. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -16
  313. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +7 -15
  314. data/lib/datadog/tracing/contrib/httprb/integration.rb +3 -0
  315. data/lib/datadog/tracing/contrib/kafka/integration.rb +3 -0
  316. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +27 -0
  317. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +48 -0
  318. data/lib/datadog/tracing/contrib/karafka/ext.rb +27 -0
  319. data/lib/datadog/tracing/contrib/karafka/integration.rb +45 -0
  320. data/lib/datadog/tracing/contrib/karafka/monitor.rb +66 -0
  321. data/lib/datadog/tracing/contrib/karafka/patcher.rb +71 -0
  322. data/lib/datadog/tracing/contrib/karafka.rb +37 -0
  323. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +8 -0
  324. data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
  325. data/lib/datadog/tracing/contrib/mongodb/integration.rb +3 -0
  326. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +18 -1
  327. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +17 -0
  328. data/lib/datadog/tracing/contrib/opensearch/ext.rb +9 -0
  329. data/lib/datadog/tracing/contrib/opensearch/integration.rb +3 -0
  330. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +5 -1
  331. data/lib/datadog/tracing/contrib/patcher.rb +5 -2
  332. data/lib/datadog/tracing/contrib/presto/integration.rb +3 -0
  333. data/lib/datadog/tracing/contrib/rack/header_collection.rb +11 -1
  334. data/lib/datadog/tracing/contrib/rack/integration.rb +2 -2
  335. data/lib/datadog/tracing/contrib/rack/middlewares.rb +1 -1
  336. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
  337. data/lib/datadog/tracing/contrib/rails/framework.rb +2 -2
  338. data/lib/datadog/tracing/contrib/rails/patcher.rb +1 -1
  339. data/lib/datadog/tracing/contrib/rest_client/integration.rb +3 -0
  340. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +5 -3
  341. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +6 -1
  342. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +3 -0
  343. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +1 -1
  344. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +6 -1
  345. data/lib/datadog/tracing/contrib/support.rb +28 -0
  346. data/lib/datadog/tracing/contrib.rb +1 -0
  347. data/lib/datadog/tracing/correlation.rb +9 -2
  348. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  349. data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
  350. data/lib/datadog/tracing/distributed/baggage.rb +131 -0
  351. data/lib/datadog/tracing/distributed/datadog.rb +4 -2
  352. data/lib/datadog/tracing/distributed/propagation.rb +25 -4
  353. data/lib/datadog/tracing/distributed/propagation_policy.rb +42 -0
  354. data/lib/datadog/tracing/metadata/errors.rb +4 -4
  355. data/lib/datadog/tracing/metadata/ext.rb +5 -0
  356. data/lib/datadog/tracing/metadata/metastruct.rb +36 -0
  357. data/lib/datadog/tracing/metadata/metastruct_tagging.rb +42 -0
  358. data/lib/datadog/tracing/metadata.rb +2 -0
  359. data/lib/datadog/tracing/sampling/rate_sampler.rb +2 -1
  360. data/lib/datadog/tracing/sampling/span/rule.rb +0 -1
  361. data/lib/datadog/tracing/span.rb +22 -5
  362. data/lib/datadog/tracing/span_event.rb +124 -4
  363. data/lib/datadog/tracing/span_operation.rb +52 -16
  364. data/lib/datadog/tracing/sync_writer.rb +9 -5
  365. data/lib/datadog/tracing/trace_digest.rb +9 -2
  366. data/lib/datadog/tracing/trace_operation.rb +44 -24
  367. data/lib/datadog/tracing/trace_segment.rb +6 -4
  368. data/lib/datadog/tracing/tracer.rb +60 -12
  369. data/lib/datadog/tracing/transport/http/api.rb +5 -4
  370. data/lib/datadog/tracing/transport/http/client.rb +5 -4
  371. data/lib/datadog/tracing/transport/http/traces.rb +13 -44
  372. data/lib/datadog/tracing/transport/http.rb +13 -70
  373. data/lib/datadog/tracing/transport/serializable_trace.rb +31 -7
  374. data/lib/datadog/tracing/transport/trace_formatter.rb +7 -0
  375. data/lib/datadog/tracing/transport/traces.rb +47 -13
  376. data/lib/datadog/tracing/utils.rb +1 -1
  377. data/lib/datadog/tracing/workers/trace_writer.rb +8 -5
  378. data/lib/datadog/tracing/workers.rb +5 -4
  379. data/lib/datadog/tracing/writer.rb +10 -6
  380. data/lib/datadog/tracing.rb +16 -3
  381. data/lib/datadog/version.rb +2 -2
  382. data/lib/datadog.rb +2 -0
  383. metadata +143 -50
  384. data/ext/datadog_profiling_loader/datadog_profiling_loader.c +0 -142
  385. data/ext/datadog_profiling_loader/extconf.rb +0 -60
  386. data/lib/datadog/appsec/contrib/devise/event.rb +0 -57
  387. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +0 -77
  388. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +0 -54
  389. data/lib/datadog/appsec/contrib/devise/resource.rb +0 -35
  390. data/lib/datadog/appsec/contrib/devise/tracking.rb +0 -57
  391. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +0 -46
  392. data/lib/datadog/appsec/contrib/patcher.rb +0 -12
  393. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +0 -69
  394. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +0 -47
  395. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +0 -53
  396. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +0 -53
  397. data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -14
  398. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +0 -48
  399. data/lib/datadog/appsec/monitor/reactive/set_user.rb +0 -45
  400. data/lib/datadog/appsec/processor/actions.rb +0 -49
  401. data/lib/datadog/appsec/processor/context.rb +0 -107
  402. data/lib/datadog/appsec/reactive/address_hash.rb +0 -22
  403. data/lib/datadog/appsec/reactive/engine.rb +0 -47
  404. data/lib/datadog/appsec/reactive/operation.rb +0 -68
  405. data/lib/datadog/appsec/reactive/subscriber.rb +0 -19
  406. data/lib/datadog/appsec/scope.rb +0 -58
  407. data/lib/datadog/appsec/utils/trace_operation.rb +0 -15
  408. data/lib/datadog/core/crashtracking/agent_base_url.rb +0 -21
  409. data/lib/datadog/core/remote/transport/http/api/instance.rb +0 -39
  410. data/lib/datadog/core/remote/transport/http/api/spec.rb +0 -21
  411. data/lib/datadog/core/remote/transport/http/builder.rb +0 -219
  412. data/lib/datadog/core/telemetry/http/env.rb +0 -20
  413. data/lib/datadog/core/telemetry/http/ext.rb +0 -28
  414. data/lib/datadog/core/telemetry/http/response.rb +0 -70
  415. data/lib/datadog/core/telemetry/http/transport.rb +0 -90
  416. data/lib/datadog/di/transport.rb +0 -81
  417. data/lib/datadog/tracing/transport/http/api/spec.rb +0 -19
@@ -29,10 +29,7 @@ VALUE datadog_gem_version(void) {
29
29
  }
30
30
 
31
31
  static VALUE log_failure_to_process_tag(VALUE err_details) {
32
- VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
33
- VALUE logger = rb_funcall(datadog_module, rb_intern("logger"), 0);
34
-
35
- return rb_funcall(logger, rb_intern("warn"), 1, rb_sprintf("Failed to convert tag: %"PRIsVALUE, err_details));
32
+ return log_warning(rb_sprintf("Failed to convert tag: %"PRIsVALUE, err_details));
36
33
  }
37
34
 
38
35
  __attribute__((warn_unused_result))
@@ -27,6 +27,9 @@
27
27
  #define ENFORCE_BOOLEAN(value) \
28
28
  { if (RB_UNLIKELY(value != Qtrue && value != Qfalse)) raise_unexpected_type(value, ADD_QUOTES(value), "true or false", __FILE__, __LINE__, __func__); }
29
29
 
30
+ #define ENFORCE_TYPED_DATA(value, type) \
31
+ { if (RB_UNLIKELY(!rb_typeddata_is_kind_of(value, type))) raise_unexpected_type(value, ADD_QUOTES(value), "TypedData of type " ADD_QUOTES(type), __FILE__, __LINE__, __func__); }
32
+
30
33
  NORETURN(void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char* function_name));
31
34
 
32
35
  // Helper to retrieve Datadog::VERSION::STRING
@@ -38,6 +41,13 @@ static inline ddog_CharSlice char_slice_from_ruby_string(VALUE string) {
38
41
  return char_slice;
39
42
  }
40
43
 
44
+ static inline VALUE log_warning(VALUE warning) {
45
+ VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
46
+ VALUE logger = rb_funcall(datadog_module, rb_intern("logger"), 0);
47
+
48
+ return rb_funcall(logger, rb_intern("warn"), 1, warning);
49
+ }
50
+
41
51
  __attribute__((warn_unused_result))
42
52
  ddog_Vec_Tag convert_tags(VALUE tags_as_array);
43
53
 
@@ -0,0 +1,79 @@
1
+ #include "encoded_profile.h"
2
+ #include "datadog_ruby_common.h"
3
+ #include "libdatadog_helpers.h"
4
+
5
+ // This class exists to wrap a ddog_prof_EncodedProfile into a Ruby object
6
+ // This file implements the native bits of the Datadog::Profiling::EncodedProfile class
7
+
8
+ static void encoded_profile_typed_data_free(void *state_ptr);
9
+ static VALUE _native_bytes(VALUE self);
10
+
11
+ static VALUE encoded_profile_class = Qnil;
12
+
13
+ void encoded_profile_init(VALUE profiling_module) {
14
+ encoded_profile_class = rb_define_class_under(profiling_module, "EncodedProfile", rb_cObject);
15
+
16
+ rb_undef_alloc_func(encoded_profile_class); // Class cannot be created from Ruby code
17
+ rb_global_variable(&encoded_profile_class);
18
+
19
+ rb_define_method(encoded_profile_class, "_native_bytes", _native_bytes, 0);
20
+ }
21
+
22
+ // This structure is used to define a Ruby object that stores a `ddog_prof_EncodedProfile`
23
+ // See also https://github.com/ruby/ruby/blob/master/doc/extension.rdoc for how this works
24
+ static const rb_data_type_t encoded_profile_typed_data = {
25
+ .wrap_struct_name = "Datadog::Profiling::EncodedProfile",
26
+ .function = {
27
+ .dmark = NULL, // We don't store references to Ruby objects so we don't need to mark any of them
28
+ .dfree = encoded_profile_typed_data_free,
29
+ .dsize = NULL, // We don't track memory usage (although it'd be cool if we did!)
30
+ //.dcompact = NULL, // Not needed -- we don't store references to Ruby objects
31
+ },
32
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
33
+ };
34
+
35
+ VALUE from_ddog_prof_EncodedProfile(ddog_prof_EncodedProfile profile) {
36
+ ddog_prof_EncodedProfile *state = ruby_xcalloc(1, sizeof(ddog_prof_EncodedProfile));
37
+ *state = profile;
38
+ return TypedData_Wrap_Struct(encoded_profile_class, &encoded_profile_typed_data, state);
39
+ }
40
+
41
+ static ddog_ByteSlice get_bytes(ddog_prof_EncodedProfile *state) {
42
+ ddog_prof_Result_ByteSlice raw_bytes = ddog_prof_EncodedProfile_bytes(state);
43
+ if (raw_bytes.tag == DDOG_PROF_RESULT_BYTE_SLICE_ERR_BYTE_SLICE) {
44
+ rb_raise(rb_eRuntimeError, "Failed to get bytes from profile: %"PRIsVALUE, get_error_details_and_drop(&raw_bytes.err));
45
+ }
46
+ return raw_bytes.ok;
47
+ }
48
+
49
+ static ddog_prof_EncodedProfile *internal_to_ddog_prof_EncodedProfile(VALUE object) {
50
+ ddog_prof_EncodedProfile *state;
51
+ TypedData_Get_Struct(object, ddog_prof_EncodedProfile, &encoded_profile_typed_data, state);
52
+ return state;
53
+ }
54
+
55
+ ddog_prof_EncodedProfile *to_ddog_prof_EncodedProfile(VALUE object) {
56
+ ddog_prof_EncodedProfile *state = internal_to_ddog_prof_EncodedProfile(object);
57
+ get_bytes(state); // Validate profile is still usable -- if it's not, this will raise an exception
58
+ return state;
59
+ }
60
+
61
+ static void encoded_profile_typed_data_free(void *state_ptr) {
62
+ ddog_prof_EncodedProfile *state = (ddog_prof_EncodedProfile *) state_ptr;
63
+
64
+ // This drops the profile itself
65
+ ddog_prof_EncodedProfile_drop(state);
66
+
67
+ // This drops the tiny bit of memory we allocated to contain the ` ddog_prof_EncodedProfile` struct
68
+ ruby_xfree(state);
69
+ }
70
+
71
+ static VALUE _native_bytes(VALUE self) {
72
+ ddog_ByteSlice bytes = get_bytes(internal_to_ddog_prof_EncodedProfile(self));
73
+ return rb_str_new((const char *) bytes.ptr, bytes.len);
74
+ }
75
+
76
+ VALUE enforce_encoded_profile_instance(VALUE object) {
77
+ ENFORCE_TYPED_DATA(object, &encoded_profile_typed_data);
78
+ return object;
79
+ }
@@ -0,0 +1,8 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+ #include <datadog/profiling.h>
5
+
6
+ VALUE from_ddog_prof_EncodedProfile(ddog_prof_EncodedProfile profile);
7
+ VALUE enforce_encoded_profile_instance(VALUE object);
8
+ ddog_prof_EncodedProfile *to_ddog_prof_EncodedProfile(VALUE object);
@@ -131,6 +131,9 @@ end
131
131
 
132
132
  have_func "malloc_stats"
133
133
 
134
+ # On Ruby 3.5, we can't ask the object_id from IMEMOs (https://github.com/ruby/ruby/pull/13347)
135
+ $defs << "-DNO_IMEMO_OBJECT_ID" unless RUBY_VERSION < "3.5"
136
+
134
137
  # On Ruby 2.5 and 3.3, this symbol was not visible. It is on 2.6 to 3.2, as well as 3.4+
135
138
  $defs << "-DNO_RB_OBJ_INFO" if RUBY_VERSION.start_with?("2.5", "3.3")
136
139
 
@@ -164,17 +167,16 @@ $defs << "-DNO_INT_FIRST_LINENO" if RUBY_VERSION < "3.2"
164
167
  # On older Rubies, "pop" was not a primitive operation
165
168
  $defs << "-DNO_PRIMITIVE_POP" if RUBY_VERSION < "3.2"
166
169
 
170
+ # We could support this for older Rubies, but since this only gets used by the OTEL context extraction, and that
171
+ # use-case is only for 3.1+, we didn't bother supporting it farther back yet.
172
+ $defs << "-DNO_CURRENT_FIBER_FOR" if RUBY_VERSION < "3.1"
173
+
167
174
  # On older Rubies, there was no tid member in the internal thread structure
168
175
  $defs << "-DNO_THREAD_TID" if RUBY_VERSION < "3.1"
169
176
 
170
177
  # On older Rubies, there was no jit_return member on the rb_control_frame_t struct
171
178
  $defs << "-DNO_JIT_RETURN" if RUBY_VERSION < "3.1"
172
179
 
173
- # On older Rubies, rb_gc_force_recycle allowed to free objects in a way that
174
- # would be invisible to free tracepoints, finalizers and without cleaning
175
- # obj_to_id_tbl mappings.
176
- $defs << "-DHAVE_WORKING_RB_GC_FORCE_RECYCLE" if RUBY_VERSION < "3.1"
177
-
178
180
  # On older Rubies, there are no Ractors
179
181
  $defs << "-DNO_RACTORS" if RUBY_VERSION < "3"
180
182
 
@@ -184,9 +186,6 @@ $defs << "-DNO_IMEMO_NAME" if RUBY_VERSION < "3"
184
186
  # On older Rubies, objects would not move
185
187
  $defs << "-DNO_T_MOVED" if RUBY_VERSION < "2.7"
186
188
 
187
- # On older Rubies, there was no RUBY_SEEN_OBJ_ID flag
188
- $defs << "-DNO_SEEN_OBJ_ID_FLAG" if RUBY_VERSION < "2.7"
189
-
190
189
  # On older Rubies, rb_global_vm_lock_struct did not include the owner field
191
190
  $defs << "-DNO_GVL_OWNER" if RUBY_VERSION < "2.6"
192
191
 
@@ -1,5 +1,7 @@
1
1
  #include <ruby.h>
2
2
  #include <ruby/thread.h>
3
+
4
+ #include "datadog_ruby_common.h"
3
5
  #include "gvl_profiling_helper.h"
4
6
 
5
7
  #if !defined(NO_GVL_INSTRUMENTATION) && !defined(USE_GVL_PROFILING_3_2_WORKAROUNDS) // Ruby 3.3+
@@ -39,14 +39,6 @@
39
39
 
40
40
  static inline void gvl_profiling_init(void) { }
41
41
 
42
- // This header gets included in private_vm_access.c which can't include datadog_ruby_common.h so we replicate this
43
- // helper here
44
- #ifdef __GNUC__
45
- #define DDTRACE_UNUSED __attribute__((unused))
46
- #else
47
- #define DDTRACE_UNUSED
48
- #endif
49
-
50
42
  // NOTE: This is a hack that relies on the knowledge that on Ruby 3.2 the
51
43
  // RUBY_INTERNAL_THREAD_EVENT_READY and RUBY_INTERNAL_THREAD_EVENT_RESUMED events always get called on the thread they
52
44
  // are about. Thus, we can use our thread local storage hack to get this data, even though the event doesn't include it.
@@ -1,16 +1,10 @@
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
 
10
- #if (defined(HAVE_WORKING_RB_GC_FORCE_RECYCLE) && ! defined(NO_SEEN_OBJ_ID_FLAG))
11
- #define CAN_APPLY_GC_FORCE_RECYCLE_BUG_WORKAROUND
12
- #endif
13
-
14
8
  // Minimum age (in GC generations) of heap objects we want to include in heap
15
9
  // recorder iterations. Object with age 0 represent objects that have yet to undergo
16
10
  // a GC and, thus, may just be noise/trash at instant of iteration and are usually not
@@ -34,7 +28,6 @@ typedef struct {
34
28
  char *filename;
35
29
  int32_t line;
36
30
  } heap_frame;
37
- static st_index_t heap_frame_hash(heap_frame*, st_index_t seed);
38
31
 
39
32
  // A compact representation of a stacktrace for a heap allocation.
40
33
  //
@@ -117,17 +110,6 @@ static void object_record_free(object_record*);
117
110
  static VALUE object_record_inspect(object_record*);
118
111
  static object_record SKIPPED_RECORD = {0};
119
112
 
120
- // A wrapper around an object record that is in the process of being recorded and was not
121
- // yet committed.
122
- typedef struct {
123
- // Pointer to the (potentially partial) object_record containing metadata about an ongoing recording.
124
- // When NULL, this symbolizes an unstarted/invalid recording.
125
- object_record *object_record;
126
- // A flag to track whether we had to force set the RUBY_FL_SEEN_OBJ_ID flag on this object
127
- // as part of our workaround around rb_gc_force_recycle issues.
128
- bool did_recycle_workaround;
129
- } recording;
130
-
131
113
  struct heap_recorder {
132
114
  // Config
133
115
  // Whether the recorder should try to determine approximate sizes for tracked objects.
@@ -147,6 +129,9 @@ struct heap_recorder {
147
129
  // outside the GVL.
148
130
  // NOTE: This table has ownership of its object_records. The keys are longs and so are
149
131
  // passed as values.
132
+ //
133
+ // TODO: @ivoanjo We've evolved to actually never need to look up on object_records (we only insert and iterate),
134
+ // so right now this seems to be just a really really fancy self-resizing list/set.
150
135
  st_table *object_records;
151
136
 
152
137
  // Map[obj_id: long, record: object_record*]
@@ -169,7 +154,7 @@ struct heap_recorder {
169
154
  long last_update_ns;
170
155
 
171
156
  // Data for a heap recording that was started but not yet ended
172
- recording active_recording;
157
+ object_record *active_recording;
173
158
 
174
159
  // Reusable location array, implementing a flyweight pattern for things like iteration.
175
160
  ddog_prof_Location *reusable_locations;
@@ -214,7 +199,7 @@ static int st_object_record_update(st_data_t, st_data_t, st_data_t);
214
199
  static int st_object_records_iterate(st_data_t, st_data_t, st_data_t);
215
200
  static int st_object_records_debug(st_data_t key, st_data_t value, st_data_t extra);
216
201
  static int update_object_record_entry(st_data_t*, st_data_t*, st_data_t, int);
217
- static void commit_recording(heap_recorder*, heap_record*, recording);
202
+ static void commit_recording(heap_recorder *, heap_record *, object_record *active_recording);
218
203
  static VALUE end_heap_allocation_recording(VALUE end_heap_allocation_args);
219
204
  static void heap_recorder_update(heap_recorder *heap_recorder, bool full_update);
220
205
  static inline double ewma_stat(double previous, double current);
@@ -235,7 +220,7 @@ heap_recorder* heap_recorder_new(void) {
235
220
  recorder->object_records = st_init_numtable();
236
221
  recorder->object_records_snapshot = NULL;
237
222
  recorder->reusable_locations = ruby_xcalloc(MAX_FRAMES_LIMIT, sizeof(ddog_prof_Location));
238
- recorder->active_recording = (recording) {0};
223
+ recorder->active_recording = NULL;
239
224
  recorder->size_enabled = true;
240
225
  recorder->sample_rate = 1; // By default do no sampling on top of what allocation profiling already does
241
226
 
@@ -261,9 +246,9 @@ void heap_recorder_free(heap_recorder *heap_recorder) {
261
246
  st_foreach(heap_recorder->heap_records, st_heap_record_entry_free, 0);
262
247
  st_free_table(heap_recorder->heap_records);
263
248
 
264
- if (heap_recorder->active_recording.object_record != NULL && heap_recorder->active_recording.object_record != &SKIPPED_RECORD) {
249
+ if (heap_recorder->active_recording != NULL && heap_recorder->active_recording != &SKIPPED_RECORD) {
265
250
  // If there's a partial object record, clean it up as well
266
- object_record_free(heap_recorder->active_recording.object_record);
251
+ object_record_free(heap_recorder->active_recording);
267
252
  }
268
253
 
269
254
  ruby_xfree(heap_recorder->reusable_locations);
@@ -308,7 +293,7 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder) {
308
293
  //
309
294
  // There is one small caveat though: fork only preserves one thread and in a Ruby app, that
310
295
  // will be the thread holding on to the GVL. Since we support iteration on the heap recorder
311
- // outside of the GVL, any state specific to that interaction may be incosistent after fork
296
+ // outside of the GVL, any state specific to that interaction may be inconsistent after fork
312
297
  // (e.g. an acquired lock for thread safety). Iteration operates on object_records_snapshot
313
298
  // though and that one will be updated on next heap_recorder_prepare_iteration so we really
314
299
  // only need to finish any iteration that might have been left unfinished.
@@ -320,18 +305,24 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder) {
320
305
  heap_recorder->stats_lifetime = (struct stats_lifetime) {0};
321
306
  }
322
307
 
323
- void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice *alloc_class) {
308
+ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice alloc_class) {
324
309
  if (heap_recorder == NULL) {
325
310
  return;
326
311
  }
327
312
 
328
- if (heap_recorder->active_recording.object_record != NULL) {
313
+ if (heap_recorder->active_recording != NULL) {
329
314
  rb_raise(rb_eRuntimeError, "Detected consecutive heap allocation recording starts without end.");
330
315
  }
331
316
 
332
- if (heap_recorder->num_recordings_skipped + 1 < heap_recorder->sample_rate) {
333
- heap_recorder->active_recording.object_record = &SKIPPED_RECORD;
334
- heap_recorder->num_recordings_skipped++;
317
+ if (++heap_recorder->num_recordings_skipped < heap_recorder->sample_rate ||
318
+ #ifdef NO_IMEMO_OBJECT_ID
319
+ // On Ruby 3.5, we can't ask the object_id from IMEMOs (https://github.com/ruby/ruby/pull/13347)
320
+ RB_BUILTIN_TYPE(new_obj) == RUBY_T_IMEMO
321
+ #else
322
+ false
323
+ #endif
324
+ ) {
325
+ heap_recorder->active_recording = &SKIPPED_RECORD;
335
326
  return;
336
327
  }
337
328
 
@@ -342,47 +333,15 @@ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj
342
333
  rb_raise(rb_eRuntimeError, "Detected a bignum object id. These are not supported by heap profiling.");
343
334
  }
344
335
 
345
- bool did_recycle_workaround = false;
346
-
347
- #ifdef CAN_APPLY_GC_FORCE_RECYCLE_BUG_WORKAROUND
348
- // If we are in a ruby version that has a working rb_gc_force_recycle implementation,
349
- // its usage may lead to an object being re-used outside of the typical GC cycle.
350
- //
351
- // This re-use is in theory invisible to us unless we're lucky enough to sample both
352
- // the original object and the replacement that uses the recycled slot.
353
- //
354
- // In practice, we've observed (https://github.com/DataDog/dd-trace-rb/pull/3366)
355
- // that non-noop implementations of rb_gc_force_recycle have an implementation bug
356
- // which results in the object that re-used the recycled slot inheriting the same
357
- // object id without setting the FL_SEEN_OBJ_ID flag. We rely on this knowledge to
358
- // "observe" implicit frees when an object we are tracking is force-recycled.
359
- //
360
- // However, it may happen that we start tracking a new object and that object was
361
- // allocated on a recycled slot. Due to the bug, this object would be missing the
362
- // FL_SEEN_OBJ_ID flag even though it was not recycled itself. If we left it be,
363
- // when we're doing our liveness check, the absence of the flag would trigger our
364
- // implicit free workaround and the object would be inferred as recycled even though
365
- // it might still be alive.
366
- //
367
- // Thus, if we detect that this new allocation is already missing the flag at the start
368
- // of the heap allocation recording, we force-set it. This should be safe since we
369
- // just called rb_obj_id on it above and the expectation is that any flaggable object
370
- // that goes through it ends up with the flag set (as evidenced by the GC_ASSERT
371
- // lines in https://github.com/ruby/ruby/blob/4a8d7246d15b2054eacb20f8ab3d29d39a3e7856/gc.c#L4050C14-L4050C14).
372
- if (RB_FL_ABLE(new_obj) && !RB_FL_TEST(new_obj, RUBY_FL_SEEN_OBJ_ID)) {
373
- RB_FL_SET(new_obj, RUBY_FL_SEEN_OBJ_ID);
374
- did_recycle_workaround = true;
336
+ heap_recorder->active_recording = object_record_new(
337
+ FIX2LONG(ruby_obj_id),
338
+ NULL,
339
+ (live_object_data) {
340
+ .weight = weight * heap_recorder->sample_rate,
341
+ .class = string_from_char_slice(alloc_class),
342
+ .alloc_gen = rb_gc_count(),
375
343
  }
376
- #endif
377
-
378
- heap_recorder->active_recording = (recording) {
379
- .object_record = object_record_new(FIX2LONG(ruby_obj_id), NULL, (live_object_data) {
380
- .weight = weight * heap_recorder->sample_rate,
381
- .class = alloc_class != NULL ? string_from_char_slice(*alloc_class) : NULL,
382
- .alloc_gen = rb_gc_count(),
383
- }),
384
- .did_recycle_workaround = did_recycle_workaround,
385
- };
344
+ );
386
345
  }
387
346
 
388
347
  // end_heap_allocation_recording_with_rb_protect gets called while the stack_recorder is holding one of the profile
@@ -390,6 +349,10 @@ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj
390
349
  // with an rb_protect.
391
350
  __attribute__((warn_unused_result))
392
351
  int end_heap_allocation_recording_with_rb_protect(struct heap_recorder *heap_recorder, ddog_prof_Slice_Location locations) {
352
+ if (heap_recorder == NULL) {
353
+ return 0;
354
+ }
355
+
393
356
  int exception_state;
394
357
  struct end_heap_allocation_args end_heap_allocation_args = {
395
358
  .heap_recorder = heap_recorder,
@@ -405,22 +368,18 @@ static VALUE end_heap_allocation_recording(VALUE end_heap_allocation_args) {
405
368
  struct heap_recorder *heap_recorder = args->heap_recorder;
406
369
  ddog_prof_Slice_Location locations = args->locations;
407
370
 
408
- if (heap_recorder == NULL) {
409
- return Qnil;
410
- }
371
+ object_record *active_recording = heap_recorder->active_recording;
411
372
 
412
- recording active_recording = heap_recorder->active_recording;
413
-
414
- if (active_recording.object_record == NULL) {
373
+ if (active_recording == NULL) {
415
374
  // Recording ended without having been started?
416
375
  rb_raise(rb_eRuntimeError, "Ended a heap recording that was not started");
417
376
  }
418
377
  // From now on, mark the global active recording as invalid so we can short-circuit at any point
419
378
  // and not end up with a still active recording. the local active_recording still holds the
420
379
  // data required for committing though.
421
- heap_recorder->active_recording = (recording) {0};
380
+ heap_recorder->active_recording = NULL;
422
381
 
423
- if (active_recording.object_record == &SKIPPED_RECORD) { // special marker when we decided to skip due to sampling
382
+ if (active_recording == &SKIPPED_RECORD) { // special marker when we decided to skip due to sampling
424
383
  return Qnil;
425
384
  }
426
385
 
@@ -685,41 +644,6 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
685
644
 
686
645
  // If we got this far, then we found a valid live object for the tracked id.
687
646
 
688
- #ifdef CAN_APPLY_GC_FORCE_RECYCLE_BUG_WORKAROUND
689
- // If we are in a ruby version that has a working rb_gc_force_recycle implementation,
690
- // its usage may lead to an object being re-used outside of the typical GC cycle.
691
- //
692
- // This re-use is in theory invisible to us and would mean that the ref from which we
693
- // collected the object_record metadata may not be the same as the current ref and
694
- // thus any further reporting would be innacurately attributed to stale metadata.
695
- //
696
- // In practice, there is a way for us to notice that this happened because of a bug
697
- // in the implementation of rb_gc_force_recycle. Our heap profiler relies on object
698
- // ids and id2ref to detect whether objects are still alive. Turns out that when an
699
- // object with an id is re-used via rb_gc_force_recycle, it will "inherit" the ID
700
- // of the old object but it will NOT have the FL_SEEN_OBJ_ID as per the experiment
701
- // in https://github.com/DataDog/dd-trace-rb/pull/3360#discussion_r1442823517
702
- //
703
- // Thus, if we detect that the ref we just resolved above is missing this flag, we can
704
- // safely say re-use happened and thus treat it as an implicit free of the object
705
- // we were tracking (the original one which got recycled).
706
- if (RB_FL_ABLE(ref) && !RB_FL_TEST(ref, RUBY_FL_SEEN_OBJ_ID)) {
707
-
708
- // NOTE: We don't really need to set this flag for heap recorder to work correctly
709
- // but doing so partially mitigates a bug in runtimes with working rb_gc_force_recycle
710
- // which leads to broken invariants and leaking of entries in obj_to_id and id_to_obj
711
- // tables in objspace. We already do the same thing when we sample a recycled object,
712
- // here we apply it as well to objects that replace recycled objects that were being
713
- // tracked. More details in https://github.com/DataDog/dd-trace-rb/pull/3366
714
- RB_FL_SET(ref, RUBY_FL_SEEN_OBJ_ID);
715
-
716
- on_committed_object_record_cleanup(recorder, record);
717
- recorder->stats_last_update.objects_dead++;
718
- return ST_DELETE;
719
- }
720
-
721
- #endif
722
-
723
647
  if (
724
648
  recorder->size_enabled &&
725
649
  recorder->update_include_old && // We only update sizes when doing a full update
@@ -732,6 +656,10 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
732
656
  record->object_data.is_frozen = RB_OBJ_FROZEN(ref);
733
657
  }
734
658
 
659
+ // Ensure that ref is kept on the stack so the Ruby garbage collector does not try to clean up the object before this
660
+ // point.
661
+ RB_GC_GUARD(ref);
662
+
735
663
  recorder->stats_last_update.objects_alive++;
736
664
  if (record->object_data.is_frozen) {
737
665
  recorder->stats_last_update.objects_frozen++;
@@ -757,7 +685,7 @@ static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t val
757
685
  for (uint16_t i = 0; i < stack->frames_len; i++) {
758
686
  const heap_frame *frame = &stack->frames[i];
759
687
  locations[i] = (ddog_prof_Location) {
760
- .mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C("")},
688
+ .mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C(""), .build_id_id = {}},
761
689
  .function = {
762
690
  .name = {.ptr = frame->name, .len = strlen(frame->name)},
763
691
  .filename = {.ptr = frame->filename, .len = strlen(frame->filename)},
@@ -770,6 +698,7 @@ static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t val
770
698
  iteration_data.object_data = record->object_data;
771
699
  iteration_data.locations = (ddog_prof_Slice_Location) {.ptr = locations, .len = stack->frames_len};
772
700
 
701
+ // This is expected to be StackRecorder's add_heap_sample_to_active_profile_without_gvl
773
702
  if (!context->for_each_callback(iteration_data, context->for_each_callback_extra_arg)) {
774
703
  return ST_STOP;
775
704
  }
@@ -787,55 +716,35 @@ static int st_object_records_debug(DDTRACE_UNUSED st_data_t key, st_data_t value
787
716
  return ST_CONTINUE;
788
717
  }
789
718
 
790
- // Struct holding data required for an update operation on heap_records
791
- typedef struct {
792
- // [in] The recording containing the new object record we want to add.
793
- // NOTE: Transfer of ownership of the contained object record is assumed, do not re-use it after call to ::update_object_record_entry
794
- recording recording;
795
-
796
- // [in] The heap recorder where the update is happening.
797
- heap_recorder *heap_recorder;
798
- } object_record_update_data;
799
-
800
- static int update_object_record_entry(DDTRACE_UNUSED st_data_t *key, st_data_t *value, st_data_t data, int existing) {
801
- object_record_update_data *update_data = (object_record_update_data*) data;
802
- recording recording = update_data->recording;
803
- object_record *new_object_record = recording.object_record;
804
- if (existing) {
805
- object_record *existing_record = (object_record*) (*value);
806
- if (recording.did_recycle_workaround) {
807
- // In this case, it's possible for an object id to be re-used and we were lucky enough to have
808
- // sampled both the original object and the replacement so cleanup the old one and replace it with
809
- // the new object_record (i.e. treat this as a combined free+allocation).
810
- on_committed_object_record_cleanup(update_data->heap_recorder, existing_record);
811
- } else {
812
- // This is not supposed to happen, raising...
813
- VALUE existing_inspect = object_record_inspect(existing_record);
814
- VALUE new_inspect = object_record_inspect(new_object_record);
815
- rb_raise(rb_eRuntimeError, "Object ids are supposed to be unique. We got 2 allocation recordings with "
816
- "the same id. previous=%"PRIsVALUE" new=%"PRIsVALUE, existing_inspect, new_inspect);
817
- }
719
+ static int update_object_record_entry(DDTRACE_UNUSED st_data_t *key, st_data_t *value, st_data_t new_object_record, int existing) {
720
+ if (!existing) {
721
+ (*value) = (st_data_t) new_object_record; // Expected to be a `object_record *`
722
+ } else {
723
+ // If key already existed, we don't touch the existing value, so it can be used for diagnostics
818
724
  }
819
- // Always carry on with the update, we want the new record to be there at the end
820
- (*value) = (st_data_t) new_object_record;
821
725
  return ST_CONTINUE;
822
726
  }
823
727
 
824
- static void commit_recording(heap_recorder *heap_recorder, heap_record *heap_record, recording recording) {
728
+ static void commit_recording(heap_recorder *heap_recorder, heap_record *heap_record, object_record *active_recording) {
825
729
  // Link the object record with the corresponding heap record. This was the last remaining thing we
826
730
  // needed to fully build the object_record.
827
- recording.object_record->heap_record = heap_record;
731
+ active_recording->heap_record = heap_record;
828
732
  if (heap_record->num_tracked_objects == UINT32_MAX) {
829
733
  rb_raise(rb_eRuntimeError, "Reached maximum number of tracked objects for heap record");
830
734
  }
831
735
  heap_record->num_tracked_objects++;
832
736
 
833
- // Update object_records with the data for this new recording
834
- object_record_update_data update_data = (object_record_update_data) {
835
- .heap_recorder = heap_recorder,
836
- .recording = recording,
837
- };
838
- st_update(heap_recorder->object_records, recording.object_record->obj_id, update_object_record_entry, (st_data_t) &update_data);
737
+ int existing_error = st_update(heap_recorder->object_records, active_recording->obj_id, update_object_record_entry, (st_data_t) active_recording);
738
+ if (existing_error) {
739
+ object_record *existing_record = NULL;
740
+ st_lookup(heap_recorder->object_records, active_recording->obj_id, (st_data_t *) &existing_record);
741
+ if (existing_record == NULL) rb_raise(rb_eRuntimeError, "Unexpected NULL when reading existing record");
742
+
743
+ VALUE existing_inspect = object_record_inspect(existing_record);
744
+ VALUE new_inspect = object_record_inspect(active_recording);
745
+ rb_raise(rb_eRuntimeError, "Object ids are supposed to be unique. We got 2 allocation recordings with "
746
+ "the same id. previous={%"PRIsVALUE"} new={%"PRIsVALUE"}", existing_inspect, new_inspect);
747
+ }
839
748
  }
840
749
 
841
750
  // Struct holding data required for an update operation on heap_records
@@ -945,7 +854,6 @@ void heap_record_free(heap_record *record) {
945
854
  ruby_xfree(record);
946
855
  }
947
856
 
948
-
949
857
  // =================
950
858
  // Object Record API
951
859
  // =================
@@ -995,25 +903,6 @@ VALUE object_record_inspect(object_record *record) {
995
903
  // ==============
996
904
  // Heap Frame API
997
905
  // ==============
998
- int heap_frame_cmp(heap_frame *f1, heap_frame *f2) {
999
- int line_diff = (int) (f1->line - f2->line);
1000
- if (line_diff != 0) {
1001
- return line_diff;
1002
- }
1003
- int cmp = strcmp(f1->name, f2->name);
1004
- if (cmp != 0) {
1005
- return cmp;
1006
- }
1007
- return strcmp(f1->filename, f2->filename);
1008
- }
1009
-
1010
- // TODO: Research potential performance improvements around hashing stuff here
1011
- // once we have a benchmarking suite.
1012
- // Example: Each call to st_hash is calling murmur_finish and we may want
1013
- // to only finish once per structure, not per field?
1014
- // Example: There may be a more efficient hashing for line that is not the
1015
- // generic st_hash algorithm?
1016
-
1017
906
  // WARN: Must be kept in-sync with ::char_slice_hash
1018
907
  st_index_t string_hash(char *str, st_index_t seed) {
1019
908
  return st_hash(str, strlen(str), seed);
@@ -1049,9 +938,7 @@ st_index_t ddog_location_hash(ddog_prof_Location location, st_index_t seed) {
1049
938
  heap_stack* heap_stack_new(ddog_prof_Slice_Location locations) {
1050
939
  uint16_t frames_len = locations.len;
1051
940
  if (frames_len > MAX_FRAMES_LIMIT) {
1052
- // This should not be happening anyway since MAX_FRAMES_LIMIT should be shared with
1053
- // the stacktrace construction mechanism. If it happens, lets just raise. This should
1054
- // be safe since only allocate with the GVL anyway.
941
+ // This is not expected as MAX_FRAMES_LIMIT is shared with the stacktrace construction mechanism
1055
942
  rb_raise(rb_eRuntimeError, "Found stack with more than %d frames (%d)", MAX_FRAMES_LIMIT, frames_len);
1056
943
  }
1057
944
  heap_stack *stack = ruby_xcalloc(1, sizeof(heap_stack) + frames_len * sizeof(heap_frame));
@@ -17,7 +17,7 @@
17
17
  typedef struct heap_recorder heap_recorder;
18
18
 
19
19
  // Extra data associated with each live object being tracked.
20
- typedef struct live_object_data {
20
+ typedef struct {
21
21
  // The weight of this object from a sampling perspective.
22
22
  //
23
23
  // A notion of weight is preserved for each tracked object to allow for an approximate
@@ -105,7 +105,7 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder);
105
105
  // The sampling weight of this object.
106
106
  //
107
107
  // WARN: It needs to be paired with a ::end_heap_allocation_recording call.
108
- void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice *alloc_class);
108
+ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice alloc_class);
109
109
 
110
110
  // End a previously started heap allocation recording on the heap recorder.
111
111
  //