ddtrace 1.14.0 → 1.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +165 -2
  3. data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +3 -5
  4. data/ext/ddtrace_profiling_native_extension/clock_id.h +0 -3
  5. data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +0 -22
  6. data/ext/ddtrace_profiling_native_extension/clock_id_noop.c +0 -1
  7. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +41 -6
  8. data/ext/ddtrace_profiling_native_extension/collectors_idle_sampling_helper.c +3 -0
  9. data/ext/ddtrace_profiling_native_extension/collectors_stack.c +76 -24
  10. data/ext/ddtrace_profiling_native_extension/collectors_stack.h +1 -1
  11. data/ext/ddtrace_profiling_native_extension/collectors_thread_context.c +207 -32
  12. data/ext/ddtrace_profiling_native_extension/collectors_thread_context.h +1 -1
  13. data/ext/ddtrace_profiling_native_extension/extconf.rb +8 -2
  14. data/ext/ddtrace_profiling_native_extension/http_transport.c +26 -10
  15. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.c +42 -0
  16. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +6 -0
  17. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +1 -16
  18. data/ext/ddtrace_profiling_native_extension/pid_controller.c +57 -0
  19. data/ext/ddtrace_profiling_native_extension/pid_controller.h +45 -0
  20. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +17 -12
  21. data/ext/ddtrace_profiling_native_extension/profiling.c +0 -2
  22. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +74 -37
  23. data/ext/ddtrace_profiling_native_extension/stack_recorder.h +13 -3
  24. data/lib/datadog/appsec/assets/waf_rules/processors.json +92 -0
  25. data/lib/datadog/appsec/assets/waf_rules/recommended.json +698 -75
  26. data/lib/datadog/appsec/assets/waf_rules/scanners.json +114 -0
  27. data/lib/datadog/appsec/assets/waf_rules/strict.json +98 -8
  28. data/lib/datadog/appsec/assets.rb +8 -0
  29. data/lib/datadog/appsec/component.rb +9 -2
  30. data/lib/datadog/appsec/configuration/settings.rb +61 -2
  31. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +6 -2
  32. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +8 -6
  33. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +2 -7
  34. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +2 -5
  35. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +2 -5
  36. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +3 -2
  37. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +24 -10
  38. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +3 -2
  39. data/lib/datadog/appsec/contrib/rails/patcher.rb +9 -3
  40. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +2 -5
  41. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +6 -4
  42. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +13 -7
  43. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +2 -5
  44. data/lib/datadog/appsec/event.rb +106 -50
  45. data/lib/datadog/appsec/monitor/gateway/watcher.rb +3 -3
  46. data/lib/datadog/appsec/monitor/reactive/set_user.rb +2 -5
  47. data/lib/datadog/appsec/processor/actions.rb +49 -0
  48. data/lib/datadog/appsec/processor/rule_merger.rb +22 -2
  49. data/lib/datadog/appsec/processor.rb +34 -6
  50. data/lib/datadog/appsec/remote.rb +4 -1
  51. data/lib/datadog/appsec/response.rb +82 -4
  52. data/lib/datadog/appsec/sample_rate.rb +21 -0
  53. data/lib/datadog/appsec.rb +2 -2
  54. data/lib/datadog/core/configuration/agent_settings_resolver.rb +29 -24
  55. data/lib/datadog/core/configuration/base.rb +1 -11
  56. data/lib/datadog/core/configuration/components.rb +7 -2
  57. data/lib/datadog/core/configuration/ext.rb +21 -0
  58. data/lib/datadog/core/configuration/option.rb +2 -4
  59. data/lib/datadog/core/configuration/option_definition.rb +17 -41
  60. data/lib/datadog/core/configuration/options.rb +5 -5
  61. data/lib/datadog/core/configuration/settings.rb +47 -45
  62. data/lib/datadog/core/environment/execution.rb +47 -9
  63. data/lib/datadog/core/environment/variable_helpers.rb +0 -69
  64. data/lib/datadog/core/error.rb +1 -0
  65. data/lib/datadog/core/git/ext.rb +2 -0
  66. data/lib/datadog/core/remote/client/capabilities.rb +1 -1
  67. data/lib/datadog/core/remote/component.rb +2 -2
  68. data/lib/datadog/core/remote/negotiation.rb +2 -2
  69. data/lib/datadog/core/remote/transport/config.rb +60 -0
  70. data/lib/datadog/core/remote/transport/http/api/instance.rb +39 -0
  71. data/lib/datadog/core/remote/transport/http/api/spec.rb +21 -0
  72. data/lib/datadog/core/remote/transport/http/api.rb +58 -0
  73. data/lib/datadog/core/remote/transport/http/builder.rb +219 -0
  74. data/lib/datadog/core/remote/transport/http/client.rb +48 -0
  75. data/lib/datadog/core/remote/transport/http/config.rb +280 -0
  76. data/lib/datadog/core/remote/transport/http/negotiation.rb +146 -0
  77. data/lib/datadog/core/remote/transport/http.rb +179 -0
  78. data/lib/datadog/core/{transport → remote/transport}/negotiation.rb +25 -23
  79. data/lib/datadog/core/remote/worker.rb +3 -1
  80. data/lib/datadog/core/telemetry/collector.rb +3 -2
  81. data/lib/datadog/core/telemetry/http/transport.rb +2 -1
  82. data/lib/datadog/core/transport/ext.rb +47 -0
  83. data/lib/datadog/core/transport/http/adapters/net.rb +168 -0
  84. data/lib/datadog/core/transport/http/adapters/registry.rb +29 -0
  85. data/lib/datadog/core/transport/http/adapters/test.rb +89 -0
  86. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +83 -0
  87. data/lib/datadog/core/transport/http/api/endpoint.rb +31 -0
  88. data/lib/datadog/core/transport/http/api/fallbacks.rb +26 -0
  89. data/lib/datadog/core/transport/http/api/map.rb +18 -0
  90. data/lib/datadog/core/transport/http/env.rb +62 -0
  91. data/lib/datadog/core/transport/http/response.rb +60 -0
  92. data/lib/datadog/core/transport/parcel.rb +22 -0
  93. data/lib/datadog/core/transport/request.rb +17 -0
  94. data/lib/datadog/core/transport/response.rb +64 -0
  95. data/lib/datadog/core/workers/polling.rb +2 -2
  96. data/lib/datadog/opentelemetry/api/context.rb +10 -3
  97. data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -1
  98. data/lib/datadog/opentelemetry/sdk/span_processor.rb +14 -2
  99. data/lib/datadog/opentelemetry/sdk/trace/span.rb +68 -0
  100. data/lib/datadog/opentelemetry/trace.rb +58 -0
  101. data/lib/datadog/opentelemetry.rb +1 -0
  102. data/lib/datadog/opentracer.rb +9 -0
  103. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +14 -19
  104. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  105. data/lib/datadog/profiling/collectors/thread_context.rb +9 -1
  106. data/lib/datadog/profiling/component.rb +24 -99
  107. data/lib/datadog/profiling/ext.rb +0 -12
  108. data/lib/datadog/profiling/flush.rb +0 -3
  109. data/lib/datadog/profiling/http_transport.rb +6 -3
  110. data/lib/datadog/profiling/native_extension.rb +0 -21
  111. data/lib/datadog/profiling/profiler.rb +36 -13
  112. data/lib/datadog/profiling/scheduler.rb +16 -9
  113. data/lib/datadog/profiling.rb +8 -81
  114. data/lib/datadog/tracing/component.rb +10 -4
  115. data/lib/datadog/tracing/configuration/agent_settings_resolver.rb +13 -0
  116. data/lib/datadog/tracing/configuration/ext.rb +4 -2
  117. data/lib/datadog/tracing/configuration/settings.rb +14 -7
  118. data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +1 -1
  119. data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +1 -1
  120. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +4 -0
  121. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +106 -197
  122. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +3 -0
  123. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +7 -0
  124. data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +14 -14
  125. data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +3 -10
  126. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +2 -1
  127. data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +8 -1
  128. data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +22 -0
  129. data/lib/datadog/tracing/contrib/configuration/settings.rb +1 -1
  130. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +6 -0
  131. data/lib/datadog/tracing/contrib/dalli/ext.rb +7 -0
  132. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +9 -2
  133. data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +1 -1
  134. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +5 -0
  135. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +5 -0
  136. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +8 -0
  137. data/lib/datadog/tracing/contrib/excon/middleware.rb +5 -0
  138. data/lib/datadog/tracing/contrib/ext.rb +3 -0
  139. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +1 -1
  140. data/lib/datadog/tracing/contrib/faraday/middleware.rb +5 -0
  141. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +21 -1
  142. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +11 -1
  143. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +18 -0
  144. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +0 -4
  145. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +3 -3
  146. data/lib/datadog/tracing/contrib/http/instrumentation.rb +5 -0
  147. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +5 -0
  148. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +5 -0
  149. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +7 -0
  150. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +13 -3
  151. data/lib/datadog/tracing/contrib/opensearch/integration.rb +2 -2
  152. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +7 -0
  153. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +5 -0
  154. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +5 -0
  155. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +1 -1
  156. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +1 -1
  157. data/lib/datadog/tracing/contrib/racecar/event.rb +5 -0
  158. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +14 -4
  159. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +4 -4
  160. data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +1 -1
  161. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +1 -1
  162. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +3 -38
  163. data/lib/datadog/tracing/contrib/redis/tags.rb +7 -2
  164. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +46 -33
  165. data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +1 -1
  166. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +5 -0
  167. data/lib/datadog/tracing/contrib/sequel/utils.rb +5 -0
  168. data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +1 -1
  169. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +1 -1
  170. data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +1 -1
  171. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +2 -2
  172. data/lib/datadog/tracing/diagnostics/environment_logger.rb +6 -0
  173. data/lib/datadog/tracing/distributed/propagation.rb +13 -33
  174. data/lib/datadog/tracing/metadata/tagging.rb +3 -3
  175. data/lib/datadog/tracing/sync_writer.rb +3 -3
  176. data/lib/datadog/tracing/tracer.rb +2 -0
  177. data/lib/datadog/{core → tracing}/transport/http/api/instance.rb +1 -1
  178. data/lib/datadog/{core → tracing}/transport/http/api/spec.rb +1 -1
  179. data/lib/datadog/tracing/transport/http/api.rb +43 -0
  180. data/lib/datadog/{core → tracing}/transport/http/builder.rb +13 -68
  181. data/lib/datadog/tracing/transport/http/client.rb +57 -0
  182. data/lib/datadog/tracing/transport/http/statistics.rb +47 -0
  183. data/lib/datadog/tracing/transport/http/traces.rb +152 -0
  184. data/lib/datadog/tracing/transport/http.rb +124 -0
  185. data/lib/datadog/tracing/transport/io/client.rb +89 -0
  186. data/lib/datadog/tracing/transport/io/response.rb +27 -0
  187. data/lib/datadog/tracing/transport/io/traces.rb +101 -0
  188. data/lib/datadog/tracing/transport/io.rb +30 -0
  189. data/lib/datadog/tracing/transport/serializable_trace.rb +126 -0
  190. data/lib/datadog/tracing/transport/statistics.rb +77 -0
  191. data/lib/datadog/tracing/transport/trace_formatter.rb +209 -0
  192. data/lib/datadog/tracing/transport/traces.rb +224 -0
  193. data/lib/datadog/tracing/workers/trace_writer.rb +5 -3
  194. data/lib/datadog/tracing/workers.rb +3 -2
  195. data/lib/datadog/tracing/writer.rb +5 -2
  196. data/lib/ddtrace/transport/ext.rb +17 -15
  197. data/lib/ddtrace/version.rb +2 -2
  198. data/lib/ddtrace.rb +1 -1
  199. metadata +73 -96
  200. data/lib/datadog/ci/configuration/components.rb +0 -32
  201. data/lib/datadog/ci/configuration/settings.rb +0 -51
  202. data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +0 -35
  203. data/lib/datadog/ci/contrib/cucumber/ext.rb +0 -22
  204. data/lib/datadog/ci/contrib/cucumber/formatter.rb +0 -94
  205. data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +0 -28
  206. data/lib/datadog/ci/contrib/cucumber/integration.rb +0 -47
  207. data/lib/datadog/ci/contrib/cucumber/patcher.rb +0 -27
  208. data/lib/datadog/ci/contrib/minitest/configuration/settings.rb +0 -35
  209. data/lib/datadog/ci/contrib/minitest/ext.rb +0 -21
  210. data/lib/datadog/ci/contrib/minitest/integration.rb +0 -49
  211. data/lib/datadog/ci/contrib/minitest/patcher.rb +0 -27
  212. data/lib/datadog/ci/contrib/minitest/test_helper.rb +0 -68
  213. data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +0 -35
  214. data/lib/datadog/ci/contrib/rspec/example.rb +0 -68
  215. data/lib/datadog/ci/contrib/rspec/ext.rb +0 -21
  216. data/lib/datadog/ci/contrib/rspec/integration.rb +0 -48
  217. data/lib/datadog/ci/contrib/rspec/patcher.rb +0 -27
  218. data/lib/datadog/ci/ext/app_types.rb +0 -9
  219. data/lib/datadog/ci/ext/environment.rb +0 -575
  220. data/lib/datadog/ci/ext/settings.rb +0 -10
  221. data/lib/datadog/ci/ext/test.rb +0 -35
  222. data/lib/datadog/ci/extensions.rb +0 -19
  223. data/lib/datadog/ci/flush.rb +0 -38
  224. data/lib/datadog/ci/test.rb +0 -81
  225. data/lib/datadog/ci.rb +0 -21
  226. data/lib/datadog/core/configuration/dependency_resolver.rb +0 -28
  227. data/lib/datadog/core/configuration/option_definition_set.rb +0 -22
  228. data/lib/datadog/core/configuration/option_set.rb +0 -10
  229. data/lib/datadog/core/transport/config.rb +0 -58
  230. data/lib/datadog/core/transport/http/api.rb +0 -57
  231. data/lib/datadog/core/transport/http/client.rb +0 -45
  232. data/lib/datadog/core/transport/http/config.rb +0 -278
  233. data/lib/datadog/core/transport/http/negotiation.rb +0 -144
  234. data/lib/datadog/core/transport/http.rb +0 -169
  235. data/lib/datadog/core/utils/object_set.rb +0 -43
  236. data/lib/datadog/core/utils/string_table.rb +0 -47
  237. data/lib/datadog/profiling/backtrace_location.rb +0 -34
  238. data/lib/datadog/profiling/buffer.rb +0 -43
  239. data/lib/datadog/profiling/collectors/old_stack.rb +0 -301
  240. data/lib/datadog/profiling/encoding/profile.rb +0 -41
  241. data/lib/datadog/profiling/event.rb +0 -15
  242. data/lib/datadog/profiling/events/stack.rb +0 -82
  243. data/lib/datadog/profiling/old_recorder.rb +0 -107
  244. data/lib/datadog/profiling/pprof/builder.rb +0 -125
  245. data/lib/datadog/profiling/pprof/converter.rb +0 -102
  246. data/lib/datadog/profiling/pprof/message_set.rb +0 -16
  247. data/lib/datadog/profiling/pprof/payload.rb +0 -20
  248. data/lib/datadog/profiling/pprof/pprof.proto +0 -212
  249. data/lib/datadog/profiling/pprof/pprof_pb.rb +0 -81
  250. data/lib/datadog/profiling/pprof/stack_sample.rb +0 -139
  251. data/lib/datadog/profiling/pprof/string_table.rb +0 -12
  252. data/lib/datadog/profiling/pprof/template.rb +0 -118
  253. data/lib/datadog/profiling/trace_identifiers/ddtrace.rb +0 -43
  254. data/lib/datadog/profiling/trace_identifiers/helper.rb +0 -45
  255. data/lib/ddtrace/transport/http/adapters/net.rb +0 -168
  256. data/lib/ddtrace/transport/http/adapters/registry.rb +0 -27
  257. data/lib/ddtrace/transport/http/adapters/test.rb +0 -85
  258. data/lib/ddtrace/transport/http/adapters/unix_socket.rb +0 -77
  259. data/lib/ddtrace/transport/http/api/endpoint.rb +0 -29
  260. data/lib/ddtrace/transport/http/api/fallbacks.rb +0 -24
  261. data/lib/ddtrace/transport/http/api/instance.rb +0 -35
  262. data/lib/ddtrace/transport/http/api/map.rb +0 -16
  263. data/lib/ddtrace/transport/http/api/spec.rb +0 -17
  264. data/lib/ddtrace/transport/http/api.rb +0 -39
  265. data/lib/ddtrace/transport/http/builder.rb +0 -176
  266. data/lib/ddtrace/transport/http/client.rb +0 -52
  267. data/lib/ddtrace/transport/http/env.rb +0 -58
  268. data/lib/ddtrace/transport/http/response.rb +0 -58
  269. data/lib/ddtrace/transport/http/statistics.rb +0 -43
  270. data/lib/ddtrace/transport/http/traces.rb +0 -144
  271. data/lib/ddtrace/transport/http.rb +0 -117
  272. data/lib/ddtrace/transport/io/client.rb +0 -85
  273. data/lib/ddtrace/transport/io/response.rb +0 -25
  274. data/lib/ddtrace/transport/io/traces.rb +0 -99
  275. data/lib/ddtrace/transport/io.rb +0 -28
  276. data/lib/ddtrace/transport/parcel.rb +0 -20
  277. data/lib/ddtrace/transport/request.rb +0 -15
  278. data/lib/ddtrace/transport/response.rb +0 -60
  279. data/lib/ddtrace/transport/serializable_trace.rb +0 -122
  280. data/lib/ddtrace/transport/statistics.rb +0 -75
  281. data/lib/ddtrace/transport/trace_formatter.rb +0 -207
  282. data/lib/ddtrace/transport/traces.rb +0 -216
