cw-datadog 2.23.0.2

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 (944) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5142 -0
  3. data/LICENSE +6 -0
  4. data/LICENSE-3rdparty.csv +7 -0
  5. data/LICENSE.Apache +200 -0
  6. data/LICENSE.BSD3 +24 -0
  7. data/NOTICE +4 -0
  8. data/README.md +24 -0
  9. data/bin/ddprofrb +15 -0
  10. data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
  11. data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +156 -0
  12. data/ext/datadog_profiling_native_extension/clock_id.h +23 -0
  13. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +55 -0
  14. data/ext/datadog_profiling_native_extension/clock_id_noop.c +21 -0
  15. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +1423 -0
  16. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +447 -0
  17. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +131 -0
  18. data/ext/datadog_profiling_native_extension/collectors_dynamic_sampling_rate.c +150 -0
  19. data/ext/datadog_profiling_native_extension/collectors_dynamic_sampling_rate.h +18 -0
  20. data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +156 -0
  21. data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.h +5 -0
  22. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +248 -0
  23. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.h +3 -0
  24. data/ext/datadog_profiling_native_extension/collectors_stack.c +659 -0
  25. data/ext/datadog_profiling_native_extension/collectors_stack.h +44 -0
  26. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +2221 -0
  27. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +31 -0
  28. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +80 -0
  29. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +63 -0
  30. data/ext/datadog_profiling_native_extension/encoded_profile.c +79 -0
  31. data/ext/datadog_profiling_native_extension/encoded_profile.h +8 -0
  32. data/ext/datadog_profiling_native_extension/extconf.rb +321 -0
  33. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +52 -0
  34. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +67 -0
  35. data/ext/datadog_profiling_native_extension/heap_recorder.c +998 -0
  36. data/ext/datadog_profiling_native_extension/heap_recorder.h +177 -0
  37. data/ext/datadog_profiling_native_extension/helpers.h +12 -0
  38. data/ext/datadog_profiling_native_extension/http_transport.c +280 -0
  39. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +84 -0
  40. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +28 -0
  41. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +244 -0
  42. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +881 -0
  43. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +81 -0
  44. data/ext/datadog_profiling_native_extension/profiling.c +284 -0
  45. data/ext/datadog_profiling_native_extension/ruby_helpers.c +235 -0
  46. data/ext/datadog_profiling_native_extension/ruby_helpers.h +88 -0
  47. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +115 -0
  48. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +12 -0
  49. data/ext/datadog_profiling_native_extension/stack_recorder.c +1145 -0
  50. data/ext/datadog_profiling_native_extension/stack_recorder.h +31 -0
  51. data/ext/datadog_profiling_native_extension/time_helpers.c +38 -0
  52. data/ext/datadog_profiling_native_extension/time_helpers.h +56 -0
  53. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +47 -0
  54. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +31 -0
  55. data/ext/libdatadog_api/crashtracker.c +125 -0
  56. data/ext/libdatadog_api/crashtracker.h +5 -0
  57. data/ext/libdatadog_api/datadog_ruby_common.c +80 -0
  58. data/ext/libdatadog_api/datadog_ruby_common.h +63 -0
  59. data/ext/libdatadog_api/ddsketch.c +106 -0
  60. data/ext/libdatadog_api/extconf.rb +110 -0
  61. data/ext/libdatadog_api/init.c +18 -0
  62. data/ext/libdatadog_api/library_config.c +172 -0
  63. data/ext/libdatadog_api/library_config.h +25 -0
  64. data/ext/libdatadog_api/process_discovery.c +118 -0
  65. data/ext/libdatadog_api/process_discovery.h +5 -0
  66. data/ext/libdatadog_extconf_helpers.rb +140 -0
  67. data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +89 -0
  68. data/lib/datadog/appsec/actions_handler.rb +49 -0
  69. data/lib/datadog/appsec/anonymizer.rb +16 -0
  70. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
  71. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +59 -0
  72. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +29 -0
  73. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
  74. data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
  75. data/lib/datadog/appsec/api_security/route_extractor.rb +77 -0
  76. data/lib/datadog/appsec/api_security/sampler.rb +60 -0
  77. data/lib/datadog/appsec/api_security.rb +23 -0
  78. data/lib/datadog/appsec/assets/blocked.html +99 -0
  79. data/lib/datadog/appsec/assets/blocked.json +1 -0
  80. data/lib/datadog/appsec/assets/blocked.text +5 -0
  81. data/lib/datadog/appsec/assets/waf_rules/README.md +46 -0
  82. data/lib/datadog/appsec/assets/waf_rules/recommended.json +10504 -0
  83. data/lib/datadog/appsec/assets/waf_rules/strict.json +3066 -0
  84. data/lib/datadog/appsec/assets.rb +46 -0
  85. data/lib/datadog/appsec/autoload.rb +13 -0
  86. data/lib/datadog/appsec/component.rb +89 -0
  87. data/lib/datadog/appsec/compressed_json.rb +40 -0
  88. data/lib/datadog/appsec/configuration/settings.rb +409 -0
  89. data/lib/datadog/appsec/configuration.rb +11 -0
  90. data/lib/datadog/appsec/context.rb +97 -0
  91. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +94 -0
  92. data/lib/datadog/appsec/contrib/active_record/integration.rb +41 -0
  93. data/lib/datadog/appsec/contrib/active_record/patcher.rb +101 -0
  94. data/lib/datadog/appsec/contrib/auto_instrument.rb +25 -0
  95. data/lib/datadog/appsec/contrib/devise/configuration.rb +52 -0
  96. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +78 -0
  97. data/lib/datadog/appsec/contrib/devise/ext.rb +35 -0
  98. data/lib/datadog/appsec/contrib/devise/integration.rb +41 -0
  99. data/lib/datadog/appsec/contrib/devise/patcher.rb +63 -0
  100. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +103 -0
  101. data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +70 -0
  102. data/lib/datadog/appsec/contrib/devise/patches/skip_signin_tracking_patch.rb +21 -0
  103. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +106 -0
  104. data/lib/datadog/appsec/contrib/excon/integration.rb +41 -0
  105. data/lib/datadog/appsec/contrib/excon/patcher.rb +28 -0
  106. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +42 -0
  107. data/lib/datadog/appsec/contrib/faraday/connection_patch.rb +22 -0
  108. data/lib/datadog/appsec/contrib/faraday/integration.rb +42 -0
  109. data/lib/datadog/appsec/contrib/faraday/patcher.rb +53 -0
  110. data/lib/datadog/appsec/contrib/faraday/rack_builder_patch.rb +22 -0
  111. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +42 -0
  112. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +29 -0
  113. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +109 -0
  114. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +56 -0
  115. data/lib/datadog/appsec/contrib/graphql/integration.rb +54 -0
  116. data/lib/datadog/appsec/contrib/graphql/patcher.rb +34 -0
  117. data/lib/datadog/appsec/contrib/integration.rb +37 -0
  118. data/lib/datadog/appsec/contrib/rack/ext.rb +47 -0
  119. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +101 -0
  120. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +30 -0
  121. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +141 -0
  122. data/lib/datadog/appsec/contrib/rack/integration.rb +44 -0
  123. data/lib/datadog/appsec/contrib/rack/patcher.rb +31 -0
  124. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +43 -0
  125. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +218 -0
  126. data/lib/datadog/appsec/contrib/rails/ext.rb +13 -0
  127. data/lib/datadog/appsec/contrib/rails/framework.rb +16 -0
  128. data/lib/datadog/appsec/contrib/rails/gateway/request.rb +67 -0
  129. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +78 -0
  130. data/lib/datadog/appsec/contrib/rails/integration.rb +43 -0
  131. data/lib/datadog/appsec/contrib/rails/patcher.rb +171 -0
  132. data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.rb +27 -0
  133. data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +33 -0
  134. data/lib/datadog/appsec/contrib/rails/request.rb +36 -0
  135. data/lib/datadog/appsec/contrib/rails/request_middleware.rb +20 -0
  136. data/lib/datadog/appsec/contrib/rest_client/integration.rb +45 -0
  137. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +28 -0
  138. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +39 -0
  139. data/lib/datadog/appsec/contrib/sinatra/framework.rb +20 -0
  140. data/lib/datadog/appsec/contrib/sinatra/gateway/request.rb +17 -0
  141. data/lib/datadog/appsec/contrib/sinatra/gateway/route_params.rb +23 -0
  142. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +105 -0
  143. data/lib/datadog/appsec/contrib/sinatra/integration.rb +43 -0
  144. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +132 -0
  145. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +31 -0
  146. data/lib/datadog/appsec/contrib/sinatra/request_middleware.rb +20 -0
  147. data/lib/datadog/appsec/event.rb +139 -0
  148. data/lib/datadog/appsec/ext.rb +23 -0
  149. data/lib/datadog/appsec/extensions.rb +16 -0
  150. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +43 -0
  151. data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +24 -0
  152. data/lib/datadog/appsec/instrumentation/gateway.rb +59 -0
  153. data/lib/datadog/appsec/instrumentation.rb +9 -0
  154. data/lib/datadog/appsec/metrics/collector.rb +58 -0
  155. data/lib/datadog/appsec/metrics/exporter.rb +35 -0
  156. data/lib/datadog/appsec/metrics/telemetry.rb +23 -0
  157. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +29 -0
  158. data/lib/datadog/appsec/metrics.rb +14 -0
  159. data/lib/datadog/appsec/monitor/gateway/watcher.rb +85 -0
  160. data/lib/datadog/appsec/monitor.rb +11 -0
  161. data/lib/datadog/appsec/processor/rule_loader.rb +119 -0
  162. data/lib/datadog/appsec/rate_limiter.rb +45 -0
  163. data/lib/datadog/appsec/remote.rb +119 -0
  164. data/lib/datadog/appsec/response.rb +99 -0
  165. data/lib/datadog/appsec/sample_rate.rb +21 -0
  166. data/lib/datadog/appsec/security_engine/engine.rb +176 -0
  167. data/lib/datadog/appsec/security_engine/result.rb +102 -0
  168. data/lib/datadog/appsec/security_engine/runner.rb +111 -0
  169. data/lib/datadog/appsec/security_engine.rb +9 -0
  170. data/lib/datadog/appsec/security_event.rb +37 -0
  171. data/lib/datadog/appsec/thread_safe_ref.rb +61 -0
  172. data/lib/datadog/appsec/trace_keeper.rb +24 -0
  173. data/lib/datadog/appsec/utils/hash_coercion.rb +23 -0
  174. data/lib/datadog/appsec/utils/http/media_range.rb +201 -0
  175. data/lib/datadog/appsec/utils/http/media_type.rb +87 -0
  176. data/lib/datadog/appsec/utils/http.rb +11 -0
  177. data/lib/datadog/appsec/utils.rb +9 -0
  178. data/lib/datadog/appsec.rb +65 -0
  179. data/lib/datadog/auto_instrument.rb +19 -0
  180. data/lib/datadog/auto_instrument_base.rb +9 -0
  181. data/lib/datadog/core/buffer/cruby.rb +55 -0
  182. data/lib/datadog/core/buffer/random.rb +150 -0
  183. data/lib/datadog/core/buffer/thread_safe.rb +58 -0
  184. data/lib/datadog/core/chunker.rb +35 -0
  185. data/lib/datadog/core/cloudwise/IMPLEMENTATION_V2.md +517 -0
  186. data/lib/datadog/core/cloudwise/QUICKSTART.md +398 -0
  187. data/lib/datadog/core/cloudwise/README.md +722 -0
  188. data/lib/datadog/core/cloudwise/app_registration_worker.rb +90 -0
  189. data/lib/datadog/core/cloudwise/client.rb +490 -0
  190. data/lib/datadog/core/cloudwise/component.rb +351 -0
  191. data/lib/datadog/core/cloudwise/heartbeat_worker.rb +137 -0
  192. data/lib/datadog/core/cloudwise/host_id_worker.rb +85 -0
  193. data/lib/datadog/core/cloudwise/license_worker.rb +108 -0
  194. data/lib/datadog/core/cloudwise/probe_state.rb +160 -0
  195. data/lib/datadog/core/configuration/agent_settings.rb +52 -0
  196. data/lib/datadog/core/configuration/agent_settings_resolver.rb +339 -0
  197. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +176 -0
  198. data/lib/datadog/core/configuration/base.rb +91 -0
  199. data/lib/datadog/core/configuration/components.rb +386 -0
  200. data/lib/datadog/core/configuration/components_state.rb +23 -0
  201. data/lib/datadog/core/configuration/config_helper.rb +100 -0
  202. data/lib/datadog/core/configuration/deprecations.rb +36 -0
  203. data/lib/datadog/core/configuration/ext.rb +49 -0
  204. data/lib/datadog/core/configuration/option.rb +368 -0
  205. data/lib/datadog/core/configuration/option_definition.rb +158 -0
  206. data/lib/datadog/core/configuration/options.rb +134 -0
  207. data/lib/datadog/core/configuration/settings.rb +1087 -0
  208. data/lib/datadog/core/configuration/stable_config.rb +32 -0
  209. data/lib/datadog/core/configuration/supported_configurations.rb +347 -0
  210. data/lib/datadog/core/configuration.rb +328 -0
  211. data/lib/datadog/core/contrib/rails/utils.rb +24 -0
  212. data/lib/datadog/core/crashtracking/component.rb +105 -0
  213. data/lib/datadog/core/crashtracking/tag_builder.rb +21 -0
  214. data/lib/datadog/core/ddsketch.rb +19 -0
  215. data/lib/datadog/core/deprecations.rb +58 -0
  216. data/lib/datadog/core/diagnostics/environment_logger.rb +170 -0
  217. data/lib/datadog/core/diagnostics/health.rb +19 -0
  218. data/lib/datadog/core/encoding.rb +90 -0
  219. data/lib/datadog/core/environment/agent_info.rb +78 -0
  220. data/lib/datadog/core/environment/cgroup.rb +51 -0
  221. data/lib/datadog/core/environment/class_count.rb +21 -0
  222. data/lib/datadog/core/environment/container.rb +89 -0
  223. data/lib/datadog/core/environment/execution.rb +103 -0
  224. data/lib/datadog/core/environment/ext.rb +45 -0
  225. data/lib/datadog/core/environment/gc.rb +20 -0
  226. data/lib/datadog/core/environment/git.rb +26 -0
  227. data/lib/datadog/core/environment/identity.rb +84 -0
  228. data/lib/datadog/core/environment/platform.rb +46 -0
  229. data/lib/datadog/core/environment/socket.rb +24 -0
  230. data/lib/datadog/core/environment/thread_count.rb +20 -0
  231. data/lib/datadog/core/environment/variable_helpers.rb +53 -0
  232. data/lib/datadog/core/environment/vm_cache.rb +64 -0
  233. data/lib/datadog/core/environment/yjit.rb +69 -0
  234. data/lib/datadog/core/error.rb +102 -0
  235. data/lib/datadog/core/extensions.rb +16 -0
  236. data/lib/datadog/core/git/ext.rb +16 -0
  237. data/lib/datadog/core/header_collection.rb +43 -0
  238. data/lib/datadog/core/logger.rb +45 -0
  239. data/lib/datadog/core/logging/ext.rb +13 -0
  240. data/lib/datadog/core/metrics/client.rb +206 -0
  241. data/lib/datadog/core/metrics/ext.rb +18 -0
  242. data/lib/datadog/core/metrics/helpers.rb +25 -0
  243. data/lib/datadog/core/metrics/logging.rb +44 -0
  244. data/lib/datadog/core/metrics/metric.rb +14 -0
  245. data/lib/datadog/core/metrics/options.rb +52 -0
  246. data/lib/datadog/core/pin.rb +71 -0
  247. data/lib/datadog/core/process_discovery/tracer_memfd.rb +13 -0
  248. data/lib/datadog/core/process_discovery.rb +61 -0
  249. data/lib/datadog/core/rate_limiter.rb +185 -0
  250. data/lib/datadog/core/remote/client/capabilities.rb +70 -0
  251. data/lib/datadog/core/remote/client.rb +245 -0
  252. data/lib/datadog/core/remote/component.rb +161 -0
  253. data/lib/datadog/core/remote/configuration/content.rb +111 -0
  254. data/lib/datadog/core/remote/configuration/digest.rb +62 -0
  255. data/lib/datadog/core/remote/configuration/path.rb +90 -0
  256. data/lib/datadog/core/remote/configuration/repository.rb +307 -0
  257. data/lib/datadog/core/remote/configuration/target.rb +74 -0
  258. data/lib/datadog/core/remote/configuration.rb +18 -0
  259. data/lib/datadog/core/remote/dispatcher.rb +59 -0
  260. data/lib/datadog/core/remote/ext.rb +13 -0
  261. data/lib/datadog/core/remote/negotiation.rb +70 -0
  262. data/lib/datadog/core/remote/tie/tracing.rb +39 -0
  263. data/lib/datadog/core/remote/tie.rb +29 -0
  264. data/lib/datadog/core/remote/transport/config.rb +61 -0
  265. data/lib/datadog/core/remote/transport/http/api.rb +53 -0
  266. data/lib/datadog/core/remote/transport/http/client.rb +49 -0
  267. data/lib/datadog/core/remote/transport/http/config.rb +252 -0
  268. data/lib/datadog/core/remote/transport/http/negotiation.rb +103 -0
  269. data/lib/datadog/core/remote/transport/http.rb +83 -0
  270. data/lib/datadog/core/remote/transport/negotiation.rb +75 -0
  271. data/lib/datadog/core/remote/worker.rb +105 -0
  272. data/lib/datadog/core/remote.rb +24 -0
  273. data/lib/datadog/core/runtime/ext.rb +40 -0
  274. data/lib/datadog/core/runtime/metrics.rb +202 -0
  275. data/lib/datadog/core/semaphore.rb +35 -0
  276. data/lib/datadog/core/tag_builder.rb +52 -0
  277. data/lib/datadog/core/telemetry/component.rb +206 -0
  278. data/lib/datadog/core/telemetry/emitter.rb +56 -0
  279. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +66 -0
  280. data/lib/datadog/core/telemetry/event/app_closing.rb +18 -0
  281. data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +33 -0
  282. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
  283. data/lib/datadog/core/telemetry/event/app_heartbeat.rb +18 -0
  284. data/lib/datadog/core/telemetry/event/app_integrations_change.rb +58 -0
  285. data/lib/datadog/core/telemetry/event/app_started.rb +287 -0
  286. data/lib/datadog/core/telemetry/event/base.rb +40 -0
  287. data/lib/datadog/core/telemetry/event/distributions.rb +18 -0
  288. data/lib/datadog/core/telemetry/event/generate_metrics.rb +43 -0
  289. data/lib/datadog/core/telemetry/event/log.rb +76 -0
  290. data/lib/datadog/core/telemetry/event/message_batch.rb +42 -0
  291. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +43 -0
  292. data/lib/datadog/core/telemetry/event.rb +37 -0
  293. data/lib/datadog/core/telemetry/ext.rb +20 -0
  294. data/lib/datadog/core/telemetry/http/adapters/net.rb +26 -0
  295. data/lib/datadog/core/telemetry/logger.rb +52 -0
  296. data/lib/datadog/core/telemetry/logging.rb +71 -0
  297. data/lib/datadog/core/telemetry/metric.rb +189 -0
  298. data/lib/datadog/core/telemetry/metrics_collection.rb +81 -0
  299. data/lib/datadog/core/telemetry/metrics_manager.rb +81 -0
  300. data/lib/datadog/core/telemetry/request.rb +71 -0
  301. data/lib/datadog/core/telemetry/transport/http/api.rb +43 -0
  302. data/lib/datadog/core/telemetry/transport/http/client.rb +49 -0
  303. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +92 -0
  304. data/lib/datadog/core/telemetry/transport/http.rb +63 -0
  305. data/lib/datadog/core/telemetry/transport/telemetry.rb +51 -0
  306. data/lib/datadog/core/telemetry/worker.rb +276 -0
  307. data/lib/datadog/core/transport/ext.rb +44 -0
  308. data/lib/datadog/core/transport/http/adapters/net.rb +175 -0
  309. data/lib/datadog/core/transport/http/adapters/registry.rb +29 -0
  310. data/lib/datadog/core/transport/http/adapters/test.rb +90 -0
  311. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +83 -0
  312. data/lib/datadog/core/transport/http/api/endpoint.rb +31 -0
  313. data/lib/datadog/core/transport/http/api/fallbacks.rb +26 -0
  314. data/lib/datadog/core/transport/http/api/instance.rb +54 -0
  315. data/lib/datadog/core/transport/http/api/map.rb +18 -0
  316. data/lib/datadog/core/transport/http/api/spec.rb +36 -0
  317. data/lib/datadog/core/transport/http/builder.rb +184 -0
  318. data/lib/datadog/core/transport/http/env.rb +70 -0
  319. data/lib/datadog/core/transport/http/response.rb +60 -0
  320. data/lib/datadog/core/transport/http.rb +75 -0
  321. data/lib/datadog/core/transport/parcel.rb +22 -0
  322. data/lib/datadog/core/transport/request.rb +17 -0
  323. data/lib/datadog/core/transport/response.rb +71 -0
  324. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
  325. data/lib/datadog/core/utils/base64.rb +22 -0
  326. data/lib/datadog/core/utils/duration.rb +52 -0
  327. data/lib/datadog/core/utils/forking.rb +63 -0
  328. data/lib/datadog/core/utils/hash.rb +79 -0
  329. data/lib/datadog/core/utils/lru_cache.rb +45 -0
  330. data/lib/datadog/core/utils/network.rb +142 -0
  331. data/lib/datadog/core/utils/only_once.rb +42 -0
  332. data/lib/datadog/core/utils/only_once_successful.rb +87 -0
  333. data/lib/datadog/core/utils/safe_dup.rb +40 -0
  334. data/lib/datadog/core/utils/sequence.rb +26 -0
  335. data/lib/datadog/core/utils/time.rb +84 -0
  336. data/lib/datadog/core/utils/truncation.rb +21 -0
  337. data/lib/datadog/core/utils/url.rb +25 -0
  338. data/lib/datadog/core/utils.rb +101 -0
  339. data/lib/datadog/core/vendor/multipart-post/LICENSE +11 -0
  340. data/lib/datadog/core/vendor/multipart-post/multipart/post/composite_read_io.rb +118 -0
  341. data/lib/datadog/core/vendor/multipart-post/multipart/post/multipartable.rb +59 -0
  342. data/lib/datadog/core/vendor/multipart-post/multipart/post/parts.rb +137 -0
  343. data/lib/datadog/core/vendor/multipart-post/multipart/post/version.rb +11 -0
  344. data/lib/datadog/core/vendor/multipart-post/multipart/post.rb +10 -0
  345. data/lib/datadog/core/vendor/multipart-post/multipart.rb +14 -0
  346. data/lib/datadog/core/vendor/multipart-post/net/http/post/multipart.rb +34 -0
  347. data/lib/datadog/core/worker.rb +24 -0
  348. data/lib/datadog/core/workers/async.rb +202 -0
  349. data/lib/datadog/core/workers/interval_loop.rb +134 -0
  350. data/lib/datadog/core/workers/polling.rb +59 -0
  351. data/lib/datadog/core/workers/queue.rb +44 -0
  352. data/lib/datadog/core/workers/runtime_metrics.rb +62 -0
  353. data/lib/datadog/core.rb +38 -0
  354. data/lib/datadog/data_streams/configuration/settings.rb +49 -0
  355. data/lib/datadog/data_streams/configuration.rb +11 -0
  356. data/lib/datadog/data_streams/ext.rb +11 -0
  357. data/lib/datadog/data_streams/extensions.rb +16 -0
  358. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  359. data/lib/datadog/data_streams/processor.rb +509 -0
  360. data/lib/datadog/data_streams/transport/http/api.rb +33 -0
  361. data/lib/datadog/data_streams/transport/http/client.rb +49 -0
  362. data/lib/datadog/data_streams/transport/http/stats.rb +87 -0
  363. data/lib/datadog/data_streams/transport/http.rb +41 -0
  364. data/lib/datadog/data_streams/transport/stats.rb +60 -0
  365. data/lib/datadog/data_streams.rb +100 -0
  366. data/lib/datadog/di/base.rb +115 -0
  367. data/lib/datadog/di/boot.rb +43 -0
  368. data/lib/datadog/di/code_tracker.rb +204 -0
  369. data/lib/datadog/di/component.rb +122 -0
  370. data/lib/datadog/di/configuration/settings.rb +212 -0
  371. data/lib/datadog/di/configuration.rb +11 -0
  372. data/lib/datadog/di/context.rb +70 -0
  373. data/lib/datadog/di/contrib/active_record.rb +12 -0
  374. data/lib/datadog/di/contrib/railtie.rb +15 -0
  375. data/lib/datadog/di/contrib.rb +28 -0
  376. data/lib/datadog/di/el/compiler.rb +164 -0
  377. data/lib/datadog/di/el/evaluator.rb +159 -0
  378. data/lib/datadog/di/el/expression.rb +42 -0
  379. data/lib/datadog/di/el.rb +5 -0
  380. data/lib/datadog/di/error.rb +82 -0
  381. data/lib/datadog/di/extensions.rb +16 -0
  382. data/lib/datadog/di/instrumenter.rb +566 -0
  383. data/lib/datadog/di/logger.rb +30 -0
  384. data/lib/datadog/di/preload.rb +18 -0
  385. data/lib/datadog/di/probe.rb +231 -0
  386. data/lib/datadog/di/probe_builder.rb +86 -0
  387. data/lib/datadog/di/probe_file_loader/railtie.rb +15 -0
  388. data/lib/datadog/di/probe_file_loader.rb +82 -0
  389. data/lib/datadog/di/probe_manager.rb +261 -0
  390. data/lib/datadog/di/probe_notification_builder.rb +236 -0
  391. data/lib/datadog/di/probe_notifier_worker.rb +305 -0
  392. data/lib/datadog/di/proc_responder.rb +32 -0
  393. data/lib/datadog/di/redactor.rb +187 -0
  394. data/lib/datadog/di/remote.rb +145 -0
  395. data/lib/datadog/di/serializer.rb +422 -0
  396. data/lib/datadog/di/transport/diagnostics.rb +62 -0
  397. data/lib/datadog/di/transport/http/api.rb +42 -0
  398. data/lib/datadog/di/transport/http/client.rb +47 -0
  399. data/lib/datadog/di/transport/http/diagnostics.rb +65 -0
  400. data/lib/datadog/di/transport/http/input.rb +77 -0
  401. data/lib/datadog/di/transport/http.rb +57 -0
  402. data/lib/datadog/di/transport/input.rb +70 -0
  403. data/lib/datadog/di/utils.rb +142 -0
  404. data/lib/datadog/di.rb +36 -0
  405. data/lib/datadog/error_tracking/collector.rb +87 -0
  406. data/lib/datadog/error_tracking/component.rb +167 -0
  407. data/lib/datadog/error_tracking/configuration/settings.rb +63 -0
  408. data/lib/datadog/error_tracking/configuration.rb +11 -0
  409. data/lib/datadog/error_tracking/ext.rb +18 -0
  410. data/lib/datadog/error_tracking/extensions.rb +16 -0
  411. data/lib/datadog/error_tracking/filters.rb +77 -0
  412. data/lib/datadog/error_tracking.rb +18 -0
  413. data/lib/datadog/kit/appsec/events/v2.rb +196 -0
  414. data/lib/datadog/kit/appsec/events.rb +180 -0
  415. data/lib/datadog/kit/enable_core_dumps.rb +49 -0
  416. data/lib/datadog/kit/identity.rb +114 -0
  417. data/lib/datadog/kit.rb +11 -0
  418. data/lib/datadog/opentelemetry/api/baggage.rb +90 -0
  419. data/lib/datadog/opentelemetry/api/baggage.rbs +26 -0
  420. data/lib/datadog/opentelemetry/api/context.rb +208 -0
  421. data/lib/datadog/opentelemetry/api/trace/span.rb +14 -0
  422. data/lib/datadog/opentelemetry/sdk/configurator.rb +37 -0
  423. data/lib/datadog/opentelemetry/sdk/id_generator.rb +26 -0
  424. data/lib/datadog/opentelemetry/sdk/propagator.rb +89 -0
  425. data/lib/datadog/opentelemetry/sdk/span_processor.rb +169 -0
  426. data/lib/datadog/opentelemetry/sdk/trace/span.rb +182 -0
  427. data/lib/datadog/opentelemetry/trace.rb +59 -0
  428. data/lib/datadog/opentelemetry.rb +52 -0
  429. data/lib/datadog/profiling/collectors/code_provenance.rb +150 -0
  430. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +147 -0
  431. data/lib/datadog/profiling/collectors/dynamic_sampling_rate.rb +14 -0
  432. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +69 -0
  433. data/lib/datadog/profiling/collectors/info.rb +156 -0
  434. data/lib/datadog/profiling/collectors/stack.rb +13 -0
  435. data/lib/datadog/profiling/collectors/thread_context.rb +102 -0
  436. data/lib/datadog/profiling/component.rb +445 -0
  437. data/lib/datadog/profiling/encoded_profile.rb +11 -0
  438. data/lib/datadog/profiling/exporter.rb +111 -0
  439. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +410 -0
  440. data/lib/datadog/profiling/ext.rb +22 -0
  441. data/lib/datadog/profiling/flush.rb +40 -0
  442. data/lib/datadog/profiling/http_transport.rb +67 -0
  443. data/lib/datadog/profiling/load_native_extension.rb +9 -0
  444. data/lib/datadog/profiling/native_extension.rb +20 -0
  445. data/lib/datadog/profiling/preload.rb +5 -0
  446. data/lib/datadog/profiling/profiler.rb +70 -0
  447. data/lib/datadog/profiling/scheduler.rb +153 -0
  448. data/lib/datadog/profiling/sequence_tracker.rb +44 -0
  449. data/lib/datadog/profiling/stack_recorder.rb +104 -0
  450. data/lib/datadog/profiling/tag_builder.rb +59 -0
  451. data/lib/datadog/profiling/tasks/exec.rb +50 -0
  452. data/lib/datadog/profiling/tasks/help.rb +18 -0
  453. data/lib/datadog/profiling/tasks/setup.rb +43 -0
  454. data/lib/datadog/profiling.rb +167 -0
  455. data/lib/datadog/single_step_instrument.rb +21 -0
  456. data/lib/datadog/tracing/analytics.rb +25 -0
  457. data/lib/datadog/tracing/buffer.rb +129 -0
  458. data/lib/datadog/tracing/client_ip.rb +61 -0
  459. data/lib/datadog/tracing/component.rb +216 -0
  460. data/lib/datadog/tracing/configuration/dynamic/option.rb +71 -0
  461. data/lib/datadog/tracing/configuration/dynamic.rb +100 -0
  462. data/lib/datadog/tracing/configuration/ext.rb +118 -0
  463. data/lib/datadog/tracing/configuration/http.rb +74 -0
  464. data/lib/datadog/tracing/configuration/settings.rb +579 -0
  465. data/lib/datadog/tracing/context.rb +68 -0
  466. data/lib/datadog/tracing/context_provider.rb +82 -0
  467. data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +39 -0
  468. data/lib/datadog/tracing/contrib/action_cable/event.rb +71 -0
  469. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +58 -0
  470. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +63 -0
  471. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +59 -0
  472. data/lib/datadog/tracing/contrib/action_cable/events.rb +37 -0
  473. data/lib/datadog/tracing/contrib/action_cable/ext.rb +33 -0
  474. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +86 -0
  475. data/lib/datadog/tracing/contrib/action_cable/integration.rb +53 -0
  476. data/lib/datadog/tracing/contrib/action_cable/patcher.rb +31 -0
  477. data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +43 -0
  478. data/lib/datadog/tracing/contrib/action_mailer/event.rb +50 -0
  479. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +65 -0
  480. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +48 -0
  481. data/lib/datadog/tracing/contrib/action_mailer/events.rb +34 -0
  482. data/lib/datadog/tracing/contrib/action_mailer/ext.rb +34 -0
  483. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +54 -0
  484. data/lib/datadog/tracing/contrib/action_mailer/patcher.rb +29 -0
  485. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +158 -0
  486. data/lib/datadog/tracing/contrib/action_pack/action_controller/patcher.rb +29 -0
  487. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +85 -0
  488. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +33 -0
  489. data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +40 -0
  490. data/lib/datadog/tracing/contrib/action_pack/ext.rb +25 -0
  491. data/lib/datadog/tracing/contrib/action_pack/integration.rb +54 -0
  492. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +29 -0
  493. data/lib/datadog/tracing/contrib/action_pack/utils.rb +39 -0
  494. data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +43 -0
  495. data/lib/datadog/tracing/contrib/action_view/event.rb +35 -0
  496. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +50 -0
  497. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +57 -0
  498. data/lib/datadog/tracing/contrib/action_view/events.rb +34 -0
  499. data/lib/datadog/tracing/contrib/action_view/ext.rb +25 -0
  500. data/lib/datadog/tracing/contrib/action_view/integration.rb +61 -0
  501. data/lib/datadog/tracing/contrib/action_view/patcher.rb +34 -0
  502. data/lib/datadog/tracing/contrib/action_view/utils.rb +36 -0
  503. data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +39 -0
  504. data/lib/datadog/tracing/contrib/active_job/event.rb +58 -0
  505. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +50 -0
  506. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +49 -0
  507. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +49 -0
  508. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +51 -0
  509. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +49 -0
  510. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +50 -0
  511. data/lib/datadog/tracing/contrib/active_job/events.rb +42 -0
  512. data/lib/datadog/tracing/contrib/active_job/ext.rb +40 -0
  513. data/lib/datadog/tracing/contrib/active_job/integration.rb +53 -0
  514. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +38 -0
  515. data/lib/datadog/tracing/contrib/active_job/patcher.rb +40 -0
  516. data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +37 -0
  517. data/lib/datadog/tracing/contrib/active_model_serializers/event.rb +68 -0
  518. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +45 -0
  519. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +47 -0
  520. data/lib/datadog/tracing/contrib/active_model_serializers/events.rb +34 -0
  521. data/lib/datadog/tracing/contrib/active_model_serializers/ext.rb +25 -0
  522. data/lib/datadog/tracing/contrib/active_model_serializers/integration.rb +44 -0
  523. data/lib/datadog/tracing/contrib/active_model_serializers/patcher.rb +32 -0
  524. data/lib/datadog/tracing/contrib/active_record/configuration/makara_resolver.rb +36 -0
  525. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +152 -0
  526. data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +48 -0
  527. data/lib/datadog/tracing/contrib/active_record/event.rb +30 -0
  528. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +60 -0
  529. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +80 -0
  530. data/lib/datadog/tracing/contrib/active_record/events.rb +34 -0
  531. data/lib/datadog/tracing/contrib/active_record/ext.rb +30 -0
  532. data/lib/datadog/tracing/contrib/active_record/integration.rb +71 -0
  533. data/lib/datadog/tracing/contrib/active_record/patcher.rb +27 -0
  534. data/lib/datadog/tracing/contrib/active_record/utils.rb +128 -0
  535. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +32 -0
  536. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +171 -0
  537. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +34 -0
  538. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +225 -0
  539. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +57 -0
  540. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +60 -0
  541. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +70 -0
  542. data/lib/datadog/tracing/contrib/active_support/ext.rb +32 -0
  543. data/lib/datadog/tracing/contrib/active_support/integration.rb +55 -0
  544. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +95 -0
  545. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +83 -0
  546. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +166 -0
  547. data/lib/datadog/tracing/contrib/active_support/patcher.rb +27 -0
  548. data/lib/datadog/tracing/contrib/analytics.rb +33 -0
  549. data/lib/datadog/tracing/contrib/auto_instrument.rb +53 -0
  550. data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +53 -0
  551. data/lib/datadog/tracing/contrib/aws/ext.rb +50 -0
  552. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +136 -0
  553. data/lib/datadog/tracing/contrib/aws/integration.rb +50 -0
  554. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +70 -0
  555. data/lib/datadog/tracing/contrib/aws/patcher.rb +61 -0
  556. data/lib/datadog/tracing/contrib/aws/service/base.rb +17 -0
  557. data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +22 -0
  558. data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +22 -0
  559. data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +32 -0
  560. data/lib/datadog/tracing/contrib/aws/service/s3.rb +22 -0
  561. data/lib/datadog/tracing/contrib/aws/service/sns.rb +30 -0
  562. data/lib/datadog/tracing/contrib/aws/service/sqs.rb +27 -0
  563. data/lib/datadog/tracing/contrib/aws/service/states.rb +40 -0
  564. data/lib/datadog/tracing/contrib/aws/services.rb +139 -0
  565. data/lib/datadog/tracing/contrib/cloudwise/propagation.rb +315 -0
  566. data/lib/datadog/tracing/contrib/component.rb +41 -0
  567. data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +20 -0
  568. data/lib/datadog/tracing/contrib/concurrent_ruby/configuration/settings.rb +24 -0
  569. data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +53 -0
  570. data/lib/datadog/tracing/contrib/concurrent_ruby/ext.rb +16 -0
  571. data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +20 -0
  572. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +47 -0
  573. data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +49 -0
  574. data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +22 -0
  575. data/lib/datadog/tracing/contrib/configurable.rb +102 -0
  576. data/lib/datadog/tracing/contrib/configuration/resolver.rb +128 -0
  577. data/lib/datadog/tracing/contrib/configuration/resolvers/pattern_resolver.rb +43 -0
  578. data/lib/datadog/tracing/contrib/configuration/settings.rb +43 -0
  579. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +58 -0
  580. data/lib/datadog/tracing/contrib/dalli/ext.rb +41 -0
  581. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +75 -0
  582. data/lib/datadog/tracing/contrib/dalli/integration.rb +52 -0
  583. data/lib/datadog/tracing/contrib/dalli/patcher.rb +28 -0
  584. data/lib/datadog/tracing/contrib/dalli/quantize.rb +26 -0
  585. data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +49 -0
  586. data/lib/datadog/tracing/contrib/delayed_job/ext.rb +29 -0
  587. data/lib/datadog/tracing/contrib/delayed_job/integration.rb +43 -0
  588. data/lib/datadog/tracing/contrib/delayed_job/patcher.rb +37 -0
  589. data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +108 -0
  590. data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +34 -0
  591. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +61 -0
  592. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +35 -0
  593. data/lib/datadog/tracing/contrib/elasticsearch/integration.rb +50 -0
  594. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +172 -0
  595. data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +87 -0
  596. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +56 -0
  597. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +229 -0
  598. data/lib/datadog/tracing/contrib/ethon/ext.rb +33 -0
  599. data/lib/datadog/tracing/contrib/ethon/integration.rb +48 -0
  600. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +102 -0
  601. data/lib/datadog/tracing/contrib/ethon/patcher.rb +30 -0
  602. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +82 -0
  603. data/lib/datadog/tracing/contrib/excon/ext.rb +31 -0
  604. data/lib/datadog/tracing/contrib/excon/integration.rb +48 -0
  605. data/lib/datadog/tracing/contrib/excon/middleware.rb +201 -0
  606. data/lib/datadog/tracing/contrib/excon/patcher.rb +31 -0
  607. data/lib/datadog/tracing/contrib/ext.rb +70 -0
  608. data/lib/datadog/tracing/contrib/extensions.rb +255 -0
  609. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +81 -0
  610. data/lib/datadog/tracing/contrib/faraday/connection.rb +22 -0
  611. data/lib/datadog/tracing/contrib/faraday/ext.rb +31 -0
  612. data/lib/datadog/tracing/contrib/faraday/integration.rb +48 -0
  613. data/lib/datadog/tracing/contrib/faraday/middleware.rb +128 -0
  614. data/lib/datadog/tracing/contrib/faraday/patcher.rb +56 -0
  615. data/lib/datadog/tracing/contrib/faraday/rack_builder.rb +22 -0
  616. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +59 -0
  617. data/lib/datadog/tracing/contrib/grape/endpoint.rb +316 -0
  618. data/lib/datadog/tracing/contrib/grape/ext.rb +30 -0
  619. data/lib/datadog/tracing/contrib/grape/instrumentation.rb +37 -0
  620. data/lib/datadog/tracing/contrib/grape/integration.rb +44 -0
  621. data/lib/datadog/tracing/contrib/grape/patcher.rb +33 -0
  622. data/lib/datadog/tracing/contrib/graphql/configuration/error_extension_env_parser.rb +21 -0
  623. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +73 -0
  624. data/lib/datadog/tracing/contrib/graphql/ext.rb +26 -0
  625. data/lib/datadog/tracing/contrib/graphql/integration.rb +56 -0
  626. data/lib/datadog/tracing/contrib/graphql/patcher.rb +58 -0
  627. data/lib/datadog/tracing/contrib/graphql/trace_patcher.rb +24 -0
  628. data/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb +28 -0
  629. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +297 -0
  630. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +31 -0
  631. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +58 -0
  632. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +123 -0
  633. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +96 -0
  634. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +107 -0
  635. data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +26 -0
  636. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +49 -0
  637. data/lib/datadog/tracing/contrib/grpc/ext.rb +29 -0
  638. data/lib/datadog/tracing/contrib/grpc/formatting.rb +127 -0
  639. data/lib/datadog/tracing/contrib/grpc/integration.rb +50 -0
  640. data/lib/datadog/tracing/contrib/grpc/intercept_with_datadog.rb +53 -0
  641. data/lib/datadog/tracing/contrib/grpc/patcher.rb +34 -0
  642. data/lib/datadog/tracing/contrib/grpc.rb +45 -0
  643. data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +47 -0
  644. data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +23 -0
  645. data/lib/datadog/tracing/contrib/hanami/ext.rb +24 -0
  646. data/lib/datadog/tracing/contrib/hanami/integration.rb +44 -0
  647. data/lib/datadog/tracing/contrib/hanami/patcher.rb +33 -0
  648. data/lib/datadog/tracing/contrib/hanami/plugin.rb +23 -0
  649. data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +41 -0
  650. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +42 -0
  651. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +34 -0
  652. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +77 -0
  653. data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +38 -0
  654. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +48 -0
  655. data/lib/datadog/tracing/contrib/http/ext.rb +30 -0
  656. data/lib/datadog/tracing/contrib/http/instrumentation.rb +152 -0
  657. data/lib/datadog/tracing/contrib/http/integration.rb +52 -0
  658. data/lib/datadog/tracing/contrib/http/patcher.rb +30 -0
  659. data/lib/datadog/tracing/contrib/http.rb +45 -0
  660. data/lib/datadog/tracing/contrib/http_annotation_helper.rb +17 -0
  661. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +76 -0
  662. data/lib/datadog/tracing/contrib/httpclient/ext.rb +31 -0
  663. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +132 -0
  664. data/lib/datadog/tracing/contrib/httpclient/integration.rb +48 -0
  665. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +29 -0
  666. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +76 -0
  667. data/lib/datadog/tracing/contrib/httprb/ext.rb +30 -0
  668. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +146 -0
  669. data/lib/datadog/tracing/contrib/httprb/integration.rb +51 -0
  670. data/lib/datadog/tracing/contrib/httprb/patcher.rb +29 -0
  671. data/lib/datadog/tracing/contrib/integration.rb +78 -0
  672. data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +39 -0
  673. data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +19 -0
  674. data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +18 -0
  675. data/lib/datadog/tracing/contrib/kafka/event.rb +53 -0
  676. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +42 -0
  677. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +49 -0
  678. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +47 -0
  679. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +47 -0
  680. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/join_group.rb +37 -0
  681. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/leave_group.rb +37 -0
  682. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/sync_group.rb +37 -0
  683. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +41 -0
  684. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +44 -0
  685. data/lib/datadog/tracing/contrib/kafka/events.rb +48 -0
  686. data/lib/datadog/tracing/contrib/kafka/ext.rb +55 -0
  687. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  688. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  689. data/lib/datadog/tracing/contrib/kafka/integration.rb +47 -0
  690. data/lib/datadog/tracing/contrib/kafka/patcher.rb +43 -0
  691. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +27 -0
  692. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +48 -0
  693. data/lib/datadog/tracing/contrib/karafka/ext.rb +27 -0
  694. data/lib/datadog/tracing/contrib/karafka/integration.rb +45 -0
  695. data/lib/datadog/tracing/contrib/karafka/monitor.rb +77 -0
  696. data/lib/datadog/tracing/contrib/karafka/patcher.rb +89 -0
  697. data/lib/datadog/tracing/contrib/karafka.rb +37 -0
  698. data/lib/datadog/tracing/contrib/lograge/configuration/settings.rb +24 -0
  699. data/lib/datadog/tracing/contrib/lograge/ext.rb +15 -0
  700. data/lib/datadog/tracing/contrib/lograge/instrumentation.rb +31 -0
  701. data/lib/datadog/tracing/contrib/lograge/integration.rb +50 -0
  702. data/lib/datadog/tracing/contrib/lograge/patcher.rb +46 -0
  703. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +64 -0
  704. data/lib/datadog/tracing/contrib/mongodb/ext.rb +39 -0
  705. data/lib/datadog/tracing/contrib/mongodb/instrumentation.rb +47 -0
  706. data/lib/datadog/tracing/contrib/mongodb/integration.rb +51 -0
  707. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +49 -0
  708. data/lib/datadog/tracing/contrib/mongodb/patcher.rb +34 -0
  709. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +160 -0
  710. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +69 -0
  711. data/lib/datadog/tracing/contrib/mysql2/ext.rb +28 -0
  712. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +109 -0
  713. data/lib/datadog/tracing/contrib/mysql2/integration.rb +43 -0
  714. data/lib/datadog/tracing/contrib/mysql2/patcher.rb +31 -0
  715. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +71 -0
  716. data/lib/datadog/tracing/contrib/opensearch/ext.rb +48 -0
  717. data/lib/datadog/tracing/contrib/opensearch/integration.rb +46 -0
  718. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +144 -0
  719. data/lib/datadog/tracing/contrib/opensearch/quantize.rb +81 -0
  720. data/lib/datadog/tracing/contrib/patchable.rb +109 -0
  721. data/lib/datadog/tracing/contrib/patcher.rb +87 -0
  722. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +69 -0
  723. data/lib/datadog/tracing/contrib/pg/ext.rb +35 -0
  724. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +214 -0
  725. data/lib/datadog/tracing/contrib/pg/integration.rb +43 -0
  726. data/lib/datadog/tracing/contrib/pg/patcher.rb +31 -0
  727. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +52 -0
  728. data/lib/datadog/tracing/contrib/presto/ext.rb +38 -0
  729. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +138 -0
  730. data/lib/datadog/tracing/contrib/presto/integration.rb +46 -0
  731. data/lib/datadog/tracing/contrib/presto/patcher.rb +25 -0
  732. data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +41 -0
  733. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +61 -0
  734. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +32 -0
  735. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +67 -0
  736. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +55 -0
  737. data/lib/datadog/tracing/contrib/que/ext.rb +33 -0
  738. data/lib/datadog/tracing/contrib/que/integration.rb +44 -0
  739. data/lib/datadog/tracing/contrib/que/patcher.rb +26 -0
  740. data/lib/datadog/tracing/contrib/que/tracer.rb +63 -0
  741. data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +47 -0
  742. data/lib/datadog/tracing/contrib/racecar/event.rb +81 -0
  743. data/lib/datadog/tracing/contrib/racecar/events/batch.rb +38 -0
  744. data/lib/datadog/tracing/contrib/racecar/events/consume.rb +35 -0
  745. data/lib/datadog/tracing/contrib/racecar/events/message.rb +38 -0
  746. data/lib/datadog/tracing/contrib/racecar/events.rb +36 -0
  747. data/lib/datadog/tracing/contrib/racecar/ext.rb +33 -0
  748. data/lib/datadog/tracing/contrib/racecar/integration.rb +44 -0
  749. data/lib/datadog/tracing/contrib/racecar/patcher.rb +29 -0
  750. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +59 -0
  751. data/lib/datadog/tracing/contrib/rack/ext.rb +30 -0
  752. data/lib/datadog/tracing/contrib/rack/header_collection.rb +50 -0
  753. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +63 -0
  754. data/lib/datadog/tracing/contrib/rack/integration.rb +50 -0
  755. data/lib/datadog/tracing/contrib/rack/middlewares.rb +475 -0
  756. data/lib/datadog/tracing/contrib/rack/patcher.rb +119 -0
  757. data/lib/datadog/tracing/contrib/rack/request_queue.rb +49 -0
  758. data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
  759. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +58 -0
  760. data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +10 -0
  761. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +76 -0
  762. data/lib/datadog/tracing/contrib/rails/ext.rb +33 -0
  763. data/lib/datadog/tracing/contrib/rails/framework.rb +148 -0
  764. data/lib/datadog/tracing/contrib/rails/integration.rb +52 -0
  765. data/lib/datadog/tracing/contrib/rails/log_injection.rb +29 -0
  766. data/lib/datadog/tracing/contrib/rails/middlewares.rb +46 -0
  767. data/lib/datadog/tracing/contrib/rails/patcher.rb +98 -0
  768. data/lib/datadog/tracing/contrib/rails/railtie.rb +19 -0
  769. data/lib/datadog/tracing/contrib/rails/runner.rb +117 -0
  770. data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +55 -0
  771. data/lib/datadog/tracing/contrib/rake/ext.rb +27 -0
  772. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +103 -0
  773. data/lib/datadog/tracing/contrib/rake/integration.rb +43 -0
  774. data/lib/datadog/tracing/contrib/rake/patcher.rb +33 -0
  775. data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +49 -0
  776. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +57 -0
  777. data/lib/datadog/tracing/contrib/redis/ext.rb +36 -0
  778. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +53 -0
  779. data/lib/datadog/tracing/contrib/redis/integration.rb +80 -0
  780. data/lib/datadog/tracing/contrib/redis/patcher.rb +92 -0
  781. data/lib/datadog/tracing/contrib/redis/quantize.rb +80 -0
  782. data/lib/datadog/tracing/contrib/redis/tags.rb +72 -0
  783. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +85 -0
  784. data/lib/datadog/tracing/contrib/redis/vendor/LICENSE +20 -0
  785. data/lib/datadog/tracing/contrib/redis/vendor/resolver.rb +160 -0
  786. data/lib/datadog/tracing/contrib/registerable.rb +50 -0
  787. data/lib/datadog/tracing/contrib/registry.rb +52 -0
  788. data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +42 -0
  789. data/lib/datadog/tracing/contrib/resque/ext.rb +22 -0
  790. data/lib/datadog/tracing/contrib/resque/integration.rb +48 -0
  791. data/lib/datadog/tracing/contrib/resque/patcher.rb +29 -0
  792. data/lib/datadog/tracing/contrib/resque/resque_job.rb +106 -0
  793. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +55 -0
  794. data/lib/datadog/tracing/contrib/rest_client/ext.rb +29 -0
  795. data/lib/datadog/tracing/contrib/rest_client/integration.rb +46 -0
  796. data/lib/datadog/tracing/contrib/rest_client/patcher.rb +28 -0
  797. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +137 -0
  798. data/lib/datadog/tracing/contrib/roda/configuration/settings.rb +38 -0
  799. data/lib/datadog/tracing/contrib/roda/ext.rb +19 -0
  800. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +78 -0
  801. data/lib/datadog/tracing/contrib/roda/integration.rb +45 -0
  802. data/lib/datadog/tracing/contrib/roda/patcher.rb +30 -0
  803. data/lib/datadog/tracing/contrib/semantic_logger/configuration/settings.rb +24 -0
  804. data/lib/datadog/tracing/contrib/semantic_logger/ext.rb +15 -0
  805. data/lib/datadog/tracing/contrib/semantic_logger/instrumentation.rb +35 -0
  806. data/lib/datadog/tracing/contrib/semantic_logger/integration.rb +52 -0
  807. data/lib/datadog/tracing/contrib/semantic_logger/patcher.rb +29 -0
  808. data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +37 -0
  809. data/lib/datadog/tracing/contrib/sequel/database.rb +62 -0
  810. data/lib/datadog/tracing/contrib/sequel/dataset.rb +67 -0
  811. data/lib/datadog/tracing/contrib/sequel/ext.rb +23 -0
  812. data/lib/datadog/tracing/contrib/sequel/integration.rb +43 -0
  813. data/lib/datadog/tracing/contrib/sequel/patcher.rb +37 -0
  814. data/lib/datadog/tracing/contrib/sequel/utils.rb +90 -0
  815. data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +43 -0
  816. data/lib/datadog/tracing/contrib/shoryuken/ext.rb +27 -0
  817. data/lib/datadog/tracing/contrib/shoryuken/integration.rb +44 -0
  818. data/lib/datadog/tracing/contrib/shoryuken/patcher.rb +28 -0
  819. data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +65 -0
  820. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +67 -0
  821. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +47 -0
  822. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +49 -0
  823. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +45 -0
  824. data/lib/datadog/tracing/contrib/sidekiq/integration.rb +61 -0
  825. data/lib/datadog/tracing/contrib/sidekiq/patcher.rb +90 -0
  826. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/heartbeat.rb +61 -0
  827. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/job_fetch.rb +36 -0
  828. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/redis_info.rb +34 -0
  829. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/scheduled_poller.rb +57 -0
  830. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/stop.rb +34 -0
  831. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +91 -0
  832. data/lib/datadog/tracing/contrib/sidekiq/utils.rb +44 -0
  833. data/lib/datadog/tracing/contrib/sidekiq.rb +37 -0
  834. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +46 -0
  835. data/lib/datadog/tracing/contrib/sinatra/env.rb +38 -0
  836. data/lib/datadog/tracing/contrib/sinatra/ext.rb +31 -0
  837. data/lib/datadog/tracing/contrib/sinatra/framework.rb +116 -0
  838. data/lib/datadog/tracing/contrib/sinatra/integration.rb +43 -0
  839. data/lib/datadog/tracing/contrib/sinatra/patcher.rb +75 -0
  840. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +90 -0
  841. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +109 -0
  842. data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +43 -0
  843. data/lib/datadog/tracing/contrib/sneakers/ext.rb +27 -0
  844. data/lib/datadog/tracing/contrib/sneakers/integration.rb +44 -0
  845. data/lib/datadog/tracing/contrib/sneakers/patcher.rb +27 -0
  846. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +60 -0
  847. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +97 -0
  848. data/lib/datadog/tracing/contrib/status_range_env_parser.rb +33 -0
  849. data/lib/datadog/tracing/contrib/status_range_matcher.rb +32 -0
  850. data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +37 -0
  851. data/lib/datadog/tracing/contrib/stripe/ext.rb +27 -0
  852. data/lib/datadog/tracing/contrib/stripe/integration.rb +43 -0
  853. data/lib/datadog/tracing/contrib/stripe/patcher.rb +28 -0
  854. data/lib/datadog/tracing/contrib/stripe/request.rb +68 -0
  855. data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +39 -0
  856. data/lib/datadog/tracing/contrib/sucker_punch/exception_handler.rb +28 -0
  857. data/lib/datadog/tracing/contrib/sucker_punch/ext.rb +28 -0
  858. data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +104 -0
  859. data/lib/datadog/tracing/contrib/sucker_punch/integration.rb +43 -0
  860. data/lib/datadog/tracing/contrib/sucker_punch/patcher.rb +35 -0
  861. data/lib/datadog/tracing/contrib/support.rb +28 -0
  862. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +63 -0
  863. data/lib/datadog/tracing/contrib/trilogy/ext.rb +27 -0
  864. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +97 -0
  865. data/lib/datadog/tracing/contrib/trilogy/integration.rb +43 -0
  866. data/lib/datadog/tracing/contrib/trilogy/patcher.rb +31 -0
  867. data/lib/datadog/tracing/contrib/utils/database.rb +31 -0
  868. data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +111 -0
  869. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +179 -0
  870. data/lib/datadog/tracing/contrib.rb +82 -0
  871. data/lib/datadog/tracing/correlation.rb +113 -0
  872. data/lib/datadog/tracing/diagnostics/environment_logger.rb +163 -0
  873. data/lib/datadog/tracing/diagnostics/ext.rb +36 -0
  874. data/lib/datadog/tracing/diagnostics/health.rb +40 -0
  875. data/lib/datadog/tracing/distributed/b3_multi.rb +73 -0
  876. data/lib/datadog/tracing/distributed/b3_single.rb +71 -0
  877. data/lib/datadog/tracing/distributed/baggage.rb +196 -0
  878. data/lib/datadog/tracing/distributed/datadog.rb +201 -0
  879. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +82 -0
  880. data/lib/datadog/tracing/distributed/fetcher.rb +21 -0
  881. data/lib/datadog/tracing/distributed/helpers.rb +65 -0
  882. data/lib/datadog/tracing/distributed/none.rb +20 -0
  883. data/lib/datadog/tracing/distributed/propagation.rb +187 -0
  884. data/lib/datadog/tracing/distributed/propagation_policy.rb +42 -0
  885. data/lib/datadog/tracing/distributed/trace_context.rb +444 -0
  886. data/lib/datadog/tracing/event.rb +74 -0
  887. data/lib/datadog/tracing/flush.rb +96 -0
  888. data/lib/datadog/tracing/metadata/analytics.rb +26 -0
  889. data/lib/datadog/tracing/metadata/errors.rb +32 -0
  890. data/lib/datadog/tracing/metadata/ext.rb +213 -0
  891. data/lib/datadog/tracing/metadata/metastruct.rb +36 -0
  892. data/lib/datadog/tracing/metadata/metastruct_tagging.rb +42 -0
  893. data/lib/datadog/tracing/metadata/tagging.rb +131 -0
  894. data/lib/datadog/tracing/metadata.rb +22 -0
  895. data/lib/datadog/tracing/pipeline/span_filter.rb +48 -0
  896. data/lib/datadog/tracing/pipeline/span_processor.rb +41 -0
  897. data/lib/datadog/tracing/pipeline.rb +63 -0
  898. data/lib/datadog/tracing/remote.rb +85 -0
  899. data/lib/datadog/tracing/runtime/metrics.rb +17 -0
  900. data/lib/datadog/tracing/sampling/all_sampler.rb +24 -0
  901. data/lib/datadog/tracing/sampling/ext.rb +58 -0
  902. data/lib/datadog/tracing/sampling/matcher.rb +119 -0
  903. data/lib/datadog/tracing/sampling/priority_sampler.rb +160 -0
  904. data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +87 -0
  905. data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +63 -0
  906. data/lib/datadog/tracing/sampling/rate_sampler.rb +59 -0
  907. data/lib/datadog/tracing/sampling/rule.rb +86 -0
  908. data/lib/datadog/tracing/sampling/rule_sampler.rb +172 -0
  909. data/lib/datadog/tracing/sampling/sampler.rb +32 -0
  910. data/lib/datadog/tracing/sampling/span/ext.rb +25 -0
  911. data/lib/datadog/tracing/sampling/span/matcher.rb +61 -0
  912. data/lib/datadog/tracing/sampling/span/rule.rb +77 -0
  913. data/lib/datadog/tracing/sampling/span/rule_parser.rb +104 -0
  914. data/lib/datadog/tracing/sampling/span/sampler.rb +70 -0
  915. data/lib/datadog/tracing/span.rb +236 -0
  916. data/lib/datadog/tracing/span_event.rb +161 -0
  917. data/lib/datadog/tracing/span_link.rb +92 -0
  918. data/lib/datadog/tracing/span_operation.rb +561 -0
  919. data/lib/datadog/tracing/sync_writer.rb +71 -0
  920. data/lib/datadog/tracing/trace_digest.rb +190 -0
  921. data/lib/datadog/tracing/trace_operation.rb +556 -0
  922. data/lib/datadog/tracing/trace_segment.rb +227 -0
  923. data/lib/datadog/tracing/tracer.rb +644 -0
  924. data/lib/datadog/tracing/transport/http/api.rb +44 -0
  925. data/lib/datadog/tracing/transport/http/client.rb +59 -0
  926. data/lib/datadog/tracing/transport/http/statistics.rb +47 -0
  927. data/lib/datadog/tracing/transport/http/traces.rb +155 -0
  928. data/lib/datadog/tracing/transport/http.rb +44 -0
  929. data/lib/datadog/tracing/transport/io/client.rb +90 -0
  930. data/lib/datadog/tracing/transport/io/response.rb +27 -0
  931. data/lib/datadog/tracing/transport/io/traces.rb +101 -0
  932. data/lib/datadog/tracing/transport/io.rb +30 -0
  933. data/lib/datadog/tracing/transport/serializable_trace.rb +155 -0
  934. data/lib/datadog/tracing/transport/statistics.rb +77 -0
  935. data/lib/datadog/tracing/transport/trace_formatter.rb +276 -0
  936. data/lib/datadog/tracing/transport/traces.rb +258 -0
  937. data/lib/datadog/tracing/utils.rb +99 -0
  938. data/lib/datadog/tracing/workers/trace_writer.rb +199 -0
  939. data/lib/datadog/tracing/workers.rb +126 -0
  940. data/lib/datadog/tracing/writer.rb +190 -0
  941. data/lib/datadog/tracing.rb +214 -0
  942. data/lib/datadog/version.rb +27 -0
  943. data/lib/datadog.rb +20 -0
  944. metadata +1074 -0
