datadog 2.17.0 → 2.37.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 (765) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +686 -1
  3. data/README.md +0 -1
  4. data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
  5. data/ext/datadog_profiling_native_extension/clock_id.h +9 -1
  6. data/ext/datadog_profiling_native_extension/clock_id_from_mach.c +66 -0
  7. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +3 -2
  8. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +344 -126
  9. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +4 -4
  10. data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +3 -2
  11. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +18 -4
  12. data/ext/datadog_profiling_native_extension/collectors_stack.c +314 -91
  13. data/ext/datadog_profiling_native_extension/collectors_stack.h +26 -3
  14. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +680 -352
  15. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +18 -5
  16. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
  17. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +65 -1
  18. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +40 -1
  19. data/ext/datadog_profiling_native_extension/encoded_profile.c +2 -1
  20. data/ext/datadog_profiling_native_extension/extconf.rb +48 -34
  21. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +4 -43
  22. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +15 -47
  23. data/ext/datadog_profiling_native_extension/heap_recorder.c +455 -430
  24. data/ext/datadog_profiling_native_extension/heap_recorder.h +16 -7
  25. data/ext/datadog_profiling_native_extension/http_transport.c +66 -68
  26. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +18 -15
  27. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +8 -10
  28. data/ext/datadog_profiling_native_extension/macos_sampler_thread.h +55 -0
  29. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +0 -13
  30. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +73 -69
  31. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +10 -4
  32. data/ext/datadog_profiling_native_extension/profiling.c +65 -19
  33. data/ext/datadog_profiling_native_extension/ruby_helpers.c +64 -59
  34. data/ext/datadog_profiling_native_extension/ruby_helpers.h +20 -16
  35. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +32 -10
  36. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +2 -1
  37. data/ext/datadog_profiling_native_extension/stack_recorder.c +253 -131
  38. data/ext/datadog_profiling_native_extension/stack_recorder.h +3 -1
  39. data/ext/datadog_profiling_native_extension/time_helpers.h +1 -0
  40. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +2 -1
  41. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +9 -4
  42. data/ext/libdatadog_api/crashtracker.c +16 -15
  43. data/ext/libdatadog_api/crashtracker_report_exception.c +126 -0
  44. data/ext/libdatadog_api/datadog_ruby_common.c +65 -1
  45. data/ext/libdatadog_api/datadog_ruby_common.h +40 -1
  46. data/ext/libdatadog_api/ddsketch.c +102 -0
  47. data/ext/libdatadog_api/di.c +203 -0
  48. data/ext/libdatadog_api/extconf.rb +21 -25
  49. data/ext/libdatadog_api/feature_flags.c +554 -0
  50. data/ext/libdatadog_api/feature_flags.h +5 -0
  51. data/ext/libdatadog_api/helpers.h +27 -0
  52. data/ext/libdatadog_api/init.c +13 -1
  53. data/ext/libdatadog_api/library_config.c +88 -37
  54. data/ext/libdatadog_api/library_config.h +6 -0
  55. data/ext/libdatadog_api/process_discovery.c +25 -24
  56. data/ext/libdatadog_extconf_helpers.rb +107 -14
  57. data/lib/datadog/ai_guard/api_client.rb +84 -0
  58. data/lib/datadog/ai_guard/autoload.rb +10 -0
  59. data/lib/datadog/ai_guard/component.rb +44 -0
  60. data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
  61. data/lib/datadog/ai_guard/configuration.rb +115 -0
  62. data/lib/datadog/ai_guard/contrib/auto_instrument.rb +24 -0
  63. data/lib/datadog/ai_guard/contrib/integration.rb +37 -0
  64. data/lib/datadog/ai_guard/contrib/rack/integration.rb +42 -0
  65. data/lib/datadog/ai_guard/contrib/rack/patcher.rb +26 -0
  66. data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +97 -0
  67. data/lib/datadog/ai_guard/contrib/rails/integration.rb +41 -0
  68. data/lib/datadog/ai_guard/contrib/rails/patcher.rb +97 -0
  69. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +80 -0
  70. data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +41 -0
  71. data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +30 -0
  72. data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
  73. data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
  74. data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
  75. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +36 -0
  76. data/lib/datadog/ai_guard/evaluation/request.rb +86 -0
  77. data/lib/datadog/ai_guard/evaluation/result.rb +45 -0
  78. data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
  79. data/lib/datadog/ai_guard/evaluation.rb +108 -0
  80. data/lib/datadog/ai_guard/ext.rb +29 -0
  81. data/lib/datadog/ai_guard.rb +181 -0
  82. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
  83. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +66 -0
  84. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +36 -0
  85. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
  86. data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
  87. data/lib/datadog/appsec/api_security/route_extractor.rb +104 -0
  88. data/lib/datadog/appsec/api_security/sampler.rb +62 -0
  89. data/lib/datadog/appsec/api_security.rb +14 -0
  90. data/lib/datadog/appsec/assets/blocked.html +10 -1
  91. data/lib/datadog/appsec/assets/blocked.json +1 -1
  92. data/lib/datadog/appsec/assets/blocked.text +3 -1
  93. data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
  94. data/lib/datadog/appsec/assets/waf_rules/recommended.json +601 -74
  95. data/lib/datadog/appsec/assets/waf_rules/strict.json +48 -75
  96. data/lib/datadog/appsec/assets.rb +1 -1
  97. data/lib/datadog/appsec/autoload.rb +2 -2
  98. data/lib/datadog/appsec/component.rb +37 -64
  99. data/lib/datadog/appsec/compressed_json.rb +3 -3
  100. data/lib/datadog/appsec/configuration.rb +427 -1
  101. data/lib/datadog/appsec/context.rb +77 -16
  102. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
  103. data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
  104. data/lib/datadog/appsec/contrib/active_record/patcher.rb +4 -1
  105. data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +75 -0
  106. data/lib/datadog/appsec/contrib/aws_lambda/integration.rb +39 -0
  107. data/lib/datadog/appsec/contrib/aws_lambda/patcher.rb +30 -0
  108. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +144 -0
  109. data/lib/datadog/appsec/contrib/devise/integration.rb +1 -1
  110. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +4 -2
  111. data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +2 -1
  112. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +1 -1
  113. data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
  114. data/lib/datadog/appsec/contrib/excon/patcher.rb +3 -1
  115. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +152 -13
  116. data/lib/datadog/appsec/contrib/faraday/integration.rb +1 -1
  117. data/lib/datadog/appsec/contrib/faraday/patcher.rb +1 -1
  118. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +138 -13
  119. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +74 -24
  120. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +4 -2
  121. data/lib/datadog/appsec/contrib/graphql/integration.rb +2 -1
  122. data/lib/datadog/appsec/contrib/rack/buffered_input.rb +83 -0
  123. data/lib/datadog/appsec/contrib/rack/ext.rb +1 -1
  124. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +47 -13
  125. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +40 -19
  126. data/lib/datadog/appsec/contrib/rack/input_peeker.rb +79 -0
  127. data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
  128. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +80 -62
  129. data/lib/datadog/appsec/contrib/rack/response_body.rb +36 -0
  130. data/lib/datadog/appsec/contrib/rails/gateway/request.rb +35 -1
  131. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +47 -5
  132. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
  133. data/lib/datadog/appsec/contrib/rails/patcher.rb +52 -26
  134. data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.rb +29 -0
  135. data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +33 -0
  136. data/lib/datadog/appsec/contrib/rest_client/integration.rb +1 -1
  137. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +3 -1
  138. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +167 -12
  139. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +63 -10
  140. data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
  141. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +11 -21
  142. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +31 -0
  143. data/lib/datadog/appsec/counter_sampler.rb +25 -0
  144. data/lib/datadog/appsec/default_header_tags.rb +52 -0
  145. data/lib/datadog/appsec/event.rb +14 -47
  146. data/lib/datadog/appsec/ext.rb +3 -0
  147. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +17 -1
  148. data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +2 -3
  149. data/lib/datadog/appsec/instrumentation/gateway.rb +2 -15
  150. data/lib/datadog/appsec/metrics/collector.rb +47 -5
  151. data/lib/datadog/appsec/metrics/exporter.rb +16 -3
  152. data/lib/datadog/appsec/metrics/telemetry.rb +21 -4
  153. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +47 -0
  154. data/lib/datadog/appsec/metrics.rb +5 -4
  155. data/lib/datadog/appsec/monitor/gateway/watcher.rb +8 -6
  156. data/lib/datadog/appsec/processor/rule_loader.rb +5 -6
  157. data/lib/datadog/appsec/remote.rb +51 -76
  158. data/lib/datadog/appsec/response.rb +18 -4
  159. data/lib/datadog/appsec/route_normalizer/rails_route_pattern.rb +176 -0
  160. data/lib/datadog/appsec/route_normalizer/route_pattern.rb +378 -0
  161. data/lib/datadog/appsec/route_normalizer/route_text.rb +57 -0
  162. data/lib/datadog/appsec/route_normalizer.rb +80 -0
  163. data/lib/datadog/appsec/security_engine/engine.rb +197 -0
  164. data/lib/datadog/appsec/security_engine/result.rb +45 -9
  165. data/lib/datadog/appsec/security_engine/runner.rb +44 -21
  166. data/lib/datadog/appsec/security_event.rb +5 -7
  167. data/lib/datadog/appsec/thread_safe_ref.rb +61 -0
  168. data/lib/datadog/appsec/trace_keeper.rb +36 -0
  169. data/lib/datadog/appsec/utils/hash_coercion.rb +23 -0
  170. data/lib/datadog/appsec/utils/http/body.rb +38 -0
  171. data/lib/datadog/appsec/utils/http/body_reader.rb +61 -0
  172. data/lib/datadog/appsec/utils/http/media_range.rb +2 -1
  173. data/lib/datadog/appsec/utils/http/media_type.rb +32 -26
  174. data/lib/datadog/appsec/utils/http/url_encoded.rb +52 -0
  175. data/lib/datadog/appsec.rb +10 -17
  176. data/lib/datadog/auto_instrument_base.rb +2 -1
  177. data/lib/datadog/core/configuration/agent_settings.rb +52 -0
  178. data/lib/datadog/core/configuration/agent_settings_resolver.rb +5 -47
  179. data/lib/datadog/core/configuration/base.rb +17 -5
  180. data/lib/datadog/core/configuration/components.rb +192 -20
  181. data/lib/datadog/core/configuration/components_state.rb +6 -1
  182. data/lib/datadog/core/configuration/config_helper.rb +109 -0
  183. data/lib/datadog/core/configuration/deprecations.rb +36 -0
  184. data/lib/datadog/core/configuration/ext.rb +0 -1
  185. data/lib/datadog/core/configuration/option.rb +95 -65
  186. data/lib/datadog/core/configuration/option_definition.rb +42 -23
  187. data/lib/datadog/core/configuration/options.rb +57 -18
  188. data/lib/datadog/core/configuration/settings.rb +160 -25
  189. data/lib/datadog/core/configuration/stable_config.rb +11 -2
  190. data/lib/datadog/core/configuration/supported_configurations.rb +413 -0
  191. data/lib/datadog/core/configuration.rb +4 -4
  192. data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
  193. data/lib/datadog/core/contrib/rails/utils.rb +7 -3
  194. data/lib/datadog/core/crashtracking/component.rb +73 -21
  195. data/lib/datadog/core/crashtracking/tag_builder.rb +9 -21
  196. data/lib/datadog/core/ddsketch.rb +19 -0
  197. data/lib/datadog/core/deprecations.rb +2 -2
  198. data/lib/datadog/core/diagnostics/environment_logger.rb +3 -1
  199. data/lib/datadog/core/environment/agent_info.rb +65 -1
  200. data/lib/datadog/core/environment/cgroup.rb +52 -25
  201. data/lib/datadog/core/environment/container.rb +140 -46
  202. data/lib/datadog/core/environment/ext.rb +13 -2
  203. data/lib/datadog/core/environment/git.rb +2 -2
  204. data/lib/datadog/core/environment/identity.rb +40 -4
  205. data/lib/datadog/core/environment/process.rb +122 -0
  206. data/lib/datadog/core/environment/socket.rb +13 -0
  207. data/lib/datadog/core/environment/variable_helpers.rb +3 -3
  208. data/lib/datadog/core/environment/yjit.rb +2 -1
  209. data/lib/datadog/core/error.rb +6 -6
  210. data/lib/datadog/core/evp.rb +11 -0
  211. data/lib/datadog/core/feature_flags.rb +61 -0
  212. data/lib/datadog/core/knuth_sampler.rb +57 -0
  213. data/lib/datadog/core/logger.rb +1 -1
  214. data/lib/datadog/core/metrics/client.rb +7 -7
  215. data/lib/datadog/core/metrics/logging.rb +1 -1
  216. data/lib/datadog/core/pin.rb +8 -8
  217. data/lib/datadog/core/process_discovery/tracer_memfd.rb +13 -0
  218. data/lib/datadog/core/process_discovery.rb +51 -21
  219. data/lib/datadog/core/rate_limiter.rb +11 -1
  220. data/lib/datadog/core/remote/client/capabilities.rb +49 -6
  221. data/lib/datadog/core/remote/client.rb +15 -7
  222. data/lib/datadog/core/remote/component.rb +64 -36
  223. data/lib/datadog/core/remote/configuration/content.rb +15 -2
  224. data/lib/datadog/core/remote/configuration/digest.rb +14 -7
  225. data/lib/datadog/core/remote/configuration/repository.rb +13 -1
  226. data/lib/datadog/core/remote/configuration/target.rb +13 -6
  227. data/lib/datadog/core/remote/transport/config.rb +9 -36
  228. data/lib/datadog/core/remote/transport/http/config.rb +15 -55
  229. data/lib/datadog/core/remote/transport/http/negotiation.rb +14 -44
  230. data/lib/datadog/core/remote/transport/http.rb +15 -24
  231. data/lib/datadog/core/remote/transport/negotiation.rb +8 -33
  232. data/lib/datadog/core/remote/worker.rb +25 -37
  233. data/lib/datadog/core/runtime/ext.rb +0 -1
  234. data/lib/datadog/core/runtime/metrics.rb +11 -2
  235. data/lib/datadog/core/semaphore.rb +1 -4
  236. data/lib/datadog/core/tag_builder.rb +52 -0
  237. data/lib/datadog/core/tag_normalizer.rb +84 -0
  238. data/lib/datadog/core/telemetry/component.rb +65 -17
  239. data/lib/datadog/core/telemetry/emitter.rb +6 -6
  240. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +3 -3
  241. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
  242. data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
  243. data/lib/datadog/core/telemetry/event/app_started.rb +203 -89
  244. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  245. data/lib/datadog/core/telemetry/event.rb +2 -7
  246. data/lib/datadog/core/telemetry/ext.rb +1 -0
  247. data/lib/datadog/core/telemetry/logger.rb +5 -2
  248. data/lib/datadog/core/telemetry/logging.rb +22 -4
  249. data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
  250. data/lib/datadog/core/telemetry/request.rb +17 -5
  251. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +8 -34
  252. data/lib/datadog/core/telemetry/transport/http.rb +21 -16
  253. data/lib/datadog/core/telemetry/transport/telemetry.rb +8 -17
  254. data/lib/datadog/core/telemetry/worker.rb +108 -32
  255. data/lib/datadog/core/transport/ext.rb +3 -0
  256. data/lib/datadog/core/transport/http/adapters/net.rb +17 -2
  257. data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
  258. data/lib/datadog/core/transport/http/api/instance.rb +4 -21
  259. data/lib/datadog/core/transport/http/builder.rb +11 -7
  260. data/lib/datadog/core/transport/http/client.rb +80 -0
  261. data/lib/datadog/core/transport/http/env.rb +8 -0
  262. data/lib/datadog/core/transport/http/response.rb +4 -0
  263. data/lib/datadog/core/transport/http.rb +24 -19
  264. data/lib/datadog/core/transport/parcel.rb +61 -9
  265. data/lib/datadog/core/transport/response.rb +16 -2
  266. data/lib/datadog/core/transport/transport.rb +90 -0
  267. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +1 -1
  268. data/lib/datadog/core/utils/{base64.rb → base64_codec.rb} +4 -3
  269. data/lib/datadog/core/utils/enumerable_compat.rb +29 -0
  270. data/lib/datadog/core/utils/fnv.rb +26 -0
  271. data/lib/datadog/core/utils/forking.rb +3 -1
  272. data/lib/datadog/core/utils/hash.rb +0 -23
  273. data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -14
  274. data/lib/datadog/core/utils/network.rb +22 -1
  275. data/lib/datadog/core/utils/only_once.rb +1 -1
  276. data/lib/datadog/core/utils/only_once_successful.rb +8 -2
  277. data/lib/datadog/core/utils/safe_dup.rb +2 -2
  278. data/lib/datadog/core/utils/sequence.rb +2 -0
  279. data/lib/datadog/core/utils/spawn_monkey_patch.rb +68 -0
  280. data/lib/datadog/core/utils/time.rb +1 -1
  281. data/lib/datadog/core/utils.rb +10 -1
  282. data/lib/datadog/core/workers/async.rb +11 -2
  283. data/lib/datadog/core/workers/interval_loop.rb +51 -3
  284. data/lib/datadog/core/workers/polling.rb +2 -0
  285. data/lib/datadog/core/workers/queue.rb +96 -1
  286. data/lib/datadog/core/workers/runtime_metrics.rb +9 -1
  287. data/lib/datadog/core.rb +11 -2
  288. data/lib/datadog/data_streams/configuration.rb +50 -0
  289. data/lib/datadog/data_streams/ext.rb +11 -0
  290. data/lib/datadog/data_streams/extensions.rb +16 -0
  291. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  292. data/lib/datadog/data_streams/processor.rb +511 -0
  293. data/lib/datadog/data_streams/transport/http/stats.rb +58 -0
  294. data/lib/datadog/data_streams/transport/http.rb +36 -0
  295. data/lib/datadog/data_streams/transport/stats.rb +39 -0
  296. data/lib/datadog/data_streams.rb +100 -0
  297. data/lib/datadog/di/base.rb +15 -7
  298. data/lib/datadog/di/boot.rb +11 -1
  299. data/lib/datadog/di/code_tracker.rb +217 -15
  300. data/lib/datadog/di/component.rb +143 -48
  301. data/lib/datadog/di/configuration.rb +235 -2
  302. data/lib/datadog/di/context.rb +76 -0
  303. data/lib/datadog/di/contrib/active_record.rb +34 -5
  304. data/lib/datadog/di/el/compiler.rb +168 -0
  305. data/lib/datadog/di/el/evaluator.rb +159 -0
  306. data/lib/datadog/di/el/expression.rb +42 -0
  307. data/lib/datadog/di/el.rb +5 -0
  308. data/lib/datadog/di/error.rb +44 -0
  309. data/lib/datadog/di/fatal_exceptions.rb +26 -0
  310. data/lib/datadog/di/instrumenter.rb +620 -132
  311. data/lib/datadog/di/logger.rb +2 -2
  312. data/lib/datadog/di/probe.rb +78 -15
  313. data/lib/datadog/di/probe_builder.rb +42 -3
  314. data/lib/datadog/di/probe_file_loader/railtie.rb +15 -0
  315. data/lib/datadog/di/probe_file_loader.rb +87 -0
  316. data/lib/datadog/di/probe_manager.rb +195 -100
  317. data/lib/datadog/di/probe_notification_builder.rb +285 -100
  318. data/lib/datadog/di/probe_notifier_worker.rb +120 -36
  319. data/lib/datadog/di/probe_repository.rb +198 -0
  320. data/lib/datadog/di/proc_responder.rb +36 -0
  321. data/lib/datadog/di/redactor.rb +24 -2
  322. data/lib/datadog/di/remote.rb +258 -95
  323. data/lib/datadog/di/serializer.rb +297 -20
  324. data/lib/datadog/di/transport/diagnostics.rb +12 -42
  325. data/lib/datadog/di/transport/http/diagnostics.rb +4 -34
  326. data/lib/datadog/di/transport/http/input.rb +12 -34
  327. data/lib/datadog/di/transport/http.rb +43 -19
  328. data/lib/datadog/di/transport/input.rb +121 -39
  329. data/lib/datadog/di/utils.rb +42 -14
  330. data/lib/datadog/di.rb +203 -10
  331. data/lib/datadog/error_tracking/collector.rb +2 -1
  332. data/lib/datadog/error_tracking/component.rb +2 -2
  333. data/lib/datadog/error_tracking/configuration.rb +55 -2
  334. data/lib/datadog/error_tracking/filters.rb +2 -2
  335. data/lib/datadog/kit/appsec/events/v2.rb +253 -0
  336. data/lib/datadog/kit/appsec/events.rb +11 -10
  337. data/lib/datadog/kit/enable_core_dumps.rb +1 -1
  338. data/lib/datadog/kit/identity.rb +17 -11
  339. data/lib/datadog/kit/tracing/method_tracer.rb +135 -0
  340. data/lib/datadog/open_feature/component.rb +100 -0
  341. data/lib/datadog/open_feature/configuration.rb +37 -0
  342. data/lib/datadog/open_feature/evaluation_engine.rb +70 -0
  343. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  344. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  345. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  346. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  347. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  348. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  349. data/lib/datadog/open_feature/ext.rb +16 -0
  350. data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +286 -0
  351. data/lib/datadog/open_feature/flag_evaluation/writer.rb +433 -0
  352. data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +101 -0
  353. data/lib/datadog/open_feature/hooks/flag_eval_metrics_hook.rb +49 -0
  354. data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
  355. data/lib/datadog/open_feature/native_evaluator.rb +60 -0
  356. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  357. data/lib/datadog/open_feature/provider.rb +191 -0
  358. data/lib/datadog/open_feature/remote.rb +67 -0
  359. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  360. data/lib/datadog/open_feature/transport.rb +121 -0
  361. data/lib/datadog/open_feature.rb +19 -0
  362. data/lib/datadog/opentelemetry/api/baggage.rb +3 -3
  363. data/lib/datadog/opentelemetry/api/context.rb +10 -9
  364. data/lib/datadog/opentelemetry/configuration/settings.rb +226 -0
  365. data/lib/datadog/opentelemetry/ext.rb +9 -0
  366. data/lib/datadog/opentelemetry/logs.rb +98 -0
  367. data/lib/datadog/opentelemetry/metrics.rb +90 -0
  368. data/lib/datadog/opentelemetry/sdk/configurator.rb +66 -2
  369. data/lib/datadog/opentelemetry/sdk/id_generator.rb +16 -10
  370. data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +30 -0
  371. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +28 -0
  372. data/lib/datadog/opentelemetry/sdk/propagator.rb +13 -7
  373. data/lib/datadog/opentelemetry/sdk/span_processor.rb +12 -9
  374. data/lib/datadog/opentelemetry/sdk/trace/span.rb +14 -10
  375. data/lib/datadog/opentelemetry/sdk.rb +19 -0
  376. data/lib/datadog/opentelemetry/signal_configuration.rb +53 -0
  377. data/lib/datadog/opentelemetry/trace.rb +4 -4
  378. data/lib/datadog/opentelemetry.rb +4 -0
  379. data/lib/datadog/profiling/collectors/code_provenance.rb +83 -15
  380. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +52 -3
  381. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +13 -4
  382. data/lib/datadog/profiling/collectors/info.rb +59 -4
  383. data/lib/datadog/profiling/collectors/thread_context.rb +17 -5
  384. data/lib/datadog/profiling/component.rb +73 -49
  385. data/lib/datadog/profiling/exporter.rb +48 -13
  386. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +24 -5
  387. data/lib/datadog/profiling/ext/exec_monkey_patch.rb +32 -0
  388. data/lib/datadog/profiling/ext.rb +2 -15
  389. data/lib/datadog/profiling/flush.rb +23 -11
  390. data/lib/datadog/profiling/http_transport.rb +24 -11
  391. data/lib/datadog/profiling/load_native_extension.rb +2 -2
  392. data/lib/datadog/profiling/profiler.rb +31 -12
  393. data/lib/datadog/profiling/scheduler.rb +12 -10
  394. data/lib/datadog/profiling/sequence_tracker.rb +44 -0
  395. data/lib/datadog/profiling/stack_recorder.rb +5 -13
  396. data/lib/datadog/profiling/tag_builder.rb +42 -38
  397. data/lib/datadog/profiling/tasks/exec.rb +10 -5
  398. data/lib/datadog/profiling/tasks/help.rb +1 -0
  399. data/lib/datadog/profiling/tasks/setup.rb +4 -2
  400. data/lib/datadog/profiling.rb +8 -11
  401. data/lib/datadog/ruby_version.rb +25 -0
  402. data/lib/datadog/single_step_instrument.rb +10 -1
  403. data/lib/datadog/symbol_database/component.rb +734 -0
  404. data/lib/datadog/symbol_database/configuration.rb +73 -0
  405. data/lib/datadog/symbol_database/extensions.rb +19 -0
  406. data/lib/datadog/symbol_database/extractor.rb +1110 -0
  407. data/lib/datadog/symbol_database/file_hash.rb +48 -0
  408. data/lib/datadog/symbol_database/logger.rb +43 -0
  409. data/lib/datadog/symbol_database/remote.rb +180 -0
  410. data/lib/datadog/symbol_database/scope.rb +102 -0
  411. data/lib/datadog/symbol_database/scope_batcher.rb +292 -0
  412. data/lib/datadog/symbol_database/service_version.rb +66 -0
  413. data/lib/datadog/symbol_database/symbol.rb +69 -0
  414. data/lib/datadog/symbol_database/transport/http/endpoint.rb +28 -0
  415. data/lib/datadog/symbol_database/transport/http.rb +45 -0
  416. data/lib/datadog/symbol_database/transport.rb +54 -0
  417. data/lib/datadog/symbol_database/uploader.rb +230 -0
  418. data/lib/datadog/symbol_database.rb +71 -0
  419. data/lib/datadog/tracing/analytics.rb +1 -1
  420. data/lib/datadog/tracing/buffer.rb +10 -10
  421. data/lib/datadog/tracing/component.rb +17 -17
  422. data/lib/datadog/tracing/configuration/dynamic.rb +6 -8
  423. data/lib/datadog/tracing/configuration/ext.rb +25 -5
  424. data/lib/datadog/tracing/configuration/settings.rb +125 -11
  425. data/lib/datadog/tracing/context.rb +2 -2
  426. data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
  427. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +4 -1
  428. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +4 -1
  429. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +4 -1
  430. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +4 -1
  431. data/lib/datadog/tracing/contrib/action_cable/integration.rb +1 -1
  432. data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -1
  433. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +1 -1
  434. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +29 -11
  435. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +47 -12
  436. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +3 -1
  437. data/lib/datadog/tracing/contrib/action_pack/ext.rb +2 -0
  438. data/lib/datadog/tracing/contrib/action_pack/integration.rb +1 -1
  439. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
  440. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +4 -1
  441. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +6 -3
  442. data/lib/datadog/tracing/contrib/active_job/event.rb +8 -8
  443. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +6 -3
  444. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +6 -3
  445. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +6 -3
  446. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +6 -3
  447. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +6 -3
  448. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +6 -3
  449. data/lib/datadog/tracing/contrib/active_job/integration.rb +1 -1
  450. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  451. data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
  452. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +2 -2
  453. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +4 -4
  454. data/lib/datadog/tracing/contrib/active_model_serializers/integration.rb +1 -2
  455. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +3 -3
  456. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +2 -2
  457. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +7 -10
  458. data/lib/datadog/tracing/contrib/active_record/integration.rb +1 -1
  459. data/lib/datadog/tracing/contrib/active_record/utils.rb +16 -16
  460. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +15 -12
  461. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +1 -4
  462. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +13 -0
  463. data/lib/datadog/tracing/contrib/active_support/integration.rb +1 -1
  464. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +2 -1
  465. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +8 -10
  466. data/lib/datadog/tracing/contrib/aws/ext.rb +1 -1
  467. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +8 -10
  468. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +3 -1
  469. data/lib/datadog/tracing/contrib/aws/patcher.rb +5 -1
  470. data/lib/datadog/tracing/contrib/aws/service/base.rb +2 -1
  471. data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +1 -1
  472. data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +1 -1
  473. data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +1 -1
  474. data/lib/datadog/tracing/contrib/aws/service/s3.rb +1 -1
  475. data/lib/datadog/tracing/contrib/aws/service/sns.rb +1 -1
  476. data/lib/datadog/tracing/contrib/aws/service/sqs.rb +1 -1
  477. data/lib/datadog/tracing/contrib/aws/service/states.rb +1 -1
  478. data/lib/datadog/tracing/contrib/aws/services.rb +7 -7
  479. data/lib/datadog/tracing/contrib/component.rb +3 -3
  480. data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +1 -1
  481. data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +1 -1
  482. data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +1 -1
  483. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +1 -1
  484. data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +1 -1
  485. data/lib/datadog/tracing/contrib/configurable.rb +22 -7
  486. data/lib/datadog/tracing/contrib/configuration/resolver.rb +14 -4
  487. data/lib/datadog/tracing/contrib/configuration/resolvers/pattern_resolver.rb +4 -4
  488. data/lib/datadog/tracing/contrib/dalli/ext.rb +3 -2
  489. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +1 -5
  490. data/lib/datadog/tracing/contrib/dalli/integration.rb +5 -2
  491. data/lib/datadog/tracing/contrib/dalli/quantize.rb +1 -1
  492. data/lib/datadog/tracing/contrib/delayed_job/integration.rb +1 -1
  493. data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +2 -0
  494. data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +1 -0
  495. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +3 -2
  496. data/lib/datadog/tracing/contrib/elasticsearch/integration.rb +4 -4
  497. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +47 -53
  498. data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +7 -7
  499. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +5 -1
  500. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +7 -8
  501. data/lib/datadog/tracing/contrib/ethon/ext.rb +4 -2
  502. data/lib/datadog/tracing/contrib/ethon/integration.rb +1 -1
  503. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +1 -8
  504. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +16 -5
  505. data/lib/datadog/tracing/contrib/excon/ext.rb +4 -2
  506. data/lib/datadog/tracing/contrib/excon/integration.rb +1 -1
  507. data/lib/datadog/tracing/contrib/excon/middleware.rb +5 -9
  508. data/lib/datadog/tracing/contrib/ext.rb +6 -4
  509. data/lib/datadog/tracing/contrib/extensions.rb +28 -11
  510. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +16 -9
  511. data/lib/datadog/tracing/contrib/faraday/ext.rb +4 -2
  512. data/lib/datadog/tracing/contrib/faraday/integration.rb +1 -1
  513. data/lib/datadog/tracing/contrib/faraday/middleware.rb +7 -9
  514. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
  515. data/lib/datadog/tracing/contrib/grape/endpoint.rb +18 -15
  516. data/lib/datadog/tracing/contrib/grape/instrumentation.rb +13 -8
  517. data/lib/datadog/tracing/contrib/grape/integration.rb +1 -1
  518. data/lib/datadog/tracing/contrib/grape/patcher.rb +6 -1
  519. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
  520. data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
  521. data/lib/datadog/tracing/contrib/graphql/integration.rb +1 -1
  522. data/lib/datadog/tracing/contrib/graphql/patcher.rb +2 -2
  523. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +105 -63
  524. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +5 -2
  525. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +11 -15
  526. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +6 -10
  527. data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +1 -1
  528. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +2 -0
  529. data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
  530. data/lib/datadog/tracing/contrib/grpc/integration.rb +1 -1
  531. data/lib/datadog/tracing/contrib/grpc.rb +1 -0
  532. data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +1 -0
  533. data/lib/datadog/tracing/contrib/hanami/ext.rb +2 -2
  534. data/lib/datadog/tracing/contrib/hanami/integration.rb +1 -1
  535. data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +2 -1
  536. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +10 -11
  537. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +16 -5
  538. data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +4 -4
  539. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +2 -0
  540. data/lib/datadog/tracing/contrib/http/ext.rb +4 -2
  541. data/lib/datadog/tracing/contrib/http/instrumentation.rb +9 -13
  542. data/lib/datadog/tracing/contrib/http/integration.rb +0 -2
  543. data/lib/datadog/tracing/contrib/http.rb +1 -0
  544. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +16 -5
  545. data/lib/datadog/tracing/contrib/httpclient/ext.rb +4 -2
  546. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +10 -10
  547. data/lib/datadog/tracing/contrib/httpclient/integration.rb +1 -1
  548. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +16 -5
  549. data/lib/datadog/tracing/contrib/httprb/ext.rb +4 -2
  550. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +8 -12
  551. data/lib/datadog/tracing/contrib/httprb/integration.rb +1 -1
  552. data/lib/datadog/tracing/contrib/kafka/event.rb +2 -1
  553. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +1 -1
  554. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +1 -1
  555. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +1 -1
  556. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +1 -1
  557. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/join_group.rb +1 -1
  558. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/leave_group.rb +1 -1
  559. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/sync_group.rb +1 -1
  560. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +1 -1
  561. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +1 -1
  562. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  563. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  564. data/lib/datadog/tracing/contrib/kafka/integration.rb +1 -1
  565. data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
  566. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +5 -1
  567. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +2 -0
  568. data/lib/datadog/tracing/contrib/karafka/ext.rb +1 -0
  569. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  570. data/lib/datadog/tracing/contrib/karafka/monitor.rb +24 -13
  571. data/lib/datadog/tracing/contrib/karafka/patcher.rb +39 -8
  572. data/lib/datadog/tracing/contrib/karafka.rb +1 -0
  573. data/lib/datadog/tracing/contrib/lograge/instrumentation.rb +1 -1
  574. data/lib/datadog/tracing/contrib/lograge/integration.rb +1 -1
  575. data/lib/datadog/tracing/contrib/lograge/patcher.rb +4 -2
  576. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +1 -1
  577. data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -1
  578. data/lib/datadog/tracing/contrib/mongodb/integration.rb +1 -1
  579. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +6 -6
  580. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +11 -14
  581. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +6 -0
  582. data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -1
  583. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +19 -12
  584. data/lib/datadog/tracing/contrib/mysql2/integration.rb +1 -1
  585. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +1 -1
  586. data/lib/datadog/tracing/contrib/opensearch/ext.rb +3 -2
  587. data/lib/datadog/tracing/contrib/opensearch/integration.rb +1 -2
  588. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +64 -70
  589. data/lib/datadog/tracing/contrib/opensearch/quantize.rb +7 -7
  590. data/lib/datadog/tracing/contrib/patcher.rb +7 -9
  591. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +6 -0
  592. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +3 -6
  593. data/lib/datadog/tracing/contrib/pg/integration.rb +1 -1
  594. data/lib/datadog/tracing/contrib/presto/ext.rb +1 -1
  595. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +9 -11
  596. data/lib/datadog/tracing/contrib/presto/integration.rb +1 -1
  597. data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +1 -1
  598. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +13 -0
  599. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +6 -2
  600. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +25 -1
  601. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +5 -2
  602. data/lib/datadog/tracing/contrib/que/ext.rb +1 -0
  603. data/lib/datadog/tracing/contrib/que/integration.rb +1 -1
  604. data/lib/datadog/tracing/contrib/que/tracer.rb +1 -0
  605. data/lib/datadog/tracing/contrib/racecar/event.rb +2 -6
  606. data/lib/datadog/tracing/contrib/racecar/events/batch.rb +2 -2
  607. data/lib/datadog/tracing/contrib/racecar/events/consume.rb +1 -1
  608. data/lib/datadog/tracing/contrib/racecar/events/message.rb +2 -2
  609. data/lib/datadog/tracing/contrib/racecar/integration.rb +1 -1
  610. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +11 -1
  611. data/lib/datadog/tracing/contrib/rack/ext.rb +28 -0
  612. data/lib/datadog/tracing/contrib/rack/header_collection.rb +1 -1
  613. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +55 -32
  614. data/lib/datadog/tracing/contrib/rack/integration.rb +1 -1
  615. data/lib/datadog/tracing/contrib/rack/middlewares.rb +83 -44
  616. data/lib/datadog/tracing/contrib/rack/patcher.rb +2 -2
  617. data/lib/datadog/tracing/contrib/rack/request_queue.rb +4 -3
  618. data/lib/datadog/tracing/contrib/rack/route_inference.rb +67 -0
  619. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +126 -2
  620. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +6 -3
  621. data/lib/datadog/tracing/contrib/rails/ext.rb +3 -1
  622. data/lib/datadog/tracing/contrib/rails/integration.rb +2 -2
  623. data/lib/datadog/tracing/contrib/rails/log_injection.rb +2 -2
  624. data/lib/datadog/tracing/contrib/rails/middlewares.rb +3 -3
  625. data/lib/datadog/tracing/contrib/rails/patcher.rb +4 -2
  626. data/lib/datadog/tracing/contrib/rails/runner.rb +64 -40
  627. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +10 -8
  628. data/lib/datadog/tracing/contrib/rake/integration.rb +1 -1
  629. data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +2 -2
  630. data/lib/datadog/tracing/contrib/redis/ext.rb +3 -2
  631. data/lib/datadog/tracing/contrib/redis/integration.rb +2 -2
  632. data/lib/datadog/tracing/contrib/redis/patcher.rb +4 -4
  633. data/lib/datadog/tracing/contrib/redis/quantize.rb +2 -2
  634. data/lib/datadog/tracing/contrib/redis/tags.rb +2 -7
  635. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +6 -4
  636. data/lib/datadog/tracing/contrib/registerable.rb +11 -0
  637. data/lib/datadog/tracing/contrib/registry.rb +1 -1
  638. data/lib/datadog/tracing/contrib/resque/integration.rb +1 -1
  639. data/lib/datadog/tracing/contrib/resque/resque_job.rb +2 -1
  640. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +5 -2
  641. data/lib/datadog/tracing/contrib/rest_client/ext.rb +4 -2
  642. data/lib/datadog/tracing/contrib/rest_client/integration.rb +1 -1
  643. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +8 -9
  644. data/lib/datadog/tracing/contrib/roda/ext.rb +1 -0
  645. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +8 -3
  646. data/lib/datadog/tracing/contrib/roda/integration.rb +1 -1
  647. data/lib/datadog/tracing/contrib/semantic_logger/instrumentation.rb +1 -1
  648. data/lib/datadog/tracing/contrib/semantic_logger/integration.rb +1 -1
  649. data/lib/datadog/tracing/contrib/sequel/database.rb +6 -5
  650. data/lib/datadog/tracing/contrib/sequel/dataset.rb +2 -1
  651. data/lib/datadog/tracing/contrib/sequel/integration.rb +1 -1
  652. data/lib/datadog/tracing/contrib/sequel/utils.rb +1 -6
  653. data/lib/datadog/tracing/contrib/shoryuken/integration.rb +1 -1
  654. data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +1 -0
  655. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +1 -0
  656. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +5 -1
  657. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +2 -0
  658. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +2 -0
  659. data/lib/datadog/tracing/contrib/sidekiq/integration.rb +1 -1
  660. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/heartbeat.rb +2 -0
  661. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/job_fetch.rb +1 -0
  662. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/redis_info.rb +1 -0
  663. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/scheduled_poller.rb +2 -0
  664. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/stop.rb +1 -0
  665. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +8 -4
  666. data/lib/datadog/tracing/contrib/sidekiq/utils.rb +1 -1
  667. data/lib/datadog/tracing/contrib/sidekiq.rb +1 -0
  668. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +5 -1
  669. data/lib/datadog/tracing/contrib/sinatra/ext.rb +1 -0
  670. data/lib/datadog/tracing/contrib/sinatra/integration.rb +1 -1
  671. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +1 -0
  672. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +40 -39
  673. data/lib/datadog/tracing/contrib/sneakers/integration.rb +15 -4
  674. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +1 -0
  675. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +1 -1
  676. data/lib/datadog/tracing/contrib/status_range_matcher.rb +13 -1
  677. data/lib/datadog/tracing/contrib/stripe/integration.rb +1 -1
  678. data/lib/datadog/tracing/contrib/stripe/request.rb +2 -2
  679. data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +1 -0
  680. data/lib/datadog/tracing/contrib/sucker_punch/integration.rb +1 -1
  681. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +6 -0
  682. data/lib/datadog/tracing/contrib/trilogy/ext.rb +1 -1
  683. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -6
  684. data/lib/datadog/tracing/contrib/trilogy/integration.rb +1 -1
  685. data/lib/datadog/tracing/contrib/utils/quantization/{hash.rb → hash_formatter.rb} +15 -13
  686. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +6 -6
  687. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +31 -0
  688. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +50 -0
  689. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +18 -0
  690. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  691. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  692. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +49 -0
  693. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  694. data/lib/datadog/tracing/contrib/waterdrop.rb +42 -0
  695. data/lib/datadog/tracing/contrib.rb +9 -0
  696. data/lib/datadog/tracing/diagnostics/environment_logger.rb +12 -4
  697. data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
  698. data/lib/datadog/tracing/distributed/baggage.rb +133 -12
  699. data/lib/datadog/tracing/distributed/datadog.rb +17 -16
  700. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +11 -26
  701. data/lib/datadog/tracing/distributed/helpers.rb +1 -1
  702. data/lib/datadog/tracing/distributed/none.rb +4 -2
  703. data/lib/datadog/tracing/distributed/propagation.rb +39 -4
  704. data/lib/datadog/tracing/distributed/propagation_policy.rb +1 -1
  705. data/lib/datadog/tracing/distributed/trace_context.rb +107 -78
  706. data/lib/datadog/tracing/event.rb +5 -7
  707. data/lib/datadog/tracing/ext.rb +9 -0
  708. data/lib/datadog/tracing/flush.rb +1 -1
  709. data/lib/datadog/tracing/metadata/analytics.rb +1 -1
  710. data/lib/datadog/tracing/metadata/ext.rb +23 -1
  711. data/lib/datadog/tracing/metadata/tagging.rb +6 -6
  712. data/lib/datadog/tracing/pipeline/span_filter.rb +3 -1
  713. data/lib/datadog/tracing/pipeline/span_processor.rb +3 -1
  714. data/lib/datadog/tracing/pipeline.rb +2 -2
  715. data/lib/datadog/tracing/remote.rb +36 -13
  716. data/lib/datadog/tracing/sampling/ext.rb +2 -2
  717. data/lib/datadog/tracing/sampling/priority_sampler.rb +16 -1
  718. data/lib/datadog/tracing/sampling/rate_sampler.rb +8 -19
  719. data/lib/datadog/tracing/sampling/rule.rb +1 -1
  720. data/lib/datadog/tracing/sampling/rule_sampler.rb +77 -48
  721. data/lib/datadog/tracing/sampling/span/rule_parser.rb +3 -3
  722. data/lib/datadog/tracing/sampling/span/sampler.rb +0 -7
  723. data/lib/datadog/tracing/span.rb +2 -2
  724. data/lib/datadog/tracing/span_event.rb +12 -12
  725. data/lib/datadog/tracing/span_link.rb +12 -12
  726. data/lib/datadog/tracing/span_operation.rb +63 -19
  727. data/lib/datadog/tracing/sync_writer.rb +1 -2
  728. data/lib/datadog/tracing/trace_digest.rb +28 -23
  729. data/lib/datadog/tracing/trace_operation.rb +209 -98
  730. data/lib/datadog/tracing/trace_segment.rb +2 -2
  731. data/lib/datadog/tracing/tracer.rb +113 -52
  732. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  733. data/lib/datadog/tracing/transport/http/traces.rb +4 -52
  734. data/lib/datadog/tracing/transport/http.rb +15 -9
  735. data/lib/datadog/tracing/transport/io/client.rb +11 -14
  736. data/lib/datadog/tracing/transport/io/traces.rb +28 -34
  737. data/lib/datadog/tracing/transport/statistics.rb +1 -1
  738. data/lib/datadog/tracing/transport/trace_formatter.rb +22 -0
  739. data/lib/datadog/tracing/transport/traces.rb +14 -82
  740. data/lib/datadog/tracing/workers.rb +4 -3
  741. data/lib/datadog/tracing/writer.rb +1 -1
  742. data/lib/datadog/tracing.rb +2 -2
  743. data/lib/datadog/version.rb +2 -2
  744. data/lib/datadog.rb +14 -0
  745. metadata +201 -40
  746. data/ext/datadog_profiling_native_extension/clock_id_noop.c +0 -21
  747. data/ext/libdatadog_api/macos_development.md +0 -26
  748. data/lib/datadog/appsec/assets/waf_rules/processors.json +0 -321
  749. data/lib/datadog/appsec/assets/waf_rules/scanners.json +0 -1023
  750. data/lib/datadog/appsec/configuration/settings.rb +0 -342
  751. data/lib/datadog/appsec/contrib/rails/ext.rb +0 -13
  752. data/lib/datadog/appsec/processor/rule_merger.rb +0 -171
  753. data/lib/datadog/appsec/processor.rb +0 -107
  754. data/lib/datadog/core/remote/transport/http/api.rb +0 -53
  755. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  756. data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
  757. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  758. data/lib/datadog/core/transport/http/api/spec.rb +0 -36
  759. data/lib/datadog/di/configuration/settings.rb +0 -212
  760. data/lib/datadog/di/transport/http/api.rb +0 -42
  761. data/lib/datadog/di/transport/http/client.rb +0 -47
  762. data/lib/datadog/error_tracking/configuration/settings.rb +0 -63
  763. data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
  764. data/lib/datadog/tracing/transport/http/api.rb +0 -44
  765. data/lib/datadog/tracing/workers/trace_writer.rb +0 -199
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../core/utils/time'
4
+ require_relative '../ruby_version'
5
+ require_relative 'fatal_exceptions'
4
6
 
