ddtrace 1.19.0 → 1.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +93 -2
- 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 +237 -65
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +422 -0
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +101 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_thread_context.c +92 -2
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/extconf.rb +5 -2
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/helpers.h +4 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/http_transport.c +10 -14
- 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 +17 -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 +2 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/time_helpers.h +2 -0
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +2 -1
- data/lib/datadog/core/configuration/components.rb +5 -5
- data/lib/datadog/core/configuration/option.rb +1 -1
- data/lib/datadog/core/configuration/settings.rb +107 -46
- data/lib/datadog/core/diagnostics/environment_logger.rb +4 -3
- data/lib/datadog/core/environment/class_count.rb +6 -6
- data/lib/datadog/core/environment/git.rb +25 -0
- data/lib/datadog/core/environment/identity.rb +18 -48
- data/lib/datadog/core/git/ext.rb +2 -23
- data/lib/datadog/core/remote/component.rb +25 -12
- data/lib/datadog/core/remote/ext.rb +1 -0
- data/lib/datadog/core/remote/negotiation.rb +2 -2
- data/lib/datadog/core/remote/tie/tracing.rb +39 -0
- data/lib/datadog/core/remote/tie.rb +27 -0
- data/lib/datadog/core/remote/worker.rb +7 -4
- data/lib/datadog/core/transport/ext.rb +2 -0
- data/lib/datadog/core/utils/url.rb +25 -0
- data/lib/datadog/opentelemetry/sdk/propagator.rb +3 -2
- data/lib/datadog/opentelemetry.rb +3 -0
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +6 -2
- data/lib/datadog/profiling/collectors/info.rb +101 -0
- data/lib/datadog/profiling/component.rb +14 -30
- data/lib/datadog/profiling/exporter.rb +19 -5
- 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 +5 -5
- data/lib/datadog/profiling/native_extension.rb +1 -1
- 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 +2 -2
- data/lib/datadog/tracing/configuration/ext.rb +0 -1
- data/lib/datadog/tracing/configuration/settings.rb +2 -1
- data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/action_cable/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/action_mailer/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/action_pack/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/action_view/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/active_job/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/active_model_serializers/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/active_record/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/active_support/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/analytics.rb +0 -1
- data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/aws/ext.rb +1 -0
- 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/dalli/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/dalli/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/delayed_job/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/excon/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/extensions.rb +6 -2
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +7 -0
- data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +1 -1
- data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +5 -0
- data/lib/datadog/tracing/contrib/grape/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +2 -2
- data/lib/datadog/tracing/contrib/http/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/httpclient/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/httprb/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/kafka/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/opensearch/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/pg/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +11 -4
- data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/presto/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/qless/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/qless/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/que/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/que/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/racecar/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +37 -6
- data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/rails/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rails/patcher.rb +16 -0
- data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/rake/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/redis/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/redis/instrumentation.rb +2 -2
- data/lib/datadog/tracing/contrib/redis/patcher.rb +34 -21
- data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/resque/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/roda/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/roda/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/sequel/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/shoryuken/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +6 -3
- data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/sneakers/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/stripe/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/sucker_punch/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +58 -0
- data/lib/datadog/tracing/contrib/trilogy/ext.rb +27 -0
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +94 -0
- data/lib/datadog/tracing/contrib/trilogy/integration.rb +43 -0
- data/lib/datadog/tracing/contrib/trilogy/patcher.rb +31 -0
- data/lib/datadog/tracing/contrib.rb +1 -0
- data/lib/datadog/tracing/metadata/ext.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/datadog/tracing.rb +8 -2
- data/lib/ddtrace/version.rb +1 -1
- metadata +62 -50
- 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/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}/heap_recorder.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/heap_recorder.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.c +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
@@ -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.
|
@@ -218,6 +223,14 @@ 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
|
|
@@ -917,6 +935,7 @@ static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instanc
|
|
917
935
|
));
|
918
936
|
rb_str_concat(result, rb_sprintf(" main_thread=%"PRIsVALUE, state->main_thread));
|
919
937
|
rb_str_concat(result, rb_sprintf(" gc_tracking=%"PRIsVALUE, gc_tracking_as_ruby_hash(state)));
|
938
|
+
rb_str_concat(result, rb_sprintf(" otel_current_span_key=%"PRIsVALUE, state->otel_current_span_key));
|
920
939
|
|
921
940
|
return result;
|
922
941
|
}
|
@@ -1104,10 +1123,19 @@ static void trace_identifiers_for(struct thread_context_collector_state *state,
|
|
1104
1123
|
|
1105
1124
|
VALUE root_span = rb_ivar_get(active_trace, at_root_span_id /* @root_span */);
|
1106
1125
|
VALUE active_span = rb_ivar_get(active_trace, at_active_span_id /* @active_span */);
|
1107
|
-
|
1126
|
+
// Note: On Ruby 3.x `rb_attr_get` is exactly the same as `rb_ivar_get`. For Ruby 2.x, the difference is that
|
1127
|
+
// `rb_ivar_get` can trigger "warning: instance variable @otel_values not initialized" if warnings are enabled and
|
1128
|
+
// opentelemetry is not in use, whereas `rb_attr_get` does the lookup without generating the warning.
|
1129
|
+
VALUE otel_values = rb_attr_get(active_trace, at_otel_values_id /* @otel_values */);
|
1130
|
+
|
1131
|
+
VALUE numeric_span_id = Qnil;
|
1132
|
+
|
1133
|
+
if (otel_values != Qnil) ddtrace_otel_trace_identifiers_for(state, &active_trace, &root_span, &numeric_span_id, active_span, otel_values);
|
1134
|
+
|
1135
|
+
if (root_span == Qnil || (active_span == Qnil && numeric_span_id == Qnil)) return;
|
1108
1136
|
|
1109
1137
|
VALUE numeric_local_root_span_id = rb_ivar_get(root_span, at_id_id /* @id */);
|
1110
|
-
|
1138
|
+
if (active_span != Qnil && numeric_span_id == Qnil) numeric_span_id = rb_ivar_get(active_span, at_id_id /* @id */);
|
1111
1139
|
if (numeric_local_root_span_id == Qnil || numeric_span_id == Qnil) return;
|
1112
1140
|
|
1113
1141
|
trace_identifiers_result->local_root_span_id = NUM2ULL(numeric_local_root_span_id);
|
@@ -1299,3 +1327,65 @@ static ddog_CharSlice ruby_value_type_to_class_name(enum ruby_value_type type) {
|
|
1299
1327
|
default: return DDOG_CHARSLICE_C("(VM Internal, Missing class)");
|
1300
1328
|
}
|
1301
1329
|
}
|
1330
|
+
|
1331
|
+
static VALUE get_otel_current_span_key(struct thread_context_collector_state *state) {
|
1332
|
+
if (state->otel_current_span_key == Qnil) {
|
1333
|
+
VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
|
1334
|
+
VALUE opentelemetry_module = rb_const_get(datadog_module, rb_intern("OpenTelemetry"));
|
1335
|
+
VALUE api_module = rb_const_get(opentelemetry_module, rb_intern("API"));
|
1336
|
+
VALUE context_module = rb_const_get(api_module, rb_intern_const("Context"));
|
1337
|
+
VALUE current_span_key = rb_const_get(context_module, rb_intern_const("CURRENT_SPAN_KEY"));
|
1338
|
+
|
1339
|
+
if (current_span_key == Qnil) {
|
1340
|
+
rb_raise(rb_eRuntimeError, "Unexpected: Missing Datadog::OpenTelemetry::API::Context::CURRENT_SPAN_KEY");
|
1341
|
+
}
|
1342
|
+
|
1343
|
+
state->otel_current_span_key = current_span_key;
|
1344
|
+
}
|
1345
|
+
|
1346
|
+
return state->otel_current_span_key;
|
1347
|
+
}
|
1348
|
+
|
1349
|
+
// This method gets used when ddtrace is being used indirectly via the otel APIs. Information gets stored slightly
|
1350
|
+
// differently, and this codepath handles it.
|
1351
|
+
static void ddtrace_otel_trace_identifiers_for(
|
1352
|
+
struct thread_context_collector_state *state,
|
1353
|
+
VALUE *active_trace,
|
1354
|
+
VALUE *root_span,
|
1355
|
+
VALUE *numeric_span_id,
|
1356
|
+
VALUE active_span,
|
1357
|
+
VALUE otel_values
|
1358
|
+
) {
|
1359
|
+
VALUE resolved_numeric_span_id =
|
1360
|
+
active_span == Qnil ?
|
1361
|
+
// For traces started from otel spans, the span id will be empty, and the @parent_span_id has the right value
|
1362
|
+
rb_ivar_get(*active_trace, at_parent_span_id_id /* @parent_span_id */) :
|
1363
|
+
// Regular span created by ddtrace
|
1364
|
+
rb_ivar_get(active_span, at_id_id /* @id */);
|
1365
|
+
|
1366
|
+
if (resolved_numeric_span_id == Qnil) return;
|
1367
|
+
|
1368
|
+
VALUE otel_current_span_key = get_otel_current_span_key(state);
|
1369
|
+
VALUE current_trace = *active_trace;
|
1370
|
+
|
1371
|
+
// ddtrace uses a different structure when spans are created from otel, where each otel span will have a unique ddtrace
|
1372
|
+
// trace and span representing it. Each ddtrace trace is then connected to the previous otel span, forming a linked
|
1373
|
+
// list. The local root span is going to be the trace/span we find at the end of this linked list.
|
1374
|
+
while (otel_values != Qnil) {
|
1375
|
+
VALUE otel_span = rb_hash_lookup(otel_values, otel_current_span_key);
|
1376
|
+
if (otel_span == Qnil) break;
|
1377
|
+
VALUE next_trace = rb_ivar_get(otel_span, at_datadog_trace_id);
|
1378
|
+
if (next_trace == Qnil) break;
|
1379
|
+
|
1380
|
+
current_trace = next_trace;
|
1381
|
+
otel_values = rb_ivar_get(current_trace, at_otel_values_id /* @otel_values */);
|
1382
|
+
}
|
1383
|
+
|
1384
|
+
// We found the last trace in the linked list. This contains the local root span
|
1385
|
+
VALUE resolved_root_span = rb_ivar_get(current_trace, at_root_span_id /* @root_span */);
|
1386
|
+
if (resolved_root_span == Qnil) return;
|
1387
|
+
|
1388
|
+
*root_span = resolved_root_span;
|
1389
|
+
*active_trace = current_trace;
|
1390
|
+
*numeric_span_id = resolved_numeric_span_id;
|
1391
|
+
}
|
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.
|
@@ -115,6 +115,7 @@ add_compiler_flag '-Wall'
|
|
115
115
|
add_compiler_flag '-Wextra'
|
116
116
|
|
117
117
|
if ENV['DDTRACE_DEBUG']
|
118
|
+
$defs << '-DDD_DEBUG'
|
118
119
|
CONFIG['optflags'] = '-O0'
|
119
120
|
CONFIG['debugflags'] = '-ggdb3'
|
120
121
|
end
|
@@ -130,6 +131,8 @@ if RUBY_PLATFORM.include?('linux')
|
|
130
131
|
$defs << '-DHAVE_PTHREAD_GETCPUCLOCKID'
|
131
132
|
end
|
132
133
|
|
134
|
+
have_func 'malloc_stats'
|
135
|
+
|
133
136
|
# On older Rubies, rb_postponed_job_preregister/rb_postponed_job_trigger did not exist
|
134
137
|
$defs << '-DNO_POSTPONED_TRIGGER' if RUBY_VERSION < '3.3'
|
135
138
|
|
@@ -234,7 +237,7 @@ Logging.message("[ddtrace] After pkg-config $LDFLAGS were set to: #{$LDFLAGS.ins
|
|
234
237
|
# This makes it easier for development (avoids "oops I forgot to rebuild when I switched my Ruby") and ensures that
|
235
238
|
# the wrong library is never loaded.
|
236
239
|
# When requiring, we need to use the exact same string, including the version and the platform.
|
237
|
-
EXTENSION_NAME = "
|
240
|
+
EXTENSION_NAME = "datadog_profiling_native_extension.#{RUBY_VERSION}_#{RUBY_PLATFORM}".freeze
|
238
241
|
|
239
242
|
if Datadog::Profiling::NativeExtensionHelpers::CAN_USE_MJIT_HEADER
|
240
243
|
mjit_header_file_name = "rb_mjit_min_header-#{RUBY_VERSION}.h"
|
data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/helpers.h
RENAMED
@@ -1,5 +1,7 @@
|
|
1
1
|
#pragma once
|
2
2
|
|
3
|
+
#include <stdint.h>
|
4
|
+
|
3
5
|
// Used to mark symbols to be exported to the outside of the extension.
|
4
6
|
// Consider very carefully before tagging a function with this.
|
5
7
|
#define DDTRACE_EXPORT __attribute__ ((visibility ("default")))
|
@@ -17,3 +19,5 @@ inline static uint64_t uint64_max_of(uint64_t a, uint64_t b) { return a > b ? a
|
|
17
19
|
inline static uint64_t uint64_min_of(uint64_t a, uint64_t b) { return a > b ? b : a; }
|
18
20
|
inline static long long_max_of(long a, long b) { return a > b ? a : b; }
|
19
21
|
inline static long long_min_of(long a, long b) { return a > b ? b : a; }
|
22
|
+
inline static double double_max_of(double a, double b) { return a > b ? a : b; }
|
23
|
+
inline static double double_min_of(double a, double b) { return a > b ? b : a; }
|
data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/http_transport.c
RENAMED
@@ -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"));
|
@@ -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 = '~> 6.0.0.2.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
@@ -1,6 +1,9 @@
|
|
1
1
|
#include <ruby.h>
|
2
2
|
#include <ruby/thread.h>
|
3
3
|
#include <errno.h>
|
4
|
+
#ifdef HAVE_MALLOC_STATS
|
5
|
+
#include <malloc.h>
|
6
|
+
#endif
|
4
7
|
|
5
8
|
#include "clock_id.h"
|
6
9
|
#include "helpers.h"
|
@@ -11,6 +14,7 @@
|
|
11
14
|
|
12
15
|
// Each class/module here is implemented in their separate file
|
13
16
|
void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module);
|
17
|
+
void collectors_discrete_dynamic_sampler_init(VALUE profiling_module);
|
14
18
|
void collectors_dynamic_sampling_rate_init(VALUE profiling_module);
|
15
19
|
void collectors_idle_sampling_helper_init(VALUE profiling_module);
|
16
20
|
void collectors_stack_init(VALUE profiling_module);
|
@@ -32,8 +36,9 @@ static void holding_the_gvl_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_U
|
|
32
36
|
static VALUE _native_trigger_holding_the_gvl_signal_handler_on(DDTRACE_UNUSED VALUE _self, VALUE background_thread);
|
33
37
|
static VALUE _native_enforce_success(DDTRACE_UNUSED VALUE _self, VALUE syserr_errno, VALUE with_gvl);
|
34
38
|
static void *trigger_enforce_success(void *trigger_args);
|
39
|
+
static VALUE _native_malloc_stats(DDTRACE_UNUSED VALUE _self);
|
35
40
|
|
36
|
-
void DDTRACE_EXPORT
|
41
|
+
void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) {
|
37
42
|
VALUE datadog_module = rb_define_module("Datadog");
|
38
43
|
VALUE profiling_module = rb_define_module_under(datadog_module, "Profiling");
|
39
44
|
VALUE native_extension_module = rb_define_module_under(profiling_module, "NativeExtension");
|
@@ -43,6 +48,7 @@ void DDTRACE_EXPORT Init_ddtrace_profiling_native_extension(void) {
|
|
43
48
|
|
44
49
|
ruby_helpers_init();
|
45
50
|
collectors_cpu_and_wall_time_worker_init(profiling_module);
|
51
|
+
collectors_discrete_dynamic_sampler_init(profiling_module);
|
46
52
|
collectors_dynamic_sampling_rate_init(profiling_module);
|
47
53
|
collectors_idle_sampling_helper_init(profiling_module);
|
48
54
|
collectors_stack_init(profiling_module);
|
@@ -65,6 +71,7 @@ void DDTRACE_EXPORT Init_ddtrace_profiling_native_extension(void) {
|
|
65
71
|
rb_define_singleton_method(testing_module, "_native_install_holding_the_gvl_signal_handler", _native_install_holding_the_gvl_signal_handler, 0);
|
66
72
|
rb_define_singleton_method(testing_module, "_native_trigger_holding_the_gvl_signal_handler_on", _native_trigger_holding_the_gvl_signal_handler_on, 1);
|
67
73
|
rb_define_singleton_method(testing_module, "_native_enforce_success", _native_enforce_success, 2);
|
74
|
+
rb_define_singleton_method(testing_module, "_native_malloc_stats", _native_malloc_stats, 0);
|
68
75
|
}
|
69
76
|
|
70
77
|
static VALUE native_working_p(DDTRACE_UNUSED VALUE _self) {
|
@@ -249,3 +256,12 @@ static void *trigger_enforce_success(void *trigger_args) {
|
|
249
256
|
ENFORCE_SUCCESS_NO_GVL(syserr_errno);
|
250
257
|
return NULL;
|
251
258
|
}
|
259
|
+
|
260
|
+
static VALUE _native_malloc_stats(DDTRACE_UNUSED VALUE _self) {
|
261
|
+
#ifdef HAVE_MALLOC_STATS
|
262
|
+
malloc_stats();
|
263
|
+
return Qtrue;
|
264
|
+
#else
|
265
|
+
return Qfalse;
|
266
|
+
#endif
|
267
|
+
}
|
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
|
+
}
|
data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/ruby_helpers.h
RENAMED
@@ -115,3 +115,5 @@ size_t ruby_obj_memsize_of(VALUE obj);
|
|
115
115
|
// return a string with the result of that call. Elsif the object responds to
|
116
116
|
// 'to_s', return a string with the result of that call. Otherwise, return Qnil.
|
117
117
|
VALUE ruby_safe_inspect(VALUE obj);
|
118
|
+
|
119
|
+
VALUE ddtrace_version(void);
|
@@ -26,7 +26,8 @@ module Datadog
|
|
26
26
|
def call(env)
|
27
27
|
return @app.call(env) unless Datadog::AppSec.enabled?
|
28
28
|
|
29
|
-
|
29
|
+
boot = Datadog::Core::Remote::Tie.boot
|
30
|
+
Datadog::Core::Remote::Tie::Tracing.tag(boot, active_span)
|
30
31
|
|
31
32
|
processor = nil
|
32
33
|
ready = false
|
@@ -81,6 +81,7 @@ module Datadog
|
|
81
81
|
|
82
82
|
def initialize(settings)
|
83
83
|
@logger = self.class.build_logger(settings)
|
84
|
+
@environment_logger_extra = {}
|
84
85
|
|
85
86
|
# This agent_settings is intended for use within Core. If you require
|
86
87
|
# agent_settings within a product outside of core you should extend
|
@@ -90,11 +91,13 @@ module Datadog
|
|
90
91
|
@remote = Remote::Component.build(settings, agent_settings)
|
91
92
|
@tracer = self.class.build_tracer(settings, logger: @logger)
|
92
93
|
|
93
|
-
@profiler = Datadog::Profiling::Component.build_profiler_component(
|
94
|
+
@profiler, profiler_logger_extra = Datadog::Profiling::Component.build_profiler_component(
|
94
95
|
settings: settings,
|
95
96
|
agent_settings: agent_settings,
|
96
97
|
optional_tracer: @tracer,
|
97
98
|
)
|
99
|
+
@environment_logger_extra.merge!(profiler_logger_extra) if profiler_logger_extra
|
100
|
+
|
98
101
|
@runtime_metrics = self.class.build_runtime_metrics_worker(settings)
|
99
102
|
@health_metrics = self.class.build_health_metrics(settings)
|
100
103
|
@telemetry = self.class.build_telemetry(settings, agent_settings, logger)
|
@@ -105,18 +108,15 @@ module Datadog
|
|
105
108
|
def startup!(settings)
|
106
109
|
if settings.profiling.enabled
|
107
110
|
if profiler
|
108
|
-
@logger.debug('Profiling started')
|
109
111
|
profiler.start
|
110
112
|
else
|
111
113
|
# Display a warning for users who expected profiling to be enabled
|
112
114
|
unsupported_reason = Profiling.unsupported_reason
|
113
115
|
logger.warn("Profiling was requested but is not supported, profiling disabled: #{unsupported_reason}")
|
114
116
|
end
|
115
|
-
else
|
116
|
-
@logger.debug('Profiling is disabled')
|
117
117
|
end
|
118
118
|
|
119
|
-
Core::Diagnostics::EnvironmentLogger.collect_and_log!
|
119
|
+
Core::Diagnostics::EnvironmentLogger.collect_and_log!(@environment_logger_extra)
|
120
120
|
end
|
121
121
|
|
122
122
|
# Shuts down all the components in use.
|
@@ -264,7 +264,7 @@ module Datadog
|
|
264
264
|
# when restoring a value from `@value_per_precedence`, and we are only running `definition.setter`
|
265
265
|
# on the original value, not on a valud that has already been processed by `definition.setter`.
|
266
266
|
@value_per_precedence[precedence] = value
|
267
|
-
context_exec(v, old_value, &definition.after_set) if definition.after_set
|
267
|
+
context_exec(v, old_value, precedence, &definition.after_set) if definition.after_set
|
268
268
|
end
|
269
269
|
end
|
270
270
|
|