ddtrace 1.14.0 → 1.16.2
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 +165 -2
- data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +3 -5
- data/ext/ddtrace_profiling_native_extension/clock_id.h +0 -3
- data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +0 -22
- data/ext/ddtrace_profiling_native_extension/clock_id_noop.c +0 -1
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +41 -6
- data/ext/ddtrace_profiling_native_extension/collectors_idle_sampling_helper.c +3 -0
- data/ext/ddtrace_profiling_native_extension/collectors_stack.c +76 -24
- data/ext/ddtrace_profiling_native_extension/collectors_stack.h +1 -1
- data/ext/ddtrace_profiling_native_extension/collectors_thread_context.c +207 -32
- data/ext/ddtrace_profiling_native_extension/collectors_thread_context.h +1 -1
- data/ext/ddtrace_profiling_native_extension/extconf.rb +8 -2
- data/ext/ddtrace_profiling_native_extension/http_transport.c +26 -10
- data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.c +42 -0
- data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +6 -0
- data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +1 -16
- data/ext/ddtrace_profiling_native_extension/pid_controller.c +57 -0
- data/ext/ddtrace_profiling_native_extension/pid_controller.h +45 -0
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +17 -12
- data/ext/ddtrace_profiling_native_extension/profiling.c +0 -2
- data/ext/ddtrace_profiling_native_extension/stack_recorder.c +74 -37
- data/ext/ddtrace_profiling_native_extension/stack_recorder.h +13 -3
- data/lib/datadog/appsec/assets/waf_rules/processors.json +92 -0
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +698 -75
- data/lib/datadog/appsec/assets/waf_rules/scanners.json +114 -0
- data/lib/datadog/appsec/assets/waf_rules/strict.json +98 -8
- data/lib/datadog/appsec/assets.rb +8 -0
- data/lib/datadog/appsec/component.rb +9 -2
- data/lib/datadog/appsec/configuration/settings.rb +61 -2
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +6 -2
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +8 -6
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +2 -7
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +2 -5
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +2 -5
- data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +3 -2
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +24 -10
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +3 -2
- data/lib/datadog/appsec/contrib/rails/patcher.rb +9 -3
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +2 -5
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +6 -4
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +13 -7
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +2 -5
- data/lib/datadog/appsec/event.rb +106 -50
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +3 -3
- data/lib/datadog/appsec/monitor/reactive/set_user.rb +2 -5
- data/lib/datadog/appsec/processor/actions.rb +49 -0
- data/lib/datadog/appsec/processor/rule_merger.rb +22 -2
- data/lib/datadog/appsec/processor.rb +34 -6
- data/lib/datadog/appsec/remote.rb +4 -1
- data/lib/datadog/appsec/response.rb +82 -4
- data/lib/datadog/appsec/sample_rate.rb +21 -0
- data/lib/datadog/appsec.rb +2 -2
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +29 -24
- data/lib/datadog/core/configuration/base.rb +1 -11
- data/lib/datadog/core/configuration/components.rb +7 -2
- data/lib/datadog/core/configuration/ext.rb +21 -0
- data/lib/datadog/core/configuration/option.rb +2 -4
- data/lib/datadog/core/configuration/option_definition.rb +17 -41
- data/lib/datadog/core/configuration/options.rb +5 -5
- data/lib/datadog/core/configuration/settings.rb +47 -45
- data/lib/datadog/core/environment/execution.rb +47 -9
- data/lib/datadog/core/environment/variable_helpers.rb +0 -69
- data/lib/datadog/core/error.rb +1 -0
- data/lib/datadog/core/git/ext.rb +2 -0
- data/lib/datadog/core/remote/client/capabilities.rb +1 -1
- data/lib/datadog/core/remote/component.rb +2 -2
- data/lib/datadog/core/remote/negotiation.rb +2 -2
- data/lib/datadog/core/remote/transport/config.rb +60 -0
- data/lib/datadog/core/remote/transport/http/api/instance.rb +39 -0
- data/lib/datadog/core/remote/transport/http/api/spec.rb +21 -0
- data/lib/datadog/core/remote/transport/http/api.rb +58 -0
- data/lib/datadog/core/remote/transport/http/builder.rb +219 -0
- data/lib/datadog/core/remote/transport/http/client.rb +48 -0
- data/lib/datadog/core/remote/transport/http/config.rb +280 -0
- data/lib/datadog/core/remote/transport/http/negotiation.rb +146 -0
- data/lib/datadog/core/remote/transport/http.rb +179 -0
- data/lib/datadog/core/{transport → remote/transport}/negotiation.rb +25 -23
- data/lib/datadog/core/remote/worker.rb +3 -1
- data/lib/datadog/core/telemetry/collector.rb +3 -2
- data/lib/datadog/core/telemetry/http/transport.rb +2 -1
- data/lib/datadog/core/transport/ext.rb +47 -0
- data/lib/datadog/core/transport/http/adapters/net.rb +168 -0
- data/lib/datadog/core/transport/http/adapters/registry.rb +29 -0
- data/lib/datadog/core/transport/http/adapters/test.rb +89 -0
- data/lib/datadog/core/transport/http/adapters/unix_socket.rb +83 -0
- data/lib/datadog/core/transport/http/api/endpoint.rb +31 -0
- data/lib/datadog/core/transport/http/api/fallbacks.rb +26 -0
- data/lib/datadog/core/transport/http/api/map.rb +18 -0
- data/lib/datadog/core/transport/http/env.rb +62 -0
- data/lib/datadog/core/transport/http/response.rb +60 -0
- data/lib/datadog/core/transport/parcel.rb +22 -0
- data/lib/datadog/core/transport/request.rb +17 -0
- data/lib/datadog/core/transport/response.rb +64 -0
- data/lib/datadog/core/workers/polling.rb +2 -2
- data/lib/datadog/opentelemetry/api/context.rb +10 -3
- data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -1
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +14 -2
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +68 -0
- data/lib/datadog/opentelemetry/trace.rb +58 -0
- data/lib/datadog/opentelemetry.rb +1 -0
- data/lib/datadog/opentracer.rb +9 -0
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +14 -19
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
- data/lib/datadog/profiling/collectors/thread_context.rb +9 -1
- data/lib/datadog/profiling/component.rb +24 -99
- data/lib/datadog/profiling/ext.rb +0 -12
- data/lib/datadog/profiling/flush.rb +0 -3
- data/lib/datadog/profiling/http_transport.rb +6 -3
- data/lib/datadog/profiling/native_extension.rb +0 -21
- data/lib/datadog/profiling/profiler.rb +36 -13
- data/lib/datadog/profiling/scheduler.rb +16 -9
- data/lib/datadog/profiling.rb +8 -81
- data/lib/datadog/tracing/component.rb +10 -4
- data/lib/datadog/tracing/configuration/agent_settings_resolver.rb +13 -0
- data/lib/datadog/tracing/configuration/ext.rb +4 -2
- data/lib/datadog/tracing/configuration/settings.rb +14 -7
- data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +4 -0
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +106 -197
- data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +3 -0
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +7 -0
- data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +14 -14
- data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +3 -10
- data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +2 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +8 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +22 -0
- data/lib/datadog/tracing/contrib/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +6 -0
- data/lib/datadog/tracing/contrib/dalli/ext.rb +7 -0
- data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +9 -2
- data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +5 -0
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +5 -0
- data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +8 -0
- data/lib/datadog/tracing/contrib/excon/middleware.rb +5 -0
- data/lib/datadog/tracing/contrib/ext.rb +3 -0
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +5 -0
- data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +21 -1
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +11 -1
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +18 -0
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +0 -4
- data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +3 -3
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +7 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +13 -3
- data/lib/datadog/tracing/contrib/opensearch/integration.rb +2 -2
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +7 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/presto/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +1 -1
- data/lib/datadog/tracing/contrib/que/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/racecar/event.rb +5 -0
- data/lib/datadog/tracing/contrib/rack/header_tagging.rb +14 -4
- data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +4 -4
- data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/redis/instrumentation.rb +3 -38
- data/lib/datadog/tracing/contrib/redis/tags.rb +7 -2
- data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +46 -33
- data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +5 -0
- data/lib/datadog/tracing/contrib/sequel/utils.rb +5 -0
- data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/utils/quantization/http.rb +2 -2
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +6 -0
- data/lib/datadog/tracing/distributed/propagation.rb +13 -33
- data/lib/datadog/tracing/metadata/tagging.rb +3 -3
- data/lib/datadog/tracing/sync_writer.rb +3 -3
- data/lib/datadog/tracing/tracer.rb +2 -0
- data/lib/datadog/{core → tracing}/transport/http/api/instance.rb +1 -1
- data/lib/datadog/{core → tracing}/transport/http/api/spec.rb +1 -1
- data/lib/datadog/tracing/transport/http/api.rb +43 -0
- data/lib/datadog/{core → tracing}/transport/http/builder.rb +13 -68
- data/lib/datadog/tracing/transport/http/client.rb +57 -0
- data/lib/datadog/tracing/transport/http/statistics.rb +47 -0
- data/lib/datadog/tracing/transport/http/traces.rb +152 -0
- data/lib/datadog/tracing/transport/http.rb +124 -0
- data/lib/datadog/tracing/transport/io/client.rb +89 -0
- data/lib/datadog/tracing/transport/io/response.rb +27 -0
- data/lib/datadog/tracing/transport/io/traces.rb +101 -0
- data/lib/datadog/tracing/transport/io.rb +30 -0
- data/lib/datadog/tracing/transport/serializable_trace.rb +126 -0
- data/lib/datadog/tracing/transport/statistics.rb +77 -0
- data/lib/datadog/tracing/transport/trace_formatter.rb +209 -0
- data/lib/datadog/tracing/transport/traces.rb +224 -0
- data/lib/datadog/tracing/workers/trace_writer.rb +5 -3
- data/lib/datadog/tracing/workers.rb +3 -2
- data/lib/datadog/tracing/writer.rb +5 -2
- data/lib/ddtrace/transport/ext.rb +17 -15
- data/lib/ddtrace/version.rb +2 -2
- data/lib/ddtrace.rb +1 -1
- metadata +73 -96
- data/lib/datadog/ci/configuration/components.rb +0 -32
- data/lib/datadog/ci/configuration/settings.rb +0 -51
- data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +0 -35
- data/lib/datadog/ci/contrib/cucumber/ext.rb +0 -22
- data/lib/datadog/ci/contrib/cucumber/formatter.rb +0 -94
- data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +0 -28
- data/lib/datadog/ci/contrib/cucumber/integration.rb +0 -47
- data/lib/datadog/ci/contrib/cucumber/patcher.rb +0 -27
- data/lib/datadog/ci/contrib/minitest/configuration/settings.rb +0 -35
- data/lib/datadog/ci/contrib/minitest/ext.rb +0 -21
- data/lib/datadog/ci/contrib/minitest/integration.rb +0 -49
- data/lib/datadog/ci/contrib/minitest/patcher.rb +0 -27
- data/lib/datadog/ci/contrib/minitest/test_helper.rb +0 -68
- data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +0 -35
- data/lib/datadog/ci/contrib/rspec/example.rb +0 -68
- data/lib/datadog/ci/contrib/rspec/ext.rb +0 -21
- data/lib/datadog/ci/contrib/rspec/integration.rb +0 -48
- data/lib/datadog/ci/contrib/rspec/patcher.rb +0 -27
- data/lib/datadog/ci/ext/app_types.rb +0 -9
- data/lib/datadog/ci/ext/environment.rb +0 -575
- data/lib/datadog/ci/ext/settings.rb +0 -10
- data/lib/datadog/ci/ext/test.rb +0 -35
- data/lib/datadog/ci/extensions.rb +0 -19
- data/lib/datadog/ci/flush.rb +0 -38
- data/lib/datadog/ci/test.rb +0 -81
- data/lib/datadog/ci.rb +0 -21
- data/lib/datadog/core/configuration/dependency_resolver.rb +0 -28
- data/lib/datadog/core/configuration/option_definition_set.rb +0 -22
- data/lib/datadog/core/configuration/option_set.rb +0 -10
- data/lib/datadog/core/transport/config.rb +0 -58
- data/lib/datadog/core/transport/http/api.rb +0 -57
- data/lib/datadog/core/transport/http/client.rb +0 -45
- data/lib/datadog/core/transport/http/config.rb +0 -278
- data/lib/datadog/core/transport/http/negotiation.rb +0 -144
- data/lib/datadog/core/transport/http.rb +0 -169
- data/lib/datadog/core/utils/object_set.rb +0 -43
- data/lib/datadog/core/utils/string_table.rb +0 -47
- data/lib/datadog/profiling/backtrace_location.rb +0 -34
- data/lib/datadog/profiling/buffer.rb +0 -43
- data/lib/datadog/profiling/collectors/old_stack.rb +0 -301
- data/lib/datadog/profiling/encoding/profile.rb +0 -41
- data/lib/datadog/profiling/event.rb +0 -15
- data/lib/datadog/profiling/events/stack.rb +0 -82
- data/lib/datadog/profiling/old_recorder.rb +0 -107
- data/lib/datadog/profiling/pprof/builder.rb +0 -125
- data/lib/datadog/profiling/pprof/converter.rb +0 -102
- data/lib/datadog/profiling/pprof/message_set.rb +0 -16
- data/lib/datadog/profiling/pprof/payload.rb +0 -20
- data/lib/datadog/profiling/pprof/pprof.proto +0 -212
- data/lib/datadog/profiling/pprof/pprof_pb.rb +0 -81
- data/lib/datadog/profiling/pprof/stack_sample.rb +0 -139
- data/lib/datadog/profiling/pprof/string_table.rb +0 -12
- data/lib/datadog/profiling/pprof/template.rb +0 -118
- data/lib/datadog/profiling/trace_identifiers/ddtrace.rb +0 -43
- data/lib/datadog/profiling/trace_identifiers/helper.rb +0 -45
- data/lib/ddtrace/transport/http/adapters/net.rb +0 -168
- data/lib/ddtrace/transport/http/adapters/registry.rb +0 -27
- data/lib/ddtrace/transport/http/adapters/test.rb +0 -85
- data/lib/ddtrace/transport/http/adapters/unix_socket.rb +0 -77
- data/lib/ddtrace/transport/http/api/endpoint.rb +0 -29
- data/lib/ddtrace/transport/http/api/fallbacks.rb +0 -24
- data/lib/ddtrace/transport/http/api/instance.rb +0 -35
- data/lib/ddtrace/transport/http/api/map.rb +0 -16
- data/lib/ddtrace/transport/http/api/spec.rb +0 -17
- data/lib/ddtrace/transport/http/api.rb +0 -39
- data/lib/ddtrace/transport/http/builder.rb +0 -176
- data/lib/ddtrace/transport/http/client.rb +0 -52
- data/lib/ddtrace/transport/http/env.rb +0 -58
- data/lib/ddtrace/transport/http/response.rb +0 -58
- data/lib/ddtrace/transport/http/statistics.rb +0 -43
- data/lib/ddtrace/transport/http/traces.rb +0 -144
- data/lib/ddtrace/transport/http.rb +0 -117
- data/lib/ddtrace/transport/io/client.rb +0 -85
- data/lib/ddtrace/transport/io/response.rb +0 -25
- data/lib/ddtrace/transport/io/traces.rb +0 -99
- data/lib/ddtrace/transport/io.rb +0 -28
- data/lib/ddtrace/transport/parcel.rb +0 -20
- data/lib/ddtrace/transport/request.rb +0 -15
- data/lib/ddtrace/transport/response.rb +0 -60
- data/lib/ddtrace/transport/serializable_trace.rb +0 -122
- data/lib/ddtrace/transport/statistics.rb +0 -75
- data/lib/ddtrace/transport/trace_formatter.rb +0 -207
- data/lib/ddtrace/transport/traces.rb +0 -216
|
@@ -23,7 +23,6 @@ struct sampling_buffer {
|
|
|
23
23
|
int *lines_buffer;
|
|
24
24
|
bool *is_ruby_frame;
|
|
25
25
|
ddog_prof_Location *locations;
|
|
26
|
-
ddog_prof_Line *lines;
|
|
27
26
|
}; // Note: typedef'd in the header to sampling_buffer
|
|
28
27
|
|
|
29
28
|
static VALUE _native_sample(
|
|
@@ -41,7 +40,7 @@ static void record_placeholder_stack_in_native_code(
|
|
|
41
40
|
sampling_buffer* buffer,
|
|
42
41
|
VALUE recorder_instance,
|
|
43
42
|
sample_values values,
|
|
44
|
-
|
|
43
|
+
sample_labels labels,
|
|
45
44
|
sampling_buffer *record_buffer,
|
|
46
45
|
int extra_frames_in_record_buffer
|
|
47
46
|
);
|
|
@@ -50,7 +49,7 @@ static void sample_thread_internal(
|
|
|
50
49
|
sampling_buffer* buffer,
|
|
51
50
|
VALUE recorder_instance,
|
|
52
51
|
sample_values values,
|
|
53
|
-
|
|
52
|
+
sample_labels labels,
|
|
54
53
|
sampling_buffer *record_buffer,
|
|
55
54
|
int extra_frames_in_record_buffer
|
|
56
55
|
);
|
|
@@ -86,13 +85,14 @@ static VALUE _native_sample(
|
|
|
86
85
|
VALUE zero = INT2NUM(0);
|
|
87
86
|
sample_values values = {
|
|
88
87
|
.cpu_time_ns = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("cpu-time"), zero)),
|
|
89
|
-
.
|
|
88
|
+
.cpu_or_wall_samples = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("cpu-samples"), zero)),
|
|
90
89
|
.wall_time_ns = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("wall-time"), zero)),
|
|
91
90
|
.alloc_samples = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("alloc-samples"), zero)),
|
|
92
91
|
};
|
|
93
92
|
|
|
94
93
|
long labels_count = RARRAY_LEN(labels_array) + RARRAY_LEN(numeric_labels_array);
|
|
95
94
|
ddog_prof_Label labels[labels_count];
|
|
95
|
+
ddog_prof_Label *state_label = NULL;
|
|
96
96
|
|
|
97
97
|
for (int i = 0; i < RARRAY_LEN(labels_array); i++) {
|
|
98
98
|
VALUE key_str_pair = rb_ary_entry(labels_array, i);
|
|
@@ -101,6 +101,10 @@ static VALUE _native_sample(
|
|
|
101
101
|
.key = char_slice_from_ruby_string(rb_ary_entry(key_str_pair, 0)),
|
|
102
102
|
.str = char_slice_from_ruby_string(rb_ary_entry(key_str_pair, 1))
|
|
103
103
|
};
|
|
104
|
+
|
|
105
|
+
if (rb_str_equal(rb_ary_entry(key_str_pair, 0), rb_str_new_cstr("state"))) {
|
|
106
|
+
state_label = &labels[i];
|
|
107
|
+
}
|
|
104
108
|
}
|
|
105
109
|
for (int i = 0; i < RARRAY_LEN(numeric_labels_array); i++) {
|
|
106
110
|
VALUE key_str_pair = rb_ary_entry(numeric_labels_array, i);
|
|
@@ -116,12 +120,14 @@ static VALUE _native_sample(
|
|
|
116
120
|
|
|
117
121
|
sampling_buffer *buffer = sampling_buffer_new(max_frames_requested);
|
|
118
122
|
|
|
123
|
+
ddog_prof_Slice_Label slice_labels = {.ptr = labels, .len = labels_count};
|
|
124
|
+
|
|
119
125
|
sample_thread(
|
|
120
126
|
thread,
|
|
121
127
|
buffer,
|
|
122
128
|
recorder_instance,
|
|
123
129
|
values,
|
|
124
|
-
(
|
|
130
|
+
(sample_labels) {.labels = slice_labels, .state_label = state_label},
|
|
125
131
|
RTEST(in_gc) ? SAMPLE_IN_GC : SAMPLE_REGULAR
|
|
126
132
|
);
|
|
127
133
|
|
|
@@ -135,7 +141,7 @@ void sample_thread(
|
|
|
135
141
|
sampling_buffer* buffer,
|
|
136
142
|
VALUE recorder_instance,
|
|
137
143
|
sample_values values,
|
|
138
|
-
|
|
144
|
+
sample_labels labels,
|
|
139
145
|
sample_type type
|
|
140
146
|
) {
|
|
141
147
|
// Samples thread into recorder
|
|
@@ -150,7 +156,7 @@ void sample_thread(
|
|
|
150
156
|
if (type == SAMPLE_IN_GC) {
|
|
151
157
|
ddog_CharSlice function_name = DDOG_CHARSLICE_C("");
|
|
152
158
|
ddog_CharSlice function_filename = DDOG_CHARSLICE_C("Garbage Collection");
|
|
153
|
-
buffer->
|
|
159
|
+
buffer->locations[0] = (ddog_prof_Location) {
|
|
154
160
|
.function = (ddog_prof_Function) {.name = function_name, .filename = function_filename},
|
|
155
161
|
.line = 0
|
|
156
162
|
};
|
|
@@ -162,7 +168,6 @@ void sample_thread(
|
|
|
162
168
|
.lines_buffer = buffer->lines_buffer + 1,
|
|
163
169
|
.is_ruby_frame = buffer->is_ruby_frame + 1,
|
|
164
170
|
.locations = buffer->locations + 1,
|
|
165
|
-
.lines = buffer->lines + 1
|
|
166
171
|
};
|
|
167
172
|
sampling_buffer *record_buffer = buffer; // We pass in the original buffer as the record_buffer, but not as the regular buffer
|
|
168
173
|
int extra_frames_in_record_buffer = 1;
|
|
@@ -173,6 +178,8 @@ void sample_thread(
|
|
|
173
178
|
rb_raise(rb_eArgError, "Unexpected value for sample_type: %d", type);
|
|
174
179
|
}
|
|
175
180
|
|
|
181
|
+
#define CHARSLICE_EQUALS(must_be_a_literal, charslice) (strlen("" must_be_a_literal) == charslice.len && strncmp(must_be_a_literal, charslice.ptr, charslice.len) == 0)
|
|
182
|
+
|
|
176
183
|
// Idea: Should we release the global vm lock (GVL) after we get the data from `rb_profile_frames`? That way other Ruby threads
|
|
177
184
|
// could continue making progress while the sample was ingested into the profile.
|
|
178
185
|
//
|
|
@@ -197,7 +204,7 @@ static void sample_thread_internal(
|
|
|
197
204
|
sampling_buffer* buffer,
|
|
198
205
|
VALUE recorder_instance,
|
|
199
206
|
sample_values values,
|
|
200
|
-
|
|
207
|
+
sample_labels labels,
|
|
201
208
|
sampling_buffer *record_buffer,
|
|
202
209
|
int extra_frames_in_record_buffer
|
|
203
210
|
) {
|
|
@@ -230,6 +237,15 @@ static void sample_thread_internal(
|
|
|
230
237
|
VALUE last_ruby_frame = Qnil;
|
|
231
238
|
int last_ruby_line = 0;
|
|
232
239
|
|
|
240
|
+
ddog_prof_Label *state_label = labels.state_label;
|
|
241
|
+
bool cpu_or_wall_sample = values.cpu_or_wall_samples > 0;
|
|
242
|
+
bool has_cpu_time = cpu_or_wall_sample && values.cpu_time_ns > 0;
|
|
243
|
+
bool only_wall_time = cpu_or_wall_sample && values.cpu_time_ns == 0 && values.wall_time_ns > 0;
|
|
244
|
+
|
|
245
|
+
if (cpu_or_wall_sample && state_label == NULL) rb_raise(rb_eRuntimeError, "BUG: Unexpected missing state_label");
|
|
246
|
+
|
|
247
|
+
if (has_cpu_time) state_label->str = DDOG_CHARSLICE_C("had cpu");
|
|
248
|
+
|
|
233
249
|
for (int i = captured_frames - 1; i >= 0; i--) {
|
|
234
250
|
VALUE name, filename;
|
|
235
251
|
int line;
|
|
@@ -250,10 +266,55 @@ static void sample_thread_internal(
|
|
|
250
266
|
name = NIL_P(name) ? missing_string : name;
|
|
251
267
|
filename = NIL_P(filename) ? missing_string : filename;
|
|
252
268
|
|
|
253
|
-
|
|
269
|
+
ddog_CharSlice name_slice = char_slice_from_ruby_string(name);
|
|
270
|
+
ddog_CharSlice filename_slice = char_slice_from_ruby_string(filename);
|
|
271
|
+
|
|
272
|
+
bool top_of_the_stack = i == 0;
|
|
273
|
+
|
|
274
|
+
// When there's only wall-time in a sample, this means that the thread was not active in the sampled period.
|
|
275
|
+
//
|
|
276
|
+
// We try to categorize what it was doing based on what we observe at the top of the stack. This is a very rough
|
|
277
|
+
// approximation, and in the future we hope to replace this with a more accurate approach (such as using the
|
|
278
|
+
// GVL instrumentation API.)
|
|
279
|
+
if (top_of_the_stack && only_wall_time) {
|
|
280
|
+
if (!buffer->is_ruby_frame[i]) {
|
|
281
|
+
// We know that known versions of Ruby implement these using native code; thus if we find a method with the
|
|
282
|
+
// same name that is not native code, we ignore it, as it's probably a user method that coincidentally
|
|
283
|
+
// has the same name. Thus, even though "matching just by method name" is kinda weak,
|
|
284
|
+
// "matching by method name" + is native code seems actually to be good enough for a lot of cases.
|
|
285
|
+
|
|
286
|
+
if (CHARSLICE_EQUALS("sleep", name_slice)) { // Expected to be Kernel.sleep
|
|
287
|
+
state_label->str = DDOG_CHARSLICE_C("sleeping");
|
|
288
|
+
} else if (CHARSLICE_EQUALS("select", name_slice)) { // Expected to be Kernel.select
|
|
289
|
+
state_label->str = DDOG_CHARSLICE_C("waiting");
|
|
290
|
+
} else if (
|
|
291
|
+
CHARSLICE_EQUALS("synchronize", name_slice) || // Expected to be Monitor/Mutex#synchronize
|
|
292
|
+
CHARSLICE_EQUALS("lock", name_slice) || // Expected to be Mutex#lock
|
|
293
|
+
CHARSLICE_EQUALS("join", name_slice) // Expected to be Thread#join
|
|
294
|
+
) {
|
|
295
|
+
state_label->str = DDOG_CHARSLICE_C("blocked");
|
|
296
|
+
} else if (CHARSLICE_EQUALS("wait_readable", name_slice)) { // Expected to be IO#wait_readable
|
|
297
|
+
state_label->str = DDOG_CHARSLICE_C("network");
|
|
298
|
+
}
|
|
299
|
+
#ifdef NO_PRIMITIVE_POP // Ruby < 3.2
|
|
300
|
+
else if (CHARSLICE_EQUALS("pop", name_slice)) { // Expected to be Queue/SizedQueue#pop
|
|
301
|
+
state_label->str = DDOG_CHARSLICE_C("waiting");
|
|
302
|
+
}
|
|
303
|
+
#endif
|
|
304
|
+
} else {
|
|
305
|
+
#ifndef NO_PRIMITIVE_POP // Ruby >= 3.2
|
|
306
|
+
// Unlike the above, Ruby actually treats this one specially and gives it a nice file name we can match on!
|
|
307
|
+
if (CHARSLICE_EQUALS("pop", name_slice) && CHARSLICE_EQUALS("<internal:thread_sync>", filename_slice)) { // Expected to be Queue/SizedQueue#pop
|
|
308
|
+
state_label->str = DDOG_CHARSLICE_C("waiting");
|
|
309
|
+
}
|
|
310
|
+
#endif
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
buffer->locations[i] = (ddog_prof_Location) {
|
|
254
315
|
.function = (ddog_prof_Function) {
|
|
255
|
-
.name =
|
|
256
|
-
.filename =
|
|
316
|
+
.name = name_slice,
|
|
317
|
+
.filename = filename_slice,
|
|
257
318
|
},
|
|
258
319
|
.line = line,
|
|
259
320
|
};
|
|
@@ -292,7 +353,7 @@ static void maybe_add_placeholder_frames_omitted(VALUE thread, sampling_buffer*
|
|
|
292
353
|
// `record_sample`. So be careful where it gets allocated. (We do have tests for this, at least!)
|
|
293
354
|
ddog_CharSlice function_name = DDOG_CHARSLICE_C("");
|
|
294
355
|
ddog_CharSlice function_filename = {.ptr = frames_omitted_message, .len = strlen(frames_omitted_message)};
|
|
295
|
-
buffer->
|
|
356
|
+
buffer->locations[buffer->max_frames - 1] = (ddog_prof_Location) {
|
|
296
357
|
.function = (ddog_prof_Function) {.name = function_name, .filename = function_filename},
|
|
297
358
|
.line = 0,
|
|
298
359
|
};
|
|
@@ -322,13 +383,13 @@ static void record_placeholder_stack_in_native_code(
|
|
|
322
383
|
sampling_buffer* buffer,
|
|
323
384
|
VALUE recorder_instance,
|
|
324
385
|
sample_values values,
|
|
325
|
-
|
|
386
|
+
sample_labels labels,
|
|
326
387
|
sampling_buffer *record_buffer,
|
|
327
388
|
int extra_frames_in_record_buffer
|
|
328
389
|
) {
|
|
329
390
|
ddog_CharSlice function_name = DDOG_CHARSLICE_C("");
|
|
330
391
|
ddog_CharSlice function_filename = DDOG_CHARSLICE_C("In native code");
|
|
331
|
-
buffer->
|
|
392
|
+
buffer->locations[0] = (ddog_prof_Location) {
|
|
332
393
|
.function = (ddog_prof_Function) {.name = function_name, .filename = function_filename},
|
|
333
394
|
.line = 0
|
|
334
395
|
};
|
|
@@ -354,14 +415,6 @@ sampling_buffer *sampling_buffer_new(unsigned int max_frames) {
|
|
|
354
415
|
buffer->lines_buffer = ruby_xcalloc(max_frames, sizeof(int));
|
|
355
416
|
buffer->is_ruby_frame = ruby_xcalloc(max_frames, sizeof(bool));
|
|
356
417
|
buffer->locations = ruby_xcalloc(max_frames, sizeof(ddog_prof_Location));
|
|
357
|
-
buffer->lines = ruby_xcalloc(max_frames, sizeof(ddog_prof_Line));
|
|
358
|
-
|
|
359
|
-
// Currently we have a 1-to-1 correspondence between lines and locations, so we just initialize the locations once
|
|
360
|
-
// here and then only mutate the contents of the lines.
|
|
361
|
-
for (unsigned int i = 0; i < max_frames; i++) {
|
|
362
|
-
ddog_prof_Slice_Line lines = (ddog_prof_Slice_Line) {.ptr = &buffer->lines[i], .len = 1};
|
|
363
|
-
buffer->locations[i] = (ddog_prof_Location) {.lines = lines};
|
|
364
|
-
}
|
|
365
418
|
|
|
366
419
|
return buffer;
|
|
367
420
|
}
|
|
@@ -373,7 +426,6 @@ void sampling_buffer_free(sampling_buffer *buffer) {
|
|
|
373
426
|
ruby_xfree(buffer->lines_buffer);
|
|
374
427
|
ruby_xfree(buffer->is_ruby_frame);
|
|
375
428
|
ruby_xfree(buffer->locations);
|
|
376
|
-
ruby_xfree(buffer->lines);
|
|
377
429
|
|
|
378
430
|
ruby_xfree(buffer);
|
|
379
431
|
}
|
|
@@ -101,6 +101,8 @@ struct thread_context_collector_state {
|
|
|
101
101
|
bool endpoint_collection_enabled;
|
|
102
102
|
// Used to omit timestamps / timeline events from collected data
|
|
103
103
|
bool timeline_enabled;
|
|
104
|
+
// Used to omit class information from collected allocation data
|
|
105
|
+
bool allocation_type_enabled;
|
|
104
106
|
// Used when calling monotonic_to_system_epoch_ns
|
|
105
107
|
monotonic_to_system_epoch_state time_converter_state;
|
|
106
108
|
// Used to identify the main thread, to give it a fallback name
|
|
@@ -157,7 +159,8 @@ static VALUE _native_initialize(
|
|
|
157
159
|
VALUE max_frames,
|
|
158
160
|
VALUE tracer_context_key,
|
|
159
161
|
VALUE endpoint_collection_enabled,
|
|
160
|
-
VALUE timeline_enabled
|
|
162
|
+
VALUE timeline_enabled,
|
|
163
|
+
VALUE allocation_type_enabled
|
|
161
164
|
);
|
|
162
165
|
static VALUE _native_sample(VALUE self, VALUE collector_instance, VALUE profiler_overhead_stack_thread);
|
|
163
166
|
static VALUE _native_on_gc_start(VALUE self, VALUE collector_instance);
|
|
@@ -178,7 +181,9 @@ static void trigger_sample_for_thread(
|
|
|
178
181
|
struct per_thread_context *thread_context,
|
|
179
182
|
sample_values values,
|
|
180
183
|
sample_type type,
|
|
181
|
-
long current_monotonic_wall_time_ns
|
|
184
|
+
long current_monotonic_wall_time_ns,
|
|
185
|
+
ddog_CharSlice *ruby_vm_type,
|
|
186
|
+
ddog_CharSlice *class_name
|
|
182
187
|
);
|
|
183
188
|
static VALUE _native_thread_list(VALUE self);
|
|
184
189
|
static struct per_thread_context *get_or_create_context_for(VALUE thread, struct thread_context_collector_state *state);
|
|
@@ -196,11 +201,12 @@ static long cpu_time_now_ns(struct per_thread_context *thread_context);
|
|
|
196
201
|
static long thread_id_for(VALUE thread);
|
|
197
202
|
static VALUE _native_stats(VALUE self, VALUE collector_instance);
|
|
198
203
|
static void trace_identifiers_for(struct thread_context_collector_state *state, VALUE thread, struct trace_identifiers *trace_identifiers_result);
|
|
199
|
-
static bool
|
|
204
|
+
static bool should_collect_resource(VALUE root_span_type);
|
|
200
205
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
|
201
206
|
static VALUE thread_list(struct thread_context_collector_state *state);
|
|
202
|
-
static VALUE _native_sample_allocation(VALUE self, VALUE collector_instance, VALUE sample_weight);
|
|
207
|
+
static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object);
|
|
203
208
|
static VALUE _native_new_empty_thread(VALUE self);
|
|
209
|
+
ddog_CharSlice ruby_value_type_to_class_name(enum ruby_value_type type);
|
|
204
210
|
|
|
205
211
|
void collectors_thread_context_init(VALUE profiling_module) {
|
|
206
212
|
VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
|
|
@@ -218,11 +224,11 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
|
218
224
|
// https://bugs.ruby-lang.org/issues/18007 for a discussion around this.
|
|
219
225
|
rb_define_alloc_func(collectors_thread_context_class, _native_new);
|
|
220
226
|
|
|
221
|
-
rb_define_singleton_method(collectors_thread_context_class, "_native_initialize", _native_initialize,
|
|
227
|
+
rb_define_singleton_method(collectors_thread_context_class, "_native_initialize", _native_initialize, 7);
|
|
222
228
|
rb_define_singleton_method(collectors_thread_context_class, "_native_inspect", _native_inspect, 1);
|
|
223
229
|
rb_define_singleton_method(collectors_thread_context_class, "_native_reset_after_fork", _native_reset_after_fork, 1);
|
|
224
230
|
rb_define_singleton_method(testing_module, "_native_sample", _native_sample, 2);
|
|
225
|
-
rb_define_singleton_method(testing_module, "_native_sample_allocation", _native_sample_allocation,
|
|
231
|
+
rb_define_singleton_method(testing_module, "_native_sample_allocation", _native_sample_allocation, 3);
|
|
226
232
|
rb_define_singleton_method(testing_module, "_native_on_gc_start", _native_on_gc_start, 1);
|
|
227
233
|
rb_define_singleton_method(testing_module, "_native_on_gc_finish", _native_on_gc_finish, 1);
|
|
228
234
|
rb_define_singleton_method(testing_module, "_native_sample_after_gc", _native_sample_after_gc, 1);
|
|
@@ -298,6 +304,9 @@ static int hash_map_per_thread_context_free_values(DDTRACE_UNUSED st_data_t _thr
|
|
|
298
304
|
static VALUE _native_new(VALUE klass) {
|
|
299
305
|
struct thread_context_collector_state *state = ruby_xcalloc(1, sizeof(struct thread_context_collector_state));
|
|
300
306
|
|
|
307
|
+
// Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
|
|
308
|
+
// being leaked.
|
|
309
|
+
|
|
301
310
|
// Update this when modifying state struct
|
|
302
311
|
state->sampling_buffer = NULL;
|
|
303
312
|
state->hash_map_per_thread_context =
|
|
@@ -308,6 +317,7 @@ static VALUE _native_new(VALUE klass) {
|
|
|
308
317
|
state->thread_list_buffer = rb_ary_new();
|
|
309
318
|
state->endpoint_collection_enabled = true;
|
|
310
319
|
state->timeline_enabled = true;
|
|
320
|
+
state->allocation_type_enabled = true;
|
|
311
321
|
state->time_converter_state = (monotonic_to_system_epoch_state) MONOTONIC_TO_SYSTEM_EPOCH_INITIALIZER;
|
|
312
322
|
state->main_thread = rb_thread_main();
|
|
313
323
|
|
|
@@ -321,10 +331,12 @@ static VALUE _native_initialize(
|
|
|
321
331
|
VALUE max_frames,
|
|
322
332
|
VALUE tracer_context_key,
|
|
323
333
|
VALUE endpoint_collection_enabled,
|
|
324
|
-
VALUE timeline_enabled
|
|
334
|
+
VALUE timeline_enabled,
|
|
335
|
+
VALUE allocation_type_enabled
|
|
325
336
|
) {
|
|
326
337
|
ENFORCE_BOOLEAN(endpoint_collection_enabled);
|
|
327
338
|
ENFORCE_BOOLEAN(timeline_enabled);
|
|
339
|
+
ENFORCE_BOOLEAN(allocation_type_enabled);
|
|
328
340
|
|
|
329
341
|
struct thread_context_collector_state *state;
|
|
330
342
|
TypedData_Get_Struct(collector_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
@@ -338,6 +350,7 @@ static VALUE _native_initialize(
|
|
|
338
350
|
state->recorder_instance = enforce_recorder_instance(recorder_instance);
|
|
339
351
|
state->endpoint_collection_enabled = (endpoint_collection_enabled == Qtrue);
|
|
340
352
|
state->timeline_enabled = (timeline_enabled == Qtrue);
|
|
353
|
+
state->allocation_type_enabled = (allocation_type_enabled == Qtrue);
|
|
341
354
|
|
|
342
355
|
if (RTEST(tracer_context_key)) {
|
|
343
356
|
ENFORCE_TYPE(tracer_context_key, T_SYMBOL);
|
|
@@ -461,9 +474,11 @@ void update_metrics_and_sample(
|
|
|
461
474
|
thread_being_sampled,
|
|
462
475
|
stack_from_thread,
|
|
463
476
|
thread_context,
|
|
464
|
-
(sample_values) {.cpu_time_ns = cpu_time_elapsed_ns, .
|
|
477
|
+
(sample_values) {.cpu_time_ns = cpu_time_elapsed_ns, .cpu_or_wall_samples = 1, .wall_time_ns = wall_time_elapsed_ns},
|
|
465
478
|
SAMPLE_REGULAR,
|
|
466
|
-
current_monotonic_wall_time_ns
|
|
479
|
+
current_monotonic_wall_time_ns,
|
|
480
|
+
NULL,
|
|
481
|
+
NULL
|
|
467
482
|
);
|
|
468
483
|
}
|
|
469
484
|
|
|
@@ -604,9 +619,11 @@ VALUE thread_context_collector_sample_after_gc(VALUE self_instance) {
|
|
|
604
619
|
/* thread: */ thread,
|
|
605
620
|
/* stack_from_thread: */ thread,
|
|
606
621
|
thread_context,
|
|
607
|
-
(sample_values) {.cpu_time_ns = gc_cpu_time_elapsed_ns, .
|
|
622
|
+
(sample_values) {.cpu_time_ns = gc_cpu_time_elapsed_ns, .cpu_or_wall_samples = 1, .wall_time_ns = gc_wall_time_elapsed_ns},
|
|
608
623
|
SAMPLE_IN_GC,
|
|
609
|
-
INVALID_TIME // For now we're not collecting timestamps for these events
|
|
624
|
+
INVALID_TIME, // For now we're not collecting timestamps for these events
|
|
625
|
+
NULL,
|
|
626
|
+
NULL
|
|
610
627
|
);
|
|
611
628
|
|
|
612
629
|
// Mark thread as no longer in GC
|
|
@@ -637,13 +654,17 @@ static void trigger_sample_for_thread(
|
|
|
637
654
|
struct per_thread_context *thread_context,
|
|
638
655
|
sample_values values,
|
|
639
656
|
sample_type type,
|
|
640
|
-
long current_monotonic_wall_time_ns
|
|
657
|
+
long current_monotonic_wall_time_ns,
|
|
658
|
+
// These two labels are only used for allocation profiling; @ivoanjo: may want to refactor this at some point?
|
|
659
|
+
ddog_CharSlice *ruby_vm_type,
|
|
660
|
+
ddog_CharSlice *class_name
|
|
641
661
|
) {
|
|
642
662
|
int max_label_count =
|
|
643
663
|
1 + // thread id
|
|
644
664
|
1 + // thread name
|
|
645
665
|
1 + // profiler overhead
|
|
646
|
-
|
|
666
|
+
2 + // ruby vm type and allocation class
|
|
667
|
+
1 + // state (only set for cpu/wall-time samples)
|
|
647
668
|
2; // local root span id and span id
|
|
648
669
|
ddog_prof_Label labels[max_label_count];
|
|
649
670
|
int label_pos = 0;
|
|
@@ -706,10 +727,31 @@ static void trigger_sample_for_thread(
|
|
|
706
727
|
};
|
|
707
728
|
}
|
|
708
729
|
|
|
709
|
-
if (
|
|
730
|
+
if (ruby_vm_type != NULL) {
|
|
710
731
|
labels[label_pos++] = (ddog_prof_Label) {
|
|
711
|
-
.key = DDOG_CHARSLICE_C("
|
|
712
|
-
.
|
|
732
|
+
.key = DDOG_CHARSLICE_C("ruby vm type"),
|
|
733
|
+
.str = *ruby_vm_type
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if (class_name != NULL) {
|
|
738
|
+
labels[label_pos++] = (ddog_prof_Label) {
|
|
739
|
+
.key = DDOG_CHARSLICE_C("allocation class"),
|
|
740
|
+
.str = *class_name
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// This label is handled specially:
|
|
745
|
+
// 1. It's only set for cpu/wall-time samples
|
|
746
|
+
// 2. We set it here to its default state of "unknown", but the `Collectors::Stack` may choose to override it with
|
|
747
|
+
// something more interesting.
|
|
748
|
+
ddog_prof_Label *state_label = NULL;
|
|
749
|
+
if (values.cpu_or_wall_samples > 0) {
|
|
750
|
+
state_label = &labels[label_pos++];
|
|
751
|
+
*state_label = (ddog_prof_Label) {
|
|
752
|
+
.key = DDOG_CHARSLICE_C("state"),
|
|
753
|
+
.str = DDOG_CHARSLICE_C("unknown"),
|
|
754
|
+
.num = 0, // This shouldn't be needed but the tracer-2.7 docker image ships a buggy gcc that complains about this
|
|
713
755
|
};
|
|
714
756
|
}
|
|
715
757
|
|
|
@@ -721,12 +763,20 @@ static void trigger_sample_for_thread(
|
|
|
721
763
|
rb_raise(rb_eRuntimeError, "BUG: Unexpected label_pos (%d) > max_label_count (%d)", label_pos, max_label_count);
|
|
722
764
|
}
|
|
723
765
|
|
|
766
|
+
ddog_prof_Slice_Label slice_labels = {.ptr = labels, .len = label_pos};
|
|
767
|
+
|
|
768
|
+
// The end_timestamp_ns is treated specially by libdatadog and that's why it's not added as a ddog_prof_Label
|
|
769
|
+
int64_t end_timestamp_ns = 0;
|
|
770
|
+
if (state->timeline_enabled && current_monotonic_wall_time_ns != INVALID_TIME) {
|
|
771
|
+
end_timestamp_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, current_monotonic_wall_time_ns);
|
|
772
|
+
}
|
|
773
|
+
|
|
724
774
|
sample_thread(
|
|
725
775
|
stack_from_thread,
|
|
726
776
|
state->sampling_buffer,
|
|
727
777
|
state->recorder_instance,
|
|
728
778
|
values,
|
|
729
|
-
(
|
|
779
|
+
(sample_labels) {.labels = slice_labels, .state_label = state_label, .end_timestamp_ns = end_timestamp_ns},
|
|
730
780
|
type
|
|
731
781
|
);
|
|
732
782
|
}
|
|
@@ -765,6 +815,27 @@ static struct per_thread_context *get_context_for(VALUE thread, struct thread_co
|
|
|
765
815
|
return thread_context;
|
|
766
816
|
}
|
|
767
817
|
|
|
818
|
+
#define LOGGING_GEM_PATH "/lib/logging/diagnostic_context.rb"
|
|
819
|
+
|
|
820
|
+
// The `logging` gem monkey patches thread creation, which makes the `invoke_location_for` useless, since every thread
|
|
821
|
+
// will point to the `logging` gem. When that happens, we avoid using the invoke location.
|
|
822
|
+
//
|
|
823
|
+
// TODO: This approach is a bit brittle, since it matches on the specific gem path, and only works for the `logging`
|
|
824
|
+
// gem.
|
|
825
|
+
// In the future we should probably explore a more generic fix (e.g. using Thread.method(:new).source_location or
|
|
826
|
+
// something like that to detect redefinition of the `Thread` methods). One difficulty of doing it is that we need
|
|
827
|
+
// to either run Ruby code during sampling (not great), or otherwise use some of the VM private APIs to detect this.
|
|
828
|
+
//
|
|
829
|
+
static bool is_logging_gem_monkey_patch(VALUE invoke_file_location) {
|
|
830
|
+
int logging_gem_path_len = strlen(LOGGING_GEM_PATH);
|
|
831
|
+
char *invoke_file = StringValueCStr(invoke_file_location);
|
|
832
|
+
int invoke_file_len = strlen(invoke_file);
|
|
833
|
+
|
|
834
|
+
if (invoke_file_len < logging_gem_path_len) return false;
|
|
835
|
+
|
|
836
|
+
return strncmp(invoke_file + invoke_file_len - logging_gem_path_len, LOGGING_GEM_PATH, logging_gem_path_len) == 0;
|
|
837
|
+
}
|
|
838
|
+
|
|
768
839
|
static void initialize_context(VALUE thread, struct per_thread_context *thread_context, struct thread_context_collector_state *state) {
|
|
769
840
|
snprintf(thread_context->thread_id, THREAD_ID_LIMIT_CHARS, "%"PRIu64" (%lu)", native_thread_id_for(thread), (unsigned long) thread_id_for(thread));
|
|
770
841
|
thread_context->thread_id_char_slice = (ddog_CharSlice) {.ptr = thread_context->thread_id, .len = strlen(thread_context->thread_id)};
|
|
@@ -772,13 +843,17 @@ static void initialize_context(VALUE thread, struct per_thread_context *thread_c
|
|
|
772
843
|
int invoke_line_location;
|
|
773
844
|
VALUE invoke_file_location = invoke_location_for(thread, &invoke_line_location);
|
|
774
845
|
if (invoke_file_location != Qnil) {
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
846
|
+
if (!is_logging_gem_monkey_patch(invoke_file_location)) {
|
|
847
|
+
snprintf(
|
|
848
|
+
thread_context->thread_invoke_location,
|
|
849
|
+
THREAD_INVOKE_LOCATION_LIMIT_CHARS,
|
|
850
|
+
"%s:%d",
|
|
851
|
+
StringValueCStr(invoke_file_location),
|
|
852
|
+
invoke_line_location
|
|
853
|
+
);
|
|
854
|
+
} else {
|
|
855
|
+
snprintf(thread_context->thread_invoke_location, THREAD_INVOKE_LOCATION_LIMIT_CHARS, "%s", "(Unnamed thread)");
|
|
856
|
+
}
|
|
782
857
|
} else if (thread != state->main_thread) {
|
|
783
858
|
// If the first function of a thread is native code, there won't be an invoke location, so we use this fallback.
|
|
784
859
|
// NOTE: In the future, I wonder if we could take the pointer to the native function, and try to see if there's a native
|
|
@@ -819,6 +894,7 @@ static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instanc
|
|
|
819
894
|
rb_str_concat(result, rb_sprintf(" stats=%"PRIsVALUE, stats_as_ruby_hash(state)));
|
|
820
895
|
rb_str_concat(result, rb_sprintf(" endpoint_collection_enabled=%"PRIsVALUE, state->endpoint_collection_enabled ? Qtrue : Qfalse));
|
|
821
896
|
rb_str_concat(result, rb_sprintf(" timeline_enabled=%"PRIsVALUE, state->timeline_enabled ? Qtrue : Qfalse));
|
|
897
|
+
rb_str_concat(result, rb_sprintf(" allocation_type_enabled=%"PRIsVALUE, state->allocation_type_enabled ? Qtrue : Qfalse));
|
|
822
898
|
rb_str_concat(result, rb_sprintf(
|
|
823
899
|
" time_converter_state={.system_epoch_ns_reference=%ld, .delta_to_epoch_ns=%ld}",
|
|
824
900
|
state->time_converter_state.system_epoch_ns_reference,
|
|
@@ -1008,7 +1084,7 @@ static void trace_identifiers_for(struct thread_context_collector_state *state,
|
|
|
1008
1084
|
if (!state->endpoint_collection_enabled) return;
|
|
1009
1085
|
|
|
1010
1086
|
VALUE root_span_type = rb_ivar_get(root_span, at_type_id /* @type */);
|
|
1011
|
-
if (root_span_type == Qnil || !
|
|
1087
|
+
if (root_span_type == Qnil || !should_collect_resource(root_span_type)) return;
|
|
1012
1088
|
|
|
1013
1089
|
VALUE trace_resource = rb_ivar_get(active_trace, at_resource_id /* @resource */);
|
|
1014
1090
|
if (RB_TYPE_P(trace_resource, T_STRING)) {
|
|
@@ -1019,11 +1095,21 @@ static void trace_identifiers_for(struct thread_context_collector_state *state,
|
|
|
1019
1095
|
}
|
|
1020
1096
|
}
|
|
1021
1097
|
|
|
1022
|
-
|
|
1098
|
+
// We only collect the resource for spans of types:
|
|
1099
|
+
// * 'web', for web requests
|
|
1100
|
+
// * proxy', used by the rack integration with request_queuing: true (e.g. also represents a web request)
|
|
1101
|
+
//
|
|
1102
|
+
// NOTE: Currently we're only interested in HTTP service endpoints. Over time, this list may be expanded.
|
|
1103
|
+
// Resources MUST NOT include personal identifiable information (PII); this should not be the case with
|
|
1104
|
+
// ddtrace integrations, but worth mentioning just in case :)
|
|
1105
|
+
static bool should_collect_resource(VALUE root_span_type) {
|
|
1023
1106
|
ENFORCE_TYPE(root_span_type, T_STRING);
|
|
1024
1107
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1108
|
+
int root_span_type_length = RSTRING_LEN(root_span_type);
|
|
1109
|
+
const char *root_span_type_value = StringValuePtr(root_span_type);
|
|
1110
|
+
|
|
1111
|
+
return (root_span_type_length == strlen("web") && (memcmp("web", root_span_type_value, strlen("web")) == 0)) ||
|
|
1112
|
+
(root_span_type_length == strlen("proxy") && (memcmp("proxy", root_span_type_value, strlen("proxy")) == 0));
|
|
1027
1113
|
}
|
|
1028
1114
|
|
|
1029
1115
|
// After the Ruby VM forks, this method gets called in the child process to clean up any leftover state from the parent.
|
|
@@ -1050,12 +1136,73 @@ static VALUE thread_list(struct thread_context_collector_state *state) {
|
|
|
1050
1136
|
return result;
|
|
1051
1137
|
}
|
|
1052
1138
|
|
|
1053
|
-
void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight) {
|
|
1139
|
+
void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object) {
|
|
1054
1140
|
struct thread_context_collector_state *state;
|
|
1055
1141
|
TypedData_Get_Struct(self_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1056
1142
|
|
|
1057
1143
|
VALUE current_thread = rb_thread_current();
|
|
1058
1144
|
|
|
1145
|
+
enum ruby_value_type type = rb_type(new_object);
|
|
1146
|
+
|
|
1147
|
+
// Tag samples with the VM internal types
|
|
1148
|
+
ddog_CharSlice ruby_vm_type = ruby_value_type_to_char_slice(type);
|
|
1149
|
+
|
|
1150
|
+
// Since this is stack allocated, be careful about moving it
|
|
1151
|
+
ddog_CharSlice class_name;
|
|
1152
|
+
ddog_CharSlice *optional_class_name = NULL;
|
|
1153
|
+
|
|
1154
|
+
if (state->allocation_type_enabled) {
|
|
1155
|
+
optional_class_name = &class_name;
|
|
1156
|
+
|
|
1157
|
+
if (
|
|
1158
|
+
type == RUBY_T_OBJECT ||
|
|
1159
|
+
type == RUBY_T_CLASS ||
|
|
1160
|
+
type == RUBY_T_MODULE ||
|
|
1161
|
+
type == RUBY_T_FLOAT ||
|
|
1162
|
+
type == RUBY_T_STRING ||
|
|
1163
|
+
type == RUBY_T_REGEXP ||
|
|
1164
|
+
type == RUBY_T_ARRAY ||
|
|
1165
|
+
type == RUBY_T_HASH ||
|
|
1166
|
+
type == RUBY_T_STRUCT ||
|
|
1167
|
+
type == RUBY_T_BIGNUM ||
|
|
1168
|
+
type == RUBY_T_FILE ||
|
|
1169
|
+
type == RUBY_T_DATA ||
|
|
1170
|
+
type == RUBY_T_MATCH ||
|
|
1171
|
+
type == RUBY_T_COMPLEX ||
|
|
1172
|
+
type == RUBY_T_RATIONAL ||
|
|
1173
|
+
type == RUBY_T_NIL ||
|
|
1174
|
+
type == RUBY_T_TRUE ||
|
|
1175
|
+
type == RUBY_T_FALSE ||
|
|
1176
|
+
type == RUBY_T_SYMBOL ||
|
|
1177
|
+
type == RUBY_T_FIXNUM
|
|
1178
|
+
) {
|
|
1179
|
+
VALUE klass = rb_class_of(new_object);
|
|
1180
|
+
|
|
1181
|
+
// Ruby sometimes plays a bit fast and loose with some of its internal objects, e.g.
|
|
1182
|
+
// `rb_str_tmp_frozen_acquire` allocates a string with no class (klass=0).
|
|
1183
|
+
// Thus, we need to make sure there's actually a class before getting its name.
|
|
1184
|
+
|
|
1185
|
+
if (klass != 0) {
|
|
1186
|
+
const char *name = rb_obj_classname(new_object);
|
|
1187
|
+
size_t name_length = name != NULL ? strlen(name) : 0;
|
|
1188
|
+
|
|
1189
|
+
if (name_length > 0) {
|
|
1190
|
+
class_name = (ddog_CharSlice) {.ptr = name, .len = name_length};
|
|
1191
|
+
} else {
|
|
1192
|
+
// @ivoanjo: I'm not sure this can ever happen, but just-in-case
|
|
1193
|
+
class_name = ruby_value_type_to_class_name(type);
|
|
1194
|
+
}
|
|
1195
|
+
} else {
|
|
1196
|
+
// Fallback for objects with no class
|
|
1197
|
+
class_name = ruby_value_type_to_class_name(type);
|
|
1198
|
+
}
|
|
1199
|
+
} else if (type == RUBY_T_IMEMO) {
|
|
1200
|
+
class_name = DDOG_CHARSLICE_C("(VM Internal, T_IMEMO)");
|
|
1201
|
+
} else {
|
|
1202
|
+
class_name = ruby_vm_type; // For other weird internal things we just use the VM type
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1059
1206
|
trigger_sample_for_thread(
|
|
1060
1207
|
state,
|
|
1061
1208
|
/* thread: */ current_thread,
|
|
@@ -1063,14 +1210,16 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
|
1063
1210
|
get_or_create_context_for(current_thread, state),
|
|
1064
1211
|
(sample_values) {.alloc_samples = sample_weight},
|
|
1065
1212
|
SAMPLE_REGULAR,
|
|
1066
|
-
INVALID_TIME // For now we're not collecting timestamps for allocation events, as per profiling team internal discussions
|
|
1213
|
+
INVALID_TIME, // For now we're not collecting timestamps for allocation events, as per profiling team internal discussions
|
|
1214
|
+
&ruby_vm_type,
|
|
1215
|
+
optional_class_name
|
|
1067
1216
|
);
|
|
1068
1217
|
}
|
|
1069
1218
|
|
|
1070
1219
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
|
1071
1220
|
// It SHOULD NOT be used for other purposes.
|
|
1072
|
-
static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight) {
|
|
1073
|
-
thread_context_collector_sample_allocation(collector_instance, NUM2UINT(sample_weight));
|
|
1221
|
+
static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object) {
|
|
1222
|
+
thread_context_collector_sample_allocation(collector_instance, NUM2UINT(sample_weight), new_object);
|
|
1074
1223
|
return Qtrue;
|
|
1075
1224
|
}
|
|
1076
1225
|
|
|
@@ -1082,3 +1231,29 @@ static VALUE new_empty_thread_inner(DDTRACE_UNUSED void *arg) { return Qnil; }
|
|
|
1082
1231
|
static VALUE _native_new_empty_thread(DDTRACE_UNUSED VALUE self) {
|
|
1083
1232
|
return rb_thread_create(new_empty_thread_inner, NULL);
|
|
1084
1233
|
}
|
|
1234
|
+
|
|
1235
|
+
ddog_CharSlice ruby_value_type_to_class_name(enum ruby_value_type type) {
|
|
1236
|
+
switch (type) {
|
|
1237
|
+
case(RUBY_T_OBJECT ): return DDOG_CHARSLICE_C("Object");
|
|
1238
|
+
case(RUBY_T_CLASS ): return DDOG_CHARSLICE_C("Class");
|
|
1239
|
+
case(RUBY_T_MODULE ): return DDOG_CHARSLICE_C("Module");
|
|
1240
|
+
case(RUBY_T_FLOAT ): return DDOG_CHARSLICE_C("Float");
|
|
1241
|
+
case(RUBY_T_STRING ): return DDOG_CHARSLICE_C("String");
|
|
1242
|
+
case(RUBY_T_REGEXP ): return DDOG_CHARSLICE_C("Regexp");
|
|
1243
|
+
case(RUBY_T_ARRAY ): return DDOG_CHARSLICE_C("Array");
|
|
1244
|
+
case(RUBY_T_HASH ): return DDOG_CHARSLICE_C("Hash");
|
|
1245
|
+
case(RUBY_T_STRUCT ): return DDOG_CHARSLICE_C("Struct");
|
|
1246
|
+
case(RUBY_T_BIGNUM ): return DDOG_CHARSLICE_C("Integer");
|
|
1247
|
+
case(RUBY_T_FILE ): return DDOG_CHARSLICE_C("File");
|
|
1248
|
+
case(RUBY_T_DATA ): return DDOG_CHARSLICE_C("(VM Internal, T_DATA)");
|
|
1249
|
+
case(RUBY_T_MATCH ): return DDOG_CHARSLICE_C("MatchData");
|
|
1250
|
+
case(RUBY_T_COMPLEX ): return DDOG_CHARSLICE_C("Complex");
|
|
1251
|
+
case(RUBY_T_RATIONAL): return DDOG_CHARSLICE_C("Rational");
|
|
1252
|
+
case(RUBY_T_NIL ): return DDOG_CHARSLICE_C("NilClass");
|
|
1253
|
+
case(RUBY_T_TRUE ): return DDOG_CHARSLICE_C("TrueClass");
|
|
1254
|
+
case(RUBY_T_FALSE ): return DDOG_CHARSLICE_C("FalseClass");
|
|
1255
|
+
case(RUBY_T_SYMBOL ): return DDOG_CHARSLICE_C("Symbol");
|
|
1256
|
+
case(RUBY_T_FIXNUM ): return DDOG_CHARSLICE_C("Integer");
|
|
1257
|
+
default: return DDOG_CHARSLICE_C("(VM Internal, Missing class)");
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
@@ -7,7 +7,7 @@ void thread_context_collector_sample(
|
|
|
7
7
|
long current_monotonic_wall_time_ns,
|
|
8
8
|
VALUE profiler_overhead_stack_thread
|
|
9
9
|
);
|
|
10
|
-
void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight);
|
|
10
|
+
void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object);
|
|
11
11
|
VALUE thread_context_collector_sample_after_gc(VALUE self_instance);
|
|
12
12
|
void thread_context_collector_on_gc_start(VALUE self_instance);
|
|
13
13
|
void thread_context_collector_on_gc_finish(VALUE self_instance);
|