datadog 2.20.0 → 2.26.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 (310) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +212 -1
  3. data/README.md +0 -1
  4. data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
  5. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +93 -23
  6. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
  7. data/ext/datadog_profiling_native_extension/collectors_stack.c +21 -5
  8. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
  9. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
  10. data/ext/datadog_profiling_native_extension/extconf.rb +9 -4
  11. data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
  12. data/ext/datadog_profiling_native_extension/http_transport.c +1 -0
  13. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +12 -0
  14. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +4 -0
  15. data/ext/datadog_profiling_native_extension/profiling.c +2 -0
  16. data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
  17. data/ext/libdatadog_api/ddsketch.c +106 -0
  18. data/ext/libdatadog_api/feature_flags.c +554 -0
  19. data/ext/libdatadog_api/feature_flags.h +5 -0
  20. data/ext/libdatadog_api/init.c +5 -0
  21. data/ext/libdatadog_api/library_config.c +34 -25
  22. data/ext/libdatadog_api/process_discovery.c +24 -18
  23. data/ext/libdatadog_extconf_helpers.rb +1 -1
  24. data/lib/datadog/ai_guard/api_client.rb +82 -0
  25. data/lib/datadog/ai_guard/component.rb +42 -0
  26. data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
  27. data/lib/datadog/ai_guard/configuration/settings.rb +98 -0
  28. data/lib/datadog/ai_guard/configuration.rb +11 -0
  29. data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
  30. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +34 -0
  31. data/lib/datadog/ai_guard/evaluation/request.rb +81 -0
  32. data/lib/datadog/ai_guard/evaluation/result.rb +43 -0
  33. data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
  34. data/lib/datadog/ai_guard/evaluation.rb +72 -0
  35. data/lib/datadog/ai_guard/ext.rb +16 -0
  36. data/lib/datadog/ai_guard.rb +153 -0
  37. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
  38. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +59 -0
  39. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +29 -0
  40. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
  41. data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
  42. data/lib/datadog/appsec/api_security/route_extractor.rb +26 -5
  43. data/lib/datadog/appsec/api_security/sampler.rb +7 -4
  44. data/lib/datadog/appsec/assets/blocked.html +8 -0
  45. data/lib/datadog/appsec/assets/blocked.json +1 -1
  46. data/lib/datadog/appsec/assets/blocked.text +3 -1
  47. data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
  48. data/lib/datadog/appsec/assets/waf_rules/recommended.json +359 -4
  49. data/lib/datadog/appsec/assets/waf_rules/strict.json +43 -2
  50. data/lib/datadog/appsec/assets.rb +1 -1
  51. data/lib/datadog/appsec/autoload.rb +1 -1
  52. data/lib/datadog/appsec/compressed_json.rb +1 -1
  53. data/lib/datadog/appsec/configuration/settings.rb +9 -0
  54. data/lib/datadog/appsec/context.rb +2 -1
  55. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
  56. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +3 -2
  57. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +3 -1
  58. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +3 -1
  59. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -4
  60. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +5 -1
  61. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -2
  62. data/lib/datadog/appsec/contrib/rails/patcher.rb +30 -0
  63. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +3 -1
  64. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +10 -4
  65. data/lib/datadog/appsec/event.rb +12 -14
  66. data/lib/datadog/appsec/metrics/collector.rb +19 -3
  67. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +2 -1
  68. data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -4
  69. data/lib/datadog/appsec/remote.rb +34 -25
  70. data/lib/datadog/appsec/response.rb +18 -4
  71. data/lib/datadog/appsec/security_engine/engine.rb +3 -3
  72. data/lib/datadog/appsec/security_engine/result.rb +29 -9
  73. data/lib/datadog/appsec/security_engine/runner.rb +19 -9
  74. data/lib/datadog/appsec/security_event.rb +5 -7
  75. data/lib/datadog/core/configuration/agent_settings_resolver.rb +4 -4
  76. data/lib/datadog/core/configuration/components.rb +59 -11
  77. data/lib/datadog/core/configuration/config_helper.rb +100 -0
  78. data/lib/datadog/core/configuration/deprecations.rb +36 -0
  79. data/lib/datadog/core/configuration/ext.rb +0 -1
  80. data/lib/datadog/core/configuration/option.rb +38 -43
  81. data/lib/datadog/core/configuration/option_definition.rb +4 -11
  82. data/lib/datadog/core/configuration/options.rb +9 -10
  83. data/lib/datadog/core/configuration/settings.rb +38 -9
  84. data/lib/datadog/core/configuration/stable_config.rb +10 -0
  85. data/lib/datadog/core/configuration/supported_configurations.rb +373 -0
  86. data/lib/datadog/core/configuration.rb +2 -2
  87. data/lib/datadog/core/ddsketch.rb +19 -0
  88. data/lib/datadog/core/deprecations.rb +2 -2
  89. data/lib/datadog/core/environment/cgroup.rb +52 -25
  90. data/lib/datadog/core/environment/container.rb +140 -46
  91. data/lib/datadog/core/environment/ext.rb +7 -2
  92. data/lib/datadog/core/environment/git.rb +2 -2
  93. data/lib/datadog/core/environment/process.rb +87 -0
  94. data/lib/datadog/core/environment/variable_helpers.rb +3 -3
  95. data/lib/datadog/core/environment/yjit.rb +2 -1
  96. data/lib/datadog/core/error.rb +6 -6
  97. data/lib/datadog/core/feature_flags.rb +61 -0
  98. data/lib/datadog/core/metrics/client.rb +2 -2
  99. data/lib/datadog/core/pin.rb +8 -8
  100. data/lib/datadog/core/process_discovery/tracer_memfd.rb +2 -4
  101. data/lib/datadog/core/process_discovery.rb +48 -23
  102. data/lib/datadog/core/rate_limiter.rb +9 -1
  103. data/lib/datadog/core/remote/client/capabilities.rb +7 -0
  104. data/lib/datadog/core/remote/client.rb +14 -6
  105. data/lib/datadog/core/remote/component.rb +10 -10
  106. data/lib/datadog/core/remote/configuration/content.rb +15 -2
  107. data/lib/datadog/core/remote/configuration/digest.rb +14 -7
  108. data/lib/datadog/core/remote/configuration/repository.rb +1 -1
  109. data/lib/datadog/core/remote/configuration/target.rb +13 -6
  110. data/lib/datadog/core/remote/transport/config.rb +4 -25
  111. data/lib/datadog/core/remote/transport/http/config.rb +10 -50
  112. data/lib/datadog/core/remote/transport/http/negotiation.rb +14 -44
  113. data/lib/datadog/core/remote/transport/http.rb +15 -24
  114. data/lib/datadog/core/remote/transport/negotiation.rb +8 -33
  115. data/lib/datadog/core/remote/worker.rb +25 -37
  116. data/lib/datadog/core/runtime/ext.rb +0 -1
  117. data/lib/datadog/core/runtime/metrics.rb +11 -1
  118. data/lib/datadog/core/semaphore.rb +1 -4
  119. data/lib/datadog/core/tag_builder.rb +0 -4
  120. data/lib/datadog/core/tag_normalizer.rb +84 -0
  121. data/lib/datadog/core/telemetry/component.rb +69 -15
  122. data/lib/datadog/core/telemetry/emitter.rb +6 -6
  123. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
  124. data/lib/datadog/core/telemetry/event/app_started.rb +89 -51
  125. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  126. data/lib/datadog/core/telemetry/event.rb +1 -0
  127. data/lib/datadog/core/telemetry/logger.rb +2 -2
  128. data/lib/datadog/core/telemetry/logging.rb +2 -8
  129. data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
  130. data/lib/datadog/core/telemetry/request.rb +17 -3
  131. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +3 -34
  132. data/lib/datadog/core/telemetry/transport/http.rb +21 -16
  133. data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -11
  134. data/lib/datadog/core/telemetry/worker.rb +88 -32
  135. data/lib/datadog/core/transport/ext.rb +2 -0
  136. data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
  137. data/lib/datadog/core/transport/http/api/instance.rb +4 -21
  138. data/lib/datadog/core/transport/http/builder.rb +9 -5
  139. data/lib/datadog/core/transport/http/client.rb +80 -0
  140. data/lib/datadog/core/transport/http.rb +22 -19
  141. data/lib/datadog/core/transport/response.rb +15 -1
  142. data/lib/datadog/core/transport/transport.rb +90 -0
  143. data/lib/datadog/core/utils/array.rb +29 -0
  144. data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
  145. data/lib/datadog/core/utils/network.rb +22 -1
  146. data/lib/datadog/core/utils/only_once_successful.rb +8 -2
  147. data/lib/datadog/core/utils/safe_dup.rb +2 -2
  148. data/lib/datadog/core/utils/sequence.rb +2 -0
  149. data/lib/datadog/core/utils/time.rb +1 -1
  150. data/lib/datadog/core/utils.rb +2 -0
  151. data/lib/datadog/core/workers/async.rb +10 -1
  152. data/lib/datadog/core/workers/interval_loop.rb +44 -3
  153. data/lib/datadog/core/workers/polling.rb +2 -0
  154. data/lib/datadog/core/workers/queue.rb +100 -1
  155. data/lib/datadog/core.rb +2 -0
  156. data/lib/datadog/data_streams/configuration/settings.rb +49 -0
  157. data/lib/datadog/data_streams/configuration.rb +11 -0
  158. data/lib/datadog/data_streams/ext.rb +11 -0
  159. data/lib/datadog/data_streams/extensions.rb +16 -0
  160. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  161. data/lib/datadog/data_streams/processor.rb +509 -0
  162. data/lib/datadog/data_streams/transport/http/stats.rb +52 -0
  163. data/lib/datadog/data_streams/transport/http.rb +40 -0
  164. data/lib/datadog/data_streams/transport/stats.rb +46 -0
  165. data/lib/datadog/data_streams.rb +100 -0
  166. data/lib/datadog/di/boot.rb +7 -3
  167. data/lib/datadog/di/component.rb +14 -16
  168. data/lib/datadog/di/context.rb +70 -0
  169. data/lib/datadog/di/contrib/active_record.rb +30 -5
  170. data/lib/datadog/di/el/compiler.rb +168 -0
  171. data/lib/datadog/di/el/evaluator.rb +159 -0
  172. data/lib/datadog/di/el/expression.rb +42 -0
  173. data/lib/datadog/di/el.rb +5 -0
  174. data/lib/datadog/di/error.rb +34 -0
  175. data/lib/datadog/di/instrumenter.rb +189 -55
  176. data/lib/datadog/di/logger.rb +2 -2
  177. data/lib/datadog/di/probe.rb +55 -15
  178. data/lib/datadog/di/probe_builder.rb +41 -2
  179. data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
  180. data/lib/datadog/di/probe_file_loader.rb +1 -1
  181. data/lib/datadog/di/probe_manager.rb +50 -35
  182. data/lib/datadog/di/probe_notification_builder.rb +121 -70
  183. data/lib/datadog/di/probe_notifier_worker.rb +5 -5
  184. data/lib/datadog/di/proc_responder.rb +32 -0
  185. data/lib/datadog/di/remote.rb +89 -84
  186. data/lib/datadog/di/serializer.rb +151 -7
  187. data/lib/datadog/di/transport/diagnostics.rb +8 -36
  188. data/lib/datadog/di/transport/http/diagnostics.rb +1 -33
  189. data/lib/datadog/di/transport/http/input.rb +1 -33
  190. data/lib/datadog/di/transport/http.rb +32 -17
  191. data/lib/datadog/di/transport/input.rb +67 -34
  192. data/lib/datadog/di.rb +61 -5
  193. data/lib/datadog/error_tracking/filters.rb +2 -2
  194. data/lib/datadog/kit/appsec/events/v2.rb +2 -3
  195. data/lib/datadog/open_feature/component.rb +60 -0
  196. data/lib/datadog/open_feature/configuration.rb +27 -0
  197. data/lib/datadog/open_feature/evaluation_engine.rb +70 -0
  198. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  199. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  200. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  201. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  202. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  203. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  204. data/lib/datadog/open_feature/ext.rb +14 -0
  205. data/lib/datadog/open_feature/native_evaluator.rb +38 -0
  206. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  207. data/lib/datadog/open_feature/provider.rb +141 -0
  208. data/lib/datadog/open_feature/remote.rb +67 -0
  209. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  210. data/lib/datadog/open_feature/transport.rb +70 -0
  211. data/lib/datadog/open_feature.rb +19 -0
  212. data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
  213. data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
  214. data/lib/datadog/opentelemetry/metrics.rb +117 -0
  215. data/lib/datadog/opentelemetry/sdk/configurator.rb +26 -2
  216. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +35 -0
  217. data/lib/datadog/opentelemetry.rb +3 -0
  218. data/lib/datadog/profiling/collectors/code_provenance.rb +41 -7
  219. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +3 -2
  220. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  221. data/lib/datadog/profiling/collectors/info.rb +6 -5
  222. data/lib/datadog/profiling/component.rb +12 -11
  223. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +18 -0
  224. data/lib/datadog/profiling/ext.rb +2 -1
  225. data/lib/datadog/profiling/http_transport.rb +5 -2
  226. data/lib/datadog/profiling/profiler.rb +4 -0
  227. data/lib/datadog/profiling/tag_builder.rb +36 -3
  228. data/lib/datadog/profiling/tasks/exec.rb +2 -2
  229. data/lib/datadog/profiling.rb +1 -2
  230. data/lib/datadog/single_step_instrument.rb +1 -1
  231. data/lib/datadog/tracing/component.rb +6 -17
  232. data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
  233. data/lib/datadog/tracing/configuration/ext.rb +9 -3
  234. data/lib/datadog/tracing/configuration/settings.rb +89 -10
  235. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
  236. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
  237. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  238. data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
  239. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
  240. data/lib/datadog/tracing/contrib/component.rb +2 -2
  241. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
  242. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
  243. data/lib/datadog/tracing/contrib/extensions.rb +10 -2
  244. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
  245. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
  246. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
  247. data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
  248. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +84 -43
  249. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
  250. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
  251. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
  252. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  253. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  254. data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
  255. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  256. data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
  257. data/lib/datadog/tracing/contrib/karafka/patcher.rb +35 -4
  258. data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
  259. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -0
  260. data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
  261. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +7 -1
  262. data/lib/datadog/tracing/contrib/rails/ext.rb +2 -1
  263. data/lib/datadog/tracing/contrib/rails/integration.rb +1 -1
  264. data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
  265. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
  266. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
  267. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
  268. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +1 -1
  269. data/lib/datadog/tracing/contrib/status_range_matcher.rb +9 -1
  270. data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +3 -1
  271. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
  272. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
  273. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
  274. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  275. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  276. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +49 -0
  277. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  278. data/lib/datadog/tracing/contrib/waterdrop.rb +41 -0
  279. data/lib/datadog/tracing/contrib.rb +1 -0
  280. data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
  281. data/lib/datadog/tracing/distributed/baggage.rb +3 -2
  282. data/lib/datadog/tracing/metadata/ext.rb +9 -1
  283. data/lib/datadog/tracing/remote.rb +1 -9
  284. data/lib/datadog/tracing/sampling/priority_sampler.rb +3 -1
  285. data/lib/datadog/tracing/span.rb +1 -1
  286. data/lib/datadog/tracing/span_event.rb +2 -2
  287. data/lib/datadog/tracing/span_operation.rb +20 -9
  288. data/lib/datadog/tracing/trace_operation.rb +44 -6
  289. data/lib/datadog/tracing/tracer.rb +42 -16
  290. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  291. data/lib/datadog/tracing/transport/http/traces.rb +2 -50
  292. data/lib/datadog/tracing/transport/http.rb +15 -9
  293. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  294. data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
  295. data/lib/datadog/tracing/transport/traces.rb +9 -71
  296. data/lib/datadog/tracing/workers/trace_writer.rb +5 -0
  297. data/lib/datadog/tracing/writer.rb +1 -0
  298. data/lib/datadog/version.rb +2 -2
  299. data/lib/datadog.rb +3 -0
  300. metadata +110 -24
  301. data/ext/libdatadog_api/macos_development.md +0 -26
  302. data/lib/datadog/core/remote/transport/http/api.rb +0 -53
  303. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  304. data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
  305. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  306. data/lib/datadog/core/transport/http/api/spec.rb +0 -36
  307. data/lib/datadog/di/transport/http/api.rb +0 -42
  308. data/lib/datadog/di/transport/http/client.rb +0 -47
  309. data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
  310. data/lib/datadog/tracing/transport/http/api.rb +0 -44
