datadog 2.12.1 → 2.17.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 (302) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +154 -2
  3. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +16 -14
  4. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +1 -4
  5. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +10 -0
  6. data/ext/datadog_profiling_native_extension/encoded_profile.c +79 -0
  7. data/ext/datadog_profiling_native_extension/encoded_profile.h +8 -0
  8. data/ext/datadog_profiling_native_extension/extconf.rb +3 -0
  9. data/ext/datadog_profiling_native_extension/heap_recorder.c +8 -1
  10. data/ext/datadog_profiling_native_extension/http_transport.c +60 -94
  11. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +8 -0
  12. data/ext/datadog_profiling_native_extension/profiling.c +2 -0
  13. data/ext/datadog_profiling_native_extension/stack_recorder.c +23 -23
  14. data/ext/libdatadog_api/crashtracker.c +11 -12
  15. data/ext/libdatadog_api/crashtracker.h +5 -0
  16. data/ext/libdatadog_api/datadog_ruby_common.c +1 -4
  17. data/ext/libdatadog_api/datadog_ruby_common.h +10 -0
  18. data/ext/libdatadog_api/init.c +15 -0
  19. data/ext/libdatadog_api/library_config.c +122 -0
  20. data/ext/libdatadog_api/library_config.h +19 -0
  21. data/ext/libdatadog_api/macos_development.md +3 -3
  22. data/ext/libdatadog_api/process_discovery.c +117 -0
  23. data/ext/libdatadog_api/process_discovery.h +5 -0
  24. data/ext/libdatadog_extconf_helpers.rb +1 -1
  25. data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +89 -0
  26. data/lib/datadog/appsec/actions_handler.rb +24 -2
  27. data/lib/datadog/appsec/anonymizer.rb +16 -0
  28. data/lib/datadog/appsec/api_security/lru_cache.rb +49 -0
  29. data/lib/datadog/appsec/api_security.rb +9 -0
  30. data/lib/datadog/appsec/assets/waf_rules/README.md +50 -5
  31. data/lib/datadog/appsec/assets/waf_rules/processors.json +239 -10
  32. data/lib/datadog/appsec/assets/waf_rules/scanners.json +926 -17
  33. data/lib/datadog/appsec/autoload.rb +1 -1
  34. data/lib/datadog/appsec/component.rb +29 -20
  35. data/lib/datadog/appsec/compressed_json.rb +40 -0
  36. data/lib/datadog/appsec/configuration/settings.rb +93 -28
  37. data/lib/datadog/appsec/context.rb +1 -1
  38. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +10 -12
  39. data/lib/datadog/appsec/contrib/active_record/integration.rb +2 -2
  40. data/lib/datadog/appsec/contrib/active_record/patcher.rb +22 -22
  41. data/lib/datadog/appsec/contrib/auto_instrument.rb +1 -1
  42. data/lib/datadog/appsec/contrib/devise/configuration.rb +7 -31
  43. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +78 -0
  44. data/lib/datadog/appsec/contrib/devise/ext.rb +22 -0
  45. data/lib/datadog/appsec/contrib/devise/integration.rb +1 -2
  46. data/lib/datadog/appsec/contrib/devise/patcher.rb +34 -23
  47. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +102 -0
  48. data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +69 -0
  49. data/lib/datadog/appsec/contrib/devise/{patcher/rememberable_patch.rb → patches/skip_signin_tracking_patch.rb} +2 -2
  50. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +106 -0
  51. data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
  52. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +9 -10
  53. data/lib/datadog/appsec/contrib/faraday/integration.rb +1 -1
  54. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +8 -9
  55. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +8 -9
  56. data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -1
  57. data/lib/datadog/appsec/contrib/rack/ext.rb +34 -0
  58. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +49 -32
  59. data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
  60. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +19 -18
  61. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +11 -13
  62. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
  63. data/lib/datadog/appsec/contrib/rails/patcher.rb +21 -21
  64. data/lib/datadog/appsec/contrib/rest_client/integration.rb +1 -1
  65. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +10 -11
  66. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +17 -23
  67. data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
  68. data/lib/datadog/appsec/event.rb +96 -135
  69. data/lib/datadog/appsec/ext.rb +4 -2
  70. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +7 -2
  71. data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +24 -0
  72. data/lib/datadog/appsec/instrumentation/gateway.rb +17 -22
  73. data/lib/datadog/appsec/metrics/telemetry.rb +1 -1
  74. data/lib/datadog/appsec/monitor/gateway/watcher.rb +49 -14
  75. data/lib/datadog/appsec/processor/rule_loader.rb +26 -28
  76. data/lib/datadog/appsec/processor/rule_merger.rb +7 -6
  77. data/lib/datadog/appsec/processor.rb +1 -1
  78. data/lib/datadog/appsec/remote.rb +23 -11
  79. data/lib/datadog/appsec/response.rb +6 -6
  80. data/lib/datadog/appsec/security_engine/runner.rb +3 -3
  81. data/lib/datadog/appsec/security_event.rb +39 -0
  82. data/lib/datadog/appsec/utils.rb +0 -2
  83. data/lib/datadog/appsec.rb +1 -1
  84. data/lib/datadog/core/buffer/random.rb +18 -2
  85. data/lib/datadog/core/configuration/agent_settings_resolver.rb +5 -5
  86. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +176 -0
  87. data/lib/datadog/core/configuration/components.rb +50 -31
  88. data/lib/datadog/core/configuration/components_state.rb +23 -0
  89. data/lib/datadog/core/configuration/ext.rb +4 -0
  90. data/lib/datadog/core/configuration/option.rb +79 -43
  91. data/lib/datadog/core/configuration/option_definition.rb +4 -4
  92. data/lib/datadog/core/configuration/options.rb +3 -3
  93. data/lib/datadog/core/configuration/settings.rb +68 -35
  94. data/lib/datadog/core/configuration/stable_config.rb +23 -0
  95. data/lib/datadog/core/configuration.rb +40 -16
  96. data/lib/datadog/core/crashtracking/component.rb +3 -10
  97. data/lib/datadog/core/diagnostics/environment_logger.rb +1 -1
  98. data/lib/datadog/core/encoding.rb +1 -1
  99. data/lib/datadog/core/environment/agent_info.rb +4 -3
  100. data/lib/datadog/core/environment/cgroup.rb +10 -12
  101. data/lib/datadog/core/environment/container.rb +38 -40
  102. data/lib/datadog/core/environment/ext.rb +6 -6
  103. data/lib/datadog/core/environment/git.rb +1 -0
  104. data/lib/datadog/core/environment/identity.rb +3 -3
  105. data/lib/datadog/core/environment/platform.rb +3 -3
  106. data/lib/datadog/core/environment/variable_helpers.rb +1 -1
  107. data/lib/datadog/core/error.rb +11 -9
  108. data/lib/datadog/core/logger.rb +2 -2
  109. data/lib/datadog/core/metrics/client.rb +20 -21
  110. data/lib/datadog/core/metrics/logging.rb +5 -5
  111. data/lib/datadog/core/process_discovery.rb +32 -0
  112. data/lib/datadog/core/rate_limiter.rb +4 -2
  113. data/lib/datadog/core/remote/client.rb +40 -32
  114. data/lib/datadog/core/remote/component.rb +6 -9
  115. data/lib/datadog/core/remote/configuration/digest.rb +7 -7
  116. data/lib/datadog/core/remote/configuration/path.rb +1 -1
  117. data/lib/datadog/core/remote/configuration/repository.rb +2 -1
  118. data/lib/datadog/core/remote/negotiation.rb +9 -9
  119. data/lib/datadog/core/remote/transport/config.rb +4 -3
  120. data/lib/datadog/core/remote/transport/http/client.rb +5 -4
  121. data/lib/datadog/core/remote/transport/http/config.rb +27 -37
  122. data/lib/datadog/core/remote/transport/http/negotiation.rb +7 -33
  123. data/lib/datadog/core/remote/transport/http.rb +22 -57
  124. data/lib/datadog/core/remote/transport/negotiation.rb +4 -3
  125. data/lib/datadog/core/runtime/metrics.rb +12 -5
  126. data/lib/datadog/core/telemetry/component.rb +78 -53
  127. data/lib/datadog/core/telemetry/emitter.rb +23 -11
  128. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +65 -0
  129. data/lib/datadog/core/telemetry/event/app_closing.rb +18 -0
  130. data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +33 -0
  131. data/lib/datadog/core/telemetry/event/app_heartbeat.rb +18 -0
  132. data/lib/datadog/core/telemetry/event/app_integrations_change.rb +58 -0
  133. data/lib/datadog/core/telemetry/event/app_started.rb +179 -0
  134. data/lib/datadog/core/telemetry/event/base.rb +40 -0
  135. data/lib/datadog/core/telemetry/event/distributions.rb +18 -0
  136. data/lib/datadog/core/telemetry/event/generate_metrics.rb +43 -0
  137. data/lib/datadog/core/telemetry/event/log.rb +76 -0
  138. data/lib/datadog/core/telemetry/event/message_batch.rb +42 -0
  139. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +43 -0
  140. data/lib/datadog/core/telemetry/event.rb +17 -472
  141. data/lib/datadog/core/telemetry/http/adapters/net.rb +12 -97
  142. data/lib/datadog/core/telemetry/logger.rb +1 -1
  143. data/lib/datadog/core/telemetry/metric.rb +8 -8
  144. data/lib/datadog/core/telemetry/request.rb +4 -4
  145. data/lib/datadog/core/telemetry/transport/http/api.rb +43 -0
  146. data/lib/datadog/core/telemetry/transport/http/client.rb +49 -0
  147. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +92 -0
  148. data/lib/datadog/core/telemetry/transport/http.rb +63 -0
  149. data/lib/datadog/core/telemetry/transport/telemetry.rb +51 -0
  150. data/lib/datadog/core/telemetry/worker.rb +90 -24
  151. data/lib/datadog/core/transport/http/adapters/test.rb +2 -1
  152. data/lib/datadog/core/transport/http/api/instance.rb +17 -0
  153. data/lib/datadog/core/transport/http/api/spec.rb +17 -0
  154. data/lib/datadog/core/transport/http/builder.rb +18 -16
  155. data/lib/datadog/core/transport/http.rb +39 -2
  156. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +6 -6
  157. data/lib/datadog/core/utils/duration.rb +32 -32
  158. data/lib/datadog/core/utils/forking.rb +2 -2
  159. data/lib/datadog/core/utils/network.rb +6 -6
  160. data/lib/datadog/core/utils/only_once_successful.rb +16 -5
  161. data/lib/datadog/core/utils/time.rb +20 -0
  162. data/lib/datadog/core/utils/truncation.rb +21 -0
  163. data/lib/datadog/core/vendor/multipart-post/multipart/post/composite_read_io.rb +1 -1
  164. data/lib/datadog/core/vendor/multipart-post/multipart/post/multipartable.rb +8 -8
  165. data/lib/datadog/core/vendor/multipart-post/multipart/post/parts.rb +7 -7
  166. data/lib/datadog/core/worker.rb +1 -1
  167. data/lib/datadog/core/workers/async.rb +29 -12
  168. data/lib/datadog/core/workers/interval_loop.rb +12 -1
  169. data/lib/datadog/core/workers/runtime_metrics.rb +2 -2
  170. data/lib/datadog/core.rb +8 -0
  171. data/lib/datadog/di/boot.rb +34 -0
  172. data/lib/datadog/di/component.rb +0 -2
  173. data/lib/datadog/di/probe_notification_builder.rb +1 -1
  174. data/lib/datadog/di/probe_notifier_worker.rb +16 -16
  175. data/lib/datadog/di/remote.rb +2 -0
  176. data/lib/datadog/di/transport/diagnostics.rb +4 -3
  177. data/lib/datadog/di/transport/http/api.rb +2 -12
  178. data/lib/datadog/di/transport/http/client.rb +4 -3
  179. data/lib/datadog/di/transport/http/diagnostics.rb +7 -34
  180. data/lib/datadog/di/transport/http/input.rb +7 -34
  181. data/lib/datadog/di/transport/http.rb +14 -62
  182. data/lib/datadog/di/transport/input.rb +4 -3
  183. data/lib/datadog/di/utils.rb +5 -0
  184. data/lib/datadog/di.rb +5 -32
  185. data/lib/datadog/error_tracking/collector.rb +87 -0
  186. data/lib/datadog/error_tracking/component.rb +167 -0
  187. data/lib/datadog/error_tracking/configuration/settings.rb +63 -0
  188. data/lib/datadog/error_tracking/configuration.rb +11 -0
  189. data/lib/datadog/error_tracking/ext.rb +18 -0
  190. data/lib/datadog/error_tracking/extensions.rb +16 -0
  191. data/lib/datadog/error_tracking/filters.rb +77 -0
  192. data/lib/datadog/error_tracking.rb +18 -0
  193. data/lib/datadog/kit/appsec/events.rb +12 -0
  194. data/lib/datadog/kit/identity.rb +5 -1
  195. data/lib/datadog/opentelemetry/api/baggage.rb +90 -0
  196. data/lib/datadog/opentelemetry/api/baggage.rbs +26 -0
  197. data/lib/datadog/opentelemetry/api/context.rb +16 -2
  198. data/lib/datadog/opentelemetry/sdk/trace/span.rb +1 -1
  199. data/lib/datadog/opentelemetry.rb +2 -1
  200. data/lib/datadog/profiling/collectors/code_provenance.rb +1 -1
  201. data/lib/datadog/profiling/collectors/info.rb +3 -0
  202. data/lib/datadog/profiling/collectors/thread_context.rb +1 -1
  203. data/lib/datadog/profiling/encoded_profile.rb +11 -0
  204. data/lib/datadog/profiling/exporter.rb +3 -4
  205. data/lib/datadog/profiling/ext.rb +0 -2
  206. data/lib/datadog/profiling/flush.rb +5 -8
  207. data/lib/datadog/profiling/http_transport.rb +5 -59
  208. data/lib/datadog/profiling/scheduler.rb +8 -1
  209. data/lib/datadog/profiling/stack_recorder.rb +4 -4
  210. data/lib/datadog/profiling/tag_builder.rb +1 -5
  211. data/lib/datadog/profiling.rb +6 -2
  212. data/lib/datadog/tracing/analytics.rb +1 -1
  213. data/lib/datadog/tracing/component.rb +15 -12
  214. data/lib/datadog/tracing/configuration/ext.rb +7 -1
  215. data/lib/datadog/tracing/configuration/settings.rb +18 -2
  216. data/lib/datadog/tracing/context_provider.rb +1 -1
  217. data/lib/datadog/tracing/contrib/active_record/integration.rb +1 -1
  218. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +4 -1
  219. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +33 -0
  220. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +4 -0
  221. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +2 -4
  222. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +10 -0
  223. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +5 -1
  224. data/lib/datadog/tracing/contrib/configuration/settings.rb +1 -1
  225. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -5
  226. data/lib/datadog/tracing/contrib/excon/middleware.rb +5 -3
  227. data/lib/datadog/tracing/contrib/ext.rb +1 -0
  228. data/lib/datadog/tracing/contrib/faraday/middleware.rb +5 -3
  229. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +7 -1
  230. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +3 -0
  231. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +0 -15
  232. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +4 -1
  233. data/lib/datadog/tracing/contrib/http/instrumentation.rb +6 -10
  234. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -16
  235. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +7 -15
  236. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +27 -0
  237. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +48 -0
  238. data/lib/datadog/tracing/contrib/karafka/ext.rb +27 -0
  239. data/lib/datadog/tracing/contrib/karafka/integration.rb +45 -0
  240. data/lib/datadog/tracing/contrib/karafka/monitor.rb +66 -0
  241. data/lib/datadog/tracing/contrib/karafka/patcher.rb +71 -0
  242. data/lib/datadog/tracing/contrib/karafka.rb +37 -0
  243. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +8 -0
  244. data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
  245. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +18 -1
  246. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +17 -0
  247. data/lib/datadog/tracing/contrib/opensearch/ext.rb +9 -0
  248. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +5 -1
  249. data/lib/datadog/tracing/contrib/patcher.rb +5 -2
  250. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
  251. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +5 -3
  252. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +6 -1
  253. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +3 -0
  254. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +1 -1
  255. data/lib/datadog/tracing/contrib/support.rb +28 -0
  256. data/lib/datadog/tracing/contrib.rb +1 -0
  257. data/lib/datadog/tracing/correlation.rb +9 -2
  258. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  259. data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
  260. data/lib/datadog/tracing/distributed/baggage.rb +131 -0
  261. data/lib/datadog/tracing/distributed/datadog.rb +4 -2
  262. data/lib/datadog/tracing/distributed/propagation.rb +25 -4
  263. data/lib/datadog/tracing/distributed/propagation_policy.rb +42 -0
  264. data/lib/datadog/tracing/metadata/errors.rb +4 -4
  265. data/lib/datadog/tracing/metadata/ext.rb +5 -0
  266. data/lib/datadog/tracing/metadata/metastruct.rb +36 -0
  267. data/lib/datadog/tracing/metadata/metastruct_tagging.rb +42 -0
  268. data/lib/datadog/tracing/metadata.rb +2 -0
  269. data/lib/datadog/tracing/sampling/rate_sampler.rb +2 -1
  270. data/lib/datadog/tracing/sampling/span/rule.rb +0 -1
  271. data/lib/datadog/tracing/span.rb +10 -1
  272. data/lib/datadog/tracing/span_event.rb +1 -1
  273. data/lib/datadog/tracing/span_operation.rb +46 -16
  274. data/lib/datadog/tracing/sync_writer.rb +1 -2
  275. data/lib/datadog/tracing/trace_digest.rb +9 -2
  276. data/lib/datadog/tracing/trace_operation.rb +44 -24
  277. data/lib/datadog/tracing/trace_segment.rb +6 -4
  278. data/lib/datadog/tracing/tracer.rb +45 -5
  279. data/lib/datadog/tracing/transport/http/api.rb +2 -10
  280. data/lib/datadog/tracing/transport/http/client.rb +5 -4
  281. data/lib/datadog/tracing/transport/http/traces.rb +13 -41
  282. data/lib/datadog/tracing/transport/http.rb +11 -44
  283. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -1
  284. data/lib/datadog/tracing/transport/trace_formatter.rb +7 -0
  285. data/lib/datadog/tracing/transport/traces.rb +26 -9
  286. data/lib/datadog/tracing/utils.rb +1 -1
  287. data/lib/datadog/tracing/workers/trace_writer.rb +2 -6
  288. data/lib/datadog/tracing/writer.rb +2 -6
  289. data/lib/datadog/tracing.rb +16 -3
  290. data/lib/datadog/version.rb +2 -2
  291. data/lib/datadog.rb +2 -3
  292. metadata +80 -19
  293. data/lib/datadog/appsec/contrib/devise/event.rb +0 -54
  294. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +0 -72
  295. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +0 -47
  296. data/lib/datadog/appsec/contrib/devise/resource.rb +0 -35
  297. data/lib/datadog/appsec/contrib/devise/tracking.rb +0 -57
  298. data/lib/datadog/appsec/utils/trace_operation.rb +0 -15
  299. data/lib/datadog/core/telemetry/http/env.rb +0 -20
  300. data/lib/datadog/core/telemetry/http/ext.rb +0 -28
  301. data/lib/datadog/core/telemetry/http/response.rb +0 -70
  302. data/lib/datadog/core/telemetry/http/transport.rb +0 -90
