datadog 2.12.1 → 2.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 (346) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +243 -2
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +63 -56
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +263 -76
  5. data/ext/datadog_profiling_native_extension/collectors_stack.h +20 -3
  6. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +78 -26
  7. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
  8. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +1 -4
  9. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +10 -0
  10. data/ext/datadog_profiling_native_extension/encoded_profile.c +79 -0
  11. data/ext/datadog_profiling_native_extension/encoded_profile.h +8 -0
  12. data/ext/datadog_profiling_native_extension/extconf.rb +10 -0
  13. data/ext/datadog_profiling_native_extension/heap_recorder.c +247 -364
  14. data/ext/datadog_profiling_native_extension/heap_recorder.h +4 -6
  15. data/ext/datadog_profiling_native_extension/http_transport.c +60 -94
  16. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +22 -0
  17. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +8 -5
  18. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +41 -21
  19. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +6 -4
  20. data/ext/datadog_profiling_native_extension/profiling.c +2 -0
  21. data/ext/datadog_profiling_native_extension/ruby_helpers.c +1 -13
  22. data/ext/datadog_profiling_native_extension/ruby_helpers.h +3 -11
  23. data/ext/datadog_profiling_native_extension/stack_recorder.c +173 -76
  24. data/ext/libdatadog_api/crashtracker.c +11 -12
  25. data/ext/libdatadog_api/crashtracker.h +5 -0
  26. data/ext/libdatadog_api/datadog_ruby_common.c +1 -4
  27. data/ext/libdatadog_api/datadog_ruby_common.h +10 -0
  28. data/ext/libdatadog_api/extconf.rb +2 -2
  29. data/ext/libdatadog_api/init.c +15 -0
  30. data/ext/libdatadog_api/library_config.c +164 -0
  31. data/ext/libdatadog_api/library_config.h +25 -0
  32. data/ext/libdatadog_api/macos_development.md +3 -3
  33. data/ext/libdatadog_api/process_discovery.c +112 -0
  34. data/ext/libdatadog_api/process_discovery.h +5 -0
  35. data/ext/libdatadog_extconf_helpers.rb +2 -2
  36. data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +89 -0
  37. data/lib/datadog/appsec/actions_handler.rb +24 -2
  38. data/lib/datadog/appsec/anonymizer.rb +16 -0
  39. data/lib/datadog/appsec/api_security/lru_cache.rb +56 -0
  40. data/lib/datadog/appsec/api_security/route_extractor.rb +71 -0
  41. data/lib/datadog/appsec/api_security/sampler.rb +59 -0
  42. data/lib/datadog/appsec/api_security.rb +23 -0
  43. data/lib/datadog/appsec/assets/waf_rules/README.md +50 -5
  44. data/lib/datadog/appsec/assets/waf_rules/recommended.json +257 -85
  45. data/lib/datadog/appsec/assets/waf_rules/strict.json +10 -78
  46. data/lib/datadog/appsec/autoload.rb +1 -1
  47. data/lib/datadog/appsec/component.rb +46 -61
  48. data/lib/datadog/appsec/compressed_json.rb +40 -0
  49. data/lib/datadog/appsec/configuration/settings.rb +153 -30
  50. data/lib/datadog/appsec/context.rb +7 -7
  51. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +10 -12
  52. data/lib/datadog/appsec/contrib/active_record/integration.rb +2 -2
  53. data/lib/datadog/appsec/contrib/active_record/patcher.rb +22 -22
  54. data/lib/datadog/appsec/contrib/auto_instrument.rb +1 -1
  55. data/lib/datadog/appsec/contrib/devise/configuration.rb +7 -31
  56. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +78 -0
  57. data/lib/datadog/appsec/contrib/devise/ext.rb +22 -0
  58. data/lib/datadog/appsec/contrib/devise/integration.rb +1 -2
  59. data/lib/datadog/appsec/contrib/devise/patcher.rb +34 -23
  60. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +102 -0
  61. data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +69 -0
  62. data/lib/datadog/appsec/contrib/devise/{patcher/rememberable_patch.rb → patches/skip_signin_tracking_patch.rb} +2 -2
  63. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +106 -0
  64. data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
  65. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +9 -10
  66. data/lib/datadog/appsec/contrib/faraday/integration.rb +1 -1
  67. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +8 -9
  68. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +8 -9
  69. data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -1
  70. data/lib/datadog/appsec/contrib/rack/ext.rb +34 -0
  71. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +49 -32
  72. data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
  73. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +42 -30
  74. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +11 -13
  75. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
  76. data/lib/datadog/appsec/contrib/rails/patcher.rb +21 -21
  77. data/lib/datadog/appsec/contrib/rest_client/integration.rb +1 -1
  78. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +10 -11
  79. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +17 -23
  80. data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
  81. data/lib/datadog/appsec/event.rb +96 -135
  82. data/lib/datadog/appsec/ext.rb +4 -2
  83. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +7 -2
  84. data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +24 -0
  85. data/lib/datadog/appsec/instrumentation/gateway.rb +17 -22
  86. data/lib/datadog/appsec/metrics/telemetry.rb +1 -1
  87. data/lib/datadog/appsec/monitor/gateway/watcher.rb +49 -14
  88. data/lib/datadog/appsec/processor/rule_loader.rb +30 -33
  89. data/lib/datadog/appsec/remote.rb +31 -59
  90. data/lib/datadog/appsec/response.rb +6 -6
  91. data/lib/datadog/appsec/security_engine/engine.rb +194 -0
  92. data/lib/datadog/appsec/security_engine/runner.rb +13 -14
  93. data/lib/datadog/appsec/security_event.rb +39 -0
  94. data/lib/datadog/appsec/utils.rb +0 -2
  95. data/lib/datadog/appsec.rb +5 -8
  96. data/lib/datadog/core/buffer/random.rb +18 -2
  97. data/lib/datadog/core/configuration/agent_settings.rb +52 -0
  98. data/lib/datadog/core/configuration/agent_settings_resolver.rb +4 -46
  99. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +176 -0
  100. data/lib/datadog/core/configuration/components.rb +48 -31
  101. data/lib/datadog/core/configuration/components_state.rb +23 -0
  102. data/lib/datadog/core/configuration/ext.rb +4 -0
  103. data/lib/datadog/core/configuration/option.rb +81 -45
  104. data/lib/datadog/core/configuration/option_definition.rb +4 -4
  105. data/lib/datadog/core/configuration/options.rb +3 -3
  106. data/lib/datadog/core/configuration/settings.rb +109 -44
  107. data/lib/datadog/core/configuration/stable_config.rb +22 -0
  108. data/lib/datadog/core/configuration.rb +40 -16
  109. data/lib/datadog/core/crashtracking/component.rb +3 -10
  110. data/lib/datadog/core/crashtracking/tag_builder.rb +4 -22
  111. data/lib/datadog/core/diagnostics/environment_logger.rb +1 -1
  112. data/lib/datadog/core/encoding.rb +1 -1
  113. data/lib/datadog/core/environment/agent_info.rb +4 -3
  114. data/lib/datadog/core/environment/cgroup.rb +10 -12
  115. data/lib/datadog/core/environment/container.rb +38 -40
  116. data/lib/datadog/core/environment/ext.rb +6 -6
  117. data/lib/datadog/core/environment/git.rb +1 -0
  118. data/lib/datadog/core/environment/identity.rb +3 -3
  119. data/lib/datadog/core/environment/platform.rb +3 -3
  120. data/lib/datadog/core/environment/variable_helpers.rb +1 -1
  121. data/lib/datadog/core/error.rb +11 -9
  122. data/lib/datadog/core/logger.rb +2 -2
  123. data/lib/datadog/core/metrics/client.rb +20 -21
  124. data/lib/datadog/core/metrics/logging.rb +5 -5
  125. data/lib/datadog/core/process_discovery/tracer_memfd.rb +15 -0
  126. data/lib/datadog/core/process_discovery.rb +36 -0
  127. data/lib/datadog/core/rate_limiter.rb +4 -2
  128. data/lib/datadog/core/remote/client.rb +40 -32
  129. data/lib/datadog/core/remote/component.rb +6 -9
  130. data/lib/datadog/core/remote/configuration/digest.rb +7 -7
  131. data/lib/datadog/core/remote/configuration/path.rb +1 -1
  132. data/lib/datadog/core/remote/configuration/repository.rb +14 -1
  133. data/lib/datadog/core/remote/negotiation.rb +9 -9
  134. data/lib/datadog/core/remote/transport/config.rb +4 -3
  135. data/lib/datadog/core/remote/transport/http/client.rb +5 -4
  136. data/lib/datadog/core/remote/transport/http/config.rb +27 -37
  137. data/lib/datadog/core/remote/transport/http/negotiation.rb +7 -33
  138. data/lib/datadog/core/remote/transport/http.rb +22 -57
  139. data/lib/datadog/core/remote/transport/negotiation.rb +4 -3
  140. data/lib/datadog/core/runtime/metrics.rb +12 -5
  141. data/lib/datadog/core/tag_builder.rb +56 -0
  142. data/lib/datadog/core/telemetry/component.rb +81 -52
  143. data/lib/datadog/core/telemetry/emitter.rb +23 -11
  144. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +66 -0
  145. data/lib/datadog/core/telemetry/event/app_closing.rb +18 -0
  146. data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +33 -0
  147. data/lib/datadog/core/telemetry/event/app_heartbeat.rb +18 -0
  148. data/lib/datadog/core/telemetry/event/app_integrations_change.rb +58 -0
  149. data/lib/datadog/core/telemetry/event/app_started.rb +287 -0
  150. data/lib/datadog/core/telemetry/event/base.rb +40 -0
  151. data/lib/datadog/core/telemetry/event/distributions.rb +18 -0
  152. data/lib/datadog/core/telemetry/event/generate_metrics.rb +43 -0
  153. data/lib/datadog/core/telemetry/event/log.rb +76 -0
  154. data/lib/datadog/core/telemetry/event/message_batch.rb +42 -0
  155. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +43 -0
  156. data/lib/datadog/core/telemetry/event.rb +17 -472
  157. data/lib/datadog/core/telemetry/http/adapters/net.rb +12 -97
  158. data/lib/datadog/core/telemetry/logger.rb +5 -4
  159. data/lib/datadog/core/telemetry/logging.rb +11 -5
  160. data/lib/datadog/core/telemetry/metric.rb +8 -8
  161. data/lib/datadog/core/telemetry/request.rb +4 -4
  162. data/lib/datadog/core/telemetry/transport/http/api.rb +43 -0
  163. data/lib/datadog/core/telemetry/transport/http/client.rb +49 -0
  164. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +92 -0
  165. data/lib/datadog/core/telemetry/transport/http.rb +63 -0
  166. data/lib/datadog/core/telemetry/transport/telemetry.rb +51 -0
  167. data/lib/datadog/core/telemetry/worker.rb +90 -24
  168. data/lib/datadog/core/transport/http/adapters/net.rb +17 -2
  169. data/lib/datadog/core/transport/http/adapters/test.rb +2 -1
  170. data/lib/datadog/core/transport/http/api/instance.rb +17 -0
  171. data/lib/datadog/core/transport/http/api/spec.rb +17 -0
  172. data/lib/datadog/core/transport/http/builder.rb +19 -17
  173. data/lib/datadog/core/transport/http/env.rb +8 -0
  174. data/lib/datadog/core/transport/http.rb +39 -2
  175. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +6 -6
  176. data/lib/datadog/core/utils/duration.rb +32 -32
  177. data/lib/datadog/core/utils/forking.rb +2 -2
  178. data/lib/datadog/core/utils/network.rb +6 -6
  179. data/lib/datadog/core/utils/only_once_successful.rb +16 -5
  180. data/lib/datadog/core/utils/time.rb +20 -0
  181. data/lib/datadog/core/utils/truncation.rb +21 -0
  182. data/lib/datadog/core/utils.rb +7 -0
  183. data/lib/datadog/core/vendor/multipart-post/multipart/post/composite_read_io.rb +1 -1
  184. data/lib/datadog/core/vendor/multipart-post/multipart/post/multipartable.rb +8 -8
  185. data/lib/datadog/core/vendor/multipart-post/multipart/post/parts.rb +7 -7
  186. data/lib/datadog/core/worker.rb +1 -1
  187. data/lib/datadog/core/workers/async.rb +29 -12
  188. data/lib/datadog/core/workers/interval_loop.rb +12 -1
  189. data/lib/datadog/core/workers/runtime_metrics.rb +2 -2
  190. data/lib/datadog/core.rb +8 -0
  191. data/lib/datadog/di/boot.rb +34 -0
  192. data/lib/datadog/di/component.rb +0 -2
  193. data/lib/datadog/di/instrumenter.rb +48 -5
  194. data/lib/datadog/di/probe_notification_builder.rb +38 -43
  195. data/lib/datadog/di/probe_notifier_worker.rb +25 -17
  196. data/lib/datadog/di/remote.rb +2 -0
  197. data/lib/datadog/di/serializer.rb +10 -2
  198. data/lib/datadog/di/transport/diagnostics.rb +4 -3
  199. data/lib/datadog/di/transport/http/api.rb +2 -12
  200. data/lib/datadog/di/transport/http/client.rb +4 -3
  201. data/lib/datadog/di/transport/http/diagnostics.rb +7 -34
  202. data/lib/datadog/di/transport/http/input.rb +18 -35
  203. data/lib/datadog/di/transport/http.rb +14 -62
  204. data/lib/datadog/di/transport/input.rb +14 -5
  205. data/lib/datadog/di/utils.rb +5 -0
  206. data/lib/datadog/di.rb +0 -33
  207. data/lib/datadog/error_tracking/collector.rb +87 -0
  208. data/lib/datadog/error_tracking/component.rb +167 -0
  209. data/lib/datadog/error_tracking/configuration/settings.rb +63 -0
  210. data/lib/datadog/error_tracking/configuration.rb +11 -0
  211. data/lib/datadog/error_tracking/ext.rb +18 -0
  212. data/lib/datadog/error_tracking/extensions.rb +16 -0
  213. data/lib/datadog/error_tracking/filters.rb +77 -0
  214. data/lib/datadog/error_tracking.rb +18 -0
  215. data/lib/datadog/kit/appsec/events/v2.rb +195 -0
  216. data/lib/datadog/kit/appsec/events.rb +12 -0
  217. data/lib/datadog/kit/identity.rb +5 -1
  218. data/lib/datadog/opentelemetry/api/baggage.rb +90 -0
  219. data/lib/datadog/opentelemetry/api/baggage.rbs +26 -0
  220. data/lib/datadog/opentelemetry/api/context.rb +16 -2
  221. data/lib/datadog/opentelemetry/sdk/trace/span.rb +1 -1
  222. data/lib/datadog/opentelemetry.rb +2 -1
  223. data/lib/datadog/profiling/collectors/code_provenance.rb +18 -9
  224. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +6 -0
  225. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -0
  226. data/lib/datadog/profiling/collectors/info.rb +44 -0
  227. data/lib/datadog/profiling/collectors/thread_context.rb +17 -2
  228. data/lib/datadog/profiling/component.rb +8 -9
  229. data/lib/datadog/profiling/encoded_profile.rb +11 -0
  230. data/lib/datadog/profiling/exporter.rb +12 -7
  231. data/lib/datadog/profiling/ext.rb +0 -14
  232. data/lib/datadog/profiling/flush.rb +5 -8
  233. data/lib/datadog/profiling/http_transport.rb +7 -61
  234. data/lib/datadog/profiling/profiler.rb +2 -0
  235. data/lib/datadog/profiling/scheduler.rb +10 -2
  236. data/lib/datadog/profiling/sequence_tracker.rb +44 -0
  237. data/lib/datadog/profiling/stack_recorder.rb +9 -9
  238. data/lib/datadog/profiling/tag_builder.rb +7 -41
  239. data/lib/datadog/profiling/tasks/setup.rb +2 -0
  240. data/lib/datadog/profiling.rb +7 -2
  241. data/lib/datadog/single_step_instrument.rb +9 -0
  242. data/lib/datadog/tracing/analytics.rb +1 -1
  243. data/lib/datadog/tracing/component.rb +15 -12
  244. data/lib/datadog/tracing/configuration/ext.rb +7 -1
  245. data/lib/datadog/tracing/configuration/settings.rb +18 -2
  246. data/lib/datadog/tracing/context_provider.rb +1 -1
  247. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +15 -0
  248. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +19 -12
  249. data/lib/datadog/tracing/contrib/action_pack/ext.rb +2 -0
  250. data/lib/datadog/tracing/contrib/active_record/integration.rb +1 -1
  251. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +11 -2
  252. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +33 -0
  253. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +4 -0
  254. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +2 -4
  255. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +13 -0
  256. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +10 -0
  257. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +5 -1
  258. data/lib/datadog/tracing/contrib/configuration/settings.rb +1 -1
  259. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -5
  260. data/lib/datadog/tracing/contrib/excon/middleware.rb +5 -3
  261. data/lib/datadog/tracing/contrib/ext.rb +1 -0
  262. data/lib/datadog/tracing/contrib/faraday/middleware.rb +5 -3
  263. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +7 -1
  264. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +3 -0
  265. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +0 -15
  266. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +4 -1
  267. data/lib/datadog/tracing/contrib/http/instrumentation.rb +6 -10
  268. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -16
  269. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +7 -15
  270. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +27 -0
  271. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +48 -0
  272. data/lib/datadog/tracing/contrib/karafka/ext.rb +27 -0
  273. data/lib/datadog/tracing/contrib/karafka/integration.rb +45 -0
  274. data/lib/datadog/tracing/contrib/karafka/monitor.rb +66 -0
  275. data/lib/datadog/tracing/contrib/karafka/patcher.rb +71 -0
  276. data/lib/datadog/tracing/contrib/karafka.rb +37 -0
  277. data/lib/datadog/tracing/contrib/lograge/patcher.rb +4 -2
  278. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +8 -0
  279. data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
  280. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +18 -1
  281. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +16 -6
  282. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +17 -0
  283. data/lib/datadog/tracing/contrib/opensearch/ext.rb +9 -0
  284. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +5 -1
  285. data/lib/datadog/tracing/contrib/patcher.rb +5 -2
  286. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
  287. data/lib/datadog/tracing/contrib/rails/patcher.rb +4 -1
  288. data/lib/datadog/tracing/contrib/rails/runner.rb +61 -40
  289. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +5 -3
  290. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +6 -1
  291. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +3 -0
  292. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
  293. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +5 -2
  294. data/lib/datadog/tracing/contrib/support.rb +28 -0
  295. data/lib/datadog/tracing/contrib.rb +1 -0
  296. data/lib/datadog/tracing/correlation.rb +9 -2
  297. data/lib/datadog/tracing/diagnostics/environment_logger.rb +3 -1
  298. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  299. data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
  300. data/lib/datadog/tracing/distributed/baggage.rb +131 -0
  301. data/lib/datadog/tracing/distributed/datadog.rb +4 -2
  302. data/lib/datadog/tracing/distributed/propagation.rb +25 -4
  303. data/lib/datadog/tracing/distributed/propagation_policy.rb +42 -0
  304. data/lib/datadog/tracing/metadata/errors.rb +4 -4
  305. data/lib/datadog/tracing/metadata/ext.rb +5 -0
  306. data/lib/datadog/tracing/metadata/metastruct.rb +36 -0
  307. data/lib/datadog/tracing/metadata/metastruct_tagging.rb +42 -0
  308. data/lib/datadog/tracing/metadata.rb +2 -0
  309. data/lib/datadog/tracing/sampling/rate_sampler.rb +2 -1
  310. data/lib/datadog/tracing/sampling/span/rule.rb +0 -1
  311. data/lib/datadog/tracing/span.rb +10 -1
  312. data/lib/datadog/tracing/span_event.rb +2 -2
  313. data/lib/datadog/tracing/span_operation.rb +68 -16
  314. data/lib/datadog/tracing/sync_writer.rb +2 -3
  315. data/lib/datadog/tracing/trace_digest.rb +9 -2
  316. data/lib/datadog/tracing/trace_operation.rb +55 -27
  317. data/lib/datadog/tracing/trace_segment.rb +6 -4
  318. data/lib/datadog/tracing/tracer.rb +51 -7
  319. data/lib/datadog/tracing/transport/http/api.rb +2 -10
  320. data/lib/datadog/tracing/transport/http/client.rb +5 -4
  321. data/lib/datadog/tracing/transport/http/traces.rb +13 -41
  322. data/lib/datadog/tracing/transport/http.rb +11 -44
  323. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -1
  324. data/lib/datadog/tracing/transport/trace_formatter.rb +7 -0
  325. data/lib/datadog/tracing/transport/traces.rb +26 -9
  326. data/lib/datadog/tracing/utils.rb +1 -1
  327. data/lib/datadog/tracing/workers/trace_writer.rb +2 -6
  328. data/lib/datadog/tracing/writer.rb +2 -6
  329. data/lib/datadog/tracing.rb +16 -3
  330. data/lib/datadog/version.rb +2 -2
  331. data/lib/datadog.rb +8 -2
  332. metadata +88 -23
  333. data/lib/datadog/appsec/assets/waf_rules/processors.json +0 -92
  334. data/lib/datadog/appsec/assets/waf_rules/scanners.json +0 -114
  335. data/lib/datadog/appsec/contrib/devise/event.rb +0 -54
  336. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +0 -72
  337. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +0 -47
  338. data/lib/datadog/appsec/contrib/devise/resource.rb +0 -35
  339. data/lib/datadog/appsec/contrib/devise/tracking.rb +0 -57
  340. data/lib/datadog/appsec/processor/rule_merger.rb +0 -170
  341. data/lib/datadog/appsec/processor.rb +0 -107
  342. data/lib/datadog/appsec/utils/trace_operation.rb +0 -15
  343. data/lib/datadog/core/telemetry/http/env.rb +0 -20
  344. data/lib/datadog/core/telemetry/http/ext.rb +0 -28
  345. data/lib/datadog/core/telemetry/http/response.rb +0 -70
  346. data/lib/datadog/core/telemetry/http/transport.rb +0 -90
