ddtrace 1.17.0 → 1.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -2
  3. data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +3 -0
  4. data/ext/ddtrace_profiling_native_extension/collectors_thread_context.c +8 -1
  5. data/ext/ddtrace_profiling_native_extension/extconf.rb +28 -10
  6. data/ext/ddtrace_profiling_native_extension/http_transport.c +5 -2
  7. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +78 -18
  8. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +6 -0
  9. data/ext/ddtrace_profiling_native_extension/profiling.c +1 -0
  10. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +1 -3
  11. data/lib/datadog/appsec/component.rb +4 -1
  12. data/lib/datadog/appsec/configuration/settings.rb +4 -0
  13. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +2 -0
  14. data/lib/datadog/appsec/processor/rule_loader.rb +60 -0
  15. data/lib/datadog/appsec/remote.rb +12 -9
  16. data/lib/datadog/core/configuration.rb +4 -0
  17. data/lib/datadog/core/remote/worker.rb +1 -0
  18. data/lib/datadog/core/workers/async.rb +1 -0
  19. data/lib/datadog/kit/enable_core_dumps.rb +5 -6
  20. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -0
  21. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -0
  22. data/lib/datadog/profiling/component.rb +13 -5
  23. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
  24. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +24 -0
  25. data/lib/datadog/tracing/workers.rb +1 -0
  26. data/lib/ddtrace/version.rb +1 -1
  27. metadata +7 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2da890705f074bf11ffaa1b5099472a4695d318fc1a1a98238d61a061d6582ec
4
- data.tar.gz: 8c6db62249ee456939c3eacdeddb0f985148e9daa78adb844144d8da1191e8bb
3
+ metadata.gz: 69e775ab06a83ce14114a7287056e3d3fb575191b7ff6ccdc5c7b33f7fd58172
4
+ data.tar.gz: 13b607a4e29e516be4988dca7827eca09b79e968cf98e0641a155039d2ec3273
5
5
  SHA512:
