datadog 2.1.0 → 2.3.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 (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
+ }