5
7
  # rubocop:disable Lint/AssignmentInCondition
8
+ # rubocop:disable Style/AndOr
6
9
 
7
10
  module Datadog
8
11
  module DI
@@ -52,6 +55,17 @@ module Datadog
52
55
  # (however, Probe instances can be replaced by OpenStruct instances
53
56
  # providing the same interface with not much effort).
54
57
  #
58
+ # Instrumenter (this class) is responsible for building snapshots.
59
+ # This is because to capture values on method entry, those values need to
60
+ # be duplicated or serialized into immutable values to prevent their
61
+ # modification by the instrumented method. Therefore this class must
62
+ # do at least some serialization/snapshot building and to keep the code
63
+ # well-encapsulated, all serialization/snapshot building should thus be
64
+ # initiated from this class rather than downstream code.
65
+ #
66
+ # As a consequence of Instrumenter building snapshots, it should not
67
+ # expose TracePoint objects to any downstream code.
68
+ #
55
69
  # @api private
56
70
  class Instrumenter
57
71
  def initialize(settings, serializer, logger, code_tracker: nil, telemetry: nil)
@@ -77,9 +91,22 @@ module Datadog
77
91
  # from the method but from outside of the method).
78
92
  Location = Struct.new(:path, :lineno, :label)
79
93
 