@@ -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
  }
@@ -312,6 +312,7 @@ VALUE thread_name_for(VALUE thread) {
312
312
  // to support our custom rb_profile_frames (see below)
313
313
  // Modifications:
314
314
  // * Support int first_lineno for Ruby 3.2.0+ (https://github.com/ruby/ruby/pull/6430)
315
+ // * Validate iseq and pos before calling `rb_iseq_line_no` as a safety measure (see comment below for details)
315
316
  //
316
317
  // `node_id` gets used depending on Ruby VM compilation settings (USE_ISEQ_NODE_ID being defined).
317
318
  // To avoid getting false "unused argument" warnings in setups where it's not used, we need to do this weird dance
@@ -358,6 +359,13 @@ calc_pos(const rb_iseq_t *iseq, const VALUE *pc, int *lineno, int *node_id)
358
359
  __builtin_trap();
359
360
  }
360
361
  #endif
362
+
363
+ // In PROF-11475 we spotted a crash when calling `rb_iseq_line_no` from this method. We couldn't reproduce or
364
+ // figure out the root cause, but "just in case", we're validating that the iseq looks valid and that the
365
+ // `n` used for the position is also sane, and if they don't look good, we don't calculate the line, rather
366
+ // than potentially trigger any issues.
367
+ if (RB_UNLIKELY(!RB_TYPE_P((VALUE) iseq, T_IMEMO) || n < 0 || n > ISEQ_BODY(iseq)->iseq_size)) return 0;
368
+
361
369
  if (lineno) *lineno = rb_iseq_line_no(iseq, pos);
