ddtrace 1.14.0 → 1.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +178 -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/watcher.rb +8 -6
  32. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +2 -7
  33. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +2 -5
  34. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +2 -5
  35. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +3 -2
  36. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +23 -9
  37. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +3 -2
  38. data/lib/datadog/appsec/contrib/rails/patcher.rb +9 -3
  39. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +2 -5
  40. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +6 -4
  41. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +13 -7
  42. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +2 -5
  43. data/lib/datadog/appsec/event.rb +106 -50
  44. data/lib/datadog/appsec/monitor/gateway/watcher.rb +3 -3
  45. data/lib/datadog/appsec/monitor/reactive/set_user.rb +2 -5
  46. data/lib/datadog/appsec/processor/actions.rb +49 -0
  47. data/lib/datadog/appsec/processor/rule_merger.rb +22 -2
  48. data/lib/datadog/appsec/processor.rb +34 -6
  49. data/lib/datadog/appsec/remote.rb +4 -1
  50. data/lib/datadog/appsec/response.rb +82 -4
  51. data/lib/datadog/appsec/sample_rate.rb +21 -0
  52. data/lib/datadog/appsec.rb +2 -2
  53. data/lib/datadog/core/configuration/agent_settings_resolver.rb +29 -24
  54. data/lib/datadog/core/configuration/base.rb +1 -11
  55. data/lib/datadog/core/configuration/components.rb +7 -2
  56. data/lib/datadog/core/configuration/ext.rb +21 -0
  57. data/lib/datadog/core/configuration/option.rb +2 -4
  58. data/lib/datadog/core/configuration/option_definition.rb +17 -41
  59. data/lib/datadog/core/configuration/options.rb +5 -5
  60. data/lib/datadog/core/configuration/settings.rb +47 -45
  61. data/lib/datadog/core/environment/execution.rb +47 -9
  62. data/lib/datadog/core/environment/variable_helpers.rb +0 -69
  63. data/lib/datadog/core/error.rb +1 -0
  64. data/lib/datadog/core/git/ext.rb +2 -0
  65. data/lib/datadog/core/remote/client/capabilities.rb +1 -1
  66. data/lib/datadog/core/remote/component.rb +2 -2
  67. data/lib/datadog/core/remote/negotiation.rb +2 -2
  68. data/lib/datadog/core/remote/transport/config.rb +60 -0
  69. data/lib/datadog/core/remote/transport/http/api/instance.rb +39 -0
  70. data/lib/datadog/core/remote/transport/http/api/spec.rb +21 -0
  71. data/lib/datadog/core/remote/transport/http/api.rb +58 -0
  72. data/lib/datadog/core/remote/transport/http/builder.rb +219 -0
  73. data/lib/datadog/core/remote/transport/http/client.rb +48 -0
  74. data/lib/datadog/core/remote/transport/http/config.rb +280 -0
  75. data/lib/datadog/core/remote/transport/http/negotiation.rb +146 -0
  76. data/lib/datadog/core/remote/transport/http.rb +179 -0
  77. data/lib/datadog/core/{transport → remote/transport}/negotiation.rb +25 -23
  78. data/lib/datadog/core/telemetry/collector.rb +3 -2
  79. data/lib/datadog/core/telemetry/http/transport.rb +2 -1
  80. data/lib/datadog/core/transport/ext.rb +47 -0
  81. data/lib/datadog/core/transport/http/adapters/net.rb +168 -0
  82. data/lib/datadog/core/transport/http/adapters/registry.rb +29 -0
  83. data/lib/datadog/core/transport/http/adapters/test.rb +89 -0
  84. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +83 -0
  85. data/lib/datadog/core/transport/http/api/endpoint.rb +31 -0
  86. data/lib/datadog/core/transport/http/api/fallbacks.rb +26 -0
  87. data/lib/datadog/core/transport/http/api/map.rb +18 -0
  88. data/lib/datadog/core/transport/http/env.rb +62 -0
  89. data/lib/datadog/core/transport/http/response.rb +60 -0
  90. data/lib/datadog/core/transport/parcel.rb +22 -0
  91. data/lib/datadog/core/transport/request.rb +17 -0
  92. data/lib/datadog/core/transport/response.rb +64 -0
  93. data/lib/datadog/core/workers/polling.rb +2 -2
  94. data/lib/datadog/opentelemetry/api/context.rb +10 -3
  95. data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -1
  96. data/lib/datadog/opentelemetry/sdk/span_processor.rb +14 -2
  97. data/lib/datadog/opentelemetry/sdk/trace/span.rb +68 -0
  98. data/lib/datadog/opentelemetry/trace.rb +58 -0
  99. data/lib/datadog/opentelemetry.rb +1 -0
  100. data/lib/datadog/opentracer.rb +9 -0
  101. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +12 -18
  102. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  103. data/lib/datadog/profiling/collectors/thread_context.rb +9 -1
  104. data/lib/datadog/profiling/component.rb +24 -99
  105. data/lib/datadog/profiling/ext.rb +0 -12
  106. data/lib/datadog/profiling/flush.rb +0 -3
  107. data/lib/datadog/profiling/http_transport.rb +6 -3
  108. data/lib/datadog/profiling/native_extension.rb +0 -21
  109. data/lib/datadog/profiling/profiler.rb +11 -12
  110. data/lib/datadog/profiling.rb +8 -81
  111. data/lib/datadog/tracing/component.rb +10 -4
  112. data/lib/datadog/tracing/configuration/agent_settings_resolver.rb +13 -0
  113. data/lib/datadog/tracing/configuration/ext.rb +4 -2
  114. data/lib/datadog/tracing/configuration/settings.rb +14 -7
  115. data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +1 -1
  116. data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +1 -1
  117. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +4 -0
  118. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +104 -197
  119. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +3 -0
  120. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +7 -0
  121. data/lib/datadog/tracing/contrib/configuration/settings.rb +1 -1
  122. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +6 -0
  123. data/lib/datadog/tracing/contrib/dalli/ext.rb +7 -0
  124. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +9 -2
  125. data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +1 -1
  126. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +5 -0
  127. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +5 -0
  128. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +8 -0
  129. data/lib/datadog/tracing/contrib/excon/middleware.rb +5 -0
  130. data/lib/datadog/tracing/contrib/ext.rb +3 -0
  131. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +1 -1
  132. data/lib/datadog/tracing/contrib/faraday/middleware.rb +5 -0
  133. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +21 -1
  134. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +11 -1
  135. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +18 -0
  136. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +0 -4
  137. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +3 -3
  138. data/lib/datadog/tracing/contrib/http/instrumentation.rb +5 -0
  139. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +5 -0
  140. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +5 -0
  141. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +7 -0
  142. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +13 -3
  143. data/lib/datadog/tracing/contrib/opensearch/integration.rb +2 -2
  144. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +7 -0
  145. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +5 -0
  146. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +5 -0
  147. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +1 -1
  148. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +1 -1
  149. data/lib/datadog/tracing/contrib/racecar/event.rb +5 -0
  150. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +14 -4
  151. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +4 -4
  152. data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +1 -1
  153. data/lib/datadog/tracing/contrib/redis/tags.rb +5 -0
  154. data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +1 -1
  155. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +5 -0
  156. data/lib/datadog/tracing/contrib/sequel/utils.rb +5 -0
  157. data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +1 -1
  158. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +1 -1
  159. data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +1 -1
  160. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +2 -2
  161. data/lib/datadog/tracing/distributed/propagation.rb +13 -33
  162. data/lib/datadog/tracing/metadata/tagging.rb +3 -3
  163. data/lib/datadog/tracing/sync_writer.rb +3 -3
  164. data/lib/datadog/tracing/tracer.rb +2 -0
  165. data/lib/datadog/{core → tracing}/transport/http/api/instance.rb +1 -1
  166. data/lib/datadog/{core → tracing}/transport/http/api/spec.rb +1 -1
  167. data/lib/datadog/tracing/transport/http/api.rb +43 -0
  168. data/lib/datadog/{core → tracing}/transport/http/builder.rb +13 -68
  169. data/lib/datadog/tracing/transport/http/client.rb +57 -0
  170. data/lib/datadog/tracing/transport/http/statistics.rb +47 -0
  171. data/lib/datadog/tracing/transport/http/traces.rb +152 -0
  172. data/lib/datadog/tracing/transport/http.rb +124 -0
  173. data/lib/datadog/tracing/transport/io/client.rb +89 -0
  174. data/lib/datadog/tracing/transport/io/response.rb +27 -0
  175. data/lib/datadog/tracing/transport/io/traces.rb +101 -0
  176. data/lib/datadog/tracing/transport/io.rb +30 -0
  177. data/lib/datadog/tracing/transport/serializable_trace.rb +126 -0
  178. data/lib/datadog/tracing/transport/statistics.rb +77 -0
  179. data/lib/datadog/tracing/transport/trace_formatter.rb +209 -0
  180. data/lib/datadog/tracing/transport/traces.rb +224 -0
  181. data/lib/datadog/tracing/workers/trace_writer.rb +5 -3
  182. data/lib/datadog/tracing/workers.rb +3 -2
  183. data/lib/datadog/tracing/writer.rb +5 -2
  184. data/lib/ddtrace/transport/ext.rb +17 -15
  185. data/lib/ddtrace/version.rb +1 -1
  186. data/lib/ddtrace.rb +1 -1
  187. metadata +72 -96
  188. data/lib/datadog/ci/configuration/components.rb +0 -32
  189. data/lib/datadog/ci/configuration/settings.rb +0 -51
  190. data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +0 -35
  191. data/lib/datadog/ci/contrib/cucumber/ext.rb +0 -22
  192. data/lib/datadog/ci/contrib/cucumber/formatter.rb +0 -94
  193. data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +0 -28
  194. data/lib/datadog/ci/contrib/cucumber/integration.rb +0 -47
  195. data/lib/datadog/ci/contrib/cucumber/patcher.rb +0 -27
  196. data/lib/datadog/ci/contrib/minitest/configuration/settings.rb +0 -35
  197. data/lib/datadog/ci/contrib/minitest/ext.rb +0 -21
  198. data/lib/datadog/ci/contrib/minitest/integration.rb +0 -49
  199. data/lib/datadog/ci/contrib/minitest/patcher.rb +0 -27
  200. data/lib/datadog/ci/contrib/minitest/test_helper.rb +0 -68
  201. data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +0 -35
  202. data/lib/datadog/ci/contrib/rspec/example.rb +0 -68
  203. data/lib/datadog/ci/contrib/rspec/ext.rb +0 -21
  204. data/lib/datadog/ci/contrib/rspec/integration.rb +0 -48
  205. data/lib/datadog/ci/contrib/rspec/patcher.rb +0 -27
  206. data/lib/datadog/ci/ext/app_types.rb +0 -9
  207. data/lib/datadog/ci/ext/environment.rb +0 -575
  208. data/lib/datadog/ci/ext/settings.rb +0 -10
  209. data/lib/datadog/ci/ext/test.rb +0 -35
  210. data/lib/datadog/ci/extensions.rb +0 -19
  211. data/lib/datadog/ci/flush.rb +0 -38
  212. data/lib/datadog/ci/test.rb +0 -81
  213. data/lib/datadog/ci.rb +0 -21
  214. data/lib/datadog/core/configuration/dependency_resolver.rb +0 -28
  215. data/lib/datadog/core/configuration/option_definition_set.rb +0 -22
  216. data/lib/datadog/core/configuration/option_set.rb +0 -10
  217. data/lib/datadog/core/transport/config.rb +0 -58
  218. data/lib/datadog/core/transport/http/api.rb +0 -57
  219. data/lib/datadog/core/transport/http/client.rb +0 -45
  220. data/lib/datadog/core/transport/http/config.rb +0 -278
  221. data/lib/datadog/core/transport/http/negotiation.rb +0 -144
  222. data/lib/datadog/core/transport/http.rb +0 -169
  223. data/lib/datadog/core/utils/object_set.rb +0 -43
  224. data/lib/datadog/core/utils/string_table.rb +0 -47
  225. data/lib/datadog/profiling/backtrace_location.rb +0 -34
  226. data/lib/datadog/profiling/buffer.rb +0 -43
  227. data/lib/datadog/profiling/collectors/old_stack.rb +0 -301
  228. data/lib/datadog/profiling/encoding/profile.rb +0 -41
  229. data/lib/datadog/profiling/event.rb +0 -15
  230. data/lib/datadog/profiling/events/stack.rb +0 -82
  231. data/lib/datadog/profiling/old_recorder.rb +0 -107
  232. data/lib/datadog/profiling/pprof/builder.rb +0 -125
  233. data/lib/datadog/profiling/pprof/converter.rb +0 -102
  234. data/lib/datadog/profiling/pprof/message_set.rb +0 -16
  235. data/lib/datadog/profiling/pprof/payload.rb +0 -20
  236. data/lib/datadog/profiling/pprof/pprof.proto +0 -212
  237. data/lib/datadog/profiling/pprof/pprof_pb.rb +0 -81
  238. data/lib/datadog/profiling/pprof/stack_sample.rb +0 -139
  239. data/lib/datadog/profiling/pprof/string_table.rb +0 -12
  240. data/lib/datadog/profiling/pprof/template.rb +0 -118
  241. data/lib/datadog/profiling/trace_identifiers/ddtrace.rb +0 -43
  242. data/lib/datadog/profiling/trace_identifiers/helper.rb +0 -45
  243. data/lib/ddtrace/transport/http/adapters/net.rb +0 -168
  244. data/lib/ddtrace/transport/http/adapters/registry.rb +0 -27
  245. data/lib/ddtrace/transport/http/adapters/test.rb +0 -85
  246. data/lib/ddtrace/transport/http/adapters/unix_socket.rb +0 -77
  247. data/lib/ddtrace/transport/http/api/endpoint.rb +0 -29
  248. data/lib/ddtrace/transport/http/api/fallbacks.rb +0 -24
  249. data/lib/ddtrace/transport/http/api/instance.rb +0 -35
  250. data/lib/ddtrace/transport/http/api/map.rb +0 -16
  251. data/lib/ddtrace/transport/http/api/spec.rb +0 -17
  252. data/lib/ddtrace/transport/http/api.rb +0 -39
  253. data/lib/ddtrace/transport/http/builder.rb +0 -176
  254. data/lib/ddtrace/transport/http/client.rb +0 -52
  255. data/lib/ddtrace/transport/http/env.rb +0 -58
  256. data/lib/ddtrace/transport/http/response.rb +0 -58
  257. data/lib/ddtrace/transport/http/statistics.rb +0 -43
  258. data/lib/ddtrace/transport/http/traces.rb +0 -144
  259. data/lib/ddtrace/transport/http.rb +0 -117
  260. data/lib/ddtrace/transport/io/client.rb +0 -85
  261. data/lib/ddtrace/transport/io/response.rb +0 -25
  262. data/lib/ddtrace/transport/io/traces.rb +0 -99
  263. data/lib/ddtrace/transport/io.rb +0 -28
  264. data/lib/ddtrace/transport/parcel.rb +0 -20
  265. data/lib/ddtrace/transport/request.rb +0 -15
  266. data/lib/ddtrace/transport/response.rb +0 -60
  267. data/lib/ddtrace/transport/serializable_trace.rb +0 -122
  268. data/lib/ddtrace/transport/statistics.rb +0 -75
  269. data/lib/ddtrace/transport/trace_formatter.rb +0 -207
  270. data/lib/ddtrace/transport/traces.rb +0 -216
