datadog 2.31.0 → 2.33.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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/ext/datadog_profiling_native_extension/clock_id.h +9 -1
  3. data/ext/datadog_profiling_native_extension/clock_id_from_mach.c +73 -0
  4. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -1
  5. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +17 -7
  6. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +16 -5
  7. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +6 -0
  8. data/ext/datadog_profiling_native_extension/extconf.rb +8 -4
  9. data/ext/datadog_profiling_native_extension/http_transport.c +10 -5
  10. data/ext/datadog_profiling_native_extension/stack_recorder.c +3 -9
  11. data/ext/datadog_profiling_native_extension/time_helpers.h +1 -0
  12. data/ext/libdatadog_api/crashtracker.c +2 -0
  13. data/ext/libdatadog_api/di.c +48 -0
  14. data/ext/libdatadog_api/extconf.rb +7 -4
  15. data/ext/libdatadog_extconf_helpers.rb +38 -1
  16. data/lib/datadog/ai_guard/autoload.rb +10 -0
  17. data/lib/datadog/ai_guard/component.rb +1 -1
  18. data/lib/datadog/ai_guard/configuration.rb +105 -2
  19. data/lib/datadog/ai_guard/contrib/auto_instrument.rb +24 -0
  20. data/lib/datadog/ai_guard/contrib/rack/integration.rb +42 -0
  21. data/lib/datadog/ai_guard/contrib/rack/patcher.rb +26 -0
  22. data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +83 -0
  23. data/lib/datadog/ai_guard/contrib/rails/integration.rb +41 -0
  24. data/lib/datadog/ai_guard/contrib/rails/patcher.rb +97 -0
  25. data/lib/datadog/ai_guard/evaluation.rb +2 -0
  26. data/lib/datadog/ai_guard/ext.rb +2 -0
  27. data/lib/datadog/ai_guard.rb +8 -0
  28. data/lib/datadog/appsec/autoload.rb +1 -1
  29. data/lib/datadog/appsec/component.rb +1 -1
  30. data/lib/datadog/appsec/configuration.rb +414 -1
  31. data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +75 -0
  32. data/lib/datadog/appsec/contrib/aws_lambda/integration.rb +39 -0
  33. data/lib/datadog/appsec/contrib/aws_lambda/patcher.rb +30 -0
  34. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +111 -0
  35. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +2 -1
  36. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  37. data/lib/datadog/appsec/contrib/rails/patcher.rb +2 -2
  38. data/lib/datadog/appsec/metrics/telemetry.rb +13 -1
  39. data/lib/datadog/appsec/security_engine/runner.rb +1 -1
  40. data/lib/datadog/appsec/trace_keeper.rb +18 -6
  41. data/lib/datadog/appsec/utils/http/url_encoded.rb +2 -2
  42. data/lib/datadog/appsec.rb +1 -0
  43. data/lib/datadog/core/configuration/components.rb +1 -1
  44. data/lib/datadog/core/configuration/settings.rb +13 -0
  45. data/lib/datadog/core/configuration/supported_configurations.rb +4 -0
  46. data/lib/datadog/core/configuration.rb +1 -1
  47. data/lib/datadog/core/contrib/rails/utils.rb +1 -1
  48. data/lib/datadog/core/crashtracking/component.rb +3 -3
  49. data/lib/datadog/core/diagnostics/environment_logger.rb +3 -1
  50. data/lib/datadog/core/environment/container.rb +2 -2
  51. data/lib/datadog/core/environment/ext.rb +1 -0
  52. data/lib/datadog/core/environment/socket.rb +13 -0
  53. data/lib/datadog/core/feature_flags.rb +1 -1
  54. data/lib/datadog/core/metrics/client.rb +5 -5
  55. data/lib/datadog/core/remote/client.rb +1 -1
  56. data/lib/datadog/core/remote/component.rb +2 -2
  57. data/lib/datadog/core/runtime/metrics.rb +1 -1
  58. data/lib/datadog/core/telemetry/emitter.rb +1 -1
  59. data/lib/datadog/core/telemetry/event/app_started.rb +2 -2
  60. data/lib/datadog/core/transport/http.rb +2 -0
  61. data/lib/datadog/core/utils.rb +1 -1
  62. data/lib/datadog/core/workers/async.rb +1 -1
  63. data/lib/datadog/core.rb +1 -1
  64. data/lib/datadog/data_streams/configuration.rb +40 -1
  65. data/lib/datadog/data_streams/pathway_context.rb +1 -1
  66. data/lib/datadog/data_streams/processor.rb +1 -1
  67. data/lib/datadog/data_streams.rb +1 -1
  68. data/lib/datadog/di/base.rb +8 -5
  69. data/lib/datadog/di/code_tracker.rb +179 -1
  70. data/lib/datadog/di/component.rb +1 -1
  71. data/lib/datadog/di/configuration.rb +235 -2
  72. data/lib/datadog/di/instrumenter.rb +46 -26
  73. data/lib/datadog/di/probe_builder.rb +1 -1
  74. data/lib/datadog/di/probe_file_loader.rb +2 -2
  75. data/lib/datadog/di/probe_manager.rb +6 -6
  76. data/lib/datadog/di/probe_notification_builder.rb +1 -1
  77. data/lib/datadog/di/probe_notifier_worker.rb +2 -2
  78. data/lib/datadog/di/remote.rb +6 -6
  79. data/lib/datadog/di/serializer.rb +1 -1
  80. data/lib/datadog/di/transport/input.rb +3 -3
  81. data/lib/datadog/error_tracking/configuration.rb +55 -2
  82. data/lib/datadog/kit/enable_core_dumps.rb +1 -1
  83. data/lib/datadog/open_feature/component.rb +18 -1
  84. data/lib/datadog/open_feature/evaluation_engine.rb +3 -3
  85. data/lib/datadog/open_feature/exposures/reporter.rb +1 -1
  86. data/lib/datadog/open_feature/exposures/worker.rb +1 -1
  87. data/lib/datadog/open_feature/hooks/flag_eval_hook.rb +49 -0
  88. data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
  89. data/lib/datadog/open_feature/provider.rb +19 -1
  90. data/lib/datadog/open_feature/remote.rb +1 -1
  91. data/lib/datadog/open_feature/transport.rb +1 -1
  92. data/lib/datadog/opentelemetry/metrics.rb +13 -4
  93. data/lib/datadog/opentelemetry/sdk/configurator.rb +1 -1
  94. data/lib/datadog/opentelemetry/sdk/id_generator.rb +16 -10
  95. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +1 -1
  96. data/lib/datadog/profiling/collectors/code_provenance.rb +35 -9
  97. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +31 -2
  98. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +8 -2
  99. data/lib/datadog/profiling/collectors/info.rb +16 -3
  100. data/lib/datadog/profiling/component.rb +3 -6
  101. data/lib/datadog/profiling/exporter.rb +37 -12
  102. data/lib/datadog/profiling/ext.rb +0 -2
  103. data/lib/datadog/profiling/flush.rb +21 -12
  104. data/lib/datadog/profiling/http_transport.rb +12 -1
  105. data/lib/datadog/profiling/load_native_extension.rb +1 -1
  106. data/lib/datadog/profiling/profiler.rb +13 -1
  107. data/lib/datadog/profiling/scheduler.rb +2 -2
  108. data/lib/datadog/profiling/stack_recorder.rb +0 -4
  109. data/lib/datadog/profiling/tasks/exec.rb +8 -3
  110. data/lib/datadog/profiling/tasks/help.rb +1 -0
  111. data/lib/datadog/profiling/tasks/setup.rb +2 -2
  112. data/lib/datadog/single_step_instrument.rb +1 -1
  113. data/lib/datadog/symbol_database/configuration.rb +65 -0
  114. data/lib/datadog/symbol_database/extractor.rb +906 -0
  115. data/lib/datadog/symbol_database/file_hash.rb +46 -0
  116. data/lib/datadog/symbol_database/logger.rb +43 -0
  117. data/lib/datadog/symbol_database/scope.rb +102 -0
  118. data/lib/datadog/symbol_database/scope_batcher.rb +280 -0
  119. data/lib/datadog/symbol_database/service_version.rb +57 -0
  120. data/lib/datadog/symbol_database/symbol.rb +66 -0
  121. data/lib/datadog/symbol_database/transport/http/endpoint.rb +28 -0
  122. data/lib/datadog/symbol_database/transport/http.rb +45 -0
  123. data/lib/datadog/symbol_database/transport.rb +54 -0
  124. data/lib/datadog/symbol_database/uploader.rb +169 -0
  125. data/lib/datadog/symbol_database.rb +49 -0
  126. data/lib/datadog/tracing/buffer.rb +3 -3
  127. data/lib/datadog/tracing/configuration/settings.rb +1 -1
  128. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -3
  129. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  130. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  131. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  132. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  133. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  134. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  135. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  136. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  137. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  138. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +2 -2
  139. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  140. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
  141. data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
  142. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +1 -1
  143. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
  144. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -1
  145. data/lib/datadog/tracing/contrib/component.rb +1 -1
  146. data/lib/datadog/tracing/contrib/configuration/resolver.rb +7 -4
  147. data/lib/datadog/tracing/contrib/dalli/quantize.rb +1 -1
  148. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +1 -1
  149. data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -2
  150. data/lib/datadog/tracing/contrib/extensions.rb +9 -0
  151. data/lib/datadog/tracing/contrib/faraday/middleware.rb +2 -2
  152. data/lib/datadog/tracing/contrib/grape/endpoint.rb +5 -5
  153. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +2 -2
  154. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +2 -2
  155. data/lib/datadog/tracing/contrib/http/instrumentation.rb +2 -2
  156. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -2
  157. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +2 -2
  158. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +2 -2
  159. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +2 -2
  160. data/lib/datadog/tracing/contrib/karafka/patcher.rb +1 -1
  161. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +3 -3
  162. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +1 -1
  163. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +3 -3
  164. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +6 -0
  165. data/lib/datadog/tracing/contrib/rack/ext.rb +27 -0
  166. data/lib/datadog/tracing/contrib/rack/patcher.rb +1 -1
  167. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
  168. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +117 -1
  169. data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
  170. data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
  171. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +2 -2
  172. data/lib/datadog/tracing/contrib/redis/quantize.rb +1 -1
  173. data/lib/datadog/tracing/contrib/redis/tags.rb +1 -1
  174. data/lib/datadog/tracing/contrib/sidekiq/utils.rb +1 -1
  175. data/lib/datadog/tracing/contrib/stripe/request.rb +1 -1
  176. data/lib/datadog/tracing/contrib.rb +8 -0
  177. data/lib/datadog/tracing/diagnostics/environment_logger.rb +3 -1
  178. data/lib/datadog/tracing/distributed/baggage.rb +59 -5
  179. data/lib/datadog/tracing/distributed/datadog.rb +11 -11
  180. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +1 -1
  181. data/lib/datadog/tracing/distributed/propagation.rb +2 -2
  182. data/lib/datadog/tracing/distributed/trace_context.rb +74 -32
  183. data/lib/datadog/tracing/event.rb +1 -1
  184. data/lib/datadog/tracing/metadata/tagging.rb +2 -2
  185. data/lib/datadog/tracing/pipeline.rb +1 -1
  186. data/lib/datadog/tracing/remote.rb +1 -1
  187. data/lib/datadog/tracing/sampling/rule.rb +1 -1
  188. data/lib/datadog/tracing/sampling/rule_sampler.rb +2 -2
  189. data/lib/datadog/tracing/sampling/span/rule_parser.rb +2 -2
  190. data/lib/datadog/tracing/span_operation.rb +3 -3
  191. data/lib/datadog/tracing/trace_operation.rb +4 -4
  192. data/lib/datadog/tracing/tracer.rb +6 -8
  193. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  194. data/lib/datadog/tracing/workers.rb +2 -1
  195. data/lib/datadog/version.rb +1 -1
  196. metadata +33 -12
  197. data/ext/datadog_profiling_native_extension/clock_id_noop.c +0 -21
  198. data/lib/datadog/ai_guard/configuration/settings.rb +0 -113
  199. data/lib/datadog/appsec/configuration/settings.rb +0 -423
  200. data/lib/datadog/data_streams/configuration/settings.rb +0 -49
  201. data/lib/datadog/di/configuration/settings.rb +0 -243
  202. data/lib/datadog/error_tracking/configuration/settings.rb +0 -63
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1328115b16393f9d96152ad1d94cb6190c307d8dd5df341087ffe21e2212ec9b
4
- data.tar.gz: f408b1437ae55de02488433b637c868bce087289ea5fc8af4aa808cfc4ee3b73
3
+ metadata.gz: ff1c01535f382bb6e3de5b529bf8ec30a6deda1d1c4aba3450bb21dbd80407bf
4
+ data.tar.gz: 966a01b956c9df2cd8d451df9175b66e0dbae3b7b3da7732ea998c7adca70442
5
5
  SHA512:
6
- metadata.gz: faf8598916d674b5da3b3ab57c013060f9ecf92b939d69a51528a81e495d07340e9a8a0ba70bf9498f6d8c303a32c08d3fbf971759199ff35b47f34a25d88b0a
7
- data.tar.gz: f7629d50a2537e08c9ceb5da1e81c25b788401c2992a3a70a2f2b29e2a44a3cbda7d2e621a316953202f5a068fa3ec201a4810d01b1b27ee4bab5bd1592a9bbd
6
+ metadata.gz: c2d4111c1b0526122be94729577ba54d1e85fb08f2cd1ae74495d9de5bd805cb883b39c872187a5a10f2d4f67a3153d6bce2dda8192e75f4b0e808c9ab9b6a79
7
+ data.tar.gz: 413cb6dc25ca7b8a79e9e85b152bfd4eed649f72ca5275a46203e30c12f893914337cbd8e5136767588b67f81eadefdc839a5a4aafebbdf5cc17e7d089798886
@@ -4,10 +4,18 @@
4
4
  #include <time.h>
5
5
  #include <ruby.h>
6
6
 
7
+ #ifdef __APPLE__
8
+ #include <mach/mach.h>
9
+ // On macOS, we use a mach_port_t to identify threads for CPU time queries
10
+ typedef mach_port_t cpu_time_id_t;
11
+ #else
12
+ typedef clockid_t cpu_time_id_t;
13
+ #endif
14
+
7
15
  // Contains the operating-system specific identifier needed to fetch CPU-time, and a flag to indicate if we failed to fetch it