@@ -0,0 +1,239 @@
1
+ // NOTE: This file is a part of the profiling native extension even though the
2
+ // runtime stacks feature is consumed by the crashtracker. The profiling
3
+ // extension already carries all the Ruby VM private header access and build
4
+ // plumbing required to safely poke at internal structures. Sharing that setup
5
+ // avoids duplicating another native extension with the same (fragile) access
6
+ // patterns, and keeps the overall install/build surface area smaller.
7
+ //
8
+ // This also means that this functionality is depend on the profiling native extension
9
+ // being available and built
10
+ #include "extconf.h"
11
+
12
+ #if defined(__linux__)
13
+
14
+ #include <datadog/crashtracker.h>
15
+ #include "datadog_ruby_common.h"
16
+ #include "private_vm_api_access.h"
17
+ #include <sys/mman.h>
18
+ #include <unistd.h>
19
+ #include <errno.h>
20
+ #include <string.h>
21
+
22
+ static const rb_data_type_t *crashtracker_thread_data_type = NULL;
23
+
24
+ static void ruby_runtime_stack_callback(
25
+ void (*emit_frame)(const ddog_crasht_RuntimeStackFrame*)
26
+ );
27
+
28
+ // Use a fixed, preallocated buffer for crash-time runtime stacks to avoid
29
+ // heap allocation in the signal/crash path.
30
+ #define RUNTIME_STACK_MAX_FRAMES 512
31
+ static frame_info runtime_stack_buffer[RUNTIME_STACK_MAX_FRAMES];
32
+
33
+ #if defined(__x86_64__)
34
+ # define SYS_MINCORE 0x1B
35
+ #elif defined(__aarch64__)
36
+ # define SYS_MINCORE 0xE8
37
+ #endif
38
+
39
+ long syscall(long number, ...);
40
+
41
+ // align down to power of two
42
+ static inline uintptr_t align_down(uintptr_t x, uintptr_t align) {
43
+ return x & ~(align - 1u);
44
+ }
45
+
46
+ static uintptr_t cached_page_size = 0;
47
+
48
+ // TODO: This function is not necessarily Ruby specific. This will be moved to
49
+ // `libdatadog` in the future as a shared utility function.
50
+ static inline bool is_pointer_readable(const void *ptr, size_t size) {
51
+ if (!ptr || size == 0) return false;
52
+
53
+ uintptr_t page_size = cached_page_size;
54
+
55
+ const uintptr_t start = align_down((uintptr_t)ptr, page_size);
56
+ const uintptr_t end = ((uintptr_t)ptr + size - 1u);
57
+ const uintptr_t last = align_down(end, page_size);
58
+
59
+ // Number of pages spanned
60
+ size_t pages = 1u + (last != start);
61
+ if (pages > 2u) pages = 2u;
62
+
63
+ unsigned char vec[2];
64
+
65
+ int retries = 5;
66
+ for (;;) {
67
+ size_t len = pages * (size_t)page_size;
68
+ long rc = syscall(SYS_MINCORE, (void*)start, len, vec);
69
+
70
+ if (rc == 0) {
71
+ return true;
72
+ }
73
+
74
+ int e = errno;
75
+ if (e == ENOMEM || e == EFAULT) {
76
+ return false;
77
+ }
78
+
79
+ if (e == EAGAIN && retries-- > 0) {
80
+ continue;
81
+ }
82
+
83
+ // Unknown errno, we assume mapped to avoid cascading faults in crash path
84
+ return true;
85
+ }
86
+ }
87
+
88
+ static inline ddog_CharSlice char_slice_from_cstr(const char *cstr) {
89
+ if (cstr == NULL) {
90
+ return (ddog_CharSlice){.ptr = NULL, .len = 0};
91
+ }
92
+ return (ddog_CharSlice){.ptr = cstr, .len = strlen(cstr)};
93
+ }
94
+
95
+ static ddog_CharSlice safe_string_value(VALUE str) {
96
+ if (str == Qnil) return DDOG_CHARSLICE_C("<nil>");
97
+
98
+ // Validate object and payload readability in one check
99
+ if (!is_pointer_readable((const void *)str, sizeof(struct RString))) return DDOG_CHARSLICE_C("<corrupted>");
100
+ if (!RB_TYPE_P(str, T_STRING)) return DDOG_CHARSLICE_C("<not_string>");
101
+
102
+ long len = RSTRING_LEN(str);
103
+ if (len < 0 || len > 1024) return DDOG_CHARSLICE_C("<length_over_limit>");
104
+
105
+ const char *ptr = RSTRING_PTR(str);
106
+ if (!ptr) return DDOG_CHARSLICE_C("<null>");
107
+
108
+ if (!is_pointer_readable(ptr, len > 0 ? len : 1)) return DDOG_CHARSLICE_C("<unreadable>");
109
+
110
+ return (ddog_CharSlice){.ptr = ptr, .len = (size_t)len};
111
+ }
112
+
113
+ static void emit_placeholder_frame(
114
+ void (*emit_frame)(const ddog_crasht_RuntimeStackFrame*),
115
+ const char *label
116
+ ) {
117
+ ddog_crasht_RuntimeStackFrame frame = {
118
+ .type_name = DDOG_CHARSLICE_C(""),
119
+ .function = char_slice_from_cstr(label),
120
+ .file = DDOG_CHARSLICE_C("<unknown>"),
121
+ .line = 0,
122
+ .column = 0
123
+ };
124
+ emit_frame(&frame);
125
+ }
126
+
127
+ // Collect the crashing thread's frames via ddtrace_rb_profile_frames into a static buffer, then emit
128
+ // them newest-first. If corruption is detected, emit placeholder frames so the crash report still
129
+ // completes. We lean on the Ruby VM helpers we already use for profiling and rely on crashtracker's
130
+ // safety nets so a failure here should not impact customers.
131
+ static void ruby_runtime_stack_callback(
132
+ void (*emit_frame)(const ddog_crasht_RuntimeStackFrame*)
133
+ ) {
134
+ // Grab the Ruby thread we crashed on; crashtracker only runs once.
135
+ VALUE current_thread = rb_thread_current();
136
+ if (current_thread == Qnil) return;
137
+
138
+ if (crashtracker_thread_data_type == NULL) return;
139
+
140
+ if (!rb_typeddata_is_kind_of(current_thread, crashtracker_thread_data_type)) {
141
+ emit_placeholder_frame(emit_frame, "<runtime stack not found>");
142
+ return;
143
+ }
144
+
145
+ // Use the profiling helper to gather frames into our static buffer.
146
+ int frame_count = ddtrace_rb_profile_frames(
147
+ current_thread,
148
+ 0,
149
+ RUNTIME_STACK_MAX_FRAMES,
150
+ runtime_stack_buffer
151
+ );
152
+
153
+ if (frame_count <= 0) {
154
+ emit_placeholder_frame(emit_frame, "<runtime stack not found>");
155
+ return;
156
+ }
157
+
158
+ for (int i = frame_count - 1; i >= 0; i--) {
159
+ frame_info *info = &runtime_stack_buffer[i];
160
+
161
+ if (info->is_ruby_frame) {
162
+ const void *iseq = (const void *)info->as.ruby_frame.iseq;
163
+ ddog_CharSlice function_slice = DDOG_CHARSLICE_C("<unknown>");
164
+ ddog_CharSlice file_slice = DDOG_CHARSLICE_C("<unknown>");
165
+
166
+ if (iseq && is_pointer_readable(iseq, sizeof_rb_iseq_t())) {
167
+ function_slice = safe_string_value(ddtrace_iseq_base_label(iseq));
168
+ file_slice = safe_string_value(ddtrace_iseq_path(iseq));
169
+ }
170
+
171
+ ddog_crasht_RuntimeStackFrame frame = {
172
+ .type_name = DDOG_CHARSLICE_C(""),
173
+ .function = function_slice,
174
+ .file = file_slice,
175
+ .line = info->as.ruby_frame.line,
176
+ .column = 0
177
+ };
178
+
179
+ emit_frame(&frame);
180
+ } else {
181
+ ddog_CharSlice function_slice = DDOG_CHARSLICE_C("<C method>");
182
+ ddog_CharSlice file_slice = DDOG_CHARSLICE_C("<C extension>");
183
+
184
+ if (info->as.native_frame.method_id) {
185
+ const char *method_name = rb_id2name(info->as.native_frame.method_id);
186
+ if (is_pointer_readable(method_name, 256)) {
187
+ size_t method_name_len = strnlen(method_name, 256);
188
+ if (method_name_len > 0 && method_name_len < 256) {
189
+ function_slice = char_slice_from_cstr(method_name);
190
+ }
191
+ }
192
+ }
193
+
194
+ ddog_crasht_RuntimeStackFrame frame = {
195
+ .type_name = DDOG_CHARSLICE_C(""),
196
+ .function = function_slice,
197
+ .file = file_slice,
198
+ .line = 0,
199
+ .column = 0
200
+ };
201
+
202
+ emit_frame(&frame);
203
+ }
204
+ }
205
+
206
+ if (frame_count == RUNTIME_STACK_MAX_FRAMES) {
207
+ emit_placeholder_frame(emit_frame, "<truncated frames>");
208
+ }
209
+ }
210
+
211
+ void crashtracking_runtime_stacks_init(void) {
212
+ cached_page_size = (uintptr_t)sysconf(_SC_PAGESIZE);
213
+ if (cached_page_size == 0 || (cached_page_size & (cached_page_size - 1u))) {
214
+ cached_page_size = 4096;
215
+ }
216
+
217
+ if (crashtracker_thread_data_type == NULL) {
218
+ VALUE current_thread = rb_thread_current();
219
+ if (current_thread == Qnil) {
220
+ rb_raise(rb_eRuntimeError, "crashtracking_runtime_stacks_init: rb_thread_current returned Qnil");
221
+ }
222
+
223
+ const rb_data_type_t *thread_data_type = RTYPEDDATA_TYPE(current_thread);
224
+ if (!thread_data_type) {
225
+ rb_raise(rb_eRuntimeError, "crashtracking_runtime_stacks_init: RTYPEDDATA_TYPE returned NULL");
226
+ }
227
+
228
+ crashtracker_thread_data_type = thread_data_type;
229
+ }
230
+
231
+ // Register immediately so Ruby doesn't need to manage this explicitly.
232
+ ddog_crasht_register_runtime_frame_callback(ruby_runtime_stack_callback);
233
+ }
234
+
235
+ #else
236
+ // Keep init symbol to satisfy linkage on non linux platforms, but do nothing
237
+ void crashtracking_runtime_stacks_init(void) {}
238
+ #endif
239
+
@@ -3,7 +3,7 @@
3
3
  // IMPORTANT: Currently this file is copy-pasted between extensions. Make sure to update all versions when doing any change!
