ddtrace 1.4.2 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (870) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +986 -2
  3. data/LICENSE-3rdparty.csv +2 -0
  4. data/README.md +10 -12
  5. data/ext/ddtrace_profiling_loader/ddtrace_profiling_loader.c +9 -2
  6. data/ext/ddtrace_profiling_loader/extconf.rb +21 -3
  7. data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +48 -12
  8. data/ext/ddtrace_profiling_native_extension/clock_id.h +1 -3
  9. data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +6 -23
  10. data/ext/ddtrace_profiling_native_extension/clock_id_noop.c +0 -1
  11. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +719 -129
  12. data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.c +150 -0
  13. data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.h +18 -0
  14. data/ext/ddtrace_profiling_native_extension/collectors_gc_profiling_helper.c +156 -0
  15. data/ext/ddtrace_profiling_native_extension/collectors_gc_profiling_helper.h +5 -0
  16. data/ext/ddtrace_profiling_native_extension/collectors_idle_sampling_helper.c +244 -0
  17. data/ext/ddtrace_profiling_native_extension/collectors_idle_sampling_helper.h +3 -0
  18. data/ext/ddtrace_profiling_native_extension/collectors_stack.c +184 -94
  19. data/ext/ddtrace_profiling_native_extension/collectors_stack.h +20 -2
  20. data/ext/ddtrace_profiling_native_extension/collectors_thread_context.c +1301 -0
  21. data/ext/ddtrace_profiling_native_extension/collectors_thread_context.h +15 -0
  22. data/ext/ddtrace_profiling_native_extension/extconf.rb +103 -28
  23. data/ext/ddtrace_profiling_native_extension/heap_recorder.c +970 -0
  24. data/ext/ddtrace_profiling_native_extension/heap_recorder.h +155 -0
  25. data/ext/ddtrace_profiling_native_extension/helpers.h +7 -0
  26. data/ext/ddtrace_profiling_native_extension/http_transport.c +133 -88
  27. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.c +62 -0
  28. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +32 -4
  29. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +55 -26
  30. data/ext/ddtrace_profiling_native_extension/pid_controller.c +57 -0
  31. data/ext/ddtrace_profiling_native_extension/pid_controller.h +45 -0
  32. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +317 -131
  33. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +25 -1
  34. data/ext/ddtrace_profiling_native_extension/profiling.c +219 -4
  35. data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +233 -1
  36. data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +60 -8
  37. data/ext/ddtrace_profiling_native_extension/setup_signal_handler.c +115 -0
  38. data/ext/ddtrace_profiling_native_extension/setup_signal_handler.h +11 -0
  39. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +585 -66
  40. data/ext/ddtrace_profiling_native_extension/stack_recorder.h +19 -30
  41. data/ext/ddtrace_profiling_native_extension/time_helpers.c +53 -0
  42. data/ext/ddtrace_profiling_native_extension/time_helpers.h +24 -0
  43. data/lib/datadog/appsec/assets/blocked.html +98 -3
  44. data/lib/datadog/appsec/assets/blocked.json +1 -0
  45. data/lib/datadog/appsec/assets/blocked.text +5 -0
  46. data/lib/datadog/appsec/assets/waf_rules/processors.json +92 -0
  47. data/lib/datadog/appsec/assets/waf_rules/recommended.json +2861 -796
  48. data/lib/datadog/appsec/assets/waf_rules/scanners.json +114 -0
  49. data/lib/datadog/appsec/assets/waf_rules/strict.json +459 -122
  50. data/lib/datadog/appsec/assets.rb +10 -4
  51. data/lib/datadog/appsec/autoload.rb +4 -11
  52. data/lib/datadog/appsec/component.rb +94 -0
  53. data/lib/datadog/appsec/configuration/settings.rb +170 -162
  54. data/lib/datadog/appsec/configuration.rb +1 -70
  55. data/lib/datadog/appsec/contrib/auto_instrument.rb +3 -5
  56. data/lib/datadog/appsec/contrib/devise/event.rb +57 -0
  57. data/lib/datadog/appsec/contrib/devise/ext.rb +13 -0
  58. data/lib/datadog/appsec/contrib/devise/integration.rb +42 -0
  59. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +76 -0
  60. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +54 -0
  61. data/lib/datadog/appsec/contrib/devise/patcher.rb +45 -0
  62. data/lib/datadog/appsec/contrib/devise/resource.rb +35 -0
  63. data/lib/datadog/appsec/contrib/devise/tracking.rb +49 -0
  64. data/lib/datadog/appsec/contrib/integration.rb +1 -1
  65. data/lib/datadog/appsec/contrib/patcher.rb +1 -1
  66. data/lib/datadog/appsec/contrib/rack/ext.rb +1 -3
  67. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +104 -0
  68. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +30 -0
  69. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +112 -121
  70. data/lib/datadog/appsec/contrib/rack/integration.rb +0 -7
  71. data/lib/datadog/appsec/contrib/rack/patcher.rb +3 -1
  72. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +33 -39
  73. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +19 -23
  74. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +25 -23
  75. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +11 -8
  76. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +122 -41
  77. data/lib/datadog/appsec/contrib/rails/ext.rb +2 -4
  78. data/lib/datadog/appsec/contrib/rails/framework.rb +2 -16
  79. data/lib/datadog/appsec/contrib/rails/gateway/request.rb +67 -0
  80. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +40 -52
  81. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -8
  82. data/lib/datadog/appsec/contrib/rails/patcher.rb +23 -20
  83. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +21 -23
  84. data/lib/datadog/appsec/contrib/rails/request.rb +4 -1
  85. data/lib/datadog/appsec/contrib/rails/request_middleware.rb +1 -1
  86. data/lib/datadog/appsec/contrib/sinatra/ext.rb +2 -3
  87. data/lib/datadog/appsec/contrib/sinatra/framework.rb +2 -16
  88. data/lib/datadog/appsec/contrib/sinatra/gateway/request.rb +17 -0
  89. data/lib/datadog/appsec/contrib/sinatra/gateway/route_params.rb +23 -0
  90. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +78 -87
  91. data/lib/datadog/appsec/contrib/sinatra/integration.rb +0 -7
  92. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +33 -15
  93. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +19 -21
  94. data/lib/datadog/appsec/contrib/sinatra/request_middleware.rb +1 -1
  95. data/lib/datadog/appsec/event.rb +98 -50
  96. data/lib/datadog/appsec/ext.rb +10 -0
  97. data/lib/datadog/appsec/extensions.rb +2 -105
  98. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +22 -0
  99. data/lib/datadog/appsec/instrumentation/gateway.rb +26 -6
  100. data/lib/datadog/appsec/instrumentation.rb +9 -0
  101. data/lib/datadog/appsec/monitor/gateway/watcher.rb +67 -0
  102. data/lib/datadog/appsec/monitor/reactive/set_user.rb +58 -0
  103. data/lib/datadog/appsec/monitor.rb +11 -0
  104. data/lib/datadog/appsec/processor/actions.rb +49 -0
  105. data/lib/datadog/appsec/processor/rule_loader.rb +123 -0
  106. data/lib/datadog/appsec/processor/rule_merger.rb +152 -0
  107. data/lib/datadog/appsec/processor.rb +68 -61
  108. data/lib/datadog/appsec/rate_limiter.rb +1 -3
  109. data/lib/datadog/appsec/reactive/address_hash.rb +6 -2
  110. data/lib/datadog/appsec/reactive/engine.rb +10 -7
  111. data/lib/datadog/appsec/reactive/operation.rb +19 -2
  112. data/lib/datadog/appsec/reactive/subscriber.rb +2 -1
  113. data/lib/datadog/appsec/remote.rb +129 -0
  114. data/lib/datadog/appsec/response.rb +151 -0
  115. data/lib/datadog/appsec/sample_rate.rb +21 -0
  116. data/lib/datadog/appsec/scope.rb +61 -0
  117. data/lib/datadog/appsec/utils/http/media_range.rb +199 -0
  118. data/lib/datadog/appsec/utils/http/media_type.rb +85 -0
  119. data/lib/datadog/appsec/utils/http.rb +11 -0
  120. data/lib/datadog/appsec/utils.rb +9 -0
  121. data/lib/datadog/appsec.rb +41 -4
  122. data/lib/datadog/core/backport.rb +51 -0
  123. data/lib/datadog/core/buffer/cruby.rb +1 -1
  124. data/lib/datadog/core/buffer/random.rb +1 -1
  125. data/lib/datadog/core/buffer/thread_safe.rb +1 -1
  126. data/lib/datadog/core/chunker.rb +1 -1
  127. data/lib/datadog/core/configuration/agent_settings_resolver.rb +96 -56
  128. data/lib/datadog/core/configuration/base.rb +7 -16
  129. data/lib/datadog/core/configuration/components.rb +40 -296
  130. data/lib/datadog/core/configuration/ext.rb +47 -0
  131. data/lib/datadog/core/configuration/option.rb +270 -22
  132. data/lib/datadog/core/configuration/option_definition.rb +81 -31
  133. data/lib/datadog/core/configuration/options.rb +26 -16
  134. data/lib/datadog/core/configuration/settings.rb +419 -305
  135. data/lib/datadog/core/configuration.rb +10 -6
  136. data/lib/datadog/core/diagnostics/environment_logger.rb +129 -230
  137. data/lib/datadog/core/diagnostics/health.rb +4 -22
  138. data/lib/datadog/core/encoding.rb +0 -4
  139. data/lib/datadog/core/environment/cgroup.rb +0 -4
  140. data/lib/datadog/core/environment/class_count.rb +1 -1
  141. data/lib/datadog/core/environment/container.rb +0 -4
  142. data/lib/datadog/core/environment/execution.rb +103 -0
  143. data/lib/datadog/core/environment/ext.rb +12 -12
  144. data/lib/datadog/core/environment/gc.rb +1 -1
  145. data/lib/datadog/core/environment/identity.rb +57 -1
  146. data/lib/datadog/core/environment/platform.rb +0 -2
  147. data/lib/datadog/core/environment/socket.rb +1 -1
  148. data/lib/datadog/core/environment/thread_count.rb +1 -1
  149. data/lib/datadog/core/environment/variable_helpers.rb +29 -44
  150. data/lib/datadog/core/environment/vm_cache.rb +18 -1
  151. data/lib/datadog/core/environment/yjit.rb +58 -0
  152. data/lib/datadog/core/error.rb +1 -2
  153. data/lib/datadog/core/extensions.rb +1 -1
  154. data/lib/datadog/core/git/ext.rb +25 -23
  155. data/lib/datadog/core/header_collection.rb +43 -0
  156. data/lib/datadog/core/logger.rb +0 -2
  157. data/lib/datadog/core/logging/ext.rb +3 -1
  158. data/lib/datadog/core/metrics/client.rb +3 -4
  159. data/lib/datadog/core/metrics/ext.rb +6 -8
  160. data/lib/datadog/core/metrics/helpers.rb +1 -1
  161. data/lib/datadog/core/metrics/logging.rb +0 -2
  162. data/lib/datadog/core/metrics/metric.rb +1 -1
  163. data/lib/datadog/core/metrics/options.rb +0 -2
  164. data/lib/datadog/core/pin.rb +0 -2
  165. data/lib/datadog/core/remote/client/capabilities.rb +62 -0
  166. data/lib/datadog/core/remote/client.rb +232 -0
  167. data/lib/datadog/core/remote/component.rb +149 -0
  168. data/lib/datadog/core/remote/configuration/content.rb +111 -0
  169. data/lib/datadog/core/remote/configuration/digest.rb +62 -0
  170. data/lib/datadog/core/remote/configuration/path.rb +90 -0
  171. data/lib/datadog/core/remote/configuration/repository.rb +294 -0
  172. data/lib/datadog/core/remote/configuration/target.rb +74 -0
  173. data/lib/datadog/core/remote/configuration.rb +18 -0
  174. data/lib/datadog/core/remote/dispatcher.rb +59 -0
  175. data/lib/datadog/core/remote/ext.rb +12 -0
  176. data/lib/datadog/core/remote/negotiation.rb +70 -0
  177. data/lib/datadog/core/remote/transport/config.rb +60 -0
  178. data/lib/datadog/core/remote/transport/http/api/instance.rb +39 -0
  179. data/lib/datadog/core/remote/transport/http/api/spec.rb +21 -0
  180. data/lib/datadog/core/remote/transport/http/api.rb +58 -0
  181. data/lib/datadog/core/remote/transport/http/builder.rb +219 -0
  182. data/lib/datadog/core/remote/transport/http/client.rb +48 -0
  183. data/lib/datadog/core/remote/transport/http/config.rb +280 -0
  184. data/lib/datadog/core/remote/transport/http/negotiation.rb +146 -0
  185. data/lib/datadog/core/remote/transport/http.rb +179 -0
  186. data/lib/datadog/core/remote/transport/negotiation.rb +62 -0
  187. data/lib/datadog/core/remote/worker.rb +99 -0
  188. data/lib/datadog/core/remote.rb +24 -0
  189. data/lib/datadog/core/runtime/ext.rb +21 -11
  190. data/lib/datadog/core/runtime/metrics.rb +64 -7
  191. data/lib/datadog/core/telemetry/client.rb +12 -4
  192. data/lib/datadog/core/telemetry/collector.rb +34 -18
  193. data/lib/datadog/core/telemetry/emitter.rb +4 -4
  194. data/lib/datadog/core/telemetry/event.rb +20 -8
  195. data/lib/datadog/core/telemetry/ext.rb +6 -2
  196. data/lib/datadog/core/telemetry/heartbeat.rb +3 -5
  197. data/lib/datadog/core/telemetry/http/adapters/net.rb +0 -2
  198. data/lib/datadog/core/telemetry/http/env.rb +1 -1
  199. data/lib/datadog/core/telemetry/http/ext.rb +10 -8
  200. data/lib/datadog/core/telemetry/http/response.rb +0 -4
  201. data/lib/datadog/core/telemetry/http/transport.rb +4 -1
  202. data/lib/datadog/core/telemetry/v1/app_event.rb +10 -3
  203. data/lib/datadog/core/telemetry/v1/application.rb +7 -1
  204. data/lib/datadog/core/telemetry/v1/dependency.rb +9 -2
  205. data/lib/datadog/core/telemetry/v1/host.rb +9 -1
  206. data/lib/datadog/core/telemetry/v1/install_signature.rb +38 -0
  207. data/lib/datadog/core/telemetry/v1/integration.rb +7 -1
  208. data/lib/datadog/core/telemetry/v1/product.rb +9 -1
  209. data/lib/datadog/core/telemetry/v1/telemetry_request.rb +7 -1
  210. data/lib/datadog/core/telemetry/v2/app_client_configuration_change.rb +41 -0
  211. data/lib/datadog/core/telemetry/v2/request.rb +29 -0
  212. data/lib/datadog/core/transport/ext.rb +47 -0
  213. data/lib/datadog/core/transport/http/adapters/net.rb +168 -0
  214. data/lib/datadog/core/transport/http/adapters/registry.rb +29 -0
  215. data/lib/datadog/core/transport/http/adapters/test.rb +89 -0
  216. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +83 -0
  217. data/lib/datadog/core/transport/http/api/endpoint.rb +31 -0
  218. data/lib/datadog/core/transport/http/api/fallbacks.rb +26 -0
  219. data/lib/datadog/core/transport/http/api/map.rb +18 -0
  220. data/lib/datadog/core/transport/http/env.rb +62 -0
  221. data/lib/datadog/core/transport/http/response.rb +60 -0
  222. data/lib/datadog/core/transport/parcel.rb +22 -0
  223. data/lib/datadog/core/transport/request.rb +17 -0
  224. data/lib/datadog/core/transport/response.rb +64 -0
  225. data/lib/datadog/core/utils/compression.rb +6 -2
  226. data/lib/datadog/core/utils/duration.rb +52 -0
  227. data/lib/datadog/core/utils/forking.rb +0 -2
  228. data/lib/datadog/core/utils/hash.rb +79 -0
  229. data/lib/datadog/core/utils/network.rb +140 -0
  230. data/lib/datadog/core/utils/only_once.rb +0 -2
  231. data/lib/datadog/core/utils/safe_dup.rb +35 -12
  232. data/lib/datadog/core/utils/sequence.rb +1 -1
  233. data/lib/datadog/core/utils/time.rb +1 -3
  234. data/lib/datadog/core/utils.rb +1 -24
  235. data/lib/datadog/core/vendor/ipaddr.rb +78 -0
  236. data/lib/datadog/core/vendor/multipart-post/multipart/post/composite_read_io.rb +0 -2
  237. data/lib/datadog/core/vendor/multipart-post/multipart/post/multipartable.rb +0 -2
  238. data/lib/datadog/core/vendor/multipart-post/multipart/post/parts.rb +0 -2
  239. data/lib/datadog/core/vendor/multipart-post/multipart/post/version.rb +0 -2
  240. data/lib/datadog/core/vendor/multipart-post/multipart/post.rb +0 -2
  241. data/lib/datadog/core/vendor/multipart-post/multipart.rb +0 -2
  242. data/lib/datadog/core/vendor/multipart-post/net/http/post/multipart.rb +0 -2
  243. data/lib/datadog/core/worker.rb +1 -1
  244. data/lib/datadog/core/workers/async.rb +7 -6
  245. data/lib/datadog/core/workers/interval_loop.rb +6 -2
  246. data/lib/datadog/core/workers/polling.rb +2 -4
  247. data/lib/datadog/core/workers/queue.rb +1 -1
  248. data/lib/datadog/core/workers/runtime_metrics.rb +1 -1
  249. data/lib/datadog/core.rb +20 -55
  250. data/lib/datadog/kit/appsec/events.rb +169 -0
  251. data/lib/datadog/kit/enable_core_dumps.rb +8 -9
  252. data/lib/datadog/kit/identity.rb +90 -49
  253. data/lib/datadog/kit.rb +1 -1
  254. data/lib/datadog/opentelemetry/api/context.rb +193 -0
  255. data/lib/datadog/opentelemetry/api/trace/span.rb +14 -0
  256. data/lib/datadog/opentelemetry/sdk/configurator.rb +37 -0
  257. data/lib/datadog/opentelemetry/sdk/id_generator.rb +26 -0
  258. data/lib/datadog/opentelemetry/sdk/propagator.rb +91 -0
  259. data/lib/datadog/opentelemetry/sdk/span_processor.rb +134 -0
  260. data/lib/datadog/opentelemetry/sdk/trace/span.rb +167 -0
  261. data/lib/datadog/opentelemetry/trace.rb +58 -0
  262. data/lib/datadog/opentelemetry.rb +48 -0
  263. data/lib/datadog/opentracer/binary_propagator.rb +1 -1
  264. data/lib/datadog/opentracer/carrier.rb +1 -1
  265. data/lib/datadog/opentracer/distributed_headers.rb +7 -11
  266. data/lib/datadog/opentracer/global_tracer.rb +1 -1
  267. data/lib/datadog/opentracer/propagator.rb +1 -1
  268. data/lib/datadog/opentracer/rack_propagator.rb +0 -5
  269. data/lib/datadog/opentracer/scope.rb +1 -1
  270. data/lib/datadog/opentracer/scope_manager.rb +1 -1
  271. data/lib/datadog/opentracer/span.rb +0 -2
  272. data/lib/datadog/opentracer/span_context.rb +1 -1
  273. data/lib/datadog/opentracer/span_context_factory.rb +1 -1
  274. data/lib/datadog/opentracer/text_map_propagator.rb +6 -9
  275. data/lib/datadog/opentracer/thread_local_scope.rb +1 -1
  276. data/lib/datadog/opentracer/thread_local_scope_manager.rb +0 -2
  277. data/lib/datadog/opentracer/tracer.rb +0 -2
  278. data/lib/datadog/opentracer.rb +10 -1
  279. data/lib/datadog/profiling/collectors/code_provenance.rb +0 -2
  280. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +53 -19
  281. data/lib/datadog/profiling/collectors/dynamic_sampling_rate.rb +14 -0
  282. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +68 -0
  283. data/lib/datadog/profiling/collectors/stack.rb +1 -1
  284. data/lib/datadog/profiling/collectors/thread_context.rb +61 -0
  285. data/lib/datadog/profiling/component.rb +434 -0
  286. data/lib/datadog/profiling/diagnostics/environment_logger.rb +39 -0
  287. data/lib/datadog/profiling/exporter.rb +12 -7
  288. data/lib/datadog/profiling/ext/forking.rb +0 -2
  289. data/lib/datadog/profiling/ext.rb +20 -32
  290. data/lib/datadog/profiling/flush.rb +8 -6
  291. data/lib/datadog/profiling/http_transport.rb +19 -8
  292. data/lib/datadog/profiling/load_native_extension.rb +7 -3
  293. data/lib/datadog/profiling/native_extension.rb +1 -22
  294. data/lib/datadog/profiling/preload.rb +1 -1
  295. data/lib/datadog/profiling/profiler.rb +42 -14
  296. data/lib/datadog/profiling/scheduler.rb +27 -25
  297. data/lib/datadog/profiling/stack_recorder.rb +30 -6
  298. data/lib/datadog/profiling/tag_builder.rb +1 -1
  299. data/lib/datadog/profiling/tasks/exec.rb +0 -2
  300. data/lib/datadog/profiling/tasks/help.rb +0 -2
  301. data/lib/datadog/profiling/tasks/setup.rb +0 -35
  302. data/lib/datadog/profiling.rb +53 -74
  303. data/lib/datadog/tracing/analytics.rb +1 -1
  304. data/lib/datadog/tracing/buffer.rb +0 -5
  305. data/lib/datadog/tracing/client_ip.rb +61 -0
  306. data/lib/datadog/tracing/component.rb +235 -0
  307. data/lib/datadog/tracing/configuration/agent_settings_resolver.rb +13 -0
  308. data/lib/datadog/tracing/configuration/dynamic/option.rb +71 -0
  309. data/lib/datadog/tracing/configuration/dynamic.rb +64 -0
  310. data/lib/datadog/tracing/configuration/ext.rb +73 -15
  311. data/lib/datadog/tracing/configuration/http.rb +74 -0
  312. data/lib/datadog/tracing/configuration/settings.rb +501 -0
  313. data/lib/datadog/tracing/context.rb +1 -1
  314. data/lib/datadog/tracing/context_provider.rb +0 -2
  315. data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +10 -7
  316. data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
  317. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +0 -2
  318. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +0 -2
  319. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +0 -2
  320. data/lib/datadog/tracing/contrib/action_cable/events.rb +1 -1
  321. data/lib/datadog/tracing/contrib/action_cable/ext.rb +19 -19
  322. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +0 -2
  323. data/lib/datadog/tracing/contrib/action_cable/integration.rb +0 -2
  324. data/lib/datadog/tracing/contrib/action_cable/patcher.rb +1 -1
  325. data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +10 -7
  326. data/lib/datadog/tracing/contrib/action_mailer/event.rb +1 -1
  327. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +0 -2
  328. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +0 -2
  329. data/lib/datadog/tracing/contrib/action_mailer/events.rb +1 -1
  330. data/lib/datadog/tracing/contrib/action_mailer/ext.rb +19 -19
  331. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +0 -2
  332. data/lib/datadog/tracing/contrib/action_mailer/patcher.rb +1 -1
  333. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +2 -26
  334. data/lib/datadog/tracing/contrib/action_pack/action_controller/patcher.rb +1 -1
  335. data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +22 -9
  336. data/lib/datadog/tracing/contrib/action_pack/ext.rb +9 -9
  337. data/lib/datadog/tracing/contrib/action_pack/integration.rb +0 -2
  338. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +1 -1
  339. data/lib/datadog/tracing/contrib/action_pack/utils.rb +0 -2
  340. data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +9 -8
  341. data/lib/datadog/tracing/contrib/action_view/event.rb +1 -1
  342. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +0 -2
  343. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +0 -2
  344. data/lib/datadog/tracing/contrib/action_view/events.rb +1 -1
  345. data/lib/datadog/tracing/contrib/action_view/ext.rb +11 -11
  346. data/lib/datadog/tracing/contrib/action_view/instrumentation/partial_renderer.rb +0 -2
  347. data/lib/datadog/tracing/contrib/action_view/instrumentation/template_renderer.rb +0 -2
  348. data/lib/datadog/tracing/contrib/action_view/integration.rb +0 -2
  349. data/lib/datadog/tracing/contrib/action_view/patcher.rb +0 -2
  350. data/lib/datadog/tracing/contrib/action_view/utils.rb +0 -2
  351. data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +14 -8
  352. data/lib/datadog/tracing/contrib/active_job/event.rb +1 -1
  353. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +0 -2
  354. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +0 -2
  355. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +0 -2
  356. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +0 -2
  357. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +0 -2
  358. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +0 -2
  359. data/lib/datadog/tracing/contrib/active_job/events.rb +1 -1
  360. data/lib/datadog/tracing/contrib/active_job/ext.rb +24 -24
  361. data/lib/datadog/tracing/contrib/active_job/integration.rb +0 -2
  362. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +2 -2
  363. data/lib/datadog/tracing/contrib/active_job/patcher.rb +2 -2
  364. data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +10 -7
  365. data/lib/datadog/tracing/contrib/active_model_serializers/event.rb +1 -1
  366. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +0 -2
  367. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +0 -2
  368. data/lib/datadog/tracing/contrib/active_model_serializers/events.rb +1 -1
  369. data/lib/datadog/tracing/contrib/active_model_serializers/ext.rb +11 -11
  370. data/lib/datadog/tracing/contrib/active_model_serializers/integration.rb +0 -2
  371. data/lib/datadog/tracing/contrib/active_model_serializers/patcher.rb +1 -1
  372. data/lib/datadog/tracing/contrib/active_record/configuration/makara_resolver.rb +0 -2
  373. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +18 -13
  374. data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +16 -9
  375. data/lib/datadog/tracing/contrib/active_record/event.rb +1 -1
  376. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +0 -2
  377. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +3 -6
  378. data/lib/datadog/tracing/contrib/active_record/events.rb +1 -1
  379. data/lib/datadog/tracing/contrib/active_record/ext.rb +16 -16
  380. data/lib/datadog/tracing/contrib/active_record/integration.rb +0 -2
  381. data/lib/datadog/tracing/contrib/active_record/patcher.rb +1 -1
  382. data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -3
  383. data/lib/datadog/tracing/contrib/active_record/vendor/connection_specification.rb +0 -2
  384. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +107 -201
  385. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +4 -1
  386. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +1 -1
  387. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +18 -8
  388. data/lib/datadog/tracing/contrib/active_support/ext.rb +17 -17
  389. data/lib/datadog/tracing/contrib/active_support/integration.rb +0 -2
  390. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +1 -1
  391. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +1 -1
  392. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +0 -2
  393. data/lib/datadog/tracing/contrib/active_support/patcher.rb +1 -1
  394. data/lib/datadog/tracing/contrib/analytics.rb +1 -1
  395. data/lib/datadog/tracing/contrib/auto_instrument.rb +1 -1
  396. data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +24 -8
  397. data/lib/datadog/tracing/contrib/aws/ext.rb +36 -14
  398. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +28 -3
  399. data/lib/datadog/tracing/contrib/aws/integration.rb +0 -2
  400. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +4 -2
  401. data/lib/datadog/tracing/contrib/aws/patcher.rb +1 -1
  402. data/lib/datadog/tracing/contrib/aws/service/base.rb +16 -0
  403. data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +22 -0
  404. data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +22 -0
  405. data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +32 -0
  406. data/lib/datadog/tracing/contrib/aws/service/s3.rb +22 -0
  407. data/lib/datadog/tracing/contrib/aws/service/sns.rb +30 -0
  408. data/lib/datadog/tracing/contrib/aws/service/sqs.rb +27 -0
  409. data/lib/datadog/tracing/contrib/aws/service/states.rb +40 -0
  410. data/lib/datadog/tracing/contrib/aws/services.rb +17 -3
  411. data/lib/datadog/tracing/contrib/concurrent_ruby/configuration/settings.rb +4 -3
  412. data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +15 -15
  413. data/lib/datadog/tracing/contrib/concurrent_ruby/ext.rb +3 -3
  414. data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +4 -11
  415. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +2 -3
  416. data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +9 -3
  417. data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +22 -0
  418. data/lib/datadog/tracing/contrib/configurable.rb +1 -1
  419. data/lib/datadog/tracing/contrib/configuration/resolver.rb +1 -1
  420. data/lib/datadog/tracing/contrib/configuration/resolvers/pattern_resolver.rb +1 -1
  421. data/lib/datadog/tracing/contrib/configuration/settings.rb +1 -5
  422. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +29 -8
  423. data/lib/datadog/tracing/contrib/dalli/ext.rb +25 -10
  424. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +23 -7
  425. data/lib/datadog/tracing/contrib/dalli/integration.rb +0 -2
  426. data/lib/datadog/tracing/contrib/dalli/patcher.rb +1 -1
  427. data/lib/datadog/tracing/contrib/dalli/quantize.rb +0 -2
  428. data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +14 -8
  429. data/lib/datadog/tracing/contrib/delayed_job/ext.rb +15 -15
  430. data/lib/datadog/tracing/contrib/delayed_job/integration.rb +0 -2
  431. data/lib/datadog/tracing/contrib/delayed_job/patcher.rb +1 -1
  432. data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +9 -1
  433. data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +3 -1
  434. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +24 -8
  435. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +20 -13
  436. data/lib/datadog/tracing/contrib/elasticsearch/integration.rb +0 -2
  437. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +105 -95
  438. data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +0 -4
  439. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +27 -10
  440. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +52 -7
  441. data/lib/datadog/tracing/contrib/ethon/ext.rb +18 -11
  442. data/lib/datadog/tracing/contrib/ethon/integration.rb +0 -2
  443. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +10 -3
  444. data/lib/datadog/tracing/contrib/ethon/patcher.rb +1 -2
  445. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +29 -11
  446. data/lib/datadog/tracing/contrib/excon/ext.rb +15 -8
  447. data/lib/datadog/tracing/contrib/excon/integration.rb +0 -2
  448. data/lib/datadog/tracing/contrib/excon/middleware.rb +27 -4
  449. data/lib/datadog/tracing/contrib/excon/patcher.rb +1 -1
  450. data/lib/datadog/tracing/contrib/ext.rb +55 -0
  451. data/lib/datadog/tracing/contrib/extensions.rb +32 -2
  452. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +30 -11
  453. data/lib/datadog/tracing/contrib/faraday/connection.rb +1 -1
  454. data/lib/datadog/tracing/contrib/faraday/ext.rb +15 -8
  455. data/lib/datadog/tracing/contrib/faraday/integration.rb +0 -2
  456. data/lib/datadog/tracing/contrib/faraday/middleware.rb +24 -6
  457. data/lib/datadog/tracing/contrib/faraday/patcher.rb +0 -2
  458. data/lib/datadog/tracing/contrib/faraday/rack_builder.rb +1 -1
  459. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +9 -7
  460. data/lib/datadog/tracing/contrib/grape/endpoint.rb +0 -4
  461. data/lib/datadog/tracing/contrib/grape/ext.rb +15 -15
  462. data/lib/datadog/tracing/contrib/grape/instrumentation.rb +0 -2
  463. data/lib/datadog/tracing/contrib/grape/integration.rb +0 -2
  464. data/lib/datadog/tracing/contrib/grape/patcher.rb +1 -1
  465. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +10 -8
  466. data/lib/datadog/tracing/contrib/graphql/ext.rb +6 -6
  467. data/lib/datadog/tracing/contrib/graphql/integration.rb +0 -2
  468. data/lib/datadog/tracing/contrib/graphql/patcher.rb +0 -2
  469. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +50 -10
  470. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +45 -19
  471. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +43 -26
  472. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +0 -6
  473. data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +26 -0
  474. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +42 -0
  475. data/lib/datadog/tracing/contrib/grpc/ext.rb +15 -11
  476. data/lib/datadog/tracing/contrib/grpc/formatting.rb +127 -0
  477. data/lib/datadog/tracing/contrib/grpc/integration.rb +8 -3
  478. data/lib/datadog/tracing/contrib/grpc/intercept_with_datadog.rb +1 -1
  479. data/lib/datadog/tracing/contrib/grpc/patcher.rb +1 -4
  480. data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +47 -0
  481. data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +23 -0
  482. data/lib/datadog/tracing/contrib/hanami/ext.rb +24 -0
  483. data/lib/datadog/tracing/contrib/hanami/integration.rb +42 -0
  484. data/lib/datadog/tracing/contrib/hanami/patcher.rb +33 -0
  485. data/lib/datadog/tracing/contrib/hanami/plugin.rb +23 -0
  486. data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +41 -0
  487. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +44 -0
  488. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +6 -9
  489. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +47 -10
  490. data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +38 -0
  491. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +37 -0
  492. data/lib/datadog/tracing/contrib/http/ext.rb +15 -8
  493. data/lib/datadog/tracing/contrib/http/instrumentation.rb +32 -12
  494. data/lib/datadog/tracing/contrib/http/integration.rb +1 -1
  495. data/lib/datadog/tracing/contrib/http/patcher.rb +1 -1
  496. data/lib/datadog/tracing/contrib/http_annotation_helper.rb +1 -1
  497. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +47 -10
  498. data/lib/datadog/tracing/contrib/httpclient/ext.rb +16 -8
  499. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +27 -8
  500. data/lib/datadog/tracing/contrib/httpclient/integration.rb +0 -2
  501. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +0 -2
  502. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +47 -10
  503. data/lib/datadog/tracing/contrib/httprb/ext.rb +15 -8
  504. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +27 -9
  505. data/lib/datadog/tracing/contrib/httprb/integration.rb +0 -2
  506. data/lib/datadog/tracing/contrib/httprb/patcher.rb +0 -2
  507. data/lib/datadog/tracing/contrib/integration.rb +1 -1
  508. data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +10 -7
  509. data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +2 -1
  510. data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +1 -1
  511. data/lib/datadog/tracing/contrib/kafka/event.rb +2 -1
  512. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +0 -2
  513. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +0 -2
  514. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +0 -2
  515. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +0 -2
  516. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/join_group.rb +0 -2
  517. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/leave_group.rb +0 -2
  518. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/sync_group.rb +0 -2
  519. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +1 -2
  520. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +1 -2
  521. data/lib/datadog/tracing/contrib/kafka/events.rb +1 -1
  522. data/lib/datadog/tracing/contrib/kafka/ext.rb +41 -39
  523. data/lib/datadog/tracing/contrib/kafka/integration.rb +0 -2
  524. data/lib/datadog/tracing/contrib/kafka/patcher.rb +1 -1
  525. data/lib/datadog/tracing/contrib/lograge/configuration/settings.rb +4 -3
  526. data/lib/datadog/tracing/contrib/lograge/ext.rb +2 -2
  527. data/lib/datadog/tracing/contrib/lograge/instrumentation.rb +3 -18
  528. data/lib/datadog/tracing/contrib/lograge/integration.rb +0 -2
  529. data/lib/datadog/tracing/contrib/lograge/patcher.rb +1 -1
  530. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +24 -8
  531. data/lib/datadog/tracing/contrib/mongodb/ext.rb +24 -14
  532. data/lib/datadog/tracing/contrib/mongodb/instrumentation.rb +1 -1
  533. data/lib/datadog/tracing/contrib/mongodb/integration.rb +0 -2
  534. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +0 -2
  535. data/lib/datadog/tracing/contrib/mongodb/patcher.rb +1 -1
  536. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +22 -4
  537. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +35 -8
  538. data/lib/datadog/tracing/contrib/mysql2/ext.rb +14 -9
  539. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +38 -4
  540. data/lib/datadog/tracing/contrib/mysql2/integration.rb +0 -2
  541. data/lib/datadog/tracing/contrib/mysql2/patcher.rb +1 -1
  542. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +52 -0
  543. data/lib/datadog/tracing/contrib/opensearch/ext.rb +37 -0
  544. data/lib/datadog/tracing/contrib/opensearch/integration.rb +44 -0
  545. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +135 -0
  546. data/lib/datadog/tracing/contrib/opensearch/quantize.rb +81 -0
  547. data/lib/datadog/tracing/contrib/patchable.rb +1 -1
  548. data/lib/datadog/tracing/contrib/patcher.rb +3 -5
  549. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +36 -8
  550. data/lib/datadog/tracing/contrib/pg/ext.rb +21 -18
  551. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +115 -39
  552. data/lib/datadog/tracing/contrib/pg/integration.rb +0 -2
  553. data/lib/datadog/tracing/contrib/pg/patcher.rb +1 -1
  554. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +23 -8
  555. data/lib/datadog/tracing/contrib/presto/ext.rb +24 -19
  556. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +17 -5
  557. data/lib/datadog/tracing/contrib/presto/integration.rb +0 -2
  558. data/lib/datadog/tracing/contrib/presto/patcher.rb +0 -2
  559. data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +41 -0
  560. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +33 -0
  561. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +28 -0
  562. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +53 -0
  563. data/lib/datadog/tracing/contrib/qless/configuration/settings.rb +13 -9
  564. data/lib/datadog/tracing/contrib/qless/ext.rb +13 -13
  565. data/lib/datadog/tracing/contrib/qless/integration.rb +0 -2
  566. data/lib/datadog/tracing/contrib/qless/patcher.rb +1 -2
  567. data/lib/datadog/tracing/contrib/qless/qless_job.rb +3 -2
  568. data/lib/datadog/tracing/contrib/qless/tracer_cleaner.rb +1 -1
  569. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +21 -14
  570. data/lib/datadog/tracing/contrib/que/ext.rb +0 -2
  571. data/lib/datadog/tracing/contrib/que/integration.rb +0 -2
  572. data/lib/datadog/tracing/contrib/que/patcher.rb +0 -2
  573. data/lib/datadog/tracing/contrib/que/tracer.rb +4 -2
  574. data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +18 -8
  575. data/lib/datadog/tracing/contrib/racecar/event.rb +7 -4
  576. data/lib/datadog/tracing/contrib/racecar/events/batch.rb +4 -3
  577. data/lib/datadog/tracing/contrib/racecar/events/consume.rb +0 -2
  578. data/lib/datadog/tracing/contrib/racecar/events/message.rb +4 -3
  579. data/lib/datadog/tracing/contrib/racecar/events.rb +1 -1
  580. data/lib/datadog/tracing/contrib/racecar/ext.rb +19 -18
  581. data/lib/datadog/tracing/contrib/racecar/integration.rb +0 -2
  582. data/lib/datadog/tracing/contrib/racecar/patcher.rb +1 -1
  583. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +16 -14
  584. data/lib/datadog/tracing/contrib/rack/ext.rb +17 -12
  585. data/lib/datadog/tracing/contrib/rack/header_collection.rb +38 -0
  586. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +63 -0
  587. data/lib/datadog/tracing/contrib/rack/integration.rb +0 -2
  588. data/lib/datadog/tracing/contrib/rack/middlewares.rb +130 -81
  589. data/lib/datadog/tracing/contrib/rack/patcher.rb +0 -2
  590. data/lib/datadog/tracing/contrib/rack/request_queue.rb +0 -2
  591. data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +0 -4
  592. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +28 -19
  593. data/lib/datadog/tracing/contrib/rails/ext.rb +6 -6
  594. data/lib/datadog/tracing/contrib/rails/framework.rb +1 -1
  595. data/lib/datadog/tracing/contrib/rails/integration.rb +0 -2
  596. data/lib/datadog/tracing/contrib/rails/log_injection.rb +7 -12
  597. data/lib/datadog/tracing/contrib/rails/middlewares.rb +1 -1
  598. data/lib/datadog/tracing/contrib/rails/patcher.rb +10 -43
  599. data/lib/datadog/tracing/contrib/rails/railtie.rb +3 -5
  600. data/lib/datadog/tracing/contrib/rails/utils.rb +2 -2
  601. data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +14 -11
  602. data/lib/datadog/tracing/contrib/rake/ext.rb +13 -13
  603. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +0 -2
  604. data/lib/datadog/tracing/contrib/rake/integration.rb +0 -2
  605. data/lib/datadog/tracing/contrib/rake/patcher.rb +1 -1
  606. data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +0 -2
  607. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +26 -10
  608. data/lib/datadog/tracing/contrib/redis/ext.rb +21 -13
  609. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +13 -39
  610. data/lib/datadog/tracing/contrib/redis/integration.rb +34 -3
  611. data/lib/datadog/tracing/contrib/redis/patcher.rb +53 -11
  612. data/lib/datadog/tracing/contrib/redis/quantize.rb +11 -10
  613. data/lib/datadog/tracing/contrib/redis/tags.rb +24 -9
  614. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +83 -0
  615. data/lib/datadog/tracing/contrib/redis/vendor/resolver.rb +0 -2
  616. data/lib/datadog/tracing/contrib/registerable.rb +1 -1
  617. data/lib/datadog/tracing/contrib/registry.rb +1 -1
  618. data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +14 -8
  619. data/lib/datadog/tracing/contrib/resque/ext.rb +8 -8
  620. data/lib/datadog/tracing/contrib/resque/integration.rb +0 -2
  621. data/lib/datadog/tracing/contrib/resque/patcher.rb +1 -1
  622. data/lib/datadog/tracing/contrib/resque/resque_job.rb +5 -1
  623. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +27 -10
  624. data/lib/datadog/tracing/contrib/rest_client/ext.rb +14 -8
  625. data/lib/datadog/tracing/contrib/rest_client/integration.rb +0 -2
  626. data/lib/datadog/tracing/contrib/rest_client/patcher.rb +1 -2
  627. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +28 -3
  628. data/lib/datadog/tracing/contrib/roda/configuration/settings.rb +37 -0
  629. data/lib/datadog/tracing/contrib/roda/ext.rb +18 -0
  630. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +76 -0
  631. data/lib/datadog/tracing/contrib/roda/integration.rb +45 -0
  632. data/lib/datadog/{ci/contrib/cucumber → tracing/contrib/roda}/patcher.rb +10 -7
  633. data/lib/datadog/tracing/contrib/semantic_logger/configuration/settings.rb +4 -3
  634. data/lib/datadog/tracing/contrib/semantic_logger/ext.rb +2 -2
  635. data/lib/datadog/tracing/contrib/semantic_logger/instrumentation.rb +6 -21
  636. data/lib/datadog/tracing/contrib/semantic_logger/integration.rb +0 -2
  637. data/lib/datadog/tracing/contrib/semantic_logger/patcher.rb +1 -1
  638. data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +10 -7
  639. data/lib/datadog/tracing/contrib/sequel/database.rb +4 -3
  640. data/lib/datadog/tracing/contrib/sequel/dataset.rb +5 -2
  641. data/lib/datadog/tracing/contrib/sequel/ext.rb +9 -9
  642. data/lib/datadog/tracing/contrib/sequel/integration.rb +0 -2
  643. data/lib/datadog/tracing/contrib/sequel/patcher.rb +1 -1
  644. data/lib/datadog/tracing/contrib/sequel/utils.rb +7 -6
  645. data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +15 -9
  646. data/lib/datadog/tracing/contrib/shoryuken/ext.rb +13 -12
  647. data/lib/datadog/tracing/contrib/shoryuken/integration.rb +0 -2
  648. data/lib/datadog/tracing/contrib/shoryuken/patcher.rb +1 -1
  649. data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +5 -1
  650. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +18 -4
  651. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +19 -11
  652. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +38 -0
  653. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +31 -31
  654. data/lib/datadog/tracing/contrib/sidekiq/integration.rb +8 -2
  655. data/lib/datadog/tracing/contrib/sidekiq/patcher.rb +15 -3
  656. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/heartbeat.rb +14 -5
  657. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/job_fetch.rb +3 -1
  658. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/redis_info.rb +3 -1
  659. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/scheduled_poller.rb +5 -1
  660. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/stop.rb +34 -0
  661. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +24 -3
  662. data/lib/datadog/tracing/contrib/sidekiq/{tracing.rb → utils.rb} +2 -2
  663. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +11 -11
  664. data/lib/datadog/tracing/contrib/sinatra/env.rb +11 -41
  665. data/lib/datadog/tracing/contrib/sinatra/ext.rb +23 -19
  666. data/lib/datadog/tracing/contrib/sinatra/framework.rb +0 -2
  667. data/lib/datadog/tracing/contrib/sinatra/integration.rb +0 -2
  668. data/lib/datadog/tracing/contrib/sinatra/patcher.rb +2 -3
  669. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +8 -82
  670. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +14 -20
  671. data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +14 -10
  672. data/lib/datadog/tracing/contrib/sneakers/ext.rb +2 -2
  673. data/lib/datadog/tracing/contrib/sneakers/integration.rb +0 -2
  674. data/lib/datadog/tracing/contrib/sneakers/patcher.rb +0 -2
  675. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +5 -2
  676. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +92 -0
  677. data/lib/datadog/tracing/contrib/status_code_matcher.rb +0 -3
  678. data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +36 -0
  679. data/lib/datadog/tracing/contrib/stripe/ext.rb +26 -0
  680. data/lib/datadog/tracing/contrib/stripe/integration.rb +43 -0
  681. data/lib/datadog/tracing/contrib/stripe/patcher.rb +28 -0
  682. data/lib/datadog/tracing/contrib/stripe/request.rb +67 -0
  683. data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +10 -7
  684. data/lib/datadog/tracing/contrib/sucker_punch/exception_handler.rb +1 -1
  685. data/lib/datadog/tracing/contrib/sucker_punch/ext.rb +14 -14
  686. data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +0 -2
  687. data/lib/datadog/tracing/contrib/sucker_punch/integration.rb +0 -2
  688. data/lib/datadog/tracing/contrib/sucker_punch/patcher.rb +1 -2
  689. data/lib/datadog/tracing/contrib/utils/database.rb +4 -4
  690. data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +0 -2
  691. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +92 -14
  692. data/lib/datadog/tracing/contrib.rb +5 -1
  693. data/lib/datadog/tracing/correlation.rb +42 -14
  694. data/lib/datadog/tracing/diagnostics/environment_logger.rb +165 -0
  695. data/lib/datadog/{core → tracing}/diagnostics/ext.rb +21 -26
  696. data/lib/datadog/tracing/diagnostics/health.rb +40 -0
  697. data/lib/datadog/tracing/distributed/b3_multi.rb +72 -0
  698. data/lib/datadog/tracing/distributed/b3_single.rb +68 -0
  699. data/lib/datadog/tracing/distributed/datadog.rb +199 -0
  700. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +84 -0
  701. data/lib/datadog/tracing/distributed/fetcher.rb +21 -0
  702. data/lib/datadog/tracing/distributed/headers/ext.rb +19 -16
  703. data/lib/datadog/tracing/distributed/helpers.rb +26 -39
  704. data/lib/datadog/tracing/distributed/none.rb +18 -0
  705. data/lib/datadog/tracing/distributed/propagation.rb +127 -0
  706. data/lib/datadog/tracing/distributed/trace_context.rb +415 -0
  707. data/lib/datadog/tracing/event.rb +0 -4
  708. data/lib/datadog/tracing/flush.rb +58 -36
  709. data/lib/datadog/tracing/metadata/analytics.rb +1 -1
  710. data/lib/datadog/tracing/metadata/errors.rb +1 -1
  711. data/lib/datadog/tracing/metadata/ext.rb +29 -14
  712. data/lib/datadog/tracing/metadata/tagging.rb +17 -4
  713. data/lib/datadog/tracing/metadata.rb +1 -1
  714. data/lib/datadog/tracing/pipeline/span_filter.rb +1 -1
  715. data/lib/datadog/tracing/pipeline/span_processor.rb +1 -1
  716. data/lib/datadog/tracing/pipeline.rb +0 -4
  717. data/lib/datadog/tracing/propagation/http.rb +4 -99
  718. data/lib/datadog/tracing/remote.rb +78 -0
  719. data/lib/datadog/tracing/runtime/metrics.rb +1 -3
  720. data/lib/datadog/tracing/sampling/all_sampler.rb +1 -1
  721. data/lib/datadog/tracing/sampling/ext.rb +30 -1
  722. data/lib/datadog/tracing/sampling/matcher.rb +1 -1
  723. data/lib/datadog/tracing/sampling/priority_sampler.rb +58 -5
  724. data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +8 -11
  725. data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +29 -8
  726. data/lib/datadog/tracing/sampling/rate_limiter.rb +3 -2
  727. data/lib/datadog/tracing/sampling/rate_sampler.rb +23 -10
  728. data/lib/datadog/tracing/sampling/rule.rb +0 -4
  729. data/lib/datadog/tracing/sampling/rule_sampler.rb +33 -7
  730. data/lib/datadog/tracing/sampling/sampler.rb +0 -2
  731. data/lib/datadog/tracing/sampling/span/ext.rb +25 -0
  732. data/lib/datadog/tracing/sampling/span/matcher.rb +9 -0
  733. data/lib/datadog/tracing/sampling/span/rule.rb +82 -0
  734. data/lib/datadog/tracing/sampling/span/rule_parser.rb +104 -0
  735. data/lib/datadog/tracing/sampling/span/sampler.rb +77 -0
  736. data/lib/datadog/tracing/span.rb +3 -21
  737. data/lib/datadog/tracing/span_operation.rb +8 -24
  738. data/lib/datadog/tracing/sync_writer.rb +4 -6
  739. data/lib/datadog/tracing/trace_digest.rb +120 -3
  740. data/lib/datadog/tracing/trace_operation.rb +61 -13
  741. data/lib/datadog/tracing/trace_segment.rb +20 -7
  742. data/lib/datadog/tracing/tracer.rb +46 -10
  743. data/lib/datadog/tracing/transport/http/api/instance.rb +37 -0
  744. data/lib/datadog/tracing/transport/http/api/spec.rb +19 -0
  745. data/lib/datadog/tracing/transport/http/api.rb +43 -0
  746. data/lib/datadog/tracing/transport/http/builder.rb +162 -0
  747. data/lib/datadog/tracing/transport/http/client.rb +57 -0
  748. data/lib/datadog/tracing/transport/http/statistics.rb +47 -0
  749. data/lib/datadog/tracing/transport/http/traces.rb +152 -0
  750. data/lib/datadog/tracing/transport/http.rb +124 -0
  751. data/lib/datadog/tracing/transport/io/client.rb +89 -0
  752. data/lib/datadog/tracing/transport/io/response.rb +27 -0
  753. data/lib/datadog/tracing/transport/io/traces.rb +101 -0
  754. data/lib/datadog/tracing/transport/io.rb +30 -0
  755. data/lib/datadog/tracing/transport/serializable_trace.rb +126 -0
  756. data/lib/datadog/tracing/transport/statistics.rb +77 -0
  757. data/lib/datadog/tracing/transport/trace_formatter.rb +209 -0
  758. data/lib/datadog/tracing/transport/traces.rb +224 -0
  759. data/lib/datadog/tracing/utils.rb +83 -0
  760. data/lib/datadog/tracing/workers/trace_writer.rb +6 -7
  761. data/lib/datadog/tracing/workers.rb +4 -6
  762. data/lib/datadog/tracing/writer.rb +12 -6
  763. data/lib/datadog/tracing.rb +1 -1
  764. data/lib/ddtrace/auto_instrument.rb +1 -1
  765. data/lib/ddtrace/auto_instrument_base.rb +1 -1
  766. data/lib/ddtrace/profiling/preload.rb +0 -2
  767. data/lib/ddtrace/transport/ext.rb +21 -15
  768. data/lib/ddtrace/version.rb +14 -15
  769. data/lib/ddtrace.rb +3 -5
  770. metadata +230 -113
  771. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.c +0 -390
  772. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.h +0 -6
  773. data/lib/datadog/appsec/assets/waf_rules/risky.json +0 -1499
  774. data/lib/datadog/appsec/contrib/configuration/settings.rb +0 -20
  775. data/lib/datadog/appsec/contrib/rack/configuration/settings.rb +0 -22
  776. data/lib/datadog/appsec/contrib/rack/request.rb +0 -58
  777. data/lib/datadog/appsec/contrib/rack/response.rb +0 -24
  778. data/lib/datadog/appsec/contrib/rails/configuration/settings.rb +0 -22
  779. data/lib/datadog/appsec/contrib/sinatra/configuration/settings.rb +0 -22
  780. data/lib/datadog/ci/configuration/components.rb +0 -32
  781. data/lib/datadog/ci/configuration/settings.rb +0 -53
  782. data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +0 -33
  783. data/lib/datadog/ci/contrib/cucumber/ext.rb +0 -22
  784. data/lib/datadog/ci/contrib/cucumber/formatter.rb +0 -94
  785. data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +0 -28
  786. data/lib/datadog/ci/contrib/cucumber/integration.rb +0 -49
  787. data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +0 -33
  788. data/lib/datadog/ci/contrib/rspec/example.rb +0 -70
  789. data/lib/datadog/ci/contrib/rspec/ext.rb +0 -21
  790. data/lib/datadog/ci/contrib/rspec/integration.rb +0 -50
  791. data/lib/datadog/ci/contrib/rspec/patcher.rb +0 -27
  792. data/lib/datadog/ci/ext/app_types.rb +0 -11
  793. data/lib/datadog/ci/ext/environment.rb +0 -505
  794. data/lib/datadog/ci/ext/settings.rb +0 -12
  795. data/lib/datadog/ci/ext/test.rb +0 -37
  796. data/lib/datadog/ci/extensions.rb +0 -19
  797. data/lib/datadog/ci/flush.rb +0 -38
  798. data/lib/datadog/ci/test.rb +0 -83
  799. data/lib/datadog/ci.rb +0 -20
  800. data/lib/datadog/core/configuration/dependency_resolver.rb +0 -28
  801. data/lib/datadog/core/configuration/option_definition_set.rb +0 -22
  802. data/lib/datadog/core/configuration/option_set.rb +0 -10
  803. data/lib/datadog/core/utils/object_set.rb +0 -43
  804. data/lib/datadog/core/utils/string_table.rb +0 -49
  805. data/lib/datadog/profiling/backtrace_location.rb +0 -34
  806. data/lib/datadog/profiling/buffer.rb +0 -43
  807. data/lib/datadog/profiling/collectors/cpu_and_wall_time.rb +0 -27
  808. data/lib/datadog/profiling/collectors/old_stack.rb +0 -298
  809. data/lib/datadog/profiling/encoding/profile.rb +0 -43
  810. data/lib/datadog/profiling/event.rb +0 -15
  811. data/lib/datadog/profiling/events/stack.rb +0 -82
  812. data/lib/datadog/profiling/old_ext.rb +0 -42
  813. data/lib/datadog/profiling/old_recorder.rb +0 -101
  814. data/lib/datadog/profiling/pprof/builder.rb +0 -127
  815. data/lib/datadog/profiling/pprof/converter.rb +0 -104
  816. data/lib/datadog/profiling/pprof/message_set.rb +0 -16
  817. data/lib/datadog/profiling/pprof/payload.rb +0 -20
  818. data/lib/datadog/profiling/pprof/pprof.proto +0 -212
  819. data/lib/datadog/profiling/pprof/pprof_pb.rb +0 -83
  820. data/lib/datadog/profiling/pprof/stack_sample.rb +0 -141
  821. data/lib/datadog/profiling/pprof/string_table.rb +0 -12
  822. data/lib/datadog/profiling/pprof/template.rb +0 -120
  823. data/lib/datadog/profiling/trace_identifiers/ddtrace.rb +0 -45
  824. data/lib/datadog/profiling/trace_identifiers/helper.rb +0 -47
  825. data/lib/datadog/profiling/transport/http/api/endpoint.rb +0 -85
  826. data/lib/datadog/profiling/transport/http/api/instance.rb +0 -38
  827. data/lib/datadog/profiling/transport/http/api/spec.rb +0 -42
  828. data/lib/datadog/profiling/transport/http/api.rb +0 -45
  829. data/lib/datadog/profiling/transport/http/builder.rb +0 -30
  830. data/lib/datadog/profiling/transport/http/client.rb +0 -37
  831. data/lib/datadog/profiling/transport/http/response.rb +0 -21
  832. data/lib/datadog/profiling/transport/http.rb +0 -118
  833. data/lib/datadog/tracing/contrib/sinatra/headers.rb +0 -35
  834. data/lib/datadog/tracing/distributed/headers/b3.rb +0 -55
  835. data/lib/datadog/tracing/distributed/headers/b3_single.rb +0 -67
  836. data/lib/datadog/tracing/distributed/headers/datadog.rb +0 -52
  837. data/lib/datadog/tracing/distributed/headers/parser.rb +0 -37
  838. data/lib/datadog/tracing/distributed/metadata/b3.rb +0 -55
  839. data/lib/datadog/tracing/distributed/metadata/b3_single.rb +0 -66
  840. data/lib/datadog/tracing/distributed/metadata/datadog.rb +0 -73
  841. data/lib/datadog/tracing/distributed/metadata/parser.rb +0 -34
  842. data/lib/datadog/tracing/propagation/grpc.rb +0 -98
  843. data/lib/ddtrace/transport/http/adapters/net.rb +0 -158
  844. data/lib/ddtrace/transport/http/adapters/registry.rb +0 -27
  845. data/lib/ddtrace/transport/http/adapters/test.rb +0 -87
  846. data/lib/ddtrace/transport/http/adapters/unix_socket.rb +0 -79
  847. data/lib/ddtrace/transport/http/api/endpoint.rb +0 -29
  848. data/lib/ddtrace/transport/http/api/fallbacks.rb +0 -24
  849. data/lib/ddtrace/transport/http/api/instance.rb +0 -35
  850. data/lib/ddtrace/transport/http/api/map.rb +0 -16
  851. data/lib/ddtrace/transport/http/api/spec.rb +0 -17
  852. data/lib/ddtrace/transport/http/api.rb +0 -41
  853. data/lib/ddtrace/transport/http/builder.rb +0 -178
  854. data/lib/ddtrace/transport/http/client.rb +0 -54
  855. data/lib/ddtrace/transport/http/env.rb +0 -58
  856. data/lib/ddtrace/transport/http/response.rb +0 -58
  857. data/lib/ddtrace/transport/http/statistics.rb +0 -45
  858. data/lib/ddtrace/transport/http/traces.rb +0 -146
  859. data/lib/ddtrace/transport/http.rb +0 -121
  860. data/lib/ddtrace/transport/io/client.rb +0 -87
  861. data/lib/ddtrace/transport/io/response.rb +0 -25
  862. data/lib/ddtrace/transport/io/traces.rb +0 -101
  863. data/lib/ddtrace/transport/io.rb +0 -28
  864. data/lib/ddtrace/transport/parcel.rb +0 -22
  865. data/lib/ddtrace/transport/request.rb +0 -15
  866. data/lib/ddtrace/transport/response.rb +0 -62
  867. data/lib/ddtrace/transport/serializable_trace.rb +0 -118
  868. data/lib/ddtrace/transport/statistics.rb +0 -75
  869. data/lib/ddtrace/transport/trace_formatter.rb +0 -187
  870. data/lib/ddtrace/transport/traces.rb +0 -216