8
16
  typedef struct {
9
17
  bool valid;
10
- clockid_t clock_id;
18
+ cpu_time_id_t clock_id;
11
19
  } thread_cpu_time_id;
12
20
 
13
21
  // Contains the current cpu time, and a flag to indicate if we failed to fetch it
@@ -0,0 +1,73 @@
1
+ #include "extconf.h"
2
+
3
+ // This file is only compiled on macOS where Mach thread APIs are available;
4
+ // Otherwise we compile clock_id_from_pthread.c (Linux)
5
+ #ifdef HAVE_MACH_THREAD_INFO
6
+
7
+ #include <pthread.h>
8
+ #include <time.h>
9
+ #include <mach/mach.h>
10
+ #include <mach/thread_info.h>
11
+
12
+ #include "clock_id.h"
13
+ #include "helpers.h"
14
+ #include "private_vm_api_access.h"
15
+ #include "ruby_helpers.h"
16
+ #include "time_helpers.h"
17
+
18
+ // Validate that our home-cooked pthread_id_for() matches pthread_self() for the current thread
19
+ void self_test_clock_id(void) {
20
+ rb_nativethread_id_t expected_pthread_id = pthread_self();
21
+ rb_nativethread_id_t actual_pthread_id = pthread_id_for(rb_thread_current());
22
+
23
+ if (expected_pthread_id != actual_pthread_id) raise_error(rb_eRuntimeError, "pthread_id_for() self-test failed");
24
+
25
+ // Also validate that we can get a valid mach thread port for the current thread
26
+ mach_port_t mach_thread = pthread_mach_thread_np(expected_pthread_id);
27
+ if (mach_thread == MACH_PORT_NULL) raise_error(rb_eRuntimeError, "pthread_mach_thread_np() self-test failed");
28
+ }
29
+
30
+ // Safety: This function is assumed never to raise exceptions by callers
31
+ thread_cpu_time_id thread_cpu_time_id_for(VALUE thread) {
32
+ rb_nativethread_id_t thread_id = pthread_id_for(thread);
33
+
34
+ if (thread_id == 0) return (thread_cpu_time_id) {.valid = false};
35
+
36
+ mach_port_t mach_thread = pthread_mach_thread_np(thread_id);
37
+
38
+ if (mach_thread == MACH_PORT_NULL) {
39
+ return (thread_cpu_time_id) {.valid = false};
40
+ }
41
+
42
+ return (thread_cpu_time_id) {.valid = true, .clock_id = mach_thread};
43
+ }
44
+
45
+ thread_cpu_time thread_cpu_time_for(thread_cpu_time_id time_id) {
46
+ thread_cpu_time error = (thread_cpu_time) {.valid = false};
47
+
48
+ if (!time_id.valid) return error;
49
+
50
+ // Fast path: clock_gettime(CLOCK_THREAD_CPUTIME_ID) is ~5x cheaper than thread_info()
51
+ // and gives sub-microsecond precision (vs microsecond), but only measures the calling
52
+ // thread on macOS (there is no pthread_getcpuclockid() equivalent).
53
+ if (time_id.clock_id == pthread_mach_thread_np(pthread_self())) {
54
+ struct timespec ts;
55
+ if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) {
56
+ return (thread_cpu_time) {.valid = true, .result_ns = SECONDS_AS_NS(ts.tv_sec) + ts.tv_nsec};
57
+ }
58
+ // Fall through to thread_info on the unlikely failure case.
59
+ }
60
+
61
+ struct thread_basic_info info;
62
+ mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
63
+ kern_return_t kr = thread_info(time_id.clock_id, THREAD_BASIC_INFO, (thread_info_t)&info, &count);
64
+
65
+ if (kr != KERN_SUCCESS) return error;
66
+
67
+ long user_ns = SECONDS_AS_NS(info.user_time.seconds) + MICROS_AS_NS(info.user_time.microseconds);
68
+ long system_ns = SECONDS_AS_NS(info.system_time.seconds) + MICROS_AS_NS(info.system_time.microseconds);
69
+
70
+ return (thread_cpu_time) {.valid = true, .result_ns = user_ns + system_ns};
71
+ }
72
+
73
+ #endif
@@ -1,7 +1,7 @@
1
1
  #include "extconf.h"