4
4
 
5
5
  #include <ruby.h>
6
- #include <datadog/profiling.h>
6
+ #include <datadog/common.h>
7
7
 
8
8
  // Used to mark symbols to be exported to the outside of the extension.
9
9
  // Consider very carefully before tagging a function with this.
@@ -138,11 +138,16 @@ if have_header("dlfcn.h")
138
138
  have_func("dladdr")
139
139
  end
140
140
 
141
- # On Ruby 3.5, we can't ask the object_id from IMEMOs (https://github.com/ruby/ruby/pull/13347)
142
- $defs << "-DNO_IMEMO_OBJECT_ID" unless RUBY_VERSION < "3.5"
141
+ # On older Rubies, there was no primitive mutex and condition variable implemented in `thread_sync.rb` (internal)
142
+ $defs << "-DNO_PRIMITIVE_MUTEX_AND_CONDITION_VARIABLE" if RUBY_VERSION < "4"
143
143
 
144
- # 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+
145
- $defs << "-DNO_RB_OBJ_INFO" if RUBY_VERSION.start_with?("2.5", "3.3")
144
+ # On Ruby 4, we can't ask the object_id from IMEMOs (https://github.com/ruby/ruby/pull/13347)
145
+ $defs << "-DNO_IMEMO_OBJECT_ID" unless RUBY_VERSION < "4"
146
+
147
+ # This symbol is exclusively visible on certain Ruby versions: 2.6 to 3.2, as well as 3.4 (but not 4.0+)
148
+ # It's only used to get extra information about an object when a failure happens, so it's a "very nice to have" but not
149
+ # actually required for correct behavior of the profiler.
150
+ $defs << "-DNO_RB_OBJ_INFO" if RUBY_VERSION.start_with?("2.5", "3.3", "4.")
146
151
 