6
- metadata.gz: a70b6856159a46f30a4ff67bc672011bb016b2aa7b1562834957a325eee90be681f9d0e153ef94c97a4ce675f7b63ac9f5a68f5573b4b9144ee40cf89a70395f
7
- data.tar.gz: 0d8d1a5d2ee255e53daab1cea7e82ec6f61c8e4a67b89ab1293853786d634c6cde2407a1ea0ceb4ff8bbb3391940dec5bdcfc56dd4b55b7b3826e35bdcff84eb
6
+ metadata.gz: d345e07c8b0a654974c51a7457b3fc6d3d7eb99226cfc5555d6bc8ee3e65b17b3782b1e582591be925297c09dd104108007b2081e28ee43c103f8f2fec3ffe5b
7
+ data.tar.gz: '085ea801f5fae16ed58cd79bab86839cd1aa23fa09261b39219264f603455633e19dbb8f60d647e407c7218d7d00052ef7b593405ec5af828af56ffecd58227d'
data/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.18.0] - 2023-12-07
6
+
7
+ ### Added
8
+
9
+ * Tracing: Support lib injection for ARM64 architecture ([#3307][])
10
+ * Tracing: Add `error_handler` for `pg` instrumentation ([#3303][])
11
+ * Appsec: Enable "Trusted IPs", a.k.a passlist with optional monitoring ([#3229][])
12
+
13
+ ### Changed
14
+
15
+ * Mark ddtrace threads as fork-safe ([#3279][])
16
+ * Bump `datadog-ci` dependency to 0.5.0 ([#3308][])
17
+ * Bump `debase-ruby_core_source` dependency to 3.2.3 ([#3284][])
18
+ * Profiling: Disable profiler on Ruby 3.3 when running with RUBY_MN_THREADS=1 ([#3259][])
19
+ * Profiling: Run without "no signals" workaround on passenger 6.0.19+ ([#3280][])
20
+
21
+ ### Fixed
22
+
23
+ * Tracing: Fix `pg` instrumentation `enabled` settings ([#3271][])
24
+ * Profiling: Fix potential crash by importing upstream `rb_profile_frames` fix ([#3289][])
25
+ * Appsec: Call `devise` RegistrationsController block ([#3286][])
26
+
5
27
  ## [1.17.0] - 2023-11-22
6
28
 
7
29
  For W3C Trace Context, this release adds [`tracecontext`](https://www.w3.org/TR/trace-context/) to the default trace propagation extraction and injection styles. The new defaults are:
@@ -23,7 +45,7 @@ For CI Visibility, you can now manually create CI traces and spans with the [new
23
45
  ### Changed
24
46
 
25
47
  * Tracing: Set 128-bit trace_id to true by default ([#3266][])
26
- * Tracing: Default trace propagation styles to `Datadog,b3multi,b3,tracecontext` ([#3248][],#3267)
48
+ * Tracing: Default trace propagation styles to `Datadog,b3multi,b3,tracecontext` ([#3248][],[#3267][])
27
49
  * Ci-App: Upgraded `datadog-ci` dependency to 0.4 ([#3270][])
28
50
 
29
51
  ## [1.16.2] - 2023-11-10
@@ -2658,7 +2680,8 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
2658
2680
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
2659
2681
 
2660
2682
 
2661
- [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v1.17.0...master
2683
+ [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v1.18.0...master
2684
+ [1.18.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.17.0...v1.18.0
2662
2685
  [1.17.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.16.2...v1.17.0
2663
2686
  [1.16.2]: https://github.com/DataDog/dd-trace-rb/compare/v1.16.1...v1.16.2
2664
2687
  [1.16.1]: https://github.com/DataDog/dd-trace-rb/compare/v1.16.0...v1.16.1
@@ -3870,6 +3893,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3870
3893
  [#3206]: https://github.com/DataDog/dd-trace-rb/issues/3206
3871
3894
  [#3207]: https://github.com/DataDog/dd-trace-rb/issues/3207
3872
3895
  [#3223]: https://github.com/DataDog/dd-trace-rb/issues/3223
3896
+ [#3229]: https://github.com/DataDog/dd-trace-rb/issues/3229
3873
3897
  [#3234]: https://github.com/DataDog/dd-trace-rb/issues/3234
3874
3898
  [#3235]: https://github.com/DataDog/dd-trace-rb/issues/3235
3875
3899
  [#3240]: https://github.com/DataDog/dd-trace-rb/issues/3240
@@ -3877,11 +3901,21 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3877
3901
  [#3248]: https://github.com/DataDog/dd-trace-rb/issues/3248
3878
3902
  [#3252]: https://github.com/DataDog/dd-trace-rb/issues/3252
3879
3903
  [#3255]: https://github.com/DataDog/dd-trace-rb/issues/3255
3904
+ [#3259]: https://github.com/DataDog/dd-trace-rb/issues/3259
3880
3905
  [#3262]: https://github.com/DataDog/dd-trace-rb/issues/3262
3881
3906
  [#3266]: https://github.com/DataDog/dd-trace-rb/issues/3266
3882
3907
  [#3267]: https://github.com/DataDog/dd-trace-rb/issues/3267
3883
3908
  [#3270]: https://github.com/DataDog/dd-trace-rb/issues/3270
3909
+ [#3271]: https://github.com/DataDog/dd-trace-rb/issues/3271
3884
3910
  [#3273]: https://github.com/DataDog/dd-trace-rb/issues/3273
3911
+ [#3279]: https://github.com/DataDog/dd-trace-rb/issues/3279
3912
+ [#3280]: https://github.com/DataDog/dd-trace-rb/issues/3280
3913
+ [#3284]: https://github.com/DataDog/dd-trace-rb/issues/3284
3914
+ [#3286]: https://github.com/DataDog/dd-trace-rb/issues/3286
3915
+ [#3289]: https://github.com/DataDog/dd-trace-rb/issues/3289
3916
+ [#3303]: https://github.com/DataDog/dd-trace-rb/issues/3303
3917
+ [#3307]: https://github.com/DataDog/dd-trace-rb/issues/3307
3918
+ [#3308]: https://github.com/DataDog/dd-trace-rb/issues/3308
3885
3919
  [@AdrianLC]: https://github.com/AdrianLC
3886
3920
  [@Azure7111]: https://github.com/Azure7111
3887
3921
  [@BabyGroot]: https://github.com/BabyGroot
@@ -25,6 +25,9 @@ void self_test_clock_id(void) {
25
25
  // Safety: This function is assumed never to raise exceptions by callers
26
26
  thread_cpu_time_id thread_cpu_time_id_for(VALUE thread) {
27
27
  rb_nativethread_id_t thread_id = pthread_id_for(thread);
28
+
29
+ if (thread_id == 0) return (thread_cpu_time_id) {.valid = false};
30
+
28
31
  clockid_t clock_id;
29
32
 
30
33
  int error = pthread_getcpuclockid(thread_id, &clock_id);
@@ -1150,6 +1150,7 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
1150
1150
  // Since this is stack allocated, be careful about moving it
1151
1151
  ddog_CharSlice class_name;
1152
1152
  ddog_CharSlice *optional_class_name = NULL;
1153
+ char imemo_type[100];
1153
1154
 
1154
1155
  if (state->allocation_type_enabled) {
1155
1156
  optional_class_name = &class_name;
@@ -1197,7 +1198,13 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
1197
1198
  class_name = ruby_value_type_to_class_name(type);
1198
1199
  }
1199
1200
  } else if (type == RUBY_T_IMEMO) {
1200
- class_name = DDOG_CHARSLICE_C("(VM Internal, T_IMEMO)");
1201
+ const char *imemo_string = imemo_kind(new_object);
1202
+ if (imemo_string != NULL) {
1203
+ snprintf(imemo_type, 100, "(VM Internal, T_IMEMO, %s)", imemo_string);
1204
+ class_name = (ddog_CharSlice) {.ptr = imemo_type, .len = strlen(imemo_type)};
1205
+ } else { // Ruby < 3
1206
+ class_name = DDOG_CHARSLICE_C("(VM Internal, T_IMEMO)");
1207
+ }
1201
1208
  } else {
1202
1209
  class_name = ruby_vm_type; // For other weird internal things we just use the VM type
1203
1210
  }
@@ -81,10 +81,7 @@ end
81
81
 
82
82
  # Because we can't control what compiler versions our customers use, shipping with -Werror by default is a no-go.
83
83
  # But we can enable it in CI, so that we quickly spot any new warnings that just got introduced.
84
- #
85
- # @ivoanjo TODO: 3.3.0-preview releases are causing issues in CI because `have_header('vm_core.h')` below triggers warnings;
86
- # I've chosen to disable `-Werror` for this Ruby version for now, and we can revisit this on a later 3.3 release.
87
- add_compiler_flag '-Werror' if ENV['DDTRACE_CI'] == 'true' && !RUBY_DESCRIPTION.include?('3.3.0preview')
84
+ add_compiler_flag '-Werror' if ENV['DDTRACE_CI'] == 'true'
88
85
 
89
86
  # Older gcc releases may not default to C99 and we need to ask for this. This is also used:
90
87
  # * by upstream Ruby -- search for gnu99 in the codebase
@@ -102,9 +99,6 @@ add_compiler_flag '-Wno-declaration-after-statement'
102
99
  # cause a segfault later. Let's ensure that never happens.
103
100
  add_compiler_flag '-Werror-implicit-function-declaration'
104
101
 
105
- # Warn on unused parameters to functions. Use `DDTRACE_UNUSED` to mark things as known-to-not-be-used.
106
- add_compiler_flag '-Wunused-parameter'
107
-
108
102
  # The native extension is not intended to expose any symbols/functions for other native libraries to use;
109
103
  # the sole exception being `Init_ddtrace_profiling_native_extension` which needs to be visible for Ruby to call it when
110
104
  # it `dlopen`s the library.
@@ -131,6 +125,9 @@ if RUBY_PLATFORM.include?('linux')
131
125
  $defs << '-DHAVE_PTHREAD_GETCPUCLOCKID'
132
126
  end
133
127
 
128
+ # On older Rubies, M:N threads were not available
129
+ $defs << '-DNO_MN_THREADS_AVAILABLE' if RUBY_VERSION < '3.3'
130
+
134
131
  # On older Rubies, we did not need to include the ractor header (this was built into the MJIT header)
135
132
  $defs << '-DNO_RACTOR_HEADER_INCLUDE' if RUBY_VERSION < '3.3'
136
133
 
@@ -152,12 +149,18 @@ $defs << '-DNO_PRIMITIVE_POP' if RUBY_VERSION < '3.2'
152
149
  # On older Rubies, there was no tid member in the internal thread structure
153
150
  $defs << '-DNO_THREAD_TID' if RUBY_VERSION < '3.1'
154
151
 
152
+ # On older Rubies, there was no jit_return member on the rb_control_frame_t struct
153
+ $defs << '-DNO_JIT_RETURN' if RUBY_VERSION < '3.1'
154
+
155
155
  # On older Rubies, we need to use a backported version of this function. See private_vm_api_access.h for details.
156
156
  $defs << '-DUSE_BACKPORTED_RB_PROFILE_FRAME_METHOD_NAME' if RUBY_VERSION < '3'
157
157
 
158
158
  # On older Rubies, there are no Ractors
159
159
  $defs << '-DNO_RACTORS' if RUBY_VERSION < '3'
160
160
 
161
+ # On older Rubies, rb_imemo_name did not exist
162
+ $defs << '-DNO_IMEMO_NAME' if RUBY_VERSION < '3'
163
+
161
164
  # On older Rubies, objects would not move
162
165
  $defs << '-DNO_T_MOVED' if RUBY_VERSION < '2.7'
163
166
 
@@ -238,6 +241,10 @@ if Datadog::Profiling::NativeExtensionHelpers::CAN_USE_MJIT_HEADER
238
241
  # NOTE: This needs to come after all changes to $defs
239
242
  create_header
240
243
 
244
+ # Warn on unused parameters to functions. Use `DDTRACE_UNUSED` to mark things as known-to-not-be-used.
245
+ # See the comment on the same flag below for why this is done last.
246
+ add_compiler_flag '-Wunused-parameter'
247
+
241
248
  create_makefile EXTENSION_NAME
242
249
  else
243
250
  # The MJIT header was introduced on 2.6 and removed on 3.3; for other Rubies we rely on
@@ -253,9 +260,20 @@ else
253
260
  Debase::RubyCoreSource
254
261
  .create_makefile_with_core(
255
262
  proc do
256
- have_header('vm_core.h') &&
257
- have_header('iseq.h') &&
258
- (RUBY_VERSION < '3.3' || have_header('ractor_core.h'))
263
+ headers_available =
264
+ have_header('vm_core.h') &&
265
+ have_header('iseq.h') &&
266
+ (RUBY_VERSION < '3.3' || have_header('ractor_core.h'))
267
+
268
+ if headers_available
269
+ # Warn on unused parameters to functions. Use `DDTRACE_UNUSED` to mark things as known-to-not-be-used.
270
+ # This is added as late as possible because in some Rubies we support (e.g. 3.3), adding this flag before
271
+ # checking if internal VM headers are available causes those checks to fail because of this warning (and not
272
+ # because the headers are not available.)
273
+ add_compiler_flag '-Wunused-parameter'
274
+ end
275
+
276
+ headers_available
259
277
  end,
260
278
  EXTENSION_NAME,
261
279
  )
@@ -16,7 +16,6 @@ static ID agent_id; // id of :agent in Ruby
16
16
 
17
17
  static ID log_failure_to_process_tag_id; // id of :log_failure_to_process_tag in Ruby
18
18
 
19
- static VALUE http_transport_class = Qnil;
20
19
  static VALUE library_version_string = Qnil;
21
20
 
22
21
  struct call_exporter_without_gvl_arguments {
@@ -54,7 +53,7 @@ static void interrupt_exporter_call(void *cancel_token);
54
53
  static VALUE ddtrace_version(void);
55
54
 
56
55
  void http_transport_init(VALUE profiling_module) {
57
- http_transport_class = rb_define_class_under(profiling_module, "HttpTransport", rb_cObject);
56
+ VALUE http_transport_class = rb_define_class_under(profiling_module, "HttpTransport", rb_cObject);
58
57
 
59
58
  rb_define_singleton_method(http_transport_class, "_native_validate_exporter", _native_validate_exporter, 1);
60
59
  rb_define_singleton_method(http_transport_class, "_native_do_export", _native_do_export, 12);
@@ -180,6 +179,10 @@ static ddog_Vec_Tag convert_tags(VALUE tags_as_array) {
180
179
  }
181
180
 
182
181
  static VALUE log_failure_to_process_tag(VALUE err_details) {
182
+ VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
183
+ VALUE profiling_module = rb_const_get(datadog_module, rb_intern("Profiling"));
184
+ VALUE http_transport_class = rb_const_get(profiling_module, rb_intern("HttpTransport"));
185
+
183
186
  return rb_funcall(http_transport_class, log_failure_to_process_tag_id, 1, err_details);
184
187
  }
185
188
 
@@ -58,9 +58,12 @@ static inline rb_thread_t *thread_struct_from_object(VALUE thread) {
58
58
  }
59
59
 
60
60
  rb_nativethread_id_t pthread_id_for(VALUE thread) {
61
- // struct rb_native_thread was introduced in Ruby 3.2 (preview2): https://github.com/ruby/ruby/pull/5836
61
+ // struct rb_native_thread was introduced in Ruby 3.2: https://github.com/ruby/ruby/pull/5836
62
62
  #ifndef NO_RB_NATIVE_THREAD
63
- return thread_struct_from_object(thread)->nt->thread_id;
63
+ struct rb_native_thread* native_thread = thread_struct_from_object(thread)->nt;
64
+ // This can be NULL on Ruby 3.3 with MN threads (RUBY_MN_THREADS=1)
65
+ if (native_thread == NULL) return 0;
66
+ return native_thread->thread_id;
64
67
  #else
65
68
  return thread_struct_from_object(thread)->thread_id;
66
69
  #endif
@@ -113,15 +116,16 @@ bool is_current_thread_holding_the_gvl(void) {
113
116
 
114
117
  if (current_owner == NULL) return (current_gvl_owner) {.valid = false};
115
118
 
116
- return (current_gvl_owner) {
117
- .valid = true,
118
- .owner =
119
- #ifndef NO_RB_NATIVE_THREAD
120
- current_owner->nt->thread_id
121
- #else
122
- current_owner->thread_id
123
- #endif
124
- };
119
+ #ifndef NO_RB_NATIVE_THREAD
120
+ struct rb_native_thread* current_owner_native_thread = current_owner->nt;
121
+
122
+ // This can be NULL on Ruby 3.3 with MN threads (RUBY_MN_THREADS=1)
123
+ if (current_owner_native_thread == NULL) return (current_gvl_owner) {.valid = false};
124
+
125
+ return (current_gvl_owner) {.valid = true, .owner = current_owner_native_thread->thread_id};
126
+ #else
127
+ return (current_gvl_owner) {.valid = true, .owner = current_owner->thread_id};
128
+ #endif
125
129
  }
126
130
  #else
127
131
  current_gvl_owner gvl_owner(void) {
@@ -182,7 +186,9 @@ uint64_t native_thread_id_for(VALUE thread) {
182
186
  // The tid is only available on Ruby >= 3.1 + Linux (and FreeBSD). It's the same as `gettid()` aka the task id as seen in /proc
183
187
  #if !defined(NO_THREAD_TID) && defined(RB_THREAD_T_HAS_NATIVE_ID)
184
188
  #ifndef NO_RB_NATIVE_THREAD
185
- return thread_struct_from_object(thread)->nt->tid;
189
+ struct rb_native_thread* native_thread = thread_struct_from_object(thread)->nt;
190
+ if (native_thread == NULL) rb_raise(rb_eRuntimeError, "BUG: rb_native_thread* is null. Is this Ruby running with RUBY_MN_THREADS=1?");
191
+ return native_thread->tid;
186
192
  #else
187
193
  return thread_struct_from_object(thread)->tid;
188
194
  #endif
@@ -407,6 +413,7 @@ calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
407
413
  // the `VALUE` returned by rb_profile_frames returns `(eval)` instead of the path of the file where the `eval`
408
414
  // was called from.
409
415
  // * Imported fix from https://github.com/ruby/ruby/pull/7116 to avoid sampling threads that are still being created
416
+ // * Imported fix from https://github.com/ruby/ruby/pull/8415 to avoid potential crash when using YJIT.
410
417
  //
411
418
  // What is rb_profile_frames?
412
419
  // `rb_profile_frames` is a Ruby VM debug API added for use by profilers for sampling the stack trace of a Ruby thread.
@@ -442,12 +449,15 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, VALUE *buff, i
442
449
  // Modified from upstream: Instead of using `GET_EC` to collect info from the current thread,
443
450
  // support sampling any thread (including the current) passed as an argument
444
451
  rb_thread_t *th = thread_struct_from_object(thread);
445
- #ifndef USE_THREAD_INSTEAD_OF_EXECUTION_CONTEXT // Modern Rubies
446
- const rb_execution_context_t *ec = th->ec;
447
- #else // Ruby < 2.5
448
- const rb_thread_t *ec = th;
449
- #endif
452
+ #ifndef USE_THREAD_INSTEAD_OF_EXECUTION_CONTEXT // Modern Rubies
453
+ const rb_execution_context_t *ec = th->ec;
454
+ #else // Ruby < 2.5
455
+ const rb_thread_t *ec = th;
456
+ #endif
450
457
  const rb_control_frame_t *cfp = ec->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
458
+ #ifndef NO_JIT_RETURN
459
+ const rb_control_frame_t *top = cfp;
460
+ #endif
451
461
  const rb_callable_method_entry_t *cme;
452
462
 
453
463
  // Avoid sampling dead threads
@@ -522,7 +532,20 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, VALUE *buff, i
522
532
  buff[i] = (VALUE)cfp->iseq;
523
533
  }
524
534
 
525
- lines[i] = calc_lineno(cfp->iseq, cfp->pc);
535
+ // The topmost frame may not have an updated PC because the JIT
536
+ // may not have set one. The JIT compiler will update the PC
537
+ // before entering a new function (so that `caller` will work),
538
+ // so only the topmost frame could possibly have an out of date PC
539
+ #ifndef NO_JIT_RETURN
540
+ if (cfp == top && cfp->jit_return) {
541
+ lines[i] = 0;
542
+ } else {
543
+ lines[i] = calc_lineno(cfp->iseq, cfp->pc);
544
+ }
545
+ #else // Ruby < 3.1
546
+ lines[i] = calc_lineno(cfp->iseq, cfp->pc);
547
+ #endif
548
+
526
549
  is_ruby_frame[i] = true;
527
550
  i++;
528
551
  }
@@ -811,3 +834,40 @@ VALUE invoke_location_for(VALUE thread, int *line_location) {
811
834
  *line_location = NUM2INT(rb_iseq_first_lineno(iseq));
812
835
  return rb_iseq_path(iseq);
813
836
  }
837
+
838
+ void self_test_mn_enabled(void) {
839
+ #ifdef NO_MN_THREADS_AVAILABLE
840
+ return;
841
+ #else
842
+ if (ddtrace_get_ractor()->threads.sched.enable_mn_threads == true) {
843
+ rb_raise(rb_eRuntimeError, "Ruby VM is running with RUBY_MN_THREADS=1. This is not yet supported");
844
+ }
845
+ #endif
846
+ }
847
+
848
+ // Taken from upstream imemo.h at commit 6ebcf25de2859b5b6402b7e8b181066c32d0e0bf (November 2023, master branch)
849
+ // (See the Ruby project copyright and license above)
850
+ // to enable calling rb_imemo_name
851
+ //
852
+ // Modifications:
853
+ // * Added IMEMO_MASK define
854
+ // * Changed return type to int to avoid having to define `enum imemo_type`
855
+ static inline int ddtrace_imemo_type(VALUE imemo) {
856
+ // This mask is the same between Ruby 2.5 and 3.3-preview3. Furthermore, the intention of this method is to be used
857
+ // to call `rb_imemo_name` which correctly handles invalid numbers so even if the mask changes in the future, at most
858
+ // we'll get incorrect results (and never a VM crash)
859
+ #define IMEMO_MASK 0x0f
860
+ return (RBASIC(imemo)->flags >> FL_USHIFT) & IMEMO_MASK;
861
+ }
862
+
863
+ // Safety: This function assumes the object passed in is of the imemo type. But in the worst case, you'll just get
864
+ // a string that doesn't make any sense.
865
+ #ifndef NO_IMEMO_NAME
866
+ const char *imemo_kind(VALUE imemo) {
867
+ return rb_imemo_name(ddtrace_imemo_type(imemo));
868
+ }
869
+ #else
870
+ const char *imemo_kind(__attribute__((unused)) VALUE imemo) {
871
+ return NULL;
872
+ }
873
+ #endif
@@ -49,3 +49,9 @@ bool ddtrace_rb_ractor_main_p(void);
49
49
  // This is what Ruby shows in `Thread#to_s`.
50
50
  // The file is returned directly, and the line is recorded onto *line_location.
51
51
  VALUE invoke_location_for(VALUE thread, int *line_location);
52
+
53
+ // Check if RUBY_MN_THREADS is enabled (aka main Ractor is not doing 1:1 threads)
54
+ void self_test_mn_enabled(void);
55
+
56
+ // Provides more specific information on what kind an imemo is
57
+ const char *imemo_kind(VALUE imemo);
@@ -68,6 +68,7 @@ void DDTRACE_EXPORT Init_ddtrace_profiling_native_extension(void) {
68
68
 
69
69
  static VALUE native_working_p(DDTRACE_UNUSED VALUE _self) {
70
70
  self_test_clock_id();
71
+ self_test_mn_enabled();
71
72
 
72
73
  return Qtrue;
73
74
  }
@@ -129,8 +129,6 @@
129
129
  static VALUE ok_symbol = Qnil; // :ok in Ruby
130
130
  static VALUE error_symbol = Qnil; // :error in Ruby
131
131
 
132
- static VALUE stack_recorder_class = Qnil;
133
-
134
132
  // Note: Please DO NOT use `VALUE_STRING` anywhere else, instead use `DDOG_CHARSLICE_C`.
135
133
  // `VALUE_STRING` is only needed because older versions of gcc (4.9.2, used in our Ruby 2.2 CI test images)
136
134
  // tripped when compiling `enabled_value_types` using `-std=gnu99` due to the extra cast that is included in
@@ -217,7 +215,7 @@ static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_
217
215
  static void reset_profile(ddog_prof_Profile *profile, ddog_Timespec *start_time /* Can be null */);
218
216
 
219
217
  void stack_recorder_init(VALUE profiling_module) {
220
- stack_recorder_class = rb_define_class_under(profiling_module, "StackRecorder", rb_cObject);
218
+ VALUE stack_recorder_class = rb_define_class_under(profiling_module, "StackRecorder", rb_cObject);
221
219
  // Hosts methods used for testing the native code using RSpec
222
220
  VALUE testing_module = rb_define_module_under(stack_recorder_class, "Testing");
223
221
 
@@ -38,12 +38,15 @@ module Datadog
38
38
 
39
39
  data = AppSec::Processor::RuleLoader.load_data(
40
40
  ip_denylist: settings.appsec.ip_denylist,
41
- user_id_denylist: settings.appsec.user_id_denylist
41
+ user_id_denylist: settings.appsec.user_id_denylist,
42
42
  )
43
43
 
44
+ exclusions = AppSec::Processor::RuleLoader.load_exclusions(ip_passlist: settings.appsec.ip_passlist)
45
+
44
46
  ruleset = AppSec::Processor::RuleMerger.merge(
45
47
  rules: [rules],
46
48
  data: data,
49
+ exclusions: exclusions,
47
50
  )
48
51
 
49
52
  processor = Processor.new(ruleset: ruleset)
@@ -54,6 +54,10 @@ module Datadog
54
54
  o.default :recommended
55
55
  end
56
56
 
57
+ option :ip_passlist do |o|
58
+ o.default []
59
+ end
60
+
57
61
  option :ip_denylist do |o|
58
62
  o.type :array
59
63
  o.default []
@@ -42,6 +42,8 @@ module Datadog
42
42
  **event_information.to_h
43
43
  )
44
44
  end
45
+
46
+ yield resource if block_given?
45
47
  end
46
48
  end
47
49
  end
@@ -47,6 +47,13 @@ module Datadog
47
47
  data
48
48
  end
49
49
 
50
+ def load_exclusions(ip_passlist: [])
51
+ exclusions = []
52
+ exclusions << [passlist_exclusions(ip_passlist)] if ip_passlist.any?
53
+
54
+ exclusions
55
+ end
56
+
50
57
  private
51
58
 
52
59
  def denylist_data(id, denylist)
@@ -56,6 +63,59 @@ module Datadog
56
63
  'data' => denylist.map { |v| { 'value' => v.to_s, 'expiration' => 2**63 } }
57
64
  }
58
65
  end
66
+
67
+ def passlist_exclusions(ip_passlist) # rubocop:disable Metrics/MethodLength
68
+ case ip_passlist
69
+ when Array
70
+ pass = ip_passlist
71
+ monitor = []
72
+ when Hash
73
+ pass = ip_passlist[:pass]
74
+ monitor = ip_passlist[:monitor]
75
+ else
76
+ pass = []
77
+ monitor = []
78
+ end
79
+
80
+ exclusions = []
81
+
82
+ exclusions << {
83
+ 'conditions' => [
84
+ {
85
+ 'operator' => 'ip_match',
86
+ 'parameters' => {
87
+ 'inputs' => [
88
+ {
89
+ 'address' => 'http.client_ip'
90
+ }
91
+ ],
92
+ 'list' => pass
93
+ }
94
+ }
95
+ ],
96
+ 'id' => SecureRandom.uuid,
97
+ }
98
+
99
+ exclusions << {
100
+ 'conditions' => [
101
+ {
102
+ 'operator' => 'ip_match',
103
+ 'parameters' => {
104
+ 'inputs' => [
105
+ {
106
+ 'address' => 'http.client_ip'
107
+ }
108
+ ],
109
+ 'list' => monitor
110
+ }
111
+ }
112
+ ],
113
+ 'id' => SecureRandom.uuid,
114
+ 'on_match' => 'monitor'
115
+ }
116
+
117
+ exclusions
118
+ end
59
119
  end
60
120
  end
61
121
  end
@@ -12,15 +12,17 @@ module Datadog
12
12
  class NoRulesError < StandardError; end
13
13
 
14
14
  class << self
15
- CAP_ASM_ACTIVATION = 1 << 1 # Remote activation via ASM_FEATURES product
16
- CAP_ASM_IP_BLOCKING = 1 << 2 # accept IP blocking data from ASM_DATA product
17
- CAP_ASM_DD_RULES = 1 << 3 # read ASM rules from ASM_DD product
18
- CAP_ASM_EXCLUSIONS = 1 << 4 # exclusion filters (passlist) via ASM product
19
- CAP_ASM_REQUEST_BLOCKING = 1 << 5 # can block on request info
20
- CAP_ASM_RESPONSE_BLOCKING = 1 << 6 # can block on response info
21
- CAP_ASM_USER_BLOCKING = 1 << 7 # accept user blocking data from ASM_DATA product
22
- CAP_ASM_CUSTOM_RULES = 1 << 8 # accept custom rules
23
- CAP_ASM_CUSTOM_BLOCKING_RESPONSE = 1 << 9 # supports custom http code or redirect sa blocking response
15
+ CAP_ASM_RESERVED_1 = 1 << 0 # RESERVED
16
+ CAP_ASM_ACTIVATION = 1 << 1 # Remote activation via ASM_FEATURES product
17
+ CAP_ASM_IP_BLOCKING = 1 << 2 # accept IP blocking data from ASM_DATA product
18
+ CAP_ASM_DD_RULES = 1 << 3 # read ASM rules from ASM_DD product
19
+ CAP_ASM_EXCLUSIONS = 1 << 4 # exclusion filters (passlist) via ASM product
20
+ CAP_ASM_REQUEST_BLOCKING = 1 << 5 # can block on request info
21
+ CAP_ASM_RESPONSE_BLOCKING = 1 << 6 # can block on response info
22
+ CAP_ASM_USER_BLOCKING = 1 << 7 # accept user blocking data from ASM_DATA product
23
+ CAP_ASM_CUSTOM_RULES = 1 << 8 # accept custom rules
24
+ CAP_ASM_CUSTOM_BLOCKING_RESPONSE = 1 << 9 # supports custom http code or redirect sa blocking response
25
+ CAP_ASM_TRUSTED_IPS = 1 << 10 # supports trusted ip
24
26
 
25
27
  # TODO: we need to dynamically add CAP_ASM_ACTIVATION once we support it
26
28
  ASM_CAPABILITIES = [
@@ -32,6 +34,7 @@ module Datadog
32
34
  CAP_ASM_DD_RULES,
33
35
  CAP_ASM_CUSTOM_RULES,
34
36
  CAP_ASM_CUSTOM_BLOCKING_RESPONSE,
37
+ CAP_ASM_TRUSTED_IPS,
35
38
  ].freeze
36
39
 
37
40
  ASM_PRODUCTS = [
@@ -273,6 +273,10 @@ module Datadog
273
273
  def handle_interrupt_shutdown!
274
274
  logger = Datadog.logger
275
275
  shutdown_thread = Thread.new { shutdown! }
276
+ unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
277
+ shutdown_thread.name = Datadog::Core::Configuration.name
278
+ end
279
+
276
280
  print_message_treshold_seconds = 0.2
277
281
 
278
282
  slow_shutdown = shutdown_thread.join(print_message_treshold_seconds).nil?
@@ -30,6 +30,7 @@ module Datadog
30
30
 
31
31
  thread = Thread.new { poll(@interval) }
32
32
  thread.name = self.class.name unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
33
+ thread.thread_variable_set(:fork_safe, true)
33
34
  @thr = thread
34
35
 
35
36
  @started = true
@@ -147,6 +147,7 @@ module Datadog
147
147
  # rubocop:enable Lint/RescueException
148
148
  end
149
149
  @worker.name = self.class.name unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
150
+ @worker.thread_variable_set(:fork_safe, true)
150
151
 
151
152
  nil
152
153
  end
@@ -16,11 +16,13 @@ module Datadog
16
16
  '(Could not open /proc/sys/kernel/core_pattern)'
17
17
  end
18
18
 
19
+ enabled_status = "Maximum size: #{maximum_size} Output pattern: '#{core_pattern}'"
20
+
19
21
  if maximum_size <= 0
20
22
  Kernel.warn("[ddtrace] Could not enable core dumps on crash, maximum size is #{maximum_size} (disabled).")
21
23
  return
22
24
  elsif maximum_size == current_size
23
- Kernel.warn('[ddtrace] Core dumps already enabled, nothing to do!')
25
+ Kernel.warn("[ddtrace] Core dumps already enabled, nothing to do. #{enabled_status}")
24
26
  return
25
27
  end
26
28
 
@@ -35,12 +37,9 @@ module Datadog
35
37
  end
36
38
 
37
39
  if current_size == 0
38
- Kernel.warn("[ddtrace] Enabled core dumps. Maximum size: #{maximum_size} Output pattern: '#{core_pattern}'")
40
+ Kernel.warn("[ddtrace] Enabled core dumps. #{enabled_status}")
39
41
  else
40
- Kernel.warn(
41
- "[ddtrace] Raised core dump limit. Old size: #{current_size} " \
42
- "Maximum size: #{maximum_size} Output pattern: '#{core_pattern}'"
43
- )
42
+ Kernel.warn("[ddtrace] Raised core dump limit. Old size: #{current_size} #{enabled_status}")
44
43
  end
45
44
  end
46
45
  end
@@ -78,6 +78,7 @@ module Datadog
78
78
  end
79
79
  end
80
80
  @worker_thread.name = self.class.name # Repeated from above to make sure thread gets named asap
81
+ @worker_thread.thread_variable_set(:fork_safe, true)
81
82
  end
82
83
 
83
84
  true
@@ -43,6 +43,7 @@ module Datadog
43
43
  end
44
44
  end
45
45
  @worker_thread.name = self.class.name # Repeated from above to make sure thread gets named asap
46
+ @worker_thread.thread_variable_set(:fork_safe, true)
46
47
  end
47
48
 
48
49
  true
@@ -171,12 +171,11 @@ module Datadog
171
171
  return true
172
172
  end
173
173
 
174
- if defined?(::PhusionPassenger)
174
+ if (defined?(::PhusionPassenger) || Gem.loaded_specs['passenger']) && incompatible_passenger_version?
175
175
  Datadog.logger.warn(
176
- 'Enabling the profiling "no signals" workaround because the passenger web server is in use. ' \
177
- 'This is needed because passenger is currently incompatible with the normal working mode ' \
178
- 'of the profiler, as detailed in <https://github.com/DataDog/dd-trace-rb/issues/2976>. ' \
179
- 'Profiling data will have lower quality.'
176
+ 'Enabling the profiling "no signals" workaround because an incompatible version of the passenger gem is ' \
177
+ 'installed. Profiling data will have lower quality.' \
178
+ 'To fix this, upgrade the passenger gem to version 6.0.19 or above.'
180
179
  )
181
180
  return true
182
181
  end
@@ -237,6 +236,15 @@ module Datadog
237
236
  true
238
237
  end
239
238
  end
239
+
240
+ # See https://github.com/datadog/dd-trace-rb/issues/2976 for details.
241
+ private_class_method def self.incompatible_passenger_version?
242
+ if Gem.loaded_specs['passenger']
243
+ Gem.loaded_specs['passenger'].version < Gem::Version.new('6.0.19')
244
+ else
245
+ true
246
+ end
247
+ end
240
248
  end
241
249
  end
242
250
  end
@@ -25,6 +25,11 @@ module Datadog
25
25
  o.default false
26
26
  end
27
27
 
28
+ option :error_handler do |o|
29
+ o.type :proc
30
+ o.default_proc(&Tracing::SpanOperation::Events::DEFAULT_ON_ERROR)
31
+ end
32
+
28
33
  option :analytics_sample_rate do |o|
29
34
  o.type :float
30
35
  o.env Ext::ENV_ANALYTICS_SAMPLE_RATE
@@ -21,18 +21,24 @@ module Datadog
21
21
  # PG::Connection patch methods
22
22
  module InstanceMethods
23
23
  def exec(sql, *args, &block)
24
+ return super unless enabled?
25
+
24
26
  trace(Ext::SPAN_EXEC, sql: sql, block: block) do |sql_statement, wrapped_block|
25
27
  super(sql_statement, *args, &wrapped_block)
26
28
  end
27
29
  end
28
30
 
29
31
  def exec_params(sql, params, *args, &block)
32
+ return super unless enabled?
33
+
30
34
  trace(Ext::SPAN_EXEC_PARAMS, sql: sql, block: block) do |sql_statement, wrapped_block|
31
35
  super(sql_statement, params, *args, &wrapped_block)
32
36
  end
33
37
  end
34
38
 
35
39
  def exec_prepared(statement_name, params, *args, &block)
40
+ return super unless enabled?
41
+
36
42
  trace(Ext::SPAN_EXEC_PREPARED, statement_name: statement_name, block: block) do |_, wrapped_block|
37
43
  super(statement_name, params, *args, &wrapped_block)
38
44
  end
@@ -40,6 +46,8 @@ module Datadog
40
46
 
41
47
  # async_exec is an alias to exec
42
48
  def async_exec(sql, *args, &block)
49
+ return super unless enabled?
50
+
43
51
  trace(Ext::SPAN_ASYNC_EXEC, sql: sql, block: block) do |sql_statement, wrapped_block|
44
52
  super(sql_statement, *args, &wrapped_block)
45
53
  end
@@ -47,6 +55,8 @@ module Datadog
47
55
 
48
56
  # async_exec_params is an alias to exec_params
49
57
  def async_exec_params(sql, params, *args, &block)
58
+ return super unless enabled?
59
+
50
60
  trace(Ext::SPAN_ASYNC_EXEC_PARAMS, sql: sql, block: block) do |sql_statement, wrapped_block|
51
61
  super(sql_statement, params, *args, &wrapped_block)
52
62
  end
@@ -54,24 +64,32 @@ module Datadog
54
64
 
55
65
  # async_exec_prepared is an alias to exec_prepared
56
66
  def async_exec_prepared(statement_name, params, *args, &block)
67
+ return super unless enabled?
68
+
57
69
  trace(Ext::SPAN_ASYNC_EXEC_PREPARED, statement_name: statement_name, block: block) do |_, wrapped_block|
58
70
  super(statement_name, params, *args, &wrapped_block)
59
71
  end
60
72
  end
61
73
 
62
74
  def sync_exec(sql, *args, &block)
75
+ return super unless enabled?
76
+
63
77
  trace(Ext::SPAN_SYNC_EXEC, sql: sql, block: block) do |sql_statement, wrapped_block|
64
78
  super(sql_statement, *args, &wrapped_block)
65
79
  end
66
80
  end
67
81
 
68
82
  def sync_exec_params(sql, params, *args, &block)
83
+ return super unless enabled?
84
+
69
85
  trace(Ext::SPAN_SYNC_EXEC_PARAMS, sql: sql, block: block) do |sql_statement, wrapped_block|
70
86
  super(sql_statement, params, *args, &wrapped_block)
71
87
  end
72
88
  end
73
89
 
74
90
  def sync_exec_prepared(statement_name, params, *args, &block)
91
+ return super unless enabled?
92
+
75
93
  trace(Ext::SPAN_SYNC_EXEC_PREPARED, statement_name: statement_name, block: block) do |_, wrapped_block|
76
94
  super(statement_name, params, *args, &wrapped_block)
77
95
  end
@@ -81,10 +99,12 @@ module Datadog
81
99
 
82
100
  def trace(name, sql: nil, statement_name: nil, block: nil)
83
101
  service = Datadog.configuration_for(self, :service_name) || datadog_configuration[:service_name]
102
+ error_handler = datadog_configuration[:error_handler]
84
103
  resource = statement_name || sql
85
104
 
86
105
  Tracing.trace(
87
106
  name,
107
+ on_error: error_handler,
88
108
  service: service,
89
109
  resource: resource,
90
110
  type: Tracing::Metadata::Ext::SQL::TYPE
@@ -172,6 +192,10 @@ module Datadog
172
192
  def comment_propagation
173
193
  datadog_configuration[:comment_propagation]
174
194
  end
195
+
196
+ def enabled?
197
+ datadog_configuration[:enabled]
198
+ end
175
199
  end
176
200
  end
177
201
  end
@@ -69,6 +69,7 @@ module Datadog
69
69
  Datadog.logger.debug { "Starting thread for: #{self}" }
70
70
  @worker = Thread.new { perform }
71
71
  @worker.name = self.class.name unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
72
+ @worker.thread_variable_set(:fork_safe, true)
72
73
 
73
74
  nil
74
75
  end
@@ -3,7 +3,7 @@
3
3
  module DDTrace
4
4
  module VERSION
5
5
  MAJOR = 1
6
- MINOR = 17
6
+ MINOR = 18
7
7
  PATCH = 0
8
8
  PRE = nil
9
9
  BUILD = nil
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ddtrace
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.17.0
4
+ version: 1.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-22 00:00:00.000000000 Z
11
+ date: 2023-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 3.2.2
33
+ version: 3.2.3
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 3.2.2
40
+ version: 3.2.3
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: libddwaf
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.4.0
75
+ version: 0.5.0
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.4.0
82
+ version: 0.5.0
83
83
  description: |
84
84
  ddtrace is Datadog's tracing client for Ruby. It is used to trace requests
85
85
  as they flow across web servers, databases and microservices so that developers
@@ -890,7 +890,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
890
890
  - !ruby/object:Gem::Version
891
891
  version: 2.0.0
892
892
  requirements: []
893
- rubygems_version: 3.4.10
893
+ rubygems_version: 3.4.21
894
894
  signing_key:
895
895
  specification_version: 4
896
896
  summary: Datadog tracing code for your Ruby applications