datadog 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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