@@ -0,0 +1,1301 @@
1
+ #include <ruby.h>
2
+
3
+ #include "collectors_thread_context.h"
4
+ #include "clock_id.h"
5
+ #include "collectors_stack.h"
6
+ #include "collectors_gc_profiling_helper.h"
7
+ #include "helpers.h"
8
+ #include "libdatadog_helpers.h"
9
+ #include "private_vm_api_access.h"
10
+ #include "stack_recorder.h"
11
+ #include "time_helpers.h"
12
+
13
+ // Used to trigger sampling of threads, based on external "events", such as:
14
+ // * periodic timer for cpu-time and wall-time
15
+ // * VM garbage collection events
16
+ // * VM object allocation events
17
+ //
18
+ // This file implements the native bits of the Datadog::Profiling::Collectors::ThreadContext class
19
+ //
20
+ // Triggering of this component (e.g. watching for the above "events") is implemented by Collectors::CpuAndWallTimeWorker.
21
+
22
+ // ---
23
+ // ## Tracking of cpu-time and wall-time spent during garbage collection
24
+ //
25
+ // This feature works by having a special state that a thread can be in: doing garbage collection. This state is
26
+ // tracked inside the thread's `per_thread_context.gc_tracking` data, and three functions, listed below. The functions
27
+ // will get called by the `Collectors::CpuAndWallTimeWorker` at very specific times in the VM lifetime.
28
+ //
29
+ // * `thread_context_collector_on_gc_start`: Called at the very beginning of the garbage collection process.
30
+ // The internal VM `during_gc` flag is set to `true`, but Ruby has not done any work yet.
31
+ // * `thread_context_collector_on_gc_finish`: Called at the very end of the garbage collection process.
32
+ // The internal VM `during_gc` flag is still set to `true`, but all the work has been done.
33
+ // * `thread_context_collector_sample_after_gc`: Called shortly after the garbage collection process.
34
+ // The internal VM `during_gc` flag is set to `false`.
35
+ //
36
+ // Inside this component, here's what happens inside those three functions:
37
+ //
38
+ // When `thread_context_collector_on_gc_start` gets called, the current cpu and wall-time get recorded to the thread
39
+ // context: `cpu_time_at_gc_start_ns` and `wall_time_at_gc_start_ns`.
40
+ //
41
+ // While `cpu_time_at_gc_start_ns` is set, regular samples (if any) do not account for cpu-time any time that passes
42
+ // after this timestamp. The idea is that this cpu-time will be blamed separately on GC, and not on the user thread.
43
+ // Wall-time accounting is not affected by this (e.g. we still record 60 seconds every 60 seconds).
44
+ //
45
+ // (Regular samples can still account for the cpu-time between the previous sample and the start of GC.)
46
+ //
47
+ // When `thread_context_collector_on_gc_finish` gets called, the cpu-time and wall-time spent during GC gets recorded
48
+ // into the global gc_tracking structure, and further samples are not affected. (The `cpu_time_at_previous_sample_ns`
49
+ // of the thread that did GC also gets adjusted to avoid double-accounting.)
50
+ //
51
+ // Finally, when `thread_context_collector_sample_after_gc` gets called, a sample gets recorded with a stack having
52
+ // a single placeholder `Garbage Collection` frame. This sample gets
53
+ // assigned the cpu-time and wall-time that was recorded between calls to `on_gc_start` and `on_gc_finish`, as well
54
+ // as metadata for the last GC.
55
+ //
56
+ // Note that the Ruby GC does not usually do all of the GC work in one go. Instead, it breaks it up into smaller steps
57
+ // so that the application can keep doing user work in between GC steps.
58
+ // The `on_gc_start` / `on_gc_finish` will trigger each time the VM executes these smaller steps, and on a benchmark
59
+ // that executes `Object.new` in a loop, I measured more than 50k of this steps per second (!!).
60
+ // Creating these many events for every GC step is a lot of overhead, so instead `on_gc_finish` coalesces time
61
+ // spent in GC and only flushes it at most every 10 ms/every complete GC collection. This reduces the amount of
62
+ // individual GC events we need to record. We use the latest GC metadata for this event, reflecting the last GC that
63
+ // happened in the coalesced period.
64
+ //
65
+ // In an earlier attempt at implementing this functionality (https://github.com/DataDog/dd-trace-rb/pull/2308), we
66
+ // discovered that we needed to factor the sampling work away from `thread_context_collector_on_gc_finish` and into a
67
+ // separate `thread_context_collector_sample_after_gc` because (as documented in more detail below),
68
+ // `sample_after_gc` could trigger memory allocation in rare occasions (usually exceptions), which is actually not
69
+ // allowed to happen during Ruby's garbage collection start/finish hooks.
70
+ // ---
71
+
72
+ #define THREAD_ID_LIMIT_CHARS 44 // Why 44? "#{2**64} (#{2**64})".size + 1 for \0
73
+ #define THREAD_INVOKE_LOCATION_LIMIT_CHARS 512
74
+ #define IS_WALL_TIME true
75
+ #define IS_NOT_WALL_TIME false
76
+ #define MISSING_TRACER_CONTEXT_KEY 0
77
+ #define TIME_BETWEEN_GC_EVENTS_NS MILLIS_AS_NS(10)
78
+
79
+ static ID at_active_span_id; // id of :@active_span in Ruby
80
+ static ID at_active_trace_id; // id of :@active_trace in Ruby
81
+ static ID at_id_id; // id of :@id in Ruby
82
+ static ID at_resource_id; // id of :@resource in Ruby
83
+ static ID at_root_span_id; // id of :@root_span in Ruby
84
+ static ID at_type_id; // id of :@type in Ruby
85
+
86
+ // Contains state for a single ThreadContext instance
87
+ struct thread_context_collector_state {
88
+ // Note: Places in this file that usually need to be changed when this struct is changed are tagged with
89
+ // "Update this when modifying state struct"
90
+
91
+ // Required by Datadog::Profiling::Collectors::Stack as a scratch buffer during sampling
92
+ sampling_buffer *sampling_buffer;
93
+ // Hashmap <Thread Object, struct per_thread_context>
94
+ st_table *hash_map_per_thread_context;
95
+ // Datadog::Profiling::StackRecorder instance
96
+ VALUE recorder_instance;
97
+ // If the tracer is available and enabled, this will be the fiber-local symbol for accessing its running context,
98
+ // to enable code hotspots and endpoint aggregation.
99
+ // When not available, this is set to MISSING_TRACER_CONTEXT_KEY.
100
+ ID tracer_context_key;
101
+ // Track how many regular samples we've taken. Does not include garbage collection samples.
102
+ // Currently **outside** of stats struct because we also use it to decide when to clean the contexts, and thus this
103
+ // is not (just) a stat.
104
+ unsigned int sample_count;
105
+ // Reusable array to get list of threads
106
+ VALUE thread_list_buffer;
107
+ // Used to omit endpoint names (retrieved from tracer) from collected data
108
+ bool endpoint_collection_enabled;
109
+ // Used to omit timestamps / timeline events from collected data
110
+ bool timeline_enabled;
111
+ // Used to omit class information from collected allocation data
112
+ bool allocation_type_enabled;
113
+ // Used when calling monotonic_to_system_epoch_ns
114
+ monotonic_to_system_epoch_state time_converter_state;
115
+ // Used to identify the main thread, to give it a fallback name
116
+ VALUE main_thread;
117
+
118
+ struct stats {
119
+ // Track how many garbage collection samples we've taken.
120
+ unsigned int gc_samples;
121
+ // See thread_context_collector_on_gc_start for details
122
+ unsigned int gc_samples_missed_due_to_missing_context;
123
+ } stats;
124
+
125
+ struct {
126
+ unsigned long accumulated_cpu_time_ns;
127
+ unsigned long accumulated_wall_time_ns;
128
+
129
+ long wall_time_at_previous_gc_ns; // Will be INVALID_TIME unless there's accumulated time above
130
+ long wall_time_at_last_flushed_gc_event_ns; // Starts at 0 and then will always be valid
131
+ } gc_tracking;
132
+ };
133
+
134
+ // Tracks per-thread state
135
+ struct per_thread_context {
136
+ char thread_id[THREAD_ID_LIMIT_CHARS];
137
+ ddog_CharSlice thread_id_char_slice;
138
+ char thread_invoke_location[THREAD_INVOKE_LOCATION_LIMIT_CHARS];
139
+ ddog_CharSlice thread_invoke_location_char_slice;
140
+ thread_cpu_time_id thread_cpu_time_id;
141
+ long cpu_time_at_previous_sample_ns; // Can be INVALID_TIME until initialized or if getting it fails for another reason
142
+ long wall_time_at_previous_sample_ns; // Can be INVALID_TIME until initialized
143
+
144
+ struct {
145
+ // Both of these fields are set by on_gc_start and kept until on_gc_finish is called.
146
+ // Outside of this window, they will be INVALID_TIME.
147
+ long cpu_time_at_start_ns;
148
+ long wall_time_at_start_ns;
149
+ } gc_tracking;
150
+ };
151
+
152
+ // Used to correlate profiles with traces
153
+ struct trace_identifiers {
154
+ bool valid;
155
+ uint64_t local_root_span_id;
156
+ uint64_t span_id;
157
+ VALUE trace_endpoint;
158
+ };
159
+
160
+ static void thread_context_collector_typed_data_mark(void *state_ptr);
161
+ static void thread_context_collector_typed_data_free(void *state_ptr);
162
+ static int hash_map_per_thread_context_mark(st_data_t key_thread, st_data_t _value, st_data_t _argument);
163
+ static int hash_map_per_thread_context_free_values(st_data_t _thread, st_data_t value_per_thread_context, st_data_t _argument);
164
+ static VALUE _native_new(VALUE klass);
165
+ static VALUE _native_initialize(
166
+ VALUE self,
167
+ VALUE collector_instance,
168
+ VALUE recorder_instance,
169
+ VALUE max_frames,
170
+ VALUE tracer_context_key,
171
+ VALUE endpoint_collection_enabled,
172
+ VALUE timeline_enabled,
173
+ VALUE allocation_type_enabled
174
+ );
175
+ static VALUE _native_sample(VALUE self, VALUE collector_instance, VALUE profiler_overhead_stack_thread);
176
+ static VALUE _native_on_gc_start(VALUE self, VALUE collector_instance);
177
+ static VALUE _native_on_gc_finish(VALUE self, VALUE collector_instance);
178
+ static VALUE _native_sample_after_gc(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
179
+ void update_metrics_and_sample(
180
+ struct thread_context_collector_state *state,
181
+ VALUE thread_being_sampled,
182
+ VALUE profiler_overhead_stack_thread,
183
+ struct per_thread_context *thread_context,
184
+ long current_cpu_time_ns,
185
+ long current_monotonic_wall_time_ns
186
+ );
187
+ static void trigger_sample_for_thread(
188
+ struct thread_context_collector_state *state,
189
+ VALUE thread,
190
+ VALUE stack_from_thread,
191
+ struct per_thread_context *thread_context,
192
+ sample_values values,
193
+ long current_monotonic_wall_time_ns,
194
+ ddog_CharSlice *ruby_vm_type,
195
+ ddog_CharSlice *class_name
196
+ );
197
+ static VALUE _native_thread_list(VALUE self);
198
+ static struct per_thread_context *get_or_create_context_for(VALUE thread, struct thread_context_collector_state *state);
199
+ static struct per_thread_context *get_context_for(VALUE thread, struct thread_context_collector_state *state);
200
+ static void initialize_context(VALUE thread, struct per_thread_context *thread_context, struct thread_context_collector_state *state);
201
+ static VALUE _native_inspect(VALUE self, VALUE collector_instance);
202
+ static VALUE per_thread_context_st_table_as_ruby_hash(struct thread_context_collector_state *state);
203
+ static int per_thread_context_as_ruby_hash(st_data_t key_thread, st_data_t value_context, st_data_t result_hash);
204
+ static VALUE stats_as_ruby_hash(struct thread_context_collector_state *state);
205
+ static VALUE gc_tracking_as_ruby_hash(struct thread_context_collector_state *state);
206
+ static void remove_context_for_dead_threads(struct thread_context_collector_state *state);
207
+ static int remove_if_dead_thread(st_data_t key_thread, st_data_t value_context, st_data_t _argument);
208
+ static VALUE _native_per_thread_context(VALUE self, VALUE collector_instance);
209
+ static long update_time_since_previous_sample(long *time_at_previous_sample_ns, long current_time_ns, long gc_start_time_ns, bool is_wall_time);
210
+ static long cpu_time_now_ns(struct per_thread_context *thread_context);
211
+ static long thread_id_for(VALUE thread);
212
+ static VALUE _native_stats(VALUE self, VALUE collector_instance);
213
+ static VALUE _native_gc_tracking(VALUE self, VALUE collector_instance);
214
+ static void trace_identifiers_for(struct thread_context_collector_state *state, VALUE thread, struct trace_identifiers *trace_identifiers_result);
215
+ static bool should_collect_resource(VALUE root_span_type);
216
+ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
217
+ static VALUE thread_list(struct thread_context_collector_state *state);
218
+ static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object);
219
+ static VALUE _native_new_empty_thread(VALUE self);
220
+ static ddog_CharSlice ruby_value_type_to_class_name(enum ruby_value_type type);
221
+
222
+ void collectors_thread_context_init(VALUE profiling_module) {
223
+ VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
224
+ VALUE collectors_thread_context_class = rb_define_class_under(collectors_module, "ThreadContext", rb_cObject);
225
+ // Hosts methods used for testing the native code using RSpec
226
+ VALUE testing_module = rb_define_module_under(collectors_thread_context_class, "Testing");
227
+
228
+ // Instances of the ThreadContext class are "TypedData" objects.
229
+ // "TypedData" objects are special objects in the Ruby VM that can wrap C structs.
230
+ // In this case, it wraps the thread_context_collector_state.
231
+ //
232
+ // Because Ruby doesn't know how to initialize native-level structs, we MUST override the allocation function for objects
233
+ // of this class so that we can manage this part. Not overriding or disabling the allocation function is a common
234
+ // gotcha for "TypedData" objects that can very easily lead to VM crashes, see for instance
235
+ // https://bugs.ruby-lang.org/issues/18007 for a discussion around this.
236
+ rb_define_alloc_func(collectors_thread_context_class, _native_new);
237
+
238
+ rb_define_singleton_method(collectors_thread_context_class, "_native_initialize", _native_initialize, 7);
239
+ rb_define_singleton_method(collectors_thread_context_class, "_native_inspect", _native_inspect, 1);
240
+ rb_define_singleton_method(collectors_thread_context_class, "_native_reset_after_fork", _native_reset_after_fork, 1);
241
+ rb_define_singleton_method(testing_module, "_native_sample", _native_sample, 2);
242
+ rb_define_singleton_method(testing_module, "_native_sample_allocation", _native_sample_allocation, 3);
243
+ rb_define_singleton_method(testing_module, "_native_on_gc_start", _native_on_gc_start, 1);
244
+ rb_define_singleton_method(testing_module, "_native_on_gc_finish", _native_on_gc_finish, 1);
245
+ rb_define_singleton_method(testing_module, "_native_sample_after_gc", _native_sample_after_gc, 1);
246
+ rb_define_singleton_method(testing_module, "_native_thread_list", _native_thread_list, 0);
247
+ rb_define_singleton_method(testing_module, "_native_per_thread_context", _native_per_thread_context, 1);
248
+ rb_define_singleton_method(testing_module, "_native_stats", _native_stats, 1);
249
+ rb_define_singleton_method(testing_module, "_native_gc_tracking", _native_gc_tracking, 1);
250
+ rb_define_singleton_method(testing_module, "_native_new_empty_thread", _native_new_empty_thread, 0);
251
+
252
+ at_active_span_id = rb_intern_const("@active_span");
253
+ at_active_trace_id = rb_intern_const("@active_trace");
254
+ at_id_id = rb_intern_const("@id");
255
+ at_resource_id = rb_intern_const("@resource");
256
+ at_root_span_id = rb_intern_const("@root_span");
257
+ at_type_id = rb_intern_const("@type");
258
+
259
+ gc_profiling_init();
260
+ }
261
+
262
+ // This structure is used to define a Ruby object that stores a pointer to a struct thread_context_collector_state
263
+ // See also https://github.com/ruby/ruby/blob/master/doc/extension.rdoc for how this works
264
+ static const rb_data_type_t thread_context_collector_typed_data = {
265
+ .wrap_struct_name = "Datadog::Profiling::Collectors::ThreadContext",
266
+ .function = {
267
+ .dmark = thread_context_collector_typed_data_mark,
268
+ .dfree = thread_context_collector_typed_data_free,
269
+ .dsize = NULL, // We don't track profile memory usage (although it'd be cool if we did!)
270
+ //.dcompact = NULL, // FIXME: Add support for compaction
271
+ },
272
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
273
+ };
274
+
275
+ // This function is called by the Ruby GC to give us a chance to mark any Ruby objects that we're holding on to,
276
+ // so that they don't get garbage collected
277
+ static void thread_context_collector_typed_data_mark(void *state_ptr) {
278
+ struct thread_context_collector_state *state = (struct thread_context_collector_state *) state_ptr;
279
+
280
+ // Update this when modifying state struct
281
+ rb_gc_mark(state->recorder_instance);
282
+ st_foreach(state->hash_map_per_thread_context, hash_map_per_thread_context_mark, 0 /* unused */);
283
+ rb_gc_mark(state->thread_list_buffer);
284
+ rb_gc_mark(state->main_thread);
285
+ }
286
+
287
+ static void thread_context_collector_typed_data_free(void *state_ptr) {
288
+ struct thread_context_collector_state *state = (struct thread_context_collector_state *) state_ptr;
289
+
290
+ // Update this when modifying state struct
291
+
292
+ // Important: Remember that we're only guaranteed to see here what's been set in _native_new, aka
293
+ // pointers that have been set NULL there may still be NULL here.
294
+ if (state->sampling_buffer != NULL) sampling_buffer_free(state->sampling_buffer);
295
+
296
+ // Free each entry in the map
297
+ st_foreach(state->hash_map_per_thread_context, hash_map_per_thread_context_free_values, 0 /* unused */);
298
+ // ...and then the map
299
+ st_free_table(state->hash_map_per_thread_context);
300
+
301
+ ruby_xfree(state);
302
+ }
303
+
304
+ // Mark Ruby thread references we keep as keys in hash_map_per_thread_context
305
+ static int hash_map_per_thread_context_mark(st_data_t key_thread, DDTRACE_UNUSED st_data_t _value, DDTRACE_UNUSED st_data_t _argument) {
306
+ VALUE thread = (VALUE) key_thread;
307
+ rb_gc_mark(thread);
308
+ return ST_CONTINUE;
309
+ }
310
+
311
+ // Used to clear each of the per_thread_contexts inside the hash_map_per_thread_context
312
+ static int hash_map_per_thread_context_free_values(DDTRACE_UNUSED st_data_t _thread, st_data_t value_per_thread_context, DDTRACE_UNUSED st_data_t _argument) {
313
+ struct per_thread_context *per_thread_context = (struct per_thread_context*) value_per_thread_context;
314
+ ruby_xfree(per_thread_context);
315
+ return ST_CONTINUE;
316
+ }
317
+
318
+ static VALUE _native_new(VALUE klass) {
319
+ struct thread_context_collector_state *state = ruby_xcalloc(1, sizeof(struct thread_context_collector_state));
320
+
321
+ // Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
322
+ // being leaked.
323
+
324
+ // Update this when modifying state struct
325
+ state->sampling_buffer = NULL;
326
+ state->hash_map_per_thread_context =
327
+ // "numtable" is an awful name, but TL;DR it's what should be used when keys are `VALUE`s.
328
+ st_init_numtable();
329
+ state->recorder_instance = Qnil;
330
+ state->tracer_context_key = MISSING_TRACER_CONTEXT_KEY;
331
+ state->thread_list_buffer = rb_ary_new();
332
+ state->endpoint_collection_enabled = true;
333
+ state->timeline_enabled = true;
334
+ state->allocation_type_enabled = true;
335
+ state->time_converter_state = (monotonic_to_system_epoch_state) MONOTONIC_TO_SYSTEM_EPOCH_INITIALIZER;
336
+ state->main_thread = rb_thread_main();
337
+ state->gc_tracking.wall_time_at_previous_gc_ns = INVALID_TIME;
338
+ state->gc_tracking.wall_time_at_last_flushed_gc_event_ns = 0;
339
+
340
+ return TypedData_Wrap_Struct(klass, &thread_context_collector_typed_data, state);
341
+ }
342
+
343
+ static VALUE _native_initialize(
344
+ DDTRACE_UNUSED VALUE _self,
345
+ VALUE collector_instance,
346
+ VALUE recorder_instance,
347
+ VALUE max_frames,
348
+ VALUE tracer_context_key,
349
+ VALUE endpoint_collection_enabled,
350
+ VALUE timeline_enabled,
351
+ VALUE allocation_type_enabled
352
+ ) {
353
+ ENFORCE_BOOLEAN(endpoint_collection_enabled);
354
+ ENFORCE_BOOLEAN(timeline_enabled);
355
+ ENFORCE_BOOLEAN(allocation_type_enabled);
356
+
357
+ struct thread_context_collector_state *state;
358
+ TypedData_Get_Struct(collector_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
359
+
360
+ int max_frames_requested = NUM2INT(max_frames);
361
+ if (max_frames_requested < 0) rb_raise(rb_eArgError, "Invalid max_frames: value must not be negative");
362
+
363
+ // Update this when modifying state struct
364
+ state->sampling_buffer = sampling_buffer_new(max_frames_requested);
365
+ // hash_map_per_thread_context is already initialized, nothing to do here
366
+ state->recorder_instance = enforce_recorder_instance(recorder_instance);
367
+ state->endpoint_collection_enabled = (endpoint_collection_enabled == Qtrue);
368
+ state->timeline_enabled = (timeline_enabled == Qtrue);
369
+ state->allocation_type_enabled = (allocation_type_enabled == Qtrue);
370
+
371
+ if (RTEST(tracer_context_key)) {
372
+ ENFORCE_TYPE(tracer_context_key, T_SYMBOL);
373
+ // Note about rb_to_id and dynamic symbols: calling `rb_to_id` prevents symbols from ever being garbage collected.
374
+ // In this case, we can't really escape this because as of this writing, ruby master still calls `rb_to_id` inside
375
+ // the implementation of Thread#[]= so any symbol that gets used as a key there will already be prevented from GC.
376
+ state->tracer_context_key = rb_to_id(tracer_context_key);
377
+ }
378
+
379
+ return Qtrue;
380
+ }
381
+
382
+ // This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
383
+ // It SHOULD NOT be used for other purposes.
384
+ static VALUE _native_sample(DDTRACE_UNUSED VALUE _self, VALUE collector_instance, VALUE profiler_overhead_stack_thread) {
385
+ if (!is_thread_alive(profiler_overhead_stack_thread)) rb_raise(rb_eArgError, "Unexpected: profiler_overhead_stack_thread is not alive");
386
+
387
+ thread_context_collector_sample(collector_instance, monotonic_wall_time_now_ns(RAISE_ON_FAILURE), profiler_overhead_stack_thread);
388
+ return Qtrue;
389
+ }
390
+
391
+ // This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
392
+ // It SHOULD NOT be used for other purposes.
393
+ static VALUE _native_on_gc_start(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
394
+ thread_context_collector_on_gc_start(collector_instance);
395
+ return Qtrue;
396
+ }
397
+
398
+ // This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
399
+ // It SHOULD NOT be used for other purposes.
400
+ static VALUE _native_on_gc_finish(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
401
+ thread_context_collector_on_gc_finish(collector_instance);
402
+ return Qtrue;
403
+ }
404
+
405
+ // This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
406
+ // It SHOULD NOT be used for other purposes.
407
+ static VALUE _native_sample_after_gc(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
408
+ thread_context_collector_sample_after_gc(collector_instance);
409
+ return Qtrue;
410
+ }
411
+
412
+ // This function gets called from the Collectors::CpuAndWallTimeWorker to trigger the actual sampling.
413
+ //
414
+ // Assumption 1: This function is called in a thread that is holding the Global VM Lock. Caller is responsible for enforcing this.
415
+ // Assumption 2: This function is allowed to raise exceptions. Caller is responsible for handling them, if needed.
416
+ // Assumption 3: This function IS NOT called from a signal handler. This function is not async-signal-safe.
417
+ // Assumption 4: This function IS NOT called in a reentrant way.
418
+ // Assumption 5: This function is called from the main Ractor (if Ruby has support for Ractors).
419
+ //
420
+ // The `profiler_overhead_stack_thread` is used to attribute the profiler overhead to a stack borrowed from a different thread
421
+ // (belonging to ddtrace), so that the overhead is visible in the profile rather than blamed on user code.
422
+ void thread_context_collector_sample(VALUE self_instance, long current_monotonic_wall_time_ns, VALUE profiler_overhead_stack_thread) {
423
+ struct thread_context_collector_state *state;
424
+ TypedData_Get_Struct(self_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
425
+
426
+ VALUE current_thread = rb_thread_current();
427
+ struct per_thread_context *current_thread_context = get_or_create_context_for(current_thread, state);
428
+ long cpu_time_at_sample_start_for_current_thread = cpu_time_now_ns(current_thread_context);
429
+
430
+ VALUE threads = thread_list(state);
431
+
432
+ const long thread_count = RARRAY_LEN(threads);
433
+ for (long i = 0; i < thread_count; i++) {
434
+ VALUE thread = RARRAY_AREF(threads, i);
435
+ struct per_thread_context *thread_context = get_or_create_context_for(thread, state);
436
+
437
+ // We account for cpu-time for the current thread in a different way -- we use the cpu-time at sampling start, to avoid
438
+ // blaming the time the profiler took on whatever's running on the thread right now
439
+ long current_cpu_time_ns = thread != current_thread ? cpu_time_now_ns(thread_context) : cpu_time_at_sample_start_for_current_thread;
440
+
441
+ update_metrics_and_sample(
442
+ state,
443
+ /* thread_being_sampled: */ thread,
444
+ /* stack_from_thread: */ thread,
445
+ thread_context,
446
+ current_cpu_time_ns,
447
+ current_monotonic_wall_time_ns
448
+ );
449
+ }
450
+
451
+ state->sample_count++;
452
+
453
+ // TODO: This seems somewhat overkill and inefficient to do often; right now we just do it every few samples
454
+ // but there's probably a better way to do this if we actually track when threads finish
455
+ if (state->sample_count % 100 == 0) remove_context_for_dead_threads(state);
456
+
457
+ update_metrics_and_sample(
458
+ state,
459
+ /* thread_being_sampled: */ current_thread,
460
+ /* stack_from_thread: */ profiler_overhead_stack_thread,
461
+ current_thread_context,
462
+ cpu_time_now_ns(current_thread_context),
463
+ monotonic_wall_time_now_ns(RAISE_ON_FAILURE)
464
+ );
465
+ }
466
+
467
+ void update_metrics_and_sample(
468
+ struct thread_context_collector_state *state,
469
+ VALUE thread_being_sampled,
470
+ VALUE stack_from_thread, // This can be different when attributing profiler overhead using a different stack
471
+ struct per_thread_context *thread_context,
472
+ long current_cpu_time_ns,
473
+ long current_monotonic_wall_time_ns
474
+ ) {
475
+ long cpu_time_elapsed_ns = update_time_since_previous_sample(
476
+ &thread_context->cpu_time_at_previous_sample_ns,
477
+ current_cpu_time_ns,
478
+ thread_context->gc_tracking.cpu_time_at_start_ns,
479
+ IS_NOT_WALL_TIME
480
+ );
481
+ long wall_time_elapsed_ns = update_time_since_previous_sample(
482
+ &thread_context->wall_time_at_previous_sample_ns,
483
+ current_monotonic_wall_time_ns,
484
+ // We explicitly pass in `INVALID_TIME` as an argument for `gc_start_time_ns` here because we don't want wall-time
485
+ // accounting to change during GC.
486
+ // E.g. if 60 seconds pass in the real world, 60 seconds of wall-time are recorded, regardless of the thread doing
487
+ // GC or not.
488
+ INVALID_TIME,
489
+ IS_WALL_TIME
490
+ );
491
+
492
+ trigger_sample_for_thread(
493
+ state,
494
+ thread_being_sampled,
495
+ stack_from_thread,
496
+ thread_context,
497
+ (sample_values) {.cpu_time_ns = cpu_time_elapsed_ns, .cpu_or_wall_samples = 1, .wall_time_ns = wall_time_elapsed_ns},
498
+ current_monotonic_wall_time_ns,
499
+ NULL,
500
+ NULL
501
+ );
502
+ }
503
+
504
+ // This function gets called when Ruby is about to start running the Garbage Collector on the current thread.
505
+ // It updates the per_thread_context of the current thread to include the current cpu/wall times, to be used to later
506
+ // create an event including the cpu/wall time spent in garbage collector work.
507
+ //
508
+ // Safety: This function gets called while Ruby is doing garbage collection. While Ruby is doing garbage collection,
509
+ // *NO ALLOCATION* is allowed. This function, and any it calls must never trigger memory or object allocation.
510
+ // This includes exceptions and use of ruby_xcalloc (because xcalloc can trigger GC)!
511
+ //
512
+ // Assumption 1: This function is called in a thread that is holding the Global VM Lock. Caller is responsible for enforcing this.
513
+ // Assumption 2: This function is called from the main Ractor (if Ruby has support for Ractors).
514
+ void thread_context_collector_on_gc_start(VALUE self_instance) {
515
+ struct thread_context_collector_state *state;
516
+ if (!rb_typeddata_is_kind_of(self_instance, &thread_context_collector_typed_data)) return;
517
+ // This should never fail the the above check passes
518
+ TypedData_Get_Struct(self_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
519
+
520
+ struct per_thread_context *thread_context = get_context_for(rb_thread_current(), state);
521
+
522
+ // If there was no previously-existing context for this thread, we won't allocate one (see safety). For now we just drop
523
+ // the GC sample, under the assumption that "a thread that is so new that we never sampled it even once before it triggers
524
+ // GC" is a rare enough case that we can just ignore it.
525
+ // We can always improve this later if we find that this happens often (and we have the counter to help us figure that out)!
526
+ if (thread_context == NULL) {
527
+ state->stats.gc_samples_missed_due_to_missing_context++;
528
+ return;
529
+ }
530
+
531
+ // Here we record the wall-time first and in on_gc_finish we record it second to try to avoid having wall-time be slightly < cpu-time
532
+ thread_context->gc_tracking.wall_time_at_start_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
533
+ thread_context->gc_tracking.cpu_time_at_start_ns = cpu_time_now_ns(thread_context);
534
+ }
535
+
536
+ // This function gets called when Ruby has finished running the Garbage Collector on the current thread.
537
+ // It records the cpu/wall-time observed during GC, which will be used to later
538
+ // create an event including the cpu/wall time spent from the start of garbage collector work until now.
539
+ //
540
+ // Safety: This function gets called while Ruby is doing garbage collection. While Ruby is doing garbage collection,
541
+ // *NO ALLOCATION* is allowed. This function, and any it calls must never trigger memory or object allocation.
542
+ // This includes exceptions and use of ruby_xcalloc (because xcalloc can trigger GC)!
543
+ //
544
+ // Assumption 1: This function is called in a thread that is holding the Global VM Lock. Caller is responsible for enforcing this.
545
+ // Assumption 2: This function is called from the main Ractor (if Ruby has support for Ractors).
546
+ bool thread_context_collector_on_gc_finish(VALUE self_instance) {
547
+ struct thread_context_collector_state *state;
548
+ if (!rb_typeddata_is_kind_of(self_instance, &thread_context_collector_typed_data)) return false;
549
+ // This should never fail the the above check passes
550
+ TypedData_Get_Struct(self_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
551
+
552
+ struct per_thread_context *thread_context = get_context_for(rb_thread_current(), state);
553
+
554
+ // If there was no previously-existing context for this thread, we won't allocate one (see safety). We keep a metric for
555
+ // how often this happens -- see on_gc_start.
556
+ if (thread_context == NULL) return false;
557
+
558
+ long cpu_time_at_start_ns = thread_context->gc_tracking.cpu_time_at_start_ns;
559
+ long wall_time_at_start_ns = thread_context->gc_tracking.wall_time_at_start_ns;
560
+
561
+ if (cpu_time_at_start_ns == INVALID_TIME && wall_time_at_start_ns == INVALID_TIME) {
562
+ // If this happened, it means that on_gc_start was either never called for the thread OR it was called but no thread
563
+ // context existed at the time. The former can be the result of a bug, but since we can't distinguish them, we just
564
+ // do nothing.
565
+ return false;
566
+ }
567
+
568
+ // Mark thread as no longer in GC
569
+ thread_context->gc_tracking.cpu_time_at_start_ns = INVALID_TIME;
570
+ thread_context->gc_tracking.wall_time_at_start_ns = INVALID_TIME;
571
+
572
+ // Here we record the wall-time second and in on_gc_start we record it first to try to avoid having wall-time be slightly < cpu-time
573
+ long cpu_time_at_finish_ns = cpu_time_now_ns(thread_context);
574
+ long wall_time_at_finish_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
575
+
576
+ // If our end timestamp is not OK, we bail out
577
+ if (wall_time_at_finish_ns == 0) return false;
578
+
579
+ long gc_cpu_time_elapsed_ns = cpu_time_at_finish_ns - cpu_time_at_start_ns;
580
+ long gc_wall_time_elapsed_ns = wall_time_at_finish_ns - wall_time_at_start_ns;
581
+
582
+ // Wall-time can go backwards if the system clock gets changed (and we observed spurious jumps back on macOS as well)
583
+ // so let's ensure we don't get negative values for time deltas.
584
+ gc_cpu_time_elapsed_ns = long_max_of(gc_cpu_time_elapsed_ns, 0);
585
+ gc_wall_time_elapsed_ns = long_max_of(gc_wall_time_elapsed_ns, 0);
586
+
587
+ if (state->gc_tracking.wall_time_at_previous_gc_ns == INVALID_TIME) {
588
+ state->gc_tracking.accumulated_cpu_time_ns = 0;
589
+ state->gc_tracking.accumulated_wall_time_ns = 0;
590
+ }
591
+
592
+ state->gc_tracking.accumulated_cpu_time_ns += gc_cpu_time_elapsed_ns;
593
+ state->gc_tracking.accumulated_wall_time_ns += gc_wall_time_elapsed_ns;
594
+ state->gc_tracking.wall_time_at_previous_gc_ns = wall_time_at_finish_ns;
595
+
596
+ // Update cpu-time accounting so it doesn't include the cpu-time spent in GC during the next sample
597
+ // We don't update the wall-time because we don't subtract the wall-time spent in GC (see call to
598
+ // `update_time_since_previous_sample` for wall-time in `update_metrics_and_sample`).
599
+ if (thread_context->cpu_time_at_previous_sample_ns != INVALID_TIME) {
600
+ thread_context->cpu_time_at_previous_sample_ns += gc_cpu_time_elapsed_ns;
601
+ }
602
+
603
+ // Let the caller know if it should schedule a flush or not. Returning true every time would cause a lot of overhead
604
+ // on the application (see GC tracking introduction at the top of the file), so instead we try to accumulate a few
605
+ // samples first.
606
+ bool finished_major_gc = gc_profiling_has_major_gc_finished();
607
+ bool over_flush_time_treshold =
608
+ (wall_time_at_finish_ns - state->gc_tracking.wall_time_at_last_flushed_gc_event_ns) >= TIME_BETWEEN_GC_EVENTS_NS;
609
+
610
+ return finished_major_gc || over_flush_time_treshold;
611
+ }
612
+
613
+ // This function gets called after one or more GC work steps (calls to on_gc_start/on_gc_finish).
614
+ // It creates a new sample including the cpu and wall-time spent by the garbage collector work, and resets any
615
+ // GC-related tracking.
616
+ //
617
+ // Assumption 1: This function is called in a thread that is holding the Global VM Lock. Caller is responsible for enforcing this.
618
+ // Assumption 2: This function is allowed to raise exceptions. Caller is responsible for handling them, if needed.
619
+ // Assumption 3: Unlike `on_gc_start` and `on_gc_finish`, this method is allowed to allocate memory as needed.
620
+ // Assumption 4: This function is called from the main Ractor (if Ruby has support for Ractors).
621
+ VALUE thread_context_collector_sample_after_gc(VALUE self_instance) {
622
+ struct thread_context_collector_state *state;
623
+ TypedData_Get_Struct(self_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
624
+
625
+ if (state->gc_tracking.wall_time_at_previous_gc_ns == INVALID_TIME) {
626
+ rb_raise(rb_eRuntimeError, "BUG: Unexpected call to sample_after_gc without valid GC information available");
627
+ }
628
+
629
+ int max_labels_needed_for_gc = 7; // Magic number gets validated inside gc_profiling_set_metadata
630
+ ddog_prof_Label labels[max_labels_needed_for_gc];
631
+ uint8_t label_pos = gc_profiling_set_metadata(labels, max_labels_needed_for_gc);
632
+
633
+ ddog_prof_Slice_Label slice_labels = {.ptr = labels, .len = label_pos};
634
+
635
+ // The end_timestamp_ns is treated specially by libdatadog and that's why it's not added as a ddog_prof_Label
636
+ int64_t end_timestamp_ns = 0;
637
+
638
+ if (state->timeline_enabled) {
639
+ end_timestamp_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, state->gc_tracking.wall_time_at_previous_gc_ns);
640
+ }
641
+
642
+ record_placeholder_stack(
643
+ state->sampling_buffer,
644
+ state->recorder_instance,
645
+ (sample_values) {
646
+ // This event gets both a regular cpu/wall-time duration, as a normal cpu/wall-time sample would, as well as a
647
+ // timeline duration.
648
+ // This is done to enable two use-cases:
649
+ // * regular cpu/wall-time makes this event show up as a regular stack in the flamegraph
650
+ // * the timeline duration is used when the event shows up in the timeline
651
+ .cpu_time_ns = state->gc_tracking.accumulated_cpu_time_ns,
652
+ .cpu_or_wall_samples = 1,
653
+ .wall_time_ns = state->gc_tracking.accumulated_wall_time_ns,
654
+ .timeline_wall_time_ns = state->gc_tracking.accumulated_wall_time_ns,
655
+ },
656
+ (sample_labels) {.labels = slice_labels, .state_label = NULL, .end_timestamp_ns = end_timestamp_ns},
657
+ DDOG_CHARSLICE_C("Garbage Collection")
658
+ );
659
+
660
+ state->gc_tracking.wall_time_at_last_flushed_gc_event_ns = state->gc_tracking.wall_time_at_previous_gc_ns;
661
+ state->gc_tracking.wall_time_at_previous_gc_ns = INVALID_TIME;
662
+
663
+ state->stats.gc_samples++;
664
+
665
+ // Return a VALUE to make it easier to call this function from Ruby APIs that expect a return value (such as rb_rescue2)
666
+ return Qnil;
667
+ }
668
+
669
+ static void trigger_sample_for_thread(
670
+ struct thread_context_collector_state *state,
671
+ VALUE thread,
672
+ VALUE stack_from_thread, // This can be different when attributing profiler overhead using a different stack
673
+ struct per_thread_context *thread_context,
674
+ sample_values values,
675
+ long current_monotonic_wall_time_ns,
676
+ // These two labels are only used for allocation profiling; @ivoanjo: may want to refactor this at some point?
677
+ ddog_CharSlice *ruby_vm_type,
678
+ ddog_CharSlice *class_name
679
+ ) {
680
+ int max_label_count =
681
+ 1 + // thread id
682
+ 1 + // thread name
683
+ 1 + // profiler overhead
684
+ 2 + // ruby vm type and allocation class
685
+ 1 + // state (only set for cpu/wall-time samples)
686
+ 2; // local root span id and span id
687
+ ddog_prof_Label labels[max_label_count];
688
+ int label_pos = 0;
689
+
690
+ labels[label_pos++] = (ddog_prof_Label) {
691
+ .key = DDOG_CHARSLICE_C("thread id"),
692
+ .str = thread_context->thread_id_char_slice
693
+ };
694
+
695
+ VALUE thread_name = thread_name_for(thread);
696
+ if (thread_name != Qnil) {
697
+ labels[label_pos++] = (ddog_prof_Label) {
698
+ .key = DDOG_CHARSLICE_C("thread name"),
699
+ .str = char_slice_from_ruby_string(thread_name)
700
+ };
701
+ } else if (thread == state->main_thread) { // Threads are often not named, but we can have a nice fallback for this special thread
702
+ ddog_CharSlice main_thread_name = DDOG_CHARSLICE_C("main");
703
+ labels[label_pos++] = (ddog_prof_Label) {
704
+ .key = DDOG_CHARSLICE_C("thread name"),
705
+ .str = main_thread_name
706
+ };
707
+ } else {
708
+ // For other threads without name, we use the "invoke location" (first file:line of the block used to start the thread), if any.
709
+ // This is what Ruby shows in `Thread#to_s`.
710
+ labels[label_pos++] = (ddog_prof_Label) {
711
+ .key = DDOG_CHARSLICE_C("thread name"),
712
+ .str = thread_context->thread_invoke_location_char_slice // This is an empty string if no invoke location was available
713
+ };
714
+ }
715
+
716
+ struct trace_identifiers trace_identifiers_result = {.valid = false, .trace_endpoint = Qnil};
717
+ trace_identifiers_for(state, thread, &trace_identifiers_result);
718
+
719
+ if (trace_identifiers_result.valid) {
720
+ labels[label_pos++] = (ddog_prof_Label) {.key = DDOG_CHARSLICE_C("local root span id"), .num = trace_identifiers_result.local_root_span_id};
721
+ labels[label_pos++] = (ddog_prof_Label) {.key = DDOG_CHARSLICE_C("span id"), .num = trace_identifiers_result.span_id};
722
+
723
+ if (trace_identifiers_result.trace_endpoint != Qnil) {
724
+ // The endpoint gets recorded in a different way because it is mutable in the tracer and can change during a
725
+ // trace.
726
+ //
727
+ // Instead of each sample for the same local_root_span_id getting a potentially-different endpoint,
728
+ // `record_endpoint` (via libdatadog) keeps a list of local_root_span_id values and their most-recently-seen
729
+ // endpoint values, and at serialization time the most-recently-seen endpoint is applied to all relevant samples.
730
+ //
731
+ // This is why the endpoint is not directly added in this function to the labels array, although it will later
732
+ // show up in the array in the output pprof.
733
+ record_endpoint(
734
+ state->recorder_instance,
735
+ trace_identifiers_result.local_root_span_id,
736
+ char_slice_from_ruby_string(trace_identifiers_result.trace_endpoint)
737
+ );
738
+ }
739
+ }
740
+
741
+ if (thread != stack_from_thread) {
742
+ labels[label_pos++] = (ddog_prof_Label) {
743
+ .key = DDOG_CHARSLICE_C("profiler overhead"),
744
+ .num = 1
745
+ };
746
+ }
747
+
748
+ if (ruby_vm_type != NULL) {
749
+ labels[label_pos++] = (ddog_prof_Label) {
750
+ .key = DDOG_CHARSLICE_C("ruby vm type"),
751
+ .str = *ruby_vm_type
752
+ };
753
+ }
754
+
755
+ if (class_name != NULL) {
756
+ labels[label_pos++] = (ddog_prof_Label) {
757
+ .key = DDOG_CHARSLICE_C("allocation class"),
758
+ .str = *class_name
759
+ };
760
+ }
761
+
762
+ // This label is handled specially:
763
+ // 1. It's only set for cpu/wall-time samples
764
+ // 2. We set it here to its default state of "unknown", but the `Collectors::Stack` may choose to override it with
765
+ // something more interesting.
766
+ ddog_prof_Label *state_label = NULL;
767
+ if (values.cpu_or_wall_samples > 0) {
768
+ state_label = &labels[label_pos++];
769
+ *state_label = (ddog_prof_Label) {
770
+ .key = DDOG_CHARSLICE_C("state"),
771
+ .str = DDOG_CHARSLICE_C("unknown"),
772
+ .num = 0, // This shouldn't be needed but the tracer-2.7 docker image ships a buggy gcc that complains about this
773
+ };
774
+ }
775
+
776
+ // The number of times `label_pos++` shows up in this function needs to match `max_label_count`. To avoid "oops I
777
+ // forgot to update max_label_count" in the future, we've also added this validation.
778
+ // @ivoanjo: I wonder if C compilers are smart enough to statically prove this check never triggers unless someone
779
+ // changes the code erroneously and remove it entirely?
780
+ if (label_pos > max_label_count) {
781
+ rb_raise(rb_eRuntimeError, "BUG: Unexpected label_pos (%d) > max_label_count (%d)", label_pos, max_label_count);
782
+ }
783
+
784
+ ddog_prof_Slice_Label slice_labels = {.ptr = labels, .len = label_pos};
785
+
786
+ // The end_timestamp_ns is treated specially by libdatadog and that's why it's not added as a ddog_prof_Label
787
+ int64_t end_timestamp_ns = 0;
788
+ if (state->timeline_enabled && current_monotonic_wall_time_ns != INVALID_TIME) {
789
+ end_timestamp_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, current_monotonic_wall_time_ns);
790
+ }
791
+
792
+ sample_thread(
793
+ stack_from_thread,
794
+ state->sampling_buffer,
795
+ state->recorder_instance,
796
+ values,
797
+ (sample_labels) {.labels = slice_labels, .state_label = state_label, .end_timestamp_ns = end_timestamp_ns}
798
+ );
799
+ }
800
+
801
+ // This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
802
+ // It SHOULD NOT be used for other purposes.
803
+ static VALUE _native_thread_list(DDTRACE_UNUSED VALUE _self) {
804
+ VALUE result = rb_ary_new();
805
+ ddtrace_thread_list(result);
806
+ return result;
807
+ }
808
+
809
+ static struct per_thread_context *get_or_create_context_for(VALUE thread, struct thread_context_collector_state *state) {
810
+ struct per_thread_context* thread_context = NULL;
811
+ st_data_t value_context = 0;
812
+
813
+ if (st_lookup(state->hash_map_per_thread_context, (st_data_t) thread, &value_context)) {
814
+ thread_context = (struct per_thread_context*) value_context;
815
+ } else {
816
+ thread_context = ruby_xcalloc(1, sizeof(struct per_thread_context));
817
+ initialize_context(thread, thread_context, state);
818
+ st_insert(state->hash_map_per_thread_context, (st_data_t) thread, (st_data_t) thread_context);
819
+ }
820
+
821
+ return thread_context;
822
+ }
823
+
824
+ static struct per_thread_context *get_context_for(VALUE thread, struct thread_context_collector_state *state) {
825
+ struct per_thread_context* thread_context = NULL;
826
+ st_data_t value_context = 0;
827
+
828
+ if (st_lookup(state->hash_map_per_thread_context, (st_data_t) thread, &value_context)) {
829
+ thread_context = (struct per_thread_context*) value_context;
830
+ }
831
+
832
+ return thread_context;
833
+ }
834
+
835
+ #define LOGGING_GEM_PATH "/lib/logging/diagnostic_context.rb"
836
+
837
+ // The `logging` gem monkey patches thread creation, which makes the `invoke_location_for` useless, since every thread
838
+ // will point to the `logging` gem. When that happens, we avoid using the invoke location.
839
+ //
840
+ // TODO: This approach is a bit brittle, since it matches on the specific gem path, and only works for the `logging`
841
+ // gem.
842
+ // In the future we should probably explore a more generic fix (e.g. using Thread.method(:new).source_location or
843
+ // something like that to detect redefinition of the `Thread` methods). One difficulty of doing it is that we need
844
+ // to either run Ruby code during sampling (not great), or otherwise use some of the VM private APIs to detect this.
845
+ //
846
+ static bool is_logging_gem_monkey_patch(VALUE invoke_file_location) {
847
+ int logging_gem_path_len = strlen(LOGGING_GEM_PATH);
848
+ char *invoke_file = StringValueCStr(invoke_file_location);
849
+ int invoke_file_len = strlen(invoke_file);
850
+
851
+ if (invoke_file_len < logging_gem_path_len) return false;
852
+
853
+ return strncmp(invoke_file + invoke_file_len - logging_gem_path_len, LOGGING_GEM_PATH, logging_gem_path_len) == 0;
854
+ }
855
+
856
+ static void initialize_context(VALUE thread, struct per_thread_context *thread_context, struct thread_context_collector_state *state) {
857
+ snprintf(thread_context->thread_id, THREAD_ID_LIMIT_CHARS, "%"PRIu64" (%lu)", native_thread_id_for(thread), (unsigned long) thread_id_for(thread));
858
+ thread_context->thread_id_char_slice = (ddog_CharSlice) {.ptr = thread_context->thread_id, .len = strlen(thread_context->thread_id)};
859
+
860
+ int invoke_line_location;
861
+ VALUE invoke_file_location = invoke_location_for(thread, &invoke_line_location);
862
+ if (invoke_file_location != Qnil) {
863
+ if (!is_logging_gem_monkey_patch(invoke_file_location)) {
864
+ snprintf(
865
+ thread_context->thread_invoke_location,
866
+ THREAD_INVOKE_LOCATION_LIMIT_CHARS,
867
+ "%s:%d",
868
+ StringValueCStr(invoke_file_location),
869
+ invoke_line_location
870
+ );
871
+ } else {
872
+ snprintf(thread_context->thread_invoke_location, THREAD_INVOKE_LOCATION_LIMIT_CHARS, "%s", "(Unnamed thread)");
873
+ }
874
+ } else if (thread != state->main_thread) {
875
+ // If the first function of a thread is native code, there won't be an invoke location, so we use this fallback.
876
+ // NOTE: In the future, I wonder if we could take the pointer to the native function, and try to see if there's a native
877
+ // symbol attached to it.
878
+ snprintf(thread_context->thread_invoke_location, THREAD_INVOKE_LOCATION_LIMIT_CHARS, "%s", "(Unnamed thread from native code)");
879
+ }
880
+
881
+ thread_context->thread_invoke_location_char_slice = (ddog_CharSlice) {
882
+ .ptr = thread_context->thread_invoke_location,
883
+ .len = strlen(thread_context->thread_invoke_location)
884
+ };
885
+
886
+ thread_context->thread_cpu_time_id = thread_cpu_time_id_for(thread);
887
+
888
+ // These will get initialized during actual sampling
889
+ thread_context->cpu_time_at_previous_sample_ns = INVALID_TIME;
890
+ thread_context->wall_time_at_previous_sample_ns = INVALID_TIME;
891
+
892
+ // These will only be used during a GC operation
893
+ thread_context->gc_tracking.cpu_time_at_start_ns = INVALID_TIME;
894
+ thread_context->gc_tracking.wall_time_at_start_ns = INVALID_TIME;
895
+ }
896
+
897
+ static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
898
+ struct thread_context_collector_state *state;
899
+ TypedData_Get_Struct(collector_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
900
+
901
+ VALUE result = rb_str_new2(" (native state)");
902
+
903
+ // Update this when modifying state struct
904
+ rb_str_concat(result, rb_sprintf(" hash_map_per_thread_context=%"PRIsVALUE, per_thread_context_st_table_as_ruby_hash(state)));
905
+ rb_str_concat(result, rb_sprintf(" recorder_instance=%"PRIsVALUE, state->recorder_instance));
906
+ VALUE tracer_context_key = state->tracer_context_key == MISSING_TRACER_CONTEXT_KEY ? Qnil : ID2SYM(state->tracer_context_key);
907
+ rb_str_concat(result, rb_sprintf(" tracer_context_key=%+"PRIsVALUE, tracer_context_key));
908
+ rb_str_concat(result, rb_sprintf(" sample_count=%u", state->sample_count));
909
+ rb_str_concat(result, rb_sprintf(" stats=%"PRIsVALUE, stats_as_ruby_hash(state)));
910
+ rb_str_concat(result, rb_sprintf(" endpoint_collection_enabled=%"PRIsVALUE, state->endpoint_collection_enabled ? Qtrue : Qfalse));
911
+ rb_str_concat(result, rb_sprintf(" timeline_enabled=%"PRIsVALUE, state->timeline_enabled ? Qtrue : Qfalse));
912
+ rb_str_concat(result, rb_sprintf(" allocation_type_enabled=%"PRIsVALUE, state->allocation_type_enabled ? Qtrue : Qfalse));
913
+ rb_str_concat(result, rb_sprintf(
914
+ " time_converter_state={.system_epoch_ns_reference=%ld, .delta_to_epoch_ns=%ld}",
915
+ state->time_converter_state.system_epoch_ns_reference,
916
+ state->time_converter_state.delta_to_epoch_ns
917
+ ));
918
+ rb_str_concat(result, rb_sprintf(" main_thread=%"PRIsVALUE, state->main_thread));
919
+ rb_str_concat(result, rb_sprintf(" gc_tracking=%"PRIsVALUE, gc_tracking_as_ruby_hash(state)));
920
+
921
+ return result;
922
+ }
923
+
924
+ static VALUE per_thread_context_st_table_as_ruby_hash(struct thread_context_collector_state *state) {
925
+ VALUE result = rb_hash_new();
926
+ st_foreach(state->hash_map_per_thread_context, per_thread_context_as_ruby_hash, result);
927
+ return result;
928
+ }
929
+
930
+ static int per_thread_context_as_ruby_hash(st_data_t key_thread, st_data_t value_context, st_data_t result_hash) {
931
+ VALUE thread = (VALUE) key_thread;
932
+ struct per_thread_context *thread_context = (struct per_thread_context*) value_context;
933
+ VALUE result = (VALUE) result_hash;
934
+ VALUE context_as_hash = rb_hash_new();
935
+ rb_hash_aset(result, thread, context_as_hash);
936
+
937
+ VALUE arguments[] = {
938
+ ID2SYM(rb_intern("thread_id")), /* => */ rb_str_new2(thread_context->thread_id),
939
+ ID2SYM(rb_intern("thread_invoke_location")), /* => */ rb_str_new2(thread_context->thread_invoke_location),
940
+ ID2SYM(rb_intern("thread_cpu_time_id_valid?")), /* => */ thread_context->thread_cpu_time_id.valid ? Qtrue : Qfalse,
941
+ ID2SYM(rb_intern("thread_cpu_time_id")), /* => */ CLOCKID2NUM(thread_context->thread_cpu_time_id.clock_id),
942
+ ID2SYM(rb_intern("cpu_time_at_previous_sample_ns")), /* => */ LONG2NUM(thread_context->cpu_time_at_previous_sample_ns),
943
+ ID2SYM(rb_intern("wall_time_at_previous_sample_ns")), /* => */ LONG2NUM(thread_context->wall_time_at_previous_sample_ns),
944
+
945
+ ID2SYM(rb_intern("gc_tracking.cpu_time_at_start_ns")), /* => */ LONG2NUM(thread_context->gc_tracking.cpu_time_at_start_ns),
946
+ ID2SYM(rb_intern("gc_tracking.wall_time_at_start_ns")), /* => */ LONG2NUM(thread_context->gc_tracking.wall_time_at_start_ns),
947
+ };
948
+ for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(context_as_hash, arguments[i], arguments[i+1]);
949
+
950
+ return ST_CONTINUE;
951
+ }
952
+
953
+ static VALUE stats_as_ruby_hash(struct thread_context_collector_state *state) {
954
+ // Update this when modifying state struct (stats inner struct)
955
+ VALUE stats_as_hash = rb_hash_new();
956
+ VALUE arguments[] = {
957
+ ID2SYM(rb_intern("gc_samples")), /* => */ UINT2NUM(state->stats.gc_samples),
958
+ ID2SYM(rb_intern("gc_samples_missed_due_to_missing_context")), /* => */ UINT2NUM(state->stats.gc_samples_missed_due_to_missing_context),
959
+ };
960
+ for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(stats_as_hash, arguments[i], arguments[i+1]);
961
+ return stats_as_hash;
962
+ }
963
+
964
+ static VALUE gc_tracking_as_ruby_hash(struct thread_context_collector_state *state) {
965
+ // Update this when modifying state struct (gc_tracking inner struct)
966
+ VALUE result = rb_hash_new();
967
+ VALUE arguments[] = {
968
+ ID2SYM(rb_intern("accumulated_cpu_time_ns")), /* => */ ULONG2NUM(state->gc_tracking.accumulated_cpu_time_ns),
969
+ ID2SYM(rb_intern("accumulated_wall_time_ns")), /* => */ ULONG2NUM(state->gc_tracking.accumulated_wall_time_ns),
970
+ ID2SYM(rb_intern("wall_time_at_previous_gc_ns")), /* => */ LONG2NUM(state->gc_tracking.wall_time_at_previous_gc_ns),
971
+ ID2SYM(rb_intern("wall_time_at_last_flushed_gc_event_ns")), /* => */ LONG2NUM(state->gc_tracking.wall_time_at_last_flushed_gc_event_ns),
972
+ };
973
+ for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(result, arguments[i], arguments[i+1]);
974
+ return result;
975
+ }
976
+
977
+ static void remove_context_for_dead_threads(struct thread_context_collector_state *state) {
978
+ st_foreach(state->hash_map_per_thread_context, remove_if_dead_thread, 0 /* unused */);
979
+ }
980
+
981
+ static int remove_if_dead_thread(st_data_t key_thread, st_data_t value_context, DDTRACE_UNUSED st_data_t _argument) {
982
+ VALUE thread = (VALUE) key_thread;
983
+ struct per_thread_context* thread_context = (struct per_thread_context*) value_context;
984
+
985
+ if (is_thread_alive(thread)) return ST_CONTINUE;
986
+
987
+ ruby_xfree(thread_context);
988
+ return ST_DELETE;
989
+ }
990
+
991
+ // This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
992
+ // It SHOULD NOT be used for other purposes.
993
+ //
994
+ // Returns the whole contents of the per_thread_context structs being tracked.
995
+ static VALUE _native_per_thread_context(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
996
+ struct thread_context_collector_state *state;
997
+ TypedData_Get_Struct(collector_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
998
+
999
+ return per_thread_context_st_table_as_ruby_hash(state);
1000
+ }
1001
+
1002
+ static long update_time_since_previous_sample(long *time_at_previous_sample_ns, long current_time_ns, long gc_start_time_ns, bool is_wall_time) {
1003
+ // If we didn't have a time for the previous sample, we use the current one
1004
+ if (*time_at_previous_sample_ns == INVALID_TIME) *time_at_previous_sample_ns = current_time_ns;
1005
+
1006
+ bool is_thread_doing_gc = gc_start_time_ns != INVALID_TIME;
1007
+ long elapsed_time_ns = -1;
1008
+
1009
+ if (is_thread_doing_gc) {
1010
+ bool previous_sample_was_during_gc = gc_start_time_ns <= *time_at_previous_sample_ns;
1011
+
1012
+ if (previous_sample_was_during_gc) {
1013
+ elapsed_time_ns = 0; // No time to account for -- any time since the last sample is going to get assigned to GC separately
1014
+ } else {
1015
+ elapsed_time_ns = gc_start_time_ns - *time_at_previous_sample_ns; // Capture time between previous sample and start of GC only
1016
+ }
1017
+
1018
+ // Remaining time (from gc_start_time to current_time_ns) will be accounted for inside `sample_after_gc`
1019
+ *time_at_previous_sample_ns = gc_start_time_ns;
1020
+ } else {
1021
+ elapsed_time_ns = current_time_ns - *time_at_previous_sample_ns; // Capture all time since previous sample
1022
+ *time_at_previous_sample_ns = current_time_ns;
1023
+ }
1024
+
1025
+ if (elapsed_time_ns < 0) {
1026
+ if (is_wall_time) {
1027
+ // Wall-time can actually go backwards (e.g. when the system clock gets set) so we can't assume time going backwards
1028
+ // was a bug.
1029
+ // @ivoanjo: I've also observed time going backwards spuriously on macOS, see discussion on
1030
+ // https://github.com/DataDog/dd-trace-rb/pull/2336.
1031
+ elapsed_time_ns = 0;
1032
+ } else {
1033
+ // We don't expect non-wall time to go backwards, so let's flag this as a bug
1034
+ rb_raise(rb_eRuntimeError, "BUG: Unexpected negative elapsed_time_ns between samples");
1035
+ }
1036
+ }
1037
+
1038
+ return elapsed_time_ns;
1039
+ }
1040
+
1041
+ // Safety: This function is assumed never to raise exceptions by callers
1042
+ static long cpu_time_now_ns(struct per_thread_context *thread_context) {
1043
+ thread_cpu_time cpu_time = thread_cpu_time_for(thread_context->thread_cpu_time_id);
1044
+
1045
+ if (!cpu_time.valid) {
1046
+ // Invalidate previous state of the counter (if any), it's no longer accurate. We need to get two good reads
1047
+ // in a row to have an accurate delta.
1048
+ thread_context->cpu_time_at_previous_sample_ns = INVALID_TIME;
1049
+ return 0;
1050
+ }
1051
+
1052
+ return cpu_time.result_ns;
1053
+ }
1054
+
1055
+ static long thread_id_for(VALUE thread) {
1056
+ VALUE object_id = rb_obj_id(thread);
1057
+
1058
+ // The API docs for Ruby state that `rb_obj_id` COULD be a BIGNUM and that if you want to be really sure you don't
1059
+ // get a BIGNUM, then you should use `rb_memory_id`. But `rb_memory_id` is less interesting because it's less visible
1060
+ // at the user level than the result of calling `#object_id`.
1061
+ //
1062
+ // It also seems uncommon to me that we'd ever get a BIGNUM; on old Ruby versions (pre-GC compaction), the object id
1063
+ // was the pointer to the object, so that's not going to be a BIGNUM; on modern Ruby versions, Ruby keeps
1064
+ // a counter, and only increments it for objects for which `#object_id`/`rb_obj_id` is called (e.g. most objects
1065
+ // won't actually have an object id allocated).
1066
+ //
1067
+ // So, for now, let's simplify: we only support FIXNUMs, and we won't break if we get a BIGNUM; we just won't
1068
+ // record the thread_id (but samples will still be collected).
1069
+ return FIXNUM_P(object_id) ? FIX2LONG(object_id) : -1;
1070
+ }
1071
+
1072
+ VALUE enforce_thread_context_collector_instance(VALUE object) {
1073
+ Check_TypedStruct(object, &thread_context_collector_typed_data);
1074
+ return object;
1075
+ }
1076
+
1077
+ // This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
1078
+ // It SHOULD NOT be used for other purposes.
1079
+ static VALUE _native_stats(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
1080
+ struct thread_context_collector_state *state;
1081
+ TypedData_Get_Struct(collector_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
1082
+
1083
+ return stats_as_ruby_hash(state);
1084
+ }
1085
+
1086
+ // This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
1087
+ // It SHOULD NOT be used for other purposes.
1088
+ static VALUE _native_gc_tracking(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
1089
+ struct thread_context_collector_state *state;
1090
+ TypedData_Get_Struct(collector_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
1091
+
1092
+ return gc_tracking_as_ruby_hash(state);
1093
+ }
1094
+
1095
+ // Assumption 1: This function is called in a thread that is holding the Global VM Lock. Caller is responsible for enforcing this.
1096
+ static void trace_identifiers_for(struct thread_context_collector_state *state, VALUE thread, struct trace_identifiers *trace_identifiers_result) {
1097
+ if (state->tracer_context_key == MISSING_TRACER_CONTEXT_KEY) return;
1098
+
1099
+ VALUE current_context = rb_thread_local_aref(thread, state->tracer_context_key);
1100
+ if (current_context == Qnil) return;
1101
+
1102
+ VALUE active_trace = rb_ivar_get(current_context, at_active_trace_id /* @active_trace */);
1103
+ if (active_trace == Qnil) return;
1104
+
1105
+ VALUE root_span = rb_ivar_get(active_trace, at_root_span_id /* @root_span */);
1106
+ VALUE active_span = rb_ivar_get(active_trace, at_active_span_id /* @active_span */);
1107
+ if (root_span == Qnil || active_span == Qnil) return;
1108
+
1109
+ VALUE numeric_local_root_span_id = rb_ivar_get(root_span, at_id_id /* @id */);
1110
+ VALUE numeric_span_id = rb_ivar_get(active_span, at_id_id /* @id */);
1111
+ if (numeric_local_root_span_id == Qnil || numeric_span_id == Qnil) return;
1112
+
1113
+ trace_identifiers_result->local_root_span_id = NUM2ULL(numeric_local_root_span_id);
1114
+ trace_identifiers_result->span_id = NUM2ULL(numeric_span_id);
1115
+
1116
+ trace_identifiers_result->valid = true;
1117
+
1118
+ if (!state->endpoint_collection_enabled) return;
1119
+
1120
+ VALUE root_span_type = rb_ivar_get(root_span, at_type_id /* @type */);
1121
+ if (root_span_type == Qnil || !should_collect_resource(root_span_type)) return;
1122
+
1123
+ VALUE trace_resource = rb_ivar_get(active_trace, at_resource_id /* @resource */);
1124
+ if (RB_TYPE_P(trace_resource, T_STRING)) {
1125
+ trace_identifiers_result->trace_endpoint = trace_resource;
1126
+ } else if (trace_resource == Qnil) {
1127
+ // Fall back to resource from span, if any
1128
+ trace_identifiers_result->trace_endpoint = rb_ivar_get(root_span, at_resource_id /* @resource */);
1129
+ }
1130
+ }
1131
+
1132
+ // We only collect the resource for spans of types:
1133
+ // * 'web', for web requests
1134
+ // * proxy', used by the rack integration with request_queuing: true (e.g. also represents a web request)
1135
+ //
1136
+ // NOTE: Currently we're only interested in HTTP service endpoints. Over time, this list may be expanded.
1137
+ // Resources MUST NOT include personal identifiable information (PII); this should not be the case with
1138
+ // ddtrace integrations, but worth mentioning just in case :)
1139
+ static bool should_collect_resource(VALUE root_span_type) {
1140
+ ENFORCE_TYPE(root_span_type, T_STRING);
1141
+
1142
+ int root_span_type_length = RSTRING_LEN(root_span_type);
1143
+ const char *root_span_type_value = StringValuePtr(root_span_type);
1144
+
1145
+ return (root_span_type_length == strlen("web") && (memcmp("web", root_span_type_value, strlen("web")) == 0)) ||
1146
+ (root_span_type_length == strlen("proxy") && (memcmp("proxy", root_span_type_value, strlen("proxy")) == 0));
1147
+ }
1148
+
1149
+ // After the Ruby VM forks, this method gets called in the child process to clean up any leftover state from the parent.
1150
+ //
1151
+ // Assumption: This method gets called BEFORE restarting profiling -- e.g. there are no components attempting to
1152
+ // trigger samples at the same time.
1153
+ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
1154
+ struct thread_context_collector_state *state;
1155
+ TypedData_Get_Struct(collector_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
1156
+
1157
+ st_clear(state->hash_map_per_thread_context);
1158
+
1159
+ state->stats = (struct stats) {}; // Resets all stats back to zero
1160
+
1161
+ rb_funcall(state->recorder_instance, rb_intern("reset_after_fork"), 0);
1162
+
1163
+ return Qtrue;
1164
+ }
1165
+
1166
+ static VALUE thread_list(struct thread_context_collector_state *state) {
1167
+ VALUE result = state->thread_list_buffer;
1168
+ rb_ary_clear(result);
1169
+ ddtrace_thread_list(result);
1170
+ return result;
1171
+ }
1172
+
1173
+ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object) {
1174
+ struct thread_context_collector_state *state;
1175
+ TypedData_Get_Struct(self_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
1176
+
1177
+ VALUE current_thread = rb_thread_current();
1178
+
1179
+ enum ruby_value_type type = rb_type(new_object);
1180
+
1181
+ // Tag samples with the VM internal types
1182
+ ddog_CharSlice ruby_vm_type = ruby_value_type_to_char_slice(type);
1183
+
1184
+ // Since this is stack allocated, be careful about moving it
1185
+ ddog_CharSlice class_name;
1186
+ ddog_CharSlice *optional_class_name = NULL;
1187
+ char imemo_type[100];
1188
+
1189
+ if (state->allocation_type_enabled) {
1190
+ optional_class_name = &class_name;
1191
+
1192
+ if (
1193
+ type == RUBY_T_OBJECT ||
1194
+ type == RUBY_T_CLASS ||
1195
+ type == RUBY_T_MODULE ||
1196
+ type == RUBY_T_FLOAT ||
1197
+ type == RUBY_T_STRING ||
1198
+ type == RUBY_T_REGEXP ||
1199
+ type == RUBY_T_ARRAY ||
1200
+ type == RUBY_T_HASH ||
1201
+ type == RUBY_T_STRUCT ||
1202
+ type == RUBY_T_BIGNUM ||
1203
+ type == RUBY_T_FILE ||
1204
+ type == RUBY_T_DATA ||
1205
+ type == RUBY_T_MATCH ||
1206
+ type == RUBY_T_COMPLEX ||
1207
+ type == RUBY_T_RATIONAL ||
1208
+ type == RUBY_T_NIL ||
1209
+ type == RUBY_T_TRUE ||
1210
+ type == RUBY_T_FALSE ||
1211
+ type == RUBY_T_SYMBOL ||
1212
+ type == RUBY_T_FIXNUM
1213
+ ) {
1214
+ VALUE klass = rb_class_of(new_object);
1215
+
1216
+ // Ruby sometimes plays a bit fast and loose with some of its internal objects, e.g.
1217
+ // `rb_str_tmp_frozen_acquire` allocates a string with no class (klass=0).
1218
+ // Thus, we need to make sure there's actually a class before getting its name.
1219
+
1220
+ if (klass != 0) {
1221
+ const char *name = rb_obj_classname(new_object);
1222
+ size_t name_length = name != NULL ? strlen(name) : 0;
1223
+
1224
+ if (name_length > 0) {
1225
+ class_name = (ddog_CharSlice) {.ptr = name, .len = name_length};
1226
+ } else {
1227
+ // @ivoanjo: I'm not sure this can ever happen, but just-in-case
1228
+ class_name = ruby_value_type_to_class_name(type);
1229
+ }
1230
+ } else {
1231
+ // Fallback for objects with no class
1232
+ class_name = ruby_value_type_to_class_name(type);
1233
+ }
1234
+ } else if (type == RUBY_T_IMEMO) {
1235
+ const char *imemo_string = imemo_kind(new_object);
1236
+ if (imemo_string != NULL) {
1237
+ snprintf(imemo_type, 100, "(VM Internal, T_IMEMO, %s)", imemo_string);
1238
+ class_name = (ddog_CharSlice) {.ptr = imemo_type, .len = strlen(imemo_type)};
1239
+ } else { // Ruby < 3
1240
+ class_name = DDOG_CHARSLICE_C("(VM Internal, T_IMEMO)");
1241
+ }
1242
+ } else {
1243
+ class_name = ruby_vm_type; // For other weird internal things we just use the VM type
1244
+ }
1245
+ }
1246
+
1247
+ track_object(state->recorder_instance, new_object, sample_weight, optional_class_name);
1248
+
1249
+ trigger_sample_for_thread(
1250
+ state,
1251
+ /* thread: */ current_thread,
1252
+ /* stack_from_thread: */ current_thread,
1253
+ get_or_create_context_for(current_thread, state),
1254
+ (sample_values) {.alloc_samples = sample_weight},
1255
+ INVALID_TIME, // For now we're not collecting timestamps for allocation events, as per profiling team internal discussions
1256
+ &ruby_vm_type,
1257
+ optional_class_name
1258
+ );
1259
+ }
1260
+
1261
+ // This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
1262
+ // It SHOULD NOT be used for other purposes.
1263
+ static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object) {
1264
+ thread_context_collector_sample_allocation(collector_instance, NUM2UINT(sample_weight), new_object);
1265
+ return Qtrue;
1266
+ }
1267
+
1268
+ static VALUE new_empty_thread_inner(DDTRACE_UNUSED void *arg) { return Qnil; }
1269
+
1270
+ // This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
1271
+ // It SHOULD NOT be used for other purposes.
1272
+ // (It creates an empty native thread, so we can test our native thread naming fallback)
1273
+ static VALUE _native_new_empty_thread(DDTRACE_UNUSED VALUE self) {
1274
+ return rb_thread_create(new_empty_thread_inner, NULL);
1275
+ }
1276
+
1277
+ static ddog_CharSlice ruby_value_type_to_class_name(enum ruby_value_type type) {
1278
+ switch (type) {
1279
+ case(RUBY_T_OBJECT ): return DDOG_CHARSLICE_C("Object");
1280
+ case(RUBY_T_CLASS ): return DDOG_CHARSLICE_C("Class");
1281
+ case(RUBY_T_MODULE ): return DDOG_CHARSLICE_C("Module");
1282
+ case(RUBY_T_FLOAT ): return DDOG_CHARSLICE_C("Float");
1283
+ case(RUBY_T_STRING ): return DDOG_CHARSLICE_C("String");
1284
+ case(RUBY_T_REGEXP ): return DDOG_CHARSLICE_C("Regexp");
1285
+ case(RUBY_T_ARRAY ): return DDOG_CHARSLICE_C("Array");
1286
+ case(RUBY_T_HASH ): return DDOG_CHARSLICE_C("Hash");
1287
+ case(RUBY_T_STRUCT ): return DDOG_CHARSLICE_C("Struct");
1288
+ case(RUBY_T_BIGNUM ): return DDOG_CHARSLICE_C("Integer");
1289
+ case(RUBY_T_FILE ): return DDOG_CHARSLICE_C("File");
1290
+ case(RUBY_T_DATA ): return DDOG_CHARSLICE_C("(VM Internal, T_DATA)");
1291
+ case(RUBY_T_MATCH ): return DDOG_CHARSLICE_C("MatchData");
1292
+ case(RUBY_T_COMPLEX ): return DDOG_CHARSLICE_C("Complex");
1293
+ case(RUBY_T_RATIONAL): return DDOG_CHARSLICE_C("Rational");
1294
+ case(RUBY_T_NIL ): return DDOG_CHARSLICE_C("NilClass");
1295
+ case(RUBY_T_TRUE ): return DDOG_CHARSLICE_C("TrueClass");
1296
+ case(RUBY_T_FALSE ): return DDOG_CHARSLICE_C("FalseClass");
1297
+ case(RUBY_T_SYMBOL ): return DDOG_CHARSLICE_C("Symbol");
1298
+ case(RUBY_T_FIXNUM ): return DDOG_CHARSLICE_C("Integer");
1299
+ default: return DDOG_CHARSLICE_C("(VM Internal, Missing class)");
1300
+ }
1301
+ }