ddtrace 1.18.0 → 1.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -1
  3. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +96 -66
  4. data/ext/ddtrace_profiling_native_extension/collectors_discrete_dynamic_sampler.c +349 -0
  5. data/ext/ddtrace_profiling_native_extension/collectors_discrete_dynamic_sampler.h +89 -0
  6. data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.c +22 -14
  7. data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.h +4 -0
  8. data/ext/ddtrace_profiling_native_extension/collectors_gc_profiling_helper.c +156 -0
  9. data/ext/ddtrace_profiling_native_extension/collectors_gc_profiling_helper.h +5 -0
  10. data/ext/ddtrace_profiling_native_extension/collectors_stack.c +43 -102
  11. data/ext/ddtrace_profiling_native_extension/collectors_stack.h +10 -3
  12. data/ext/ddtrace_profiling_native_extension/collectors_thread_context.c +159 -124
  13. data/ext/ddtrace_profiling_native_extension/collectors_thread_context.h +2 -1
  14. data/ext/ddtrace_profiling_native_extension/extconf.rb +19 -0
  15. data/ext/ddtrace_profiling_native_extension/heap_recorder.c +970 -0
  16. data/ext/ddtrace_profiling_native_extension/heap_recorder.h +155 -0
  17. data/ext/ddtrace_profiling_native_extension/helpers.h +6 -0
  18. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.c +20 -0
  19. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +11 -0
  20. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +5 -0
  21. data/ext/ddtrace_profiling_native_extension/profiling.c +17 -0
  22. data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +147 -0
  23. data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +28 -0
  24. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +329 -10
  25. data/ext/ddtrace_profiling_native_extension/stack_recorder.h +3 -0
  26. data/ext/ddtrace_profiling_native_extension/time_helpers.h +2 -0
  27. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +2 -1
  28. data/lib/datadog/core/configuration/settings.rb +153 -21
  29. data/lib/datadog/core/environment/class_count.rb +6 -6
  30. data/lib/datadog/core/remote/component.rb +25 -12
  31. data/lib/datadog/core/remote/ext.rb +1 -0
  32. data/lib/datadog/core/remote/tie/tracing.rb +39 -0
  33. data/lib/datadog/core/remote/tie.rb +27 -0
  34. data/lib/datadog/core/telemetry/collector.rb +10 -0
  35. data/lib/datadog/core/telemetry/event.rb +2 -1
  36. data/lib/datadog/core/telemetry/ext.rb +3 -0
  37. data/lib/datadog/core/telemetry/v1/app_event.rb +8 -1
  38. data/lib/datadog/core/telemetry/v1/install_signature.rb +38 -0
  39. data/lib/datadog/opentelemetry/sdk/propagator.rb +3 -2
  40. data/lib/datadog/opentelemetry.rb +3 -0
  41. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +5 -12
  42. data/lib/datadog/profiling/component.rb +183 -13
  43. data/lib/datadog/profiling/scheduler.rb +4 -6
  44. data/lib/datadog/profiling/stack_recorder.rb +13 -2
  45. data/lib/datadog/tracing/configuration/ext.rb +0 -1
  46. data/lib/datadog/tracing/configuration/settings.rb +2 -1
  47. data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +1 -0
  48. data/lib/datadog/tracing/contrib/action_cable/ext.rb +1 -0
  49. data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +1 -0
  50. data/lib/datadog/tracing/contrib/action_mailer/ext.rb +1 -0
  51. data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +1 -0
  52. data/lib/datadog/tracing/contrib/action_pack/ext.rb +1 -0
  53. data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +1 -0
  54. data/lib/datadog/tracing/contrib/action_view/ext.rb +1 -0
  55. data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +1 -0
  56. data/lib/datadog/tracing/contrib/active_job/ext.rb +1 -0
  57. data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +1 -0
  58. data/lib/datadog/tracing/contrib/active_model_serializers/ext.rb +1 -0
  59. data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +1 -0
  60. data/lib/datadog/tracing/contrib/active_record/ext.rb +1 -0
  61. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +1 -0
  62. data/lib/datadog/tracing/contrib/active_support/ext.rb +1 -0
  63. data/lib/datadog/tracing/contrib/analytics.rb +0 -1
  64. data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +1 -0
  65. data/lib/datadog/tracing/contrib/aws/ext.rb +1 -0
  66. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +1 -0
  67. data/lib/datadog/tracing/contrib/dalli/ext.rb +1 -0
  68. data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +1 -0
  69. data/lib/datadog/tracing/contrib/delayed_job/ext.rb +1 -0
  70. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +1 -0
  71. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +1 -0
  72. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +1 -0
  73. data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
  74. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +1 -0
  75. data/lib/datadog/tracing/contrib/excon/ext.rb +1 -0
  76. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +7 -0
  77. data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
  78. data/lib/datadog/tracing/contrib/faraday/middleware.rb +1 -1
  79. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +1 -0
  80. data/lib/datadog/tracing/contrib/grape/ext.rb +1 -0
  81. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +1 -0
  82. data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
  83. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +1 -0
  84. data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
  85. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +1 -0
  86. data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +2 -2
  87. data/lib/datadog/tracing/contrib/http/ext.rb +1 -0
  88. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +1 -0
  89. data/lib/datadog/tracing/contrib/httpclient/ext.rb +1 -0
  90. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +1 -0
  91. data/lib/datadog/tracing/contrib/httprb/ext.rb +1 -0
  92. data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +1 -0
  93. data/lib/datadog/tracing/contrib/kafka/ext.rb +1 -0
  94. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +1 -0
  95. data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
  96. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
  97. data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -0
  98. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +2 -1
  99. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +1 -0
  100. data/lib/datadog/tracing/contrib/opensearch/ext.rb +1 -0
  101. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +1 -0
  102. data/lib/datadog/tracing/contrib/pg/ext.rb +1 -0
  103. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +1 -0
  104. data/lib/datadog/tracing/contrib/presto/ext.rb +1 -0
  105. data/lib/datadog/tracing/contrib/qless/configuration/settings.rb +1 -0
  106. data/lib/datadog/tracing/contrib/qless/ext.rb +1 -0
  107. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +1 -0
  108. data/lib/datadog/tracing/contrib/que/ext.rb +1 -0
  109. data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +1 -0
  110. data/lib/datadog/tracing/contrib/racecar/ext.rb +1 -0
  111. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +1 -0
  112. data/lib/datadog/tracing/contrib/rack/ext.rb +1 -0
  113. data/lib/datadog/tracing/contrib/rack/middlewares.rb +9 -2
  114. data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +0 -2
  115. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +1 -0
  116. data/lib/datadog/tracing/contrib/rails/ext.rb +1 -0
  117. data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +1 -0
  118. data/lib/datadog/tracing/contrib/rake/ext.rb +1 -0
  119. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +1 -0
  120. data/lib/datadog/tracing/contrib/redis/ext.rb +1 -0
  121. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +2 -2
  122. data/lib/datadog/tracing/contrib/redis/patcher.rb +34 -21
  123. data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +1 -0
  124. data/lib/datadog/tracing/contrib/resque/ext.rb +1 -0
  125. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +1 -0
  126. data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
  127. data/lib/datadog/tracing/contrib/roda/configuration/settings.rb +1 -0
  128. data/lib/datadog/tracing/contrib/roda/ext.rb +1 -0
  129. data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +1 -0
  130. data/lib/datadog/tracing/contrib/sequel/ext.rb +1 -0
  131. data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +1 -0
  132. data/lib/datadog/tracing/contrib/shoryuken/ext.rb +1 -0
  133. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +1 -0
  134. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
  135. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +1 -0
  136. data/lib/datadog/tracing/contrib/sinatra/ext.rb +1 -0
  137. data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +1 -0
  138. data/lib/datadog/tracing/contrib/sneakers/ext.rb +1 -0
  139. data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +1 -0
  140. data/lib/datadog/tracing/contrib/stripe/ext.rb +1 -0
  141. data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +1 -0
  142. data/lib/datadog/tracing/contrib/sucker_punch/ext.rb +1 -0
  143. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +58 -0
  144. data/lib/datadog/tracing/contrib/trilogy/ext.rb +27 -0
  145. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +94 -0
  146. data/lib/datadog/tracing/contrib/trilogy/integration.rb +43 -0
  147. data/lib/datadog/tracing/contrib/trilogy/patcher.rb +31 -0
  148. data/lib/datadog/tracing/contrib.rb +1 -0
  149. data/lib/datadog/tracing.rb +8 -2
  150. data/lib/ddtrace/version.rb +1 -1
  151. metadata +20 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69e775ab06a83ce14114a7287056e3d3fb575191b7ff6ccdc5c7b33f7fd58172
