ddtrace 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (484) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -1
  3. data/LICENSE-3rdparty.csv +2 -0
  4. data/ext/ddtrace_profiling_loader/ddtrace_profiling_loader.c +1 -1
  5. data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +9 -2
  6. data/ext/ddtrace_profiling_native_extension/clock_id.h +20 -0
  7. data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +30 -1
  8. data/ext/ddtrace_profiling_native_extension/clock_id_noop.c +10 -1
  9. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.c +148 -28
  10. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.h +6 -0
  11. data/ext/ddtrace_profiling_native_extension/collectors_stack.c +11 -7
  12. data/ext/ddtrace_profiling_native_extension/extconf.rb +24 -19
  13. data/ext/ddtrace_profiling_native_extension/helpers.h +12 -0
  14. data/ext/ddtrace_profiling_native_extension/http_transport.c +40 -47
  15. data/ext/ddtrace_profiling_native_extension/{libddprof_helpers.h → libdatadog_helpers.h} +2 -1
  16. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +36 -20
  17. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +30 -29
  18. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +2 -5
  19. data/ext/ddtrace_profiling_native_extension/profiling.c +2 -3
  20. data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +25 -0
  21. data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +33 -1
  22. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +292 -18
  23. data/ext/ddtrace_profiling_native_extension/stack_recorder.h +1 -1
  24. data/lib/datadog/appsec/autoload.rb +4 -2
  25. data/lib/datadog/appsec/configuration.rb +1 -1
  26. data/lib/datadog/appsec/contrib/auto_instrument.rb +0 -2
  27. data/lib/datadog/appsec/contrib/configuration/settings.rb +1 -1
  28. data/lib/datadog/appsec/contrib/rack/configuration/settings.rb +2 -2
  29. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +6 -6
  30. data/lib/datadog/appsec/contrib/rack/integration.rb +5 -5
  31. data/lib/datadog/appsec/contrib/rack/patcher.rb +2 -2
  32. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +1 -1
  33. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +1 -1
  34. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +1 -1
  35. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +2 -2
  36. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +3 -3
  37. data/lib/datadog/appsec/contrib/rails/configuration/settings.rb +2 -2
  38. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +4 -4
  39. data/lib/datadog/appsec/contrib/rails/integration.rb +4 -4
  40. data/lib/datadog/appsec/contrib/rails/patcher.rb +16 -12
  41. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +1 -1
  42. data/lib/datadog/appsec/contrib/sinatra/configuration/settings.rb +2 -2
  43. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +5 -5
  44. data/lib/datadog/appsec/contrib/sinatra/integration.rb +4 -4
  45. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +16 -12
  46. data/lib/datadog/appsec/event.rb +3 -3
  47. data/lib/datadog/appsec/extensions.rb +1 -1
  48. data/lib/datadog/appsec/processor.rb +1 -1
  49. data/lib/datadog/appsec/reactive/engine.rb +2 -2
  50. data/lib/datadog/appsec/reactive/operation.rb +3 -3
  51. data/lib/datadog/appsec.rb +5 -5
  52. data/lib/datadog/ci/configuration/components.rb +1 -1
  53. data/lib/datadog/ci/configuration/settings.rb +1 -1
  54. data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +2 -2
  55. data/lib/datadog/ci/contrib/cucumber/formatter.rb +5 -5
  56. data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +1 -1
  57. data/lib/datadog/ci/contrib/cucumber/integration.rb +3 -3
  58. data/lib/datadog/ci/contrib/cucumber/patcher.rb +2 -2
  59. data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +2 -2
  60. data/lib/datadog/ci/contrib/rspec/example.rb +5 -5
  61. data/lib/datadog/ci/contrib/rspec/integration.rb +3 -3
  62. data/lib/datadog/ci/contrib/rspec/patcher.rb +2 -2
  63. data/lib/datadog/ci/ext/environment.rb +8 -6
  64. data/lib/datadog/ci/extensions.rb +4 -4
  65. data/lib/datadog/ci/flush.rb +2 -2
  66. data/lib/datadog/ci/test.rb +3 -3
  67. data/lib/datadog/ci.rb +6 -6
  68. data/lib/datadog/core/buffer/cruby.rb +1 -1
  69. data/lib/datadog/core/buffer/thread_safe.rb +1 -1
  70. data/lib/datadog/core/configuration/agent_settings_resolver.rb +6 -6
  71. data/lib/datadog/core/configuration/base.rb +2 -2
  72. data/lib/datadog/core/configuration/components.rb +26 -43
  73. data/lib/datadog/core/configuration/option_definition.rb +1 -1
  74. data/lib/datadog/core/configuration/option_definition_set.rb +1 -1
  75. data/lib/datadog/core/configuration/options.rb +3 -3
  76. data/lib/datadog/core/configuration/settings.rb +9 -7
  77. data/lib/datadog/core/configuration.rb +4 -4
  78. data/lib/datadog/core/diagnostics/environment_logger.rb +1 -1
  79. data/lib/datadog/core/diagnostics/health.rb +2 -2
  80. data/lib/datadog/core/environment/cgroup.rb +1 -1
  81. data/lib/datadog/core/environment/container.rb +1 -1
  82. data/lib/datadog/core/environment/ext.rb +1 -1
  83. data/lib/datadog/core/environment/identity.rb +2 -2
  84. data/lib/datadog/core/environment/platform.rb +1 -1
  85. data/lib/datadog/core/environment/socket.rb +1 -1
  86. data/lib/datadog/core/error.rb +1 -1
  87. data/lib/datadog/core/extensions.rb +1 -1
  88. data/lib/datadog/core/metrics/client.rb +8 -8
  89. data/lib/datadog/core/metrics/options.rb +3 -3
  90. data/lib/datadog/core/runtime/metrics.rb +6 -6
  91. data/lib/datadog/core/utils/object_set.rb +1 -1
  92. data/lib/datadog/core/utils/string_table.rb +1 -1
  93. data/lib/datadog/core/utils/time.rb +3 -3
  94. data/lib/datadog/core/utils.rb +2 -2
  95. data/lib/datadog/core/vendor/multipart-post/multipart/post/multipartable.rb +2 -2
  96. data/lib/datadog/core/vendor/multipart-post/net/http/post/multipart.rb +3 -3
  97. data/lib/datadog/core/workers/async.rb +1 -1
  98. data/lib/datadog/core/workers/polling.rb +2 -2
  99. data/lib/datadog/core/workers/runtime_metrics.rb +4 -4
  100. data/lib/datadog/core.rb +50 -50
  101. data/lib/datadog/kit.rb +1 -1
  102. data/lib/datadog/opentracer/distributed_headers.rb +2 -2
  103. data/lib/datadog/opentracer/rack_propagator.rb +11 -7
  104. data/lib/datadog/opentracer/span.rb +1 -1
  105. data/lib/datadog/opentracer/text_map_propagator.rb +9 -6
  106. data/lib/datadog/opentracer/thread_local_scope_manager.rb +1 -1
  107. data/lib/datadog/opentracer/tracer.rb +19 -15
  108. data/lib/datadog/opentracer.rb +16 -16
  109. data/lib/datadog/profiling/buffer.rb +3 -3
  110. data/lib/datadog/profiling/collectors/cpu_and_wall_time.rb +4 -19
  111. data/lib/datadog/profiling/collectors/old_stack.rb +7 -7
  112. data/lib/datadog/profiling/collectors/stack.rb +3 -8
  113. data/lib/datadog/profiling/encoding/profile.rb +1 -1
  114. data/lib/datadog/profiling/events/stack.rb +1 -1
  115. data/lib/datadog/profiling/exporter.rb +17 -9
  116. data/lib/datadog/profiling/ext/forking.rb +36 -37
  117. data/lib/datadog/profiling/ext.rb +1 -0
  118. data/lib/datadog/profiling/flush.rb +0 -3
  119. data/lib/datadog/profiling/http_transport.rb +4 -3
  120. data/lib/datadog/profiling/old_recorder.rb +2 -7
  121. data/lib/datadog/profiling/pprof/builder.rb +4 -4
  122. data/lib/datadog/profiling/pprof/converter.rb +1 -1
  123. data/lib/datadog/profiling/pprof/message_set.rb +1 -1
  124. data/lib/datadog/profiling/pprof/stack_sample.rb +4 -4
  125. data/lib/datadog/profiling/pprof/string_table.rb +1 -1
  126. data/lib/datadog/profiling/pprof/template.rb +5 -5
  127. data/lib/datadog/profiling/preload.rb +1 -1
  128. data/lib/datadog/profiling/scheduler.rb +5 -4
  129. data/lib/datadog/profiling/stack_recorder.rb +14 -4
  130. data/lib/datadog/profiling/tag_builder.rb +6 -1
  131. data/lib/datadog/profiling/tasks/setup.rb +2 -2
  132. data/lib/datadog/profiling/trace_identifiers/ddtrace.rb +2 -2
  133. data/lib/datadog/profiling/trace_identifiers/helper.rb +1 -1
  134. data/lib/datadog/profiling/transport/http/api/endpoint.rb +5 -5
  135. data/lib/datadog/profiling/transport/http/api/instance.rb +2 -2
  136. data/lib/datadog/profiling/transport/http/api/spec.rb +1 -1
  137. data/lib/datadog/profiling/transport/http/api.rb +5 -5
  138. data/lib/datadog/profiling/transport/http/builder.rb +3 -3
  139. data/lib/datadog/profiling/transport/http/client.rb +2 -2
  140. data/lib/datadog/profiling/transport/http/response.rb +1 -1
  141. data/lib/datadog/profiling/transport/http.rb +21 -15
  142. data/lib/datadog/profiling.rb +20 -20
  143. data/lib/datadog/tracing/analytics.rb +1 -1
  144. data/lib/datadog/tracing/buffer.rb +5 -5
  145. data/lib/datadog/tracing/context.rb +1 -1
  146. data/lib/datadog/tracing/context_provider.rb +2 -2
  147. data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +2 -2
  148. data/lib/datadog/tracing/contrib/action_cable/event.rb +4 -5
  149. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +4 -4
  150. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +3 -3
  151. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +4 -4
  152. data/lib/datadog/tracing/contrib/action_cable/events.rb +4 -4
  153. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +3 -4
  154. data/lib/datadog/tracing/contrib/action_cable/integration.rb +4 -4
  155. data/lib/datadog/tracing/contrib/action_cable/patcher.rb +4 -4
  156. data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +2 -2
  157. data/lib/datadog/tracing/contrib/action_mailer/event.rb +3 -3
  158. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +3 -3
  159. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +3 -3
  160. data/lib/datadog/tracing/contrib/action_mailer/events.rb +2 -2
  161. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +4 -4
  162. data/lib/datadog/tracing/contrib/action_mailer/patcher.rb +3 -3
  163. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +6 -6
  164. data/lib/datadog/tracing/contrib/action_pack/action_controller/patcher.rb +2 -2
  165. data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +2 -2
  166. data/lib/datadog/tracing/contrib/action_pack/integration.rb +4 -4
  167. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +2 -2
  168. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -1
  169. data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +2 -2
  170. data/lib/datadog/tracing/contrib/action_view/event.rb +1 -1
  171. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +5 -5
  172. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +5 -5
  173. data/lib/datadog/tracing/contrib/action_view/events.rb +2 -2
  174. data/lib/datadog/tracing/contrib/action_view/instrumentation/partial_renderer.rb +2 -2
  175. data/lib/datadog/tracing/contrib/action_view/instrumentation/template_renderer.rb +2 -2
  176. data/lib/datadog/tracing/contrib/action_view/integration.rb +4 -4
  177. data/lib/datadog/tracing/contrib/action_view/patcher.rb +7 -7
  178. data/lib/datadog/tracing/contrib/action_view/utils.rb +1 -1
  179. data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +3 -3
  180. data/lib/datadog/tracing/contrib/active_job/event.rb +3 -3
  181. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +4 -4
  182. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +4 -4
  183. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +4 -4
  184. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +4 -4
  185. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +4 -4
  186. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +4 -4
  187. data/lib/datadog/tracing/contrib/active_job/events.rb +6 -6
  188. data/lib/datadog/tracing/contrib/active_job/integration.rb +4 -4
  189. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +0 -2
  190. data/lib/datadog/tracing/contrib/active_job/patcher.rb +4 -4
  191. data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +2 -2
  192. data/lib/datadog/tracing/contrib/active_model_serializers/event.rb +4 -5
  193. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +3 -3
  194. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +2 -2
  195. data/lib/datadog/tracing/contrib/active_model_serializers/events.rb +2 -2
  196. data/lib/datadog/tracing/contrib/active_model_serializers/integration.rb +3 -3
  197. data/lib/datadog/tracing/contrib/active_model_serializers/patcher.rb +3 -4
  198. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +2 -2
  199. data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +3 -3
  200. data/lib/datadog/tracing/contrib/active_record/event.rb +1 -1
  201. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +4 -4
  202. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +6 -6
  203. data/lib/datadog/tracing/contrib/active_record/events.rb +2 -2
  204. data/lib/datadog/tracing/contrib/active_record/integration.rb +6 -6
  205. data/lib/datadog/tracing/contrib/active_record/patcher.rb +2 -2
  206. data/lib/datadog/tracing/contrib/active_record/utils.rb +2 -2
  207. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +19 -9
  208. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +2 -2
  209. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +1 -1
  210. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +2 -2
  211. data/lib/datadog/tracing/contrib/active_support/integration.rb +5 -5
  212. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +1 -1
  213. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +1 -1
  214. data/lib/datadog/tracing/contrib/active_support/patcher.rb +2 -2
  215. data/lib/datadog/tracing/contrib/analytics.rb +1 -1
  216. data/lib/datadog/tracing/contrib/auto_instrument.rb +4 -4
  217. data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +2 -2
  218. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +3 -4
  219. data/lib/datadog/tracing/contrib/aws/integration.rb +3 -3
  220. data/lib/datadog/tracing/contrib/aws/patcher.rb +5 -5
  221. data/lib/datadog/tracing/contrib/concurrent_ruby/configuration/settings.rb +2 -2
  222. data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +1 -1
  223. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +3 -3
  224. data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +2 -2
  225. data/lib/datadog/tracing/contrib/configurable.rb +2 -2
  226. data/lib/datadog/tracing/contrib/configuration/resolvers/pattern_resolver.rb +1 -1
  227. data/lib/datadog/tracing/contrib/configuration/settings.rb +2 -2
  228. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +2 -2
  229. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +4 -5
  230. data/lib/datadog/tracing/contrib/dalli/integration.rb +3 -3
  231. data/lib/datadog/tracing/contrib/dalli/patcher.rb +3 -3
  232. data/lib/datadog/tracing/contrib/dalli/quantize.rb +1 -1
  233. data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +3 -3
  234. data/lib/datadog/tracing/contrib/delayed_job/ext.rb +2 -0
  235. data/lib/datadog/tracing/contrib/delayed_job/integration.rb +3 -3
  236. data/lib/datadog/tracing/contrib/delayed_job/patcher.rb +8 -2
  237. data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +3 -4
  238. data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +32 -0
  239. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +2 -2
  240. data/lib/datadog/tracing/contrib/elasticsearch/integration.rb +3 -3
  241. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -7
  242. data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +1 -1
  243. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +2 -2
  244. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -5
  245. data/lib/datadog/tracing/contrib/ethon/integration.rb +4 -4
  246. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +3 -4
  247. data/lib/datadog/tracing/contrib/ethon/patcher.rb +3 -3
  248. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +2 -2
  249. data/lib/datadog/tracing/contrib/excon/integration.rb +4 -4
  250. data/lib/datadog/tracing/contrib/excon/middleware.rb +6 -7
  251. data/lib/datadog/tracing/contrib/excon/patcher.rb +2 -2
  252. data/lib/datadog/tracing/contrib/extensions.rb +3 -3
  253. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +2 -2
  254. data/lib/datadog/tracing/contrib/faraday/integration.rb +4 -4
  255. data/lib/datadog/tracing/contrib/faraday/middleware.rb +5 -6
  256. data/lib/datadog/tracing/contrib/faraday/patcher.rb +5 -5
  257. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +3 -3
  258. data/lib/datadog/tracing/contrib/grape/endpoint.rb +4 -5
  259. data/lib/datadog/tracing/contrib/grape/integration.rb +3 -3
  260. data/lib/datadog/tracing/contrib/grape/patcher.rb +4 -4
  261. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +2 -2
  262. data/lib/datadog/tracing/contrib/graphql/integration.rb +3 -3
  263. data/lib/datadog/tracing/contrib/graphql/patcher.rb +2 -3
  264. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +3 -3
  265. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +4 -4
  266. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +6 -6
  267. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +3 -4
  268. data/lib/datadog/tracing/contrib/grpc/integration.rb +3 -3
  269. data/lib/datadog/tracing/contrib/grpc/patcher.rb +5 -5
  270. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +1 -2
  271. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +2 -2
  272. data/lib/datadog/tracing/contrib/http/instrumentation.rb +3 -4
  273. data/lib/datadog/tracing/contrib/http/integration.rb +6 -6
  274. data/lib/datadog/tracing/contrib/http/patcher.rb +3 -3
  275. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +2 -2
  276. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +4 -5
  277. data/lib/datadog/tracing/contrib/httpclient/integration.rb +4 -4
  278. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +3 -3
  279. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +2 -2
  280. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +4 -5
  281. data/lib/datadog/tracing/contrib/httprb/integration.rb +4 -4
  282. data/lib/datadog/tracing/contrib/httprb/patcher.rb +3 -3
  283. data/lib/datadog/tracing/contrib/integration.rb +3 -3
  284. data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +2 -2
  285. data/lib/datadog/tracing/contrib/kafka/event.rb +3 -3
  286. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +2 -2
  287. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +3 -3
  288. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +3 -3
  289. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +4 -4
  290. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/join_group.rb +4 -4
  291. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/leave_group.rb +4 -4
  292. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/sync_group.rb +4 -4
  293. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +2 -2
  294. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +2 -2
  295. data/lib/datadog/tracing/contrib/kafka/events.rb +9 -9
  296. data/lib/datadog/tracing/contrib/kafka/integration.rb +3 -3
  297. data/lib/datadog/tracing/contrib/kafka/patcher.rb +3 -3
  298. data/lib/datadog/tracing/contrib/lograge/configuration/settings.rb +2 -2
  299. data/lib/datadog/tracing/contrib/lograge/instrumentation.rb +1 -2
  300. data/lib/datadog/tracing/contrib/lograge/integration.rb +3 -3
  301. data/lib/datadog/tracing/contrib/lograge/patcher.rb +2 -2
  302. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +2 -2
  303. data/lib/datadog/tracing/contrib/mongodb/instrumentation.rb +3 -3
  304. data/lib/datadog/tracing/contrib/mongodb/integration.rb +4 -4
  305. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +1 -1
  306. data/lib/datadog/tracing/contrib/mongodb/patcher.rb +3 -3
  307. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +4 -4
  308. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +2 -2
  309. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +3 -4
  310. data/lib/datadog/tracing/contrib/mysql2/integration.rb +3 -3
  311. data/lib/datadog/tracing/contrib/mysql2/patcher.rb +2 -2
  312. data/lib/datadog/tracing/contrib/patcher.rb +2 -2
  313. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +2 -2
  314. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +3 -4
  315. data/lib/datadog/tracing/contrib/pg/integration.rb +3 -3
  316. data/lib/datadog/tracing/contrib/pg/patcher.rb +2 -2
  317. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +2 -2
  318. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +2 -3
  319. data/lib/datadog/tracing/contrib/presto/integration.rb +3 -3
  320. data/lib/datadog/tracing/contrib/presto/patcher.rb +4 -4
  321. data/lib/datadog/tracing/contrib/qless/configuration/settings.rb +2 -2
  322. data/lib/datadog/tracing/contrib/qless/integration.rb +3 -3
  323. data/lib/datadog/tracing/contrib/qless/patcher.rb +1 -2
  324. data/lib/datadog/tracing/contrib/qless/qless_job.rb +2 -3
  325. data/lib/datadog/tracing/contrib/qless/tracer_cleaner.rb +0 -2
  326. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +3 -3
  327. data/lib/datadog/tracing/contrib/que/integration.rb +4 -4
  328. data/lib/datadog/tracing/contrib/que/patcher.rb +1 -1
  329. data/lib/datadog/tracing/contrib/que/tracer.rb +1 -1
  330. data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +2 -2
  331. data/lib/datadog/tracing/contrib/racecar/event.rb +4 -5
  332. data/lib/datadog/tracing/contrib/racecar/events/batch.rb +2 -2
  333. data/lib/datadog/tracing/contrib/racecar/events/consume.rb +2 -2
  334. data/lib/datadog/tracing/contrib/racecar/events/message.rb +2 -2
  335. data/lib/datadog/tracing/contrib/racecar/events.rb +3 -3
  336. data/lib/datadog/tracing/contrib/racecar/integration.rb +3 -3
  337. data/lib/datadog/tracing/contrib/racecar/patcher.rb +3 -3
  338. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +2 -2
  339. data/lib/datadog/tracing/contrib/rack/integration.rb +4 -4
  340. data/lib/datadog/tracing/contrib/rack/middlewares.rb +24 -20
  341. data/lib/datadog/tracing/contrib/rack/patcher.rb +4 -2
  342. data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +1 -1
  343. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +1 -1
  344. data/lib/datadog/tracing/contrib/rails/framework.rb +16 -21
  345. data/lib/datadog/tracing/contrib/rails/integration.rb +4 -4
  346. data/lib/datadog/tracing/contrib/rails/log_injection.rb +0 -2
  347. data/lib/datadog/tracing/contrib/rails/middlewares.rb +1 -2
  348. data/lib/datadog/tracing/contrib/rails/patcher.rb +7 -8
  349. data/lib/datadog/tracing/contrib/rails/railtie.rb +3 -3
  350. data/lib/datadog/tracing/contrib/rails/utils.rb +1 -1
  351. data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +17 -2
  352. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +12 -7
  353. data/lib/datadog/tracing/contrib/rake/integration.rb +3 -3
  354. data/lib/datadog/tracing/contrib/rake/patcher.rb +3 -4
  355. data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +1 -1
  356. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +2 -2
  357. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +6 -7
  358. data/lib/datadog/tracing/contrib/redis/integration.rb +3 -3
  359. data/lib/datadog/tracing/contrib/redis/patcher.rb +6 -6
  360. data/lib/datadog/tracing/contrib/redis/tags.rb +3 -4
  361. data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +3 -3
  362. data/lib/datadog/tracing/contrib/resque/integration.rb +3 -3
  363. data/lib/datadog/tracing/contrib/resque/patcher.rb +2 -2
  364. data/lib/datadog/tracing/contrib/resque/resque_job.rb +3 -4
  365. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +2 -2
  366. data/lib/datadog/tracing/contrib/rest_client/integration.rb +3 -3
  367. data/lib/datadog/tracing/contrib/rest_client/patcher.rb +2 -2
  368. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -5
  369. data/lib/datadog/tracing/contrib/semantic_logger/configuration/settings.rb +2 -2
  370. data/lib/datadog/tracing/contrib/semantic_logger/instrumentation.rb +1 -2
  371. data/lib/datadog/tracing/contrib/semantic_logger/integration.rb +3 -3
  372. data/lib/datadog/tracing/contrib/semantic_logger/patcher.rb +2 -2
  373. data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +2 -2
  374. data/lib/datadog/tracing/contrib/sequel/database.rb +4 -5
  375. data/lib/datadog/tracing/contrib/sequel/dataset.rb +4 -5
  376. data/lib/datadog/tracing/contrib/sequel/integration.rb +3 -3
  377. data/lib/datadog/tracing/contrib/sequel/patcher.rb +3 -3
  378. data/lib/datadog/tracing/contrib/sequel/utils.rb +2 -2
  379. data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +3 -3
  380. data/lib/datadog/tracing/contrib/shoryuken/integration.rb +4 -4
  381. data/lib/datadog/tracing/contrib/shoryuken/patcher.rb +1 -1
  382. data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +1 -1
  383. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +4 -5
  384. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +3 -3
  385. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +6 -0
  386. data/lib/datadog/tracing/contrib/sidekiq/integration.rb +3 -3
  387. data/lib/datadog/tracing/contrib/sidekiq/patcher.rb +14 -7
  388. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/heartbeat.rb +19 -1
  389. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/{scheduled_push.rb → redis_info.rb} +5 -6
  390. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/scheduled_poller.rb +53 -0
  391. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +5 -6
  392. data/lib/datadog/tracing/contrib/sidekiq/tracing.rb +2 -2
  393. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +2 -2
  394. data/lib/datadog/tracing/contrib/sinatra/env.rb +2 -2
  395. data/lib/datadog/tracing/contrib/sinatra/framework.rb +0 -2
  396. data/lib/datadog/tracing/contrib/sinatra/headers.rb +1 -1
  397. data/lib/datadog/tracing/contrib/sinatra/integration.rb +3 -3
  398. data/lib/datadog/tracing/contrib/sinatra/patcher.rb +5 -5
  399. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +7 -8
  400. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +6 -7
  401. data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +2 -2
  402. data/lib/datadog/tracing/contrib/sneakers/integration.rb +4 -4
  403. data/lib/datadog/tracing/contrib/sneakers/patcher.rb +2 -2
  404. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +2 -3
  405. data/lib/datadog/tracing/contrib/status_code_matcher.rb +2 -2
  406. data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +2 -2
  407. data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +3 -4
  408. data/lib/datadog/tracing/contrib/sucker_punch/integration.rb +3 -3
  409. data/lib/datadog/tracing/contrib/sucker_punch/patcher.rb +4 -5
  410. data/lib/datadog/tracing/contrib.rb +48 -48
  411. data/lib/datadog/tracing/correlation.rb +1 -1
  412. data/lib/datadog/tracing/distributed/headers/b3.rb +4 -4
  413. data/lib/datadog/tracing/distributed/headers/b3_single.rb +4 -4
  414. data/lib/datadog/tracing/distributed/headers/datadog.rb +3 -3
  415. data/lib/datadog/tracing/distributed/headers/parser.rb +1 -1
  416. data/lib/datadog/tracing/distributed/helpers.rb +2 -2
  417. data/lib/datadog/tracing/distributed/metadata/b3.rb +4 -4
  418. data/lib/datadog/tracing/distributed/metadata/b3_single.rb +4 -4
  419. data/lib/datadog/tracing/distributed/metadata/datadog.rb +2 -2
  420. data/lib/datadog/tracing/distributed/metadata/parser.rb +1 -1
  421. data/lib/datadog/tracing/event.rb +1 -1
  422. data/lib/datadog/tracing/metadata/analytics.rb +2 -2
  423. data/lib/datadog/tracing/metadata/errors.rb +2 -2
  424. data/lib/datadog/tracing/metadata/tagging.rb +2 -2
  425. data/lib/datadog/tracing/metadata.rb +3 -3
  426. data/lib/datadog/tracing/pipeline/span_filter.rb +10 -6
  427. data/lib/datadog/tracing/pipeline.rb +3 -3
  428. data/lib/datadog/tracing/propagation/grpc.rb +6 -6
  429. data/lib/datadog/tracing/propagation/http.rb +8 -8
  430. data/lib/datadog/tracing/runtime/metrics.rb +1 -1
  431. data/lib/datadog/tracing/sampling/all_sampler.rb +1 -1
  432. data/lib/datadog/tracing/sampling/priority_sampler.rb +5 -5
  433. data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +2 -2
  434. data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +3 -3
  435. data/lib/datadog/tracing/sampling/rate_limiter.rb +1 -1
  436. data/lib/datadog/tracing/sampling/rate_sampler.rb +5 -5
  437. data/lib/datadog/tracing/sampling/rule.rb +3 -3
  438. data/lib/datadog/tracing/sampling/rule_sampler.rb +4 -4
  439. data/lib/datadog/tracing/span.rb +4 -4
  440. data/lib/datadog/tracing/span_operation.rb +9 -9
  441. data/lib/datadog/tracing/sync_writer.rb +5 -5
  442. data/lib/datadog/tracing/trace_operation.rb +16 -9
  443. data/lib/datadog/tracing/trace_segment.rb +5 -5
  444. data/lib/datadog/tracing/tracer.rb +15 -15
  445. data/lib/datadog/tracing/workers/trace_writer.rb +9 -9
  446. data/lib/datadog/tracing/workers.rb +3 -3
  447. data/lib/datadog/tracing/writer.rb +5 -5
  448. data/lib/datadog/tracing.rb +8 -8
  449. data/lib/ddtrace/auto_instrument.rb +2 -2
  450. data/lib/ddtrace/transport/ext.rb +7 -0
  451. data/lib/ddtrace/transport/http/adapters/net.rb +2 -2
  452. data/lib/ddtrace/transport/http/adapters/test.rb +1 -1
  453. data/lib/ddtrace/transport/http/adapters/unix_socket.rb +2 -2
  454. data/lib/ddtrace/transport/http/api/map.rb +1 -1
  455. data/lib/ddtrace/transport/http/api.rb +4 -4
  456. data/lib/ddtrace/transport/http/builder.rb +5 -5
  457. data/lib/ddtrace/transport/http/client.rb +2 -2
  458. data/lib/ddtrace/transport/http/response.rb +1 -1
  459. data/lib/ddtrace/transport/http/statistics.rb +1 -1
  460. data/lib/ddtrace/transport/http/traces.rb +5 -5
  461. data/lib/ddtrace/transport/http.rb +12 -9
  462. data/lib/ddtrace/transport/io/client.rb +2 -2
  463. data/lib/ddtrace/transport/io/response.rb +1 -1
  464. data/lib/ddtrace/transport/io/traces.rb +3 -3
  465. data/lib/ddtrace/transport/io.rb +3 -3
  466. data/lib/ddtrace/transport/statistics.rb +2 -2
  467. data/lib/ddtrace/transport/trace_formatter.rb +5 -5
  468. data/lib/ddtrace/transport/traces.rb +5 -5
  469. data/lib/ddtrace/version.rb +1 -1
  470. data/lib/ddtrace.rb +6 -6
  471. metadata +14 -22
  472. data/.editorconfig +0 -22
  473. data/.gitignore +0 -58
  474. data/CONTRIBUTING.md +0 -81
  475. data/ddtrace.gemspec +0 -71
  476. data/docs/0.x-trace.png +0 -0
  477. data/docs/1.0-trace.png +0 -0
  478. data/docs/AutoInstrumentation.md +0 -36
  479. data/docs/Deprecation.md +0 -8
  480. data/docs/DevelopmentGuide.md +0 -259
  481. data/docs/GettingStarted.md +0 -2712
  482. data/docs/ProfilingDevelopment.md +0 -109
  483. data/docs/PublicApi.md +0 -14
  484. data/docs/UpgradeGuide.md +0 -736