362
370
  #ifdef USE_ISEQ_NODE_ID
363
371
  if (node_id) *node_id = rb_iseq_node_id(iseq, pos);
@@ -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();
@@ -8,6 +8,7 @@
8
8
  #include "ruby_helpers.h"
9
9
  #include "time_helpers.h"
10
10
  #include "heap_recorder.h"
11
+ #include "encoded_profile.h"
11
12
 
12
13
  // Used to wrap a ddog_prof_Profile in a Ruby object and expose Ruby-level serialization APIs
13
14
  // This file implements the native bits of the Datadog::Profiling::StackRecorder class
@@ -181,6 +182,7 @@ typedef struct {
181
182
  typedef struct {
182
183
  ddog_prof_Profile profile;
183
184
  stats_slot stats;
185
+ ddog_Timespec start_timestamp;
184
186
  } profile_slot;
185
187
 
186
188
  // Contains native state for each instance
@@ -252,7 +254,7 @@ static ddog_Timespec system_epoch_now_timespec(void);
252
254
  static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_instance);
253
255
  static void serializer_set_start_timestamp_for_next_profile(stack_recorder_state *state, ddog_Timespec start_time);
254
256
  static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE local_root_span_id, VALUE endpoint);
255
- static void reset_profile_slot(profile_slot *slot, ddog_Timespec *start_time /* Can be null */);
257
+ static void reset_profile_slot(profile_slot *slot, ddog_Timespec start_timestamp);
256
258
  static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE new_obj, VALUE weight, VALUE alloc_class);
