datadog 2.1.0 → 2.2.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +53 -2
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +19 -1
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +41 -0
  5. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +1 -1
  6. data/ext/datadog_profiling_native_extension/crashtracker.c +1 -1
  7. data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
  8. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +47 -1
  9. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +1 -1
  10. data/ext/datadog_profiling_native_extension/stack_recorder.c +13 -6
  11. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
  12. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +1 -1
  13. data/lib/datadog/appsec/extensions.rb +1 -0
  14. data/lib/datadog/core/configuration/components.rb +6 -3
  15. data/lib/datadog/core/configuration/settings.rb +39 -0
  16. data/lib/datadog/core/configuration.rb +3 -17
  17. data/lib/datadog/core/deprecations.rb +58 -0
  18. data/lib/datadog/core/environment/yjit.rb +5 -0
  19. data/lib/datadog/core/runtime/ext.rb +1 -0
  20. data/lib/datadog/core/runtime/metrics.rb +6 -0
  21. data/lib/datadog/core/telemetry/component.rb +107 -0
  22. data/lib/datadog/core/telemetry/event.rb +100 -25
  23. data/lib/datadog/core/telemetry/ext.rb +2 -0
  24. data/lib/datadog/core/telemetry/http/adapters/net.rb +1 -1
  25. data/lib/datadog/core/telemetry/metric.rb +167 -0
  26. data/lib/datadog/core/telemetry/metrics_collection.rb +81 -0
  27. data/lib/datadog/core/telemetry/metrics_manager.rb +81 -0
  28. data/lib/datadog/core/telemetry/request.rb +1 -1
  29. data/lib/datadog/core/telemetry/worker.rb +173 -0
  30. data/lib/datadog/core/utils/only_once_successful.rb +76 -0
  31. data/lib/datadog/core.rb +2 -19
  32. data/lib/datadog/opentelemetry/sdk/propagator.rb +5 -10
  33. data/lib/datadog/opentelemetry/sdk/span_processor.rb +5 -2
  34. data/lib/datadog/profiling/collectors/code_provenance.rb +18 -5
  35. data/lib/datadog/profiling/component.rb +18 -1
  36. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +410 -0
  37. data/lib/datadog/profiling.rb +1 -0
  38. data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
  39. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +1 -1
  40. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +1 -1
  41. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +1 -1
  42. data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -6
  43. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +9 -4
  44. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +3 -2
  45. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +1 -5
  46. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  47. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  48. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  49. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  50. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  51. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  52. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  53. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  54. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  55. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  56. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
  57. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +32 -0
  58. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +156 -0
  59. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +34 -0
  60. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +45 -41
  61. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +17 -40
  62. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -1
  63. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +29 -6
  64. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +16 -4
  65. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +33 -29
  66. data/lib/datadog/tracing/contrib/analytics.rb +5 -0
  67. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +5 -0
  68. data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -2
  69. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +166 -0
  70. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +25 -0
  71. data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -1
  72. data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +1 -1
  73. data/lib/datadog/tracing/contrib/kafka/event.rb +1 -1
  74. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +3 -3
  75. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +3 -3
  76. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +3 -3
  77. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +3 -3
  78. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +3 -3
  79. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +3 -3
  80. data/lib/datadog/tracing/contrib/racecar/event.rb +2 -2
  81. data/lib/datadog/tracing/contrib/rails/ext.rb +9 -0
  82. data/lib/datadog/tracing/contrib/rails/patcher.rb +7 -0
  83. data/lib/datadog/tracing/contrib/rails/runner.rb +95 -0
  84. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  85. data/lib/datadog/tracing/distributed/b3_single.rb +3 -1
  86. data/lib/datadog/tracing/distributed/datadog.rb +2 -2
  87. data/lib/datadog/tracing/distributed/propagation.rb +9 -2
  88. data/lib/datadog/tracing/distributed/trace_context.rb +3 -2
  89. data/lib/datadog/tracing/span_operation.rb +3 -2
  90. data/lib/datadog/tracing/trace_operation.rb +7 -3
  91. data/lib/datadog/tracing/trace_segment.rb +4 -1
  92. data/lib/datadog/tracing/tracer.rb +9 -2
  93. data/lib/datadog/tracing.rb +5 -1
  94. data/lib/datadog/version.rb +2 -2
  95. metadata +22 -9
  96. data/lib/datadog/core/telemetry/client.rb +0 -95
  97. data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0297728b2d8779a69af39f3dffa86af6b3765e3f2dd19cb685340ca7ad8e2ac
4
- data.tar.gz: 3c7d3506df76790fea0b2e2047d1b2c4919e9d928ccb644086194197125391e7
3
+ metadata.gz: 895fe8f9fdd8391d5c6c86e8d39d0a8e241c85bbce42b35c9744e1f94095853f
4
+ data.tar.gz: d5a6e88ec35816de59a5d080f66896a4c85656216d2853b46bb1268dfa64df35
5
5
  SHA512:
6
- metadata.gz: ec2862773c6ee93e5d698d6b586a576413689a84f7d94a621c20088759469e1ad6742e13fc1e7f9250046212cec0e284abf4fb0048e255802c19f378cf7d428c
7
- data.tar.gz: 507fbfd3f8879333c11336646e1a56ca453fbaf92c84fb0bae3b0e38435f3ea1ec6b3ddc756f848c76113a4e5832f8097cd521532541bf9ca8fdff6cca1e5ff3
6
+ metadata.gz: fef3c78c7835c47507a1f09d87c2ee84ddcc97303f9b9d7c6b4d601381ac62a47133f3450ddbc888cf54c56353ef4417ff337009a1e60d2ae239fb19093d721b
7
+ data.tar.gz: 061154162ab97a6e1cdc87f53c18d98fd0eea75b6cb7e71fd1e4e5c4a536ff7722c4e1202258760113ce5a7f0ce3838622e700858103fa97dddc33d0322275cc
data/CHANGELOG.md CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [2.2.0] - 2024-07-11
6
+
7
+ ### Added
8
+
9
+ * Tracing: Add `Rails` Runner instrumentation ([#2509][])
10
+ * Tracing: Introduce a new, reworked `GraphQL` tracer to comply with span attributes specification ([#3672][])
11
+ * Tracing: Enhance `ActiveSupport::Cache` instrumentation with `ActiveSupport::Notifications` subscription ([#3772][])
12
+ * Profiling: Track unscaled allocation counts in allocation profiler ([#3770][])
13
+
14
+ ### Changed
15
+
16
+ * Core: Send Telemetry events in batches ([#3749][])
17
+ * Tracing: Populate spans from `ActiveSupport::Notifications` as early as possible ([#3725][])
18
+ * Profiling: Upgrade to `libdatadog` 10 ([#3753][])
19
+ * Profiling: Optimize `CodeProvenance#record_loaded_files` to avoid allocations ([#3757][])
20
+
21
+ ### Fixed
22
+
23
+ * Core: Fix Telemetry events blocking main thread ([#3718][])
24
+ * Core: Fix deadlock from Telemetry threads ([#3743][])
25
+ * Tracing: Fix empty log correlation when tracing is disabled ([#3731][])
26
+ * Tracing: Fix HTTP propagation when missing parent span id ([#3730][])
27
+ * Tracing: Ensure `_dd.p.tid` tag with fixed size ([#3729][])
28
+ * OTel: Fix ids encoding/decoding for propagation ([#3709][])
29
+ * Profiling: Workaround Ruby `Dir` returning incorrect results ([#3720][])
30
+ * Profiling: Fix `Phusion Passenger` detection when missing from `Gemfile`/`gems.rb` ([#3750][])
31
+ * Profiling: Fix `rpath` for linking to libdatadog when loading extension ([#3706][])
32
+ * Profiling: Fix incorrect code provenance due to broken JSON monkey patch ([#3695][])
33
+ * Profiling: Fix aggregation of actionview template classes ([#3759][], [#3774][])
34
+
5
35
  ## [2.1.0] - 2024-06-10
6
36
 
7
37
  ### Added
@@ -2904,7 +2934,8 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
2904
2934
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
2905
2935
 
2906
2936
 
2907
- [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.1.0...master
2937
+ [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.2.0...master
2938
+ [2.2.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.1.0...v2.2.0
2908
2939
  [2.1.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0...v2.1.0
2909
2940
  [2.0.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0.rc1...v2.0.0
2910
2941
  [2.0.0.rc1]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0.beta2...v2.0.0.rc1
@@ -3901,6 +3932,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3901
3932
  [#2497]: https://github.com/DataDog/dd-trace-rb/issues/2497
3902
3933
  [#2501]: https://github.com/DataDog/dd-trace-rb/issues/2501
3903
3934
  [#2504]: https://github.com/DataDog/dd-trace-rb/issues/2504
3935
+ [#2509]: https://github.com/DataDog/dd-trace-rb/issues/2509
3904
3936
  [#2512]: https://github.com/DataDog/dd-trace-rb/issues/2512
3905
3937
  [#2513]: https://github.com/DataDog/dd-trace-rb/issues/2513
3906
3938
  [#2522]: https://github.com/DataDog/dd-trace-rb/issues/2522
@@ -4283,6 +4315,25 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
4283
4315
  [#3651]: https://github.com/DataDog/dd-trace-rb/issues/3651
4284
4316
  [#3657]: https://github.com/DataDog/dd-trace-rb/issues/3657
4285
4317
  [#3664]: https://github.com/DataDog/dd-trace-rb/issues/3664
4318
+ [#3672]: https://github.com/DataDog/dd-trace-rb/issues/3672
4319
+ [#3695]: https://github.com/DataDog/dd-trace-rb/issues/3695
4320
+ [#3706]: https://github.com/DataDog/dd-trace-rb/issues/3706
4321
+ [#3709]: https://github.com/DataDog/dd-trace-rb/issues/3709
4322
+ [#3718]: https://github.com/DataDog/dd-trace-rb/issues/3718
4323
+ [#3720]: https://github.com/DataDog/dd-trace-rb/issues/3720
4324
+ [#3725]: https://github.com/DataDog/dd-trace-rb/issues/3725
4325
+ [#3729]: https://github.com/DataDog/dd-trace-rb/issues/3729
4326
+ [#3730]: https://github.com/DataDog/dd-trace-rb/issues/3730
4327
+ [#3731]: https://github.com/DataDog/dd-trace-rb/issues/3731
4328
+ [#3743]: https://github.com/DataDog/dd-trace-rb/issues/3743
4329
+ [#3749]: https://github.com/DataDog/dd-trace-rb/issues/3749
4330
+ [#3750]: https://github.com/DataDog/dd-trace-rb/issues/3750
4331
+ [#3753]: https://github.com/DataDog/dd-trace-rb/issues/3753
4332
+ [#3757]: https://github.com/DataDog/dd-trace-rb/issues/3757
4333
+ [#3759]: https://github.com/DataDog/dd-trace-rb/issues/3759
4334
+ [#3770]: https://github.com/DataDog/dd-trace-rb/issues/3770
4335
+ [#3772]: https://github.com/DataDog/dd-trace-rb/issues/3772
4336
+ [#3774]: https://github.com/DataDog/dd-trace-rb/issues/3774
4286
4337
  [@AdrianLC]: https://github.com/AdrianLC
4287
4338
  [@Azure7111]: https://github.com/Azure7111
4288
4339
  [@BabyGroot]: https://github.com/BabyGroot
@@ -4434,4 +4485,4 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
4434
4485
  [@y-yagi]: https://github.com/y-yagi
4435
4486
  [@yujideveloper]: https://github.com/yujideveloper
4436
4487
  [@yukimurasawa]: https://github.com/yukimurasawa
4437
- [@zachmccormick]: https://github.com/zachmccormick
4488
+ [@zachmccormick]: https://github.com/zachmccormick
@@ -222,6 +222,8 @@ static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self);
222
222
  static VALUE rescued_sample_allocation(VALUE tracepoint_data);
223
223
  static void delayed_error(struct cpu_and_wall_time_worker_state *state, const char *error);
224
224
  static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
225
+ static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self);
226
+ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);
225
227
 
226
228
  // Note on sampler global state safety:
227
229
  //
@@ -285,7 +287,9 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
285
287
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_allocation_count", _native_allocation_count, 0);
286
288
  rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_is_running?", _native_is_running, 1);
287
289
  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
290
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_hold_signals", _native_hold_signals, 0);
291
+ rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_resume_signals", _native_resume_signals, 0);
292
+ // TODO: Remove `_native_is_running` from `testing_module` (should be in class) once `prof-correctness` has been updated to not need it
289
293
  rb_define_singleton_method(testing_module, "_native_is_running?", _native_is_running, 1);
290
294
  rb_define_singleton_method(testing_module, "_native_install_testing_signal_handler", _native_install_testing_signal_handler, 0);
291
295
  rb_define_singleton_method(testing_module, "_native_remove_testing_signal_handler", _native_remove_testing_signal_handler, 0);
@@ -1159,3 +1163,17 @@ static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VA
1159
1163
 
1160
1164
  return Qnil;
1161
1165
  }
1166
+
1167
+ // Masks SIGPROF interruptions for the current thread. Please don't use this -- you may end up with incomplete
1168
+ // profiling data.
1169
+ static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self) {
1170
+ block_sigprof_signal_handler_from_running_in_current_thread();
1171
+ return Qtrue;
1172
+ }
1173
+
1174
+ // Unmasks SIGPROF interruptions for the current thread. If there's a pending sample, it'll be triggered inside this
1175
+ // method.
1176
+ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
1177
+ unblock_sigprof_signal_handler_from_running_in_current_thread();
1178
+ return Qtrue;
1179
+ }
@@ -34,6 +34,7 @@ static VALUE _native_sample(
34
34
  );
35
35
  static void maybe_add_placeholder_frames_omitted(VALUE thread, sampling_buffer* buffer, char *frames_omitted_message, int frames_omitted_message_size);
36
36
  static void record_placeholder_stack_in_native_code(sampling_buffer* buffer, VALUE recorder_instance, sample_values values, sample_labels labels);
37
+ static void maybe_trim_template_random_ids(ddog_CharSlice *name_slice, ddog_CharSlice *filename_slice);
37
38
 
38
39
  void collectors_stack_init(VALUE profiling_module) {
39
40
  VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
@@ -69,6 +70,7 @@ static VALUE _native_sample(
69
70
  .cpu_or_wall_samples = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("cpu-samples"), zero)),
70
71
  .wall_time_ns = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("wall-time"), zero)),
71
72
  .alloc_samples = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("alloc-samples"), zero)),
73
+ .alloc_samples_unscaled = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("alloc-samples-unscaled"), zero)),
72
74
  .timeline_wall_time_ns = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("timeline"), zero)),
73
75
  };
74
76
 
@@ -199,6 +201,8 @@ void sample_thread(
199
201
  ddog_CharSlice name_slice = char_slice_from_ruby_string(name);
200
202
  ddog_CharSlice filename_slice = char_slice_from_ruby_string(filename);
201
203
 
204
+ maybe_trim_template_random_ids(&name_slice, &filename_slice);
205
+
202
206
  bool top_of_the_stack = i == 0;
203
207
 
204
208
  // When there's only wall-time in a sample, this means that the thread was not active in the sampled period.
@@ -268,6 +272,43 @@ void sample_thread(
268
272
  );
269
273
  }
270
274
 
275
+ // Rails's ActionView likes to dynamically generate method names with suffixed hashes/ids, resulting in methods with
276
+ // names such as:
277
+ // * "_app_views_layouts_explore_html_haml__2304485752546535910_211320" (__number_number suffix -- two underscores)
278
+ // * "_app_views_articles_index_html_erb___2022809201779434309_12900" (___number_number suffix -- three underscores)
279
+ // This makes these stacks not aggregate well, as well as being not-very-useful data.
280
+ // (Reference:
281
+ // https://github.com/rails/rails/blob/4fa56814f18fd3da49c83931fa773caa727d8096/actionview/lib/action_view/template.rb#L389
282
+ // The two vs three underscores happen when @identifier.hash is negative in that method: the "-" gets replaced with
283
+ // the extra "_".)
284
+ //
285
+ // This method trims these suffixes, so that we keep less data + the names correctly aggregate together.
286
+ static void maybe_trim_template_random_ids(ddog_CharSlice *name_slice, ddog_CharSlice *filename_slice) {
287
+ // Check filename doesn't end with ".rb"; templates are usually along the lines of .html.erb/.html.haml/...
288
+ if (filename_slice->len < 3 || memcmp(filename_slice->ptr + filename_slice->len - 3, ".rb", 3) == 0) return;
289
+
290
+ int pos = name_slice->len - 1;
291
+
292
+ // Let's match on something__number_number:
293
+ // Find start of id suffix from the end...
294
+ if (name_slice->ptr[pos] < '0' || name_slice->ptr[pos] > '9') return;
295
+
296
+ // ...now match a bunch of numbers and interspersed underscores
297
+ for (int underscores = 0; pos >= 0 && underscores < 2; pos--) {
298
+ if (name_slice->ptr[pos] == '_') underscores++;
299
+ else if (name_slice->ptr[pos] < '0' || name_slice->ptr[pos] > '9') return;
300
+ }
301
+
302
+ // Make sure there's something left before the underscores (hence the <= instead of <) + match the last underscore
303
+ if (pos <= 0 || name_slice->ptr[pos] != '_') return;
304
+
305
+ // Does it have the optional third underscore? If so, remove it as well
306
+ if (pos > 1 && name_slice->ptr[pos-1] == '_') pos--;
307
+
308
+ // If we got here, we matched on our pattern. Let's slice the length of the string to exclude it.
309
+ name_slice->len = pos;
310
+ }
311
+
271
312
  static void maybe_add_placeholder_frames_omitted(VALUE thread, sampling_buffer* buffer, char *frames_omitted_message, int frames_omitted_message_size) {
272
313
  ptrdiff_t frames_omitted = stack_depth_for(thread) - buffer->max_frames;
273
314
 
@@ -1290,7 +1290,7 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
1290
1290
  /* thread: */ current_thread,
1291
1291
  /* stack_from_thread: */ current_thread,
1292
1292
  get_or_create_context_for(current_thread, state),
1293
- (sample_values) {.alloc_samples = sample_weight},
1293
+ (sample_values) {.alloc_samples = sample_weight, .alloc_samples_unscaled = 1},
1294
1294
  INVALID_TIME, // For now we're not collecting timestamps for allocation events, as per profiling team internal discussions
1295
1295
  &ruby_vm_type,
1296
1296
  optional_class_name
@@ -57,7 +57,7 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
57
57
  // "Process.kill('SEGV', Process.pid)" gets run.
58
58
  .create_alt_stack = false,
59
59
  .endpoint = endpoint,
60
- .resolve_frames = DDOG_PROF_STACKTRACE_COLLECTION_ENABLED,
60
+ .resolve_frames = DDOG_PROF_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER,
61
61
  .timeout_secs = FIX2INT(upload_timeout_seconds),
62
62
  };
63
63
 
@@ -211,12 +211,14 @@ unless have_type('atomic_int', ['stdatomic.h'])
211
211
  skip_building_extension!(Datadog::Profiling::NativeExtensionHelpers::Supported::COMPILER_ATOMIC_MISSING)
212
212
  end
213
213
 
214
- # See comments on the helper method being used for why we need to additionally set this.
214
+ # See comments on the helper methods being used for why we need to additionally set this.
215
215
  # The extremely excessive escaping around ORIGIN below seems to be correct and was determined after a lot of
216
216
  # experimentation. We need to get these special characters across a lot of tools untouched...
217
- $LDFLAGS += \
218
- ' -Wl,-rpath,$$$\\\\{ORIGIN\\}/' \
219
- "#{Datadog::Profiling::NativeExtensionHelpers.libdatadog_folder_relative_to_native_lib_folder}"
217
+ extra_relative_rpaths = [
218
+ Datadog::Profiling::NativeExtensionHelpers.libdatadog_folder_relative_to_native_lib_folder,
219
+ *Datadog::Profiling::NativeExtensionHelpers.libdatadog_folder_relative_to_ruby_extensions_folders,
220
+ ]
221
+ extra_relative_rpaths.each { |folder| $LDFLAGS += " -Wl,-rpath,$$$\\\\{ORIGIN\\}/#{folder.to_str}" }
220
222
  Logging.message("[datadog] After pkg-config $LDFLAGS were set to: #{$LDFLAGS.inspect}\n")
221
223
 
222
224
  # Tag the native extension library with the Ruby version and Ruby platform.
@@ -15,7 +15,7 @@ module Datadog
15
15
  # The MJIT header was introduced on 2.6 and removed on 3.3; for other Rubies we rely on debase-ruby_core_source
16
16
  CAN_USE_MJIT_HEADER = RUBY_VERSION.start_with?('2.6', '2.7', '3.0.', '3.1.', '3.2.')
17
17
 
18
- LIBDATADOG_VERSION = '~> 9.0.0.1.0'
18
+ LIBDATADOG_VERSION = '~> 10.0.0.1.0'
19
19
 
20
20
  def self.fail_install_if_missing_extension?
21
21
  ENV[ENV_FAIL_INSTALL_IF_MISSING_EXTENSION].to_s.strip.downcase == 'true'
@@ -67,6 +67,52 @@ module Datadog
67
67
  Pathname.new(libdatadog_lib_folder).relative_path_from(Pathname.new(profiling_native_lib_folder)).to_s
68
68
  end
69
69
 
70
+ # In https://github.com/DataDog/dd-trace-rb/pull/3582 we got a report of a customer for which the native extension
71
+ # only got installed into the extensions folder.
72
+ #
73
+ # But then this fix was not enough to fully get them moving because then they started to see the issue from
74
+ # https://github.com/DataDog/dd-trace-rb/issues/2067 / https://github.com/DataDog/dd-trace-rb/pull/2125 :
75
+ #
76
+ # > Profiling was requested but is not supported, profiling disabled: There was an error loading the profiling
77
+ # > native extension due to 'RuntimeError Failure to load datadog_profiling_native_extension.3.2.2_x86_64-linux
78
+ # > due to libdatadog_profiling.so: cannot open shared object file: No such file or directory
79
+ #
80
+ # The problem is that when loading the native extension from the extensions directory, the relative rpath we add
81
+ # with the #libdatadog_folder_relative_to_native_lib_folder helper above is not correct, we need to add a relative
82
+ # rpath to the extensions directory.
83
+ #
84
+ # So how do we find the full path where the native extension is placed?
85
+ # * From https://github.com/ruby/ruby/blob/83f02d42e0a3c39661dc99c049ab9a70ff227d5b/lib/bundler/runtime.rb#L166
86
+ # `extension_dirs = Dir["#{Gem.dir}/extensions/*/*/*"] + Dir["#{Gem.dir}/bundler/gems/extensions/*/*/*"]`
87
+ # we get that's in one of two fixed subdirectories of `Gem.dir`
88
+ # * From https://github.com/ruby/ruby/blob/83f02d42e0a3c39661dc99c049ab9a70ff227d5b/lib/rubygems/basic_specification.rb#L111-L115
89
+ # we get the structure of the subdirectory (platform/extension_api_version/gem_and_version)
90
+ #
91
+ # Thus, `Gem.dir` of `/var/app/current/vendor/bundle/ruby/3.2.0` becomes (for instance)
92
+ # `/var/app/current/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/datadog-2.0.0/` or
93
+ # `/var/app/current/vendor/bundle/ruby/3.2.0/bundler/gems/extensions/x86_64-linux/3.2.0/datadog-2.0.0/`
94
+ #
95
+ # We then compute the relative path between these folders and the libdatadog folder, and use that as a relative path.
96
+ def self.libdatadog_folder_relative_to_ruby_extensions_folders(
97
+ gem_dir: Gem.dir,
98
+ libdatadog_pkgconfig_folder: Libdatadog.pkgconfig_folder
99
+ )
100
+ return unless libdatadog_pkgconfig_folder
101
+
102
+ # For the purposes of calculating a folder relative to the other, we don't actually NEED to fill in the
103
+ # platform, extension_api_version and gem version. We're basically just after how many folders it is deep from
104
+ # the Gem.dir.
105
+ expected_ruby_extensions_folders = [
106
+ "#{gem_dir}/extensions/platform/extension_api_version/datadog_version/",
107
+ "#{gem_dir}/bundler/gems/extensions/platform/extension_api_version/datadog_version/",
108
+ ]
109
+ libdatadog_lib_folder = "#{libdatadog_pkgconfig_folder}/../"
110
+
111
+ expected_ruby_extensions_folders.map do |folder|
112
+ Pathname.new(libdatadog_lib_folder).relative_path_from(Pathname.new(folder)).to_s
113
+ end
114
+ end
115
+
70
116
  # Used to check if profiler is supported, including user-visible clear messages explaining why their
71
117
  # system may not be supported.
72
118
  module Supported
@@ -91,7 +91,7 @@ void remove_sigprof_signal_handler(void) {
91
91
  if (sigaction(SIGPROF, &signal_handler_config, NULL) != 0) rb_sys_fail("Failure while removing the signal handler");
92
92
  }
93
93
 
94
- static void toggle_sigprof_signal_handler_for_current_thread(int action) {
94
+ static inline void toggle_sigprof_signal_handler_for_current_thread(int action) {
95
95
  sigset_t signals_to_toggle;
96
96
  sigemptyset(&signals_to_toggle);
97
97
  sigaddset(&signals_to_toggle, SIGPROF);
@@ -151,21 +151,23 @@ static VALUE error_symbol = Qnil; // :error in Ruby
151
151
  #define WALL_TIME_VALUE_ID 2
152
152
  #define ALLOC_SAMPLES_VALUE {.type_ = VALUE_STRING("alloc-samples"), .unit = VALUE_STRING("count")}
153
153
  #define ALLOC_SAMPLES_VALUE_ID 3
154
+ #define ALLOC_SAMPLES_UNSCALED_VALUE {.type_ = VALUE_STRING("alloc-samples-unscaled"), .unit = VALUE_STRING("count")}
155
+ #define ALLOC_SAMPLES_UNSCALED_VALUE_ID 4
154
156
  #define HEAP_SAMPLES_VALUE {.type_ = VALUE_STRING("heap-live-samples"), .unit = VALUE_STRING("count")}
155
- #define HEAP_SAMPLES_VALUE_ID 4
157
+ #define HEAP_SAMPLES_VALUE_ID 5
156
158
  #define HEAP_SIZE_VALUE {.type_ = VALUE_STRING("heap-live-size"), .unit = VALUE_STRING("bytes")}
157
- #define HEAP_SIZE_VALUE_ID 5
159
+ #define HEAP_SIZE_VALUE_ID 6
158
160
  #define TIMELINE_VALUE {.type_ = VALUE_STRING("timeline"), .unit = VALUE_STRING("nanoseconds")}
159
- #define TIMELINE_VALUE_ID 6
161
+ #define TIMELINE_VALUE_ID 7
160
162
 
161
163
  static const ddog_prof_ValueType all_value_types[] =
162
- {CPU_TIME_VALUE, CPU_SAMPLES_VALUE, WALL_TIME_VALUE, ALLOC_SAMPLES_VALUE, HEAP_SAMPLES_VALUE, HEAP_SIZE_VALUE, TIMELINE_VALUE};
164
+ {CPU_TIME_VALUE, CPU_SAMPLES_VALUE, WALL_TIME_VALUE, ALLOC_SAMPLES_VALUE, ALLOC_SAMPLES_UNSCALED_VALUE, HEAP_SAMPLES_VALUE, HEAP_SIZE_VALUE, TIMELINE_VALUE};
163
165
 
164
166
  // This array MUST be kept in sync with all_value_types above and is intended to act as a "hashmap" between VALUE_ID and the position it
165
167
  // occupies on the all_value_types array.
166
168
  // E.g. all_value_types_positions[CPU_TIME_VALUE_ID] => 0, means that CPU_TIME_VALUE was declared at position 0 of all_value_types.
167
169
  static const uint8_t all_value_types_positions[] =
168
- {CPU_TIME_VALUE_ID, CPU_SAMPLES_VALUE_ID, WALL_TIME_VALUE_ID, ALLOC_SAMPLES_VALUE_ID, HEAP_SAMPLES_VALUE_ID, HEAP_SIZE_VALUE_ID, TIMELINE_VALUE_ID};
170
+ {CPU_TIME_VALUE_ID, CPU_SAMPLES_VALUE_ID, WALL_TIME_VALUE_ID, ALLOC_SAMPLES_VALUE_ID, ALLOC_SAMPLES_UNSCALED_VALUE_ID, HEAP_SAMPLES_VALUE_ID, HEAP_SIZE_VALUE_ID, TIMELINE_VALUE_ID};
169
171
 
170
172
  #define ALL_VALUE_TYPES_COUNT (sizeof(all_value_types) / sizeof(ddog_prof_ValueType))
171
173
 
@@ -429,7 +431,7 @@ static VALUE _native_initialize(
429
431
 
430
432
  uint8_t requested_values_count = ALL_VALUE_TYPES_COUNT -
431
433
  (cpu_time_enabled == Qtrue ? 0 : 1) -
432
- (alloc_samples_enabled == Qtrue? 0 : 1) -
434
+ (alloc_samples_enabled == Qtrue? 0 : 2) -
433
435
  (heap_samples_enabled == Qtrue ? 0 : 1) -
434
436
  (heap_size_enabled == Qtrue ? 0 : 1) -
435
437
  (timeline_enabled == Qtrue ? 0 : 1);
@@ -464,8 +466,12 @@ static VALUE _native_initialize(
464
466
  if (alloc_samples_enabled == Qtrue) {
465
467
  enabled_value_types[next_enabled_pos] = (ddog_prof_ValueType) ALLOC_SAMPLES_VALUE;
466
468
  state->position_for[ALLOC_SAMPLES_VALUE_ID] = next_enabled_pos++;
469
+
470
+ enabled_value_types[next_enabled_pos] = (ddog_prof_ValueType) ALLOC_SAMPLES_UNSCALED_VALUE;
471
+ state->position_for[ALLOC_SAMPLES_UNSCALED_VALUE_ID] = next_enabled_pos++;
467
472
  } else {
468
473
  state->position_for[ALLOC_SAMPLES_VALUE_ID] = next_disabled_pos++;
474
+ state->position_for[ALLOC_SAMPLES_UNSCALED_VALUE_ID] = next_disabled_pos++;
469
475
  }
470
476
 
471
477
  if (heap_samples_enabled == Qtrue) {
@@ -603,6 +609,7 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations,
603
609
  metric_values[position_for[CPU_SAMPLES_VALUE_ID]] = values.cpu_or_wall_samples;
604
610
  metric_values[position_for[WALL_TIME_VALUE_ID]] = values.wall_time_ns;
605
611
  metric_values[position_for[ALLOC_SAMPLES_VALUE_ID]] = values.alloc_samples;
612
+ metric_values[position_for[ALLOC_SAMPLES_UNSCALED_VALUE_ID]] = values.alloc_samples_unscaled;
606
613
  metric_values[position_for[TIMELINE_VALUE_ID]] = values.timeline_wall_time_ns;
607
614
 
608
615
  if (values.alloc_samples != 0) {
@@ -8,6 +8,7 @@ typedef struct {
8
8
  int64_t wall_time_ns;
9
9
  uint32_t cpu_or_wall_samples;
10
10
  uint32_t alloc_samples;
11
+ uint32_t alloc_samples_unscaled;
11
12
  int64_t timeline_wall_time_ns;
12
13
  } sample_values;
13
14
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../../tracing/contrib/rack/middlewares'
3
+ require_relative '../../../tracing/contrib'
4
4
 
5
5
  require_relative '../patcher'
6
6
  require_relative '../../response'
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'configuration'
4
+ require_relative '../core/configuration'
4
5
 
5
6
  module Datadog
6
7
  module AppSec
@@ -6,7 +6,7 @@ require_relative '../diagnostics/environment_logger'
6
6
  require_relative '../diagnostics/health'
7
7
  require_relative '../logger'
8
8
  require_relative '../runtime/metrics'
9
- require_relative '../telemetry/client'
9
+ require_relative '../telemetry/component'
10
10
  require_relative '../workers/runtime_metrics'
11
11
 
12
12
  require_relative '../remote/component'
@@ -62,9 +62,11 @@ module Datadog
62
62
  logger.debug { "Telemetry disabled. Agent network adapter not supported: #{agent_settings.adapter}" }
63
63
  end
64
64
 
65
- Telemetry::Client.new(
65
+ Telemetry::Component.new(
66
66
  enabled: enabled,
67
+ metrics_enabled: enabled && settings.telemetry.metrics_enabled,
67
68
  heartbeat_interval_seconds: settings.telemetry.heartbeat_interval_seconds,
69
+ metrics_aggregation_interval_seconds: settings.telemetry.metrics_aggregation_interval_seconds,
68
70
  dependency_collection: settings.telemetry.dependency_collection
69
71
  )
70
72
  end
@@ -169,8 +171,9 @@ module Datadog
169
171
  unused_statsd = (old_statsd - (old_statsd & new_statsd))
170
172
  unused_statsd.each(&:close)
171
173
 
172
- telemetry.stop!
174
+ # enqueue closing event before stopping telemetry so it will be send out on shutdown
173
175
  telemetry.emit_closing! unless replacement
176
+ telemetry.stop!
174
177
  end
175
178
  end
176
179
  end
@@ -397,6 +397,22 @@ module Datadog
397
397
  end
398
398
  end
399
399
 
400
+ # The profiler gathers data by sending `SIGPROF` unix signals to Ruby application threads.
401
+ #
402
+ # We've discovered that this can trigger a bug in a number of Ruby APIs in the `Dir` class, as
403
+ # described in https://github.com/DataDog/dd-trace-rb/issues/3450 . This workaround prevents the issue
404
+ # from happening by monkey patching the affected APIs.
405
+ #
406
+ # (In the future, once a fix lands upstream, we'll disable this workaround for Rubies that don't need it)
407
+ #
408
+ # @default `DD_PROFILING_DIR_INTERRUPTION_WORKAROUND_ENABLED` environment variable as a boolean,
409
+ # otherwise `true`
410
+ option :dir_interruption_workaround_enabled do |o|
411
+ o.env 'DD_PROFILING_DIR_INTERRUPTION_WORKAROUND_ENABLED'
412
+ o.type :bool
413
+ o.default true
414
+ end
415
+
400
416
  # Configures how much wall-time overhead the profiler targets. The profiler will dynamically adjust the
401
417
  # interval between samples it takes so as to try and maintain the property that it spends no longer than
402
418
  # this amount of wall-clock time profiling. For example, with the default value of 2%, the profiler will
@@ -647,6 +663,16 @@ module Datadog
647
663
  o.type :bool
648
664
  end
649
665
 
666
+ # Enable metrics collection for telemetry. Metrics collection only works when telemetry is enabled and
667
+ # metrics are enabled.
668
+ # @default `DD_TELEMETRY_METRICS_ENABLED` environment variable, otherwise `true`.
669
+ # @return [Boolean]
670
+ option :metrics_enabled do |o|
671
+ o.type :bool
672
+ o.env Core::Telemetry::Ext::ENV_METRICS_ENABLED
673
+ o.default true
674
+ end
675
+
650
676
  # The interval in seconds when telemetry must be sent.
651
677
  #
652
678
  # This method is used internally, for testing purposes only.
@@ -660,6 +686,19 @@ module Datadog
660
686
  o.default 60.0
661
687
  end
662
688
 
689
+ # The interval in seconds when telemetry metrics are aggregated.
690
+ # Should be a denominator of `heartbeat_interval_seconds`.
691
+ #
692
+ # This method is used internally, for testing purposes only.
693
+ # @default `DD_TELEMETRY_METRICS_AGGREGATION_INTERVAL` environment variable, otherwise `10`.
694
+ # @return [Float]
695
+ # @!visibility private
696
+ option :metrics_aggregation_interval_seconds do |o|
697
+ o.type :float
698
+ o.env Core::Telemetry::Ext::ENV_METRICS_AGGREGATION_INTERVAL
699
+ o.default 10.0
700
+ end
701
+
663
702
  # The install id of the application.
664
703
  #
665
704
  # This method is used internally, by library injection.
@@ -84,23 +84,16 @@ module Datadog
84
84
  configuration = self.configuration
85
85
  yield(configuration)
86
86
 
87
- built_components = false
88
-
89
- components = safely_synchronize do |write_components|
87
+ safely_synchronize do |write_components|
90
88
  write_components.call(
91
89
  if components?
92
90
  replace_components!(configuration, @components)
93
91
  else
94
- components = build_components(configuration)
95
- built_components = true
96
- components
92
+ build_components(configuration)
97
93
  end
98
94
  )
99
95
  end
100
96
 
101
- # Should only be called the first time components are built
102
- components.telemetry.started! if built_components
103
-
104
97
  configuration
105
98
  end
106
99
 
@@ -200,20 +193,13 @@ module Datadog
200
193
  current_components = COMPONENTS_READ_LOCK.synchronize { defined?(@components) && @components }
201
194
  return current_components if current_components || !allow_initialization
202
195
 
203
- built_components = false
204
-
205
- components = safely_synchronize do |write_components|
196
+ safely_synchronize do |write_components|
206
197
  if defined?(@components) && @components
207
198
  @components
208
199
  else
209
- built_components = true
210
200
  write_components.call(build_components(configuration))
211
201
  end
212
202
  end
213
-
214
- # Should only be called the first time components are built
215
- components&.telemetry&.started! if built_components
216
- components
217
203
  end
218
204
 
219
205
  private
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Core
5
+ # Contains behavior for handling deprecated functions in the codebase.
6
+ module Deprecations
7
+ # Records the occurrence of a deprecated operation in this library.
8
+ #
9
+ # Currently, these operations are logged to `Datadog.logger` at `warn` level.
10
+ #
11
+ # `disallowed_next_major` adds a message informing that the deprecated operation
12
+ # won't be allowed in the next major release.
13
+ #
14
+ # @yieldreturn [String] a String with the lazily evaluated deprecation message.
15
+ # @param [Boolean] disallowed_next_major whether this deprecation will be enforced in the next major release.
16
+ # @param [Object] key A unique key for the deprecation. Only the first message with the same key will be logged.
17
+ def log_deprecation(disallowed_next_major: true, key: nil)
18
+ return unless log_deprecation?(key)
19
+
20
+ Datadog.logger.warn do
21
+ message = yield
22
+ message += ' This will be enforced in the next major release.' if disallowed_next_major
23
+ message
24
+ end
25
+
26
+ # Track the deprecation being logged.
27
+ deprecation_logged!(key)
28
+
29
+ nil
30
+ end
31
+
32
+ private
33
+
34
+ # Determines whether a deprecation message should be logged.
35
+ #
36
+ # Internal use only.
37
+ def log_deprecation?(key)
38
+ return true if key.nil?
39
+
40
+ # Only allow a deprecation to be logged once.
41
+ !logged_deprecations.key?(key)
42
+ end
43
+
44
+ def deprecation_logged!(key)
45
+ return if key.nil?
46
+
47
+ logged_deprecations[key] += 1
48
+ end
49
+
50
+ # Tracks what deprecation warnings have already been logged
51
+ #
52
+ # Internal use only.
53
+ def logged_deprecations
54
+ @logged_deprecations ||= Hash.new(0)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -47,6 +47,11 @@ module Datadog
47
47
  ::RubyVM::YJIT.runtime_stats[:object_shape_count]
48
48
  end
49
49
 
50
+ # Size of memory Rust allocated for metadata
51
+ def yjit_alloc_size
52
+ ::RubyVM::YJIT.runtime_stats[:yjit_alloc_size]
53
+ end
54
+
50
55
  def available?
51
56
  defined?(::RubyVM::YJIT) \
52
57
  && ::RubyVM::YJIT.enabled? \
@@ -30,6 +30,7 @@ module Datadog
30
30
  METRIC_YJIT_LIVE_PAGE_COUNT = 'runtime.ruby.yjit.live_page_count'
31
31
  METRIC_YJIT_OBJECT_SHAPE_COUNT = 'runtime.ruby.yjit.object_shape_count'
32
32
  METRIC_YJIT_OUTLINED_CODE_SIZE = 'runtime.ruby.yjit.outlined_code_size'
33
+ METRIC_YJIT_YJIT_ALLOC_SIZE = 'runtime.ruby.yjit.yjit_alloc_size'
33
34
 
34
35
  TAG_SERVICE = 'service'
35
36
  end
@@ -140,6 +140,7 @@ module Datadog
140
140
  gauge(metric_name, metric_value) if metric_value
141
141
  end
142
142
 
143
+ # rubocop:disable Metrics/MethodLength
143
144
  def flush_yjit_stats
144
145
  # Only on Ruby >= 3.2
145
146
  try_flush do
@@ -176,9 +177,14 @@ module Datadog
176
177
  Core::Runtime::Ext::Metrics::METRIC_YJIT_OUTLINED_CODE_SIZE,
177
178
  Core::Environment::YJIT.outlined_code_size
178
179
  )
180
+ gauge_if_not_nil(
181
+ Core::Runtime::Ext::Metrics::METRIC_YJIT_YJIT_ALLOC_SIZE,
182
+ Core::Environment::YJIT.yjit_alloc_size
183
+ )
179
184
  end
180
185
  end
181
186
  end
187
+ # rubocop:enable Metrics/MethodLength
182
188
  end
183
189
  end
184
190
  end