@@ -2,13 +2,20 @@
2
2
 
3
3
  #include <ruby.h>
4
4
 
5
+ #include "helpers.h"
6
+
5
7
  // Processes any pending interruptions, including exceptions to be raised.
6
8
  // If there's an exception to be raised, it raises it. In that case, this function does not return.
7
- static inline VALUE process_pending_interruptions(VALUE _unused) {
9
+ static inline VALUE process_pending_interruptions(DDTRACE_UNUSED VALUE _) {
8
10
  rb_thread_check_ints();
9
11
  return Qnil;
10
12
  }
11
13
 
14
+ // RB_UNLIKELY is not supported on Ruby 2.2 and 2.3
15
+ #ifndef RB_UNLIKELY
16
+ #define RB_UNLIKELY(x) x
17
+ #endif
18
+
12
19
  // Calls process_pending_interruptions BUT "rescues" any exceptions to be raised, returning them instead as
13
20
  // a non-zero `pending_exception`.
14
21
  //
@@ -31,3 +38,28 @@ static inline int check_if_pending_exception(void) {
31
38
  rb_protect(process_pending_interruptions, Qnil, &pending_exception);
32
39
  return pending_exception;
33
40
  }
41
+
42
+ #define ADD_QUOTES_HELPER(x) #x
43
+ #define ADD_QUOTES(x) ADD_QUOTES_HELPER(x)
44
+
45
+ // Ruby has a Check_Type(value, type) that is roughly equivalent to this BUT Ruby's version is rather cryptic when it fails
46
+ // e.g. "wrong argument type nil (expected String)". This is a replacement that prints more information to help debugging.
47
+ #define ENFORCE_TYPE(value, type) \
48
+ { if (RB_UNLIKELY(!RB_TYPE_P(value, type))) raise_unexpected_type(value, type, ADD_QUOTES(value), ADD_QUOTES(type), __FILE__, __LINE__, __func__); }
49
+
50
+ // Called by ENFORCE_TYPE; should not be used directly
51
+ NORETURN(void raise_unexpected_type(
52
+ VALUE value,
53
+ enum ruby_value_type type,
54
+ const char *value_name,
55
+ const char *type_name,
56
+ const char *file,
57
+ int line,
58
+ const char* function_name
59
+ ));
60
+
61
+ // This API is exported as a public symbol by the VM BUT the function header is not defined in any public header, so we
62
+ // repeat it here to be able to use in our code.
63
+ //
64
+ // Queries if the current thread is the owner of the global VM lock.
65
+ int ruby_thread_has_gvl_p(void);
@@ -1,12 +1,130 @@
1
1
  #include <ruby.h>
2
2
  #include <ruby/thread.h>
3
+ #include <pthread.h>
4
+ #include <errno.h>
5
+ #include "helpers.h"
3
6
  #include "stack_recorder.h"
4
- #include "libddprof_helpers.h"
7
+ #include "libdatadog_helpers.h"
5
8
  #include "ruby_helpers.h"
6
9
 
7
10
  // Used to wrap a ddprof_ffi_Profile in a Ruby object and expose Ruby-level serialization APIs
8
11
  // This file implements the native bits of the Datadog::Profiling::StackRecorder class
9
12
 
13
+ // ---
14
+ // ## Synchronization mechanism for safe parallel access design notes
15
+ //
16
+ // The state of the StackRecorder is managed using a set of locks to avoid concurrency issues.
17
+ //
18
+ // This is needed because the state is expected to be accessed, in parallel, by two different threads.
19
+ //
20
+ // 1. The thread that is taking a stack sample and that called `record_sample`, let's call it the **sampler thread**.
21
+ // In the current implementation of the profiler, there can only exist one **sampler thread** at a time; if this
22
+ // constraint changes, we should revise the design of the StackRecorder.
23
+ //
24
+ // 2. The thread that serializes and reports profiles, let's call it the **serializer thread**. We enforce that there
25
+ // cannot be more than one thread attempting to serialize profiles at a time.
26
+ //
27
+ // If both the sampler and serializer threads are trying to access the same `ddprof_ffi_Profile` in parallel, we will
28
+ // have a concurrency issue. Thus, the StackRecorder has an added mechanism to avoid this.
29
+ //
30
+ // As an additional constraint, the **sampler thread** has absolute priority and must never block while
31
+ // recording a sample.
32
+ //
33
+ // ### The solution: Keep two profiles at the same time
34
+ //
35
+ // To solve for the constraints above, the StackRecorder keeps two `ddprof_ffi_Profile` profile instances inside itself.
36
+ // They are called the `slot_one_profile` and `slot_two_profile`.
37
+ //
38
+ // Each profile is paired with its own mutex. `slot_one_profile` is protected by `slot_one_mutex` and `slot_two_profile`
39
+ // is protected by `slot_two_mutex`.
40
+ //
41
+ // We additionally introduce the concept of **active** and **inactive** profile slots. At any point, the sampler thread
42
+ // can probe the mutexes to discover which of the profiles corresponds to the active slot, and then records samples in it.
43
+ // When the serializer thread is ready to serialize data, it flips the active and inactive slots; it reports the data
44
+ // on the previously-active profile slot, and the sampler thread can continue to record in the previously-inactive
45
+ // profile slot.
46
+ //
47
+ // Thus, the sampler and serializer threads never cross paths, avoiding concurrency issues. The sampler thread writes to
48
+ // the active profile slot, and the serializer thread reads from the inactive profile slot.
49
+ //
50
+ // ### Locking protocol, high-level
51
+ //
52
+ // The active profile slot is the slot for which its corresponding mutex **is unlocked**. That is, if the sampler
53
+ // thread can grab a lock for a profile slot, then that slot is the active one. (Here you see where the constraint
54
+ // stated above that only one sampler thread can exist kicks in -- this part would need to be more complex if multiple
55
+ // sampler threads were in play.)
56
+ //
57
+ // As a counterpart, the inactive profile slot mutex is **kept locked** until such time the serializer
58
+ // thread is ready to work and decides to flip the slots.
59
+ //
60
+ // When a new StackRecorder is initialized, the `slot_one_mutex` is unlocked, and the `slot_two_mutex` is kept locked,
61
+ // that is, a new instance always starts with slot one active.
62
+ //
63
+ // Additionally, an `active_slot` field is kept, containing a `1` or `2`; this is only kept for the serializer thread
64
+ // to use as a simplification, as well as for testing and debugging; the **sampler thread must never use the `active_slot`
65
+ // field**.
66
+ //
67
+ // ### Locking protocol, from the sampler thread side
68
+ //
69
+ // When the sampler thread wants to record a sample, it goes through the following steps to discover which is the
70
+ // active profile slot:
71
+ //
72
+ // 1. `pthread_mutex_trylock(slot_one_mutex)`. If it succeeds to grab the lock, this means the active profile slot is
73
+ // slot one. If it fails, we move to the next step.
74
+ //
75
+ // 2. `pthread_mutex_trylock(slot_two_mutex)`. If it succeeds to grab the lock, this means the active profile slot is
76
+ // slot two. If it fails, we move to the next step.
77
+ //
78
+ // 3. What does it mean for the sampler thread to have observed both `slot_one_mutex` as well as `slot_two_mutex` as
79
+ // being locked? There are two options:
80
+ // a. The sampler thread got really unlucky. When it tried to grab the `slot_one_mutex`, the active profile slot was
81
+ // the second one BUT then the serializer thread flipped the slots, and by the time the sampler thread probed the
82
+ // `slot_two_mutex`, that one was taken. Since the serializer thread is expected only to work once a minute,
83
+ // we retry steps 1. and 2. and should be able to find an active slot.
84
+ // b. Something is incorrect in the StackRecorder state. In this situation, the sampler thread should give up on
85
+ // sampling and enter an error state.
86
+ //
87
+ // Note that in the steps above, and because the sampler thread uses `trylock` to probe the mutexes, that the
88
+ // sampler thread never blocks. It either is able to find an active profile slot in a bounded amount of steps or it
89
+ // enters an error state.
90
+ //
91
+ // This guarantees that sampler performance is never constrained by serializer performance.
92
+ //
93
+ // ### Locking protocol, from the serializer thread side
94
+ //
95
+ // When the serializer thread wants to serialize a profile, it first flips the active and inactive profile slots.
96
+ //
97
+ // The flipping action is described below. Consider previously-inactive and previously-active as the state of the slots
98
+ // before the flipping happens.
99
+ //
100
+ // The flipping steps are the following:
101
+ //
102
+ // 1. Release the mutex for the previously-inactive profile slot. That slot, as seen by the sampler thread, is now
103
+ // active.
104
+ //
105
+ // 2. Grab the mutex for the previously-active profile slot. Note that this can lead to the serializer thread blocking,
106
+ // if the sampler thread is holding this mutex. After the mutex is grabbed, the previously-active slot becomes inactive,
107
+ // as seen by the sampler thread.
108
+ //
109
+ // 3. Update `active_slot`.
110
+ //
111
+ // After flipping the profile slots, the serializer thread is now free to serialize the inactive profile slot. The slot
112
+ // is kept inactive until the next time the serializer thread wants to serialize data.
113
+ //
114
+ // Note there can be a brief period between steps 1 and 2 where the serializer thread holds no lock, which means that
115
+ // the sampler thread can pick either slot. This is OK: if the sampler thread picks the previously-inactive slot, the
116
+ // samples will be reported on the next serialization; if the sampler thread picks the previously-active slot, the
117
+ // samples are still included in the current serialization. Either option is correct.
118
+ //
119
+ // ### Additional notes
120
+ //
121
+ // Q: Can the sampler thread and the serializer thread ever be the same thread? (E.g. sampling in interrupt handler)
122
+ // A: No; the current profiler design requires that sampling happens only on the thread that is holding the Global VM
123
+ // Lock (GVL). The serializer thread flipping occurs after the serializer thread releases the GVL, and thus the
124
+ // serializer thread will not be able to host the sampling process.
125
+ //
126
+ // ---
127
+
10
128
  static VALUE ok_symbol = Qnil; // :ok in Ruby
11
129
  static VALUE error_symbol = Qnil; // :error in Ruby
12
130
 
@@ -14,9 +132,32 @@ static ID ruby_time_from_id; // id of :ruby_time_from in Ruby
14
132
 
15
133
  static VALUE stack_recorder_class = Qnil;
16
134
 
135
+ // Contains native state for each instance
136
+ struct stack_recorder_state {
137
+ pthread_mutex_t slot_one_mutex;
138
+ ddprof_ffi_Profile *slot_one_profile;
139
+
140
+ pthread_mutex_t slot_two_mutex;
141
+ ddprof_ffi_Profile *slot_two_profile;
142
+
143
+ short active_slot; // MUST NEVER BE ACCESSED FROM record_sample; this is NOT for the sampler thread to use.
144
+ };
145
+
146
+ // Used to return a pair of values from sampler_lock_active_profile()
147
+ struct active_slot_pair {
148
+ pthread_mutex_t *mutex;
149
+ ddprof_ffi_Profile *profile;
150
+ };
151
+
17
152
  struct call_serialize_without_gvl_arguments {
153
+ // Set by caller
154
+ struct stack_recorder_state *state;
155
+
156
+ // Set by callee
18
157
  ddprof_ffi_Profile *profile;
19
158
  ddprof_ffi_SerializeResult result;
159
+
160
+ // Set by both
20
161
  bool serialize_ran;
21
162
  };
22
163
 
@@ -25,21 +166,33 @@ static void stack_recorder_typed_data_free(void *data);
25
166
  static VALUE _native_serialize(VALUE self, VALUE recorder_instance);
26
167
  static VALUE ruby_time_from(ddprof_ffi_Timespec ddprof_time);
27
168
  static void *call_serialize_without_gvl(void *call_args);
169
+ static struct active_slot_pair sampler_lock_active_profile();
170
+ static void sampler_unlock_active_profile(struct active_slot_pair active_slot);
171
+ static ddprof_ffi_Profile *serializer_flip_active_and_inactive_slots(struct stack_recorder_state *state);
172
+ static VALUE _native_active_slot(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
173
+ static VALUE _native_is_slot_one_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
174
+ static VALUE _native_is_slot_two_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
175
+ static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot);
28
176
 
29
177
  void stack_recorder_init(VALUE profiling_module) {
30
178
  stack_recorder_class = rb_define_class_under(profiling_module, "StackRecorder", rb_cObject);
179
+ // Hosts methods used for testing the native code using RSpec
180
+ VALUE testing_module = rb_define_module_under(stack_recorder_class, "Testing");
31
181
 
32
182
  // Instances of the StackRecorder class are "TypedData" objects.
33
183
  // "TypedData" objects are special objects in the Ruby VM that can wrap C structs.
34
- // In our case, we're going to keep a libddprof profile reference inside our object.
184
+ // In this case, it wraps the stack_recorder_state.
35
185
  //
36
- // Because Ruby doesn't know how to initialize libddprof profiles, we MUST override the allocation function for objects
186
+ // Because Ruby doesn't know how to initialize native-level structs, we MUST override the allocation function for objects
37
187
  // of this class so that we can manage this part. Not overriding or disabling the allocation function is a common
38
188
  // gotcha for "TypedData" objects that can very easily lead to VM crashes, see for instance
39
189
  // https://bugs.ruby-lang.org/issues/18007 for a discussion around this.
40
190
  rb_define_alloc_func(stack_recorder_class, _native_new);
41
191
 
42
192
  rb_define_singleton_method(stack_recorder_class, "_native_serialize", _native_serialize, 1);
193
+ rb_define_singleton_method(testing_module, "_native_active_slot", _native_active_slot, 1);
194
+ rb_define_singleton_method(testing_module, "_native_slot_one_mutex_locked?", _native_is_slot_one_mutex_locked, 1);
195
+ rb_define_singleton_method(testing_module, "_native_slot_two_mutex_locked?", _native_is_slot_two_mutex_locked, 1);
43
196
 
44
197
  ok_symbol = ID2SYM(rb_intern_const("ok"));
45
198
  error_symbol = ID2SYM(rb_intern_const("error"));
@@ -59,24 +212,46 @@ static const rb_data_type_t stack_recorder_typed_data = {
59
212
  };
60
213
 
61
214
  static VALUE _native_new(VALUE klass) {
215
+ struct stack_recorder_state *state = ruby_xcalloc(1, sizeof(struct stack_recorder_state));
216
+
62
217
  ddprof_ffi_Slice_value_type sample_types = {.ptr = enabled_value_types, .len = ENABLED_VALUE_TYPES_COUNT};
63
218
 
64
- ddprof_ffi_Profile *profile = ddprof_ffi_Profile_new(sample_types, NULL /* Period is optional */);
219
+ state->slot_one_mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
220
+ state->slot_two_mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
221
+
222
+ // A newly-created StackRecorder starts with slot one being active for samples, so let's lock slot two
223
+ int error = pthread_mutex_lock(&state->slot_two_mutex);
224
+ if (error) rb_syserr_fail(error, "Unexpected failure during pthread_mutex_lock");
225
+
226
+ state->active_slot = 1;
227
+
228
+ // Note: Don't raise exceptions after this point, since it'll lead to libdatadog memory leaking!
65
229
 
66
- return TypedData_Wrap_Struct(klass, &stack_recorder_typed_data, profile);
230
+ state->slot_one_profile = ddprof_ffi_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
231
+ state->slot_two_profile = ddprof_ffi_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
232
+
233
+ return TypedData_Wrap_Struct(klass, &stack_recorder_typed_data, state);
67
234
  }
68
235
 
69
- static void stack_recorder_typed_data_free(void *data) {
70
- ddprof_ffi_Profile_free((ddprof_ffi_Profile *) data);
236
+ static void stack_recorder_typed_data_free(void *state_ptr) {
237
+ struct stack_recorder_state *state = (struct stack_recorder_state *) state_ptr;
238
+
239
+ pthread_mutex_destroy(&state->slot_one_mutex);
240
+ ddprof_ffi_Profile_free(state->slot_one_profile);
241
+
242
+ pthread_mutex_destroy(&state->slot_two_mutex);
243
+ ddprof_ffi_Profile_free(state->slot_two_profile);
244
+
245
+ ruby_xfree(state);
71
246
  }
72
247
 
73
- static VALUE _native_serialize(VALUE self, VALUE recorder_instance) {
74
- ddprof_ffi_Profile *profile;
75
- TypedData_Get_Struct(recorder_instance, ddprof_ffi_Profile, &stack_recorder_typed_data, profile);
248
+ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
249
+ struct stack_recorder_state *state;
250
+ TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
76
251
 
77
252
  // We'll release the Global VM Lock while we're calling serialize, so that the Ruby VM can continue to work while this
78
253
  // is pending
79
- struct call_serialize_without_gvl_arguments args = {.profile = profile, .serialize_ran = false};
254
+ struct call_serialize_without_gvl_arguments args = {.state = state, .serialize_ran = false};
80
255
 
81
256
  while (!args.serialize_ran) {
82
257
  // Give the Ruby VM an opportunity to process any pending interruptions (including raising exceptions).
@@ -89,7 +264,7 @@ static VALUE _native_serialize(VALUE self, VALUE recorder_instance) {
89
264
 
90
265
  // We use rb_thread_call_without_gvl2 here because unlike the regular _gvl variant, gvl2 does not process
91
266
  // interruptions and thus does not raise exceptions after running our code.
92
- rb_thread_call_without_gvl2(call_serialize_without_gvl, &args, /* No interruption function supported */ NULL, NULL);
267
+ rb_thread_call_without_gvl2(call_serialize_without_gvl, &args, NULL /* No interruption function needed in this case */, NULL /* Not needed */);
93
268
  }
94
269
 
95
270
  ddprof_ffi_SerializeResult serialized_profile = args.result;
@@ -105,13 +280,15 @@ static VALUE _native_serialize(VALUE self, VALUE recorder_instance) {
105
280
  ddprof_ffi_Timespec ddprof_start = serialized_profile.ok.start;
106
281
  ddprof_ffi_Timespec ddprof_finish = serialized_profile.ok.end;
107
282
 
108
- // Clean up libddprof object to avoid leaking in case ruby_time_from raises an exception
283
+ // Clean up libdatadog object to avoid leaking in case ruby_time_from raises an exception
109
284
  ddprof_ffi_SerializeResult_drop(serialized_profile);
110
285
 
111
286
  VALUE start = ruby_time_from(ddprof_start);
112
287
  VALUE finish = ruby_time_from(ddprof_finish);
113
288
 
114
- if (!ddprof_ffi_Profile_reset(profile)) return rb_ary_new_from_args(2, error_symbol, rb_str_new_cstr("Failed to reset profile"));
289
+ if (!ddprof_ffi_Profile_reset(args.profile, NULL /* start_time is optional */ )) {
290
+ return rb_ary_new_from_args(2, error_symbol, rb_str_new_cstr("Failed to reset profile"));
291
+ }
115
292
 
116
293
  return rb_ary_new_from_args(2, ok_symbol, rb_ary_new_from_args(3, start, finish, encoded_pprof));
117
294
  }
@@ -127,16 +304,21 @@ static VALUE ruby_time_from(ddprof_ffi_Timespec ddprof_time) {
127
304
  }
128
305
 
129
306
  void record_sample(VALUE recorder_instance, ddprof_ffi_Sample sample) {
130
- ddprof_ffi_Profile *profile;
131
- TypedData_Get_Struct(recorder_instance, ddprof_ffi_Profile, &stack_recorder_typed_data, profile);
307
+ struct stack_recorder_state *state;
308
+ TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
132
309
 
133
- ddprof_ffi_Profile_add(profile, sample);
310
+ struct active_slot_pair active_slot = sampler_lock_active_profile(state);
311
+
312
+ ddprof_ffi_Profile_add(active_slot.profile, sample);
313
+
314
+ sampler_unlock_active_profile(active_slot);
134
315
  }
135
316
 
136
317
  static void *call_serialize_without_gvl(void *call_args) {
137
318
  struct call_serialize_without_gvl_arguments *args = (struct call_serialize_without_gvl_arguments *) call_args;
138
319
 
139
- args->result = ddprof_ffi_Profile_serialize(args->profile);
320
+ args->profile = serializer_flip_active_and_inactive_slots(args->state);
321
+ args->result = ddprof_ffi_Profile_serialize(args->profile, NULL /* end_time is optional */, NULL /* duration_nanos is optional */);
140
322
  args->serialize_ran = true;
141
323
 
142
324
  return NULL; // Unused
@@ -145,3 +327,95 @@ static void *call_serialize_without_gvl(void *call_args) {
145
327
  void enforce_recorder_instance(VALUE object) {
146
328
  Check_TypedStruct(object, &stack_recorder_typed_data);
147
329
  }
330
+
331
+ static struct active_slot_pair sampler_lock_active_profile(struct stack_recorder_state *state) {
332
+ int error;
333
+
334
+ for (int attempts = 0; attempts < 2; attempts++) {
335
+ error = pthread_mutex_trylock(&state->slot_one_mutex);
336
+ if (error && error != EBUSY) rb_syserr_fail(error, "Unexpected failure during sampler_lock_active_profile for slot_one_mutex");
337
+
338
+ // Slot one is active
339
+ if (!error) return (struct active_slot_pair) {.mutex = &state->slot_one_mutex, .profile = state->slot_one_profile};
340
+
341
+ // If we got here, slot one was not active, let's try slot two
342
+
343
+ error = pthread_mutex_trylock(&state->slot_two_mutex);
344
+ if (error && error != EBUSY) rb_syserr_fail(error, "Unexpected failure during sampler_lock_active_profile for slot_two_mutex");
345
+
346
+ // Slot two is active
347
+ if (!error) return (struct active_slot_pair) {.mutex = &state->slot_two_mutex, .profile = state->slot_two_profile};
348
+ }
349
+
350
+ // We already tried both multiple times, and we did not succeed. This is not expected to happen. Let's stop sampling.
351
+ rb_raise(rb_eRuntimeError, "Failed to grab either mutex in sampler_lock_active_profile");
352
+ }
353
+
354
+ static void sampler_unlock_active_profile(struct active_slot_pair active_slot) {
355
+ int error = pthread_mutex_unlock(active_slot.mutex);
356
+ if (error != 0) rb_syserr_fail(error, "Unexpected failure in sampler_unlock_active_profile");
357
+ }
358
+
359
+ static ddprof_ffi_Profile *serializer_flip_active_and_inactive_slots(struct stack_recorder_state *state) {
360
+ int error;
361
+ int previously_active_slot = state->active_slot;
362
+
363
+ if (previously_active_slot != 1 && previously_active_slot != 2) {
364
+ rb_raise(rb_eRuntimeError, "Unexpected active_slot state %d in serializer_flip_active_and_inactive_slots", previously_active_slot);
365
+ }
366
+
367
+ pthread_mutex_t *previously_active = (previously_active_slot == 1) ? &state->slot_one_mutex : &state->slot_two_mutex;
368
+ pthread_mutex_t *previously_inactive = (previously_active_slot == 1) ? &state->slot_two_mutex : &state->slot_one_mutex;
369
+
370
+ // Release the lock, thus making this slot active
371
+ error = pthread_mutex_unlock(previously_inactive);
372
+ if (error) rb_syserr_fail(error, "Unexpected failure during serializer_flip_active_and_inactive_slots for previously_inactive");
373
+
374
+ // Grab the lock, thus making this slot inactive
375
+ error = pthread_mutex_lock(previously_active);
376
+ if (error) rb_syserr_fail(error, "Unexpected failure during serializer_flip_active_and_inactive_slots for previously_active");
377
+
378
+ // Update active_slot
379
+ state->active_slot = (previously_active_slot == 1) ? 2 : 1;
380
+
381
+ // Return profile for previously active slot (now inactive)
382
+ return (previously_active_slot == 1) ? state->slot_one_profile : state->slot_two_profile;
383
+ }
384
+
385
+ // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
386
+ // It SHOULD NOT be used for other purposes.
387
+ static VALUE _native_active_slot(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
388
+ struct stack_recorder_state *state;
389
+ TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
390
+
391
+ return INT2NUM(state->active_slot);
392
+ }
393
+
394
+ // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
395
+ // It SHOULD NOT be used for other purposes.
396
+ static VALUE _native_is_slot_one_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) { return test_slot_mutex_state(recorder_instance, 1); }
397
+
398
+ // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
399
+ // It SHOULD NOT be used for other purposes.
400
+ static VALUE _native_is_slot_two_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) { return test_slot_mutex_state(recorder_instance, 2); }
401
+
402
+ static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot) {
403
+ struct stack_recorder_state *state;
404
+ TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
405
+
406
+ pthread_mutex_t *slot_mutex = (slot == 1) ? &state->slot_one_mutex : &state->slot_two_mutex;
407
+
408
+ // Like Heisenberg's uncertainty principle, we can't observe without affecting...
409
+ int error = pthread_mutex_trylock(slot_mutex);
410
+
411
+ if (error == 0) {
412
+ // Mutex was unlocked
413
+ pthread_mutex_unlock(slot_mutex);
414
+ return Qfalse;
415
+ } else if (error == EBUSY) {
416
+ // Mutex was locked
417
+ return Qtrue;
418
+ } else {
419
+ rb_syserr_fail(error, "Unexpected failure when checking mutex state");
420
+ }
421
+ }
@@ -3,7 +3,7 @@
3
3
  #include <ddprof/ffi.h>
4
4
 
5
5
  // Note: Please DO NOT use `VALUE_STRING` anywhere else, instead use `DDPROF_FFI_CHARSLICE_C`.
6
- // `VALUE_STRING` is only needed because older versions of gcc (4.9.2, used in our Ruby 2.1 and 2.2 CI test images)
6
+ // `VALUE_STRING` is only needed because older versions of gcc (4.9.2, used in our Ruby 2.2 CI test images)
7
7
  // tripped when compiling `enabled_value_types` using `-std=gnu99` due to the extra cast that is included in
8
8
  // `DDPROF_FFI_CHARSLICE_C` with the following error:
9
9
  //
@@ -1,14 +1,16 @@
1
1
  # typed: ignore
2
2
 
3
3
  if %w[1 true].include?((ENV['DD_APPSEC_ENABLED'] || '').downcase)
4
+ require_relative '../../ddtrace'
5
+
4
6
  begin
5
- require 'datadog/appsec'
7
+ require_relative '../appsec'
6
8
  rescue StandardError => e
7
9
  puts "AppSec failed to load. No security check will be performed. error: #{e.class.name} #{e.message}"
8
10
  end
9
11
 
10
12
  begin
11
- require 'datadog/appsec/contrib/auto_instrument'
13
+ require_relative 'contrib/auto_instrument'
12
14
  Datadog::AppSec::Contrib::AutoInstrument.patch_all
13
15
  rescue StandardError => e
14
16
  puts "AppSec failed to instrument. No security check will be performed. error: #{e.class.name} #{e.message}"
@@ -1,6 +1,6 @@
1
1
  # typed: true
2
2
 
3
- require 'datadog/appsec/configuration/settings'
3
+ require_relative 'configuration/settings'
4
4
 
5
5
  module Datadog
6
6
  module AppSec
@@ -1,7 +1,5 @@
1
1
  # typed: true
2
2
 
3
- require 'ddtrace'
4
-
5
3
  module Datadog
6
4
  module AppSec
7
5
  module Contrib
@@ -1,6 +1,6 @@
1
1
  # typed: false
2
2
 
3
- require 'datadog/core/configuration/base'
3
+ require_relative '../../../core/configuration/base'
4
4
 
5
5
  module Datadog
6
6
  module AppSec
@@ -1,7 +1,7 @@
1
1
  # typed: false
2
2
 
3
- require 'datadog/appsec/contrib/configuration/settings'
4
- require 'datadog/appsec/contrib/rack/ext'
3
+ require_relative '../../configuration/settings'
4
+ require_relative '../ext'
5
5
 
6
6
  module Datadog
7
7
  module AppSec
@@ -1,11 +1,11 @@
1
1
  # typed: false
2
2
 
3
- require 'datadog/appsec/instrumentation/gateway'
4
- require 'datadog/appsec/reactive/operation'
5
- require 'datadog/appsec/contrib/rack/reactive/request'
6
- require 'datadog/appsec/contrib/rack/reactive/request_body'
7
- require 'datadog/appsec/contrib/rack/reactive/response'
8
- require 'datadog/appsec/event'
3
+ require_relative '../../../instrumentation/gateway'
4
+ require_relative '../../../reactive/operation'
5
+ require_relative '../reactive/request'
6
+ require_relative '../reactive/request_body'
7
+ require_relative '../reactive/response'
8
+ require_relative '../../../event'
9
9
 
10
10
  module Datadog
11
11
  module AppSec
@@ -1,11 +1,11 @@
1
1
  # typed: ignore
2
2
 
3
- require 'datadog/appsec/contrib/integration'
3
+ require_relative '../integration'
4
4
 
5
- require 'datadog/appsec/contrib/rack/configuration/settings'
6
- require 'datadog/appsec/contrib/rack/patcher'
7
- require 'datadog/appsec/contrib/rack/request_middleware'
8
- require 'datadog/appsec/contrib/rack/request_body_middleware'
5
+ require_relative 'configuration/settings'
6
+ require_relative 'patcher'
7
+ require_relative 'request_middleware'
8
+ require_relative 'request_body_middleware'
9
9
 
10
10
  module Datadog
11
11
  module AppSec
@@ -1,7 +1,7 @@
1
1
  # typed: ignore
2
2
 
3
- require 'datadog/appsec/contrib/patcher'
4
- require 'datadog/appsec/contrib/rack/gateway/watcher'
3
+ require_relative '../patcher'
4
+ require_relative 'gateway/watcher'
5
5
 
6
6
  module Datadog
7
7
  module AppSec
@@ -1,6 +1,6 @@
1
1
  # typed: true
2
2
 
3
- require 'datadog/appsec/contrib/rack/request'
3
+ require_relative '../request'
4
4
 
5
5
  module Datadog
6
6
  module AppSec
@@ -1,6 +1,6 @@
1
1
  # typed: true
2
2
 
3
- require 'datadog/appsec/contrib/rack/request'
3
+ require_relative '../request'
4
4
 
5
5
  module Datadog
6
6
  module AppSec
@@ -1,6 +1,6 @@
1
1
  # typed: true
2
2
 
3
- require 'datadog/appsec/contrib/rack/response'
3
+ require_relative '../response'
4
4
 
5
5
  module Datadog
6
6
  module AppSec
@@ -1,7 +1,7 @@
1
1
  # typed: ignore
2
2
 
3
- require 'datadog/appsec/instrumentation/gateway'
4
- require 'datadog/appsec/assets'
3
+ require_relative '../../instrumentation/gateway'
4
+ require_relative '../../assets'
5
5
 
6
6
  module Datadog
7
7
  module AppSec
@@ -2,9 +2,9 @@
2
2
 
3
3
  require 'json'
4
4
 
5
- require 'datadog/appsec/instrumentation/gateway'
6
- require 'datadog/appsec/processor'
7
- require 'datadog/appsec/assets'
5
+ require_relative '../../instrumentation/gateway'
6
+ require_relative '../../processor'
7
+ require_relative '../../assets'
8
8
 
9
9
  module Datadog
10
10
  module AppSec
@@ -1,7 +1,7 @@
1
1
  # typed: false
2
2
 
3
- require 'datadog/appsec/contrib/configuration/settings'
4
- require 'datadog/appsec/contrib/rails/ext'
3
+ require_relative '../../configuration/settings'
4
+ require_relative '../ext'
5
5
 
6
6
  module Datadog
7
7
  module AppSec
@@ -1,9 +1,9 @@
1
1
  # typed: false
2
2
 
3
- require 'datadog/appsec/instrumentation/gateway'
4
- require 'datadog/appsec/reactive/operation'
5
- require 'datadog/appsec/contrib/rails/reactive/action'
6
- require 'datadog/appsec/event'
3
+ require_relative '../../../instrumentation/gateway'
4
+ require_relative '../../../reactive/operation'
5
+ require_relative '../reactive/action'
6
+ require_relative '../../../event'
7
7
 
8
8
  module Datadog
9
9
  module AppSec