@@ -0,0 +1,1423 @@
1
+ #include <ruby.h>
2
+ #include <ruby/thread.h>
3
+ #include <ruby/thread_native.h>
4
+ #include <ruby/debug.h>
5
+ #include <stdbool.h>
6
+ #include <stdatomic.h>
7
+ #include <signal.h>
8
+ #include <errno.h>
9
+
10
+ #include "helpers.h"
11
+ #include "ruby_helpers.h"
12
+ #include "collectors_thread_context.h"
13
+ #include "collectors_dynamic_sampling_rate.h"
14
+ #include "collectors_idle_sampling_helper.h"
15
+ #include "collectors_discrete_dynamic_sampler.h"
16
+ #include "private_vm_api_access.h"
17
+ #include "setup_signal_handler.h"
18
+ #include "time_helpers.h"
19
+
20
+ // Used to trigger the execution of Collectors::ThreadContext, which implements all of the sampling logic
21
+ // itself; this class only implements the "when to do it" part.
22
+ //
23
+ // This file implements the native bits of the Datadog::Profiling::Collectors::CpuAndWallTimeWorker class
24
+
25
+ // ---
26
+ // Here be dragons: This component is quite fiddly and probably one of the more complex in the profiler as it deals with
27
+ // multiple threads, signal handlers, global state, etc.
28
+ //
29
+ // ## Design notes for this class:
30
+ //
31
+ // ### Constraints
32
+ //
33
+ // Currently, sampling Ruby threads requires calling Ruby VM APIs that are only safe to call while holding on to the
34
+ // global VM lock (and are not async-signal safe -- cannot be called from a signal handler).
35
+ //
36
+ // @ivoanjo: As a note, I don't think we should think of this constraint as set in stone. Since we can reach inside the Ruby
37
+ // internals, we may be able to figure out a way of overcoming it. But it's definitely going to be hard so for now
38
+ // we're considering it as a given.
39
+ //
40
+ // ### Flow for triggering CPU/Wall-time samples
41
+ //
42
+ // The flow for triggering samples is as follows:
43
+ //
44
+ // 1. Inside the `run_sampling_trigger_loop` function (running in the `CpuAndWallTimeWorker` background thread),
45
+ // a `SIGPROF` signal gets sent to the current process.
46
+ //
47
+ // 2. The `handle_sampling_signal` signal handler function gets called to handle the `SIGPROF` signal.
48
+ //
49
+ // Which thread the signal handler function gets called on by the operating system is quite important. We need to perform
50
+ // an operation -- calling the `rb_postponed_job_register_one` API -- that can only be called from the thread that
51
+ // is holding on to the global VM lock. So this is the thread we're "hoping" our signal lands on.
52
+ //
53
+ // The signal never lands on the `CpuAndWallTimeWorker` background thread because we explicitly block it off from that
54
+ // thread in `block_sigprof_signal_handler_from_running_in_current_thread`.
55
+ //
56
+ // If the signal lands on a thread that is not holding onto the global VM lock, we can't proceed to the next step,
57
+ // and we need to restart the sampling flow from step 1. (There's still quite a few improvements we can make here,
58
+ // but this is the current state of the implementation).
59
+ //
60
+ // 3. Inside `handle_sampling_signal`, if it's getting executed by the Ruby thread that is holding the global VM lock,
61
+ // we can call `rb_postponed_job_register_one` to ask the Ruby VM to call our `sample_from_postponed_job` function
62
+ // "as soon as it can".
63
+ //
64
+ // 4. The Ruby VM calls our `sample_from_postponed_job` from a thread holding the global VM lock. A sample is recorded by
65
+ // calling `thread_context_collector_sample`.
66
+ //
67
+ // ### TracePoints and Forking
68
+ //
69
+ // When the Ruby VM forks, the CPU/Wall-time profiling stops naturally because it's triggered by a background thread
70
+ // that doesn't get automatically restarted by the VM on the child process. (The profiler does trigger its restart at
71
+ // some point -- see `Profiling::Tasks::Setup` for details).
72
+ //
73
+ // But this doesn't apply to any `TracePoint`s this class may use, which will continue to be active. Thus, we need to
74
+ // always remember consider this case of -- the worker thread may not be alive but the `TracePoint`s can continue to
75
+ // trigger samples.
76
+ //
77
+ // ---
78
+
79
+ #define ERR_CLOCK_FAIL "failed to get clock time"
80
+
81
+ // Maximum allowed value for an allocation weight. Attempts to use higher values will result in clamping.
82
+ // See https://docs.google.com/document/d/1lWLB714wlLBBq6T4xZyAc4a5wtWhSmr4-hgiPKeErlA/edit#heading=h.ugp0zxcj5iqh
83
+ // (Datadog-only link) for research backing the choice of this value.
84
+ unsigned int MAX_ALLOC_WEIGHT = 10000;
85
+
86
+ #ifndef NO_POSTPONED_TRIGGER
87
+ // Used to call the rb_postponed_job_trigger from Ruby 3.3+. These get initialized in
88
+ // `collectors_cpu_and_wall_time_worker_init` below and always get reused after that.
89
+ static rb_postponed_job_handle_t sample_from_postponed_job_handle;
90
+ static rb_postponed_job_handle_t after_gc_from_postponed_job_handle;
91
+ static rb_postponed_job_handle_t after_gvl_running_from_postponed_job_handle;
92
+ #endif
93
+
94
+ // Contains state for a single CpuAndWallTimeWorker instance
95
+ typedef struct {
96
+ // These are immutable after initialization
97
+
98
+ bool gc_profiling_enabled;
99
+ bool no_signals_workaround_enabled;
100
+ bool dynamic_sampling_rate_enabled;
101
+ bool allocation_profiling_enabled;
102
+ bool allocation_counting_enabled;
103
+ bool gvl_profiling_enabled;
104
+ bool skip_idle_samples_for_testing;
105
+ bool sighandler_sampling_enabled;
106
+ VALUE self_instance;
107
+ VALUE thread_context_collector_instance;
108
+ VALUE idle_sampling_helper_instance;
109
+ VALUE owner_thread;
110
+ dynamic_sampling_rate_state cpu_dynamic_sampling_rate;
111
+ discrete_dynamic_sampler allocation_sampler;
112
+ VALUE gc_tracepoint; // Used to get gc start/finish information
113
+
114
+ // These are mutable and used to signal things between the worker thread and other threads
115
+
116
+ atomic_bool should_run;
117
+ // When something goes wrong during sampling, we record the Ruby exception here, so that it can be "re-raised" on
118
+ // the CpuAndWallTimeWorker thread
119
+ VALUE failure_exception;
120
+ // Used by `_native_stop` to flag the worker thread to start (see comment on `_native_sampling_loop`)
121
+ VALUE stop_thread;
122
+
123
+ // Others
124
+
125
+ // Used to detect/avoid nested sampling, e.g. when on_newobj_event gets triggered by a memory allocation
126
+ // that happens during another sample, or when the signal handler gets triggered while we're already in the middle of
127
+ // sampling.
128
+ //
129
+ // @ivoanjo: Right now we always sample inside `safely_call`; if that ever changes, this flag may need to become
130
+ // volatile/atomic/have some barriers to ensure it's visible during e.g. signal handlers.
131
+ bool during_sample;
132
+
133
+ #ifndef NO_GVL_INSTRUMENTATION
134
+ // Only set when sampling is active (gets created at start and cleaned on stop)
135
+ rb_internal_thread_event_hook_t *gvl_profiling_hook;
136
+ #endif
137
+
138
+ struct stats {
139
+ // # Generic stats
140
+ // How many times we tried to trigger a sample
141
+ unsigned int trigger_sample_attempts;
142
+ // How many times we tried to simulate signal delivery
143
+ unsigned int trigger_simulated_signal_delivery_attempts;
144
+ // How many times we actually simulated signal delivery
145
+ unsigned int simulated_signal_delivery;
146
+ // How many times we actually called rb_postponed_job_register_one from the signal handler
147
+ unsigned int signal_handler_enqueued_sample;
148
+ // How many times we prepared a sample (sampled directly) from the signal handler
149
+ unsigned int signal_handler_prepared_sample;
150
+ // How many times the signal handler was called from the wrong thread
151
+ unsigned int signal_handler_wrong_thread;
152
+ // How many times we actually tried to interrupt a thread for sampling
153
+ unsigned int interrupt_thread_attempts;
154
+
155
+ // # CPU/Walltime sampling stats
156
+ // How many times we actually CPU/wall sampled
157
+ unsigned int cpu_sampled;
158
+ // How many times we skipped a CPU/wall sample because of the dynamic sampling rate mechanism
159
+ unsigned int cpu_skipped;
160
+ // Min/max/total wall-time spent on CPU/wall sampling
161
+ uint64_t cpu_sampling_time_ns_min;
162
+ uint64_t cpu_sampling_time_ns_max;
163
+ uint64_t cpu_sampling_time_ns_total;
164
+
165
+ // # Allocation sampling stats
166
+ // How many times we actually allocation sampled
167
+ uint64_t allocation_sampled;
168
+ // How many times we skipped an allocation sample because of the dynamic sampling rate mechanism
169
+ uint64_t allocation_skipped;
170
+ // Min/max/total wall-time spent on allocation sampling
171
+ uint64_t allocation_sampling_time_ns_min;
172
+ uint64_t allocation_sampling_time_ns_max;
173
+ uint64_t allocation_sampling_time_ns_total;
174
+ // How many times we saw allocations being done inside a sample
175
+ unsigned int allocations_during_sample;
176
+
177
+ // # GVL profiling stats
178
+ // How many times we triggered the after_gvl_running sampling
179
+ unsigned int after_gvl_running;
180
+ // How many times we skipped the after_gvl_running sampling
181
+ unsigned int gvl_dont_sample;
182
+ // Min/max/total wall-time spent on gvl sampling
183
+ uint64_t gvl_sampling_time_ns_min;
184
+ uint64_t gvl_sampling_time_ns_max;
185
+ uint64_t gvl_sampling_time_ns_total;
186
+ } stats;
187
+ } cpu_and_wall_time_worker_state;
188
+
189
+ static VALUE _native_new(VALUE klass);
190
+ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
191
+ static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr);
192
+ static VALUE _native_sampling_loop(VALUE self, VALUE instance);
193
+ static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread);
194
+ static VALUE stop(VALUE self_instance, VALUE optional_exception);
195
+ static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception);
196
+ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
197
+ static void *run_sampling_trigger_loop(void *state_ptr);
198
+ static void interrupt_sampling_trigger_loop(void *state_ptr);
199
+ static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused);
200
+ static VALUE rescued_sample_from_postponed_job(VALUE self_instance);
201
+ static VALUE handle_sampling_failure(VALUE self_instance, VALUE exception);
202
+ static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self);
203
+ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance);
204
+ static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance);
205
+ static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
206
+ static VALUE _native_install_testing_signal_handler(DDTRACE_UNUSED VALUE self);
207
+ static VALUE _native_remove_testing_signal_handler(DDTRACE_UNUSED VALUE self);
208
+ static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self);
209
+ static VALUE _native_gc_tracepoint(DDTRACE_UNUSED VALUE self, VALUE instance);
210
+ static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused);
211
+ static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused);
212
+ static VALUE safely_call(VALUE (*function_to_call_safely)(VALUE), VALUE function_to_call_safely_arg, VALUE instance);
213
+ static VALUE _native_simulate_handle_sampling_signal(DDTRACE_UNUSED VALUE self);
214
+ static VALUE _native_simulate_sample_from_postponed_job(DDTRACE_UNUSED VALUE self);
215
+ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance);
216
+ static VALUE _native_is_sigprof_blocked_in_current_thread(DDTRACE_UNUSED VALUE self);
217
+ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance);
218
+ static VALUE _native_stats_reset_not_thread_safe(DDTRACE_UNUSED VALUE self, VALUE instance);
219
+ void *simulate_sampling_signal_delivery(DDTRACE_UNUSED void *_unused);
220
+ static void grab_gvl_and_sample(void);
221
+ static void reset_stats_not_thread_safe(cpu_and_wall_time_worker_state *state);
222
+ static void sleep_for(uint64_t time_ns);
223
+ static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self);
224
+ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *unused2);
225
+ static void disable_tracepoints(cpu_and_wall_time_worker_state *state);
226
+ static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self);
227
+ static VALUE rescued_sample_allocation(VALUE tracepoint_data);
228
+ static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error);
229
+ static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
230
+ static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self);
231
+ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);
232
+ #ifndef NO_GVL_INSTRUMENTATION
233
+ static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused);
234
+ static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
235
+ #endif
236
+ static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance);
237
+ static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance);
238
+ static inline void during_sample_enter(cpu_and_wall_time_worker_state* state);
239
+ static inline void during_sample_exit(cpu_and_wall_time_worker_state* state);
240
+
241
+ // We're using `on_newobj_event` function with `rb_add_event_hook2`, which requires in its public signature a function
242
+ // with signature `rb_event_hook_func_t` which doesn't match `on_newobj_event`.
243
+ //
244
+ // But in practice, because we pass the `RUBY_EVENT_HOOK_FLAG_RAW_ARG` flag to `rb_add_event_hook2`, it casts the
245
+ // expected signature into a `rb_event_hook_raw_arg_func_t`:
246
+ // > typedef void (*rb_event_hook_raw_arg_func_t)(VALUE data, const rb_trace_arg_t *arg); (from vm_trace.c)
247
+ // which does match `on_newobj_event`.
248
+ //
249
+ // So TL;DR we're just doing this here to avoid the warning and explain why the apparent mismatch in function signatures.
250
+ #pragma GCC diagnostic push
251
+ #pragma GCC diagnostic ignored "-Wcast-function-type"
252
+ static const rb_event_hook_func_t on_newobj_event_as_hook = (rb_event_hook_func_t) on_newobj_event;
253
+ #pragma GCC diagnostic pop
254
+
255
+ // Note on sampler global state safety:
256
+ //
257
+ // Both `active_sampler_instance` and `active_sampler_instance_state` are **GLOBAL** state. Be careful when accessing
258
+ // or modifying them.
259
+ // In particular, it's important to only mutate them while holding the global VM lock, to ensure correctness.
260
+ //
261
+ // This global state is needed because a bunch of functions on this file need to access it from situations
262
+ // (e.g. signal handler) where it's impossible or just awkward to pass it as an argument.
263
+ static VALUE active_sampler_instance = Qnil;
264
+ static cpu_and_wall_time_worker_state *active_sampler_instance_state = NULL;
265
+
266
+ // See handle_sampling_signal for details on what this does
267
+ #ifdef NO_POSTPONED_TRIGGER
268
+ static void *gc_finalize_deferred_workaround;
269
+ #endif
270
+
271
+ // Used to implement CpuAndWallTimeWorker._native_allocation_count . To be able to use cheap thread-local variables
272
+ // (here with `__thread`, see https://gcc.gnu.org/onlinedocs/gcc/Thread-Local.html), this needs to be global.
273
+ //
274
+ // Carryover of state between profiler instances can happen and is not considered to be a problem -- see expectations for this
275
+ // API documented in profiling.rb .
276
+ __thread uint64_t allocation_count = 0;
277
+
278
+ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
279
+ rb_global_variable(&active_sampler_instance);
280
+
281
+ #ifndef NO_POSTPONED_TRIGGER
282
+ int unused_flags = 0;
283
+ sample_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, sample_from_postponed_job, NULL);
284
+ after_gc_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gc_from_postponed_job, NULL);
285
+ after_gvl_running_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gvl_running_from_postponed_job, NULL);
286
+
287
+ if (
288
+ sample_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
289
+ after_gc_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
290
+ after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
291
+ ) {
292
+ rb_raise(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
293
+ }
294
+ #else
295
+ gc_finalize_deferred_workaround = objspace_ptr_for_gc_finalize_deferred_workaround();
296
+ #endif
297
+
298
+ VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
299
+ VALUE collectors_cpu_and_wall_time_worker_class = rb_define_class_under(collectors_module, "CpuAndWallTimeWorker", rb_cObject);
300
+ // Hosts methods used for testing the native code using RSpec
301
+ VALUE testing_module = rb_define_module_under(collectors_cpu_and_wall_time_worker_class, "Testing");
302
+
303
+ // Instances of the CpuAndWallTimeWorker class are "TypedData" objects.
304
+ // "TypedData" objects are special objects in the Ruby VM that can wrap C structs.
305
+ // In this case, it wraps the cpu_and_wall_time_worker_state.
306
+ //
307
+ // Because Ruby doesn't know how to initialize native-level structs, we MUST override the allocation function for objects
308
+ // of this class so that we can manage this part. Not overriding or disabling the allocation function is a common
309
+ // gotcha for "TypedData" objects that can very easily lead to VM crashes, see for instance
310
+ // https://bugs.ruby-lang.org/issues/18007 for a discussion around this.
311
+ rb_define_alloc_func(collectors_cpu_and_wall_time_worker_class, _native_new);
312
+
313
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_initialize", _native_initialize, -1);
314
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_sampling_loop", _native_sampling_loop, 1);
315
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stop", _native_stop, 2);
316
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_reset_after_fork", _native_reset_after_fork, 1);
317
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stats", _native_stats, 1);
318
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stats_reset_not_thread_safe", _native_stats_reset_not_thread_safe, 1);
319
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_allocation_count", _native_allocation_count, 0);
320
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_is_running?", _native_is_running, 1);
321
+ rb_define_singleton_method(testing_module, "_native_current_sigprof_signal_handler", _native_current_sigprof_signal_handler, 0);
322
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_hold_signals", _native_hold_signals, 0);
323
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_resume_signals", _native_resume_signals, 0);
324
+ rb_define_singleton_method(testing_module, "_native_install_testing_signal_handler", _native_install_testing_signal_handler, 0);
325
+ rb_define_singleton_method(testing_module, "_native_remove_testing_signal_handler", _native_remove_testing_signal_handler, 0);
326
+ rb_define_singleton_method(testing_module, "_native_trigger_sample", _native_trigger_sample, 0);
327
+ rb_define_singleton_method(testing_module, "_native_gc_tracepoint", _native_gc_tracepoint, 1);
328
+ rb_define_singleton_method(testing_module, "_native_simulate_handle_sampling_signal", _native_simulate_handle_sampling_signal, 0);
329
+ rb_define_singleton_method(testing_module, "_native_simulate_sample_from_postponed_job", _native_simulate_sample_from_postponed_job, 0);
330
+ rb_define_singleton_method(testing_module, "_native_is_sigprof_blocked_in_current_thread", _native_is_sigprof_blocked_in_current_thread, 0);
331
+ rb_define_singleton_method(testing_module, "_native_with_blocked_sigprof", _native_with_blocked_sigprof, 0);
332
+ rb_define_singleton_method(testing_module, "_native_delayed_error", _native_delayed_error, 2);
333
+ rb_define_singleton_method(testing_module, "_native_gvl_profiling_hook_active", _native_gvl_profiling_hook_active, 1);
334
+ }
335
+
336
+ // This structure is used to define a Ruby object that stores a pointer to a cpu_and_wall_time_worker_state
337
+ // See also https://github.com/ruby/ruby/blob/master/doc/extension.rdoc for how this works
338
+ static const rb_data_type_t cpu_and_wall_time_worker_typed_data = {
339
+ .wrap_struct_name = "Datadog::Profiling::Collectors::CpuAndWallTimeWorker",
340
+ .function = {
341
+ .dmark = cpu_and_wall_time_worker_typed_data_mark,
342
+ .dfree = RUBY_DEFAULT_FREE,
343
+ .dsize = NULL, // We don't track memory usage (although it'd be cool if we did!)
344
+ //.dcompact = NULL, // FIXME: Add support for compaction
345
+ },
346
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
347
+ };
348
+
349
+ static VALUE _native_new(VALUE klass) {
350
+ long now = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
351
+
352
+ cpu_and_wall_time_worker_state *state = ruby_xcalloc(1, sizeof(cpu_and_wall_time_worker_state));
353
+
354
+ // Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
355
+ // being leaked.
356
+
357
+ state->gc_profiling_enabled = false;
358
+ state->no_signals_workaround_enabled = false;
359
+ state->dynamic_sampling_rate_enabled = true;
360
+ state->allocation_profiling_enabled = false;
361
+ state->allocation_counting_enabled = false;
362
+ state->gvl_profiling_enabled = false;
363
+ state->skip_idle_samples_for_testing = false;
364
+ state->sighandler_sampling_enabled = false;
365
+ state->thread_context_collector_instance = Qnil;
366
+ state->idle_sampling_helper_instance = Qnil;
367
+ state->owner_thread = Qnil;
368
+ dynamic_sampling_rate_init(&state->cpu_dynamic_sampling_rate);
369
+ state->gc_tracepoint = Qnil;
370
+
371
+ atomic_init(&state->should_run, false);
372
+ state->failure_exception = Qnil;
373
+ state->stop_thread = Qnil;
374
+
375
+ during_sample_exit(state);
376
+
377
+ #ifndef NO_GVL_INSTRUMENTATION
378
+ state->gvl_profiling_hook = NULL;
379
+ #endif
380
+
381
+ reset_stats_not_thread_safe(state);
382
+ discrete_dynamic_sampler_init(&state->allocation_sampler, "allocation", now);
383
+
384
+ // Note: As of this writing, no new Ruby objects get created and stored in the state. If that ever changes, remember
385
+ // to keep them on the stack and mark them with RB_GC_GUARD -- otherwise it's possible for a GC to run and
386
+ // since the instance representing the state does not yet exist, such objects will not get marked.
387
+
388
+ return state->self_instance = TypedData_Wrap_Struct(klass, &cpu_and_wall_time_worker_typed_data, state);
389
+ }
390
+
391
+ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self) {
392
+ VALUE options;
393
+ rb_scan_args(argc, argv, "0:", &options);
394
+ if (options == Qnil) options = rb_hash_new();
395
+
396
+ VALUE self_instance = rb_hash_fetch(options, ID2SYM(rb_intern("self_instance")));
397
+ VALUE thread_context_collector_instance = rb_hash_fetch(options, ID2SYM(rb_intern("thread_context_collector")));
398
+ VALUE gc_profiling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("gc_profiling_enabled")));
399
+ VALUE idle_sampling_helper_instance = rb_hash_fetch(options, ID2SYM(rb_intern("idle_sampling_helper")));
400
+ VALUE no_signals_workaround_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("no_signals_workaround_enabled")));
401
+ VALUE dynamic_sampling_rate_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("dynamic_sampling_rate_enabled")));
402
+ VALUE dynamic_sampling_rate_overhead_target_percentage = rb_hash_fetch(options, ID2SYM(rb_intern("dynamic_sampling_rate_overhead_target_percentage")));
403
+ VALUE allocation_profiling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("allocation_profiling_enabled")));
404
+ VALUE allocation_counting_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("allocation_counting_enabled")));
405
+ VALUE gvl_profiling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("gvl_profiling_enabled")));
406
+ VALUE skip_idle_samples_for_testing = rb_hash_fetch(options, ID2SYM(rb_intern("skip_idle_samples_for_testing")));
407
+ VALUE sighandler_sampling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("sighandler_sampling_enabled")));
408
+
409
+ ENFORCE_BOOLEAN(gc_profiling_enabled);
410
+ ENFORCE_BOOLEAN(no_signals_workaround_enabled);
411
+ ENFORCE_BOOLEAN(dynamic_sampling_rate_enabled);
412
+ ENFORCE_TYPE(dynamic_sampling_rate_overhead_target_percentage, T_FLOAT);
413
+ ENFORCE_BOOLEAN(allocation_profiling_enabled);
414
+ ENFORCE_BOOLEAN(allocation_counting_enabled);
415
+ ENFORCE_BOOLEAN(gvl_profiling_enabled);
416
+ ENFORCE_BOOLEAN(skip_idle_samples_for_testing)
417
+ ENFORCE_BOOLEAN(sighandler_sampling_enabled)
418
+
419
+ cpu_and_wall_time_worker_state *state;
420
+ TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
421
+
422
+ state->gc_profiling_enabled = (gc_profiling_enabled == Qtrue);
423
+ state->no_signals_workaround_enabled = (no_signals_workaround_enabled == Qtrue);
424
+ state->dynamic_sampling_rate_enabled = (dynamic_sampling_rate_enabled == Qtrue);
425
+ state->allocation_profiling_enabled = (allocation_profiling_enabled == Qtrue);
426
+ state->allocation_counting_enabled = (allocation_counting_enabled == Qtrue);
427
+ state->gvl_profiling_enabled = (gvl_profiling_enabled == Qtrue);
428
+ state->skip_idle_samples_for_testing = (skip_idle_samples_for_testing == Qtrue);
429
+ state->sighandler_sampling_enabled = (sighandler_sampling_enabled == Qtrue);
430
+
431
+ double total_overhead_target_percentage = NUM2DBL(dynamic_sampling_rate_overhead_target_percentage);
432
+ if (!state->allocation_profiling_enabled) {
433
+ dynamic_sampling_rate_set_overhead_target_percentage(&state->cpu_dynamic_sampling_rate, total_overhead_target_percentage);
434
+ } else {
435
+ // TODO: May be nice to offer customization here? Distribute available "overhead" margin with a bias towards one or the other
436
+ // sampler.
437
+ dynamic_sampling_rate_set_overhead_target_percentage(&state->cpu_dynamic_sampling_rate, total_overhead_target_percentage / 2);
438
+ long now = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
439
+ discrete_dynamic_sampler_set_overhead_target_percentage(&state->allocation_sampler, total_overhead_target_percentage / 2, now);
440
+ }
441
+
442
+ state->thread_context_collector_instance = enforce_thread_context_collector_instance(thread_context_collector_instance);
443
+ state->idle_sampling_helper_instance = idle_sampling_helper_instance;
444
+ state->gc_tracepoint = rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_GC_ENTER | RUBY_INTERNAL_EVENT_GC_EXIT, on_gc_event, NULL /* unused */);
445
+
446
+ return Qtrue;
447
+ }
448
+
449
+ // Since our state contains references to Ruby objects, we need to tell the Ruby GC about them
450
+ static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr) {
451
+ cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
452
+
453
+ rb_gc_mark(state->thread_context_collector_instance);
454
+ rb_gc_mark(state->idle_sampling_helper_instance);
455
+ rb_gc_mark(state->owner_thread);
456
+ rb_gc_mark(state->failure_exception);
457
+ rb_gc_mark(state->stop_thread);
458
+ rb_gc_mark(state->gc_tracepoint);
459
+ }
460
+
461
+ // Called in a background thread created in CpuAndWallTimeWorker#start
462
+ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
463
+ cpu_and_wall_time_worker_state *state;
464
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
465
+
466
+ // If we already got a delayed exception registered even before starting, raise before starting
467
+ if (state->failure_exception != Qnil) {
468
+ disable_tracepoints(state);
469
+ rb_exc_raise(state->failure_exception);
470
+ }
471
+
472
+ cpu_and_wall_time_worker_state *old_state = active_sampler_instance_state;
473
+ if (old_state != NULL) {
474
+ if (is_thread_alive(old_state->owner_thread)) {
475
+ rb_raise(
476
+ rb_eRuntimeError,
477
+ "Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread"
478
+ );
479
+ } else {
480
+ // The previously active thread seems to have died without cleaning up after itself.
481
+ // In this case, we can still go ahead and start the profiler BUT we make sure to disable any existing tracepoint
482
+ // first as:
483
+ // a) If this is a new instance of the CpuAndWallTimeWorker, we don't want the tracepoint from the old instance
484
+ // being kept around
485
+ // b) If this is the same instance of the CpuAndWallTimeWorker if we call enable on a tracepoint that is already
486
+ // enabled, it will start firing more than once, see https://bugs.ruby-lang.org/issues/19114 for details.
487
+ disable_tracepoints(old_state);
488
+ }
489
+ }
490
+
491
+ // We use `stop_thread` to distinguish when `_native_stop` was called before we actually had a chance to start. In this
492
+ // situation we stop immediately and never even start the sampling trigger loop.
493
+ if (state->stop_thread == rb_thread_current()) return Qnil;
494
+
495
+ // Reset the dynamic sampling rate state, if any (reminder: the monotonic clock reference may change after a fork)
496
+ dynamic_sampling_rate_reset(&state->cpu_dynamic_sampling_rate);
497
+ long now = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
498
+ discrete_dynamic_sampler_reset(&state->allocation_sampler, now);
499
+
500
+ // This write to a global is thread-safe BECAUSE we're still holding on to the global VM lock at this point
501
+ active_sampler_instance_state = state;
502
+ active_sampler_instance = instance;
503
+ state->owner_thread = rb_thread_current();
504
+
505
+ atomic_store(&state->should_run, true);
506
+
507
+ block_sigprof_signal_handler_from_running_in_current_thread(); // We want to interrupt the thread with the global VM lock, never this one
508
+
509
+ // Release GVL, get to the actual work!
510
+ int exception_state;
511
+ rb_protect(release_gvl_and_run_sampling_trigger_loop, instance, &exception_state);
512
+
513
+ // The sample trigger loop finished (either cleanly or with an error); let's clean up
514
+
515
+ disable_tracepoints(state);
516
+
517
+ active_sampler_instance_state = NULL;
518
+ active_sampler_instance = Qnil;
519
+ state->owner_thread = Qnil;
520
+
521
+ // If this `Thread` is about to die, why is this important? It's because Ruby caches native threads for a period after
522
+ // the `Thread` dies, and reuses them if a new Ruby `Thread` gets created. This means that while conceptually the
523
+ // worker background `Thread` is about to die, the low-level native OS thread can be reused for something else in the Ruby app.
524
+ // Then, the reused thread would "inherit" the SIGPROF blocking, which is... really unexpected.
525
+ // This actually caused a flaky test -- the `native_extension_spec.rb` creates a `Thread` and tries to specifically
526
+ // send SIGPROF signals to it, and oops it could fail if it got the reused native thread from the worker which still
527
+ // had SIGPROF delivery blocked. :hide_the_pain_harold:
528
+ unblock_sigprof_signal_handler_from_running_in_current_thread();
529
+
530
+ // Why replace and not use remove the signal handler? We do this because when a process receives a SIGPROF without
531
+ // having an explicit signal handler set up, the process will instantly terminate with a confusing
532
+ // "Profiling timer expired" message left behind. (This message doesn't come from us -- it's the default message for
533
+ // an unhandled SIGPROF. Pretty confusing UNIX/POSIX behavior...)
534
+ //
535
+ // Unfortunately, because signal delivery is asynchronous, there's no way to guarantee that there are no pending
536
+ // profiler-sent signals by the time we get here and want to clean up.
537
+ // @ivoanjo: I suspect this will never happen, but the cost of getting it wrong is really high (VM terminates) so this
538
+ // is a just-in-case situation.
539
+ //
540
+ // Note 2: This can raise exceptions as well, so make sure that all cleanups are done by the time we get here.
541
+ replace_sigprof_signal_handler_with_empty_handler(handle_sampling_signal);
542
+
543
+ // Ensure that instance is not garbage collected while the native sampling loop is running; this is probably not needed, but just in case
544
+ RB_GC_GUARD(instance);
545
+
546
+ if (exception_state) rb_jump_tag(exception_state); // Re-raise any exception that happened
547
+
548
+ return Qnil;
549
+ }
550
+
551
+ static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread) {
552
+ cpu_and_wall_time_worker_state *state;
553
+ TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
554
+
555
+ state->stop_thread = worker_thread;
556
+
557
+ return stop(self_instance, /* optional_exception: */ Qnil);
558
+ }
559
+
560
+ static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception) {
561
+ atomic_store(&state->should_run, false);
562
+ state->failure_exception = optional_exception;
563
+
564
+ // Disable the tracepoints as soon as possible, so the VM doesn't keep on calling them
565
+ disable_tracepoints(state);
566
+ }
567
+
568
+ static VALUE stop(VALUE self_instance, VALUE optional_exception) {
569
+ cpu_and_wall_time_worker_state *state;
570
+ TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
571
+
572
+ stop_state(state, optional_exception);
573
+
574
+ return Qtrue;
575
+ }
576
+
577
+ // NOTE: Remember that this will run in the thread and within the scope of user code, including user C code.
578
+ // We need to be careful not to change any state that may be observed OR to restore it if we do. For instance, if anything
579
+ // we do here can set `errno`, then we must be careful to restore the old `errno` after the fact.
580
+ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext) {
581
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
582
+
583
+ // This can potentially happen if the CpuAndWallTimeWorker was stopped while the signal delivery was happening; nothing to do
584
+ if (state == NULL) return;
585
+
586
+ if (
587
+ !ruby_native_thread_p() || // Not a Ruby thread
588
+ !is_current_thread_holding_the_gvl() || // Not safe to enqueue a sample from this thread
589
+ !ddtrace_rb_ractor_main_p() // We're not on the main Ractor; we currently don't support profiling non-main Ractors
590
+ ) {
591
+ state->stats.signal_handler_wrong_thread++;
592
+ return;
593
+ }
594
+
595
+ // We assume there can be no concurrent nor nested calls to handle_sampling_signal because
596
+ // a) we get triggered using SIGPROF, and the docs state a second SIGPROF will not interrupt an existing one (see sigaction docs on sa_mask)
597
+ // b) we validate we are in the thread that has the global VM lock; if a different thread gets a signal, it will return early
598
+ // because it will not have the global VM lock
599
+
600
+ state->stats.signal_handler_enqueued_sample++;
601
+
602
+ bool sample_from_signal_handler =
603
+ state->sighandler_sampling_enabled &&
604
+ // Don't sample if we're already in the middle of processing a sample
605
+ !state->during_sample;
606
+
607
+ if (sample_from_signal_handler) {
608
+ // Buffer current stack trace. Note that this will not actually record the sample, for that we still need to wait
609
+ // until the postponed job below gets run.
610
+ bool prepared = thread_context_collector_prepare_sample_inside_signal_handler(state->thread_context_collector_instance);
611
+
612
+ if (prepared) state->stats.signal_handler_prepared_sample++;
613
+ }
614
+
615
+ #ifndef NO_POSTPONED_TRIGGER // Ruby 3.3+
616
+ rb_postponed_job_trigger(sample_from_postponed_job_handle);
617
+ #else
618
+ // Passing in `gc_finalize_deferred_workaround` is a workaround for https://bugs.ruby-lang.org/issues/19991 (for Ruby < 3.3)
619
+ //
620
+ // TL;DR the `rb_postponed_job_register_one` API is not atomic (which is why it got replaced by `rb_postponed_job_trigger`)
621
+ // and in rare cases can cause VM crashes.
622
+ //
623
+ // Specifically, if we're interrupting `rb_postponed_job_flush` (the function that processes postponed jobs), the way
624
+ // that this function reads the jobs is not atomic, and can cause our call to
625
+ // `rb_postponed_job_register(function, arg)` to clobber an existing job that is getting dequeued.
626
+ // Clobbering an existing job is somewhat annoying, but the worst part is that it can happen that we clobber only
627
+ // the existing job's arguments.
628
+ // As surveyed in https://github.com/ruby/ruby/pull/8949#issuecomment-1821441370 clobbering the arguments turns out
629
+ // to not matter in many cases as usually `rb_postponed_job_register` calls in the VM and ecosystem ignore the argument.
630
+ //
631
+ // https://bugs.ruby-lang.org/issues/19991 is the exception: inside Ruby's `gc.c`, when dealing with object
632
+ // finalizers, Ruby calls `gc_finalize_deferred_register` which internally calls
633
+ // `rb_postponed_job_register_one(gc_finalize_deferred, objspace)`.
634
+ // Clobbering this call means that `gc_finalize_deferred` would get called with `NULL`, causing a segmentation fault.
635
+ //
636
+ // Note that this is quite rare: our signal needs to land at exactly the point where the VM has read the function
637
+ // to execute, but has yet to read the arguments. @ivoanjo: I could only reproduce it by manually changing the VM
638
+ // code to simulate this happening.
639
+ //
640
+ // Thus, our workaround is simple: we pass in objspace as our argument, just in case the clobbering happens.
641
+ // In the happy path, we never use this argument so it makes no difference. In the buggy path, we avoid crashing the VM.
642
+ rb_postponed_job_register(0, sample_from_postponed_job, gc_finalize_deferred_workaround /* instead of NULL */);
643
+ #endif
644
+ }
645
+
646
+ // The actual sampling trigger loop always runs **without** the global vm lock.
647
+ static void *run_sampling_trigger_loop(void *state_ptr) {
648
+ cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
649
+
650
+ uint64_t minimum_time_between_signals = MILLIS_AS_NS(10);
651
+
652
+ while (atomic_load(&state->should_run)) {
653
+ state->stats.trigger_sample_attempts++;
654
+
655
+ if (state->no_signals_workaround_enabled) {
656
+ // In the no_signals_workaround_enabled mode, the profiler never sends SIGPROF signals.
657
+ //
658
+ // This is a fallback for a few incompatibilities and limitations -- see the code that decides when to enable
659
+ // `no_signals_workaround_enabled` in `Profiling::Component` for details.
660
+ //
661
+ // Thus, we instead pretty please ask Ruby to let us run. This means profiling data can be biased by when the Ruby
662
+ // scheduler chooses to schedule us.
663
+ state->stats.trigger_simulated_signal_delivery_attempts++;
664
+ grab_gvl_and_sample(); // Note: Can raise exceptions
665
+ } else {
666
+ current_gvl_owner owner = gvl_owner();
667
+ if (owner.valid) {
668
+ // Note that reading the GVL owner and sending them a signal is a race -- the Ruby VM keeps on executing while
669
+ // we're doing this, so we may still not signal the correct thread from time to time, but our signal handler
670
+ // includes a check to see if it got called in the right thread
671
+ state->stats.interrupt_thread_attempts++;
672
+ pthread_kill(owner.owner, SIGPROF);
673
+ } else {
674
+ if (state->skip_idle_samples_for_testing) {
675
+ // This was added to make sure our tests don't accidentally pass due to idle samples. Specifically, if we
676
+ // comment out the thread interruption code inside `if (owner.valid)` above, our tests should not pass!
677
+ } else {
678
+ // If no thread owns the Global VM Lock, the application is probably idle at the moment. We still want to sample
679
+ // so we "ask a friend" (the IdleSamplingHelper component) to grab the GVL and simulate getting a SIGPROF.
680
+ //
681
+ // In a previous version of the code, we called `grab_gvl_and_sample` directly BUT this was problematic because
682
+ // Ruby may concurrently get busy and so the CpuAndWallTimeWorker would be blocked in line to acquire the GVL
683
+ // for an uncontrolled amount of time. (This can still happen to the IdleSamplingHelper, but the
684
+ // CpuAndWallTimeWorker will still be free to interrupt the Ruby VM and keep sampling for the entire blocking period).
685
+ state->stats.trigger_simulated_signal_delivery_attempts++;
686
+ idle_sampling_helper_request_action(state->idle_sampling_helper_instance, grab_gvl_and_sample);
687
+ }
688
+ }
689
+ }
690
+
691
+ sleep_for(minimum_time_between_signals);
692
+
693
+ // The dynamic sampling rate module keeps track of how long samples are taking, and in here we extend our sleep time
694
+ // to take that into account.
695
+ // Note that we deliberately should NOT combine this sleep_for with the one above because the result of
696
+ // `dynamic_sampling_rate_get_sleep` may have changed while the above sleep was ongoing.
697
+ uint64_t extra_sleep =
698
+ dynamic_sampling_rate_get_sleep(&state->cpu_dynamic_sampling_rate, monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE));
699
+ if (state->dynamic_sampling_rate_enabled && extra_sleep > 0) sleep_for(extra_sleep);
700
+ }
701
+
702
+ return NULL; // Unused
703
+ }
704
+
705
+ // This is called by the Ruby VM when it wants to shut down the background thread
706
+ static void interrupt_sampling_trigger_loop(void *state_ptr) {
707
+ cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
708
+
709
+ atomic_store(&state->should_run, false);
710
+ }
711
+
712
+ // Note: If we ever want to get rid of the postponed job execution, remember not to clobber Ruby exceptions, as
713
+ // this function does this helpful job for us now -- https://github.com/ruby/ruby/commit/a98e343d39c4d7bf1e2190b076720f32d9f298b3.
714
+ static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused) {
715
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
716
+
717
+ // This can potentially happen if the CpuAndWallTimeWorker was stopped while the postponed job was waiting to be executed; nothing to do
718
+ if (state == NULL) return;
719
+
720
+ // @ivoanjo: I'm not sure this can ever happen because `handle_sampling_signal` only enqueues this callback if
721
+ // it's running on the main Ractor, but just in case...
722
+ if (!ddtrace_rb_ractor_main_p()) {
723
+ return; // We're not on the main Ractor; we currently don't support profiling non-main Ractors
724
+ }
725
+
726
+ during_sample_enter(state);
727
+
728
+ // Rescue against any exceptions that happen during sampling
729
+ safely_call(rescued_sample_from_postponed_job, state->self_instance, state->self_instance);
730
+
731
+ during_sample_exit(state);
732
+ }
733
+
734
+ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
735
+ cpu_and_wall_time_worker_state *state;
736
+ TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
737
+
738
+ long wall_time_ns_before_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
739
+
740
+ if (state->dynamic_sampling_rate_enabled && !dynamic_sampling_rate_should_sample(&state->cpu_dynamic_sampling_rate, wall_time_ns_before_sample)) {
741
+ state->stats.cpu_skipped++;
742
+ return Qnil;
743
+ }
744
+
745
+ state->stats.cpu_sampled++;
746
+
747
+ VALUE profiler_overhead_stack_thread = state->owner_thread; // Used to attribute profiler overhead to a different stack
748
+ thread_context_collector_sample(state->thread_context_collector_instance, wall_time_ns_before_sample, profiler_overhead_stack_thread);
749
+
750
+ long wall_time_ns_after_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
751
+ long delta_ns = wall_time_ns_after_sample - wall_time_ns_before_sample;
752
+
753
+ // Guard against wall-time going backwards, see https://github.com/DataDog/dd-trace-rb/pull/2336 for discussion.
754
+ uint64_t sampling_time_ns = delta_ns < 0 ? 0 : delta_ns;
755
+
756
+ state->stats.cpu_sampling_time_ns_min = uint64_min_of(sampling_time_ns, state->stats.cpu_sampling_time_ns_min);
757
+ state->stats.cpu_sampling_time_ns_max = uint64_max_of(sampling_time_ns, state->stats.cpu_sampling_time_ns_max);
758
+ state->stats.cpu_sampling_time_ns_total += sampling_time_ns;
759
+
760
+ dynamic_sampling_rate_after_sample(&state->cpu_dynamic_sampling_rate, wall_time_ns_after_sample, sampling_time_ns);
761
+
762
+ // Return a dummy VALUE because we're called from rb_rescue2 which requires it
763
+ return Qnil;
764
+ }
765
+
766
+ static VALUE handle_sampling_failure(VALUE self_instance, VALUE exception) {
767
+ stop(self_instance, exception);
768
+ return Qnil;
769
+ }
770
+
771
+ // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
772
+ // It SHOULD NOT be used for other purposes.
773
+ static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self) {
774
+ struct sigaction existing_signal_handler_config = {.sa_sigaction = NULL};
775
+ if (sigaction(SIGPROF, NULL, &existing_signal_handler_config) != 0) {
776
+ rb_sys_fail("Failed to probe existing handler");
777
+ }
778
+
779
+ if (existing_signal_handler_config.sa_sigaction == handle_sampling_signal) {
780
+ return ID2SYM(rb_intern("profiling"));
781
+ } else if (existing_signal_handler_config.sa_sigaction == empty_signal_handler) {
782
+ return ID2SYM(rb_intern("empty"));
783
+ } else if (existing_signal_handler_config.sa_sigaction != NULL) {
784
+ return ID2SYM(rb_intern("other"));
785
+ } else {
786
+ return Qnil;
787
+ }
788
+ }
789
+
790
+ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
791
+ cpu_and_wall_time_worker_state *state;
792
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
793
+
794
+ // Final preparations: Setup signal handler and enable tracepoints. We run these here and not in `_native_sampling_loop`
795
+ // because they may raise exceptions.
796
+ install_sigprof_signal_handler(handle_sampling_signal, "handle_sampling_signal");
797
+ if (state->gc_profiling_enabled) rb_tracepoint_enable(state->gc_tracepoint);
798
+ if (state->allocation_profiling_enabled) {
799
+ rb_add_event_hook2(
800
+ on_newobj_event_as_hook,
801
+ RUBY_INTERNAL_EVENT_NEWOBJ,
802
+ state->self_instance,
803
+ RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG)
804
+ ;
805
+ }
806
+
807
+ if (state->gvl_profiling_enabled) {
808
+ #ifndef NO_GVL_INSTRUMENTATION
809
+ #ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS
810
+ gvl_profiling_state_thread_tracking_workaround();
811
+ #endif
812
+
813
+ state->gvl_profiling_hook = rb_internal_thread_add_event_hook(
814
+ on_gvl_event,
815
+ (
816
+ // For now we're only asking for these events, even though there's more
817
+ // (e.g. check docs or gvl-tracing gem)
818
+ RUBY_INTERNAL_THREAD_EVENT_READY /* waiting for gvl */ |
819
+ RUBY_INTERNAL_THREAD_EVENT_RESUMED /* running/runnable */
820
+ ),
821
+ NULL
822
+ );
823
+ #else
824
+ rb_raise(rb_eArgError, "GVL profiling is not supported in this Ruby version");
825
+ #endif
826
+ }
827
+
828
+ // Flag the profiler as running before we release the GVL, in case anyone's waiting to know about it
829
+ rb_funcall(instance, rb_intern("signal_running"), 0);
830
+
831
+ rb_thread_call_without_gvl(run_sampling_trigger_loop, state, interrupt_sampling_trigger_loop, state);
832
+
833
+ // If we stopped sampling due to an exception, re-raise it (now in the worker thread)
834
+ if (state->failure_exception != Qnil) rb_exc_raise(state->failure_exception);
835
+
836
+ return Qnil;
837
+ }
838
+
839
+ // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
840
+ // It SHOULD NOT be used for other purposes.
841
+ static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance) {
842
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
843
+
844
+ return (state != NULL && is_thread_alive(state->owner_thread) && state->self_instance == instance) ? Qtrue : Qfalse;
845
+ }
846
+
847
+ static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext) {
848
+ /* Does nothing on purpose */
849
+ }
850
+
851
+ // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
852
+ // It SHOULD NOT be used for other purposes.
853
+ static VALUE _native_install_testing_signal_handler(DDTRACE_UNUSED VALUE self) {
854
+ install_sigprof_signal_handler(testing_signal_handler, "testing_signal_handler");
855
+ return Qtrue;
856
+ }
857
+
858
+ // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
859
+ // It SHOULD NOT be used for other purposes.
860
+ static VALUE _native_remove_testing_signal_handler(DDTRACE_UNUSED VALUE self) {
861
+ remove_sigprof_signal_handler();
862
+ return Qtrue;
863
+ }
864
+
865
+ // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
866
+ // It SHOULD NOT be used for other purposes.
867
+ static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self) {
868
+ sample_from_postponed_job(NULL);
869
+ return Qtrue;
870
+ }
871
+
872
+ // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
873
+ // It SHOULD NOT be used for other purposes.
874
+ static VALUE _native_gc_tracepoint(DDTRACE_UNUSED VALUE self, VALUE instance) {
875
+ cpu_and_wall_time_worker_state *state;
876
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
877
+
878
+ return state->gc_tracepoint;
879
+ }
880
+
881
+ // Implements tracking of cpu-time and wall-time spent doing GC. This function is called by Ruby from the `gc_tracepoint`
882
+ // when the RUBY_INTERNAL_EVENT_GC_ENTER and RUBY_INTERNAL_EVENT_GC_EXIT events are triggered.
883
+ //
884
+ // See the comments on
885
+ // * thread_context_collector_on_gc_start
886
+ // * thread_context_collector_on_gc_finish
887
+ // * thread_context_collector_sample_after_gc
888
+ //
889
+ // For the expected times in which to call them, and their assumptions.
890
+ //
891
+ // Safety: This function gets called while Ruby is doing garbage collection. While Ruby is doing garbage collection,
892
+ // *NO ALLOCATION* is allowed. This function, and any it calls must never trigger memory or object allocation.
893
+ // This includes exceptions and use of ruby_xcalloc (because xcalloc can trigger GC)!
894
+ static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused) {
895
+ if (!ddtrace_rb_ractor_main_p()) {
896
+ return; // We're not on the main Ractor; we currently don't support profiling non-main Ractors
897
+ }
898
+
899
+ int event = rb_tracearg_event_flag(rb_tracearg_from_tracepoint(tracepoint_data));
900
+ if (event != RUBY_INTERNAL_EVENT_GC_ENTER && event != RUBY_INTERNAL_EVENT_GC_EXIT) return; // Unknown event
901
+
902
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
903
+
904
+ // This should not happen in a normal situation because the tracepoint is always enabled after the instance is set
905
+ // and disabled before it is cleared, but just in case...
906
+ if (state == NULL) return;
907
+
908
+ if (event == RUBY_INTERNAL_EVENT_GC_ENTER) {
909
+ thread_context_collector_on_gc_start(state->thread_context_collector_instance);
910
+ } else if (event == RUBY_INTERNAL_EVENT_GC_EXIT) {
911
+ bool should_flush = thread_context_collector_on_gc_finish(state->thread_context_collector_instance);
912
+
913
+ // We use rb_postponed_job_register_one to ask Ruby to run thread_context_collector_sample_after_gc when the
914
+ // thread collector flags it's time to flush.
915
+ if (should_flush) {
916
+ #ifndef NO_POSTPONED_TRIGGER // Ruby 3.3+
917
+ rb_postponed_job_trigger(after_gc_from_postponed_job_handle);
918
+ #else
919
+ rb_postponed_job_register_one(0, after_gc_from_postponed_job, NULL);
920
+ #endif
921
+ }
922
+ }
923
+ }
924
+
925
+ static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused) {
926
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
927
+
928
+ // This can potentially happen if the CpuAndWallTimeWorker was stopped while the postponed job was waiting to be executed; nothing to do
929
+ if (state == NULL) return;
930
+
931
+ // @ivoanjo: I'm not sure this can ever happen because `on_gc_event` only enqueues this callback if
932
+ // it's running on the main Ractor, but just in case...
933
+ if (!ddtrace_rb_ractor_main_p()) {
934
+ return; // We're not on the main Ractor; we currently don't support profiling non-main Ractors
935
+ }
936
+
937
+ during_sample_enter(state);
938
+
939
+ safely_call(thread_context_collector_sample_after_gc, state->thread_context_collector_instance, state->self_instance);
940
+
941
+ during_sample_exit(state);
942
+ }
943
+
944
+ // Equivalent to Ruby begin/rescue call, where we call a C function and jump to the exception handler if an
945
+ // exception gets raised within
946
+ static VALUE safely_call(VALUE (*function_to_call_safely)(VALUE), VALUE function_to_call_safely_arg, VALUE instance) {
947
+ VALUE exception_handler_function_arg = instance;
948
+ return rb_rescue2(
949
+ function_to_call_safely,
950
+ function_to_call_safely_arg,
951
+ handle_sampling_failure,
952
+ exception_handler_function_arg,
953
+ rb_eException, // rb_eException is the base class of all Ruby exceptions
954
+ 0 // Required by API to be the last argument
955
+ );
956
+ }
957
+
958
+ // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
959
+ // It SHOULD NOT be used for other purposes.
960
+ static VALUE _native_simulate_handle_sampling_signal(DDTRACE_UNUSED VALUE self) {
961
+ handle_sampling_signal(0, NULL, NULL);
962
+ return Qtrue;
963
+ }
964
+
965
+ // This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
966
+ // It SHOULD NOT be used for other purposes.
967
+ static VALUE _native_simulate_sample_from_postponed_job(DDTRACE_UNUSED VALUE self) {
968
+ sample_from_postponed_job(NULL);
969
+ return Qtrue;
970
+ }
971
+
972
+ // After the Ruby VM forks, this method gets called in the child process to clean up any leftover state from the parent.
973
+ //
974
+ // Assumption: This method gets called BEFORE restarting profiling. Note that profiling-related tracepoints may still
975
+ // be active, so we make sure to disable them before calling into anything else, so that there are no components
976
+ // attempting to trigger samples at the same time as the reset is done.
977
+ //
978
+ // In the future, if we add more other components with tracepoints, we will need to coordinate stopping all such
979
+ // tracepoints before doing the other cleaning steps.
980
+ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance) {
981
+ cpu_and_wall_time_worker_state *state;
982
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
983
+
984
+ // Disable all tracepoints, so that there are no more attempts to mutate the profile
985
+ disable_tracepoints(state);
986
+
987
+ reset_stats_not_thread_safe(state);
988
+
989
+ // Remove all state from the `Collectors::ThreadState` and connected downstream components
990
+ rb_funcall(state->thread_context_collector_instance, rb_intern("reset_after_fork"), 0);
991
+
992
+ return Qtrue;
993
+ }
994
+
995
+ static VALUE _native_is_sigprof_blocked_in_current_thread(DDTRACE_UNUSED VALUE self) {
996
+ return is_sigprof_blocked_in_current_thread();
997
+ }
998
+
999
+ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
1000
+ cpu_and_wall_time_worker_state *state;
1001
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1002
+
1003
+ unsigned long total_cpu_samples_attempted = state->stats.cpu_sampled + state->stats.cpu_skipped;
1004
+ VALUE effective_cpu_sample_rate =
1005
+ total_cpu_samples_attempted == 0 ? Qnil : DBL2NUM(((double) state->stats.cpu_sampled) / total_cpu_samples_attempted);
1006
+ unsigned long total_allocation_samples_attempted = state->stats.allocation_sampled + state->stats.allocation_skipped;
1007
+ VALUE effective_allocation_sample_rate =
1008
+ total_allocation_samples_attempted == 0 ? Qnil : DBL2NUM(((double) state->stats.allocation_sampled) / total_allocation_samples_attempted);
1009
+
1010
+ VALUE allocation_sampler_snapshot = state->allocation_profiling_enabled && state->dynamic_sampling_rate_enabled ?
1011
+ discrete_dynamic_sampler_state_snapshot(&state->allocation_sampler) : Qnil;
1012
+
1013
+ VALUE stats_as_hash = rb_hash_new();
1014
+ VALUE arguments[] = {
1015
+ ID2SYM(rb_intern("trigger_sample_attempts")), /* => */ UINT2NUM(state->stats.trigger_sample_attempts),
1016
+ ID2SYM(rb_intern("trigger_simulated_signal_delivery_attempts")), /* => */ UINT2NUM(state->stats.trigger_simulated_signal_delivery_attempts),
1017
+ ID2SYM(rb_intern("simulated_signal_delivery")), /* => */ UINT2NUM(state->stats.simulated_signal_delivery),
1018
+ ID2SYM(rb_intern("signal_handler_enqueued_sample")), /* => */ UINT2NUM(state->stats.signal_handler_enqueued_sample),
1019
+ ID2SYM(rb_intern("signal_handler_prepared_sample")), /* => */ UINT2NUM(state->stats.signal_handler_prepared_sample),
1020
+ ID2SYM(rb_intern("signal_handler_wrong_thread")), /* => */ UINT2NUM(state->stats.signal_handler_wrong_thread),
1021
+ ID2SYM(rb_intern("interrupt_thread_attempts")), /* => */ UINT2NUM(state->stats.interrupt_thread_attempts),
1022
+
1023
+ // CPU Stats
1024
+ ID2SYM(rb_intern("cpu_sampled")), /* => */ UINT2NUM(state->stats.cpu_sampled),
1025
+ ID2SYM(rb_intern("cpu_skipped")), /* => */ UINT2NUM(state->stats.cpu_skipped),
1026
+ ID2SYM(rb_intern("cpu_effective_sample_rate")), /* => */ effective_cpu_sample_rate,
1027
+ ID2SYM(rb_intern("cpu_sampling_time_ns_min")), /* => */ RUBY_NUM_OR_NIL(state->stats.cpu_sampling_time_ns_min, != UINT64_MAX, ULL2NUM),
1028
+ ID2SYM(rb_intern("cpu_sampling_time_ns_max")), /* => */ RUBY_NUM_OR_NIL(state->stats.cpu_sampling_time_ns_max, > 0, ULL2NUM),
1029
+ ID2SYM(rb_intern("cpu_sampling_time_ns_total")), /* => */ RUBY_NUM_OR_NIL(state->stats.cpu_sampling_time_ns_total, > 0, ULL2NUM),
1030
+ ID2SYM(rb_intern("cpu_sampling_time_ns_avg")), /* => */ RUBY_AVG_OR_NIL(state->stats.cpu_sampling_time_ns_total, state->stats.cpu_sampled),
1031
+
1032
+ // Allocation stats
1033
+ ID2SYM(rb_intern("allocation_sampled")), /* => */ state->allocation_profiling_enabled ? ULONG2NUM(state->stats.allocation_sampled) : Qnil,
1034
+ ID2SYM(rb_intern("allocation_skipped")), /* => */ state->allocation_profiling_enabled ? ULONG2NUM(state->stats.allocation_skipped) : Qnil,
1035
+ ID2SYM(rb_intern("allocation_effective_sample_rate")), /* => */ effective_allocation_sample_rate,
1036
+ ID2SYM(rb_intern("allocation_sampling_time_ns_min")), /* => */ RUBY_NUM_OR_NIL(state->stats.allocation_sampling_time_ns_min, != UINT64_MAX, ULL2NUM),
1037
+ ID2SYM(rb_intern("allocation_sampling_time_ns_max")), /* => */ RUBY_NUM_OR_NIL(state->stats.allocation_sampling_time_ns_max, > 0, ULL2NUM),
1038
+ ID2SYM(rb_intern("allocation_sampling_time_ns_total")), /* => */ RUBY_NUM_OR_NIL(state->stats.allocation_sampling_time_ns_total, > 0, ULL2NUM),
1039
+ ID2SYM(rb_intern("allocation_sampling_time_ns_avg")), /* => */ RUBY_AVG_OR_NIL(state->stats.allocation_sampling_time_ns_total, state->stats.allocation_sampled),
1040
+ ID2SYM(rb_intern("allocation_sampler_snapshot")), /* => */ allocation_sampler_snapshot,
1041
+ ID2SYM(rb_intern("allocations_during_sample")), /* => */ state->allocation_profiling_enabled ? UINT2NUM(state->stats.allocations_during_sample) : Qnil,
1042
+
1043
+ // GVL profiling stats
1044
+ ID2SYM(rb_intern("after_gvl_running")), /* => */ UINT2NUM(state->stats.after_gvl_running),
1045
+ ID2SYM(rb_intern("gvl_dont_sample")), /* => */ UINT2NUM(state->stats.gvl_dont_sample),
1046
+ ID2SYM(rb_intern("gvl_sampling_time_ns_min")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_min, != UINT64_MAX, ULL2NUM),
1047
+ ID2SYM(rb_intern("gvl_sampling_time_ns_max")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_max, > 0, ULL2NUM),
1048
+ ID2SYM(rb_intern("gvl_sampling_time_ns_total")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_total, > 0, ULL2NUM),
1049
+ ID2SYM(rb_intern("gvl_sampling_time_ns_avg")), /* => */ RUBY_AVG_OR_NIL(state->stats.gvl_sampling_time_ns_total, state->stats.after_gvl_running),
1050
+ };
1051
+ for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(stats_as_hash, arguments[i], arguments[i+1]);
1052
+ return stats_as_hash;
1053
+ }
1054
+
1055
+ static VALUE _native_stats_reset_not_thread_safe(DDTRACE_UNUSED VALUE self, VALUE instance) {
1056
+ cpu_and_wall_time_worker_state *state;
1057
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1058
+ reset_stats_not_thread_safe(state);
1059
+ return Qnil;
1060
+ }
1061
+
1062
+ void *simulate_sampling_signal_delivery(DDTRACE_UNUSED void *_unused) {
1063
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1064
+
1065
+ // This can potentially happen if the CpuAndWallTimeWorker was stopped while the IdleSamplingHelper was trying to execute this action
1066
+ if (state == NULL) return NULL;
1067
+
1068
+ state->stats.simulated_signal_delivery++;
1069
+
1070
+ // `handle_sampling_signal` does a few things extra on top of `sample_from_postponed_job` so that's why we don't shortcut here
1071
+ handle_sampling_signal(0, NULL, NULL);
1072
+
1073
+ return NULL; // Unused
1074
+ }
1075
+
1076
+ static void grab_gvl_and_sample(void) { rb_thread_call_with_gvl(simulate_sampling_signal_delivery, NULL); }
1077
+
1078
+ static void reset_stats_not_thread_safe(cpu_and_wall_time_worker_state *state) {
1079
+ // NOTE: This is not really thread safe so ongoing sampling operations that are concurrent with a reset can have their stats:
1080
+ // * Lost (writes after stats retrieval but before reset).
1081
+ // * Included in the previous stats window (writes before stats retrieval and reset).
1082
+ // * Included in the following stats window (writes after stats retrieval and reset).
1083
+ // Given the expected infrequency of resetting (~once per 60s profile) and the auxiliary/non-critical nature of these stats
1084
+ // this momentary loss of accuracy is deemed acceptable to keep overhead to a minimum.
1085
+ state->stats = (struct stats) {
1086
+ // All these values are initialized to their highest value possible since we always take the min between existing and latest sample
1087
+ .cpu_sampling_time_ns_min = UINT64_MAX,
1088
+ .allocation_sampling_time_ns_min = UINT64_MAX,
1089
+ .gvl_sampling_time_ns_min = UINT64_MAX,
1090
+ };
1091
+ }
1092
+
1093
+ static void sleep_for(uint64_t time_ns) {
1094
+ // As a simplification, we currently only support setting .tv_nsec
1095
+ if (time_ns >= SECONDS_AS_NS(1)) {
1096
+ grab_gvl_and_raise(rb_eArgError, "sleep_for can only sleep for less than 1 second, time_ns: %"PRIu64, time_ns);
1097
+ }
1098
+
1099
+ struct timespec time_to_sleep = {.tv_nsec = time_ns};
1100
+
1101
+ while (nanosleep(&time_to_sleep, &time_to_sleep) != 0) {
1102
+ if (errno == EINTR) {
1103
+ // We were interrupted. nanosleep updates "time_to_sleep" to contain only the remaining time, so we just let the
1104
+ // loop keep going.
1105
+ } else {
1106
+ ENFORCE_SUCCESS_NO_GVL(errno);
1107
+ }
1108
+ }
1109
+ }
1110
+
1111
+ static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
1112
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state;
1113
+
1114
+ bool are_allocations_being_tracked = state != NULL && state->allocation_profiling_enabled && state->allocation_counting_enabled;
1115
+
1116
+ return are_allocations_being_tracked ? ULL2NUM(allocation_count) : Qnil;
1117
+ }
1118
+
1119
+ #define HANDLE_CLOCK_FAILURE(call) ({ \
1120
+ long _result = (call); \
1121
+ if (_result == 0) { \
1122
+ delayed_error(state, ERR_CLOCK_FAIL); \
1123
+ return; \
1124
+ } \
1125
+ _result; \
1126
+ })
1127
+
1128
+ // Implements memory-related profiling events. This function is called by Ruby via the `rb_add_event_hook2`
1129
+ // when the RUBY_INTERNAL_EVENT_NEWOBJ event is triggered.
1130
+ //
1131
+ // When allocation sampling is enabled, this function gets called for almost all* objects allocated by the Ruby VM.
1132
+ // (*In some weird cases the VM may skip this tracepoint.)
1133
+ //
1134
+ // At a high level, there's two paths through this function:
1135
+ // 1. should_sample == false -> return
1136
+ // 2. should_sample == true -> sample
1137
+ //
1138
+ // On big applications, path 1. is the hottest, since we don't sample every object. So it's quite important for it to
1139
+ // be as fast as possible.
1140
+ //
1141
+ // NOTE: You may be wondering why we don't use any of the arguments to this function. It turns out it's possible to just
1142
+ // call `rb_tracearg_from_tracepoint(anything)` anywhere during this function or its callees to get the data, so that's
1143
+ // why it's not being passed as an argument.
1144
+ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *unused2) {
1145
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1146
+
1147
+ // This should not happen in a normal situation because the tracepoint is always enabled after the instance is set
1148
+ // and disabled before it is cleared, but just in case...
1149
+ if (state == NULL) return;
1150
+
1151
+ if (RB_UNLIKELY(state->allocation_counting_enabled)) {
1152
+ // Update thread-local allocation count
1153
+ if (RB_UNLIKELY(allocation_count == UINT64_MAX)) {
1154
+ allocation_count = 0;
1155
+ } else {
1156
+ allocation_count++;
1157
+ }
1158
+ }
1159
+
1160
+ // In rare cases, we may actually be allocating an object as part of profiler sampling. We don't want to recursively
1161
+ // sample, so we just return early
1162
+ if (state->during_sample) {
1163
+ state->stats.allocations_during_sample++;
1164
+ return;
1165
+ }
1166
+
1167
+ // If Ruby is in the middle of raising an exception, we don't want to try to sample. This is because if we accidentally
1168
+ // trigger an exception inside the profiler code, bad things will happen (specifically, Ruby will try to kill off the
1169
+ // thread even though we may try to catch the exception).
1170
+ //
1171
+ // Note that "in the middle of raising an exception" means the exception itself has already been allocated.
1172
+ // What's getting allocated now is probably the backtrace objects (@ivoanjo or at least that's what I've observed)
1173
+ if (is_raised_flag_set(rb_thread_current())) {
1174
+ return;
1175
+ }
1176
+
1177
+ // Hot path: Dynamic sampling rate is usually enabled and the sampling decision is usually false
1178
+ if (RB_LIKELY(state->dynamic_sampling_rate_enabled && !discrete_dynamic_sampler_should_sample(&state->allocation_sampler))) {
1179
+ state->stats.allocation_skipped++;
1180
+
1181
+ coarse_instant now = monotonic_coarse_wall_time_now_ns();
1182
+ HANDLE_CLOCK_FAILURE(now.timestamp_ns);
1183
+
1184
+ bool needs_readjust = discrete_dynamic_sampler_skipped_sample(&state->allocation_sampler, now);
1185
+ if (RB_UNLIKELY(needs_readjust)) {
1186
+ // We rarely readjust, so this is a cold path
1187
+ // Also, while above we used the cheaper monotonic_coarse, for this call we want the regular monotonic call,
1188
+ // which is why we end up getting time "again".
1189
+ discrete_dynamic_sampler_readjust(
1190
+ &state->allocation_sampler, HANDLE_CLOCK_FAILURE(monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE))
1191
+ );
1192
+ }
1193
+
1194
+ return;
1195
+ }
1196
+
1197
+ // From here on, we've decided to go ahead with the sample, which is way less common than skipping it
1198
+
1199
+ discrete_dynamic_sampler_before_sample(
1200
+ &state->allocation_sampler, HANDLE_CLOCK_FAILURE(monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE))
1201
+ );
1202
+
1203
+ during_sample_enter(state);
1204
+
1205
+ // Rescue against any exceptions that happen during sampling
1206
+ safely_call(rescued_sample_allocation, Qnil, state->self_instance);
1207
+
1208
+ if (state->dynamic_sampling_rate_enabled) {
1209
+ long now = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
1210
+ if (now == 0) {
1211
+ delayed_error(state, ERR_CLOCK_FAIL);
1212
+ // NOTE: Not short-circuiting here to make sure cleanup happens
1213
+ }
1214
+ uint64_t sampling_time_ns = discrete_dynamic_sampler_after_sample(&state->allocation_sampler, now);
1215
+ // NOTE: To keep things lean when dynamic sampling rate is disabled we skip clock interactions which is
1216
+ // why we're fine with having this inside this conditional.
1217
+ state->stats.allocation_sampling_time_ns_min = uint64_min_of(sampling_time_ns, state->stats.allocation_sampling_time_ns_min);
1218
+ state->stats.allocation_sampling_time_ns_max = uint64_max_of(sampling_time_ns, state->stats.allocation_sampling_time_ns_max);
1219
+ state->stats.allocation_sampling_time_ns_total += sampling_time_ns;
1220
+ }
1221
+
1222
+ state->stats.allocation_sampled++;
1223
+
1224
+ during_sample_exit(state);
1225
+ }
1226
+
1227
+ static void disable_tracepoints(cpu_and_wall_time_worker_state *state) {
1228
+ if (state->gc_tracepoint != Qnil) {
1229
+ rb_tracepoint_disable(state->gc_tracepoint);
1230
+ }
1231
+
1232
+ rb_remove_event_hook_with_data(on_newobj_event_as_hook, state->self_instance);
1233
+
1234
+ #ifndef NO_GVL_INSTRUMENTATION
1235
+ if (state->gvl_profiling_hook) {
1236
+ rb_internal_thread_remove_event_hook(state->gvl_profiling_hook);
1237
+ state->gvl_profiling_hook = NULL;
1238
+ }
1239
+ #endif
1240
+ }
1241
+
1242
+ static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self) {
1243
+ block_sigprof_signal_handler_from_running_in_current_thread();
1244
+ int exception_state;
1245
+ VALUE result = rb_protect(rb_yield, Qundef, &exception_state);
1246
+ unblock_sigprof_signal_handler_from_running_in_current_thread();
1247
+
1248
+ if (exception_state) {
1249
+ rb_jump_tag(exception_state);
1250
+ } else {
1251
+ return result;
1252
+ }
1253
+ }
1254
+
1255
+ static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {
1256
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1257
+
1258
+ // This should not happen in a normal situation because on_newobj_event already checked for this, but just in case...
1259
+ if (state == NULL) return Qnil;
1260
+
1261
+ // If we're getting called from inside a tracepoint/event hook, Ruby exposes the data using this function.
1262
+ rb_trace_arg_t *data = rb_tracearg_from_tracepoint(Qnil);
1263
+ VALUE new_object = rb_tracearg_object(data);
1264
+
1265
+ unsigned long allocations_since_last_sample = state->dynamic_sampling_rate_enabled ?
1266
+ // if we're doing dynamic sampling, ask the sampler how many events since last sample
1267
+ discrete_dynamic_sampler_events_since_last_sample(&state->allocation_sampler) :
1268
+ // if we aren't, then we're sampling every event
1269
+ 1;
1270
+
1271
+ // To control bias from sampling, we clamp the maximum weight attributed to a single allocation sample. This avoids
1272
+ // assigning a very large number to a sample, if for instance the dynamic sampling mechanism chose a really big interval.
1273
+ unsigned int weight = allocations_since_last_sample > MAX_ALLOC_WEIGHT ? MAX_ALLOC_WEIGHT : (unsigned int) allocations_since_last_sample;
1274
+ thread_context_collector_sample_allocation(state->thread_context_collector_instance, weight, new_object);
1275
+ // ...but we still represent the skipped samples in the profile, thus the data will account for all allocations.
1276
+ if (weight < allocations_since_last_sample) {
1277
+ uint32_t skipped_samples = (uint32_t) uint64_min_of(allocations_since_last_sample - weight, UINT32_MAX);
1278
+ thread_context_collector_sample_skipped_allocation_samples(state->thread_context_collector_instance, skipped_samples);
1279
+ }
1280
+
1281
+ // Return a dummy VALUE because we're called from rb_rescue2 which requires it
1282
+ return Qnil;
1283
+ }
1284
+
1285
+ static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error) {
1286
+ // If we can't raise an immediate exception at the calling site, use the asynchronous flow through the main worker loop.
1287
+ stop_state(state, rb_exc_new_cstr(rb_eRuntimeError, error));
1288
+ }
1289
+
1290
+ static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg) {
1291
+ ENFORCE_TYPE(error_msg, T_STRING);
1292
+
1293
+ cpu_and_wall_time_worker_state *state;
1294
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1295
+
1296
+ delayed_error(state, rb_string_value_cstr(&error_msg));
1297
+
1298
+ return Qnil;
1299
+ }
1300
+
1301
+ // Masks SIGPROF interruptions for the current thread. Please don't use this -- you may end up with incomplete
1302
+ // profiling data.
1303
+ static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self) {
1304
+ block_sigprof_signal_handler_from_running_in_current_thread();
1305
+ return Qtrue;
1306
+ }
1307
+
1308
+ // Unmasks SIGPROF interruptions for the current thread. If there's a pending sample, it'll be triggered inside this
1309
+ // method.
1310
+ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
1311
+ unblock_sigprof_signal_handler_from_running_in_current_thread();
1312
+ return Qtrue;
1313
+ }
1314
+
1315
+ #ifndef NO_GVL_INSTRUMENTATION
1316
+ static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused) {
1317
+ // Be very careful about touching the `state` here or doing anything at all:
1318
+ // This function gets called without the GVL, and potentially from background Ractors!
1319
+ //
1320
+ // In fact, the `target_thread` that this event is about may not even be the current thread. (So be careful with thread locals that
1321
+ // are not directly tied to the `target_thread` object and the like)
1322
+ gvl_profiling_thread target_thread = thread_from_event(event_data);
1323
+
1324
+ if (event_id == RUBY_INTERNAL_THREAD_EVENT_READY) { /* waiting for gvl */
1325
+ thread_context_collector_on_gvl_waiting(target_thread);
1326
+ } else if (event_id == RUBY_INTERNAL_THREAD_EVENT_RESUMED) { /* running/runnable */
1327
+ // Interesting note: A RUBY_INTERNAL_THREAD_EVENT_RESUMED is guaranteed to be called with the GVL being acquired.
1328
+ // (And... I think target_thread will be == rb_thread_current()?)
1329
+ //
1330
+ // But we're not sure if we're on the main Ractor yet. The thread context collector actually can actually help here:
1331
+ // it tags threads it's tracking, so if a thread is tagged then by definition we know that thread belongs to the main
1332
+ // Ractor. Thus, if we get a ON_GVL_RUNNING_UNKNOWN result we shouldn't touch any state, but otherwise we're good to go.
1333
+
1334
+ #ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS
1335
+ target_thread = gvl_profiling_state_maybe_initialize();
1336
+ #endif
1337
+
1338
+ on_gvl_running_result result = thread_context_collector_on_gvl_running(target_thread);
1339
+
1340
+ if (result == ON_GVL_RUNNING_SAMPLE) {
1341
+ #ifndef NO_POSTPONED_TRIGGER
1342
+ rb_postponed_job_trigger(after_gvl_running_from_postponed_job_handle);
1343
+ #else
1344
+ rb_postponed_job_register_one(0, after_gvl_running_from_postponed_job, NULL);
1345
+ #endif
1346
+ } else if (result == ON_GVL_RUNNING_DONT_SAMPLE) {
1347
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1348
+
1349
+ if (state == NULL) return; // This should not happen, but just in case...
1350
+
1351
+ state->stats.gvl_dont_sample++;
1352
+ }
1353
+ } else {
1354
+ // This is a very delicate time and it's hard for us to raise an exception so let's at least complain to stderr
1355
+ fprintf(stderr, "[ddtrace] Unexpected value in on_gvl_event (%d)\n", event_id);
1356
+ }
1357
+ }
1358
+
1359
+ static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused) {
1360
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1361
+
1362
+ // This can potentially happen if the CpuAndWallTimeWorker was stopped while the postponed job was waiting to be executed; nothing to do
1363
+ if (state == NULL) return;
1364
+
1365
+ during_sample_enter(state);
1366
+
1367
+ // Rescue against any exceptions that happen during sampling
1368
+ safely_call(rescued_after_gvl_running_from_postponed_job, state->self_instance, state->self_instance);
1369
+
1370
+ during_sample_exit(state);
1371
+ }
1372
+
1373
+ static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance) {
1374
+ cpu_and_wall_time_worker_state *state;
1375
+ TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1376
+
1377
+ long wall_time_ns_before_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
1378
+ thread_context_collector_sample_after_gvl_running(state->thread_context_collector_instance, rb_thread_current(), wall_time_ns_before_sample);
1379
+ long wall_time_ns_after_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
1380
+
1381
+ long delta_ns = wall_time_ns_after_sample - wall_time_ns_before_sample;
1382
+
1383
+ // Guard against wall-time going backwards, see https://github.com/DataDog/dd-trace-rb/pull/2336 for discussion.
1384
+ uint64_t sampling_time_ns = delta_ns < 0 ? 0 : delta_ns;
1385
+
1386
+ state->stats.gvl_sampling_time_ns_min = uint64_min_of(sampling_time_ns, state->stats.gvl_sampling_time_ns_min);
1387
+ state->stats.gvl_sampling_time_ns_max = uint64_max_of(sampling_time_ns, state->stats.gvl_sampling_time_ns_max);
1388
+ state->stats.gvl_sampling_time_ns_total += sampling_time_ns;
1389
+
1390
+ state->stats.after_gvl_running++;
1391
+
1392
+ return Qnil;
1393
+ }
1394
+
1395
+ static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance) {
1396
+ cpu_and_wall_time_worker_state *state;
1397
+ TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
1398
+
1399
+ return state->gvl_profiling_hook != NULL ? Qtrue : Qfalse;
1400
+ }
1401
+ #else
1402
+ static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, DDTRACE_UNUSED VALUE instance) {
1403
+ return Qfalse;
1404
+ }
1405
+ #endif
1406
+
1407
+ static inline void during_sample_enter(cpu_and_wall_time_worker_state* state) {
1408
+ // Tell the compiler it's not allowed to reorder the `during_sample` flag with anything that happens after.
1409
+ //
1410
+ // In a few cases, we may be checking this flag from a signal handler, so we need to make sure the compiler didn't
1411
+ // get clever and reordered things in such a way that makes us miss the flag update.
1412
+ //
1413
+ // See https://github.com/ruby/ruby/pull/11036 for a similar change made to the Ruby VM with more context.
1414
+ state->during_sample = true;
1415
+ atomic_signal_fence(memory_order_seq_cst);
1416
+ }
1417
+
1418
+ static inline void during_sample_exit(cpu_and_wall_time_worker_state* state) {
1419
+ // See `during_sample_enter` for more context; in this case we set the fence before to make sure anything that
1420
+ // happens before the fence is not reordered with the flag update.
1421
+ atomic_signal_fence(memory_order_seq_cst);
1422
+ state->during_sample = false;
1423
+ }