80
- def hook_method(probe, &block)
81
- unless block
82
- raise ArgumentError, 'block is required'
94
+ # Method probes can only target instance methods. The implementation uses
95
+ # Module#prepend with a module that defines an instance method matching the
96
+ # probe's target — class/singleton methods (def self.foo, module_function)
97
+ # are not reachable via prepend on the class itself. Line probes are
98
+ # unaffected since they install via TracePoint, not method dispatch.
99
+ def hook_method(probe, responder)
100
+ # Reject method probes targeting any class or module in the Datadog
101
+ # namespace. The tracer uses these classes internally while building
102
+ # and dispatching probe snapshots; allowing a method probe on them
103
+ # would let DI's own code paths re-enter the probe wrapper. Line
104
+ # probes are unaffected (TracePoint is self-disabling during its
105
+ # callback) and are not rejected here.
106
+ type_name = probe.type_name
107
+ if type_name && datadog_namespace_type_name?(type_name)
108
+ raise Error::ProbeTargetForbidden,
109
+ "Method probes on the Datadog namespace are not permitted: #{type_name}##{probe.method_name}"
83
110
  end
84
111
 
85
112
  lock.synchronize do
@@ -90,10 +117,9 @@ module Datadog
90
117
  end
91
118
 
92
119
  cls = symbolize_class_name(probe.type_name)