2
2
 
3
3
  // This file is only compiled on systems where pthread_getcpuclockid() is available;
4
- // Otherwise we compile clock_id_noop.c
4
+ // Otherwise we compile clock_id_from_mach.c (macOS)
5
5
  #ifdef HAVE_PTHREAD_GETCPUCLOCKID
6
6
 
7
7
  #include <pthread.h>
@@ -186,6 +186,11 @@ typedef struct {
186
186
  uint64_t gvl_sampling_time_ns_min;
187
187
  uint64_t gvl_sampling_time_ns_max;
188
188
  uint64_t gvl_sampling_time_ns_total;
189
+
190
+ struct vm_metrics {
191
+ // Total time spent waiting for the GVL
192
+ uint64_t gvl_waiting_time_ns_total;
193
+ } vm_metrics;
189
194
  } stats;
190
195
  } cpu_and_wall_time_worker_state;
191
196
 
@@ -1101,6 +1106,7 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
1101
1106
  ID2SYM(rb_intern("gvl_sampling_time_ns_max")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_max, > 0, ULL2NUM),
1102
1107
  ID2SYM(rb_intern("gvl_sampling_time_ns_total")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_total, > 0, ULL2NUM),
1103
1108
  ID2SYM(rb_intern("gvl_sampling_time_ns_avg")), /* => */ RUBY_AVG_OR_NIL(state->stats.gvl_sampling_time_ns_total, state->stats.after_gvl_running),
1109
+ ID2SYM(rb_intern("gvl_waiting_time_ns_total")), /* => */ state->gvl_profiling_enabled ? ULL2NUM(state->stats.vm_metrics.gvl_waiting_time_ns_total) : Qnil,
1104
1110
  };
1105
1111
  for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(stats_as_hash, arguments[i], arguments[i+1]);
1106
1112
  return stats_as_hash;
@@ -1141,6 +1147,7 @@ static void reset_stats_not_thread_safe(cpu_and_wall_time_worker_state *state) {
1141
1147
  .cpu_sampling_time_ns_min = UINT64_MAX,
1142
1148
  .allocation_sampling_time_ns_min = UINT64_MAX,
1143
1149
  .gvl_sampling_time_ns_min = UINT64_MAX,
1150
+ // Other fields are reset to 0
1144
1151
  };
1145
1152
  }
1146
1153
 
@@ -1399,7 +1406,7 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
1399
1406
  // Interesting note: A RUBY_INTERNAL_THREAD_EVENT_RESUMED is guaranteed to be called with the GVL being acquired.
1400
1407
  // (And... I think target_thread will be == rb_thread_current()?)
1401
1408
  //
1402
- // But we're not sure if we're on the main Ractor yet. The thread context collector actually can actually help here:
1409
+ // But we're not sure if we're on the main Ractor yet. The thread context collector can actually help here:
1403
1410
  // it tags threads it's tracking, so if a thread is tagged then by definition we know that thread belongs to the main
1404
1411
  // Ractor. Thus, if we get a ON_GVL_RUNNING_UNKNOWN result we shouldn't touch any state, but otherwise we're good to go.
1405
1412
 
@@ -1407,19 +1414,22 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
1407
1414
  target_thread = gvl_profiling_state_maybe_initialize();
1408
1415
  #endif
1409
1416
 
1417
+ cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1418
+ if (state == NULL) return; // This should not happen, but just in case...
1419
+
1410
1420
  on_gvl_running_result result = thread_context_collector_on_gvl_running(target_thread);
1411
1421
 
