datadog 2.1.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +101 -1
  3. data/ext/datadog_profiling_loader/extconf.rb +15 -15
  4. data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
  5. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
  6. data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
  7. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +132 -44
  8. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +49 -26
  9. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
  10. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
  11. data/ext/datadog_profiling_native_extension/collectors_stack.c +90 -37
  12. data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
  13. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +81 -19
  14. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
  15. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +110 -0
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +57 -0
  17. data/ext/datadog_profiling_native_extension/extconf.rb +69 -62
  18. data/ext/datadog_profiling_native_extension/heap_recorder.c +34 -6
  19. data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
  20. data/ext/datadog_profiling_native_extension/helpers.h +6 -17
  21. data/ext/datadog_profiling_native_extension/http_transport.c +3 -3
  22. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
  23. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
  24. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -126
  25. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +64 -138
  26. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +17 -11
  27. data/ext/datadog_profiling_native_extension/profiling.c +0 -2
  28. data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
  29. data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
  30. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +1 -1
  31. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
  32. data/ext/datadog_profiling_native_extension/stack_recorder.c +27 -8
  33. data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -0
  34. data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
  35. data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
  36. data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +20 -7
  37. data/ext/libdatadog_api/datadog_ruby_common.c +110 -0
  38. data/ext/libdatadog_api/datadog_ruby_common.h +57 -0
  39. data/ext/libdatadog_api/extconf.rb +108 -0
  40. data/ext/libdatadog_api/macos_development.md +26 -0
  41. data/ext/libdatadog_extconf_helpers.rb +130 -0
  42. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +49 -0
  43. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +73 -0
  44. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +68 -0
  45. data/lib/datadog/appsec/contrib/graphql/integration.rb +41 -0
  46. data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
  47. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
  48. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  49. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +1 -1
  50. data/lib/datadog/appsec/extensions.rb +1 -0
  51. data/lib/datadog/appsec/processor/actions.rb +1 -1
  52. data/lib/datadog/appsec/response.rb +15 -1
  53. data/lib/datadog/appsec.rb +1 -0
  54. data/lib/datadog/core/configuration/components.rb +17 -12
  55. data/lib/datadog/core/configuration/settings.rb +93 -7
  56. data/lib/datadog/core/configuration.rb +3 -17
  57. data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
  58. data/lib/datadog/core/crashtracking/component.rb +111 -0
  59. data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
  60. data/lib/datadog/core/deprecations.rb +58 -0
  61. data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
  62. data/lib/datadog/core/environment/yjit.rb +5 -0
  63. data/lib/datadog/core/runtime/ext.rb +1 -0
  64. data/lib/datadog/core/runtime/metrics.rb +6 -0
  65. data/lib/datadog/core/telemetry/component.rb +154 -0
  66. data/lib/datadog/core/telemetry/emitter.rb +9 -11
  67. data/lib/datadog/core/telemetry/event.rb +132 -26
  68. data/lib/datadog/core/telemetry/ext.rb +3 -0
  69. data/lib/datadog/core/telemetry/http/adapters/net.rb +11 -13
  70. data/lib/datadog/core/telemetry/http/ext.rb +3 -0
  71. data/lib/datadog/core/telemetry/http/transport.rb +38 -9
  72. data/lib/datadog/core/telemetry/logging.rb +35 -0
  73. data/lib/datadog/core/telemetry/metric.rb +167 -0
  74. data/lib/datadog/core/telemetry/metrics_collection.rb +81 -0
  75. data/lib/datadog/core/telemetry/metrics_manager.rb +81 -0
  76. data/lib/datadog/core/telemetry/request.rb +1 -1
  77. data/lib/datadog/core/telemetry/worker.rb +173 -0
  78. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
  79. data/lib/datadog/core/utils/only_once_successful.rb +76 -0
  80. data/lib/datadog/core.rb +2 -19
  81. data/lib/datadog/kit/appsec/events.rb +2 -4
  82. data/lib/datadog/opentelemetry/sdk/propagator.rb +5 -10
  83. data/lib/datadog/opentelemetry/sdk/span_processor.rb +15 -2
  84. data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
  85. data/lib/datadog/profiling/collectors/code_provenance.rb +24 -11
  86. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +17 -17
  87. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
  88. data/lib/datadog/profiling/collectors/info.rb +3 -3
  89. data/lib/datadog/profiling/collectors/thread_context.rb +4 -2
  90. data/lib/datadog/profiling/component.rb +85 -90
  91. data/lib/datadog/profiling/exporter.rb +3 -3
  92. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +410 -0
  93. data/lib/datadog/profiling/ext.rb +21 -21
  94. data/lib/datadog/profiling/flush.rb +1 -1
  95. data/lib/datadog/profiling/http_transport.rb +8 -6
  96. data/lib/datadog/profiling/load_native_extension.rb +5 -5
  97. data/lib/datadog/profiling/preload.rb +1 -1
  98. data/lib/datadog/profiling/profiler.rb +5 -8
  99. data/lib/datadog/profiling/scheduler.rb +31 -25
  100. data/lib/datadog/profiling/tag_builder.rb +2 -2
  101. data/lib/datadog/profiling/tasks/exec.rb +5 -5
  102. data/lib/datadog/profiling/tasks/setup.rb +16 -35
  103. data/lib/datadog/profiling.rb +5 -5
  104. data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
  105. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +1 -1
  106. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +1 -1
  107. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +1 -1
  108. data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -6
  109. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +9 -4
  110. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +3 -2
  111. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +1 -5
  112. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  113. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  114. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  115. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  116. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  117. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  118. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  119. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  120. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  121. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  122. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +2 -1
  123. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +32 -0
  124. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +156 -0
  125. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +34 -0
  126. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +45 -41
  127. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +17 -40
  128. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -1
  129. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +29 -6
  130. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +16 -4
  131. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +33 -29
  132. data/lib/datadog/tracing/contrib/analytics.rb +5 -0
  133. data/lib/datadog/tracing/contrib/ext.rb +14 -0
  134. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +5 -0
  135. data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -2
  136. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +166 -0
  137. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +28 -0
  138. data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -1
  139. data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +1 -1
  140. data/lib/datadog/tracing/contrib/kafka/event.rb +1 -1
  141. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +3 -3
  142. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +3 -3
  143. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +3 -3
  144. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +3 -3
  145. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +3 -3
  146. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +3 -3
  147. data/lib/datadog/tracing/contrib/lograge/patcher.rb +16 -0
  148. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
  149. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
  150. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
  151. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
  152. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
  153. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
  154. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
  155. data/lib/datadog/tracing/contrib/racecar/event.rb +2 -2
  156. data/lib/datadog/tracing/contrib/rails/ext.rb +9 -0
  157. data/lib/datadog/tracing/contrib/rails/patcher.rb +7 -0
  158. data/lib/datadog/tracing/contrib/rails/runner.rb +95 -0
  159. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
  160. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
  161. data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
  162. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  163. data/lib/datadog/tracing/distributed/b3_single.rb +3 -1
  164. data/lib/datadog/tracing/distributed/datadog.rb +2 -2
  165. data/lib/datadog/tracing/distributed/propagation.rb +9 -2
  166. data/lib/datadog/tracing/distributed/trace_context.rb +3 -2
  167. data/lib/datadog/tracing/metadata/errors.rb +9 -1
  168. data/lib/datadog/tracing/metadata/ext.rb +4 -0
  169. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  170. data/lib/datadog/tracing/span.rb +9 -2
  171. data/lib/datadog/tracing/span_event.rb +41 -0
  172. data/lib/datadog/tracing/span_operation.rb +9 -4
  173. data/lib/datadog/tracing/trace_operation.rb +7 -3
  174. data/lib/datadog/tracing/trace_segment.rb +4 -1
  175. data/lib/datadog/tracing/tracer.rb +9 -2
  176. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
  177. data/lib/datadog/tracing.rb +5 -1
  178. data/lib/datadog/version.rb +2 -2
  179. metadata +43 -12
  180. data/lib/datadog/core/telemetry/client.rb +0 -95
  181. data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
  182. data/lib/datadog/profiling/crashtracker.rb +0 -91
  183. data/lib/datadog/profiling/ext/forking.rb +0 -98
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0297728b2d8779a69af39f3dffa86af6b3765e3f2dd19cb685340ca7ad8e2ac
4
- data.tar.gz: 3c7d3506df76790fea0b2e2047d1b2c4919e9d928ccb644086194197125391e7
3
+ metadata.gz: 5d6610c8ef7e86c023f8a3fca884807bd7e9cf6b84fc6cbdd79b98e8a8762c2e
4
+ data.tar.gz: 236fafc4b8e2c809552d97c8eb025654c0ef4279685c741ad9504b2af8265b94
5
5
  SHA512:
6
- metadata.gz: ec2862773c6ee93e5d698d6b586a576413689a84f7d94a621c20088759469e1ad6742e13fc1e7f9250046212cec0e284abf4fb0048e255802c19f378cf7d428c
7
- data.tar.gz: 507fbfd3f8879333c11336646e1a56ca453fbaf92c84fb0bae3b0e38435f3ea1ec6b3ddc756f848c76113a4e5832f8097cd521532541bf9ca8fdff6cca1e5ff3
6
+ metadata.gz: e633db76f69b5d151629cde5e1b7024a6bea43343aa348ea50857d5e74d049d2b6a833be252c6c1c7aee95976805a795e1607a81d42e8c3246126abd9811777d
7
+ data.tar.gz: fb2bcf3803689d8e499f2266e7d063f1c5b9b7d08bb28b5d27f81bd16e7e373c01c1d5224027d43229a5ca609f66c5179aa521ef272f347d069ca73e3613bdc3
data/CHANGELOG.md CHANGED
@@ -2,6 +2,64 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [2.3.0] - 2024-08-22
6
+
7
+ ### Added
8
+
9
+ * Core: Support agentless telemetry ([#3779][])
10
+ * Tracing: Add support for span events ([#3776][])
11
+ * Tracing: Add tags to enable inferred service dependencies for databases ([#3789][])
12
+ * Tracing: Emit log message and instructions for incompatible Lograge setup ([#3812][], [#3839][])
13
+ * Tracing: Add `append_comment` option to append SQL comment propagation for `mysql`, `pg` and `trilogy` ([#3809][])
14
+ * AppSec: Add threat detection and protection for `graphql` ([#3769][], [#3814][])
15
+
16
+ ### Changed
17
+
18
+ * Core: Enable crashtracking by default ([#3826][])
19
+ * Profiling: Reduce allocation overhead ([#3805][], [#3797][])
20
+ * Profiling: Speed up stack sampling ([#3837][])
21
+ * Profiling: Upgrade to libdatadog 11 ([#3799][])
22
+ * Profiling: Disable allocation counting feature by default ([#3798][])
23
+ * Profiling: Reduce the maximum biased result for allocation samples ([#3793][])
24
+ * Tracing: Reduce noisy integration logs ([#3785][])
25
+
26
+ ### Fixed
27
+
28
+ * Tracing: Fix `require` issue for `graphql` ([#3813][])
29
+ * AppSec: Fix an error when parsing http headers with integer value ([#3790][])
30
+ * AppSec: Fix an error when tracking login failure without `user_id` ([#3841][])
31
+ * Fix a syntax error for Ruby < 2.4 during single step instrumentation ([#3795][])
32
+
33
+ ## [2.2.0] - 2024-07-11
34
+
35
+ ### Added
36
+
37
+ * Tracing: Add `Rails` Runner instrumentation ([#2509][])
38
+ * Tracing: Introduce a new, reworked `GraphQL` tracer to comply with span attributes specification ([#3672][])
39
+ * Tracing: Enhance `ActiveSupport::Cache` instrumentation with `ActiveSupport::Notifications` subscription ([#3772][])
40
+ * Profiling: Track unscaled allocation counts in allocation profiler ([#3770][])
41
+
42
+ ### Changed
43
+
44
+ * Core: Send Telemetry events in batches ([#3749][])
45
+ * Tracing: Populate spans from `ActiveSupport::Notifications` as early as possible ([#3725][])
46
+ * Profiling: Upgrade to `libdatadog` 10 ([#3753][])
47
+ * Profiling: Optimize `CodeProvenance#record_loaded_files` to avoid allocations ([#3757][])
48
+
49
+ ### Fixed
50
+
51
+ * Core: Fix Telemetry events blocking main thread ([#3718][])
52
+ * Core: Fix deadlock from Telemetry threads ([#3743][])
53
+ * Tracing: Fix empty log correlation when tracing is disabled ([#3731][])
54
+ * Tracing: Fix HTTP propagation when missing parent span id ([#3730][])
55
+ * Tracing: Ensure `_dd.p.tid` tag with fixed size ([#3729][])
56
+ * OTel: Fix ids encoding/decoding for propagation ([#3709][])
57
+ * Profiling: Workaround Ruby `Dir` returning incorrect results ([#3720][])
58
+ * Profiling: Fix `Phusion Passenger` detection when missing from `Gemfile`/`gems.rb` ([#3750][])
59
+ * Profiling: Fix `rpath` for linking to libdatadog when loading extension ([#3706][])
60
+ * Profiling: Fix incorrect code provenance due to broken JSON monkey patch ([#3695][])
61
+ * Profiling: Fix aggregation of actionview template classes ([#3759][], [#3774][])
62
+
5
63
  ## [2.1.0] - 2024-06-10
6
64
 
7
65
  ### Added
@@ -2904,7 +2962,9 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
2904
2962
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
2905
2963
 
2906
2964
 
2907
- [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.1.0...master
2965
+ [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.3.0...master
2966
+ [2.3.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.2.0...v2.3.0
2967
+ [2.2.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.1.0...v2.2.0
2908
2968
  [2.1.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0...v2.1.0
2909
2969
  [2.0.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0.rc1...v2.0.0
2910
2970
  [2.0.0.rc1]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0.beta2...v2.0.0.rc1
@@ -3901,6 +3961,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3901
3961
  [#2497]: https://github.com/DataDog/dd-trace-rb/issues/2497
3902
3962
  [#2501]: https://github.com/DataDog/dd-trace-rb/issues/2501
3903
3963
  [#2504]: https://github.com/DataDog/dd-trace-rb/issues/2504
3964
+ [#2509]: https://github.com/DataDog/dd-trace-rb/issues/2509
3904
3965
  [#2512]: https://github.com/DataDog/dd-trace-rb/issues/2512
3905
3966
  [#2513]: https://github.com/DataDog/dd-trace-rb/issues/2513
3906
3967
  [#2522]: https://github.com/DataDog/dd-trace-rb/issues/2522
@@ -4283,6 +4344,45 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
4283
4344
  [#3651]: https://github.com/DataDog/dd-trace-rb/issues/3651
4284
4345
  [#3657]: https://github.com/DataDog/dd-trace-rb/issues/3657
4285
4346
  [#3664]: https://github.com/DataDog/dd-trace-rb/issues/3664
4347
+ [#3672]: https://github.com/DataDog/dd-trace-rb/issues/3672
4348
+ [#3695]: https://github.com/DataDog/dd-trace-rb/issues/3695
4349
+ [#3706]: https://github.com/DataDog/dd-trace-rb/issues/3706
4350
+ [#3709]: https://github.com/DataDog/dd-trace-rb/issues/3709
4351
+ [#3718]: https://github.com/DataDog/dd-trace-rb/issues/3718
4352
+ [#3720]: https://github.com/DataDog/dd-trace-rb/issues/3720
4353
+ [#3725]: https://github.com/DataDog/dd-trace-rb/issues/3725
4354
+ [#3729]: https://github.com/DataDog/dd-trace-rb/issues/3729
4355
+ [#3730]: https://github.com/DataDog/dd-trace-rb/issues/3730
4356
+ [#3731]: https://github.com/DataDog/dd-trace-rb/issues/3731
4357
+ [#3743]: https://github.com/DataDog/dd-trace-rb/issues/3743
4358
+ [#3749]: https://github.com/DataDog/dd-trace-rb/issues/3749
4359
+ [#3750]: https://github.com/DataDog/dd-trace-rb/issues/3750
4360
+ [#3753]: https://github.com/DataDog/dd-trace-rb/issues/3753
4361
+ [#3757]: https://github.com/DataDog/dd-trace-rb/issues/3757
4362
+ [#3759]: https://github.com/DataDog/dd-trace-rb/issues/3759
4363
+ [#3769]: https://github.com/DataDog/dd-trace-rb/issues/3769
4364
+ [#3770]: https://github.com/DataDog/dd-trace-rb/issues/3770
4365
+ [#3772]: https://github.com/DataDog/dd-trace-rb/issues/3772
4366
+ [#3774]: https://github.com/DataDog/dd-trace-rb/issues/3774
4367
+ [#3776]: https://github.com/DataDog/dd-trace-rb/issues/3776
4368
+ [#3779]: https://github.com/DataDog/dd-trace-rb/issues/3779
4369
+ [#3785]: https://github.com/DataDog/dd-trace-rb/issues/3785
4370
+ [#3789]: https://github.com/DataDog/dd-trace-rb/issues/3789
4371
+ [#3790]: https://github.com/DataDog/dd-trace-rb/issues/3790
4372
+ [#3793]: https://github.com/DataDog/dd-trace-rb/issues/3793
4373
+ [#3795]: https://github.com/DataDog/dd-trace-rb/issues/3795
4374
+ [#3797]: https://github.com/DataDog/dd-trace-rb/issues/3797
4375
+ [#3798]: https://github.com/DataDog/dd-trace-rb/issues/3798
4376
+ [#3799]: https://github.com/DataDog/dd-trace-rb/issues/3799
4377
+ [#3805]: https://github.com/DataDog/dd-trace-rb/issues/3805
4378
+ [#3809]: https://github.com/DataDog/dd-trace-rb/issues/3809
4379
+ [#3812]: https://github.com/DataDog/dd-trace-rb/issues/3812
4380
+ [#3813]: https://github.com/DataDog/dd-trace-rb/issues/3813
4381
+ [#3814]: https://github.com/DataDog/dd-trace-rb/issues/3814
4382
+ [#3826]: https://github.com/DataDog/dd-trace-rb/issues/3826
4383
+ [#3837]: https://github.com/DataDog/dd-trace-rb/issues/3837
4384
+ [#3839]: https://github.com/DataDog/dd-trace-rb/issues/3839
4385
+ [#3841]: https://github.com/DataDog/dd-trace-rb/issues/3841
4286
4386
  [@AdrianLC]: https://github.com/AdrianLC
4287
4387
  [@Azure7111]: https://github.com/Azure7111
4288
4388
  [@BabyGroot]: https://github.com/BabyGroot
@@ -1,22 +1,22 @@
1
1
  # rubocop:disable Style/StderrPuts
2
2
  # rubocop:disable Style/GlobalVars
3
3
 
4
- if RUBY_ENGINE != 'ruby' || Gem.win_platform?
4
+ if RUBY_ENGINE != "ruby" || Gem.win_platform?
5
5
  $stderr.puts(
6
- 'WARN: Skipping build of Datadog profiling loader. See Datadog profiling native extension note for details.'
6
+ "WARN: Skipping build of Datadog profiling loader. See Datadog profiling native extension note for details."
7
7
  )
8
8
 
9
- File.write('Makefile', 'all install clean: # dummy makefile that does nothing')
9
+ File.write("Makefile", "all install clean: # dummy makefile that does nothing")
10
10
  exit
11
11
  end
12
12
 
13
- require 'mkmf'
13
+ require "mkmf"
14
14
 
15
15
  # mkmf on modern Rubies actually has an append_cflags that does something similar
16
16
  # (see https://github.com/ruby/ruby/pull/5760), but as usual we need a bit more boilerplate to deal with legacy Rubies
17
17
  def add_compiler_flag(flag)
18
18
  if try_cflags(flag)
19
- $CFLAGS << ' ' << flag
19
+ $CFLAGS << " " << flag
20
20
  else
21
21
  $stderr.puts("WARNING: '#{flag}' not accepted by compiler, skipping it")
22
22
  end
@@ -24,26 +24,26 @@ end
24
24
 
25
25
  # Because we can't control what compiler versions our customers use, shipping with -Werror by default is a no-go.
26
26
  # But we can enable it in CI, so that we quickly spot any new warnings that just got introduced.
27
- add_compiler_flag '-Werror' if ENV['DATADOG_GEM_CI'] == 'true'
27
+ add_compiler_flag "-Werror" if ENV["DATADOG_GEM_CI"] == "true"
28
28
 
29
29
  # Older gcc releases may not default to C99 and we need to ask for this. This is also used:
30
30
  # * by upstream Ruby -- search for gnu99 in the codebase
31
31
  # * by msgpack, another datadog gem dependency
32
32
  # (https://github.com/msgpack/msgpack-ruby/blob/18ce08f6d612fe973843c366ac9a0b74c4e50599/ext/msgpack/extconf.rb#L8)
33
- add_compiler_flag '-std=gnu99'
33
+ add_compiler_flag "-std=gnu99"
34
34
 
35
35
  # Gets really noisy when we include the MJIT header, let's omit it (TODO: Use #pragma GCC diagnostic instead?)
36
- add_compiler_flag '-Wno-unused-function'
36
+ add_compiler_flag "-Wno-unused-function"
37
37
 
38
38
  # Allow defining variables at any point in a function
39
- add_compiler_flag '-Wno-declaration-after-statement'
39
+ add_compiler_flag "-Wno-declaration-after-statement"
40
40
 
41
41
  # If we forget to include a Ruby header, the function call may still appear to work, but then
42
42
  # cause a segfault later. Let's ensure that never happens.
43
- add_compiler_flag '-Werror-implicit-function-declaration'
43
+ add_compiler_flag "-Werror-implicit-function-declaration"
44
44
 
45
45
  # Warn on unused parameters to functions. Use `DDTRACE_UNUSED` to mark things as known-to-not-be-used.
46
- add_compiler_flag '-Wunused-parameter'
46
+ add_compiler_flag "-Wunused-parameter"
47
47
 
48
48
  # The native extension is not intended to expose any symbols/functions for other native libraries to use;
49
49
  # the sole exception being `Init_datadog_profiling_loader` which needs to be visible for Ruby to call it when
@@ -51,14 +51,14 @@ add_compiler_flag '-Wunused-parameter'
51
51
  #
52
52
  # By setting this compiler flag, we tell it to assume that everything is private unless explicitly stated.
53
53
  # For more details see https://gcc.gnu.org/wiki/Visibility
54
- add_compiler_flag '-fvisibility=hidden'
54
+ add_compiler_flag "-fvisibility=hidden"
55
55
 
56
56
  # Avoid legacy C definitions
57
- add_compiler_flag '-Wold-style-definition'
57
+ add_compiler_flag "-Wold-style-definition"
58
58
 
59
59
  # Enable all other compiler warnings
60
- add_compiler_flag '-Wall'
61
- add_compiler_flag '-Wextra'
60
+ add_compiler_flag "-Wall"
61
+ add_compiler_flag "-Wextra"
62
62
 
63
63
  # Tag the native extension library with the Ruby version and Ruby platform.
64
64
  # This makes it easier for development (avoids "oops I forgot to rebuild when I switched my Ruby") and ensures that
@@ -2,6 +2,7 @@
2
2
 
3
3
  #include <stdbool.h>
4
4
  #include <time.h>
5
+ #include <ruby.h>
5
6
 
6
7
  // Contains the operating-system specific identifier needed to fetch CPU-time, and a flag to indicate if we failed to fetch it
7
8
  typedef struct thread_cpu_time_id {
@@ -7,11 +7,10 @@
7
7
  #include <pthread.h>
8
8
  #include <time.h>
9
9
  #include <errno.h>
10
- #include <ruby.h>
11
10
 
11
+ #include "clock_id.h"
12
12
  #include "helpers.h"
13
13
  #include "private_vm_api_access.h"
14
- #include "clock_id.h"
15
14
  #include "time_helpers.h"
16
15
 
17
16
  // Validate that our home-cooked pthread_id_for() matches pthread_self() for the current thread
@@ -4,10 +4,9 @@
4
4
  // is not available.
5
5
  #ifndef HAVE_PTHREAD_GETCPUCLOCKID
6
6
 
7
- #include <ruby.h>
8
-
9
7
  #include "clock_id.h"
10
8
  #include "helpers.h"
9
+ #include "datadog_ruby_common.h"
11
10
 
12
11
  void self_test_clock_id(void) { } // Nothing to check
13
12
 
@@ -20,7 +20,9 @@
20
20
  #define ERR_CLOCK_FAIL "failed to get clock time"
21
21
 
22
22
  // Maximum allowed value for an allocation weight. Attempts to use higher values will result in clamping.
23
- unsigned int MAX_ALLOC_WEIGHT = 65535;
23
+ // See https://docs.google.com/document/d/1lWLB714wlLBBq6T4xZyAc4a5wtWhSmr4-hgiPKeErlA/edit#heading=h.ugp0zxcj5iqh
24
+ // (Datadog-only link) for research backing the choice of this value.
25
+ unsigned int MAX_ALLOC_WEIGHT = 10000;
24
26
 
25
27
  // Used to trigger the execution of Collectors::ThreadState, which implements all of the sampling logic
26
28
  // itself; this class only implements the "when to do it" part.
@@ -96,6 +98,7 @@ struct cpu_and_wall_time_worker_state {
96
98
  bool no_signals_workaround_enabled;
97
99
  bool dynamic_sampling_rate_enabled;
98
100
  bool allocation_profiling_enabled;
101
+ bool allocation_counting_enabled;
99
102
  bool skip_idle_samples_for_testing;
100
103
  VALUE self_instance;
101
104
  VALUE thread_context_collector_instance;
@@ -104,7 +107,6 @@ struct cpu_and_wall_time_worker_state {
104
107
  dynamic_sampling_rate_state cpu_dynamic_sampling_rate;
105
108
  discrete_dynamic_sampler allocation_sampler;
106
109
  VALUE gc_tracepoint; // Used to get gc start/finish information
107
- VALUE object_allocation_tracepoint; // Used to get allocation counts and allocation profiling
108
110
 
109
111
  // These are mutable and used to signal things between the worker thread and other threads
110
112
 
@@ -117,7 +119,7 @@ struct cpu_and_wall_time_worker_state {
117
119
 
118
120
  // Others
119
121
 
120
- // Used to detect/avoid nested sampling, e.g. when the object_allocation_tracepoint gets triggered by a memory allocation
122
+ // Used to detect/avoid nested sampling, e.g. when on_newobj_event gets triggered by a memory allocation
121
123
  // that happens during another sample.
122
124
  bool during_sample;
123
125
 
@@ -181,6 +183,7 @@ static VALUE _native_initialize(
181
183
  VALUE dynamic_sampling_rate_enabled,
182
184
  VALUE dynamic_sampling_rate_overhead_target_percentage,
183
185
  VALUE allocation_profiling_enabled,
186
+ VALUE allocation_counting_enabled,
184
187
  VALUE skip_idle_samples_for_testing
185
188
  );
186
189
  static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr);
@@ -216,12 +219,28 @@ static void grab_gvl_and_sample(void);
216
219
  static void reset_stats_not_thread_safe(struct cpu_and_wall_time_worker_state *state);
217
220
  static void sleep_for(uint64_t time_ns);
218
221
  static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self);
219
- static void on_newobj_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused);
222
+ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *unused2);
220
223
  static void disable_tracepoints(struct cpu_and_wall_time_worker_state *state);
221
224
  static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self);
222
225
  static VALUE rescued_sample_allocation(VALUE tracepoint_data);
223
226
  static void delayed_error(struct cpu_and_wall_time_worker_state *state, const char *error);
224
227
  static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
228
+ static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self);
229
+ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);
230
+
231
+ // We're using `on_newobj_event` function with `rb_add_event_hook2`, which requires in its public signature a function
232
+ // with signature `rb_event_hook_func_t` which doesn't match `on_newobj_event`.
233
+ //
234
+ // But in practice, because we pass the `RUBY_EVENT_HOOK_FLAG_RAW_ARG` flag to `rb_add_event_hook2`, it casts the
235
+ // expected signature into a `rb_event_hook_raw_arg_func_t`:
236
+ // > typedef void (*rb_event_hook_raw_arg_func_t)(VALUE data, const rb_trace_arg_t *arg); (from vm_trace.c)
237
+ // which does match `on_newobj_event`.
238
+ //
239
+ // So TL;DR we're just doing this here to avoid the warning and explain why the apparent mismatch in function signatures.
240
+ #pragma GCC diagnostic push
241
+ #pragma GCC diagnostic ignored "-Wcast-function-type"
242
+ static const rb_event_hook_func_t on_newobj_event_as_hook = (rb_event_hook_func_t) on_newobj_event;
243
+ #pragma GCC diagnostic pop
225
244
 
226
245
  // Note on sampler global state safety:
227
246
  //
@@ -276,7 +295,7 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
276
295
  // https://bugs.ruby-lang.org/issues/18007 for a discussion around this.
277
296
  rb_define_alloc_func(collectors_cpu_and_wall_time_worker_class, _native_new);
278
297
 
279
- rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_initialize", _native_initialize, 9);
298
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_initialize", _native_initialize, 10);
280
299
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_sampling_loop", _native_sampling_loop, 1);
281
300
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stop", _native_stop, 2);
282
301
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_reset_after_fork", _native_reset_after_fork, 1);
@@ -285,7 +304,9 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
285
304
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_allocation_count", _native_allocation_count, 0);
286
305
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_is_running?", _native_is_running, 1);
287
306
  rb_define_singleton_method(testing_module, "_native_current_sigprof_signal_handler", _native_current_sigprof_signal_handler, 0);
288
- // TODO: Remove `_native_is_running` from `testing_module` once `prof-correctness` has been updated to not need it
307
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_hold_signals", _native_hold_signals, 0);
308
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_resume_signals", _native_resume_signals, 0);
309
+ // TODO: Remove `_native_is_running` from `testing_module` (should be in class) once `prof-correctness` has been updated to not need it
289
310
  rb_define_singleton_method(testing_module, "_native_is_running?", _native_is_running, 1);
290
311
  rb_define_singleton_method(testing_module, "_native_install_testing_signal_handler", _native_install_testing_signal_handler, 0);
291
312
  rb_define_singleton_method(testing_module, "_native_remove_testing_signal_handler", _native_remove_testing_signal_handler, 0);
@@ -312,6 +333,8 @@ static const rb_data_type_t cpu_and_wall_time_worker_typed_data = {
312
333
  };
313
334
 
314
335
  static VALUE _native_new(VALUE klass) {
336
+ long now = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
337
+
315
338
  struct cpu_and_wall_time_worker_state *state = ruby_xcalloc(1, sizeof(struct cpu_and_wall_time_worker_state));
316
339
 
317
340
  // Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
@@ -321,13 +344,13 @@ static VALUE _native_new(VALUE klass) {
321
344
  state->no_signals_workaround_enabled = false;
322
345
  state->dynamic_sampling_rate_enabled = true;
323
346
  state->allocation_profiling_enabled = false;
347
+ state->allocation_counting_enabled = false;
324
348
  state->skip_idle_samples_for_testing = false;
325
349
  state->thread_context_collector_instance = Qnil;
326
350
  state->idle_sampling_helper_instance = Qnil;
327
351
  state->owner_thread = Qnil;
328
352
  dynamic_sampling_rate_init(&state->cpu_dynamic_sampling_rate);
329
353
  state->gc_tracepoint = Qnil;
330
- state->object_allocation_tracepoint = Qnil;
331
354
 
332
355
  atomic_init(&state->should_run, false);
333
356
  state->failure_exception = Qnil;
@@ -336,15 +359,12 @@ static VALUE _native_new(VALUE klass) {
336
359
  state->during_sample = false;
337
360
 
338
361
  reset_stats_not_thread_safe(state);
339
-
340
- long now = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
341
- if (now == 0) {
342
- ruby_xfree(state);
343
- rb_raise(rb_eRuntimeError, ERR_CLOCK_FAIL);
344
- }
345
-
346
362
  discrete_dynamic_sampler_init(&state->allocation_sampler, "allocation", now);
347
363
 
364
+ // Note: As of this writing, no new Ruby objects get created and stored in the state. If that ever changes, remember
365
+ // to keep them on the stack and mark them with RB_GC_GUARD -- otherwise it's possible for a GC to run and
366
+ // since the instance representing the state does not yet exist, such objects will not get marked.
367
+
348
368
  return state->self_instance = TypedData_Wrap_Struct(klass, &cpu_and_wall_time_worker_typed_data, state);
349
369
  }
350
370
 
@@ -358,6 +378,7 @@ static VALUE _native_initialize(
358
378
  VALUE dynamic_sampling_rate_enabled,
359
379
  VALUE dynamic_sampling_rate_overhead_target_percentage,
360
380
  VALUE allocation_profiling_enabled,
381
+ VALUE allocation_counting_enabled,
361
382
  VALUE skip_idle_samples_for_testing
362
383
  ) {
363
384
  ENFORCE_BOOLEAN(gc_profiling_enabled);
@@ -365,6 +386,7 @@ static VALUE _native_initialize(
365
386
  ENFORCE_BOOLEAN(dynamic_sampling_rate_enabled);
366
387
  ENFORCE_TYPE(dynamic_sampling_rate_overhead_target_percentage, T_FLOAT);
367
388
  ENFORCE_BOOLEAN(allocation_profiling_enabled);
389
+ ENFORCE_BOOLEAN(allocation_counting_enabled);
368
390
  ENFORCE_BOOLEAN(skip_idle_samples_for_testing)
369
391
 
370
392
  struct cpu_and_wall_time_worker_state *state;
@@ -374,6 +396,7 @@ static VALUE _native_initialize(
374
396
  state->no_signals_workaround_enabled = (no_signals_workaround_enabled == Qtrue);
375
397
  state->dynamic_sampling_rate_enabled = (dynamic_sampling_rate_enabled == Qtrue);
376
398
  state->allocation_profiling_enabled = (allocation_profiling_enabled == Qtrue);
399
+ state->allocation_counting_enabled = (allocation_counting_enabled == Qtrue);
377
400
  state->skip_idle_samples_for_testing = (skip_idle_samples_for_testing == Qtrue);
378
401
 
379
402
  double total_overhead_target_percentage = NUM2DBL(dynamic_sampling_rate_overhead_target_percentage);
@@ -390,7 +413,6 @@ static VALUE _native_initialize(
390
413
  state->thread_context_collector_instance = enforce_thread_context_collector_instance(thread_context_collector_instance);
391
414
  state->idle_sampling_helper_instance = idle_sampling_helper_instance;
392
415
  state->gc_tracepoint = rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_GC_ENTER | RUBY_INTERNAL_EVENT_GC_EXIT, on_gc_event, NULL /* unused */);
393
- state->object_allocation_tracepoint = rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_NEWOBJ, on_newobj_event, NULL /* unused */);
394
416
 
395
417
  return Qtrue;
396
418
  }
@@ -405,7 +427,6 @@ static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr) {
405
427
  rb_gc_mark(state->failure_exception);
406
428
  rb_gc_mark(state->stop_thread);
407
429
  rb_gc_mark(state->gc_tracepoint);
408
- rb_gc_mark(state->object_allocation_tracepoint);
409
430
  }
410
431
 
411
432
  // Called in a background thread created in CpuAndWallTimeWorker#start
@@ -751,7 +772,14 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
751
772
  // because they may raise exceptions.
752
773
  install_sigprof_signal_handler(handle_sampling_signal, "handle_sampling_signal");
753
774
  if (state->gc_profiling_enabled) rb_tracepoint_enable(state->gc_tracepoint);
754
- if (state->allocation_profiling_enabled) rb_tracepoint_enable(state->object_allocation_tracepoint);
775
+ if (state->allocation_profiling_enabled) {
776
+ rb_add_event_hook2(
777
+ on_newobj_event_as_hook,
778
+ RUBY_INTERNAL_EVENT_NEWOBJ,
779
+ state->self_instance,
780
+ RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG)
781
+ ;
782
+ }
755
783
 
756
784
  // Flag the profiler as running before we release the GVL, in case anyone's waiting to know about it
757
785
  rb_funcall(instance, rb_intern("signal_running"), 0);
@@ -1032,46 +1060,87 @@ static void sleep_for(uint64_t time_ns) {
1032
1060
  }
1033
1061
 
1034
1062
  static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
1035
- bool are_allocations_being_tracked = active_sampler_instance_state != NULL && active_sampler_instance_state->allocation_profiling_enabled;
1063
+ struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state;
1064
+
1065
+ bool are_allocations_being_tracked = state != NULL && state->allocation_profiling_enabled && state->allocation_counting_enabled;
1036
1066
 
1037
1067
  return are_allocations_being_tracked ? ULL2NUM(allocation_count) : Qnil;
1038
1068
  }
1039
1069
 
1040
- // Implements memory-related profiling events. This function is called by Ruby via the `object_allocation_tracepoint`
1041
- // when the RUBY_INTERNAL_EVENT_NEWOBJ event is triggered.
1042
- static void on_newobj_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused) {
1043
- // Update thread-local allocation count
1044
- if (RB_UNLIKELY(allocation_count == UINT64_MAX)) {
1045
- allocation_count = 0;
1046
- } else {
1047
- allocation_count++;
1048
- }
1070
+ #define HANDLE_CLOCK_FAILURE(call) ({ \
1071
+ long _result = (call); \
1072
+ if (_result == 0) { \
1073
+ delayed_error(state, ERR_CLOCK_FAIL); \
1074
+ return; \
1075
+ } \
1076
+ _result; \
1077
+ })
1049
1078
 
1079
+ // Implements memory-related profiling events. This function is called by Ruby via the `rb_add_event_hook2`
1080
+ // when the RUBY_INTERNAL_EVENT_NEWOBJ event is triggered.
1081
+ //
1082
+ // When allocation sampling is enabled, this function gets called for almost all* objects allocated by the Ruby VM.
1083
+ // (*In some weird cases the VM may skip this tracepoint.)
1084
+ //
1085
+ // At a high level, there's two paths through this function:
1086
+ // 1. should_sample == false -> return
1087
+ // 2. should_sample == true -> sample
1088
+ //
1089
+ // On big applications, path 1. is the hottest, since we don't sample every object. So it's quite important for it to
1090
+ // be as fast as possible.
1091
+ //
1092
+ // NOTE: You may be wondering why we don't use any of the arguments to this function. It turns out it's possible to just
1093
+ // call `rb_tracearg_from_tracepoint(anything)` anywhere during this function or its callees to get the data, so that's
1094
+ // why it's not being passed as an argument.
1095
+ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *unused2) {
1050
1096
  struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1051
1097
 
1052
1098
  // This should not happen in a normal situation because the tracepoint is always enabled after the instance is set
1053
1099
  // and disabled before it is cleared, but just in case...
1054
1100
  if (state == NULL) return;
1055
1101
 
1056
- // In a few cases, we may actually be allocating an object as part of profiler sampling. We don't want to recursively
1102
+ if (RB_UNLIKELY(state->allocation_counting_enabled)) {
1103
+ // Update thread-local allocation count
1104
+ if (RB_UNLIKELY(allocation_count == UINT64_MAX)) {
1105
+ allocation_count = 0;
1106
+ } else {
1107
+ allocation_count++;
1108
+ }
1109
+ }
1110
+
1111
+ // In rare cases, we may actually be allocating an object as part of profiler sampling. We don't want to recursively
1057
1112
  // sample, so we just return early
1058
1113
  if (state->during_sample) {
1059
1114
  state->stats.allocations_during_sample++;
1060
1115
  return;
1061
1116
  }
1062
1117
 
1063
- if (state->dynamic_sampling_rate_enabled) {
1064
- long now = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
1065
- if (now == 0) {
1066
- delayed_error(state, ERR_CLOCK_FAIL);
1067
- return;
1068
- }
1069
- if (!discrete_dynamic_sampler_should_sample(&state->allocation_sampler, now)) {
1070
- state->stats.allocation_skipped++;
1071
- return;
1118
+ // Hot path: Dynamic sampling rate is usually enabled and the sampling decision is usually false
1119
+ if (RB_LIKELY(state->dynamic_sampling_rate_enabled && !discrete_dynamic_sampler_should_sample(&state->allocation_sampler))) {
1120
+ state->stats.allocation_skipped++;
1121
+
1122
+ coarse_instant now = monotonic_coarse_wall_time_now_ns();
1123
+ HANDLE_CLOCK_FAILURE(now.timestamp_ns);
1124
+
1125
+ bool needs_readjust = discrete_dynamic_sampler_skipped_sample(&state->allocation_sampler, now);
1126
+ if (RB_UNLIKELY(needs_readjust)) {
1127
+ // We rarely readjust, so this is a cold path
1128
+ // Also, while above we used the cheaper monotonic_coarse, for this call we want the regular monotonic call,
1129
+ // which is why we end up getting time "again".
1130
+ discrete_dynamic_sampler_readjust(
1131
+ &state->allocation_sampler, HANDLE_CLOCK_FAILURE(monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE))
1132
+ );
1072
1133
  }
1134
+
1135
+ return;
1073
1136
  }
1074
1137
 
1138
+ // From here on, we've decided to go ahead with the sample, which is way less common than skipping it
1139
+
1140
+ discrete_dynamic_sampler_before_sample(
1141
+ &state->allocation_sampler, HANDLE_CLOCK_FAILURE(monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE))
1142
+ );
1143
+
1075
1144
  // @ivoanjo: Strictly speaking, this is not needed because Ruby should not call the same tracepoint while a previous
1076
1145
  // invocation is still pending, (e.g. it wouldn't call `on_newobj_event` while it's already running), but I decided
1077
1146
  // to keep this here for consistency -- every call to the thread context (other than the special gc calls which are
@@ -1079,7 +1148,7 @@ static void on_newobj_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused)
1079
1148
  state->during_sample = true;
1080
1149
 
1081
1150
  // Rescue against any exceptions that happen during sampling
1082
- safely_call(rescued_sample_allocation, tracepoint_data, state->self_instance);
1151
+ safely_call(rescued_sample_allocation, Qnil, state->self_instance);
1083
1152
 
1084
1153
  if (state->dynamic_sampling_rate_enabled) {
1085
1154
  long now = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
@@ -1104,9 +1173,7 @@ static void disable_tracepoints(struct cpu_and_wall_time_worker_state *state) {
1104
1173
  if (state->gc_tracepoint != Qnil) {
1105
1174
  rb_tracepoint_disable(state->gc_tracepoint);
1106
1175
  }
1107
- if (state->object_allocation_tracepoint != Qnil) {
1108
- rb_tracepoint_disable(state->object_allocation_tracepoint);
1109
- }
1176
+ rb_remove_event_hook_with_data(on_newobj_event_as_hook, state->self_instance);
1110
1177
  }
1111
1178
 
1112
1179
  static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self) {
@@ -1122,13 +1189,14 @@ static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self) {
1122
1189
  }
1123
1190
  }
1124
1191
 
1125
- static VALUE rescued_sample_allocation(VALUE tracepoint_data) {
1192
+ static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {
1126
1193
  struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1127
1194
 
1128
1195
  // This should not happen in a normal situation because on_newobj_event already checked for this, but just in case...
1129
1196
  if (state == NULL) return Qnil;
1130
1197
 
1131
- rb_trace_arg_t *data = rb_tracearg_from_tracepoint(tracepoint_data);
1198
+ // If we're getting called from inside a tracepoint/event hook, Ruby exposes the data using this function.
1199
+ rb_trace_arg_t *data = rb_tracearg_from_tracepoint(Qnil);
1132
1200
  VALUE new_object = rb_tracearg_object(data);
1133
1201
 
1134
1202
  unsigned long allocations_since_last_sample = state->dynamic_sampling_rate_enabled ?
@@ -1136,9 +1204,15 @@ static VALUE rescued_sample_allocation(VALUE tracepoint_data) {
1136
1204
  discrete_dynamic_sampler_events_since_last_sample(&state->allocation_sampler) :
1137
1205
  // if we aren't, then we're sampling every event
1138
1206
  1;
1139
- // TODO: Signal in the profile that clamping happened?
1207
+
1208
+ // To control bias from sampling, we clamp the maximum weight attributed to a single allocation sample. This avoids
1209
+ // assigning a very large number to a sample, if for instance the dynamic sampling mechanism chose a really big interval.
1140
1210
  unsigned int weight = allocations_since_last_sample > MAX_ALLOC_WEIGHT ? MAX_ALLOC_WEIGHT : (unsigned int) allocations_since_last_sample;
1141
1211
  thread_context_collector_sample_allocation(state->thread_context_collector_instance, weight, new_object);
1212
+ // ...but we still represent the skipped samples in the profile, thus the data will account for all allocations.
1213
+ if (weight < allocations_since_last_sample) {
1214
+ thread_context_collector_sample_skipped_allocation_samples(state->thread_context_collector_instance, allocations_since_last_sample - weight);
1215
+ }
1142
1216
 
1143
1217
  // Return a dummy VALUE because we're called from rb_rescue2 which requires it
1144
1218
  return Qnil;
@@ -1159,3 +1233,17 @@ static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VA
1159
1233
 
1160
1234
  return Qnil;
1161
1235
  }
1236
+
1237
+ // Masks SIGPROF interruptions for the current thread. Please don't use this -- you may end up with incomplete
1238
+ // profiling data.
1239
+ static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self) {
1240
+ block_sigprof_signal_handler_from_running_in_current_thread();
1241
+ return Qtrue;
1242
+ }
1243
+
1244
+ // Unmasks SIGPROF interruptions for the current thread. If there's a pending sample, it'll be triggered inside this
1245
+ // method.
1246
+ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
1247
+ unblock_sigprof_signal_handler_from_running_in_current_thread();
1248
+ return Qtrue;
1249
+ }