@@ -1,301 +0,0 @@
1
- require_relative '../../core/utils/only_once'
2
- require_relative '../../core/utils/time'
3
- require_relative '../../core/worker'
4
- require_relative '../../core/workers/polling'
5
- require_relative '../backtrace_location'
6
- require_relative '../events/stack'
7
- require_relative '../native_extension'
8
-
9
- module Datadog
10
- module Profiling
11
- module Collectors
12
- # Collects stack trace samples from Ruby threads for both CPU-time (if available) and wall-clock.
13
- # Runs on its own background thread.
14
- #
15
- # This class has the prefix "Old" because it will be deprecated by the new native CPU Profiler
16
- class OldStack < Core::Worker
17
- include Core::Workers::Polling
18
-
19
- DEFAULT_MAX_TIME_USAGE_PCT = 2.0
20
- MIN_INTERVAL = 0.01
21
- THREAD_LAST_CPU_TIME_KEY = :datadog_profiler_last_cpu_time
22
- THREAD_LAST_WALL_CLOCK_KEY = :datadog_profiler_last_wall_clock
23
- SYNTHETIC_STACK_IN_NATIVE_CODE = [BacktraceLocation.new('', 0, 'In native code').freeze].freeze
24
-
25
- # This default was picked based on the current sampling performance and on expected concurrency on an average
26
- # Ruby MRI application. Lowering this optimizes for latency (less impact each time we sample), and raising
27
- # optimizes for coverage (less chance to miss what a given thread is doing).
28
- DEFAULT_MAX_THREADS_SAMPLED = 16
29
-
30
- attr_reader \
31
- :recorder,
32
- :max_frames,
33
- :trace_identifiers_helper,
34
- :ignore_thread,
35
- :max_time_usage_pct,
36
- :thread_api,
37
- :cpu_time_provider
38
-
39
- def initialize(
40
- recorder,
41
- max_frames:,
42
- trace_identifiers_helper:, # Usually an instance of Profiling::TraceIdentifiers::Helper
43
- ignore_thread: nil,
44
- max_time_usage_pct: DEFAULT_MAX_TIME_USAGE_PCT,
45
- max_threads_sampled: DEFAULT_MAX_THREADS_SAMPLED,
46
- thread_api: Thread,
47
- cpu_time_provider: Profiling::NativeExtension,
48
- fork_policy: Core::Workers::Async::Thread::FORK_POLICY_RESTART, # Restart in forks by default
49
- interval: MIN_INTERVAL,
50
- enabled: true
51
- )
52
- @recorder = recorder
53
- @max_frames = max_frames
54
- @trace_identifiers_helper = trace_identifiers_helper
55
- @ignore_thread = ignore_thread
56
- @max_time_usage_pct = max_time_usage_pct
57
- @max_threads_sampled = max_threads_sampled
58
- @thread_api = thread_api
59
- # Only set the provider if it's able to work in the current Ruby/OS combo
60
- @cpu_time_provider = cpu_time_provider unless cpu_time_provider.cpu_time_ns_for(thread_api.current).nil?
61
-
62
- # Workers::Async::Thread settings
63
- self.fork_policy = fork_policy
64
-
65
- # Workers::IntervalLoop settings
66
- self.loop_base_interval = interval
67
-
68
- # Workers::Polling settings
69
- self.enabled = enabled
70
-
71
- # Cache this proc, since it's pretty expensive to keep recreating it
72
- @build_backtrace_location = method(:build_backtrace_location).to_proc
73
- # Cache this buffer, since it's pretty expensive to keep accessing it
74
- @stack_sample_event_recorder = recorder[Events::StackSample]
75
- # See below for details on why this is needed
76
- @needs_process_waiter_workaround = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7')
77
- end
78
-
79
- def start
80
- reset_cpu_time_tracking
81
- perform
82
- end
83
-
84
- def perform
85
- collect_and_wait
86
- end
87
-
88
- def collect_and_wait
89
- run_time = Core::Utils::Time.measure do
90
- collect_events
91
- end
92
-
93
- # Update wait time to throttle profiling
94
- self.loop_wait_time = compute_wait_time(run_time)
95
- end
96
-
97
- def collect_events
98
- events = []
99
- current_wall_time_ns = get_current_wall_time_timestamp_ns
100
-
101
- # Collect backtraces from each thread
102
- threads_to_sample.each do |thread|
103
- next unless thread.alive?
104
- next if ignore_thread.is_a?(Proc) && ignore_thread.call(thread)
105
-
106
- event = collect_thread_event(thread, current_wall_time_ns)
107
- events << event unless event.nil?
108
- end
109
-
110
- # Send events to recorder
111
- recorder.push(events) unless events.empty?
112
-
113
- events
114
- end
115
-
116
- def collect_thread_event(thread, current_wall_time_ns)
117
- locations = thread.backtrace_locations
118
- return if locations.nil?
119
-
120
- # Having empty locations means that the thread is alive, but we don't know what it's doing:
121
- #
122
- # 1. It can be starting up
123
- # ```
124
- # > Thread.new { sleep }.backtrace
125
- # => [] # <-- note the thread hasn't actually started running sleep yet, we got there first
126
- # ```
127
- # 2. It can be running native code
128
- # ```
129
- # > t = Process.detach(fork { sleep })
130
- # => #<Process::Waiter:0x00007ffe7285f7a0 run>
131
- # > t.backtrace
132
- # => [] # <-- this can happen even minutes later, e.g. it's not a race as in 1.
133
- # ```
134
- # This effect has been observed in threads created by the Iodine web server and the ffi gem
135
- #
136
- # To give customers visibility into these threads, we replace the empty stack with one containing a
137
- # synthetic placeholder frame, so that these threads are properly represented in the UX.
138
- locations = SYNTHETIC_STACK_IN_NATIVE_CODE if locations.empty?
139
-
140
- # Get actual stack size then trim the stack
141
- stack_size = locations.length
142
- locations = locations[0..(max_frames - 1)]
143
-
144
- # Convert backtrace locations into structs
145
- locations = convert_backtrace_locations(locations)
146
-
147
- thread_id = thread.object_id
148
- root_span_id, span_id, trace_resource = trace_identifiers_helper.trace_identifiers_for(thread)
149
- cpu_time = get_cpu_time_interval!(thread)
150
- wall_time_interval_ns =
151
- get_elapsed_since_last_sample_and_set_value(thread, THREAD_LAST_WALL_CLOCK_KEY, current_wall_time_ns)
152
-
153
- Events::StackSample.new(
154
- nil,
155
- locations,
156
- stack_size,
157
- thread_id,
158
- root_span_id,
159
- span_id,
160
- trace_resource,
161
- cpu_time,
162
- wall_time_interval_ns
163
- )
164
- end
165
-
166
- def get_cpu_time_interval!(thread)
167
- return unless cpu_time_provider
168
-
169
- current_cpu_time_ns = cpu_time_provider.cpu_time_ns_for(thread)
170
-
171
- return unless current_cpu_time_ns
172
-
173
- get_elapsed_since_last_sample_and_set_value(thread, THREAD_LAST_CPU_TIME_KEY, current_cpu_time_ns)
174
- end
175
-
176
- def compute_wait_time(used_time)
177
- # We took used_time to get the last sample.
178
- #
179
- # What we're computing here is -- if used_time corresponds to max_time_usage_pct of the time we should
180
- # spend working, how much is (100% - max_time_usage_pct) of the time?
181
- #
182
- # For instance, if we took 10ms to sample, and max_time_usage_pct is 1%, then the other 99% is 990ms, which
183
- # means we need to sleep for 990ms to guarantee that we don't spend more than 1% of the time working.
184
- used_time_ns = used_time * 1e9
185
- interval = (used_time_ns / (max_time_usage_pct / 100.0)) - used_time_ns
186
- [interval / 1e9, MIN_INTERVAL].max
187
- end
188
-
189
- # Convert backtrace locations into structs
190
- # Re-use old backtrace location objects if they already exist in the buffer
191
- def convert_backtrace_locations(locations)
192
- locations.collect do |location|
193
- # Re-use existing BacktraceLocation if identical copy, otherwise build a new one.
194
- @stack_sample_event_recorder.cache(:backtrace_locations).fetch(
195
- # Function name
196
- location.base_label,
197
- # Line number
198
- location.lineno,
199
- # Filename
200
- location.path,
201
- # Build function
202
- &@build_backtrace_location
203
- )
204
- end
205
- end
206
-
207
- def build_backtrace_location(_id, base_label, lineno, path)
208
- string_table = @stack_sample_event_recorder.string_table
209
-
210
- Profiling::BacktraceLocation.new(
211
- string_table.fetch_string(base_label),
212
- lineno,
213
- string_table.fetch_string(path)
214
- )
215
- end
216
-
217
- def reset_after_fork
218
- recorder.reset_after_fork
219
-
220
- # NOTE: We could perhaps also call #reset_cpu_time_tracking here, although it's not needed because we always
221
- # call in in #start.
222
- end
223
-
224
- private
225
-
226
- # If the profiler is started for a while, stopped and then restarted OR whenever the process forks, we need to
227
- # clean up any leftover per-thread counters, so that the first sample after starting doesn't end up with:
228
- #
229
- # a) negative time: At least on my test docker container, and on the reliability environment, after the process
230
- # forks, the cpu time reference changes and (old cpu time - new cpu time) can be < 0
231
- #
232
- # b) large amount of time: if the profiler was started, then stopped for some amount of time, and then
233
- # restarted, we don't want the first sample to be "blamed" for multiple minutes of CPU time
234
- #
235
- # By resetting the last cpu time seen, we start with a clean slate every time we start the stack collector.
236
- def reset_cpu_time_tracking
237
- thread_api.list.each do |thread|
238
- # See below for details on why this is needed
239
- next if @needs_process_waiter_workaround && thread.is_a?(::Process::Waiter)
240
-
241
- thread.thread_variable_set(THREAD_LAST_CPU_TIME_KEY, nil)
242
- thread.thread_variable_set(THREAD_LAST_WALL_CLOCK_KEY, nil)
243
- end
244
- end
245
-
246
- def get_elapsed_since_last_sample_and_set_value(thread, key, current_value)
247
- # Process::Waiter crash workaround:
248
- #
249
- # This is a workaround for a Ruby VM segfault (usually something like
250
- # "[BUG] Segmentation fault at 0x0000000000000008") in the affected Ruby versions.
251
- # See https://bugs.ruby-lang.org/issues/17807 for details.
252
- #
253
- # In those Ruby versions, there's a very special subclass of `Thread` called `Process::Waiter` that causes VM
254
- # crashes whenever something tries to read its instance or thread variables. This subclass of thread only
255
- # shows up when the `Process.detach` API gets used.
256
- # In the specs you'll find crash regression tests that include a way of reproducing it.
257
- #
258
- # As workaround for now we just skip it for the affected Rubies
259
- return 0 if @needs_process_waiter_workaround && thread.is_a?(::Process::Waiter)
260
-
261
- last_value = thread.thread_variable_get(key) || current_value
262
- thread.thread_variable_set(key, current_value)
263
-
264
- current_value - last_value
265
- end
266
-
267
- # Whenever there are more than max_threads_sampled active, we only sample a subset of them.
268
- # We do this to avoid impacting the latency of the service being profiled. We want to avoid doing
269
- # a big burst of work all at once (sample everything), and instead do a little work each time
270
- # (sample a bit by bit).
271
- #
272
- # Because we pick the threads to sample randomly, we'll eventually sample all threads -- just not at once.
273
- # Notice also that this will interact with our dynamic sampling mechanism -- if samples are faster, we take
274
- # them more often, if they are slower, we take them less often -- which again means that over a longer period
275
- # we should take sample roughly the same samples.
276
- #
277
- # One downside of this approach is that if there really are many threads, the resulting wall clock times
278
- # in a one minute profile may "drift" around the 60 second mark, e.g. maybe we only sampled a thread once per
279
- # second and only 59 times, so we'll report 59s, but on the next report we'll include the missing one, so
280
- # then the result will be 61s. I've observed 60 +- 1.68 secs for an app with ~65 threads, given the
281
- # default maximum of 16 threads. This seems a reasonable enough margin of error given the improvement to
282
- # latency (especially on such a large application! -> even bigger latency impact if we tried to sample all
283
- # threads).
284
- #
285
- def threads_to_sample
286
- all_threads = thread_api.list
287
-
288
- if all_threads.size > @max_threads_sampled
289
- all_threads.sample(@max_threads_sampled)
290
- else
291
- all_threads
292
- end
293
- end
294
-
295
- def get_current_wall_time_timestamp_ns
296
- Core::Utils::Time.get_time(:nanosecond)
297
- end
298
- end
299
- end
300
- end
301
- end
@@ -1,41 +0,0 @@
1
- require 'time'
2
-
3
- require_relative '../pprof/template'
4
-
5
- module Datadog
6
- module Profiling
7
- module Encoding
8
- module Profile
9
- # Encodes gathered data into the pprof format
10
- module Protobuf
11
- module_function
12
-
13
- def encode(event_count:, event_groups:, start:, finish:)
14
- # Create a pprof template from the list of event types
15
- event_classes = event_groups.collect(&:event_class).uniq
16
- template = Pprof::Template.for_event_classes(event_classes)
17
-
18
- # Add all events to the pprof
19
- event_groups.each { |event_group| template.add_events!(event_group.event_class, event_group.events) }
20
-
21
- Datadog.logger.debug do
22
- max_events = Datadog.configuration.profiling.advanced.max_events
23
- events_sampled =
24
- if event_count == max_events
25
- 'max events limit hit, events were sampled [profile will be biased], '
26
- else
27
- ''
28
- end
29
-
30
- "Encoding profile covering #{start.iso8601} to #{finish.iso8601}, " \
31
- "events: #{event_count} (#{events_sampled}#{template.debug_statistics})"
32
- end
33
-
34
- # Build the profile and encode it
35
- template.to_pprof(start: start, finish: finish)
36
- end
37
- end
38
- end
39
- end
40
- end
41
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Datadog
4
- module Profiling
5
- # Describes a sample of some data obtained from the runtime.
6
- class Event
7
- attr_reader \
8
- :timestamp
9
-
10
- def initialize(timestamp = nil)
11
- @timestamp = timestamp || Time.now.utc.to_f
12
- end
13
- end
14
- end
15
- end
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../event'
4
-
5
- module Datadog
6
- module Profiling
7
- module Events
8
- # Describes a stack profiling event
9
- class Stack < Event
10
- attr_reader \
11
- :hash,
12
- :frames,
13
- :total_frame_count,
14
- :thread_id,
15
- :root_span_id,
16
- :span_id,
17
- :trace_resource
18
-
19
- def initialize(
20
- timestamp,
21
- frames,
22
- total_frame_count,
23
- thread_id,
24
- root_span_id,
25
- span_id,
26
- trace_resource
27
- )
28
- super(timestamp)
29
-
30
- @frames = frames
31
- @total_frame_count = total_frame_count
32
- @thread_id = thread_id
33
- @root_span_id = root_span_id
34
- @span_id = span_id
35
- @trace_resource = trace_resource
36
-
37
- @hash = [
38
- thread_id,
39
- root_span_id,
40
- span_id,
41
- # trace_resource is deliberately not included -- events that share the same (root_span_id, span_id) refer
42
- # to the same trace
43
- frames.collect(&:hash),
44
- total_frame_count
45
- ].hash
46
- end
47
- end
48
-
49
- # Describes a stack sample
50
- class StackSample < Stack
51
- attr_reader \
52
- :cpu_time_interval_ns,
53
- :wall_time_interval_ns
54
-
55
- def initialize(
56
- timestamp,
57
- frames,
58
- total_frame_count,
59
- thread_id,
60
- root_span_id,
61
- span_id,
62
- trace_resource,
63
- cpu_time_interval_ns,
64
- wall_time_interval_ns
65
- )
66
- super(
67
- timestamp,
68
- frames,
69
- total_frame_count,
70
- thread_id,
71
- root_span_id,
72
- span_id,
73
- trace_resource
74
- )
75
-
76
- @cpu_time_interval_ns = cpu_time_interval_ns
77
- @wall_time_interval_ns = wall_time_interval_ns
78
- end
79
- end
80
- end
81
- end
82
- end
@@ -1,107 +0,0 @@
1
- require_relative 'buffer'
2
- require_relative 'encoding/profile'
3
-
4
- module Datadog
5
- module Profiling
6
- # Stores profiling events gathered by the `Stack` collector
7
- class OldRecorder
8
- attr_reader :max_size
9
-
10
- def initialize(
11
- event_classes,
12
- max_size,
13
- last_flush_time: Time.now.utc
14
- )
15
- @buffers = {}
16
- @last_flush_time = last_flush_time
17
- @max_size = max_size
18
-
19
- # Add a buffer for each class
20
- event_classes.each do |event_class|
21
- @buffers[event_class] = Profiling::Buffer.new(max_size)
22
- end
23
-
24
- # Event classes can only be added ahead of time
25
- @buffers.freeze
26
- end
27
-
28
- def [](event_class)
29
- @buffers[event_class]
30
- end
31
-
32
- def push(events)
33
- if events.is_a?(Array)
34
- # Push multiple events
35
- event_class = events.first.class
36
- raise UnknownEventError, event_class unless @buffers.key?(event_class)
37
-
38
- @buffers[event_class].concat(events)
39
- else
40
- # Push single event
41
- event_class = events.class
42
- raise UnknownEventError, event_class unless @buffers.key?(event_class)
43
-
44
- @buffers[event_class].push(events)
45
- end
46
- end
47
-
48
- def serialize
49
- event_count = 0
50
-
51
- event_groups, start, finish = update_time do
52
- @buffers.collect do |event_class, buffer|
53
- events = buffer.pop
54
- next if events.empty?
55
-
56
- event_count += events.length
57
- EventGroup.new(event_class, events)
58
- end.compact
59
- end
60
-
61
- return if event_count.zero? # We don't want to report empty profiles
62
-
63
- encoded_pprof =
64
- Datadog::Profiling::Encoding::Profile::Protobuf.encode(
65
- event_count: event_count,
66
- event_groups: event_groups,
67
- start: start,
68
- finish: finish,
69
- )
70
-
71
- [start, finish, encoded_pprof]
72
- end
73
-
74
- def reset_after_fork
75
- Datadog.logger.debug('Resetting OldRecorder in child process after fork')
76
-
77
- # NOTE: A bit of a heavy-handed approach, but it doesn't happen often and this class will be removed soon anyway
78
- serialize
79
- nil
80
- end
81
-
82
- # Error when event of an unknown type is used with the OldRecorder
83
- class UnknownEventError < StandardError
84
- attr_reader :event_class
85
-
86
- def initialize(event_class)
87
- @event_class = event_class
88
- end
89
-
90
- def message
91
- @message ||= "Unknown event class '#{event_class}' for profiling recorder."
92
- end
93
- end
94
-
95
- private
96
-
97
- def update_time
98
- start = @last_flush_time
99
- result = yield
100
- @last_flush_time = Time.now.utc
101
-
102
- # Return event groups, start time, finish time
103
- [result, start, @last_flush_time]
104
- end
105
- end
106
- end
107
- end
@@ -1,125 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../flush'
4
- require_relative 'message_set'
5
- require_relative 'string_table'
6
- require_relative '../../core/utils/time'
7
-
8
- module Datadog
9
- module Profiling
10
- module Pprof
11
- # Accumulates profile data and produces a Perftools::Profiles::Profile
12
- class Builder
13
- DEFAULT_ENCODING = 'UTF-8'
14
- DESC_FRAME_OMITTED = 'frame omitted'
15
- DESC_FRAMES_OMITTED = 'frames omitted'
16
-
17
- attr_reader \
18
- :functions,
19
- :locations,
20
- :mappings,
21
- :sample_types,
22
- :samples,
23
- :string_table
24
-
25
- def initialize
26
- @functions = MessageSet.new(1)
27
- @locations = initialize_locations_hash
28
- @mappings = MessageSet.new(1)
29
- @sample_types = MessageSet.new
30
- @samples = []
31
- @string_table = StringTable.new
32
-
33
- # Cache this proc, since it's pretty expensive to keep recreating it
34
- @build_function = method(:build_function).to_proc
35
- end
36
-
37
- # The locations hash maps unique BacktraceLocation instances to their corresponding pprof Location objects;
38
- # there's a 1:1 correspondence, since BacktraceLocations were already deduped
39
- def initialize_locations_hash
40
- sequence = Core::Utils::Sequence.new(1)
41
- Hash.new do |locations_hash, backtrace_location|
42
- locations_hash[backtrace_location] = build_location(sequence.next, backtrace_location)
43
- end
44
- end
45
-
46
- def encode_profile(profile)
47
- Perftools::Profiles::Profile.encode(profile).force_encoding(DEFAULT_ENCODING)
48
- end
49
-
50
- def build_profile(start:, finish:)
51
- start_ns = Core::Utils::Time.as_utc_epoch_ns(start)
52
- finish_ns = Core::Utils::Time.as_utc_epoch_ns(finish)
53
-
54
- Perftools::Profiles::Profile.new(
55
- sample_type: @sample_types.messages,
56
- sample: @samples,
57
- mapping: @mappings.messages,
58
- location: @locations.values,
59
- function: @functions.messages,
60
- string_table: @string_table.strings,
61
- time_nanos: start_ns,
62
- duration_nanos: finish_ns - start_ns,
63
- )
64
- end
65
-
66
- def build_value_type(type, unit)
67
- Perftools::Profiles::ValueType.new(
68
- type: @string_table.fetch(type),
69
- unit: @string_table.fetch(unit)
70
- )
71
- end
72
-
73
- def build_locations(backtrace_locations, length)
74
- locations = backtrace_locations.collect { |backtrace_location| @locations[backtrace_location] }
75
-
76
- omitted = length - backtrace_locations.length
77
-
78
- # Add placeholder stack frame if frames were truncated
79
- if omitted > 0
80
- desc = omitted == 1 ? DESC_FRAME_OMITTED : DESC_FRAMES_OMITTED
81
- locations << @locations[Profiling::BacktraceLocation.new('', 0, "#{omitted} #{desc}")]
82
- end
83
-
84
- locations
85
- end
86
-
87
- def build_location(id, backtrace_location)
88
- Perftools::Profiles::Location.new(
89
- id: id,
90
- line: [build_line(
91
- @functions.fetch(
92
- backtrace_location.path,
93
- backtrace_location.base_label,
94
- &@build_function
95
- ).id,
96
- backtrace_location.lineno
97
- )]
98
- )
99
- end
100
-
101
- def build_line(function_id, line_number)
102
- Perftools::Profiles::Line.new(
103
- function_id: function_id,
104
- line: line_number
105
- )
106
- end
107
-
108
- def build_function(id, filename, function_name)
109
- Perftools::Profiles::Function.new(
110
- id: id,
111
- name: @string_table.fetch(function_name),
112
- filename: @string_table.fetch(filename)
113
- )
114
- end
115
-
116
- def build_mapping(id, filename)
117
- Perftools::Profiles::Mapping.new(
118
- id: id,
119
- filename: @string_table.fetch(filename)
120
- )
121
- end
122
- end
123
- end
124
- end
125
- end