1412
- if (result == ON_GVL_RUNNING_SAMPLE) {
1422
+ if (result.waiting_for_gvl_duration_ns > 0) {
1423
+ state->stats.vm_metrics.gvl_waiting_time_ns_total += (uint64_t) result.waiting_for_gvl_duration_ns;
1424
+ }
1425
+
1426
+ if (result.action == ON_GVL_RUNNING_SAMPLE) {
1413
1427
  #ifndef NO_POSTPONED_TRIGGER
1414
1428
  rb_postponed_job_trigger(after_gvl_running_from_postponed_job_handle);
1415
1429
  #else
1416
1430
  rb_postponed_job_register_one(0, after_gvl_running_from_postponed_job, NULL);
1417
1431
  #endif
1418
- } else if (result == ON_GVL_RUNNING_DONT_SAMPLE) {
1419
- cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
1420
-
1421
- if (state == NULL) return; // This should not happen, but just in case...
1422
-
1432
+ } else if (result.action == ON_GVL_RUNNING_DONT_SAMPLE) {
1423
1433
  state->stats.gvl_dont_sample++;
1424
1434
  }
1425
1435
  } else {
@@ -1201,7 +1201,11 @@ static int per_thread_context_as_ruby_hash(st_data_t key_thread, st_data_t value
1201
1201
  ID2SYM(rb_intern("thread_id")), /* => */ rb_str_new2(thread_context->thread_id),
1202
1202
  ID2SYM(rb_intern("thread_invoke_location")), /* => */ rb_str_new2(thread_context->thread_invoke_location),
1203
1203
  ID2SYM(rb_intern("thread_cpu_time_id_valid?")), /* => */ thread_context->thread_cpu_time_id.valid ? Qtrue : Qfalse,
1204
- ID2SYM(rb_intern("thread_cpu_time_id")), /* => */ CLOCKID2NUM(thread_context->thread_cpu_time_id.clock_id),
1204
+ #ifdef __APPLE__
1205
+ ID2SYM(rb_intern("thread_cpu_time_id")), /* => */ ULL2NUM(thread_context->thread_cpu_time_id.clock_id),
1206
+ #else
1207
+ ID2SYM(rb_intern("thread_cpu_time_id")), /* => */ CLOCKID2NUM(thread_context->thread_cpu_time_id.clock_id),
1208
+ #endif
1205
1209
  ID2SYM(rb_intern("cpu_time_at_previous_sample_ns")), /* => */ LONG2NUM(thread_context->cpu_time_at_previous_sample_ns),
1206
1210
  ID2SYM(rb_intern("wall_time_at_previous_sample_ns")), /* => */ LONG2NUM(thread_context->wall_time_at_previous_sample_ns),
1207
1211
 
@@ -1924,10 +1928,14 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
1924
1928
  intptr_t gvl_waiting_at = gvl_profiling_state_get(thread);
1925
1929
 
1926
1930
  // Thread was not being profiled / not waiting on gvl
1927
- if (gvl_waiting_at == 0 || gvl_waiting_at == GVL_WAITING_ENABLED_EMPTY) return ON_GVL_RUNNING_UNKNOWN;
1931
+ if (gvl_waiting_at == 0 || gvl_waiting_at == GVL_WAITING_ENABLED_EMPTY) {
1932
+ return (on_gvl_running_result) {.action = ON_GVL_RUNNING_UNKNOWN, .waiting_for_gvl_duration_ns = 0};
1933
+ }
1928
1934
 
1929
1935
  // @ivoanjo: I'm not sure if this can happen -- It means we should've sampled already but haven't gotten the chance yet?
1930
- if (gvl_waiting_at < 0) return ON_GVL_RUNNING_SAMPLE;
1936
+ if (gvl_waiting_at < 0) {
1937
+ return (on_gvl_running_result) {.action = ON_GVL_RUNNING_SAMPLE, .waiting_for_gvl_duration_ns = 0};
1938
+ }
1931
1939
 
1932
1940
  long waiting_for_gvl_duration_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE) - gvl_waiting_at;
1933
1941
 
@@ -1943,7 +1951,10 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
1943
1951
  gvl_profiling_state_set(thread, GVL_WAITING_ENABLED_EMPTY);
1944
1952
  }
1945
1953
 
1946
- return should_sample ? ON_GVL_RUNNING_SAMPLE : ON_GVL_RUNNING_DONT_SAMPLE;
1954
+ return (on_gvl_running_result) {
1955
+ .action = should_sample ? ON_GVL_RUNNING_SAMPLE : ON_GVL_RUNNING_DONT_SAMPLE,
1956
+ .waiting_for_gvl_duration_ns = waiting_for_gvl_duration_ns,
1957
+ };
1947
1958
  }
1948
1959
 
1949
1960
  __attribute__((warn_unused_result))
@@ -2146,7 +2157,7 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
2146
2157
 
2147
2158
  debug_enter_unsafe_context();
2148
2159
 
2149
- VALUE result = thread_context_collector_on_gvl_running(thread_from_thread_object(thread)) == ON_GVL_RUNNING_SAMPLE ? Qtrue : Qfalse;
2160
+ VALUE result = thread_context_collector_on_gvl_running(thread_from_thread_object(thread)).action == ON_GVL_RUNNING_SAMPLE ? Qtrue : Qfalse;
2150
2161
 
2151
2162
  debug_leave_unsafe_context();
2152
2163
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  #include <ruby.h>
4
4
  #include <stdbool.h>
5
+ #include <stdint.h>
5
6
 
6
7
  #include "gvl_profiling_helper.h"
7
8
 
@@ -25,6 +26,11 @@ VALUE enforce_thread_context_collector_instance(VALUE object);
25
26
  ON_GVL_RUNNING_UNKNOWN, // Thread is not known, it may not even be from the current Ractor
26
27
  ON_GVL_RUNNING_DONT_SAMPLE, // Thread is known, but "Waiting for GVL" period was too small to be sampled
27
28
  ON_GVL_RUNNING_SAMPLE, // Thread is known, and "Waiting for GVL" period should be sampled
29
+ } on_gvl_running_enum;
30
+
31
+ typedef struct {
32
+ on_gvl_running_enum action;
33
+ long waiting_for_gvl_duration_ns;
28
34
  } on_gvl_running_result;
29
35
 
30
36
  void thread_context_collector_on_gvl_waiting(gvl_profiling_thread thread);