257
259
  static VALUE _native_check_heap_hashes(DDTRACE_UNUSED VALUE _self, VALUE locations);
258
260
  static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
@@ -358,24 +360,26 @@ static void initialize_slot_concurrency_control(stack_recorder_state *state) {
358
360
  }
359
361
 
360
362
  static void initialize_profiles(stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types) {
363
+ ddog_Timespec start_timestamp = system_epoch_now_timespec();
364
+
361
365
  ddog_prof_Profile_NewResult slot_one_profile_result =
362
- ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
366
+ ddog_prof_Profile_new(sample_types, NULL /* period is optional */);
363
367
 
364
368
  if (slot_one_profile_result.tag == DDOG_PROF_PROFILE_NEW_RESULT_ERR) {
365
369
  rb_raise(rb_eRuntimeError, "Failed to initialize slot one profile: %"PRIsVALUE, get_error_details_and_drop(&slot_one_profile_result.err));
366
370
  }
367
371
 
368
- state->profile_slot_one = (profile_slot) { .profile = slot_one_profile_result.ok };
372
+ state->profile_slot_one = (profile_slot) { .profile = slot_one_profile_result.ok, .start_timestamp = start_timestamp };
369
373
 
370
374
  ddog_prof_Profile_NewResult slot_two_profile_result =
371
- ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
375
+ ddog_prof_Profile_new(sample_types, NULL /* period is optional */);
372
376
 
373
377
  if (slot_two_profile_result.tag == DDOG_PROF_PROFILE_NEW_RESULT_ERR) {
374
378
  // Note: No need to take any special care of slot one, it'll get cleaned up by stack_recorder_typed_data_free
375
379
  rb_raise(rb_eRuntimeError, "Failed to initialize slot two profile: %"PRIsVALUE, get_error_details_and_drop(&slot_two_profile_result.err));
376
380
  }
377
381
 
378
- state->profile_slot_two = (profile_slot) { .profile = slot_two_profile_result.ok };
382
+ state->profile_slot_two = (profile_slot) { .profile = slot_two_profile_result.ok, .start_timestamp = start_timestamp };
379
383
  }