147
152
  # On older Rubies, rb_postponed_job_preregister/rb_postponed_job_trigger did not exist
148
153
  $defs << "-DNO_POSTPONED_TRIGGER" if RUBY_VERSION < "3.3"
@@ -305,7 +305,7 @@ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj
305
305
 
306
306
  if (++heap_recorder->num_recordings_skipped < heap_recorder->sample_rate ||
307
307
  #ifdef NO_IMEMO_OBJECT_ID
308
- // On Ruby 3.5, we can't ask the object_id from IMEMOs (https://github.com/ruby/ruby/pull/13347)
308
+ // On Ruby 4, we can't ask the object_id from IMEMOs (https://github.com/ruby/ruby/pull/13347)
309
309
  RB_BUILTIN_TYPE(new_obj) == RUBY_T_IMEMO
310
310
  #else
311
311
  false
@@ -136,6 +136,7 @@ static VALUE perform_export(
136
136
  files_to_compress_and_export,
137
137
  /* files_to_export_unmodified: */ ddog_prof_Exporter_Slice_File_empty(),
138
138
  /* optional_additional_tags: */ NULL,
139
+ /* optional_process_tags: */ NULL,
139
140
  &internal_metadata,
140
141
  &info
141
142
  );
@@ -76,6 +76,18 @@ rb_nativethread_id_t pthread_id_for(VALUE thread) {
76
76
  #endif
77
77
  }
78
78
 
79
+ size_t sizeof_rb_iseq_t(void) {
80
+ return sizeof(rb_iseq_t);
81
+ }
82
+
83
+ VALUE ddtrace_iseq_base_label(const void *iseq) {
84
+ return rb_iseq_base_label((const rb_iseq_t *)iseq);
85
+ }
86
+
87
+ VALUE ddtrace_iseq_path(const void *iseq) {
88
+ return rb_iseq_path((const rb_iseq_t *)iseq);
89
+ }
90
+
79
91
  // Queries if the current thread is the owner of the global VM lock.
80
92
  //
81
93
  // @ivoanjo: Ruby has a similarly-named `ruby_thread_has_gvl_p` but that API is insufficient for our needs because it can
@@ -47,6 +47,10 @@ VALUE thread_name_for(VALUE thread);
47
47
 
48
48
  int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, frame_info *stack_buffer);