93
- serializer = self.serializer
94
- method_name = probe.method_name
95
- loc = begin
96
- cls.instance_method(method_name).source_location
120
+ method_name = probe.method_name!
121
+ target_method = begin
122
+ cls.instance_method(method_name)
97
123
  rescue NameError
98
124
  # The target method is not defined.
99
125
  # This could be because it will be explicitly defined later
@@ -101,80 +127,97 @@ module Datadog
101
127
  # or the method is virtual (provided by a method_missing handler).
102
128
  # In these cases we do not have a source location for the
103
129
  # target method here.
130
+ nil
131
+ end
132
+
133
+ # Reject method probes whose target method resolves to Kernel#lambda,
134
+ # including inherited targets. Every class inherits Kernel#lambda, so a
135
+ # probe naming any type that does not override lambda (Object#lambda,
136
+ # String#lambda, a plain user class, ...) prepends the wrapper ahead of
137
+ # Kernel#lambda and reaches the wrapper's super(&block) path. On
138
+ # Ruby 3.3+ that path raises ArgumentError for every lambda { ... }
139
+ # call site passing a captured block, breaking lambda creation
140
+ # process-wide. Resolving the method owner catches the inherited case
141
+ # that a textual "Kernel" name match misses; a type that defines its
142
+ # own lambda has a different owner and is left alone. Probing lambda
143
+ # creation has no legitimate customer use case. See #5560.
144
+ if method_name == "lambda" && target_method&.owner == ::Kernel
145
+ raise Error::ProbeTargetForbidden,
146
+ "Method probes on Kernel#lambda are not permitted: #{probe.type_name}##{method_name}"
104
147
  end
