ddtrace 1.20.0 → 1.23.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +130 -1
- data/LICENSE-3rdparty.csv +1 -1
- data/bin/ddprofrb +15 -0
- data/bin/ddtracerb +3 -1
- data/ext/{ddtrace_profiling_loader/ddtrace_profiling_loader.c → datadog_profiling_loader/datadog_profiling_loader.c} +2 -2
- data/ext/{ddtrace_profiling_loader → datadog_profiling_loader}/extconf.rb +3 -3
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_cpu_and_wall_time_worker.c +226 -61
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_discrete_dynamic_sampler.c +145 -72
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_discrete_dynamic_sampler.h +17 -5
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_thread_context.c +115 -14
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/extconf.rb +2 -2
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/heap_recorder.c +81 -4
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/heap_recorder.h +12 -1
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/http_transport.c +15 -19
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/native_extension_helpers.rb +4 -4
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/private_vm_api_access.c +14 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/private_vm_api_access.h +4 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/profiling.c +1 -1
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/ruby_helpers.c +10 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/ruby_helpers.h +5 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/stack_recorder.c +161 -62
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -13
- data/lib/datadog/appsec/event.rb +1 -1
- data/lib/datadog/auto_instrument.rb +3 -0
- data/lib/datadog/core/configuration/components.rb +7 -6
- data/lib/datadog/core/configuration/option.rb +8 -6
- data/lib/datadog/core/configuration/settings.rb +130 -63
- data/lib/datadog/core/configuration.rb +20 -4
- data/lib/datadog/core/diagnostics/environment_logger.rb +4 -3
- data/lib/datadog/core/environment/git.rb +25 -0
- data/lib/datadog/core/environment/identity.rb +18 -48
- data/lib/datadog/core/environment/platform.rb +7 -1
- data/lib/datadog/core/git/ext.rb +2 -23
- data/lib/datadog/core/remote/client/capabilities.rb +1 -1
- data/lib/datadog/core/remote/negotiation.rb +2 -2
- data/lib/datadog/core/remote/transport/http/config.rb +1 -1
- data/lib/datadog/core/remote/worker.rb +7 -4
- data/lib/datadog/core/telemetry/client.rb +18 -10
- data/lib/datadog/core/telemetry/emitter.rb +9 -13
- data/lib/datadog/core/telemetry/event.rb +247 -57
- data/lib/datadog/core/telemetry/ext.rb +1 -0
- data/lib/datadog/core/telemetry/heartbeat.rb +1 -3
- data/lib/datadog/core/telemetry/http/ext.rb +4 -1
- data/lib/datadog/core/telemetry/http/response.rb +4 -0
- data/lib/datadog/core/telemetry/http/transport.rb +9 -4
- data/lib/datadog/core/telemetry/request.rb +59 -0
- data/lib/datadog/core/transport/ext.rb +2 -0
- data/lib/datadog/core/utils/url.rb +25 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +10 -4
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +31 -0
- data/lib/datadog/profiling/collectors/info.rb +101 -0
- data/lib/datadog/profiling/component.rb +34 -28
- data/lib/datadog/profiling/exporter.rb +23 -6
- data/lib/datadog/profiling/ext.rb +2 -0
- data/lib/datadog/profiling/flush.rb +6 -3
- data/lib/datadog/profiling/http_transport.rb +5 -1
- data/lib/datadog/profiling/load_native_extension.rb +19 -6
- data/lib/datadog/profiling/native_extension.rb +1 -1
- data/lib/datadog/profiling/stack_recorder.rb +6 -2
- data/lib/datadog/profiling/tag_builder.rb +5 -0
- data/lib/datadog/profiling/tasks/exec.rb +3 -3
- data/lib/datadog/profiling/tasks/help.rb +3 -3
- data/lib/datadog/profiling.rb +13 -2
- data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +11 -4
- data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +20 -0
- data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +11 -1
- data/lib/datadog/tracing/contrib/configurable.rb +1 -1
- data/lib/datadog/tracing/contrib/extensions.rb +6 -2
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +11 -4
- data/lib/datadog/tracing/sampling/matcher.rb +23 -3
- data/lib/datadog/tracing/sampling/rule.rb +7 -2
- data/lib/datadog/tracing/sampling/rule_sampler.rb +2 -0
- data/lib/datadog/tracing/trace_operation.rb +1 -2
- data/lib/datadog/tracing/transport/http.rb +1 -0
- data/lib/datadog/tracing/transport/trace_formatter.rb +31 -0
- data/lib/ddtrace/version.rb +1 -1
- metadata +58 -65
- data/ext/ddtrace_profiling_native_extension/pid_controller.c +0 -57
- data/ext/ddtrace_profiling_native_extension/pid_controller.h +0 -45
- data/lib/datadog/core/telemetry/collector.rb +0 -250
- data/lib/datadog/core/telemetry/v1/app_event.rb +0 -59
- data/lib/datadog/core/telemetry/v1/application.rb +0 -92
- data/lib/datadog/core/telemetry/v1/configuration.rb +0 -25
- data/lib/datadog/core/telemetry/v1/dependency.rb +0 -43
- data/lib/datadog/core/telemetry/v1/host.rb +0 -59
- data/lib/datadog/core/telemetry/v1/install_signature.rb +0 -38
- data/lib/datadog/core/telemetry/v1/integration.rb +0 -64
- data/lib/datadog/core/telemetry/v1/product.rb +0 -36
- data/lib/datadog/core/telemetry/v1/telemetry_request.rb +0 -106
- data/lib/datadog/core/telemetry/v2/app_client_configuration_change.rb +0 -41
- data/lib/datadog/core/telemetry/v2/request.rb +0 -29
- data/lib/datadog/profiling/diagnostics/environment_logger.rb +0 -39
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/NativeExtensionDesign.md +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id_from_pthread.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id_noop.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_dynamic_sampling_rate.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_dynamic_sampling_rate.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_gc_profiling_helper.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_gc_profiling_helper.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_idle_sampling_helper.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_idle_sampling_helper.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_stack.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_stack.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_thread_context.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/helpers.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/libdatadog_helpers.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/libdatadog_helpers.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/setup_signal_handler.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/setup_signal_handler.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/stack_recorder.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/time_helpers.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/time_helpers.h +0 -0
|
@@ -82,6 +82,9 @@ static ID at_id_id; // id of :@id in Ruby
|
|
|
82
82
|
static ID at_resource_id; // id of :@resource in Ruby
|
|
83
83
|
static ID at_root_span_id; // id of :@root_span in Ruby
|
|
84
84
|
static ID at_type_id; // id of :@type in Ruby
|
|
85
|
+
static ID at_otel_values_id; // id of :@otel_values in Ruby
|
|
86
|
+
static ID at_parent_span_id_id; // id of :@parent_span_id in Ruby
|
|
87
|
+
static ID at_datadog_trace_id; // id of :@datadog_trace in Ruby
|
|
85
88
|
|
|
86
89
|
// Contains state for a single ThreadContext instance
|
|
87
90
|
struct thread_context_collector_state {
|
|
@@ -114,6 +117,8 @@ struct thread_context_collector_state {
|
|
|
114
117
|
monotonic_to_system_epoch_state time_converter_state;
|
|
115
118
|
// Used to identify the main thread, to give it a fallback name
|
|
116
119
|
VALUE main_thread;
|
|
120
|
+
// Used when extracting trace identifiers from otel spans. Lazily initialized.
|
|
121
|
+
VALUE otel_current_span_key;
|
|
117
122
|
|
|
118
123
|
struct stats {
|
|
119
124
|
// Track how many garbage collection samples we've taken.
|
|
@@ -212,12 +217,20 @@ static long thread_id_for(VALUE thread);
|
|
|
212
217
|
static VALUE _native_stats(VALUE self, VALUE collector_instance);
|
|
213
218
|
static VALUE _native_gc_tracking(VALUE self, VALUE collector_instance);
|
|
214
219
|
static void trace_identifiers_for(struct thread_context_collector_state *state, VALUE thread, struct trace_identifiers *trace_identifiers_result);
|
|
215
|
-
static bool should_collect_resource(VALUE
|
|
220
|
+
static bool should_collect_resource(VALUE root_span);
|
|
216
221
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
|
217
222
|
static VALUE thread_list(struct thread_context_collector_state *state);
|
|
218
223
|
static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object);
|
|
219
224
|
static VALUE _native_new_empty_thread(VALUE self);
|
|
220
225
|
static ddog_CharSlice ruby_value_type_to_class_name(enum ruby_value_type type);
|
|
226
|
+
static void ddtrace_otel_trace_identifiers_for(
|
|
227
|
+
struct thread_context_collector_state *state,
|
|
228
|
+
VALUE *active_trace,
|
|
229
|
+
VALUE *root_span,
|
|
230
|
+
VALUE *numeric_span_id,
|
|
231
|
+
VALUE active_span,
|
|
232
|
+
VALUE otel_values
|
|
233
|
+
);
|
|
221
234
|
|
|
222
235
|
void collectors_thread_context_init(VALUE profiling_module) {
|
|
223
236
|
VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
|
|
@@ -255,6 +268,9 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
|
255
268
|
at_resource_id = rb_intern_const("@resource");
|
|
256
269
|
at_root_span_id = rb_intern_const("@root_span");
|
|
257
270
|
at_type_id = rb_intern_const("@type");
|
|
271
|
+
at_otel_values_id = rb_intern_const("@otel_values");
|
|
272
|
+
at_parent_span_id_id = rb_intern_const("@parent_span_id");
|
|
273
|
+
at_datadog_trace_id = rb_intern_const("@datadog_trace");
|
|
258
274
|
|
|
259
275
|
gc_profiling_init();
|
|
260
276
|
}
|
|
@@ -282,6 +298,7 @@ static void thread_context_collector_typed_data_mark(void *state_ptr) {
|
|
|
282
298
|
st_foreach(state->hash_map_per_thread_context, hash_map_per_thread_context_mark, 0 /* unused */);
|
|
283
299
|
rb_gc_mark(state->thread_list_buffer);
|
|
284
300
|
rb_gc_mark(state->main_thread);
|
|
301
|
+
rb_gc_mark(state->otel_current_span_key);
|
|
285
302
|
}
|
|
286
303
|
|
|
287
304
|
static void thread_context_collector_typed_data_free(void *state_ptr) {
|
|
@@ -334,6 +351,7 @@ static VALUE _native_new(VALUE klass) {
|
|
|
334
351
|
state->allocation_type_enabled = true;
|
|
335
352
|
state->time_converter_state = (monotonic_to_system_epoch_state) MONOTONIC_TO_SYSTEM_EPOCH_INITIALIZER;
|
|
336
353
|
state->main_thread = rb_thread_main();
|
|
354
|
+
state->otel_current_span_key = Qnil;
|
|
337
355
|
state->gc_tracking.wall_time_at_previous_gc_ns = INVALID_TIME;
|
|
338
356
|
state->gc_tracking.wall_time_at_last_flushed_gc_event_ns = 0;
|
|
339
357
|
|
|
@@ -603,11 +621,14 @@ bool thread_context_collector_on_gc_finish(VALUE self_instance) {
|
|
|
603
621
|
// Let the caller know if it should schedule a flush or not. Returning true every time would cause a lot of overhead
|
|
604
622
|
// on the application (see GC tracking introduction at the top of the file), so instead we try to accumulate a few
|
|
605
623
|
// samples first.
|
|
606
|
-
bool finished_major_gc = gc_profiling_has_major_gc_finished();
|
|
607
624
|
bool over_flush_time_treshold =
|
|
608
625
|
(wall_time_at_finish_ns - state->gc_tracking.wall_time_at_last_flushed_gc_event_ns) >= TIME_BETWEEN_GC_EVENTS_NS;
|
|
609
626
|
|
|
610
|
-
|
|
627
|
+
if (over_flush_time_treshold) {
|
|
628
|
+
return true;
|
|
629
|
+
} else {
|
|
630
|
+
return gc_profiling_has_major_gc_finished();
|
|
631
|
+
}
|
|
611
632
|
}
|
|
612
633
|
|
|
613
634
|
// This function gets called after one or more GC work steps (calls to on_gc_start/on_gc_finish).
|
|
@@ -917,6 +938,7 @@ static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instanc
|
|
|
917
938
|
));
|
|
918
939
|
rb_str_concat(result, rb_sprintf(" main_thread=%"PRIsVALUE, state->main_thread));
|
|
919
940
|
rb_str_concat(result, rb_sprintf(" gc_tracking=%"PRIsVALUE, gc_tracking_as_ruby_hash(state)));
|
|
941
|
+
rb_str_concat(result, rb_sprintf(" otel_current_span_key=%"PRIsVALUE, state->otel_current_span_key));
|
|
920
942
|
|
|
921
943
|
return result;
|
|
922
944
|
}
|
|
@@ -1104,10 +1126,19 @@ static void trace_identifiers_for(struct thread_context_collector_state *state,
|
|
|
1104
1126
|
|
|
1105
1127
|
VALUE root_span = rb_ivar_get(active_trace, at_root_span_id /* @root_span */);
|
|
1106
1128
|
VALUE active_span = rb_ivar_get(active_trace, at_active_span_id /* @active_span */);
|
|
1107
|
-
|
|
1129
|
+
// Note: On Ruby 3.x `rb_attr_get` is exactly the same as `rb_ivar_get`. For Ruby 2.x, the difference is that
|
|
1130
|
+
// `rb_ivar_get` can trigger "warning: instance variable @otel_values not initialized" if warnings are enabled and
|
|
1131
|
+
// opentelemetry is not in use, whereas `rb_attr_get` does the lookup without generating the warning.
|
|
1132
|
+
VALUE otel_values = rb_attr_get(active_trace, at_otel_values_id /* @otel_values */);
|
|
1133
|
+
|
|
1134
|
+
VALUE numeric_span_id = Qnil;
|
|
1135
|
+
|
|
1136
|
+
if (otel_values != Qnil) ddtrace_otel_trace_identifiers_for(state, &active_trace, &root_span, &numeric_span_id, active_span, otel_values);
|
|
1137
|
+
|
|
1138
|
+
if (root_span == Qnil || (active_span == Qnil && numeric_span_id == Qnil)) return;
|
|
1108
1139
|
|
|
1109
1140
|
VALUE numeric_local_root_span_id = rb_ivar_get(root_span, at_id_id /* @id */);
|
|
1110
|
-
|
|
1141
|
+
if (active_span != Qnil && numeric_span_id == Qnil) numeric_span_id = rb_ivar_get(active_span, at_id_id /* @id */);
|
|
1111
1142
|
if (numeric_local_root_span_id == Qnil || numeric_span_id == Qnil) return;
|
|
1112
1143
|
|
|
1113
1144
|
trace_identifiers_result->local_root_span_id = NUM2ULL(numeric_local_root_span_id);
|
|
@@ -1115,10 +1146,7 @@ static void trace_identifiers_for(struct thread_context_collector_state *state,
|
|
|
1115
1146
|
|
|
1116
1147
|
trace_identifiers_result->valid = true;
|
|
1117
1148
|
|
|
1118
|
-
if (!state->endpoint_collection_enabled) return;
|
|
1119
|
-
|
|
1120
|
-
VALUE root_span_type = rb_ivar_get(root_span, at_type_id /* @type */);
|
|
1121
|
-
if (root_span_type == Qnil || !should_collect_resource(root_span_type)) return;
|
|
1149
|
+
if (!state->endpoint_collection_enabled || !should_collect_resource(root_span)) return;
|
|
1122
1150
|
|
|
1123
1151
|
VALUE trace_resource = rb_ivar_get(active_trace, at_resource_id /* @resource */);
|
|
1124
1152
|
if (RB_TYPE_P(trace_resource, T_STRING)) {
|
|
@@ -1129,21 +1157,32 @@ static void trace_identifiers_for(struct thread_context_collector_state *state,
|
|
|
1129
1157
|
}
|
|
1130
1158
|
}
|
|
1131
1159
|
|
|
1132
|
-
// We
|
|
1160
|
+
// We opt-in to collecting the resource for spans of types:
|
|
1133
1161
|
// * 'web', for web requests
|
|
1134
|
-
// * proxy', used by the rack integration with request_queuing: true (e.g. also represents a web request)
|
|
1162
|
+
// * 'proxy', used by the rack integration with request_queuing: true (e.g. also represents a web request)
|
|
1163
|
+
// * 'worker', used for sidekiq and similar background job processors
|
|
1135
1164
|
//
|
|
1136
|
-
//
|
|
1165
|
+
// Over time, this list may be expanded.
|
|
1137
1166
|
// Resources MUST NOT include personal identifiable information (PII); this should not be the case with
|
|
1138
1167
|
// ddtrace integrations, but worth mentioning just in case :)
|
|
1139
|
-
static bool should_collect_resource(VALUE
|
|
1168
|
+
static bool should_collect_resource(VALUE root_span) {
|
|
1169
|
+
VALUE root_span_type = rb_ivar_get(root_span, at_type_id /* @type */);
|
|
1170
|
+
if (root_span_type == Qnil) return false;
|
|
1140
1171
|
ENFORCE_TYPE(root_span_type, T_STRING);
|
|
1141
1172
|
|
|
1142
1173
|
int root_span_type_length = RSTRING_LEN(root_span_type);
|
|
1143
1174
|
const char *root_span_type_value = StringValuePtr(root_span_type);
|
|
1144
1175
|
|
|
1145
|
-
|
|
1176
|
+
bool is_web_request =
|
|
1177
|
+
(root_span_type_length == strlen("web") && (memcmp("web", root_span_type_value, strlen("web")) == 0)) ||
|
|
1146
1178
|
(root_span_type_length == strlen("proxy") && (memcmp("proxy", root_span_type_value, strlen("proxy")) == 0));
|
|
1179
|
+
|
|
1180
|
+
if (is_web_request) return true;
|
|
1181
|
+
|
|
1182
|
+
bool is_worker_request =
|
|
1183
|
+
(root_span_type_length == strlen("worker") && (memcmp("worker", root_span_type_value, strlen("worker")) == 0));
|
|
1184
|
+
|
|
1185
|
+
return is_worker_request;
|
|
1147
1186
|
}
|
|
1148
1187
|
|
|
1149
1188
|
// After the Ruby VM forks, this method gets called in the child process to clean up any leftover state from the parent.
|
|
@@ -1299,3 +1338,65 @@ static ddog_CharSlice ruby_value_type_to_class_name(enum ruby_value_type type) {
|
|
|
1299
1338
|
default: return DDOG_CHARSLICE_C("(VM Internal, Missing class)");
|
|
1300
1339
|
}
|
|
1301
1340
|
}
|
|
1341
|
+
|
|
1342
|
+
static VALUE get_otel_current_span_key(struct thread_context_collector_state *state) {
|
|
1343
|
+
if (state->otel_current_span_key == Qnil) {
|
|
1344
|
+
VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
|
|
1345
|
+
VALUE opentelemetry_module = rb_const_get(datadog_module, rb_intern("OpenTelemetry"));
|
|
1346
|
+
VALUE api_module = rb_const_get(opentelemetry_module, rb_intern("API"));
|
|
1347
|
+
VALUE context_module = rb_const_get(api_module, rb_intern_const("Context"));
|
|
1348
|
+
VALUE current_span_key = rb_const_get(context_module, rb_intern_const("CURRENT_SPAN_KEY"));
|
|
1349
|
+
|
|
1350
|
+
if (current_span_key == Qnil) {
|
|
1351
|
+
rb_raise(rb_eRuntimeError, "Unexpected: Missing Datadog::OpenTelemetry::API::Context::CURRENT_SPAN_KEY");
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
state->otel_current_span_key = current_span_key;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
return state->otel_current_span_key;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
// This method gets used when ddtrace is being used indirectly via the otel APIs. Information gets stored slightly
|
|
1361
|
+
// differently, and this codepath handles it.
|
|
1362
|
+
static void ddtrace_otel_trace_identifiers_for(
|
|
1363
|
+
struct thread_context_collector_state *state,
|
|
1364
|
+
VALUE *active_trace,
|
|
1365
|
+
VALUE *root_span,
|
|
1366
|
+
VALUE *numeric_span_id,
|
|
1367
|
+
VALUE active_span,
|
|
1368
|
+
VALUE otel_values
|
|
1369
|
+
) {
|
|
1370
|
+
VALUE resolved_numeric_span_id =
|
|
1371
|
+
active_span == Qnil ?
|
|
1372
|
+
// For traces started from otel spans, the span id will be empty, and the @parent_span_id has the right value
|
|
1373
|
+
rb_ivar_get(*active_trace, at_parent_span_id_id /* @parent_span_id */) :
|
|
1374
|
+
// Regular span created by ddtrace
|
|
1375
|
+
rb_ivar_get(active_span, at_id_id /* @id */);
|
|
1376
|
+
|
|
1377
|
+
if (resolved_numeric_span_id == Qnil) return;
|
|
1378
|
+
|
|
1379
|
+
VALUE otel_current_span_key = get_otel_current_span_key(state);
|
|
1380
|
+
VALUE current_trace = *active_trace;
|
|
1381
|
+
|
|
1382
|
+
// ddtrace uses a different structure when spans are created from otel, where each otel span will have a unique ddtrace
|
|
1383
|
+
// trace and span representing it. Each ddtrace trace is then connected to the previous otel span, forming a linked
|
|
1384
|
+
// list. The local root span is going to be the trace/span we find at the end of this linked list.
|
|
1385
|
+
while (otel_values != Qnil) {
|
|
1386
|
+
VALUE otel_span = rb_hash_lookup(otel_values, otel_current_span_key);
|
|
1387
|
+
if (otel_span == Qnil) break;
|
|
1388
|
+
VALUE next_trace = rb_ivar_get(otel_span, at_datadog_trace_id);
|
|
1389
|
+
if (next_trace == Qnil) break;
|
|
1390
|
+
|
|
1391
|
+
current_trace = next_trace;
|
|
1392
|
+
otel_values = rb_ivar_get(current_trace, at_otel_values_id /* @otel_values */);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
// We found the last trace in the linked list. This contains the local root span
|
|
1396
|
+
VALUE resolved_root_span = rb_ivar_get(current_trace, at_root_span_id /* @root_span */);
|
|
1397
|
+
if (resolved_root_span == Qnil) return;
|
|
1398
|
+
|
|
1399
|
+
*root_span = resolved_root_span;
|
|
1400
|
+
*active_trace = current_trace;
|
|
1401
|
+
*numeric_span_id = resolved_numeric_span_id;
|
|
1402
|
+
}
|
data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/extconf.rb
RENAMED
|
@@ -100,7 +100,7 @@ add_compiler_flag '-Wno-declaration-after-statement'
|
|
|
100
100
|
add_compiler_flag '-Werror-implicit-function-declaration'
|
|
101
101
|
|
|
102
102
|
# The native extension is not intended to expose any symbols/functions for other native libraries to use;
|
|
103
|
-
# the sole exception being `
|
|
103
|
+
# the sole exception being `Init_datadog_profiling_native_extension` which needs to be visible for Ruby to call it when
|
|
104
104
|
# it `dlopen`s the library.
|
|
105
105
|
#
|
|
106
106
|
# By setting this compiler flag, we tell it to assume that everything is private unless explicitly stated.
|
|
@@ -237,7 +237,7 @@ Logging.message("[ddtrace] After pkg-config $LDFLAGS were set to: #{$LDFLAGS.ins
|
|
|
237
237
|
# This makes it easier for development (avoids "oops I forgot to rebuild when I switched my Ruby") and ensures that
|
|
238
238
|
# the wrong library is never loaded.
|
|
239
239
|
# When requiring, we need to use the exact same string, including the version and the platform.
|
|
240
|
-
EXTENSION_NAME = "
|
|
240
|
+
EXTENSION_NAME = "datadog_profiling_native_extension.#{RUBY_VERSION}_#{RUBY_PLATFORM}".freeze
|
|
241
241
|
|
|
242
242
|
if Datadog::Profiling::NativeExtensionHelpers::CAN_USE_MJIT_HEADER
|
|
243
243
|
mjit_header_file_name = "rb_mjit_min_header-#{RUBY_VERSION}.h"
|
data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/heap_recorder.c
RENAMED
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
#define CAN_APPLY_GC_FORCE_RECYCLE_BUG_WORKAROUND
|
|
11
11
|
#endif
|
|
12
12
|
|
|
13
|
+
// Minimum age (in GC generations) of heap objects we want to include in heap
|
|
14
|
+
// recorder iterations. Object with age 0 represent objects that have yet to undergo
|
|
15
|
+
// a GC and, thus, may just be noise/trash at instant of iteration and are usually not
|
|
16
|
+
// relevant for heap profiles as the great majority should be trivially reclaimed
|
|
17
|
+
// during the next GC.
|
|
18
|
+
#define ITERATION_MIN_AGE 1
|
|
19
|
+
|
|
13
20
|
// A compact representation of a stacktrace frame for a heap allocation.
|
|
14
21
|
typedef struct {
|
|
15
22
|
char *name;
|
|
@@ -137,6 +144,11 @@ struct heap_recorder {
|
|
|
137
144
|
// mutation of the data so iteration can occur without acquiring a lock.
|
|
138
145
|
// NOTE: Contrary to object_records, this table has no ownership of its data.
|
|
139
146
|
st_table *object_records_snapshot;
|
|
147
|
+
// The GC gen/epoch/count in which we prepared the current iteration.
|
|
148
|
+
//
|
|
149
|
+
// This enables us to calculate the age of iterated objects in the above snapshot by
|
|
150
|
+
// comparing it against an object's alloc_gen.
|
|
151
|
+
size_t iteration_gen;
|
|
140
152
|
|
|
141
153
|
// Data for a heap recording that was started but not yet ended
|
|
142
154
|
recording active_recording;
|
|
@@ -146,6 +158,13 @@ struct heap_recorder {
|
|
|
146
158
|
|
|
147
159
|
// Sampling state
|
|
148
160
|
uint num_recordings_skipped;
|
|
161
|
+
|
|
162
|
+
struct stats_last_update {
|
|
163
|
+
size_t objects_alive;
|
|
164
|
+
size_t objects_dead;
|
|
165
|
+
size_t objects_skipped;
|
|
166
|
+
size_t objects_frozen;
|
|
167
|
+
} stats_last_update;
|
|
149
168
|
};
|
|
150
169
|
static heap_record* get_or_create_heap_record(heap_recorder*, ddog_prof_Slice_Location);
|
|
151
170
|
static void cleanup_heap_record_if_unused(heap_recorder*, heap_record*);
|
|
@@ -353,11 +372,16 @@ void heap_recorder_prepare_iteration(heap_recorder *heap_recorder) {
|
|
|
353
372
|
return;
|
|
354
373
|
}
|
|
355
374
|
|
|
375
|
+
heap_recorder->iteration_gen = rb_gc_count();
|
|
376
|
+
|
|
356
377
|
if (heap_recorder->object_records_snapshot != NULL) {
|
|
357
378
|
// we could trivially handle this but we raise to highlight and catch unexpected usages.
|
|
358
379
|
rb_raise(rb_eRuntimeError, "New heap recorder iteration prepared without the previous one having been finished.");
|
|
359
380
|
}
|
|
360
381
|
|
|
382
|
+
// Reset last update stats, we'll be building them from scratch during the st_foreach call below
|
|
383
|
+
heap_recorder->stats_last_update = (struct stats_last_update) {};
|
|
384
|
+
|
|
361
385
|
st_foreach(heap_recorder->object_records, st_object_record_update, (st_data_t) heap_recorder);
|
|
362
386
|
|
|
363
387
|
heap_recorder->object_records_snapshot = st_copy(heap_recorder->object_records);
|
|
@@ -413,6 +437,22 @@ bool heap_recorder_for_each_live_object(
|
|
|
413
437
|
return true;
|
|
414
438
|
}
|
|
415
439
|
|
|
440
|
+
VALUE heap_recorder_state_snapshot(heap_recorder *heap_recorder) {
|
|
441
|
+
VALUE arguments[] = {
|
|
442
|
+
ID2SYM(rb_intern("num_object_records")), /* => */ LONG2NUM(heap_recorder->object_records->num_entries),
|
|
443
|
+
ID2SYM(rb_intern("num_heap_records")), /* => */ LONG2NUM(heap_recorder->heap_records->num_entries),
|
|
444
|
+
|
|
445
|
+
// Stats as of last update
|
|
446
|
+
ID2SYM(rb_intern("last_update_objects_alive")), /* => */ LONG2NUM(heap_recorder->stats_last_update.objects_alive),
|
|
447
|
+
ID2SYM(rb_intern("last_update_objects_dead")), /* => */ LONG2NUM(heap_recorder->stats_last_update.objects_dead),
|
|
448
|
+
ID2SYM(rb_intern("last_update_objects_skipped")), /* => */ LONG2NUM(heap_recorder->stats_last_update.objects_skipped),
|
|
449
|
+
ID2SYM(rb_intern("last_update_objects_frozen")), /* => */ LONG2NUM(heap_recorder->stats_last_update.objects_frozen),
|
|
450
|
+
};
|
|
451
|
+
VALUE hash = rb_hash_new();
|
|
452
|
+
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(hash, arguments[i], arguments[i+1]);
|
|
453
|
+
return hash;
|
|
454
|
+
}
|
|
455
|
+
|
|
416
456
|
void heap_recorder_testonly_assert_hash_matches(ddog_prof_Slice_Location locations) {
|
|
417
457
|
heap_stack *stack = heap_stack_new(locations);
|
|
418
458
|
heap_record_key stack_based_key = (heap_record_key) {
|
|
@@ -459,6 +499,13 @@ static int st_object_record_entry_free(DDTRACE_UNUSED st_data_t key, st_data_t v
|
|
|
459
499
|
return ST_DELETE;
|
|
460
500
|
}
|
|
461
501
|
|
|
502
|
+
// Check to see if an object should not be included in a heap recorder iteration.
|
|
503
|
+
// This centralizes the checking logic to ensure it's equally applied between
|
|
504
|
+
// preparation and iteration codepaths.
|
|
505
|
+
static inline bool should_exclude_from_iteration(object_record *obj_record) {
|
|
506
|
+
return obj_record->object_data.gen_age < ITERATION_MIN_AGE;
|
|
507
|
+
}
|
|
508
|
+
|
|
462
509
|
static int st_object_record_update(st_data_t key, st_data_t value, st_data_t extra_arg) {
|
|
463
510
|
long obj_id = (long) key;
|
|
464
511
|
object_record *record = (object_record*) value;
|
|
@@ -466,9 +513,24 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
|
|
|
466
513
|
|
|
467
514
|
VALUE ref;
|
|
468
515
|
|
|
516
|
+
size_t iteration_gen = recorder->iteration_gen;
|
|
517
|
+
size_t alloc_gen = record->object_data.alloc_gen;
|
|
518
|
+
// Guard against potential overflows given unsigned types here.
|
|
519
|
+
record->object_data.gen_age = alloc_gen < iteration_gen ? iteration_gen - alloc_gen : 0;
|
|
520
|
+
|
|
521
|
+
if (should_exclude_from_iteration(record)) {
|
|
522
|
+
// If an object won't be included in the current iteration, there's
|
|
523
|
+
// no point checking for liveness or updating its size, so exit early.
|
|
524
|
+
// NOTE: This means that there should be an equivalent check during actual
|
|
525
|
+
// iteration otherwise we'd iterate/expose stale object data.
|
|
526
|
+
recorder->stats_last_update.objects_skipped++;
|
|
527
|
+
return ST_CONTINUE;
|
|
528
|
+
}
|
|
529
|
+
|
|
469
530
|
if (!ruby_ref_from_id(LONG2NUM(obj_id), &ref)) {
|
|
470
531
|
// Id no longer associated with a valid ref. Need to delete this object record!
|
|
471
532
|
on_committed_object_record_cleanup(recorder, record);
|
|
533
|
+
recorder->stats_last_update.objects_dead++;
|
|
472
534
|
return ST_DELETE;
|
|
473
535
|
}
|
|
474
536
|
|
|
@@ -503,6 +565,7 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
|
|
|
503
565
|
RB_FL_SET(ref, RUBY_FL_SEEN_OBJ_ID);
|
|
504
566
|
|
|
505
567
|
on_committed_object_record_cleanup(recorder, record);
|
|
568
|
+
recorder->stats_last_update.objects_dead++;
|
|
506
569
|
return ST_DELETE;
|
|
507
570
|
}
|
|
508
571
|
|
|
@@ -516,6 +579,11 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
|
|
|
516
579
|
record->object_data.is_frozen = RB_OBJ_FROZEN(ref);
|
|
517
580
|
}
|
|
518
581
|
|
|
582
|
+
recorder->stats_last_update.objects_alive++;
|
|
583
|
+
if (record->object_data.is_frozen) {
|
|
584
|
+
recorder->stats_last_update.objects_frozen++;
|
|
585
|
+
}
|
|
586
|
+
|
|
519
587
|
return ST_CONTINUE;
|
|
520
588
|
}
|
|
521
589
|
|
|
@@ -525,8 +593,16 @@ static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t val
|
|
|
525
593
|
const heap_stack *stack = record->heap_record->stack;
|
|
526
594
|
iteration_context *context = (iteration_context*) extra;
|
|
527
595
|
|
|
528
|
-
|
|
596
|
+
const heap_recorder *recorder = context->heap_recorder;
|
|
597
|
+
|
|
598
|
+
if (should_exclude_from_iteration(record)) {
|
|
599
|
+
// Skip objects that should not be included in iteration
|
|
600
|
+
// NOTE: This matches the short-circuiting condition in st_object_record_update
|
|
601
|
+
// and prevents iteration over stale objects.
|
|
602
|
+
return ST_CONTINUE;
|
|
603
|
+
}
|
|
529
604
|
|
|
605
|
+
ddog_prof_Location *locations = recorder->reusable_locations;
|
|
530
606
|
for (uint16_t i = 0; i < stack->frames_len; i++) {
|
|
531
607
|
const heap_frame *frame = &stack->frames[i];
|
|
532
608
|
ddog_prof_Location *location = &locations[i];
|
|
@@ -725,9 +801,10 @@ void object_record_free(object_record *record) {
|
|
|
725
801
|
|
|
726
802
|
VALUE object_record_inspect(object_record *record) {
|
|
727
803
|
heap_frame top_frame = record->heap_record->stack->frames[0];
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
804
|
+
live_object_data object_data = record->object_data;
|
|
805
|
+
VALUE inspect = rb_sprintf("obj_id=%ld weight=%d size=%zu location=%s:%d alloc_gen=%zu gen_age=%zu frozen=%d ",
|
|
806
|
+
record->obj_id, object_data.weight, object_data.size, top_frame.filename,
|
|
807
|
+
(int) top_frame.line, object_data.alloc_gen, object_data.gen_age, object_data.is_frozen);
|
|
731
808
|
|
|
732
809
|
const char *class = record->object_data.class;
|
|
733
810
|
if (class != NULL) {
|
data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/heap_recorder.h
RENAMED
|
@@ -27,7 +27,9 @@ typedef struct live_object_data {
|
|
|
27
27
|
// could be seen as being representative of 50 objects.
|
|
28
28
|
unsigned int weight;
|
|
29
29
|
|
|
30
|
-
// Size of this object
|
|
30
|
+
// Size of this object in memory.
|
|
31
|
+
// NOTE: This only gets updated during heap_recorder_prepare_iteration and only
|
|
32
|
+
// for those objects that meet the minimum iteration age requirements.
|
|
31
33
|
size_t size;
|
|
32
34
|
|
|
33
35
|
// The class of the object that we're tracking.
|
|
@@ -39,6 +41,10 @@ typedef struct live_object_data {
|
|
|
39
41
|
// This enables us to calculate the age of this object in terms of GC executions.
|
|
40
42
|
size_t alloc_gen;
|
|
41
43
|
|
|
44
|
+
// The age of this object in terms of GC generations.
|
|
45
|
+
// NOTE: This only gets updated during heap_recorder_prepare_iteration
|
|
46
|
+
size_t gen_age;
|
|
47
|
+
|
|
42
48
|
// Whether this object was previously seen as being frozen. If this is the case,
|
|
43
49
|
// we'll skip any further size updates since frozen objects are supposed to be
|
|
44
50
|
// immutable.
|
|
@@ -144,6 +150,11 @@ bool heap_recorder_for_each_live_object(
|
|
|
144
150
|
bool (*for_each_callback)(heap_recorder_iteration_data data, void* extra_arg),
|
|
145
151
|
void *for_each_callback_extra_arg);
|
|
146
152
|
|
|
153
|
+
// Return a Ruby hash containing a snapshot of this recorder's interesting state at calling time.
|
|
154
|
+
// WARN: This allocates in the Ruby VM and therefore should not be called without the
|
|
155
|
+
// VM lock or during GC.
|
|
156
|
+
VALUE heap_recorder_state_snapshot(heap_recorder *heap_recorder);
|
|
157
|
+
|
|
147
158
|
// v--- TEST-ONLY APIs ---v
|
|
148
159
|
|
|
149
160
|
// Assert internal hashing logic is valid for the provided locations and its
|
data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/http_transport.c
RENAMED
|
@@ -30,7 +30,7 @@ inline static ddog_ByteSlice byte_slice_from_ruby_string(VALUE string);
|
|
|
30
30
|
static VALUE _native_validate_exporter(VALUE self, VALUE exporter_configuration);
|
|
31
31
|
static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array);
|
|
32
32
|
static VALUE handle_exporter_failure(ddog_prof_Exporter_NewResult exporter_result);
|
|
33
|
-
static
|
|
33
|
+
static ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration);
|
|
34
34
|
static ddog_Vec_Tag convert_tags(VALUE tags_as_array);
|
|
35
35
|
static void safely_log_failure_to_process_tag(ddog_Vec_Tag tags, VALUE err_details);
|
|
36
36
|
static VALUE _native_do_export(
|
|
@@ -46,17 +46,17 @@ static VALUE _native_do_export(
|
|
|
46
46
|
VALUE code_provenance_file_name,
|
|
47
47
|
VALUE code_provenance_data,
|
|
48
48
|
VALUE tags_as_array,
|
|
49
|
-
VALUE internal_metadata_json
|
|
49
|
+
VALUE internal_metadata_json,
|
|
50
|
+
VALUE info_json
|
|
50
51
|
);
|
|
51
52
|
static void *call_exporter_without_gvl(void *call_args);
|
|
52
53
|
static void interrupt_exporter_call(void *cancel_token);
|
|
53
|
-
static VALUE ddtrace_version(void);
|
|
54
54
|
|
|
55
55
|
void http_transport_init(VALUE profiling_module) {
|
|
56
56
|
VALUE http_transport_class = rb_define_class_under(profiling_module, "HttpTransport", rb_cObject);
|
|
57
57
|
|
|
58
58
|
rb_define_singleton_method(http_transport_class, "_native_validate_exporter", _native_validate_exporter, 1);
|
|
59
|
-
rb_define_singleton_method(http_transport_class, "_native_do_export", _native_do_export,
|
|
59
|
+
rb_define_singleton_method(http_transport_class, "_native_do_export", _native_do_export, 13);
|
|
60
60
|
|
|
61
61
|
ok_symbol = ID2SYM(rb_intern_const("ok"));
|
|
62
62
|
error_symbol = ID2SYM(rb_intern_const("error"));
|
|
@@ -94,7 +94,7 @@ static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration
|
|
|
94
94
|
|
|
95
95
|
// This needs to be called BEFORE convert_tags since it can raise an exception and thus cause the ddog_Vec_Tag
|
|
96
96
|
// to be leaked.
|
|
97
|
-
|
|
97
|
+
ddog_prof_Endpoint endpoint = endpoint_from(exporter_configuration);
|
|
98
98
|
|
|
99
99
|
ddog_Vec_Tag tags = convert_tags(tags_as_array);
|
|
100
100
|
|
|
@@ -116,7 +116,7 @@ static VALUE handle_exporter_failure(ddog_prof_Exporter_NewResult exporter_resul
|
|
|
116
116
|
rb_ary_new_from_args(2, error_symbol, get_error_details_and_drop(&exporter_result.err));
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
static
|
|
119
|
+
static ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration) {
|
|
120
120
|
ENFORCE_TYPE(exporter_configuration, T_ARRAY);
|
|
121
121
|
|
|
122
122
|
ID working_mode = SYM2ID(rb_ary_entry(exporter_configuration, 0)); // SYM2ID verifies its input so we can do this safely
|
|
@@ -131,12 +131,12 @@ static ddog_Endpoint endpoint_from(VALUE exporter_configuration) {
|
|
|
131
131
|
ENFORCE_TYPE(site, T_STRING);
|
|
132
132
|
ENFORCE_TYPE(api_key, T_STRING);
|
|
133
133
|
|
|
134
|
-
return
|
|
134
|
+
return ddog_prof_Endpoint_agentless(char_slice_from_ruby_string(site), char_slice_from_ruby_string(api_key));
|
|
135
135
|
} else { // agent_id
|
|
136
136
|
VALUE base_url = rb_ary_entry(exporter_configuration, 1);
|
|
137
137
|
ENFORCE_TYPE(base_url, T_STRING);
|
|
138
138
|
|
|
139
|
-
return
|
|
139
|
+
return ddog_prof_Endpoint_agent(char_slice_from_ruby_string(base_url));
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
|
|
@@ -208,6 +208,7 @@ static VALUE perform_export(
|
|
|
208
208
|
ddog_prof_Exporter_Slice_File files_to_export_unmodified,
|
|
209
209
|
ddog_Vec_Tag *additional_tags,
|
|
210
210
|
ddog_CharSlice internal_metadata,
|
|
211
|
+
ddog_CharSlice info,
|
|
211
212
|
uint64_t timeout_milliseconds
|
|
212
213
|
) {
|
|
213
214
|
ddog_prof_ProfiledEndpointsStats *endpoints_stats = NULL; // Not in use yet
|
|
@@ -220,6 +221,7 @@ static VALUE perform_export(
|
|
|
220
221
|
additional_tags,
|
|
221
222
|
endpoints_stats,
|
|
222
223
|
&internal_metadata,
|
|
224
|
+
&info,
|
|
223
225
|
timeout_milliseconds
|
|
224
226
|
);
|
|
225
227
|
|
|
@@ -290,7 +292,8 @@ static VALUE _native_do_export(
|
|
|
290
292
|
VALUE code_provenance_file_name,
|
|
291
293
|
VALUE code_provenance_data,
|
|
292
294
|
VALUE tags_as_array,
|
|
293
|
-
VALUE internal_metadata_json
|
|
295
|
+
VALUE internal_metadata_json,
|
|
296
|
+
VALUE info_json
|
|
294
297
|
) {
|
|
295
298
|
ENFORCE_TYPE(upload_timeout_milliseconds, T_FIXNUM);
|
|
296
299
|
ENFORCE_TYPE(start_timespec_seconds, T_FIXNUM);
|
|
@@ -301,6 +304,7 @@ static VALUE _native_do_export(
|
|
|
301
304
|
ENFORCE_TYPE(pprof_data, T_STRING);
|
|
302
305
|
ENFORCE_TYPE(code_provenance_file_name, T_STRING);
|
|
303
306
|
ENFORCE_TYPE(internal_metadata_json, T_STRING);
|
|
307
|
+
ENFORCE_TYPE(info_json, T_STRING);
|
|
304
308
|
|
|
305
309
|
// Code provenance can be disabled and in that case will be set to nil
|
|
306
310
|
bool have_code_provenance = !NIL_P(code_provenance_data);
|
|
@@ -335,6 +339,7 @@ static VALUE _native_do_export(
|
|
|
335
339
|
|
|
336
340
|
ddog_Vec_Tag *null_additional_tags = NULL;
|
|
337
341
|
ddog_CharSlice internal_metadata = char_slice_from_ruby_string(internal_metadata_json);
|
|
342
|
+
ddog_CharSlice info = char_slice_from_ruby_string(info_json);
|
|
338
343
|
|
|
339
344
|
ddog_prof_Exporter_NewResult exporter_result = create_exporter(exporter_configuration, tags_as_array);
|
|
340
345
|
// Note: Do not add anything that can raise exceptions after this line, as otherwise the exporter memory will leak
|
|
@@ -350,6 +355,7 @@ static VALUE _native_do_export(
|
|
|
350
355
|
files_to_export_unmodified,
|
|
351
356
|
null_additional_tags,
|
|
352
357
|
internal_metadata,
|
|
358
|
+
info,
|
|
353
359
|
timeout_milliseconds
|
|
354
360
|
);
|
|
355
361
|
}
|
|
@@ -367,13 +373,3 @@ static void *call_exporter_without_gvl(void *call_args) {
|
|
|
367
373
|
static void interrupt_exporter_call(void *cancel_token) {
|
|
368
374
|
ddog_CancellationToken_cancel((ddog_CancellationToken *) cancel_token);
|
|
369
375
|
}
|
|
370
|
-
|
|
371
|
-
static VALUE ddtrace_version(void) {
|
|
372
|
-
VALUE ddtrace_module = rb_const_get(rb_cObject, rb_intern("DDTrace"));
|
|
373
|
-
ENFORCE_TYPE(ddtrace_module, T_MODULE);
|
|
374
|
-
VALUE version_module = rb_const_get(ddtrace_module, rb_intern("VERSION"));
|
|
375
|
-
ENFORCE_TYPE(version_module, T_MODULE);
|
|
376
|
-
VALUE version_string = rb_const_get(version_module, rb_intern("STRING"));
|
|
377
|
-
ENFORCE_TYPE(version_string, T_STRING);
|
|
378
|
-
return version_string;
|
|
379
|
-
}
|
|
@@ -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 = '~>
|
|
18
|
+
LIBDATADOG_VERSION = '~> 7.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'
|
|
@@ -29,7 +29,7 @@ module Datadog
|
|
|
29
29
|
# native extension), we need to add a "runpath" -- a list of folders to search for libdatadog.
|
|
30
30
|
#
|
|
31
31
|
# This runpath gets hardcoded at native library linking time. You can look at it using the `readelf` tool in
|
|
32
|
-
# Linux: e.g. `readelf -d
|
|
32
|
+
# Linux: e.g. `readelf -d datadog_profiling_native_extension.2.7.3_x86_64-linux.so`.
|
|
33
33
|
#
|
|
34
34
|
# In older versions of ddtrace, we only set as runpath an absolute path to libdatadog.
|
|
35
35
|
# (This gets set automatically by the call
|
|
@@ -305,8 +305,8 @@ module Datadog
|
|
|
305
305
|
no_binaries_for_current_platform = explain_issue(
|
|
306
306
|
'the `libdatadog` gem installed on your system is missing binaries for your',
|
|
307
307
|
'platform variant.',
|
|
308
|
-
"(Your platform: `#{
|
|
309
|
-
'(Available binaries:
|
|
308
|
+
"(Your platform: `#{Libdatadog.current_platform}`)",
|
|
309
|
+
'(Available binaries:',
|
|
310
310
|
"`#{Libdatadog.available_binaries.join('`, `')}`)",
|
|
311
311
|
suggested: CONTACT_SUPPORT,
|
|
312
312
|
)
|
|
@@ -876,3 +876,17 @@ static inline int ddtrace_imemo_type(VALUE imemo) {
|
|
|
876
876
|
return NULL;
|
|
877
877
|
}
|
|
878
878
|
#endif
|
|
879
|
+
|
|
880
|
+
// This is used to workaround a VM bug. See "handle_sampling_signal" in "collectors_cpu_and_wall_time_worker" for details.
|
|
881
|
+
#ifdef NO_POSTPONED_TRIGGER
|
|
882
|
+
void *objspace_ptr_for_gc_finalize_deferred_workaround(void) {
|
|
883
|
+
rb_vm_t *vm =
|
|
884
|
+
#ifndef NO_GET_VM // TODO: Inline GET_VM below once we drop support in dd-trace-rb 2.x for < Ruby 2.5
|
|
885
|
+
GET_VM();
|
|
886
|
+
#else
|
|
887
|
+
thread_struct_from_object(rb_thread_current())->vm;
|
|
888
|
+
#endif
|
|
889
|
+
|
|
890
|
+
return vm->objspace;
|
|
891
|
+
}
|
|
892
|
+
#endif
|
data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/profiling.c
RENAMED
|
@@ -38,7 +38,7 @@ static VALUE _native_enforce_success(DDTRACE_UNUSED VALUE _self, VALUE syserr_er
|
|
|
38
38
|
static void *trigger_enforce_success(void *trigger_args);
|
|
39
39
|
static VALUE _native_malloc_stats(DDTRACE_UNUSED VALUE _self);
|
|
40
40
|
|
|
41
|
-
void DDTRACE_EXPORT
|
|
41
|
+
void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) {
|
|
42
42
|
VALUE datadog_module = rb_define_module("Datadog");
|
|
43
43
|
VALUE profiling_module = rb_define_module_under(datadog_module, "Profiling");
|
|
44
44
|
VALUE native_extension_module = rb_define_module_under(profiling_module, "NativeExtension");
|
data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/ruby_helpers.c
RENAMED
|
@@ -255,3 +255,13 @@ VALUE ruby_safe_inspect(VALUE obj) {
|
|
|
255
255
|
return rb_str_new_cstr("(Not inspectable)");
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
|
+
|
|
259
|
+
VALUE ddtrace_version(void) {
|
|
260
|
+
VALUE ddtrace_module = rb_const_get(rb_cObject, rb_intern("DDTrace"));
|
|
261
|
+
ENFORCE_TYPE(ddtrace_module, T_MODULE);
|
|
262
|
+
VALUE version_module = rb_const_get(ddtrace_module, rb_intern("VERSION"));
|
|
263
|
+
ENFORCE_TYPE(version_module, T_MODULE);
|
|
264
|
+
VALUE version_string = rb_const_get(version_module, rb_intern("STRING"));
|
|
265
|
+
ENFORCE_TYPE(version_string, T_STRING);
|
|
266
|
+
return version_string;
|
|
267
|
+
}
|