49
49
 
50
+ size_t sizeof_rb_iseq_t(void);
51
+ VALUE ddtrace_iseq_base_label(const void *iseq);
52
+ VALUE ddtrace_iseq_path(const void *iseq);
53
+
50
54
  // Returns true if the current thread belongs to the main Ractor or if Ruby has no Ractor support
51
55
  bool ddtrace_rb_ractor_main_p(void);
52
56
 
@@ -23,6 +23,7 @@ void collectors_thread_context_init(VALUE profiling_module);
23
23
  void encoded_profile_init(VALUE profiling_module);
24
24
  void http_transport_init(VALUE profiling_module);
25
25
  void stack_recorder_init(VALUE profiling_module);
26
+ void crashtracking_runtime_stacks_init(void);
26
27
 
27
28
  static VALUE native_working_p(VALUE self);
28
29
  static VALUE _native_grab_gvl_and_raise(DDTRACE_UNUSED VALUE _self, VALUE exception_class, VALUE test_message, VALUE test_message_arg, VALUE release_gvl);
@@ -65,6 +66,7 @@ void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) {
65
66
  encoded_profile_init(profiling_module);
66
67
  http_transport_init(profiling_module);
67
68
  stack_recorder_init(profiling_module);
69
+ crashtracking_runtime_stacks_init();
68
70
  unsafe_api_calls_check_init();
69
71
 
70
72
  // Hosts methods used for testing the native code using RSpec
@@ -3,7 +3,7 @@
3
3
  // IMPORTANT: Currently this file is copy-pasted between extensions. Make sure to update all versions when doing any change!
4
4
 
5
5
  #include <ruby.h>
6
- #include <datadog/profiling.h>
6
+ #include <datadog/common.h>
7
7
 
8
8
  // Used to mark symbols to be exported to the outside of the extension.
9
9
  // Consider very carefully before tagging a function with this.
@@ -0,0 +1,106 @@
1
+ #include <ruby.h>
2
+ #include <datadog/ddsketch.h>
3
+
4
+ #include "datadog_ruby_common.h"
5
+
6
+ static VALUE _native_new(VALUE klass);
7
+ static void ddsketch_free(void *ptr);
8
+ static VALUE native_add(VALUE self, VALUE point);
9
+ static VALUE native_add_with_count(VALUE self, VALUE point, VALUE count);
10
+ static VALUE native_count(VALUE self);
11
+ static VALUE native_encode(VALUE self);
12
+ NORETURN(static void raise_ddsketch_error(const char *message, ddog_VoidResult result));
13
+
14
+ void ddsketch_init(VALUE core_module) {
15
+ VALUE ddsketch_class = rb_define_class_under(core_module, "DDSketch", rb_cObject);
16
+
17
+ rb_define_alloc_func(ddsketch_class, _native_new);
18
+ rb_define_method(ddsketch_class, "add", native_add, 1);
19
+ rb_define_method(ddsketch_class, "add_with_count", native_add_with_count, 2);
20
+ rb_define_method(ddsketch_class, "count", native_count, 0);
21
+ rb_define_method(ddsketch_class, "encode", native_encode, 0);
22
+ }
23
+
24
+ // This structure is used to define a Ruby object that stores a pointer to a ddsketch_Handle_DDSketch
25
+ // See also https://github.com/ruby/ruby/blob/master/doc/extension.rdoc for how this works
26
+ static const rb_data_type_t ddsketch_typed_data = {
27
+ .wrap_struct_name = "Datadog::DDSketch",
28
+ .function = {
29
+ .dmark = NULL, // We don't store references to Ruby objects so we don't need to mark any of them
30
+ .dfree = ddsketch_free,
31
+ .dsize = NULL, // We don't track memory usage (although it'd be cool if we did!)
32
+ //.dcompact = NULL, // Not needed -- we don't store references to Ruby objects
33
+ },
34
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
35
+ };
36
+
37
+ static VALUE _native_new(VALUE klass) {
38
+ ddsketch_Handle_DDSketch *state = ruby_xcalloc(1, sizeof(ddsketch_Handle_DDSketch));
39
+
40
+ *state = ddog_ddsketch_new();
41
+
42
+ return TypedData_Wrap_Struct(klass, &ddsketch_typed_data, state);
43
+ }
44
+
45
+ static void ddsketch_free(void *ptr) {
46
+ ddsketch_Handle_DDSketch *state = (ddsketch_Handle_DDSketch *) ptr;
47
+ ddog_ddsketch_drop(state);
48
+ ruby_xfree(ptr);
49
+ }
50
+
51
+ static void raise_ddsketch_error(const char *message, ddog_VoidResult result) {
52
+ rb_raise(rb_eRuntimeError, "%s: %"PRIsVALUE, message, get_error_details_and_drop(&result.err));
53
+ }
54
+
55
+ static VALUE native_add(VALUE self, VALUE point) {
56
+ ddsketch_Handle_DDSketch *state;
57
+ TypedData_Get_Struct(self, ddsketch_Handle_DDSketch, &ddsketch_typed_data, state);
58
+
59
+ ddog_VoidResult result = ddog_ddsketch_add(state, NUM2DBL(point));
60
+
61
+ if (result.tag == DDOG_VOID_RESULT_ERR) raise_ddsketch_error("DDSketch add failed", result);
62
+
63
+ return self;
64
+ }
65
+
66
+ static VALUE native_add_with_count(VALUE self, VALUE point, VALUE count) {
67
+ ddsketch_Handle_DDSketch *state;
68
+ TypedData_Get_Struct(self, ddsketch_Handle_DDSketch, &ddsketch_typed_data, state);
69
+
70
+ ddog_VoidResult result = ddog_ddsketch_add_with_count(state, NUM2DBL(point), NUM2DBL(count));
71
+
72
+ if (result.tag == DDOG_VOID_RESULT_ERR) raise_ddsketch_error("DDSketch add_with_count failed", result);
73
+
74
+ return self;
75
+ }
76
+
77
+ static VALUE native_count(VALUE self) {
78
+ ddsketch_Handle_DDSketch *state;
79
+ TypedData_Get_Struct(self, ddsketch_Handle_DDSketch, &ddsketch_typed_data, state);
80
+
81
+ double count_out;
82
+ ddog_VoidResult result = ddog_ddsketch_count(state, &count_out);
83
+
84
+ if (result.tag == DDOG_VOID_RESULT_ERR) raise_ddsketch_error("DDSketch count failed", result);
85
+
86
+ return DBL2NUM(count_out);
87
+ }
88
+
89
+ static VALUE native_encode(VALUE self) {
90
+ ddsketch_Handle_DDSketch *state;
91
+ TypedData_Get_Struct(self, ddsketch_Handle_DDSketch, &ddsketch_typed_data, state);
92
+
93
+ ddog_Vec_U8 encoded = ddog_ddsketch_encode(state);
94
+
95
+ // Copy into a Ruby string
96
+ VALUE bytes = rb_str_new((const char *) encoded.ptr, encoded.len);
97
+
98
+ ddog_Vec_U8_drop(encoded);
99
+
100
+ // The sketch is consumed by encode; to make this a bit more user-friendly for
101
+ // a Ruby API (since we can't "kill" the Ruby object), let's re-initialize it so
102
+ // it can be used again.
103
+ *state = ddog_ddsketch_new();
104
+
105
+ return bytes;
106
+ }