105
- rate_limiter = probe.rate_limiter
106
- settings = self.settings
148
+
149
+ loc = target_method&.source_location
150
+ instrumenter = self
107
151
 
108
152
  mod = Module.new do
109
- define_method(method_name) do |*args, **kwargs, &target_block| # steep:ignore
110
- if rate_limiter.nil? || rate_limiter.allow?
111
- # Arguments may be mutated by the method, therefore
112
- # they need to be serialized prior to method invocation.
113
- entry_args = if probe.capture_snapshot?
114
- serializer.serialize_args(args, kwargs,
115
- depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
116
- attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count)
153
+ # Argument delegation follows docs/Delegation.md: on Ruby 3+
154
+ # positional and keyword arguments are captured and forwarded
155
+ # separately; on Ruby < 3 everything is captured into a single
156
+ # splat that ruby2_keywords flags, which is the only way to forward
157
+ # a trailing hash while preserving its positional-vs-keyword
158
+ # identity. The pre-3 branch is what lets an empty trailing hash
159
+ # (e.g. foo('x', {})) reach the original method intact.
160
+ #
161
+ # Re-entrancy guard: if we are already inside a DI method-probe
162
+ # callback, skip DI processing and call the original method
163
+ # directly. This prevents SystemStackError when a method probe is
164
+ # set on a stdlib method that DI itself calls during method-probe
165
+ # snapshot building (e.g., String#length, Hash#each). Line probes
166
+ # do not enter the guard — they rely on Ruby's TracePoint, which
167
+ # self-disables during its own callback. Nested invocations during
168
+ # DI processing also bypass the rate limiter: they are not
169
+ # user-observable probe firings, just internal calls that happen to
170
+ # land on a probed method. Guard storage is fiber-local;
171
+ # DI.in_probe?/enter_probe/leave_probe are implemented in C and
172
+ # access it directly, bypassing Thread#[]/Thread#[]= method dispatch
173
+ # so user probes on those cannot recurse.
174
+ #
175
+ # The original method is invoked via super. That super call lives
176
+ # in a block passed to #run_method_probe and captures its binding
177
+ # from inside this define_method block — the only place super
178
+ # resolves to the prepended-on class's method. #run_method_probe
179
+ # invokes it with yield, which does not dispatch Proc#call, so a
180
+ # user probe on Proc#call cannot intercept the trampoline, and no
181
+ # Proc is allocated for the block.
182
+ if RubyVersion.is?('>= 3')
183
+ define_method(method_name) do |*args, **kwargs, &target_block| # steep:ignore NoMethod
184
+ # steep:ignore FallbackAny below: Steep cannot narrow the
185
+ # **kwargs parameter inside this define_method block, so it
186
+ # falls back to untyped at the super and run_method_probe sites.
187
+ if DI.in_probe?
188
+ return super(*args, **kwargs, &target_block) # steep:ignore FallbackAny
117
189
  end
118
- start_time = Core::Utils::Time.get_time
119
- # Under Ruby 2.6 we cannot just call super(*args, **kwargs)
120
- # for methods defined via method_missing.
121
- rv = if args.any?
122
- if kwargs.any?
123
- super(*args, **kwargs, &target_block)
124
- else
125
- super(*args, &target_block)
126
- end
127
- elsif kwargs.any?
128
- super(**kwargs, &target_block)
129
- else
130
- super(&target_block)
190
+
191
+ instrumenter.run_method_probe(
192
+ args, kwargs, target_block, # steep:ignore FallbackAny
193
+ self,
194
+ probe, responder,
195
+ loc, method_name,
196
+ ) do
197
+ super(*args, **kwargs, &target_block) # steep:ignore FallbackAny
131
198
  end