380
384
 
381
385
  static void stack_recorder_typed_data_free(void *state_ptr) {
@@ -564,18 +568,14 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan
564
568
 
565
569
  state->stats_lifetime.serialization_successes++;
566
570
 
567
- VALUE encoded_pprof = ruby_string_from_vec_u8(serialized_profile.ok.buffer);
568
-
569
- ddog_Timespec ddprof_start = serialized_profile.ok.start;
570
- ddog_Timespec ddprof_finish = serialized_profile.ok.end;
571
-
572
- ddog_prof_EncodedProfile_drop(&serialized_profile.ok);
571
+ // Once we wrap this into a Ruby object, our `EncodedProfile` class will automatically manage memory for it
572
+ VALUE encoded_profile = from_ddog_prof_EncodedProfile(serialized_profile.ok);
573
573
 
574
- VALUE start = ruby_time_from(ddprof_start);
575
- VALUE finish = ruby_time_from(ddprof_finish);
574
+ VALUE start = ruby_time_from(args.slot->start_timestamp);
575
+ VALUE finish = ruby_time_from(finish_timestamp);
576
576
  VALUE profile_stats = build_profile_stats(args.slot, serialization_time_ns, heap_iteration_prep_time_ns, args.heap_profile_build_time_ns);
577
577
 
578
- return rb_ary_new_from_args(2, ok_symbol, rb_ary_new_from_args(4, start, finish, encoded_pprof, profile_stats));
578
+ return rb_ary_new_from_args(2, ok_symbol, rb_ary_new_from_args(4, start, finish, encoded_profile, profile_stats));
579
579
  }