4
- data.tar.gz: 13b607a4e29e516be4988dca7827eca09b79e968cf98e0641a155039d2ec3273
3
+ metadata.gz: 1baa733b2ad3335ab214de620e849f1eab598effb15d24f336aa20147e6e34be
4
+ data.tar.gz: 684bbf501475346f6e2a32b5a9284d59e193344e23d63b912624de77ac8bbbad
5
5
  SHA512:
6
- metadata.gz: d345e07c8b0a654974c51a7457b3fc6d3d7eb99226cfc5555d6bc8ee3e65b17b3782b1e582591be925297c09dd104108007b2081e28ee43c103f8f2fec3ffe5b
7
- data.tar.gz: '085ea801f5fae16ed58cd79bab86839cd1aa23fa09261b39219264f603455633e19dbb8f60d647e407c7218d7d00052ef7b593405ec5af828af56ffecd58227d'
6
+ metadata.gz: a10dcafb4ff9e9655e06e149ae83a872a0b614f44d69db15ed120f6aa6990f858bb94897d7b8fe30c1b41fe74dc71f054bdf7f0387ded0c0b66058d0d9a15492
7
+ data.tar.gz: 87be0dc85c2fc044a0f95096d147dbc79dda101babad85bfdfd29a37c42cd9cb70180fdedae00ff95321fe6eec0e4d61a6aaab852c9c6d3f10fc39ff7f06d87c
data/CHANGELOG.md CHANGED
@@ -2,6 +2,54 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.20.0] - 2024-02-05
6
+
7
+ ### Added
8
+
9
+ * Tracing: Add `Trilogy` instrumentation ([#3274][])
10
+ * Rack: Add remote configuration boot tags ([#3315][])
11
+ * Faraday: Add `on_error` option ([#3431][])
12
+ * Profiling: Add dynamic allocation sampling ([#3395][])
13
+
14
+ ### Changed
15
+
16
+ * Bump `datadog-ci` dependency to 0.7.0 ([#3408][])
17
+ * Improve performance of gathering ClassCount metric ([#3386][])
18
+
19
+ ### Fixed
20
+
21
+ * OpenTelemetry: Fix internal loading ([#3400][])
22
+ * Core: Fix logger deadlock ([#3426][])
23
+ * Rack: Fix missing active trace ([#3420][])
24
+ * Redis: Fix instance configuration ([#3278][])
25
+
26
+ ## [1.19.0] - 2024-01-10
27
+
28
+ ### Highlights
29
+ Alpha support for memory profiling has been added. For more details, check the [release notes](https://github.com/DataDog/dd-trace-rb/releases/tag/v1.19.0)
30
+
31
+ ### Added
32
+ * Tracing: Add `on_error` settings for `mysql2` ([#3316][])
33
+ * Core: Add install_signature to app-started telemetry event ([#3349][])
34
+ * Profiling: Heap Profiling ([#3281][]) ([#3287][]) ([#3328][]) ([#3329][]) ([#3333][]) ([#3360][])
35
+ * Profiling: Redesign GC profiling to add timeline support and reduce overhead ([#3313][])
36
+ * Core: Use Ruby 3.3 stable for CI testing ([#3354][])
37
+
38
+ ### Changed
39
+ * Core: Bump `datadog-ci` dependency to 0.6.0 ([#3361][])
40
+ * Core: Bump debase-ruby_core_source dependency to 3.3.1 ([#3373][])
41
+ * Docs: Backport "List Ruby 3.3 as supported in the docs" to master branch ([#3374][])
42
+ * Profiling: Import upstream `rb_profile_frames` fix ([#3352][])
43
+ * Profiling: Allow the dynamic sampling rate overhead target to be set ([#3310][])
44
+ * Profiling: Split profiling tests into ractor and non-ractor suites. ([#3320][])
45
+
46
+ ### Fixed
47
+ * Docs: Fix `pg` doc markdown format ([#3317][])
48
+ * Tracing: Fix recursive `require` in Railtie ([#3365][])
49
+ * Profiling: Fix issues stemming from rb_gc_force_recycle ([#3366][])
50
+ * Profiling: Fix Ruby 3.3 CI being broken in master due to profiler ([#3356][])
51
+ * Profiling: Fix "no signals" workaround detection when mariadb is in use ([#3362][])
52
+
5
53
  ## [1.18.0] - 2023-12-07
6
54
 
7
55
  ### Added
@@ -2680,7 +2728,9 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
2680
2728
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
2681
2729
 
2682
2730
 
2683
- [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v1.18.0...master
2731
+ [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v1.20.0...master
2732
+ [1.20.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.19.0...v1.20.0
2733
+ [1.19.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.18.0...v1.19.0
2684
2734
  [1.18.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.17.0...v1.18.0
2685
2735
  [1.17.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.16.2...v1.17.0
2686
2736
  [1.16.2]: https://github.com/DataDog/dd-trace-rb/compare/v1.16.1...v1.16.2
@@ -3908,14 +3958,45 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3908
3958
  [#3270]: https://github.com/DataDog/dd-trace-rb/issues/3270
3909
3959
  [#3271]: https://github.com/DataDog/dd-trace-rb/issues/3271
3910
3960
  [#3273]: https://github.com/DataDog/dd-trace-rb/issues/3273
3961
+ [#3274]: https://github.com/DataDog/dd-trace-rb/issues/3274
3962
+ [#3278]: https://github.com/DataDog/dd-trace-rb/issues/3278
3911
3963
  [#3279]: https://github.com/DataDog/dd-trace-rb/issues/3279
3912
3964
  [#3280]: https://github.com/DataDog/dd-trace-rb/issues/3280
3965
+ [#3281]: https://github.com/DataDog/dd-trace-rb/issues/3281
3913
3966
  [#3284]: https://github.com/DataDog/dd-trace-rb/issues/3284
3914
3967
  [#3286]: https://github.com/DataDog/dd-trace-rb/issues/3286
3968
+ [#3287]: https://github.com/DataDog/dd-trace-rb/issues/3287
3915
3969
  [#3289]: https://github.com/DataDog/dd-trace-rb/issues/3289
3916
3970
  [#3303]: https://github.com/DataDog/dd-trace-rb/issues/3303
3917
3971
  [#3307]: https://github.com/DataDog/dd-trace-rb/issues/3307
3918
3972
  [#3308]: https://github.com/DataDog/dd-trace-rb/issues/3308
3973
+ [#3310]: https://github.com/DataDog/dd-trace-rb/issues/3310
3974
+ [#3313]: https://github.com/DataDog/dd-trace-rb/issues/3313
3975
+ [#3315]: https://github.com/DataDog/dd-trace-rb/issues/3315
3976
+ [#3316]: https://github.com/DataDog/dd-trace-rb/issues/3316
3977
+ [#3317]: https://github.com/DataDog/dd-trace-rb/issues/3317
3978
+ [#3320]: https://github.com/DataDog/dd-trace-rb/issues/3320
3979
+ [#3328]: https://github.com/DataDog/dd-trace-rb/issues/3328
3980
+ [#3329]: https://github.com/DataDog/dd-trace-rb/issues/3329
3981
+ [#3333]: https://github.com/DataDog/dd-trace-rb/issues/3333
3982
+ [#3349]: https://github.com/DataDog/dd-trace-rb/issues/3349
3983
+ [#3352]: https://github.com/DataDog/dd-trace-rb/issues/3352
3984
+ [#3354]: https://github.com/DataDog/dd-trace-rb/issues/3354
3985
+ [#3356]: https://github.com/DataDog/dd-trace-rb/issues/3356
3986
+ [#3360]: https://github.com/DataDog/dd-trace-rb/issues/3360
3987
+ [#3361]: https://github.com/DataDog/dd-trace-rb/issues/3361
3988
+ [#3362]: https://github.com/DataDog/dd-trace-rb/issues/3362
3989
+ [#3365]: https://github.com/DataDog/dd-trace-rb/issues/3365
3990
+ [#3366]: https://github.com/DataDog/dd-trace-rb/issues/3366
3991
+ [#3373]: https://github.com/DataDog/dd-trace-rb/issues/3373
3992
+ [#3374]: https://github.com/DataDog/dd-trace-rb/issues/3374
3993
+ [#3386]: https://github.com/DataDog/dd-trace-rb/issues/3386
3994
+ [#3395]: https://github.com/DataDog/dd-trace-rb/issues/3395
3995
+ [#3400]: https://github.com/DataDog/dd-trace-rb/issues/3400
3996
+ [#3408]: https://github.com/DataDog/dd-trace-rb/issues/3408
3997
+ [#3420]: https://github.com/DataDog/dd-trace-rb/issues/3420
3998
+ [#3426]: https://github.com/DataDog/dd-trace-rb/issues/3426
3999
+ [#3431]: https://github.com/DataDog/dd-trace-rb/issues/3431
3919
4000
  [@AdrianLC]: https://github.com/AdrianLC
3920
4001
  [@Azure7111]: https://github.com/Azure7111
3921
4002
  [@BabyGroot]: https://github.com/BabyGroot
@@ -12,10 +12,14 @@
12
12
  #include "collectors_thread_context.h"
13
13
  #include "collectors_dynamic_sampling_rate.h"
14
14
  #include "collectors_idle_sampling_helper.h"
15
+ #include "collectors_discrete_dynamic_sampler.h"
15
16
  #include "private_vm_api_access.h"
16
17
  #include "setup_signal_handler.h"
17
18
  #include "time_helpers.h"
18
19
 
20
+ // Maximum allowed value for an allocation weight. Attempts to use higher values will result in clamping.
21
+ unsigned int MAX_ALLOC_WEIGHT = 65535;
22
+
19
23
  // Used to trigger the execution of Collectors::ThreadState, which implements all of the sampling logic
20
24
  // itself; this class only implements the "when to do it" part.
21
25
  //
@@ -75,20 +79,27 @@
75
79
  //
76
80
  // ---
77
81
 
82
+ #ifndef NO_POSTPONED_TRIGGER
83
+ // Used to call the rb_postponed_job_trigger from Ruby 3.3+. These get initialized in
84
+ // `collectors_cpu_and_wall_time_worker_init` below and always get reused after that.
85
+ static rb_postponed_job_handle_t sample_from_postponed_job_handle;
86
+ static rb_postponed_job_handle_t after_gc_from_postponed_job_handle;
87
+ #endif
88
+
78
89
  // Contains state for a single CpuAndWallTimeWorker instance
79
90
  struct cpu_and_wall_time_worker_state {
80
91
  // These are immutable after initialization
81
92
 
82
93
  bool gc_profiling_enabled;
83
- bool allocation_counting_enabled;
84
94
  bool no_signals_workaround_enabled;
85
95
  bool dynamic_sampling_rate_enabled;
86
- int allocation_sample_every; // Temporarily used for development/testing of allocation profiling
96
+ bool allocation_profiling_enabled;
87
97
  VALUE self_instance;
88
98
  VALUE thread_context_collector_instance;
89
99
  VALUE idle_sampling_helper_instance;
90
100
  VALUE owner_thread;
91
- dynamic_sampling_rate_state dynamic_sampling_rate;
101
+ dynamic_sampling_rate_state cpu_dynamic_sampling_rate;
102
+ discrete_dynamic_sampler allocation_sampler;
92
103
  VALUE gc_tracepoint; // Used to get gc start/finish information
93
104
  VALUE object_allocation_tracepoint; // Used to get allocation counts and allocation profiling
94
105
 
@@ -149,10 +160,10 @@ static VALUE _native_initialize(
149
160
  VALUE thread_context_collector_instance,
150
161
  VALUE gc_profiling_enabled,
151
162
  VALUE idle_sampling_helper_instance,
152
- VALUE allocation_counting_enabled,
153
163
  VALUE no_signals_workaround_enabled,
154
164
  VALUE dynamic_sampling_rate_enabled,
155
- VALUE allocation_sample_every
165
+ VALUE dynamic_sampling_rate_overhead_target_percentage,
166
+ VALUE allocation_profiling_enabled
156
167
  );
157
168
  static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr);
158
169
  static VALUE _native_sampling_loop(VALUE self, VALUE instance);
@@ -211,6 +222,16 @@ __thread uint64_t allocation_count = 0;
211
222
  void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
212
223
  rb_global_variable(&active_sampler_instance);
213
224
 
225
+ #ifndef NO_POSTPONED_TRIGGER
226
+ int unused_flags = 0;
227
+ sample_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, sample_from_postponed_job, NULL);
228
+ after_gc_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gc_from_postponed_job, NULL);
229
+
230
+ if (sample_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID || after_gc_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID) {
231
+ rb_raise(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
232
+ }
233
+ #endif
234
+
214
235
  VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
215
236
  VALUE collectors_cpu_and_wall_time_worker_class = rb_define_class_under(collectors_module, "CpuAndWallTimeWorker", rb_cObject);
216
237
  // Hosts methods used for testing the native code using RSpec
@@ -264,14 +285,14 @@ static VALUE _native_new(VALUE klass) {
264
285
  // being leaked.
265
286
 
266
287
  state->gc_profiling_enabled = false;
267
- state->allocation_counting_enabled = false;
268
288
  state->no_signals_workaround_enabled = false;
269
289
  state->dynamic_sampling_rate_enabled = true;
270
- state->allocation_sample_every = 0;
290
+ state->allocation_profiling_enabled = false;
271
291
  state->thread_context_collector_instance = Qnil;
272
292
  state->idle_sampling_helper_instance = Qnil;
273
293
  state->owner_thread = Qnil;
274
- dynamic_sampling_rate_init(&state->dynamic_sampling_rate);
294
+ dynamic_sampling_rate_init(&state->cpu_dynamic_sampling_rate);
295
+ discrete_dynamic_sampler_init(&state->allocation_sampler, "allocation");
275
296
  state->gc_tracepoint = Qnil;
276
297
  state->object_allocation_tracepoint = Qnil;
277
298
 
@@ -292,28 +313,33 @@ static VALUE _native_initialize(
292
313
  VALUE thread_context_collector_instance,
293
314
  VALUE gc_profiling_enabled,
294
315
  VALUE idle_sampling_helper_instance,
295
- VALUE allocation_counting_enabled,
296
316
  VALUE no_signals_workaround_enabled,
297
317
  VALUE dynamic_sampling_rate_enabled,
298
- VALUE allocation_sample_every
318
+ VALUE dynamic_sampling_rate_overhead_target_percentage,
319
+ VALUE allocation_profiling_enabled
299
320
  ) {
300
321
  ENFORCE_BOOLEAN(gc_profiling_enabled);
301
- ENFORCE_BOOLEAN(allocation_counting_enabled);
302
322
  ENFORCE_BOOLEAN(no_signals_workaround_enabled);
303
323
  ENFORCE_BOOLEAN(dynamic_sampling_rate_enabled);
304
- ENFORCE_TYPE(allocation_sample_every, T_FIXNUM);
324
+ ENFORCE_TYPE(dynamic_sampling_rate_overhead_target_percentage, T_FLOAT);
325
+ ENFORCE_BOOLEAN(allocation_profiling_enabled);
305
326
 
306
327
  struct cpu_and_wall_time_worker_state *state;
307
328
  TypedData_Get_Struct(self_instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
308
329
 
309
330
  state->gc_profiling_enabled = (gc_profiling_enabled == Qtrue);
310
- state->allocation_counting_enabled = (allocation_counting_enabled == Qtrue);
311
331
  state->no_signals_workaround_enabled = (no_signals_workaround_enabled == Qtrue);
312
332
  state->dynamic_sampling_rate_enabled = (dynamic_sampling_rate_enabled == Qtrue);
313
- state->allocation_sample_every = NUM2INT(allocation_sample_every);
333
+ state->allocation_profiling_enabled = (allocation_profiling_enabled == Qtrue);
314
334
 
315
- if (state->allocation_sample_every < 0) {
316
- rb_raise(rb_eArgError, "Unexpected value for allocation_sample_every: %d. This value must be >= 0.", state->allocation_sample_every);
335
+ double total_overhead_target_percentage = NUM2DBL(dynamic_sampling_rate_overhead_target_percentage);
336
+ if (!state->allocation_profiling_enabled) {
337
+ dynamic_sampling_rate_set_overhead_target_percentage(&state->cpu_dynamic_sampling_rate, total_overhead_target_percentage);
338
+ } else {
339
+ // TODO: May be nice to offer customization here? Distribute available "overhead" margin with a bias towards one or the other
340
+ // sampler.
341
+ dynamic_sampling_rate_set_overhead_target_percentage(&state->cpu_dynamic_sampling_rate, total_overhead_target_percentage / 2);
342
+ discrete_dynamic_sampler_set_overhead_target_percentage(&state->allocation_sampler, total_overhead_target_percentage / 2);
317
343
  }
318
344
 
319
345
  state->thread_context_collector_instance = enforce_thread_context_collector_instance(thread_context_collector_instance);
@@ -366,7 +392,8 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
366
392
  if (state->stop_thread == rb_thread_current()) return Qnil;
367
393
 
368
394
  // Reset the dynamic sampling rate state, if any (reminder: the monotonic clock reference may change after a fork)
369
- dynamic_sampling_rate_reset(&state->dynamic_sampling_rate);
395
+ dynamic_sampling_rate_reset(&state->cpu_dynamic_sampling_rate);
396
+ discrete_dynamic_sampler_reset(&state->allocation_sampler);
370
397
 
371
398
  // This write to a global is thread-safe BECAUSE we're still holding on to the global VM lock at this point
372
399
  active_sampler_instance_state = state;
@@ -472,20 +499,25 @@ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED si
472
499
 
473
500
  // Note: If we ever want to get rid of rb_postponed_job_register_one, remember not to clobber Ruby exceptions, as
474
501
  // this function does this helpful job for us now -- https://github.com/ruby/ruby/commit/a98e343d39c4d7bf1e2190b076720f32d9f298b3.
475
- int result = rb_postponed_job_register_one(0, sample_from_postponed_job, NULL);
476
-
477
- // Officially, the result of rb_postponed_job_register_one is documented as being opaque, but in practice it does not
478
- // seem to have changed between Ruby 2.3 and 3.2, and so we track it as a debugging mechanism
479
- switch (result) {
480
- case 0:
481
- state->stats.postponed_job_full++; break;
482
- case 1:
483
- state->stats.postponed_job_success++; break;
484
- case 2:
485
- state->stats.postponed_job_skipped_already_existed++; break;
486
- default:
487
- state->stats.postponed_job_unknown_result++;
488
- }
502
+ #ifndef NO_POSTPONED_TRIGGER // Ruby 3.3+
503
+ rb_postponed_job_trigger(sample_from_postponed_job_handle);
504
+ state->stats.postponed_job_success++; // Always succeeds
505
+ #else
506
+ int result = rb_postponed_job_register_one(0, sample_from_postponed_job, NULL);
507
+
508
+ // Officially, the result of rb_postponed_job_register_one is documented as being opaque, but in practice it does not
509
+ // seem to have changed between Ruby 2.3 and 3.2, and so we track it as a debugging mechanism
510
+ switch (result) {
511
+ case 0:
512
+ state->stats.postponed_job_full++; break;
513
+ case 1:
514
+ state->stats.postponed_job_success++; break;
515
+ case 2:
516
+ state->stats.postponed_job_skipped_already_existed++; break;
517
+ default:
518
+ state->stats.postponed_job_unknown_result++;
519
+ }
520
+ #endif
489
521
  }
490
522
 
491
523
  // The actual sampling trigger loop always runs **without** the global vm lock.
@@ -534,7 +566,7 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
534
566
  // Note that we deliberately should NOT combine this sleep_for with the one above because the result of
535
567
  // `dynamic_sampling_rate_get_sleep` may have changed while the above sleep was ongoing.
536
568
  uint64_t extra_sleep =
537
- dynamic_sampling_rate_get_sleep(&state->dynamic_sampling_rate, monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE));
569
+ dynamic_sampling_rate_get_sleep(&state->cpu_dynamic_sampling_rate, monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE));
538
570
  if (state->dynamic_sampling_rate_enabled && extra_sleep > 0) sleep_for(extra_sleep);
539
571
  }
540
572
 
@@ -574,7 +606,7 @@ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
574
606
 
575
607
  long wall_time_ns_before_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
576
608
 
577
- if (state->dynamic_sampling_rate_enabled && !dynamic_sampling_rate_should_sample(&state->dynamic_sampling_rate, wall_time_ns_before_sample)) {
609
+ if (state->dynamic_sampling_rate_enabled && !dynamic_sampling_rate_should_sample(&state->cpu_dynamic_sampling_rate, wall_time_ns_before_sample)) {
578
610
  state->stats.skipped_sample_because_of_dynamic_sampling_rate++;
579
611
  return Qnil;
580
612
  }
@@ -594,7 +626,7 @@ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
594
626
  state->stats.sampling_time_ns_max = uint64_max_of(sampling_time_ns, state->stats.sampling_time_ns_max);
595
627
  state->stats.sampling_time_ns_total += sampling_time_ns;
596
628
 
597
- dynamic_sampling_rate_after_sample(&state->dynamic_sampling_rate, wall_time_ns_after_sample, sampling_time_ns);
629
+ dynamic_sampling_rate_after_sample(&state->cpu_dynamic_sampling_rate, wall_time_ns_after_sample, sampling_time_ns);
598
630
 
599
631
  // Return a dummy VALUE because we're called from rb_rescue2 which requires it
600
632
  return Qnil;
@@ -632,7 +664,7 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
632
664
  // because they may raise exceptions.
633
665
  install_sigprof_signal_handler(handle_sampling_signal, "handle_sampling_signal");
634
666
  if (state->gc_profiling_enabled) rb_tracepoint_enable(state->gc_tracepoint);
635
- if (state->allocation_counting_enabled) rb_tracepoint_enable(state->object_allocation_tracepoint);
667
+ if (state->allocation_profiling_enabled) rb_tracepoint_enable(state->object_allocation_tracepoint);
636
668
 
637
669
  rb_thread_call_without_gvl(run_sampling_trigger_loop, state, interrupt_sampling_trigger_loop, state);
638
670
 
@@ -714,28 +746,17 @@ static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused) {
714
746
  if (event == RUBY_INTERNAL_EVENT_GC_ENTER) {
715
747
  thread_context_collector_on_gc_start(state->thread_context_collector_instance);
716
748
  } else if (event == RUBY_INTERNAL_EVENT_GC_EXIT) {
717
- // Design: In an earlier iteration of this feature (see https://github.com/DataDog/dd-trace-rb/pull/2308) we
718
- // actually had a single method to implement the behavior of both thread_context_collector_on_gc_finish
719
- // and thread_context_collector_sample_after_gc (the latter is called via after_gc_from_postponed_job).
720
- //
721
- // Unfortunately, then we discovered the safety issue around no allocations, and thus decided to separate them -- so that
722
- // the sampling could run outside the tight safety constraints of the garbage collection process.
723
- //
724
- // There is a downside: The sample is now taken very very shortly afterwards the GC finishes, and not immediately
725
- // as the GC finishes, which means the stack captured may by affected by "skid", e.g. point slightly after where
726
- // it should be pointing at.
727
- // Alternatives to solve this would be to capture no stack for garbage collection (as we do for Java and .net);
728
- // making the sampling process allocation-safe (very hard); or separate stack sampling from sample recording,
729
- // e.g. enabling us to capture the stack in thread_context_collector_on_gc_finish and do the rest later
730
- // (medium hard).
731
-
732
- thread_context_collector_on_gc_finish(state->thread_context_collector_instance);
733
- // We use rb_postponed_job_register_one to ask Ruby to run thread_context_collector_sample_after_gc after if
734
- // fully finishes the garbage collection, so that one is allowed to do allocations and throw exceptions as usual.
735
- //
736
- // Note: If we ever want to get rid of rb_postponed_job_register_one, remember not to clobber Ruby exceptions, as
737
- // this function does this helpful job for us now -- https://github.com/ruby/ruby/commit/a98e343d39c4d7bf1e2190b076720f32d9f298b3.
738
- rb_postponed_job_register_one(0, after_gc_from_postponed_job, NULL);
749
+ bool should_flush = thread_context_collector_on_gc_finish(state->thread_context_collector_instance);
750
+
751
+ // We use rb_postponed_job_register_one to ask Ruby to run thread_context_collector_sample_after_gc when the
752
+ // thread collector flags it's time to flush.
753
+ if (should_flush) {
754
+ #ifndef NO_POSTPONED_TRIGGER // Ruby 3.3+
755
+ rb_postponed_job_trigger(after_gc_from_postponed_job_handle);
756
+ #else
757
+ rb_postponed_job_register_one(0, after_gc_from_postponed_job, NULL);
758
+ #endif
759
+ }
739
760
  }
740
761
  }
741
762
 
@@ -888,9 +909,9 @@ static void sleep_for(uint64_t time_ns) {
888
909
  }
889
910
 
890
911
  static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
891
- bool is_profiler_running = active_sampler_instance_state != NULL;
912
+ bool are_allocations_being_tracked = active_sampler_instance_state != NULL && active_sampler_instance_state->allocation_profiling_enabled;
892
913
 
893
- return is_profiler_running ? ULL2NUM(allocation_count) : Qnil;
914
+ return are_allocations_being_tracked ? ULL2NUM(allocation_count) : Qnil;
894
915
  }
895
916
 
896
917
  // Implements memory-related profiling events. This function is called by Ruby via the `object_allocation_tracepoint`
@@ -916,18 +937,20 @@ static void on_newobj_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused)
916
937
  return;
917
938
  }
918
939
 
940
+ if (state->dynamic_sampling_rate_enabled && !discrete_dynamic_sampler_should_sample(&state->allocation_sampler)) {
941
+ return;
942
+ }
943
+
919
944
  // @ivoanjo: Strictly speaking, this is not needed because Ruby should not call the same tracepoint while a previous
920
945
  // invocation is still pending, (e.g. it wouldn't call `on_newobj_event` while it's already running), but I decided
921
946
  // to keep this here for consistency -- every call to the thread context (other than the special gc calls which are
922
947
  // defined as not being able to allocate) sets this.
923
948
  state->during_sample = true;
924
949
 
925
- // TODO: This is a placeholder sampling decision strategy. We plan to replace it with a better one soon (e.g. before
926
- // beta), and having something here allows us to test the rest of feature, sampling decision aside.
927
- if (state->allocation_sample_every > 0 && ((allocation_count % state->allocation_sample_every) == 0)) {
928
- // Rescue against any exceptions that happen during sampling
929
- safely_call(rescued_sample_allocation, tracepoint_data, state->self_instance);
930
- }
950
+ // Rescue against any exceptions that happen during sampling
951
+ safely_call(rescued_sample_allocation, tracepoint_data, state->self_instance);
952
+
953
+ discrete_dynamic_sampler_after_sample(&state->allocation_sampler);
931
954
 
932
955
  state->during_sample = false;
933
956
  }
@@ -959,7 +982,14 @@ static VALUE rescued_sample_allocation(VALUE tracepoint_data) {
959
982
  rb_trace_arg_t *data = rb_tracearg_from_tracepoint(tracepoint_data);
960
983
  VALUE new_object = rb_tracearg_object(data);
961
984
 
962
- thread_context_collector_sample_allocation(state->thread_context_collector_instance, state->allocation_sample_every, new_object);
985
+ unsigned long allocations_since_last_sample = state->dynamic_sampling_rate_enabled ?
986
+ // if we're doing dynamic sampling, ask the sampler how many events since last sample
987
+ discrete_dynamic_sampler_events_since_last_sample(&state->allocation_sampler) :
988
+ // if we aren't, then we're sampling every event
989
+ 1;
990
+ // TODO: Signal in the profile that clamping happened?
991
+ unsigned int weight = allocations_since_last_sample > MAX_ALLOC_WEIGHT ? MAX_ALLOC_WEIGHT : (unsigned int) allocations_since_last_sample;
992
+ thread_context_collector_sample_allocation(state->thread_context_collector_instance, weight, new_object);
963
993
 
964
994
  // Return a dummy VALUE because we're called from rb_rescue2 which requires it
965
995
  return Qnil;