132
- duration = Core::Utils::Time.get_time - start_time
133
- # The method itself is not part of the stack trace because
134
- # we are getting the stack trace from outside of the method.
135
- # Add the method in manually as the top frame.
136
- method_frame = if loc
137
- [Location.new(loc.first, loc.last, method_name)]
138
- else
139
- # For virtual and lazily-defined methods, we do not have
140
- # the original source location here, and they won't be
141
- # included in the stack trace currently.
142
- # TODO when begin/end trace points are added for local
143
- # variable capture in method probes, we should be able
144
- # to obtain actual method execution location and use
145
- # that location here.
146
- []
199
+ end
200
+ else
201
+ define_method(method_name) do |*args, &target_block| # steep:ignore NoMethod
202
+ if DI.in_probe?
203
+ return super(*args, &target_block)
147
204
  end
148
- caller_locs = method_frame + caller_locations # steep:ignore
149
- # TODO capture arguments at exit
150
- # & is to stop steep complaints, block is always present here.
151
- block&.call(probe: probe, rv: rv, duration: duration, caller_locations: caller_locs,
152
- serialized_entry_args: entry_args)
153
- rv
154
- else
155
- # stop standard from trying to mess up my code
156
- _ = 42
157
-
158
- # The necessity to invoke super in each of these specific
159
- # ways is very difficult to test.
160
- # Existing tests, even though I wrote many, still don't
161
- # cause a failure if I replace all of the below with a
162
- # simple super(*args, **kwargs, &target_block).
163
- # But, let's be safe and go through the motions in case
164
- # there is actually a legitimate need for the breakdown.
165
- # TODO figure out how to test this properly.
166
- if args.any?
167
- if kwargs.any?
168
- super(*args, **kwargs, &target_block)
169
- else
170
- super(*args, &target_block)
171
- end
172
- elsif kwargs.any?
173
- super(**kwargs, &target_block)
174
- else
175
- super(&target_block)
205
+
206
+ # The super block replays the original *args (closed over here)
207
+ # so the ruby2_keywords-flagged trailing hash keeps its
208
+ # identity. run_method_probe receives the serialization-split
209
+ # arguments separately; forwarding does not use them.
210
+ pos_args, kwargs = instrumenter.kwargs_from_splat(args)
211
+ instrumenter.run_method_probe(
212
+ pos_args, kwargs, target_block,
213
+ self,
214
+ probe, responder,
215
+ loc, method_name,
216
+ ) do
217
+ super(*args, &target_block)
176
218
  end
177
219
  end
220
+ ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true) # steep:ignore NoMethod
178
221
  end
179
222
  end
180
223
 
@@ -186,6 +229,8 @@ module Datadog
186
229
 
187
230
  probe.instrumentation_module = mod
188
231
  cls.send(:prepend, mod)
232
+
233
+ DI.instrumented_count_inc(:method)
189
234
  end
190
235
  end
191
236
 
@@ -198,6 +243,8 @@ module Datadog
198
243
  if mod = probe.instrumentation_module
199
244
  mod.send(:remove_method, probe.method_name)
200
245
  probe.instrumentation_module = nil
246
+
247
+ DI.instrumented_count_dec(:method)
201
248
  end
202
249
  end
203
250
  end
@@ -207,11 +254,7 @@ module Datadog
207
254
  # not for eval'd code, unless the eval'd code is associated with
208
255
  # a file name and client invokes this method with the correct
209
256
  # file name for the eval'd code.
210
- def hook_line(probe, &block)
211
- unless block
212
- raise ArgumentError, 'No block given to hook_line'
213
- end
214
-
257
+ def hook_line(probe, responder)
215
258
  lock.synchronize do
216
259
  if probe.instrumentation_trace_point
217
260
  # Already instrumented, warn?
@@ -220,7 +263,6 @@ module Datadog
220
263
  end
221
264
 
222
265
  line_no = probe.line_no!
223
- rate_limiter = probe.rate_limiter
224
266
 
225
267
  # Memoize the value to ensure this method always uses the same
226
268
  # value for the setting.
@@ -232,7 +274,14 @@ module Datadog
232
274
 
233
275
  iseq = nil
234
276
  if code_tracker
235
- ret = code_tracker.iseqs_for_path_suffix(probe.file) # steep:ignore
277
+ # Steep: Complex type narrowing (before calling hook_line,
278
+ # we check that probe.line? is true which itself checks that probe.file is not nil)
279
+ # Annotation do not work here as `file` is a method on probe, not a local variable.
280
+ ret = if code_tracker.respond_to?(:iseq_for_line)
281
+ code_tracker.iseq_for_line(probe.file, line_no) # steep:ignore ArgumentTypeMismatch
282
+ else
283
+ code_tracker.iseqs_for_path_suffix(probe.file) # steep:ignore ArgumentTypeMismatch
284
+ end
236
285
  unless ret
237
286
  if permit_untargeted_trace_points
238
287
  # Continue withoout targeting the trace point.
@@ -250,16 +299,16 @@ module Datadog
250
299
  # to instrument and install the hook when the file in
251
300
  # question is loaded (and hopefully, by then code tracking
252
301
  # is active, otherwise the line will never be instrumented.)
253
- raise_if_probe_in_loaded_features(probe)
254
- raise Error::DITargetNotDefined, "File not in code tracker registry: #{probe.file}"
302
+ raise_if_probe_in_loaded_features(probe, line_no, code_tracker)
303
+ raise Error::DITargetNotDefined, "File not in code tracker registry: #{probe.file}:#{line_no}"
255
304
  end
256
305
  end
257
306
  elsif !permit_untargeted_trace_points
258
307
  # Same as previous comment, if untargeted trace points are not
259
308
  # explicitly defined, and we do not have code tracking, do not
260
309
  # instrument the method.
261
- raise_if_probe_in_loaded_features(probe)
262
- raise Error::DITargetNotDefined, "File not in code tracker registry: #{probe.file}"
310
+ raise_if_probe_in_loaded_features(probe, line_no, nil)
311
+ raise Error::DITargetNotDefined, "File not in code tracker registry: #{probe.file}:#{line_no}"
263
312
  end
264
313
 
265
314
  if ret
@@ -288,36 +337,14 @@ module Datadog
288
337
  [:line]
289
338
  end
290
339
  tp = TracePoint.new(*types) do |tp|
291
- begin
292
- # If trace point is not targeted, we must verify that the invocation
293
- # is the file & line that we want, because untargeted trace points
294
- # are invoked for *each* line of Ruby executed.
295
- # TODO find out exactly when the path in trace point is relative.
296
- # Looks like this is the case when line trace point is not targeted?
297
- if iseq || tp.lineno == probe.line_no && (
298
- probe.file == tp.path || probe.file_matches?(tp.path)
299
- )
300
- if rate_limiter.nil? || rate_limiter.allow?
301
- # & is to stop steep complaints, block is always present here.
302
- block&.call(probe: probe, trace_point: tp, caller_locations: caller_locations)
303
- end
304
- end
305
- rescue => exc
306
- raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
307
- logger.debug { "di: unhandled exception in line trace point: #{exc.class}: #{exc}" }
308
- telemetry&.report(exc, description: "Unhandled exception in line trace point")
309
- # TODO test this path
310
- end
311
- rescue => exc
312
- raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
313
- logger.debug { "di: unhandled exception in line trace point: #{exc.class}: #{exc}" }
314
- telemetry&.report(exc, description: "Unhandled exception in line trace point")
315
- # TODO test this path
340
+ line_trace_point_callback(probe, iseq, responder, tp)
316
341
  end
317
342
 
318
- # TODO internal check - remove or use a proper exception
343
+ # Internal sanity check - untargeted trace points create a huge
344
+ # performance impact, and we absolutely do not want to set them
345
+ # accidentally.
319
346
  if !iseq && !permit_untargeted_trace_points
320
- raise "Trying to use an untargeted trace point when user did not permit it"
347
+ raise Error::InternalError, "Trying to use an untargeted trace point when user did not permit it"
321
348
  end
322
349
 
323
350
  lock.synchronize do
@@ -330,12 +357,16 @@ module Datadog
330
357
  # actual_path could be nil if we don't use targeted trace points.
331
358
  probe.instrumented_path = actual_path
332
359
 
333
- if iseq
360
+ # TracePoint#enable returns false when it succeeds.
361
+ rv = if iseq
334
362
  tp.enable(target: iseq, target_line: line_no)
335
363
  else
336
364
  tp.enable
337
365
  end
338
- # TracePoint#enable returns false when it succeeds.
366
+
367
+ DI.instrumented_count_inc(:line)
368
+
369
+ rv
339
370
  end
340
371
  true
341
372
  end
@@ -345,15 +376,17 @@ module Datadog
345
376
  if tp = probe.instrumentation_trace_point
346
377
  tp.disable
347
378
  probe.instrumentation_trace_point = nil
379
+
380
+ DI.instrumented_count_dec(:line)
348
381
  end
349
382
  end
350
383
  end
351
384
 
352
- def hook(probe, &block)
385
+ def hook(probe, responder)
353
386
  if probe.method?
354
- hook_method(probe, &block)
387
+ hook_method(probe, responder)
355
388
  elsif probe.line?
356
- hook_line(probe, &block)
389
+ hook_line(probe, responder)
357
390
  else
358
391
  # TODO add test coverage for this path
359
392
  logger.debug { "di: unknown probe type to hook: #{probe}" }
@@ -371,38 +404,493 @@ module Datadog
371
404
  end
372
405
  end
373
406
 
407
+ class << self
408
+ def get_local_variables(trace_point)
409
+ # binding appears to be constructed on access, therefore
410
+ # 1) we should attempt to cache it and
411
+ # 2) we should not call +binding+ until we actually need variable values.
412
+ binding = trace_point.binding
413
+
414
+ # steep hack - should never happen
415
+ return {} unless binding
416
+
417
+ binding.local_variables.each_with_object({}) do |name, map|
418
+ value = binding.local_variable_get(name)
419
+ map[name] = value
420
+ end
421
+ end
422
+ end
423
+
374
424
  private
375
425
 
376
426
  attr_reader :lock
377
427
 
