ddtrace 1.18.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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;