@@ -69,11 +69,16 @@ $stderr.puts(
69
69
  # NOTE: we MUST NOT require 'mkmf' before we check the #skip_building_extension? because the require triggers checks
70
70
  # that may fail on an environment not properly setup for building Ruby extensions.
71
71
  require "mkmf"
72
+ Datadog::LibdatadogExtconfHelpers.dump_mkmf_log_on_failure!
72
73
 
73
74
  Logging.message("[datadog] Using compiler:\n")
74
75
  xsystem("#{CONFIG["CC"]} -v")
75
76
  Logging.message("[datadog] End of compiler information\n")
76
77
 
78
+ # We must *never* `append_cflags "-Wall"` or `append_cflags "-Wextra"`, because those add the flag at the end,
79
+ # which then overrides earlier -Wno-* flags from RbConfig::CONFIG["warnflags"], and causes errors if -Werror is added.
80
+ # There is no need to add them anyway, Ruby since 1.9 has RbConfig::CONFIG["warnflags"] starting with "-Wall -Wextra ".
81
+
77
82
  # Because we can't control what compiler versions our customers use, shipping with -Werror by default is a no-go.
78
83
  # But we can enable it in CI, so that we quickly spot any new warnings that just got introduced.
79
84
  append_cflags "-Werror" if ENV["DATADOG_GEM_CI"] == "true"
@@ -106,10 +111,6 @@ append_cflags "-fvisibility=hidden"
106
111
  # Avoid legacy C definitions
107
112
  append_cflags "-Wold-style-definition"
108
113
 
109
- # Enable all other compiler warnings
110
- append_cflags "-Wall"
111
- append_cflags "-Wextra"
112
-
113
114
  if ENV["DDTRACE_DEBUG"] == "true"
114
115
  $defs << "-DDD_DEBUG"
115
116
  CONFIG["optflags"] = "-O0"
@@ -128,6 +129,9 @@ if RUBY_PLATFORM.include?("linux")
128
129
 
129
130
  # Not available on macOS
130
131
  $defs << "-DHAVE_CLOCK_MONOTONIC_COARSE"
132
+ elsif RUBY_PLATFORM.include?("darwin")
133
+ # On macOS, we use Mach thread APIs to get per-thread CPU time
134
+ $defs << "-DHAVE_MACH_THREAD_INFO"
131
135
  end
132
136
 
133
137
  have_func "malloc_stats"
@@ -198,15 +198,15 @@ static VALUE _native_do_export(
198
198
  VALUE flush
199
199
  ) {
200
200
  VALUE encoded_profile = rb_funcall(flush, rb_intern("encoded_profile"), 0);
201
- VALUE code_provenance_file_name = rb_funcall(flush, rb_intern("code_provenance_file_name"), 0);
202
201
  VALUE code_provenance_data = rb_funcall(flush, rb_intern("code_provenance_data"), 0);
202
+ VALUE metrics_json = rb_funcall(flush, rb_intern("metrics"), 0);
203
203
  VALUE tags_as_array = rb_funcall(flush, rb_intern("tags_as_array"), 0);
204
204
  VALUE internal_metadata_json = rb_funcall(flush, rb_intern("internal_metadata_json"), 0);
205
205
  VALUE info_json = rb_funcall(flush, rb_intern("info_json"), 0);
206
206
  VALUE process_tags = rb_funcall(flush, rb_intern("process_tags"), 0);
207
207
 
208
208
  enforce_encoded_profile_instance(encoded_profile);
209
- ENFORCE_TYPE(code_provenance_file_name, T_STRING);
209
+ ENFORCE_TYPE(metrics_json, T_STRING);
210
210
  ENFORCE_TYPE(tags_as_array, T_ARRAY);
211
211
  ENFORCE_TYPE(internal_metadata_json, T_STRING);
212
212
  ENFORCE_TYPE(info_json, T_STRING);
@@ -216,13 +216,18 @@ static VALUE _native_do_export(
216
216
  bool have_code_provenance = !NIL_P(code_provenance_data);
217
217
  if (have_code_provenance) ENFORCE_TYPE(code_provenance_data, T_STRING);
218
218
 
219
- int to_compress_length = have_code_provenance ? 1 : 0;
219
+ int to_compress_length = 1 + (have_code_provenance ? 1 : 0);
220
220
  ddog_prof_Exporter_File to_compress[to_compress_length];
221
221
  ddog_prof_Exporter_Slice_File files_to_compress_and_export = {.ptr = to_compress, .len = to_compress_length};
222
222
 
223
+ to_compress[0] = (ddog_prof_Exporter_File) {
224
+ .name = DDOG_CHARSLICE_C("metrics.json"),
225
+ .file = byte_slice_from_ruby_string(metrics_json),
226
+ };
227
+
223
228
  if (have_code_provenance) {
224
- to_compress[0] = (ddog_prof_Exporter_File) {
225
- .name = char_slice_from_ruby_string(code_provenance_file_name),
229
+ to_compress[1] = (ddog_prof_Exporter_File) {
230
+ .name = DDOG_CHARSLICE_C("code-provenance.json"),
226
231
  .file = byte_slice_from_ruby_string(code_provenance_data),
227
232
  };
228
233
  }
@@ -416,7 +416,6 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
416
416
  if (options == Qnil) options = rb_hash_new();
417
417
 
418
418
  VALUE recorder_instance = rb_hash_fetch(options, ID2SYM(rb_intern("self_instance")));
419
- VALUE cpu_time_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("cpu_time_enabled")));
420
419
  VALUE alloc_samples_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("alloc_samples_enabled")));
421
420
  VALUE heap_samples_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("heap_samples_enabled")));
422
421
  VALUE heap_size_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("heap_size_enabled")));
@@ -424,7 +423,6 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
424
423
  VALUE timeline_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("timeline_enabled")));
425
424
  VALUE heap_clean_after_gc_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("heap_clean_after_gc_enabled")));
426
425
 
427
- ENFORCE_BOOLEAN(cpu_time_enabled);
428
426
  ENFORCE_BOOLEAN(alloc_samples_enabled);
429
427
  ENFORCE_BOOLEAN(heap_samples_enabled);
430
428
  ENFORCE_BOOLEAN(heap_size_enabled);
@@ -440,7 +438,6 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
440
438
  heap_recorder_set_sample_rate(state->heap_recorder, NUM2INT(heap_sample_every));
441
439
 
442
440
  uint8_t requested_values_count = ALL_VALUE_TYPES_COUNT -
443
- (cpu_time_enabled == Qtrue ? 0 : 1) -
444
441
  (alloc_samples_enabled == Qtrue? 0 : 2) -
445
442
  (heap_samples_enabled == Qtrue ? 0 : 1) -