@@ -34,7 +34,7 @@ typedef struct {
34
34
 
35
35
  // The class of the object that we're tracking.
36
36
  // NOTE: This is optional and will be set to NULL if not set.
37
- char* class;
37
+ ddog_prof_ManagedStringId class;
38
38
 
39
39
  // The GC allocation gen in which we saw this object being allocated.
40
40
  //
@@ -59,7 +59,7 @@ typedef struct {
59
59
  } heap_recorder_iteration_data;
60
60
 
61
61
  // Initialize a new heap recorder.
62
- heap_recorder* heap_recorder_new(void);
62
+ heap_recorder* heap_recorder_new(ddog_prof_ManagedStringStorage string_storage);
63
63
 
64
64
  // Free a previously initialized heap recorder.
65
65
  void heap_recorder_free(heap_recorder *heap_recorder);
@@ -164,10 +164,6 @@ VALUE heap_recorder_state_snapshot(heap_recorder *heap_recorder);
164
164
 
165
165
  // v--- TEST-ONLY APIs ---v
166
166
 
167
- // Assert internal hashing logic is valid for the provided locations and its
168
- // corresponding internal representations in heap recorder.
169
- void heap_recorder_testonly_assert_hash_matches(ddog_prof_Slice_Location locations);
170
-
171
167
  // Returns a Ruby string with a representation of internal data helpful to
172
168
  // troubleshoot issues such as unexpected test failures.
173
169
  VALUE heap_recorder_testonly_debug(heap_recorder *heap_recorder);
@@ -177,3 +173,5 @@ VALUE heap_recorder_testonly_is_object_recorded(heap_recorder *heap_recorder, VA
177
173
 
178
174
  // Used to ensure that a GC actually triggers an update of the objects
179
175
  void heap_recorder_testonly_reset_last_update(heap_recorder *heap_recorder);
176
+
177
+ void heap_recorder_testonly_benchmark_intern(heap_recorder *heap_recorder, ddog_CharSlice string, int times, bool use_all);
@@ -4,6 +4,7 @@
4
4
  #include "helpers.h"
5
5
  #include "libdatadog_helpers.h"
6
6
  #include "ruby_helpers.h"
7
+ #include "encoded_profile.h"
7
8
 
8
9
  // Used to report profiling data to Datadog.
9
10
  // This file implements the native bits of the Datadog::Profiling::HttpTransport class
@@ -14,32 +15,22 @@ static VALUE error_symbol = Qnil; // :error in Ruby
14
15
  static VALUE library_version_string = Qnil;
15
16
 
16
17
  typedef struct {
17
- ddog_prof_Exporter *exporter;
18
- ddog_prof_Exporter_Request_BuildResult *build_result;
18
+ ddog_prof_ProfileExporter *exporter;
19
+ ddog_prof_Request_Result *build_result;
19
20
  ddog_CancellationToken *cancel_token;
20
- ddog_prof_Exporter_SendResult result;
21
+ ddog_prof_Result_HttpStatus result;
21
22
  bool send_ran;
22
23
  } call_exporter_without_gvl_arguments;
23
24
 
24
25
  static inline ddog_ByteSlice byte_slice_from_ruby_string(VALUE string);
25
26
  static VALUE _native_validate_exporter(VALUE self, VALUE exporter_configuration);
26
- static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array);
27
- static VALUE handle_exporter_failure(ddog_prof_Exporter_NewResult exporter_result);
27
+ static ddog_prof_ProfileExporter_Result create_exporter(VALUE exporter_configuration, VALUE tags_as_array);
28
+ static VALUE handle_exporter_failure(ddog_prof_ProfileExporter_Result exporter_result);
28
29
  static VALUE _native_do_export(
29
30
  VALUE self,
30
31
  VALUE exporter_configuration,
31
32
  VALUE upload_timeout_milliseconds,
32
- VALUE start_timespec_seconds,
33
- VALUE start_timespec_nanoseconds,
34
- VALUE finish_timespec_seconds,
35
- VALUE finish_timespec_nanoseconds,
36
- VALUE pprof_file_name,
37
- VALUE pprof_data,
38
- VALUE code_provenance_file_name,
39
- VALUE code_provenance_data,
40
- VALUE tags_as_array,
41
- VALUE internal_metadata_json,
42
- VALUE info_json
33
+ VALUE flush
43
34
  );
44
35
  static void *call_exporter_without_gvl(void *call_args);
45
36
  static void interrupt_exporter_call(void *cancel_token);
@@ -48,7 +39,7 @@ void http_transport_init(VALUE profiling_module) {
48
39
  VALUE http_transport_class = rb_define_class_under(profiling_module, "HttpTransport", rb_cObject);
49
40
 
50
41
  rb_define_singleton_method(http_transport_class, "_native_validate_exporter", _native_validate_exporter, 1);
51
- rb_define_singleton_method(http_transport_class, "_native_do_export", _native_do_export, 13);
42
+ rb_define_singleton_method(http_transport_class, "_native_do_export", _native_do_export, 3);
52
43
 
53
44
  ok_symbol = ID2SYM(rb_intern_const("ok"));
54
45
  error_symbol = ID2SYM(rb_intern_const("error"));
@@ -65,14 +56,14 @@ static inline ddog_ByteSlice byte_slice_from_ruby_string(VALUE string) {
65
56
 
66
57
  static VALUE _native_validate_exporter(DDTRACE_UNUSED VALUE _self, VALUE exporter_configuration) {
67
58
  ENFORCE_TYPE(exporter_configuration, T_ARRAY);
68
- ddog_prof_Exporter_NewResult exporter_result = create_exporter(exporter_configuration, rb_ary_new());
59
+ ddog_prof_ProfileExporter_Result exporter_result = create_exporter(exporter_configuration, rb_ary_new());
69
60
 
70
61
  VALUE failure_tuple = handle_exporter_failure(exporter_result);
71
62
  if (!NIL_P(failure_tuple)) return failure_tuple;
72
63
 
73
64
  // We don't actually need the exporter for now -- we just wanted to validate that we could create it with the
74
65
  // settings we were given
75
- ddog_prof_Exporter_drop(exporter_result.ok);
66
+ ddog_prof_Exporter_drop(&exporter_result.ok);
76
67
 
77
68
  return rb_ary_new_from_args(2, ok_symbol, Qnil);
78
69
  }
@@ -84,26 +75,21 @@ static ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration) {
84
75
  ENFORCE_TYPE(exporter_working_mode, T_SYMBOL);
85
76
  ID working_mode = SYM2ID(exporter_working_mode);
86
77
 
87
- ID agentless_id = rb_intern("agentless");
88
- ID agent_id = rb_intern("agent");
89
-
90
- if (working_mode != agentless_id && working_mode != agent_id) {
91
- rb_raise(rb_eArgError, "Failed to initialize transport: Unexpected working mode, expected :agentless or :agent");
92
- }
93
-
94
- if (working_mode == agentless_id) {
78
+ if (working_mode == rb_intern("agentless")) {
95
79
  VALUE site = rb_ary_entry(exporter_configuration, 1);
96
80
  VALUE api_key = rb_ary_entry(exporter_configuration, 2);
97
81
 
98
82
  return ddog_prof_Endpoint_agentless(char_slice_from_ruby_string(site), char_slice_from_ruby_string(api_key));
99
- } else { // agent_id
83
+ } else if (working_mode == rb_intern("agent")) {
100
84
  VALUE base_url = rb_ary_entry(exporter_configuration, 1);
101
85
 
102
86
  return ddog_prof_Endpoint_agent(char_slice_from_ruby_string(base_url));
87
+ } else {
88
+ rb_raise(rb_eArgError, "Failed to initialize transport: Unexpected working mode, expected :agentless or :agent");
103
89
  }
104
90
  }
105
91
 
106
- static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array) {
92
+ static ddog_prof_ProfileExporter_Result create_exporter(VALUE exporter_configuration, VALUE tags_as_array) {
107
93
  ENFORCE_TYPE(exporter_configuration, T_ARRAY);
108
94
  ENFORCE_TYPE(tags_as_array, T_ARRAY);
109
95
 
@@ -117,7 +103,7 @@ static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration
117
103
  ddog_CharSlice library_version = char_slice_from_ruby_string(library_version_string);
118
104
  ddog_CharSlice profiling_family = DDOG_CHARSLICE_C("ruby");
119
105
 
120
- ddog_prof_Exporter_NewResult exporter_result =
106
+ ddog_prof_ProfileExporter_Result exporter_result =
121
107
  ddog_prof_Exporter_new(library_name, library_version, profiling_family, &tags, endpoint);
122
108
 
123
109
  ddog_Vec_Tag_drop(tags);
@@ -125,8 +111,12 @@ static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration
125
111
  return exporter_result;
126
112
  }
127
113
 
128
- static VALUE handle_exporter_failure(ddog_prof_Exporter_NewResult exporter_result) {
129
- return exporter_result.tag == DDOG_PROF_EXPORTER_NEW_RESULT_OK ?
114
+ static void validate_token(ddog_CancellationToken token, const char *file, int line) {
115
+ if (token.inner == NULL) rb_raise(rb_eRuntimeError, "Unexpected: Validation token was empty at %s:%d", file, line);
116
+ }
117
+
118
+ static VALUE handle_exporter_failure(ddog_prof_ProfileExporter_Result exporter_result) {
119
+ return exporter_result.tag == DDOG_PROF_PROFILE_EXPORTER_RESULT_OK_HANDLE_PROFILE_EXPORTER ?
130
120
  Qnil :
131
121
  rb_ary_new_from_args(2, error_symbol, get_error_details_and_drop(&exporter_result.err));
132
122
  }
@@ -134,39 +124,37 @@ static VALUE handle_exporter_failure(ddog_prof_Exporter_NewResult exporter_resul
134
124
  // Note: This function handles a bunch of libdatadog dynamically-allocated objects, so it MUST not use any Ruby APIs
135
125
  // which can raise exceptions, otherwise the objects will be leaked.
136
126
  static VALUE perform_export(
137
- ddog_prof_Exporter *exporter,
138
- ddog_Timespec start,
139
- ddog_Timespec finish,
127
+ ddog_prof_ProfileExporter *exporter,
128
+ ddog_prof_EncodedProfile *profile,
140
129
  ddog_prof_Exporter_Slice_File files_to_compress_and_export,
141
- ddog_prof_Exporter_Slice_File files_to_export_unmodified,
142
- ddog_Vec_Tag *additional_tags,
143
130
  ddog_CharSlice internal_metadata,
144
131
  ddog_CharSlice info
145
132
  ) {
146
- ddog_prof_ProfiledEndpointsStats *endpoints_stats = NULL; // Not in use yet
147
- ddog_prof_Exporter_Request_BuildResult build_result = ddog_prof_Exporter_Request_build(
133
+ ddog_prof_Request_Result build_result = ddog_prof_Exporter_Request_build(
148
134
  exporter,
149
- start,
150
- finish,
135
+ profile,
151
136
  files_to_compress_and_export,
152
- files_to_export_unmodified,
153
- additional_tags,
154
- endpoints_stats,
137
+ /* files_to_export_unmodified: */ ddog_prof_Exporter_Slice_File_empty(),
138
+ /* optional_additional_tags: */ NULL,
155
139
  &internal_metadata,
156
140
  &info
157
141
  );
158
142
 
159
- if (build_result.tag == DDOG_PROF_EXPORTER_REQUEST_BUILD_RESULT_ERR) {
143
+ if (build_result.tag == DDOG_PROF_REQUEST_RESULT_ERR_HANDLE_REQUEST) {
160
144
  ddog_prof_Exporter_drop(exporter);
161
145
  return rb_ary_new_from_args(2, error_symbol, get_error_details_and_drop(&build_result.err));
162
146
  }
163
147
 
164
- ddog_CancellationToken *cancel_token = ddog_CancellationToken_new();
148
+ ddog_CancellationToken cancel_token_request = ddog_CancellationToken_new();
149
+ ddog_CancellationToken cancel_token_interrupt = ddog_CancellationToken_clone(&cancel_token_request);
150
+
151
+ validate_token(cancel_token_request, __FILE__, __LINE__);
152
+ validate_token(cancel_token_interrupt, __FILE__, __LINE__);
165
153
 
166
154
  // We'll release the Global VM Lock while we're calling send, so that the Ruby VM can continue to work while this
167
155
  // is pending
168
156
  call_exporter_without_gvl_arguments args =
169
- {.exporter = exporter, .build_result = &build_result, .cancel_token = cancel_token, .send_ran = false};
157
+ {.exporter = exporter, .build_result = &build_result, .cancel_token = &cancel_token_request, .send_ran = false};
170
158
 
171
159
  // We use rb_thread_call_without_gvl2 instead of rb_thread_call_without_gvl as the gvl2 variant never raises any
172
160
  // exceptions.
@@ -183,14 +171,15 @@ static VALUE perform_export(
183
171
  int pending_exception = 0;
184
172
 
185
173
  while (!args.send_ran && !pending_exception) {
186
- rb_thread_call_without_gvl2(call_exporter_without_gvl, &args, interrupt_exporter_call, cancel_token);
174
+ rb_thread_call_without_gvl2(call_exporter_without_gvl, &args, interrupt_exporter_call, &cancel_token_interrupt);
187
175
 
188
176
  // To make sure we don't leak memory, we never check for pending exceptions if send ran
189
177
  if (!args.send_ran) pending_exception = check_if_pending_exception();
190
178
  }
191
179
 
192
180
  // Cleanup exporter and token, no longer needed
193
- ddog_CancellationToken_drop(cancel_token);
181
+ ddog_CancellationToken_drop(&cancel_token_request);
182
+ ddog_CancellationToken_drop(&cancel_token_interrupt);
194
183
  ddog_prof_Exporter_drop(exporter);
195
184
 
196
185
  if (pending_exception) {
@@ -203,10 +192,10 @@ static VALUE perform_export(
203
192
 
204
193
  // The request itself does not need to be freed as libdatadog takes ownership of it as part of sending.
205
194
 
206
- ddog_prof_Exporter_SendResult result = args.result;
195
+ ddog_prof_Result_HttpStatus result = args.result;
207
196
 
208
- return result.tag == DDOG_PROF_EXPORTER_SEND_RESULT_HTTP_RESPONSE ?
209
- rb_ary_new_from_args(2, ok_symbol, UINT2NUM(result.http_response.code)) :
197
+ return result.tag == DDOG_PROF_RESULT_HTTP_STATUS_OK_HTTP_STATUS ?
198
+ rb_ary_new_from_args(2, ok_symbol, UINT2NUM(result.ok.code)) :
210
199
  rb_ary_new_from_args(2, error_symbol, get_error_details_and_drop(&result.err));
211
200
  }
212
201
 
@@ -214,26 +203,19 @@ static VALUE _native_do_export(
214
203
  DDTRACE_UNUSED VALUE _self,
215
204
  VALUE exporter_configuration,
216
205
  VALUE upload_timeout_milliseconds,
217
- VALUE start_timespec_seconds,
218
- VALUE start_timespec_nanoseconds,
219
- VALUE finish_timespec_seconds,
220
- VALUE finish_timespec_nanoseconds,
221
- VALUE pprof_file_name,
222
- VALUE pprof_data,
223
- VALUE code_provenance_file_name,
224
- VALUE code_provenance_data,
225
- VALUE tags_as_array,
226
- VALUE internal_metadata_json,
227
- VALUE info_json
206
+ VALUE flush
228
207
  ) {
208
+ VALUE encoded_profile = rb_funcall(flush, rb_intern("encoded_profile"), 0);
209
+ VALUE code_provenance_file_name = rb_funcall(flush, rb_intern("code_provenance_file_name"), 0);
210
+ VALUE code_provenance_data = rb_funcall(flush, rb_intern("code_provenance_data"), 0);
211
+ VALUE tags_as_array = rb_funcall(flush, rb_intern("tags_as_array"), 0);
212
+ VALUE internal_metadata_json = rb_funcall(flush, rb_intern("internal_metadata_json"), 0);
213
+ VALUE info_json = rb_funcall(flush, rb_intern("info_json"), 0);
214
+
229
215
  ENFORCE_TYPE(upload_timeout_milliseconds, T_FIXNUM);
230
- ENFORCE_TYPE(start_timespec_seconds, T_FIXNUM);
231
- ENFORCE_TYPE(start_timespec_nanoseconds, T_FIXNUM);
232
- ENFORCE_TYPE(finish_timespec_seconds, T_FIXNUM);
233
- ENFORCE_TYPE(finish_timespec_nanoseconds, T_FIXNUM);
234
- ENFORCE_TYPE(pprof_file_name, T_STRING);
235
- ENFORCE_TYPE(pprof_data, T_STRING);
216
+ enforce_encoded_profile_instance(encoded_profile);
236
217
  ENFORCE_TYPE(code_provenance_file_name, T_STRING);
218
+ ENFORCE_TYPE(tags_as_array, T_ARRAY);
237
219
  ENFORCE_TYPE(internal_metadata_json, T_STRING);
238
220
  ENFORCE_TYPE(info_json, T_STRING);
239
221
 
@@ -243,23 +225,9 @@ static VALUE _native_do_export(
243
225
 
244
226
  uint64_t timeout_milliseconds = NUM2ULONG(upload_timeout_milliseconds);
245
227
 
246
- ddog_Timespec start =
247
- {.seconds = NUM2LONG(start_timespec_seconds), .nanoseconds = NUM2UINT(start_timespec_nanoseconds)};
248
- ddog_Timespec finish =
249
- {.seconds = NUM2LONG(finish_timespec_seconds), .nanoseconds = NUM2UINT(finish_timespec_nanoseconds)};
250
-
251
228
  int to_compress_length = have_code_provenance ? 1 : 0;
252
229
  ddog_prof_Exporter_File to_compress[to_compress_length];
253
- int already_compressed_length = 1; // pprof
254
- ddog_prof_Exporter_File already_compressed[already_compressed_length];
255
-
256
230
  ddog_prof_Exporter_Slice_File files_to_compress_and_export = {.ptr = to_compress, .len = to_compress_length};
257
- ddog_prof_Exporter_Slice_File files_to_export_unmodified = {.ptr = already_compressed, .len = already_compressed_length};
258
-
259
- already_compressed[0] = (ddog_prof_Exporter_File) {
260
- .name = char_slice_from_ruby_string(pprof_file_name),
261
- .file = byte_slice_from_ruby_string(pprof_data),
262
- };
263
231
 
264
232
  if (have_code_provenance) {
265
233
  to_compress[0] = (ddog_prof_Exporter_File) {
@@ -268,32 +236,28 @@ static VALUE _native_do_export(
268
236
  };
269
237
  }
270
238
 
271
- ddog_Vec_Tag *null_additional_tags = NULL;
272
239
  ddog_CharSlice internal_metadata = char_slice_from_ruby_string(internal_metadata_json);
273
240
  ddog_CharSlice info = char_slice_from_ruby_string(info_json);
274
241
 
275
- ddog_prof_Exporter_NewResult exporter_result = create_exporter(exporter_configuration, tags_as_array);
242
+ ddog_prof_ProfileExporter_Result exporter_result = create_exporter(exporter_configuration, tags_as_array);
276
243
  // Note: Do not add anything that can raise exceptions after this line, as otherwise the exporter memory will leak
277
244
 
278
245
  VALUE failure_tuple = handle_exporter_failure(exporter_result);
279
246
  if (!NIL_P(failure_tuple)) return failure_tuple;
280
247
 
281
- ddog_prof_MaybeError timeout_result = ddog_prof_Exporter_set_timeout(exporter_result.ok, timeout_milliseconds);
282
- if (timeout_result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
248
+ ddog_VoidResult timeout_result = ddog_prof_Exporter_set_timeout(&exporter_result.ok, timeout_milliseconds);
249
+ if (timeout_result.tag == DDOG_VOID_RESULT_ERR) {
283
250
  // NOTE: Seems a bit harsh to fail the upload if we can't set a timeout. OTOH, this is only expected to fail
284
251
  // if the exporter is not well built. Because such a situation should already be caught above I think it's
285
252
  // preferable to leave this here as a virtually unreachable exception rather than ignoring it.
286
- ddog_prof_Exporter_drop(exporter_result.ok);
287
- return rb_ary_new_from_args(2, error_symbol, get_error_details_and_drop(&timeout_result.some));
253
+ ddog_prof_Exporter_drop(&exporter_result.ok);
254
+ return rb_ary_new_from_args(2, error_symbol, get_error_details_and_drop(&timeout_result.err));
288
255
  }
289
256
 
290
257
  return perform_export(
291
- exporter_result.ok,
292
- start,
293
- finish,
258
+ &exporter_result.ok,
259
+ to_ddog_prof_EncodedProfile(encoded_profile),
294
260
  files_to_compress_and_export,
295
- files_to_export_unmodified,
296
- null_additional_tags,
297
261
  internal_metadata,
298
262
  info
299
263
  );
@@ -310,5 +274,7 @@ static void *call_exporter_without_gvl(void *call_args) {
310
274
 
311
275
  // Called by Ruby when it wants to interrupt call_exporter_without_gvl above, e.g. when the app wants to exit cleanly
312
276
  static void interrupt_exporter_call(void *cancel_token) {
277
+ // TODO: False here can mean two things: it was already cancelled OR it failed to cancel.
278
+ // Would be nice to change libdatadog to be able to distinguish between them...
313
279
  ddog_CancellationToken_cancel((ddog_CancellationToken *) cancel_token);
314
280
  }
@@ -60,3 +60,25 @@ size_t read_ddogerr_string_and_drop(ddog_Error *error, char *string, size_t capa
60
60
  ddog_Error_drop(error);
61
61
  return error_msg_size;
62
62
  }
63
+
64
+ ddog_prof_ManagedStringId intern_or_raise(ddog_prof_ManagedStringStorage string_storage, ddog_CharSlice string) {
65
+ if (string.len == 0) return (ddog_prof_ManagedStringId) { 0 }; // Id 0 is always an empty string, no need to ask
66
+
67
+ ddog_prof_ManagedStringStorageInternResult intern_result = ddog_prof_ManagedStringStorage_intern(string_storage, string);
68
+ if (intern_result.tag == DDOG_PROF_MANAGED_STRING_STORAGE_INTERN_RESULT_ERR) {
69
+ rb_raise(rb_eRuntimeError, "Failed to intern string: %"PRIsVALUE, get_error_details_and_drop(&intern_result.err));
70
+ }
71
+ return intern_result.ok;
72
+ }
73
+
74
+ void intern_all_or_raise(
75
+ ddog_prof_ManagedStringStorage string_storage,
76
+ ddog_prof_Slice_CharSlice strings,
77
+ ddog_prof_ManagedStringId *output_ids,
78
+ uintptr_t output_ids_size
79
+ ) {
80
+ ddog_prof_MaybeError result = ddog_prof_ManagedStringStorage_intern_all(string_storage, strings, output_ids, output_ids_size);
81
+ if (result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
82
+ rb_raise(rb_eRuntimeError, "Failed to intern_all: %"PRIsVALUE, get_error_details_and_drop(&result.some));
83
+ }
84
+ }
@@ -18,8 +18,11 @@ size_t read_ddogerr_string_and_drop(ddog_Error *error, char *string, size_t capa
18
18
  const char *ruby_value_type_to_string(enum ruby_value_type type);
19
19
  ddog_CharSlice ruby_value_type_to_char_slice(enum ruby_value_type type);
20
20
 
21
- // Returns a dynamically allocated string from the provided char slice.
22
- // WARN: The returned string must be explicitly freed with ruby_xfree.
23
- static inline char* string_from_char_slice(ddog_CharSlice slice) {
24
- return ruby_strndup(slice.ptr, slice.len);
25
- }
21
+ ddog_prof_ManagedStringId intern_or_raise(ddog_prof_ManagedStringStorage string_storage, ddog_CharSlice string);
22
+
23
+ void intern_all_or_raise(
24
+ ddog_prof_ManagedStringStorage string_storage,
25
+ ddog_prof_Slice_CharSlice strings,
26
+ ddog_prof_ManagedStringId *output_ids,
27
+ uintptr_t output_ids_size
28
+ );
@@ -212,21 +212,6 @@ uint64_t native_thread_id_for(VALUE thread) {
212
212
  #endif
213
213
  }
214
214
 
215
- // Returns the stack depth by using the same approach as rb_profile_frames and backtrace_each: get the positions
216
- // of the end and current frame pointers and subtracting them.
217
- ptrdiff_t stack_depth_for(VALUE thread) {
218
- const rb_execution_context_t *ec = thread_struct_from_object(thread)->ec;
219
- const rb_control_frame_t *cfp = ec->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
220
-
221
- if (end_cfp == NULL) return 0;
222
-
223
- // Skip dummy frame, as seen in `backtrace_each` (`vm_backtrace.c`) and our custom rb_profile_frames
224
- // ( https://github.com/ruby/ruby/blob/4bd38e8120f2fdfdd47a34211720e048502377f1/vm_backtrace.c#L890-L914 )
225
- end_cfp = RUBY_VM_NEXT_CONTROL_FRAME(end_cfp);
226
-
227
- return end_cfp <= cfp ? 0 : end_cfp - cfp - 1;
228
- }
229
-
230
215
  // This was renamed in Ruby 3.2
231
216
  #if !defined(ccan_list_for_each) && defined(list_for_each)
232
217
  #define ccan_list_for_each list_for_each
@@ -312,6 +297,7 @@ VALUE thread_name_for(VALUE thread) {
312
297
  // to support our custom rb_profile_frames (see below)
313
298
  // Modifications:
314
299
  // * Support int first_lineno for Ruby 3.2.0+ (https://github.com/ruby/ruby/pull/6430)
300
+ // * Validate iseq and pos before calling `rb_iseq_line_no` as a safety measure (see comment below for details)
315
301
  //
316
302
  // `node_id` gets used depending on Ruby VM compilation settings (USE_ISEQ_NODE_ID being defined).
317
303
  // To avoid getting false "unused argument" warnings in setups where it's not used, we need to do this weird dance
@@ -358,6 +344,18 @@ calc_pos(const rb_iseq_t *iseq, const VALUE *pc, int *lineno, int *node_id)
358
344
  __builtin_trap();
359
345
  }
360
346
  #endif
347
+
348
+ // In PROF-11475 we spotted a crash when calling `rb_iseq_line_no` from this method.
349
+ // We were only able to reproduce this issue on Ruby 2.6 and 2.7, not 2.5 or the 3.x series (tried 3.0, 3.2 and 3.4).
350
+ // Note that going out of bounds doesn't crash every time, as usual with C we may just read garbage or get lucky.
351
+ //
352
+ // For those problematic Rubies, we observed that when we try to take a sample in the middle of processing the
353
+ // VM `LEAVE` instruction, the value of `n` can violate the documented assumptions above and be
354
+ // `n > ISEQ_BODY(iseq)->iseq_size)`.
355
+ //
356
+ // To work around this and any other potential issues, we validate here that the bytecode position is sane.
357
+ if (RB_UNLIKELY(n < 0 || n > ISEQ_BODY(iseq)->iseq_size)) return 0;
358
+
361
359
  if (lineno) *lineno = rb_iseq_line_no(iseq, pos);
362
360
  #ifdef USE_ISEQ_NODE_ID
363
361
  if (node_id) *node_id = rb_iseq_node_id(iseq, pos);
@@ -402,6 +400,9 @@ calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
402
400
  // * Add frame_flags.same_frame and logic to skip redoing work if the buffer already contains the same data we're collecting
403
401
  // * Skipped use of rb_callable_method_entry_t (cme) for Ruby frames as it doesn't impact us.
404
402
  // * Imported fix from https://github.com/ruby/ruby/pull/8280 to keep us closer to upstream
403
+ // * Added potential fix for https://github.com/ruby/ruby/pull/13643 (this one is a just-in-case, unclear if it happens
404
+ // for ddtrace)
405
+ // * Reversed order of iteration to better enable caching
405
406
  //
406
407
  // What is rb_profile_frames?
407
408
  // `rb_profile_frames` is a Ruby VM debug API added for use by profilers for sampling the stack trace of a Ruby thread.
@@ -437,6 +438,16 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, frame_info *st
437
438
  // support sampling any thread (including the current) passed as an argument
438
439
  rb_thread_t *th = thread_struct_from_object(thread);
439
440
  const rb_execution_context_t *ec = th->ec;
441
+
442
+ // As of this writing, we don't support profiling with MN enabled, and this only happens in that mode, but as we
443
+ // probably want to experiment with it in the future, I've decided to import https://github.com/ruby/ruby/pull/9310
444
+ // here.
445
+ if (ec == NULL) return 0;
446
+
447
+ // I suspect this won't happen for ddtrace, but just-in-case we've imported a potential fix for
448
+ // https://github.com/ruby/ruby/pull/13643 by assuming that these can be NULL/zero with the cfp being non-NULL yet.
449
+ if (ec->vm_stack == NULL || ec->vm_stack_size == 0) return 0;
450
+
440
451
  const rb_control_frame_t *cfp = ec->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
441
452
  #ifndef NO_JIT_RETURN
442
453
  const rb_control_frame_t *top = cfp;
@@ -454,11 +465,6 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, frame_info *st
454
465
  // it from https://github.com/ruby/ruby/pull/7116 in a "just in case" kind of mindset.
455
466
  if (cfp == NULL) return 0;
456
467
 
457
- // As of this writing, we don't support profiling with MN enabled, and this only happens in that mode, but as we
458
- // probably want to experiment with it in the future, I've decided to import https://github.com/ruby/ruby/pull/9310
459
- // here.
460
- if (ec == NULL) return 0;
461
-
462
468
  // Fix: Skip dummy frame that shows up in main thread.
463
469
  //
464
470
  // According to a comment in `backtrace_each` (`vm_backtrace.c`), there's two dummy frames that we should ignore
@@ -475,7 +481,20 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, frame_info *st
475
481
  // See comment on `record_placeholder_stack_in_native_code` for a full explanation of what this means (and why we don't just return 0)
476
482
  if (end_cfp <= cfp) return PLACEHOLDER_STACK_IN_NATIVE_CODE;
477
483
 
478
- for (i=0; i<limit && cfp != end_cfp; cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) {
484
+ // This is the position just after the top of the stack -- e.g. where a new frame pushed on the stack would end up.
485
+ const rb_control_frame_t *top_sentinel = RUBY_VM_NEXT_CONTROL_FRAME(cfp);
486
+
487
+ // We iterate the stack from bottom (beginning of thread) to the top (currently-active frame). This is different
488
+ // from upstream rb_profile_frames, but actually matches what `backtrace_each` does (yes, different Ruby VM APIs
489
+ // iterate in different directions).
490
+ // We do this to better take advantage of the `same_frame` caching mechanism: By starting from the bottom of the
491
+ // stack towards the top, we can usually keep most of the stack intact when the code is only going up and down
492
+ // a few methods at the top. Before this change, the cache was really only useful if between samples the app had
493
+ // not moved from the current stack, as adding or removing one frame would invalidate the existing cache (because
494
+ // every position would shift).
495
+ cfp = RUBY_VM_NEXT_CONTROL_FRAME(end_cfp);
496
+
497
+ for (i=0; i<limit && cfp != top_sentinel; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
479
498
  if (cfp->iseq && !cfp->pc) {
480
499
  // Fix: Do nothing -- this frame should not be used
481
500
  //
@@ -561,6 +580,7 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, frame_info *st
561
580
 
562
581
  stack_buffer[i].as.native_frame.caching_cme = (VALUE)cme;
563
582
  stack_buffer[i].as.native_frame.method_id = cme->def->original_id;
583
+ stack_buffer[i].as.native_frame.function = cme->def->body.cfunc.func;
564
584
  stack_buffer[i].is_ruby_frame = false;
565
585
  i++;
566
586
  }
@@ -18,16 +18,19 @@ typedef struct {
18
18
  rb_nativethread_id_t owner;
19
19
  } current_gvl_owner;
20
20
 
21
+ // If a sample is kept around for later use, some of its fields need marking. Remember to
22
+ // update the marking code in `sampling_buffer_mark` if new fields are added.
21
23
  typedef struct {
22
24
  union {
23
25
  struct {
24
- VALUE iseq;
25
- void *caching_pc; // For caching only
26
+ VALUE iseq; // Needs marking if kept around
27
+ void *caching_pc; // For caching validation/invalidation only (does not need marking)
26
28
  int line;
27
29
  } ruby_frame;
28
30
  struct {
29
- VALUE caching_cme; // For caching only
31
+ VALUE caching_cme; // For caching validation/invalidation only (does not need marking)
30
32
  ID method_id;
33
+ void *function;
31
34
  } native_frame;
32
35
  } as;
33
36
  bool is_ruby_frame : 1;
@@ -38,7 +41,6 @@ rb_nativethread_id_t pthread_id_for(VALUE thread);
38
41
  bool is_current_thread_holding_the_gvl(void);
39
42
  current_gvl_owner gvl_owner(void);
40
43
  uint64_t native_thread_id_for(VALUE thread);
41
- ptrdiff_t stack_depth_for(VALUE thread);
42
44
  void ddtrace_thread_list(VALUE result_array);
43
45
  bool is_thread_alive(VALUE thread);
44
46
  VALUE thread_name_for(VALUE thread);
@@ -20,6 +20,7 @@ void collectors_dynamic_sampling_rate_init(VALUE profiling_module);
20
20
  void collectors_idle_sampling_helper_init(VALUE profiling_module);
21
21
  void collectors_stack_init(VALUE profiling_module);
22
22
  void collectors_thread_context_init(VALUE profiling_module);
23
+ void encoded_profile_init(VALUE profiling_module);
23
24
  void http_transport_init(VALUE profiling_module);
24
25
  void stack_recorder_init(VALUE profiling_module);
25
26
 
@@ -61,6 +62,7 @@ void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) {
61
62
  collectors_idle_sampling_helper_init(profiling_module);
62
63
  collectors_stack_init(profiling_module);
63
64
  collectors_thread_context_init(profiling_module);
65
+ encoded_profile_init(profiling_module);
64
66
  http_transport_init(profiling_module);
65
67
  stack_recorder_init(profiling_module);
66
68
  unsafe_api_calls_check_init();
@@ -103,16 +103,6 @@ void raise_syserr(
103
103
  }
104
104
  }
105
105
 
106
- char* ruby_strndup(const char *str, size_t size) {
107
- char *dup;
108
-
109
- dup = xmalloc(size + 1);
110
- memcpy(dup, str, size);
111
- dup[size] = '\0';
112
-
113
- return dup;
114
- }
115
-
116
106
  static VALUE _id2ref(VALUE obj_id) {
117
107
  // Call ::ObjectSpace._id2ref natively. It will raise if the id is no longer valid
118
108
  return rb_funcall(module_object_space, _id2ref_id, 1, obj_id);
@@ -122,9 +112,7 @@ static VALUE _id2ref_failure(DDTRACE_UNUSED VALUE _unused1, DDTRACE_UNUSED VALUE
122
112
  return Qfalse;
123
113
  }
124
114
 
125
- // Native wrapper to get an object ref from an id. Returns true on success and
126
- // writes the ref to the value pointer parameter if !NULL. False if id doesn't
127
- // reference a valid object (in which case value is not changed).
115
+ // See notes on header for important details
128
116
  bool ruby_ref_from_id(VALUE obj_id, VALUE *value) {
129
117
  // Call ::ObjectSpace._id2ref natively. It will raise if the id is no longer valid
130
118
  // so we need to call it via rb_rescue2
@@ -67,20 +67,12 @@ NORETURN(void raise_syserr(
67
67
  const char *function_name
68
68
  ));
69
69
 
70
- // Alternative to ruby_strdup that takes a size argument.
71
- // Similar to C's strndup but slightly less smart as size is expected to
72
- // be smaller or equal to the real size of str (minus null termination if it
73
- // exists).
74
- // A new string will be returned with size+1 bytes and last byte set to '\0'.
75
- // The returned string must be freed explicitly.
76
- //
77
- // WARN: Cannot be used during GC or outside the GVL.
78
- char* ruby_strndup(const char *str, size_t size);
79
-
80
70
  // Native wrapper to get an object ref from an id. Returns true on success and
81
71
  // writes the ref to the value pointer parameter if !NULL. False if id doesn't
82
72
  // reference a valid object (in which case value is not changed).
83
- bool ruby_ref_from_id(size_t id, VALUE *value);
73
+ //
74
+ // Note: GVL can be released and other threads may get to run before this method returns
75
+ bool ruby_ref_from_id(VALUE obj_id, VALUE *value);
84
76
 
85
77
  // Native wrapper to get the approximate/estimated current size of the passed
86
78
  // object.