378
- def raise_if_probe_in_loaded_features(probe)
379
- return unless probe.file
428
+ # Body of the method probe wrapper. Extracted from the define_method
429
+ # block in #hook_method so the begin/ensure structure can use normal
430
+ # indentation. The original method is invoked with yield, calling the
431
+ # block passed by #hook_method; that block captures super's binding
432
+ # from inside the define_method block, the only place super resolves
433
+ # to the prepended-on class's method.
434
+ #
435
+ # Performance: this method takes eight positional parameters, not
436
+ # keyword parameters, deliberately. Every probed method invocation
437
+ # passes through here on the firing/disabled/rate-limited paths.
438
+ # Keyword-argument calls in Ruby allocate a fresh Hash on every call
439
+ # to materialize the keyword bindings; with eight keyword arguments
440
+ # (~300-1000 ns of allocation per invocation), that hash dominated
441
+ # the wrapper's per-call overhead. Positional parameters skip the
442
+ # allocation entirely. The cost is a long unlabeled signature and
443
+ # an unlabeled call site in #hook_method — both small price for
444
+ # eliminating per-call allocation on the hot path. The argument
445
+ # order matches #hook_method's call site exactly; do not reorder
446
+ # without updating both sides.
447
+ #
448
+ # @param args [Array] positional arguments passed to the probed method
449
+ # @param kwargs [Hash{Symbol => Object}] keyword arguments passed to the probed method
450
+ # @param target_block [Proc, nil] block argument passed to the probed method
451
+ # @param target_self [Object] the receiver of the probed method invocation
452
+ # @param probe [Datadog::DI::Probe] the probe whose callback this invocation runs
453
+ # @param responder [#probe_executed_callback, #probe_condition_evaluation_failed_callback] callback target invoked with the built Context
454
+ # @param loc [Array(String, Integer), nil] source location of the probed method, or nil for virtual/lazily-defined methods
455
+ # @param method_name [String] name of the probed method, used as the synthetic top stack frame label
456
+ # @yield invokes the original method via super and returns its value
457
+ # @return [Object] the original method's return value, or re-raises its exception
458
+ def run_method_probe(args, kwargs, target_block, target_self,
459
+ probe, responder, loc, method_name)
460
+ # Disabled probe: skip DI processing entirely. enter_probe is not
461
+ # called on this path so the guard is never set — there is no DI
462
+ # work to guard, and any nested probed methods invoked by the
463
+ # original method should fire normally without short-circuit.
464
+ return yield unless probe.enabled?
465
+
466
+ DI.enter_probe
467
+ begin
468
+ di_start_time = Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID)
469
+
470
+ continue = true
471
+ if condition = probe.condition
472
+ begin
473
+ # This context will be recreated later, unlike for line probes.
474
+ #
475
+ # We do not need the stack for condition evaluation, therefore
476
+ # stack is not passed to Context here.
477
+ context = Context.new(
478
+ locals: serializer.combine_args(args, kwargs, target_self),
479
+ target_self: target_self,
480
+ probe: probe, settings: settings, serializer: serializer,
481
+ )
482
+ continue = condition.satisfied?(context)
483
+ rescue Exception => exc # standard:disable Lint/RescueException
484
+ Datadog::DI.reraise_if_fatal(exc)
485
+ # Evaluation error exception can be raised for "expected"
486
+ # errors, we probably need another setting to control whether
487
+ # these exceptions are propagated.
488
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions &&
489
+ !exc.is_a?(DI::Error::ExpressionEvaluationError)
490
+
491
+ if context
492
+ # We want to report evaluation errors for conditions
493
+ # as probe snapshots. However, if we failed to create
494
+ # the context, we won't be able to report anything as
495
+ # the probe notifier builder requires a context.
496
+ begin
497
+ responder.probe_condition_evaluation_failed_callback(context, exc)
498
+ rescue Exception => nested_exc # standard:disable Lint/RescueException
499
+ Datadog::DI.reraise_if_fatal(nested_exc)
500
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
501
+
502
+ logger.debug { "di: error in probe condition evaluation failed callback: #{nested_exc.class}: #{nested_exc.message}" }
503
+ telemetry&.report(nested_exc, description: "Error in probe condition evaluation failed callback")
504
+ end
505
+ else
506
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
507
+
508
+ logger.debug { "di: error evaluating condition without context (tracer bug?): #{exc.class}: #{exc.message}" }
509
+ telemetry&.report(exc, description: "Error evaluating condition without context")
510
+ # If execution gets here, there is probably a bug in the tracer.
511
+ end
512
+
513
+ continue = false
514
+ end
515
+ end
516
+
517
+ rate_limiter = probe.rate_limiter
518
+ if continue and rate_limiter.nil? || rate_limiter.allow?
519
+ # Arguments may be mutated by the method, therefore
520
+ # they need to be serialized prior to method invocation.
521
+ serialized_entry_args = if probe.capture_snapshot?
522
+ serializer.serialize_args(args, kwargs, target_self,
523
+ depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
524
+ attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count)
525
+ end
526
+ # We intentionally do not use Core::Utils::Time.get_time
527
+ # here because the time provider may be overridden by the
528
+ # customer, and DI is not allowed to invoke customer code.
529
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
530
+
531
+ di_duration = Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID) - di_start_time
532
+
533
+ # Release re-entrancy guard for the original method so that
534
+ # probes on other methods (or recursive calls) fire normally
535
+ # during user code execution.
536
+ DI.leave_probe
537
+
538
+ rv = nil
539
+ begin
540
+ # yield invokes the super block without dispatching Proc#call,
541
+ # so a user probe on Proc#call cannot intercept the trampoline.
542
+ # The guard was just released above so nested probes on the
543
+ # customer method body fire normally — but Proc#call must
544
+ # remain bypassed regardless, because the disabled-probe early
545
+ # return at the top of this method also invokes the block.
546
+ rv = yield
547
+ rescue Exception => exc # standard:disable Lint/RescueException
548
+ Datadog::DI.reraise_if_fatal(exc)
549
+ # We will raise the (non-fatal) exception captured here later,
550
+ # after the instrumentation callback runs.
551
+ end
552
+
553
+ # Re-acquire re-entrancy guard for DI post-processing
554
+ # (building context, notification callback).
555
+ DI.enter_probe
556
+
557
+ end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
558
+ duration = end_time - start_time
559
+
560
+ # Restart DI timer.
561
+ # The DI execution duration covers time spent in DI code before
562
+ # the customer method is invoked and time spent in DI code
563
+ # after the customer method finishes.
564
+ di_start_time = Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID)
380
565
 
381
- # If the probe file is in the list of loaded files
382
- # (as per $LOADED_FEATURES, using either exact or suffix match),
383
- # raise an error indicating that
384
- # code tracker is missing the loaded file because the file
385
- # won't be loaded again (DI only works in production environments
386
- # that do not normally reload code).
387
- if $LOADED_FEATURES.include?(probe.file)
388
- raise Error::DITargetNotInRegistry, "File loaded but is not in code tracker registry: #{probe.file}"
566
+ # The method itself is not part of the stack trace because
567
+ # we are getting the stack trace from outside of the method.
568
+ # Add the method in manually as the top frame.
569
+ method_frame = if loc
570
+ [Location.new(loc.first, loc.last, method_name)]
571
+ else
572
+ # For virtual and lazily-defined methods, we do not have
573
+ # the original source location here, and they won't be
574
+ # included in the stack trace currently.
575
+ # TODO when begin/end trace points are added for local
576
+ # variable capture in method probes, we should be able
577
+ # to obtain actual method execution location and use
578
+ # that location here.
579
+ []
580
+ end
581
+ # depth 2 skips run_method_probe and the wrapper define_method
582
+ # block, leaving the user's call site as the first returned frame.
583
+ # caller_locations is RBS-nilable but never nil at this depth.
584
+ caller_locs = method_frame + (caller_locations(2) || [])
585
+ # TODO capture arguments at exit
586
+
587
+ context = Context.new(locals: nil, target_self: target_self,
588
+ probe: probe, settings: settings, serializer: serializer,
589
+ serialized_entry_args: serialized_entry_args,
590
+ caller_locations: caller_locs,
591
+ return_value: rv, duration: duration, exception: exc,)
592
+
593
+ begin
594
+ responder.probe_executed_callback(context)
595
+
596
+ check_and_disable_if_exceeded(probe, responder, di_start_time, di_duration)
597
+ rescue Exception => di_exc # standard:disable Lint/RescueException
598
+ Datadog::DI.reraise_if_fatal(di_exc)
599
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
600
+
601
+ logger.debug { "di: unhandled exception in method probe: #{di_exc.class}: #{di_exc.message}" }
602
+ telemetry&.report(di_exc, description: "Unhandled exception in method probe")
603
+ end
604
+
605
+ if exc
606
+ raise exc
607
+ else
608
+ rv
609
+ end
610
+ else
611
+ # Condition not satisfied or rate-limited: skip DI processing
612
+ # and call super. (The disabled-probe case is handled by the
613
+ # early return at the top of this method, so it never reaches
614
+ # this branch.)
615
+ #
616
+ # The guard must be released here (not relied on the ensure)
617
+ # because the super block invokes the customer's method, which
618
+ # may call other probed methods. Those nested probes should fire
619
+ # normally — they would not if the guard were still set,
620
+ # because the wrapper's early-return short-circuits when
621
+ # DI.in_probe? is true.
622
+ #
623
+ # No post-processing in this branch, so we don't re-acquire
624
+ # the guard after super. The ensure's leave_probe below is
625
+ # a no-op here (guard already cleared) and serves the
626
+ # firing branch above, where post-processing re-enters.
627
+ DI.leave_probe
628
+ # yield bypasses Proc#call dispatch — see the firing branch
629
+ # above for the same rationale.
630
+ yield
631
+ end
632
+ ensure
633
+ DI.leave_probe
634
+ end
635
+ end
636
+ # `run_method_probe` is documentation-private (`# @api private`) but must
637
+ # be lexically public so that the prepended wrapper can call it without
638
+ # `.send`. The wrapper hot path is reached when a probed customer method
639
+ # is invoked, and a customer probe on `Object#send` / `Kernel#send`
640
+ # would intercept any `.send` call before the wrapper had a chance to
641
+ # set the re-entrancy guard, causing infinite recursion. Calling the
642
+ # method directly avoids `Object#send` dispatch entirely.
643
+ public :run_method_probe
644
+
645
+ # Splits a Ruby < 3 splat-captured argument list into positional
646
+ # arguments and the trailing keyword-argument hash, for argument
647
+ # serialization only. Returns `[positional_args, kwargs]`. Forwarding
648
+ # does not use this — the wrapper's do_super replays the original
649
+ # splat so the trailing hash keeps its keyword identity.
650
+ #
651
+ # A trailing hash is treated as keyword arguments regardless of how it
652
+ # was passed at the call site, matching the previous `**kwargs`
653
+ # capture (which, on Ruby 2.x, absorbed a trailing hash into kwargs).
654
+ # The ruby2_keywords flag is intentionally not consulted here: callers
655
+ # that pass a hash positionally (e.g. `m(opts)`) still expect it shown
656
+ # as a keyword argument, consistent with Ruby 2.x serialization.
657
+ #
658
+ # The trailing-element test goes through DI.hash? (a C type check)
659
+ # rather than `last.is_a?(Hash)`: kwargs_from_splat runs before the
660
+ # re-entrancy guard is set, so a customer method probe on Kernel#is_a?
661
+ # would otherwise re-enter the wrapper and recurse. DI.hash? is a
662
+ # singleton method and cannot be targeted by a method probe.
663
+ #
664
+ # Defined only on Ruby < 3; the Ruby 3+ wrapper captures keyword
665
+ # arguments directly and never calls this.
666
+ if RubyVersion.is?('< 3')
667
+ def kwargs_from_splat(args)
668
+ last = args.last
669
+ if DI.hash?(last)
670
+ [args[0...-1] || [], last]
671
+ else
672
+ [args, {}]
673
+ end
674
+ end
675
+ # Lexically public for the same reason as #run_method_probe: the
676
+ # prepended wrapper must call it without `.send`, which a customer
677
+ # probe on Object#send could intercept.
678
+ public :kwargs_from_splat
679
+ end
680
+
681
+ def line_trace_point_callback(probe, iseq, responder, tp)
682
+ di_start_time = Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID)
683
+
684
+ # Check if probe is enabled before doing any processing
685
+ return unless probe.enabled?
686
+
687
+ # If trace point is not targeted, we must verify that the invocation
688
+ # is the file & line that we want, because untargeted trace points
689
+ # are invoked for *each* line of Ruby executed.
690
+ # TODO find out exactly when the path in trace point is relative.
691
+ # Looks like this is the case when line trace point is not targeted?
692
+ unless iseq
693
+ return unless tp.lineno == probe.line_no && ( # standard:disable Style/UnlessLogicalOperators
694
+ probe.file == tp.path || probe.file_matches?(tp.path)
695
+ )
696
+ end
697
+
698
+ # We set the trace point on :return to be able to instrument
699
+ # 'end' lines. This also causes the trace point to be invoked on
700
+ # non-'end' lines when a line raises an exception, since the
701
+ # exception causes the method to stop executing and stack unwends.
702
+ # We do not want two invocations of the trace point.
703
+ # Therefore, if a trace point is invoked with a :line event,
704
+ # mark it as such and ignore subsequent :return events.
705
+ if probe.executed_on_line?
706
+ return unless tp.event == :line
707
+ else
708
+ _ = 42 # stop standard from changing this code
709
+
710
+ if tp.event == :line
711
+ probe.executed_on_line!
712
+ end
713
+ end
714
+
715
+ if condition = probe.condition
716
+ begin
717
+ context = build_trace_point_context(probe, tp)
718
+ return unless condition.satisfied?(context)
719
+ rescue Exception => exc # standard:disable Lint/RescueException
720
+ Datadog::DI.reraise_if_fatal(exc)
721
+ # Evaluation error exception can be raised for "expected"
722
+ # errors, we probably need another setting to control whether
723
+ # these exceptions are propagated.
724
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions &&
725
+ !exc.is_a?(DI::Error::ExpressionEvaluationError)
726
+
727
+ if context
728
+ # We want to report evaluation errors for conditions
729
+ # as probe snapshots. However, if we failed to create
730
+ # the context, we won't be able to report anything as
731
+ # the probe notifier builder requires a context.
732
+ begin
733
+ responder.probe_condition_evaluation_failed_callback(context, condition, exc)
734
+ rescue Exception => nested_exc # standard:disable Lint/RescueException
735
+ Datadog::DI.reraise_if_fatal(nested_exc)
736
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
737
+
738
+ logger.debug { "di: error in probe condition evaluation failed callback: #{nested_exc.class}: #{nested_exc.message}" }
739
+ telemetry&.report(nested_exc, description: "Error in probe condition evaluation failed callback")
740
+ end
741
+
742
+ return
743
+ else
744
+ _ = 42 # stop standard from wrecking this code
745
+
746
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
747
+
748
+ logger.debug { "di: error evaluating condition without context (tracer bug?): #{exc.class}: #{exc.message}" }
749
+ telemetry&.report(exc, description: "Error evaluating condition without context")
750
+ # If execution gets here, there is probably a bug in the tracer.
751
+ end
752
+ end
389
753
  end