446
443
  (heap_size_enabled == Qtrue ? 0 : 1) -
@@ -466,12 +463,9 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
466
463
  enabled_sample_types[next_enabled_pos] = DDOG_PROF_SAMPLE_TYPE_WALL_TIME;
467
464
  state->position_for[WALL_TIME_VALUE_ID] = next_enabled_pos++;
468
465
 
469
- if (cpu_time_enabled == Qtrue) {
470
- enabled_sample_types[next_enabled_pos] = DDOG_PROF_SAMPLE_TYPE_CPU_TIME;
471
- state->position_for[CPU_TIME_VALUE_ID] = next_enabled_pos++;
472
- } else {
473
- state->position_for[CPU_TIME_VALUE_ID] = next_disabled_pos++;
474
- }
466
+ // CPU_TIME is always enabled
467
+ enabled_sample_types[next_enabled_pos] = DDOG_PROF_SAMPLE_TYPE_CPU_TIME;
468
+ state->position_for[CPU_TIME_VALUE_ID] = next_enabled_pos++;
475
469
 
476
470
  if (alloc_samples_enabled == Qtrue) {
477
471
  enabled_sample_types[next_enabled_pos] = DDOG_PROF_SAMPLE_TYPE_ALLOC_SAMPLES;
@@ -9,6 +9,7 @@
9
9
 
10
10
  #define SECONDS_AS_NS(value) (value * 1000 * 1000 * 1000L)
11
11
  #define MILLIS_AS_NS(value) (value * 1000 * 1000L)
12
+ #define MICROS_AS_NS(value) (value * 1000L)
12
13
 
13
14
  typedef enum { RAISE_ON_FAILURE, DO_NOT_RAISE_ON_FAILURE } raise_on_failure_setting;
14
15
 
@@ -72,6 +72,8 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
72
72
  .endpoint = {.url = char_slice_from_ruby_string(agent_base_url)},
73
73
  .resolve_frames = DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER,
74
74
  .timeout_ms = FIX2INT(upload_timeout_seconds) * 1000,
75
+ .collect_all_threads = true,
76
+ .max_threads = 128,
75
77
  };
76
78
 