@@ -82,9 +82,9 @@ end
82
82
  # Because we can't control what compiler versions our customers use, shipping with -Werror by default is a no-go.
83
83
  # But we can enable it in CI, so that we quickly spot any new warnings that just got introduced.
84
84
  #
85
- # @ivoanjo TODO: Ruby 3.3.0-preview1 was causing issues in CI because `have_header('vm_core.h')` below triggers warnings;
85
+ # @ivoanjo TODO: 3.3.0-preview releases are causing issues in CI because `have_header('vm_core.h')` below triggers warnings;
86
86
  # I've chosen to disable `-Werror` for this Ruby version for now, and we can revisit this on a later 3.3 release.
87
- add_compiler_flag '-Werror' if ENV['DDTRACE_CI'] == 'true' && !RUBY_DESCRIPTION.include?('3.3.0preview1')
87
+ add_compiler_flag '-Werror' if ENV['DDTRACE_CI'] == 'true' && !RUBY_DESCRIPTION.include?('3.3.0preview')
88
88
 
89
89
  # Older gcc releases may not default to C99 and we need to ask for this. This is also used:
90
90
  # * by upstream Ruby -- search for gnu99 in the codebase
@@ -146,6 +146,9 @@ $defs << '-DNO_RB_THREAD_SCHED' if RUBY_VERSION < '3.2'
146
146
  # On older Rubies, the first_lineno inside a location was a VALUE and not a int (https://github.com/ruby/ruby/pull/6430)