390
- # Ths is an expensive check
391
- $LOADED_FEATURES.each do |path|
392
- if Utils.path_matches_suffix?(path, probe.file)
393
- raise Error::DITargetNotInRegistry, "File matching probe path (#{probe.file}) was loaded and is not in code tracker registry: #{path}"
754
+
755
+ # In practice we should always have a rate limiter, but be safe
756
+ # and check that it is in fact set.
757
+ return if probe.rate_limiter && !probe.rate_limiter.allow?
758
+
759
+ # The context creation is relatively expensive and we don't
760
+ # want to run it if the callback won't be executed due to the
761
+ # rate limit.
762
+ # Thus the copy-paste of the creation call here.
763
+ context ||= build_trace_point_context(probe, tp)
764
+
765
+ responder.probe_executed_callback(context)
766
+
767
+ check_and_disable_if_exceeded(probe, responder, di_start_time)
768
+ rescue Exception => exc # standard:disable Lint/RescueException
769
+ Datadog::DI.reraise_if_fatal(exc)
770
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
771
+ logger.debug { "di: unhandled exception in line trace point: #{exc.class}: #{exc.message}" }
772
+ telemetry&.report(exc, description: "Unhandled exception in line trace point")
773
+ # TODO test this path
774
+ end
775
+
776
+ def build_trace_point_context(probe, tp)
777
+ stack = caller_locations
778
+ # We have two helper methods being invoked from the trace point
779
+ # handler block, remove them from the stack.
780
+ #
781
+ # According to steep stack may be nil.
782
+ stack&.shift(2)
783
+ Context.new(
784
+ locals: Instrumenter.get_local_variables(tp),
785
+ target_self: tp.self,
786
+ probe: probe,
787
+ settings: settings,
788
+ serializer: serializer,
789
+ path: tp.path,
790
+ caller_locations: stack,
791
+ )
792
+ end
793
+
794
+ # Circuit breaker: disables the probe if total CPU time consumed by
795
+ # DI processing exceeds the configured threshold.
796
+ def check_and_disable_if_exceeded(probe, responder, di_start_time, accumulated_duration = 0.0)
797
+ return unless max_processing_time = settings.dynamic_instrumentation.internal.max_processing_time
798
+
799
+ di_duration = accumulated_duration + Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID) - di_start_time
800
+ if di_duration > max_processing_time
801
+ logger.debug { "di: disabling probe: consumed #{di_duration}: #{probe}" }
802
+ # We disable the probe here rather than remove it to
803
+ # avoid a dependency on ProbeManager from Instrumenter.
804
+ probe.disable!
805
+ responder.probe_disabled_callback(probe, di_duration)
806
+ end
807
+ end
808
+
809
+ def raise_if_probe_in_loaded_features(probe, line_no, code_tracker)
810
+ return unless probe.file
811
+
812
+ # Find the loaded path matching the probe file. Case-sensitive
813
+ # matching is attempted first, with case-insensitive matching as a
814
+ # fallback (see the design comment in utils.rb, steps 5-8). Leading
815
+ # directory components are stripped so probes whose sourceFile carries
816
+ # a source-repo prefix that does not exist on disk still resolve to
817
+ # the loaded file — matching the behavior of
818
+ # CodeTracker#iseqs_for_path_suffix.
819
+ loaded_path = if $LOADED_FEATURES.include?(probe.file)
820
+ probe.file
821
+ else
822
+ # Expensive suffix check.
823
+ suffix = Utils.normalize_windows_separators(probe.file)
824
+ found = nil #: ::String?
825
+ [false, true].each do |case_insensitive|
826
+ working_suffix = suffix.dup
827
+ loop do
828
+ found = $LOADED_FEATURES.find do |path|
829
+ Utils.path_matches_suffix?(path, working_suffix, case_insensitive: case_insensitive)
830
+ end
831
+ break if found
832
+ break unless working_suffix.include?('/')
833
+ working_suffix.sub!(%r{.*/+}, '')
834
+ end
835
+ break if found
394
836
  end
837
+ found
838
+ end
839
+
840
+ return unless loaded_path
841
+
842
+ # Distinguish between "no iseqs at all" and "has per-method iseqs
843
+ # but none cover this line".
844
+ has_per_method = code_tracker&.send(:instance_variable_defined?, :@per_method_registry) &&
845
+ code_tracker.send(:per_method_registry).key?(loaded_path)
846
+
847
+ if has_per_method
848
+ raise Error::DITargetNotInRegistry,
849
+ "File #{loaded_path} is loaded and has per-method iseqs, " \
850
+ "but none cover line #{line_no}. " \
851
+ "The line may be in file-level setup code outside any method."
852
+ else
853
+ raise Error::DITargetNotInRegistry,
854
+ "File #{loaded_path} is loaded but has no surviving iseqs " \
855
+ "(whole-file iseq was garbage collected and no per-method iseqs remain). " \
856
+ "Line probes cannot target this file."
395
857
  end
396
858
  end
397
859
 
398
860
  # TODO test that this resolves qualified names e.g. A::B
399
861
  def symbolize_class_name(cls_name)
400
- Object.const_get(cls_name)
862
+ Object.const_get(cls_name) # steep:ignore ArgumentTypeMismatch
401
863
  rescue NameError => exc
402
- raise Error::DITargetNotDefined, "Class not defined: #{cls_name}: #{exc.class}: #{exc}"
864
+ raise Error::DITargetNotDefined, "Class not defined: #{cls_name}: #{exc.class}: #{exc.message}"
865
+ end
866
+
867
+ # Matches a class name in the Datadog namespace, tolerating the alias
868
+ # forms that Object.const_get resolves to the same top-level constants.
869
+ # Two such forms exist: an optional leading "::" (Ruby's root-namespace
870
+ # prefix), and any number of leading "Object::" segments. Top-level
871
+ # constants are constants of Object, so "Object::Datadog::DI::Instrumenter"
872
+ # resolves to Datadog::DI::Instrumenter; without stripping "Object::" a
873
+ # probe could name a tracer-internal class through an Object:: alias and
874
+ # bypass the rejection, then get prepended onto tracer internals. These
875
+ # are the only alias paths to the top-level Datadog: "Foo::Datadog" does
876
+ # not resolve through const_get for an arbitrary Foo. Anchored at \A so
877
+ # the match decides in O(prefix length) without scanning.
878
+ DATADOG_NAMESPACE_TYPE_NAME = /\A(?:::)?(?:Object::)*Datadog(?:::|\z)/
879
+ private_constant :DATADOG_NAMESPACE_TYPE_NAME
880
+
881
+ # Returns true when +cls_name+ names the +Datadog+ module itself or
882
+ # any class/module under it (e.g. +Datadog::Tracing::SpanOperation+).
883
+ # The check is purely textual on the probe's declared type name; it
884
+ # does not require the class to be loaded. A leading +::+ and any
885
+ # number of leading +Object::+ segments are treated the same as the
886
+ # bare form, since Object.const_get resolves those aliases to the same
887
+ # top-level constants.
888
+ def datadog_namespace_type_name?(cls_name)
889
+ DATADOG_NAMESPACE_TYPE_NAME.match?(cls_name)
403
890
  end
404
891
  end
405
892
  end
406
893
  end
407
894
 
408
895
  # rubocop:enable Lint/AssignmentInCondition
896
+ # rubocop:enable Style/AndOr