580
580
 
581
581
  static VALUE ruby_time_from(ddog_Timespec ddprof_time) {
@@ -763,7 +763,6 @@ static void *call_serialize_without_gvl(void *call_args) {
763
763
  long serialize_no_gvl_start_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
764
764
 
765
765
  profile_slot *slot_now_inactive = serializer_flip_active_and_inactive_slots(args->state);
766
-
767
766
  args->slot = slot_now_inactive;
768
767
 
769
768
  // Now that we have the inactive profile with all but heap samples, lets fill it with heap data
@@ -772,7 +771,7 @@ static void *call_serialize_without_gvl(void *call_args) {
772
771
  args->heap_profile_build_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE) - serialize_no_gvl_start_time_ns;
773
772
 
774
773
  // Note: The profile gets reset by the serialize call
775
- args->result = ddog_prof_Profile_serialize(&args->slot->profile, &args->finish_timestamp, NULL /* duration_nanos is optional */, NULL /* start_time is optional */);
774
+ args->result = ddog_prof_Profile_serialize(&args->slot->profile, &args->slot->start_timestamp, &args->finish_timestamp);
776
775
  args->serialize_ran = true;
777
776
  args->serialize_no_gvl_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE) - serialize_no_gvl_start_time_ns;
778
777
 
@@ -780,7 +779,7 @@ static void *call_serialize_without_gvl(void *call_args) {
780
779
  }
781
780
 
782
781
  VALUE enforce_recorder_instance(VALUE object) {
783
- Check_TypedStruct(object, &stack_recorder_typed_data);
782
+ ENFORCE_TYPED_DATA(object, &stack_recorder_typed_data);
784
783
  return object;
785
784
  }
786
785
 
@@ -889,9 +888,9 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_
889
888
  // In case the fork happened halfway through `serializer_flip_active_and_inactive_slots` execution and the
890
889
  // resulting state is inconsistent, we make sure to reset it back to the initial state.
891
890
  initialize_slot_concurrency_control(state);
892
-
893
- reset_profile_slot(&state->profile_slot_one, /* start_time: */ NULL);
894
- reset_profile_slot(&state->profile_slot_two, /* start_time: */ NULL);
891
+ ddog_Timespec start_timestamp = system_epoch_now_timespec();
892
+ reset_profile_slot(&state->profile_slot_one, start_timestamp);
893
+ reset_profile_slot(&state->profile_slot_two, start_timestamp);
895
894
 
896
895
  heap_recorder_after_fork(state->heap_recorder);
897
896
 
@@ -903,7 +902,7 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_
903
902
  static void serializer_set_start_timestamp_for_next_profile(stack_recorder_state *state, ddog_Timespec start_time) {
904
903
  // Before making this profile active, we reset it so that it uses the correct start_time for its start
905
904
  profile_slot *next_profile_slot = (state->active_slot == 1) ? &state->profile_slot_two : &state->profile_slot_one;
906
- reset_profile_slot(next_profile_slot, &start_time);
905
+ reset_profile_slot(next_profile_slot, start_time);
907
906
  }
908
907
 
909
908
  static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE local_root_span_id, VALUE endpoint) {
@@ -948,11 +947,12 @@ static VALUE _native_check_heap_hashes(DDTRACE_UNUSED VALUE _self, VALUE locatio
948
947
  return Qnil;
949
948
  }
950
949
 
951
- static void reset_profile_slot(profile_slot *slot, ddog_Timespec *start_time /* Can be null */) {
952
- ddog_prof_Profile_Result reset_result = ddog_prof_Profile_reset(&slot->profile, start_time);
950
+ static void reset_profile_slot(profile_slot *slot, ddog_Timespec start_timestamp) {
951
+ ddog_prof_Profile_Result reset_result = ddog_prof_Profile_reset(&slot->profile);
953
952
  if (reset_result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
954
953
  rb_raise(rb_eRuntimeError, "Failed to reset profile: %"PRIsVALUE, get_error_details_and_drop(&reset_result.err));
955
954
  }
955
+ slot->start_timestamp = start_timestamp;
956
956
  slot->stats = (stats_slot) {};
957
957
  }
958
958
 
@@ -5,20 +5,14 @@
5
5
 
6
6
  static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
7
7
  static VALUE _native_stop(DDTRACE_UNUSED VALUE _self);
8
- static void crashtracker_init(VALUE crashtracking_module);
8
+
9
+ static bool first_init = true;
9
10
 
10
11
  // Used to report Ruby VM crashes.
11
12
  // Once initialized, segfaults will be reported automatically using libdatadog.
12
13
 
13
- void DDTRACE_EXPORT Init_libdatadog_api(void) {
14
- VALUE datadog_module = rb_define_module("Datadog");
15
- VALUE core_module = rb_define_module_under(datadog_module, "Core");
14
+ void crashtracker_init(VALUE core_module) {
16
15
  VALUE crashtracking_module = rb_define_module_under(core_module, "Crashtracking");
17
-
18
- crashtracker_init(crashtracking_module);
19
- }
20
-
21
- void crashtracker_init(VALUE crashtracking_module) {
22
16
  VALUE crashtracker_class = rb_define_class_under(crashtracking_module, "Component", rb_cObject);
23
17
 
24
18
  rb_define_singleton_method(crashtracker_class, "_native_start_or_update_on_fork", _native_start_or_update_on_fork, -1);
@@ -70,7 +64,7 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
70
64
  // "Process.kill('SEGV', Process.pid)" gets run.
71
65
  //
72
66
  // This actually changed in libdatadog 14, so I could see no issues with `create_alt_stack = true`, but not
73
- // overridding what Ruby set up seems a saner default to keep anyway.
67
+ // overriding what Ruby set up seems a saner default to keep anyway.
74
68
  .create_alt_stack = false,
75
69
  .use_alt_stack = true,
76
70
  .endpoint = endpoint,
@@ -100,9 +94,14 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
100
94
 
101
95
  ddog_VoidResult result =
102
96
  action == start_action ?
103
- ddog_crasht_init(config, receiver_config, metadata) :
97
+ (first_init ?
98
+ ddog_crasht_init(config, receiver_config, metadata) :
99
+ ddog_crasht_reconfigure(config, receiver_config, metadata)
100
+ ) :
104
101
  ddog_crasht_update_on_fork(config, receiver_config, metadata);
105
102
 
103
+ first_init = false;
104
+
106
105
  // Clean up before potentially raising any exceptions
107
106
  ddog_Vec_Tag_drop(tags);
108
107
  ddog_endpoint_drop(endpoint);
@@ -116,7 +115,7 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
116
115
  }
117
116
 
118
117
  static VALUE _native_stop(DDTRACE_UNUSED VALUE _self) {
119
- ddog_VoidResult result = ddog_crasht_shutdown();
118
+ ddog_VoidResult result = ddog_crasht_disable();
120
119
 
121
120
  if (result.tag == DDOG_VOID_RESULT_ERR) {
122
121
  rb_raise(rb_eRuntimeError, "Failed to stop the crash tracker: %"PRIsVALUE, get_error_details_and_drop(&result.err));
@@ -0,0 +1,5 @@
1
+ #pragma once
2
+
3
+ #include "datadog_ruby_common.h"
4
+
5
+ void crashtracker_init(VALUE core_module);
@@ -29,10 +29,7 @@ VALUE datadog_gem_version(void) {
29
29
  }
30
30
 
31
31
  static VALUE log_failure_to_process_tag(VALUE err_details) {
32
- VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
33
- VALUE logger = rb_funcall(datadog_module, rb_intern("logger"), 0);
34
-
35
- return rb_funcall(logger, rb_intern("warn"), 1, rb_sprintf("Failed to convert tag: %"PRIsVALUE, err_details));
32
+ return log_warning(rb_sprintf("Failed to convert tag: %"PRIsVALUE, err_details));
36
33
  }
37
34
 
38
35
  __attribute__((warn_unused_result))
@@ -27,6 +27,9 @@
27
27
  #define ENFORCE_BOOLEAN(value) \
28
28
  { if (RB_UNLIKELY(value != Qtrue && value != Qfalse)) raise_unexpected_type(value, ADD_QUOTES(value), "true or false", __FILE__, __LINE__, __func__); }
29
29
 
30
+ #define ENFORCE_TYPED_DATA(value, type) \
31
+ { if (RB_UNLIKELY(!rb_typeddata_is_kind_of(value, type))) raise_unexpected_type(value, ADD_QUOTES(value), "TypedData of type " ADD_QUOTES(type), __FILE__, __LINE__, __func__); }
32
+
30
33
  NORETURN(void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char* function_name));
31
34
 
32
35
  // Helper to retrieve Datadog::VERSION::STRING
@@ -38,6 +41,13 @@ static inline ddog_CharSlice char_slice_from_ruby_string(VALUE string) {
38
41
  return char_slice;
39
42
  }
40
43
 
44
+ static inline VALUE log_warning(VALUE warning) {
45
+ VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
46
+ VALUE logger = rb_funcall(datadog_module, rb_intern("logger"), 0);
47
+
48
+ return rb_funcall(logger, rb_intern("warn"), 1, warning);
49
+ }
50
+
41
51
  __attribute__((warn_unused_result))
42
52
  ddog_Vec_Tag convert_tags(VALUE tags_as_array);
43
53
 
@@ -0,0 +1,15 @@
1
+ #include <ruby.h>
2
+
3
+ #include "datadog_ruby_common.h"
4
+ #include "crashtracker.h"
5
+ #include "process_discovery.h"
6
+ #include "library_config.h"
7
+
8
+ void DDTRACE_EXPORT Init_libdatadog_api(void) {
9
+ VALUE datadog_module = rb_define_module("Datadog");
10
+ VALUE core_module = rb_define_module_under(datadog_module, "Core");
11
+
12
+ crashtracker_init(core_module);
13
+ process_discovery_init(core_module);
14
+ library_config_init(core_module);
15
+ }