147
147
  $defs << '-DNO_INT_FIRST_LINENO' if RUBY_VERSION < '3.2'
148
148
 
149
+ # On older Rubies, "pop" was not a primitive operation
150
+ $defs << '-DNO_PRIMITIVE_POP' if RUBY_VERSION < '3.2'
151
+
149
152
  # On older Rubies, there was no tid member in the internal thread structure
150
153
  $defs << '-DNO_THREAD_TID' if RUBY_VERSION < '3.1'
151
154
 
@@ -155,6 +158,9 @@ $defs << '-DUSE_BACKPORTED_RB_PROFILE_FRAME_METHOD_NAME' if RUBY_VERSION < '3'
155
158
  # On older Rubies, there are no Ractors
156
159
  $defs << '-DNO_RACTORS' if RUBY_VERSION < '3'
157
160
 
161
+ # On older Rubies, objects would not move
162
+ $defs << '-DNO_T_MOVED' if RUBY_VERSION < '2.7'
163
+
158
164
  # On older Rubies, rb_global_vm_lock_struct did not include the owner field
159
165
  $defs << '-DNO_GVL_OWNER' if RUBY_VERSION < '2.6'
160
166
 
@@ -201,7 +201,8 @@ static VALUE perform_export(
201
201
  ddog_prof_Exporter *exporter,
202
202
  ddog_Timespec start,
203
203
  ddog_Timespec finish,
204
- ddog_prof_Exporter_Slice_File slice_files,
204
+ ddog_prof_Exporter_Slice_File files_to_compress_and_export,
205
+ ddog_prof_Exporter_Slice_File files_to_export_unmodified,
205
206
  ddog_Vec_Tag *additional_tags,
206
207
  ddog_CharSlice internal_metadata,
207
208
  uint64_t timeout_milliseconds
@@ -211,7 +212,8 @@ static VALUE perform_export(
211
212
  exporter,
212
213
  start,
213
214
  finish,
214
- slice_files,
215
+ files_to_compress_and_export,
216
+ files_to_export_unmodified,
215
217
  additional_tags,
216
218
  endpoints_stats,
217
219
  &internal_metadata,
@@ -308,18 +310,23 @@ static VALUE _native_do_export(
308
310
  ddog_Timespec finish =
309
311
  {.seconds = NUM2LONG(finish_timespec_seconds), .nanoseconds = NUM2UINT(finish_timespec_nanoseconds)};
310
312
 
311
- int files_to_report = 1 + (have_code_provenance ? 1 : 0);
312
- ddog_prof_Exporter_File files[files_to_report];
313
- ddog_prof_Exporter_Slice_File slice_files = {.ptr = files, .len = files_to_report};
313
+ int to_compress_length = have_code_provenance ? 1 : 0;
314
+ ddog_prof_Exporter_File to_compress[to_compress_length];
315
+ int already_compressed_length = 1; // pprof
316
+ ddog_prof_Exporter_File already_compressed[already_compressed_length];
314
317
 
315
- files[0] = (ddog_prof_Exporter_File) {
318
+ ddog_prof_Exporter_Slice_File files_to_compress_and_export = {.ptr = to_compress, .len = to_compress_length};
319
+ ddog_prof_Exporter_Slice_File files_to_export_unmodified = {.ptr = already_compressed, .len = already_compressed_length};
320
+
321
+ already_compressed[0] = (ddog_prof_Exporter_File) {
316
322
  .name = char_slice_from_ruby_string(pprof_file_name),
317
- .file = byte_slice_from_ruby_string(pprof_data)
323
+ .file = byte_slice_from_ruby_string(pprof_data),
318
324
  };
325
+
319
326
  if (have_code_provenance) {
320
- files[1] = (ddog_prof_Exporter_File) {
327
+ to_compress[0] = (ddog_prof_Exporter_File) {
321
328
  .name = char_slice_from_ruby_string(code_provenance_file_name),
322
- .file = byte_slice_from_ruby_string(code_provenance_data)
329
+ .file = byte_slice_from_ruby_string(code_provenance_data),
323
330
  };
324
331
  }
325
332
 
@@ -332,7 +339,16 @@ static VALUE _native_do_export(
332
339
  VALUE failure_tuple = handle_exporter_failure(exporter_result);
333
340
  if (!NIL_P(failure_tuple)) return failure_tuple;
334
341
 
335
- return perform_export(exporter_result.ok, start, finish, slice_files, null_additional_tags, internal_metadata, timeout_milliseconds);
342
+ return perform_export(
343
+ exporter_result.ok,
344
+ start,
345
+ finish,
346
+ files_to_compress_and_export,
347
+ files_to_export_unmodified,
348
+ null_additional_tags,
349
+ internal_metadata,
350
+ timeout_milliseconds
351
+ );
336
352
  }
337
353
 
338
354
  static void *call_exporter_without_gvl(void *call_args) {
@@ -0,0 +1,42 @@
1
+ #include "libdatadog_helpers.h"
2
+
3
+ #include <ruby.h>
4
+
5
+ const char *ruby_value_type_to_string(enum ruby_value_type type) {
6
+ return ruby_value_type_to_char_slice(type).ptr;
7
+ }
8
+
9
+ ddog_CharSlice ruby_value_type_to_char_slice(enum ruby_value_type type) {
10
+ switch (type) {
11
+ case(RUBY_T_NONE ): return DDOG_CHARSLICE_C("T_NONE");
12
+ case(RUBY_T_OBJECT ): return DDOG_CHARSLICE_C("T_OBJECT");
13
+ case(RUBY_T_CLASS ): return DDOG_CHARSLICE_C("T_CLASS");
14
+ case(RUBY_T_MODULE ): return DDOG_CHARSLICE_C("T_MODULE");
15
+ case(RUBY_T_FLOAT ): return DDOG_CHARSLICE_C("T_FLOAT");
16
+ case(RUBY_T_STRING ): return DDOG_CHARSLICE_C("T_STRING");
17
+ case(RUBY_T_REGEXP ): return DDOG_CHARSLICE_C("T_REGEXP");
18
+ case(RUBY_T_ARRAY ): return DDOG_CHARSLICE_C("T_ARRAY");
19
+ case(RUBY_T_HASH ): return DDOG_CHARSLICE_C("T_HASH");
20
+ case(RUBY_T_STRUCT ): return DDOG_CHARSLICE_C("T_STRUCT");
21
+ case(RUBY_T_BIGNUM ): return DDOG_CHARSLICE_C("T_BIGNUM");
22
+ case(RUBY_T_FILE ): return DDOG_CHARSLICE_C("T_FILE");
23
+ case(RUBY_T_DATA ): return DDOG_CHARSLICE_C("T_DATA");
24
+ case(RUBY_T_MATCH ): return DDOG_CHARSLICE_C("T_MATCH");
25
+ case(RUBY_T_COMPLEX ): return DDOG_CHARSLICE_C("T_COMPLEX");
26
+ case(RUBY_T_RATIONAL): return DDOG_CHARSLICE_C("T_RATIONAL");
27
+ case(RUBY_T_NIL ): return DDOG_CHARSLICE_C("T_NIL");
28
+ case(RUBY_T_TRUE ): return DDOG_CHARSLICE_C("T_TRUE");
29
+ case(RUBY_T_FALSE ): return DDOG_CHARSLICE_C("T_FALSE");
30
+ case(RUBY_T_SYMBOL ): return DDOG_CHARSLICE_C("T_SYMBOL");
31
+ case(RUBY_T_FIXNUM ): return DDOG_CHARSLICE_C("T_FIXNUM");
32
+ case(RUBY_T_UNDEF ): return DDOG_CHARSLICE_C("T_UNDEF");
33
+ case(RUBY_T_IMEMO ): return DDOG_CHARSLICE_C("T_IMEMO");
34
+ case(RUBY_T_NODE ): return DDOG_CHARSLICE_C("T_NODE");
35
+ case(RUBY_T_ICLASS ): return DDOG_CHARSLICE_C("T_ICLASS");
36
+ case(RUBY_T_ZOMBIE ): return DDOG_CHARSLICE_C("T_ZOMBIE");
37
+ #ifndef NO_T_MOVED
38
+ case(RUBY_T_MOVED ): return DDOG_CHARSLICE_C("T_MOVED");
39
+ #endif
40
+ default: return DDOG_CHARSLICE_C("BUG: Unknown value for ruby_value_type");
41
+ }
42
+ }
@@ -23,3 +23,9 @@ inline static VALUE get_error_details_and_drop(ddog_Error *error) {
23
23
  ddog_Error_drop(error);
24
24
  return result;
25
25
  }
26
+
27
+ // Used for pretty printing this Ruby enum. Returns "T_UNKNOWN_OR_MISSING_RUBY_VALUE_TYPE_ENTRY" for unknown elements.
28
+ // In practice, there's a few types that the profiler will probably never encounter, but I've added all entries of
29
+ // ruby_value_type that Ruby uses so that we can also use this for debugging.
30
+ const char *ruby_value_type_to_string(enum ruby_value_type type);
31
+ ddog_CharSlice ruby_value_type_to_char_slice(enum ruby_value_type type);
@@ -15,7 +15,7 @@ module Datadog
15
15
  # The MJIT header was introduced on 2.6 and removed on 3.3; for other Rubies we rely on debase-ruby_core_source
16
16
  CAN_USE_MJIT_HEADER = RUBY_VERSION.start_with?('2.6', '2.7', '3.0.', '3.1.', '3.2.')
17
17
 
18
- LIBDATADOG_VERSION = '~> 3.0.0.1.0'
18
+ LIBDATADOG_VERSION = '~> 5.0.0.1.0'
19
19
 
20
20
  def self.fail_install_if_missing_extension?
21
21
  ENV[ENV_FAIL_INSTALL_IF_MISSING_EXTENSION].to_s.strip.downcase == 'true'
@@ -87,7 +87,6 @@ module Datadog
87
87
  on_unknown_os? ||
88
88
  on_unsupported_cpu_arch? ||
89
89
  on_unsupported_ruby_version? ||
90
- on_ruby_3_3? ||
91
90
  expected_to_use_mjit_but_mjit_is_disabled? ||
92
91
  libdatadog_not_available? ||
93
92
  libdatadog_not_usable?
@@ -270,20 +269,6 @@ module Datadog
270
269
  ruby_version_not_supported if RUBY_VERSION.start_with?('2.1.', '2.2.')
271
270
  end
272
271
 
273
- private_class_method def self.on_ruby_3_3?
274
- incompatible_with_3_3 = explain_issue(
275
- 'the profiler in the current version of ddtrace does not yet support',
276
- 'Ruby version 3.3.',
277
- '(See https://github.com/datadog/dd-trace-rb/issues/3053 for details).',
278
- suggested: [
279
- 'Try upgrading to the latest ddtrace, as this issue may have been',
280
- 'fixed by now.',
281
- ] + CONTACT_SUPPORT,
282
- )
283
-
284
- incompatible_with_3_3 if RUBY_VERSION.start_with?('3.3.')
285
- end
286
-
287
272
  # On some Rubies, we require the mjit header to be present. If Ruby was installed without MJIT support, we also skip
288
273
  # building the extension.
289
274
  private_class_method def self.expected_to_use_mjit_but_mjit_is_disabled?
@@ -0,0 +1,57 @@
1
+ /*
2
+ * Copyright 2023 Datadog, Inc
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ // Originally imported from https://github.com/DataDog/java-profiler/blob/11fe6206c31a14c6e5134e8401eaec8b22c618d7/ddprof-lib/src/main/cpp/pidController.cpp
18
+
19
+ #include "pid_controller.h"
20
+
21
+ #include <math.h>
22
+
23
+ inline static double computeAlpha(float cutoff) {
24
+ if (cutoff <= 0)
25
+ return 1;
26
+ // α(fₙ) = cos(2πfₙ) - 1 + √( cos(2πfₙ)² - 4 cos(2πfₙ) + 3 )
27
+ const double c = cos(2 * ((double) M_PI) * cutoff);
28
+ return c - 1 + sqrt(c * c - 4 * c + 3);
29
+ }
30
+
31
+ void pid_controller_init(pid_controller *controller, u64 target_per_second, double proportional_gain, double integral_gain, double derivative_gain, int sampling_window, double cutoff_secs) {
32
+ controller->_target = target_per_second * sampling_window;
33
+ controller->_proportional_gain = proportional_gain;
34
+ controller->_integral_gain = integral_gain * sampling_window;
35
+ controller->_derivative_gain = derivative_gain / sampling_window;
36
+ controller->_alpha = computeAlpha(sampling_window / cutoff_secs);
37
+ controller->_avg_error= 0;
38
+ controller->_integral_value = 0;
39
+ }
40
+
41
+ double pid_controller_compute(pid_controller *controller, u64 input, double time_delta_coefficient) {
42
+ // time_delta_coefficient allows variable sampling window
43
+ // the values are linearly scaled using that coefficient to reinterpret the given value within the expected sampling window
44
+ double absolute_error = (((double) controller->_target) - ((double) input)) * time_delta_coefficient;
45
+
46
+ double avg_error = (controller->_alpha * absolute_error) + ((1 - controller->_alpha) * controller->_avg_error);
47
+ double derivative = avg_error - controller->_avg_error;
48
+
49
+ // PID formula:
50
+ // u[k] = Kp e[k] + Ki e_i[k] + Kd e_d[k], control signal
51
+ double signal = controller->_proportional_gain * absolute_error + controller->_integral_gain * controller->_integral_value + controller->_derivative_gain * derivative;
52
+
53
+ controller->_integral_value += absolute_error;
54
+ controller->_avg_error = avg_error;
55
+
56
+ return signal;
57
+ }
@@ -0,0 +1,45 @@
1
+ /*
2
+ * Copyright 2023 Datadog, Inc
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ // Originally imported from https://github.com/DataDog/java-profiler/blob/11fe6206c31a14c6e5134e8401eaec8b22c618d7/ddprof-lib/src/main/cpp/pidController.h
18
+
19
+ #ifndef _PIDCONTROLLER_H
20
+ #define _PIDCONTROLLER_H
21
+
22
+ // From arch.h in java-profiler
23
+ typedef unsigned long long u64;
24
+
25
+ /*
26
+ * A simple implementation of a PID controller.
27
+ * Heavily influenced by https://tttapa.github.io/Pages/Arduino/Control-Theory/Motor-Fader/PID-Cpp-Implementation.html
28
+ */
29
+
30
+ typedef struct {
31
+ u64 _target;
32
+ double _proportional_gain;
33
+ double _derivative_gain;
34
+ double _integral_gain;
35
+ double _alpha;
36
+
37
+ double _avg_error;
38
+ long long _integral_value;
39
+ } pid_controller;
40
+
41
+ void pid_controller_init(pid_controller *controller, u64 target_per_second, double proportional_gain, double integral_gain, double derivative_gain, int sampling_window, double cutoff_secs);
42
+
43
+ double pid_controller_compute(pid_controller *controller, u64 input, double time_delta_seconds);
44
+
45
+ #endif
@@ -82,6 +82,16 @@ bool is_current_thread_holding_the_gvl(void) {
82
82
  return owner.valid && pthread_equal(pthread_self(), owner.owner);
83
83
  }
84
84
 
85
+ #ifdef HAVE_RUBY_RACTOR_H
86
+ static inline rb_ractor_t *ddtrace_get_ractor(void) {
87
+ #ifndef USE_RACTOR_INTERNAL_APIS_DIRECTLY // Ruby >= 3.3
88
+ return thread_struct_from_object(rb_thread_current())->ractor;
89
+ #else
90
+ return GET_RACTOR();
91
+ #endif
92
+ }
93
+ #endif
94
+
85
95
  #ifndef NO_GVL_OWNER // Ruby < 2.6 doesn't have the owner/running field
86
96
  // NOTE: Reading the owner in this is a racy read, because we're not grabbing the lock that Ruby uses to protect it.
87
97
  //
@@ -94,9 +104,9 @@ bool is_current_thread_holding_the_gvl(void) {
94
104
  current_gvl_owner gvl_owner(void) {
95
105
  const rb_thread_t *current_owner =
96
106
  #ifndef NO_RB_THREAD_SCHED // Introduced in Ruby 3.2 as a replacement for struct rb_global_vm_lock_struct
97
- GET_RACTOR()->threads.sched.running;
107
+ ddtrace_get_ractor()->threads.sched.running;
98
108
  #elif HAVE_RUBY_RACTOR_H
99
- GET_RACTOR()->threads.gvl.owner;
109
+ ddtrace_get_ractor()->threads.gvl.owner;
100
110
  #else
101
111
  GET_VM()->gvl.owner;
102
112
  #endif
@@ -234,7 +244,7 @@ void ddtrace_thread_list(VALUE result_array) {
234
244
  // I suspect the design in `rb_ractor_thread_list` may be done that way to perhaps in the future expose it to be
235
245
  // called from a different Ractor, but I'm not sure...
236
246
  #ifdef HAVE_RUBY_RACTOR_H
237
- rb_ractor_t *current_ractor = GET_RACTOR();
247
+ rb_ractor_t *current_ractor = ddtrace_get_ractor();
238
248
  ccan_list_for_each(&current_ractor->threads.set, thread, lt_node) {
239
249
  #else
240
250
  rb_vm_t *vm =
@@ -736,15 +746,10 @@ check_method_entry(VALUE obj, int can_be_svar)
736
746
  // versions, so we need to do a bit more work.
737
747
  struct rb_ractor_struct *ruby_single_main_ractor = NULL;
738
748
 
739
- // Taken from upstream ractor.c at commit a1b01e7701f9fc370f8dff777aad6d39a2c5a3e3 (May 2023, Ruby 3.3.0-preview1)
740
- // to allow us to ensure that we're always operating on the main ractor (if Ruby has ractors)
741
- // Modifications:
742
- // * None
743
- bool rb_ractor_main_p_(void)
744
- {
745
- VM_ASSERT(rb_multi_ractor_p());
746
- rb_execution_context_t *ec = GET_EC();
747
- return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor;
749
+ // Alternative implementation of rb_ractor_main_p_ that avoids relying on non-public symbols
750
+ bool rb_ractor_main_p_(void) {
751
+ // We need to get the main ractor in a bit of a roundabout way, since Ruby >= 3.3 hid `GET_VM()`
752
+ return ddtrace_get_ractor() == thread_struct_from_object(rb_thread_current())->vm->ractor.main_ractor;
748
753
  }
749
754
  #else
750
755
  // Directly access Ruby internal fast path for detecting multiple Ractors.
@@ -41,8 +41,6 @@ void DDTRACE_EXPORT Init_ddtrace_profiling_native_extension(void) {
41
41
  rb_define_singleton_method(native_extension_module, "native_working?", native_working_p, 0);
42
42
  rb_funcall(native_extension_module, rb_intern("private_class_method"), 1, ID2SYM(rb_intern("native_working?")));
43
43
 
44
- rb_define_singleton_method(native_extension_module, "clock_id_for", clock_id_for, 1); // from clock_id.h
45
-
46
44
  collectors_cpu_and_wall_time_worker_init(profiling_module);
47
45
  collectors_dynamic_sampling_rate_init(profiling_module);
48
46
  collectors_idle_sampling_helper_init(profiling_module);
@@ -165,10 +165,10 @@ static const uint8_t all_value_types_positions[] = {CPU_TIME_VALUE_ID, CPU_SAMPL
165
165
  // Contains native state for each instance
166
166
  struct stack_recorder_state {
167
167
  pthread_mutex_t slot_one_mutex;
168
- ddog_prof_Profile *slot_one_profile;
168
+ ddog_prof_Profile slot_one_profile;
169
169
 
170
170
  pthread_mutex_t slot_two_mutex;
171
- ddog_prof_Profile *slot_two_profile;
171
+ ddog_prof_Profile slot_two_profile;
172
172
 
173
173
  short active_slot; // MUST NEVER BE ACCESSED FROM record_sample; this is NOT for the sampler thread to use.
174
174
 
@@ -197,6 +197,7 @@ struct call_serialize_without_gvl_arguments {
197
197
 
198
198
  static VALUE _native_new(VALUE klass);
199
199
  static void initialize_slot_concurrency_control(struct stack_recorder_state *state);
200
+ static void initialize_profiles(struct stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types);
200
201
  static void stack_recorder_typed_data_free(void *data);
201
202
  static VALUE _native_initialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE cpu_time_enabled, VALUE alloc_samples_enabled);
202
203
  static VALUE _native_serialize(VALUE self, VALUE recorder_instance);
@@ -211,8 +212,9 @@ static VALUE _native_is_slot_two_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE
211
212
  static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot);
212
213
  static ddog_Timespec system_epoch_now_timespec(void);
213
214
  static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_instance);
214
- static void serializer_set_start_timestamp_for_next_profile(struct stack_recorder_state *state, ddog_Timespec timestamp);
215
+ static void serializer_set_start_timestamp_for_next_profile(struct stack_recorder_state *state, ddog_Timespec start_time);
215
216
  static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE local_root_span_id, VALUE endpoint);
217
+ static void reset_profile(ddog_prof_Profile *profile, ddog_Timespec *start_time /* Can be null */);
216
218
 
217
219
  void stack_recorder_init(VALUE profiling_module) {
218
220
  stack_recorder_class = rb_define_class_under(profiling_module, "StackRecorder", rb_cObject);
@@ -256,18 +258,25 @@ static const rb_data_type_t stack_recorder_typed_data = {
256
258
  static VALUE _native_new(VALUE klass) {
257
259
  struct stack_recorder_state *state = ruby_xcalloc(1, sizeof(struct stack_recorder_state));
258
260
 
261
+ // Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
262
+ // being leaked.
263
+
259
264
  ddog_prof_Slice_ValueType sample_types = {.ptr = all_value_types, .len = ALL_VALUE_TYPES_COUNT};
260
265
 
261
266
  initialize_slot_concurrency_control(state);
262
267
  for (uint8_t i = 0; i < ALL_VALUE_TYPES_COUNT; i++) { state->position_for[i] = all_value_types_positions[i]; }
263
268
  state->enabled_values_count = ALL_VALUE_TYPES_COUNT;
264
269
 
270
+ // Note: At this point, slot_one_profile and slot_two_profile contain null pointers. Libdatadog validates pointers
271
+ // before using them so it's ok for us to go ahead and create the StackRecorder object.
272
+
273
+ VALUE stack_recorder = TypedData_Wrap_Struct(klass, &stack_recorder_typed_data, state);
274
+
265
275
  // Note: Don't raise exceptions after this point, since it'll lead to libdatadog memory leaking!
266
276
 
267
- state->slot_one_profile = ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
268
- state->slot_two_profile = ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
277
+ initialize_profiles(state, sample_types);
269
278
 
270
- return TypedData_Wrap_Struct(klass, &stack_recorder_typed_data, state);
279
+ return stack_recorder;
271
280
  }
272
281
 
273
282
  static void initialize_slot_concurrency_control(struct stack_recorder_state *state) {
@@ -280,14 +289,36 @@ static void initialize_slot_concurrency_control(struct stack_recorder_state *sta
280
289
  state->active_slot = 1;
281
290
  }
282
291
 
292
+ static void initialize_profiles(struct stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types) {
293
+ ddog_prof_Profile_NewResult slot_one_profile_result =
294
+ ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
295
+
296
+ if (slot_one_profile_result.tag == DDOG_PROF_PROFILE_NEW_RESULT_ERR) {
297
+ rb_raise(rb_eRuntimeError, "Failed to initialize slot one profile: %"PRIsVALUE, get_error_details_and_drop(&slot_one_profile_result.err));
298
+ }
299
+
300
+ ddog_prof_Profile_NewResult slot_two_profile_result =
301
+ ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
302
+
303
+ if (slot_two_profile_result.tag == DDOG_PROF_PROFILE_NEW_RESULT_ERR) {
304
+ // Uff! Though spot. We need to make sure to properly clean up the other profile as well first
305
+ ddog_prof_Profile_drop(&slot_one_profile_result.ok);
306
+ // And now we can raise...
307
+ rb_raise(rb_eRuntimeError, "Failed to initialize slot two profile: %"PRIsVALUE, get_error_details_and_drop(&slot_two_profile_result.err));
308
+ }
309
+
310
+ state->slot_one_profile = slot_one_profile_result.ok;
311
+ state->slot_two_profile = slot_two_profile_result.ok;
312
+ }
313
+
283
314
  static void stack_recorder_typed_data_free(void *state_ptr) {
284
315
  struct stack_recorder_state *state = (struct stack_recorder_state *) state_ptr;
285
316
 
286
317
  pthread_mutex_destroy(&state->slot_one_mutex);
287
- ddog_prof_Profile_drop(state->slot_one_profile);
318
+ ddog_prof_Profile_drop(&state->slot_one_profile);
288
319
 
289
320
  pthread_mutex_destroy(&state->slot_two_mutex);
290
- ddog_prof_Profile_drop(state->slot_two_profile);
321
+ ddog_prof_Profile_drop(&state->slot_two_profile);
291
322
 
292
323
  ruby_xfree(state);
293
324
  }
@@ -333,13 +364,11 @@ static VALUE _native_initialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_insta
333
364
  state->position_for[ALLOC_SAMPLES_VALUE_ID] = next_disabled_pos++;
334
365
  }
335
366
 
336
- ddog_prof_Slice_ValueType sample_types = {.ptr = enabled_value_types, .len = state->enabled_values_count};
337
-
338
- ddog_prof_Profile_drop(state->slot_one_profile);
339
- ddog_prof_Profile_drop(state->slot_two_profile);
367
+ ddog_prof_Profile_drop(&state->slot_one_profile);
368
+ ddog_prof_Profile_drop(&state->slot_two_profile);
340
369
 
341
- state->slot_one_profile = ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
342
- state->slot_two_profile = ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
370
+ ddog_prof_Slice_ValueType sample_types = {.ptr = enabled_value_types, .len = state->enabled_values_count};
371
+ initialize_profiles(state, sample_types);
343
372
 
344
373
  return Qtrue;
345
374
  }
@@ -386,10 +415,6 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan
386
415
  VALUE start = ruby_time_from(ddprof_start);
387
416
  VALUE finish = ruby_time_from(ddprof_finish);
388
417
 
389
- if (!ddog_prof_Profile_reset(args.profile, NULL /* start_time is optional */ )) {
390
- return rb_ary_new_from_args(2, error_symbol, rb_str_new_cstr("Failed to reset profile"));
391
- }
392
-
393
418
  return rb_ary_new_from_args(2, ok_symbol, rb_ary_new_from_args(3, start, finish, encoded_pprof));
394
419
  }
395
420
 
@@ -399,7 +424,7 @@ static VALUE ruby_time_from(ddog_Timespec ddprof_time) {
399
424
  return rb_time_timespec_new(&time, utc);
400
425
  }
401
426
 
402
- void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations, sample_values values, ddog_prof_Slice_Label labels) {
427
+ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations, sample_values values, sample_labels labels) {
403
428
  struct stack_recorder_state *state;
404
429
  TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
405
430
 
@@ -413,22 +438,23 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations,
413
438
  uint8_t *position_for = state->position_for;
414
439
 
415
440
  metric_values[position_for[CPU_TIME_VALUE_ID]] = values.cpu_time_ns;
416
- metric_values[position_for[CPU_SAMPLES_VALUE_ID]] = values.cpu_samples;
441
+ metric_values[position_for[CPU_SAMPLES_VALUE_ID]] = values.cpu_or_wall_samples;
417
442
  metric_values[position_for[WALL_TIME_VALUE_ID]] = values.wall_time_ns;
418
443
  metric_values[position_for[ALLOC_SAMPLES_VALUE_ID]] = values.alloc_samples;
419
444
 
420
- ddog_prof_Profile_AddResult result = ddog_prof_Profile_add(
445
+ ddog_prof_Profile_Result result = ddog_prof_Profile_add(
421
446
  active_slot.profile,
422
447
  (ddog_prof_Sample) {
423
448
  .locations = locations,
424
449
  .values = (ddog_Slice_I64) {.ptr = metric_values, .len = state->enabled_values_count},
425
- .labels = labels
426
- }
450
+ .labels = labels.labels
451
+ },
452
+ labels.end_timestamp_ns
427
453
  );
428
454
 
429
455
  sampler_unlock_active_profile(active_slot);
430
456
 
431
- if (result.tag == DDOG_PROF_PROFILE_ADD_RESULT_ERR) {
457
+ if (result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
432
458
  rb_raise(rb_eArgError, "Failed to record sample: %"PRIsVALUE, get_error_details_and_drop(&result.err));
433
459
  }
434
460
  }
@@ -439,16 +465,21 @@ void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_
439
465
 
440
466
  struct active_slot_pair active_slot = sampler_lock_active_profile(state);
441
467
 
442
- ddog_prof_Profile_set_endpoint(active_slot.profile, local_root_span_id, endpoint);
468
+ ddog_prof_Profile_Result result = ddog_prof_Profile_set_endpoint(active_slot.profile, local_root_span_id, endpoint);
443
469
 
444
470
  sampler_unlock_active_profile(active_slot);
471
+
472
+ if (result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
473
+ rb_raise(rb_eArgError, "Failed to record endpoint: %"PRIsVALUE, get_error_details_and_drop(&result.err));
474
+ }
445
475
  }
446
476
 
447
477
  static void *call_serialize_without_gvl(void *call_args) {
448
478
  struct call_serialize_without_gvl_arguments *args = (struct call_serialize_without_gvl_arguments *) call_args;
449
479
 
450
480
  args->profile = serializer_flip_active_and_inactive_slots(args->state);
451
- args->result = ddog_prof_Profile_serialize(args->profile, &args->finish_timestamp, NULL /* duration_nanos is optional */);
481
+ // Note: The profile gets reset by the serialize call
482
+ args->result = ddog_prof_Profile_serialize(args->profile, &args->finish_timestamp, NULL /* duration_nanos is optional */, NULL /* start_time is optional */);
452
483
  args->serialize_ran = true;
453
484
 
454
485
  return NULL; // Unused
@@ -467,7 +498,7 @@ static struct active_slot_pair sampler_lock_active_profile(struct stack_recorder
467
498
  if (error && error != EBUSY) ENFORCE_SUCCESS_GVL(error);
468
499
 
469
500
  // Slot one is active
470
- if (!error) return (struct active_slot_pair) {.mutex = &state->slot_one_mutex, .profile = state->slot_one_profile};
501
+ if (!error) return (struct active_slot_pair) {.mutex = &state->slot_one_mutex, .profile = &state->slot_one_profile};
471
502
 
472
503
  // If we got here, slot one was not active, let's try slot two
473
504
 
@@ -475,7 +506,7 @@ static struct active_slot_pair sampler_lock_active_profile(struct stack_recorder
475
506
  if (error && error != EBUSY) ENFORCE_SUCCESS_GVL(error);
476
507
 
477
508
  // Slot two is active
478
- if (!error) return (struct active_slot_pair) {.mutex = &state->slot_two_mutex, .profile = state->slot_two_profile};
509
+ if (!error) return (struct active_slot_pair) {.mutex = &state->slot_two_mutex, .profile = &state->slot_two_profile};
479
510
  }
480
511
 
481
512
  // We already tried both multiple times, and we did not succeed. This is not expected to happen. Let's stop sampling.
@@ -506,7 +537,7 @@ static ddog_prof_Profile *serializer_flip_active_and_inactive_slots(struct stack
506
537
  state->active_slot = (previously_active_slot == 1) ? 2 : 1;
507
538
 
508
539
  // Return profile for previously active slot (now inactive)
509
- return (previously_active_slot == 1) ? state->slot_one_profile : state->slot_two_profile;
540
+ return (previously_active_slot == 1) ? &state->slot_one_profile : &state->slot_two_profile;
510
541
  }
511
542
 
512
543
  // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
@@ -565,19 +596,18 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_
565
596
  // resulting state is inconsistent, we make sure to reset it back to the initial state.
566
597
  initialize_slot_concurrency_control(state);
567
598
 
568
- ddog_prof_Profile_reset(state->slot_one_profile, /* start_time: */ NULL);
569
- ddog_prof_Profile_reset(state->slot_two_profile, /* start_time: */ NULL);
599
+ reset_profile(&state->slot_one_profile, /* start_time: */ NULL);
600
+ reset_profile(&state->slot_two_profile, /* start_time: */ NULL);
570
601
 
571
602
  return Qtrue;
572
603
  }
573
604
 
574
- // Assumption 1: This method is called with the GVL being held, because `ddog_prof_Profile_reset` mutates the profile and should
605
+ // Assumption 1: This method is called with the GVL being held, because `ddog_prof_Profile_reset` mutates the profile and must
575
606
  // not be interrupted part-way through by a VM fork.
576
- static void serializer_set_start_timestamp_for_next_profile(struct stack_recorder_state *state, ddog_Timespec timestamp) {
577
- // Before making this profile active, we reset it so that it uses the correct timestamp for its start
578
- ddog_prof_Profile *next_profile = (state->active_slot == 1) ? state->slot_two_profile : state->slot_one_profile;
579
-
580
- if (!ddog_prof_Profile_reset(next_profile, &timestamp)) rb_raise(rb_eRuntimeError, "Failed to reset profile");
607
+ static void serializer_set_start_timestamp_for_next_profile(struct stack_recorder_state *state, ddog_Timespec start_time) {
608
+ // Before making this profile active, we reset it so that it uses the correct start_time for its start
609
+ ddog_prof_Profile *next_profile = (state->active_slot == 1) ? &state->slot_two_profile : &state->slot_one_profile;
610
+ reset_profile(next_profile, &start_time);
581
611
  }
582
612
 
583
613
  static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE local_root_span_id, VALUE endpoint) {
@@ -585,3 +615,10 @@ static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_
585
615
  record_endpoint(recorder_instance, NUM2ULL(local_root_span_id), char_slice_from_ruby_string(endpoint));
586
616
  return Qtrue;
587
617
  }
618
+
619
+ static void reset_profile(ddog_prof_Profile *profile, ddog_Timespec *start_time /* Can be null */) {
620
+ ddog_prof_Profile_Result reset_result = ddog_prof_Profile_reset(profile, start_time);
621
+ if (reset_result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
622
+ rb_raise(rb_eRuntimeError, "Failed to reset profile: %"PRIsVALUE, get_error_details_and_drop(&reset_result.err));
623
+ }
624
+ }
@@ -2,13 +2,23 @@
2
2
 
3
3
  #include <datadog/profiling.h>
4
4
 
5
- typedef struct sample_values {
5
+ typedef struct {
6
6
  int64_t cpu_time_ns;
7
7
  int64_t wall_time_ns;
8
- uint32_t cpu_samples;
8
+ uint32_t cpu_or_wall_samples;
9
9
  uint32_t alloc_samples;
10
10
  } sample_values;
11
11
 
12
- void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations, sample_values values, ddog_prof_Slice_Label labels);
12
+ typedef struct sample_labels {
13
+ ddog_prof_Slice_Label labels;
14
+
15
+ // This is used to allow the `Collectors::Stack` to modify the existing label, if any. This MUST be NULL or point
16
+ // somewhere inside the labels slice above.
17
+ ddog_prof_Label *state_label;
18
+
19
+ int64_t end_timestamp_ns;
20
+ } sample_labels;
21
+
22
+ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations, sample_values values, sample_labels labels);
13
23
  void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_CharSlice endpoint);
14
24
  VALUE enforce_recorder_instance(VALUE object);