77
79
  ddog_crasht_Metadata metadata = {
@@ -3,7 +3,12 @@
3
3
  #include "datadog_ruby_common.h"
4
4
 
5
5
  // Prototypes for Ruby functions declared in internal Ruby headers.
6
+ // rb_iseqw_new wraps an internal iseq pointer into a Ruby-visible
7
+ // RubyVM::InstructionSequence object.
6
8
  VALUE rb_iseqw_new(const void *iseq);
9
+ // rb_iseqw_to_iseq unwraps a RubyVM::InstructionSequence object back
10
+ // to its internal iseq pointer.
11
+ const void *rb_iseqw_to_iseq(VALUE iseqw);
7
12
  int rb_objspace_internal_object_p(VALUE obj);
8
13
  void rb_objspace_each_objects(
9
14
  int (*callback)(void *start, void *end, size_t stride, void *data),
@@ -70,10 +75,53 @@ static VALUE exception_message(DDTRACE_UNUSED VALUE _self, VALUE exception) {
70
75
  return rb_ivar_get(exception, id_mesg);
71
76
  }
72
77
 
78
+ // rb_iseq_type was added in Ruby 3.1 (commit 89a02d89 by Koichi Sasada,
79
+ // 2021-12-19). It returns the iseq type as a Symbol. On Ruby < 3.1 this
80
+ // function does not exist, so have_func('rb_iseq_type') in extconf.rb
81
+ // gates compilation. When unavailable, backfill_registry falls back to
82
+ // the first_lineno == 0 heuristic.
83
+ #ifdef HAVE_RB_ISEQ_TYPE
84
+ VALUE rb_iseq_type(const void *iseq);
85
+
86
+ /*
87
+ * call-seq:
88
+ * DI.iseq_type(iseq) -> Symbol
89
+ *
90
+ * Returns the type of an InstructionSequence as a symbol by calling
91
+ * the internal rb_iseq_type() function (available since Ruby 3.1).
92
+ *
93
+ * This method is only defined when rb_iseq_type is detected at compile
94
+ * time via have_func in extconf.rb. On Ruby < 3.1 it is not available
95
+ * and callers must use an alternative (e.g. first_lineno heuristic).
96
+ *
97
+ * Possible return values: :top, :method, :block, :class, :rescue,
98
+ * :ensure, :eval, :main, :plain.
99
+ *
100
+ * :top and :main represent whole-file iseqs (from require/load and the
101
+ * entry point script respectively). Other types represent sub-file
102
+ * constructs (method definitions, class bodies, blocks, etc.).
103
+ *
104
+ * Used by CodeTracker#backfill_registry to distinguish whole-file iseqs
105
+ * from per-method/block/class iseqs when populating the registry from
106
+ * the object space.
107
+ *
108
+ * @param iseq [RubyVM::InstructionSequence] The instruction sequence
109
+ * @return [Symbol] The iseq type
110
+ */
111
+ static VALUE iseq_type(DDTRACE_UNUSED VALUE _self, VALUE iseq_val) {
112
+ const void *iseq = rb_iseqw_to_iseq(iseq_val);
113
+ if (!iseq) return Qnil;
114
+ return rb_iseq_type(iseq);
115
+ }
116
+ #endif
117
+
73
118
  void di_init(VALUE datadog_module) {
74
119
  id_mesg = rb_intern("mesg");
75
120
 
76
121
  VALUE di_module = rb_define_module_under(datadog_module, "DI");
77
122
  rb_define_singleton_method(di_module, "all_iseqs", all_iseqs, 0);
78
123
  rb_define_singleton_method(di_module, "exception_message", exception_message, 1);
124
+ #ifdef HAVE_RB_ISEQ_TYPE
125
+ rb_define_singleton_method(di_module, "iseq_type", iseq_type, 1);
126
+ #endif
79
127
  }
@@ -31,6 +31,11 @@ libdatadog_issue = Datadog::LibdatadogExtconfHelpers.load_libdatadog_or_get_issu
31
31
  skip_building_extension!("issue setting up `libdatadog` gem: #{libdatadog_issue}") if libdatadog_issue
32
32
 
33
33
  require 'mkmf'
34
+ Datadog::LibdatadogExtconfHelpers.dump_mkmf_log_on_failure!
35
+
36
+ # We must *never* `append_cflags "-Wall"` or `append_cflags "-Wextra"`, because those add the flag at the end,
37
+ # which then overrides earlier -Wno-* flags from RbConfig::CONFIG["warnflags"], and causes errors if -Werror is added.
38
+ # There is no need to add them anyway, Ruby since 1.9 has RbConfig::CONFIG["warnflags"] starting with "-Wall -Wextra ".
34
39
 
35
40
  # Because we can't control what compiler versions our customers use, shipping with -Werror by default is a no-go.
36
41
  # But we can enable it in CI, so that we quickly spot any new warnings that just got introduced.
@@ -64,10 +69,6 @@ append_cflags '-fvisibility=hidden'
64
69
  # Avoid legacy C definitions
65
70
  append_cflags '-Wold-style-definition'
66
71
 
67
- # Enable all other compiler warnings
68
- append_cflags '-Wall'
69
- append_cflags '-Wextra'
70
-
71
72
  if ENV['DDTRACE_DEBUG'] == 'true'
72
73
  $defs << '-DDD_DEBUG'
73
74
  CONFIG['optflags'] = '-O0'
@@ -89,6 +90,8 @@ Datadog::LibdatadogExtconfHelpers.add_libdatadog_version_define
89
90
  # When requiring, we need to use the exact same string, including the version and the platform.
90
91
  EXTENSION_NAME = "libdatadog_api.#{RUBY_VERSION[/\d+.\d+/]}_#{RUBY_PLATFORM}".freeze
91
92
 
93
+ have_func('rb_iseq_type')
94
+
92
95
  create_makefile(EXTENSION_NAME)
93
96
 
94
97
  # rubocop:enable Style/GlobalVars
@@ -10,7 +10,7 @@ module Datadog
10
10
  module LibdatadogExtconfHelpers
11
11
  # Used to make sure the correct gem version gets loaded, as extconf.rb does not get run with "bundle exec" and thus
12
12
  # may see multiple libdatadog versions. See https://github.com/DataDog/dd-trace-rb/pull/2531 for the horror story.
13
- LIBDATADOG_VERSION = '~> 30.0.0.1.0'
13
+ LIBDATADOG_VERSION = '~> 33.0.0.1.0'
14
14
 
15
15
  # Used as an workaround for a limitation with how dynamic linking works in environments where the datadog gem and
16
16
  # libdatadog are moved after the extension gets compiled.
@@ -170,6 +170,43 @@ module Datadog
170
170
  end
171
171
  # rubocop:enable Style/GlobalVars
172
172
 
173
+ # When setting up the extension build fails (e.g. a missing header), the actual failure ends up in mkmf.log
174
+ # and is not printed, which is confusing and complicates debugging. To fix that, this monkey patch actually
175
+ # prints the failure.
176
+ def self.dump_mkmf_log_on_failure!
177
+ # Doesn't work on 2.5/2.6 and not worth extra complexity to support
178
+ return if RUBY_VERSION < '2.7'
179
+
180
+ MakeMakefile.prepend(DumpMkmfLogOnFailure)
181
+ end
182
+
183
+ # rubocop:disable Style/GlobalVars,Style/StderrPuts
184
+ # See `dump_mkmf_log_on_failure!` for details.
185
+ module DumpMkmfLogOnFailure
186
+ def mkmf_failed(path)
187
+ unless $makefile_created || File.exist?('Makefile')
188
+ log_path = File.expand_path('mkmf.log')
189
+ if File.exist?(log_path)
190
+ # The full log is very verbose so let's grab only the last check which should be the one that failed
191
+ entries = File.read(log_path).split(/^-{20}$\n?/)
192
+ last_entry = entries.reverse.find { |e| !e.strip.empty? } || ''
193
+
194
+ $stderr.puts(
195
+ "+------------------------------------------------------------------------------+\n" \
196
+ "| There was an issue setting up extension build: |\n" \
197
+ "+------------------------------------------------------------------------------+" \
198
+ "#{last_entry.chomp}" \
199
+ "+------------------------------------------------------------------------------+\n" \
200
+ "| Full failure log is at #{log_path}\n" \
201
+ "+------------------------------------------------------------------------------+\n" \
202
+ )
203
+ end
204
+ end
205
+ super
206
+ end
207
+ end
208
+ # rubocop:enable Style/GlobalVars,Style/StderrPuts
209
+
173
210
  # Note: This helper is currently only used in the `libdatadog_api/extconf.rb` BUT still lives here to enable testing.
174
211
  def self.load_libdatadog_or_get_issue
175
212
  try_loading_libdatadog do |exception|
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ if %w[1 true].include?((Datadog::DATADOG_ENV["DD_AI_GUARD_ENABLED"] || "").downcase)
4
+ begin
5
+ require_relative "contrib/auto_instrument"
6
+ Datadog::AIGuard::Contrib::AutoInstrument.patch_all
7
+ rescue => e
8
+ Kernel.warn("[datadog] AI Guard failed to auto-instrument. error: #{e.class}: #{e.message}")
9
+ end
10
+ end
@@ -15,7 +15,7 @@ module Datadog
15
15
  module AIGuard
16
16
  # Component for API Guard product
17
17
  class Component
18
- attr_reader :api_client, :logger
18
+ attr_reader :api_client, :logger, :telemetry
19
19
 
20
20
  def self.build(settings, logger:, telemetry:)
21
21
  return unless settings.respond_to?(:ai_guard) && settings.ai_guard.enabled