ddtrace 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +100 -1
- data/README.md +2 -2
- data/ext/ddtrace_profiling_loader/extconf.rb +4 -1
- data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +1 -1
- data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +3 -2
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.c +24 -50
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.h +1 -1
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +284 -74
- data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.c +142 -0
- data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.h +14 -0
- data/ext/ddtrace_profiling_native_extension/collectors_idle_sampling_helper.c +241 -0
- data/ext/ddtrace_profiling_native_extension/collectors_idle_sampling_helper.h +3 -0
- data/ext/ddtrace_profiling_native_extension/collectors_stack.c +32 -32
- data/ext/ddtrace_profiling_native_extension/collectors_stack.h +2 -2
- data/ext/ddtrace_profiling_native_extension/extconf.rb +21 -7
- data/ext/ddtrace_profiling_native_extension/helpers.h +5 -0
- data/ext/ddtrace_profiling_native_extension/http_transport.c +50 -49
- data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +5 -1
- data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +42 -12
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +116 -22
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +9 -0
- data/ext/ddtrace_profiling_native_extension/profiling.c +205 -0
- data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +86 -0
- data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +28 -6
- data/ext/ddtrace_profiling_native_extension/setup_signal_handler.c +23 -4
- data/ext/ddtrace_profiling_native_extension/setup_signal_handler.h +4 -0
- data/ext/ddtrace_profiling_native_extension/stack_recorder.c +47 -50
- data/ext/ddtrace_profiling_native_extension/stack_recorder.h +4 -4
- data/ext/ddtrace_profiling_native_extension/time_helpers.c +17 -0
- data/ext/ddtrace_profiling_native_extension/time_helpers.h +10 -0
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +75 -8
- data/lib/datadog/appsec/assets/waf_rules/risky.json +1 -1
- data/lib/datadog/appsec/assets/waf_rules/strict.json +1 -1
- data/lib/datadog/appsec/assets.rb +1 -1
- data/lib/datadog/appsec/configuration/settings.rb +35 -22
- data/lib/datadog/appsec/configuration.rb +4 -2
- data/lib/datadog/appsec/contrib/auto_instrument.rb +1 -1
- data/lib/datadog/appsec/contrib/configuration/settings.rb +1 -1
- data/lib/datadog/appsec/contrib/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/patcher.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/configuration/settings.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/ext.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/request.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/response.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/configuration/settings.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/ext.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/framework.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/request.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/request_middleware.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/configuration/settings.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/ext.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/framework.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/request_middleware.rb +1 -1
- data/lib/datadog/appsec/event.rb +1 -1
- data/lib/datadog/appsec/extensions.rb +36 -26
- data/lib/datadog/appsec/instrumentation/gateway.rb +3 -3
- data/lib/datadog/appsec/processor.rb +15 -19
- data/lib/datadog/appsec/rate_limiter.rb +1 -1
- data/lib/datadog/appsec/reactive/address_hash.rb +1 -1
- data/lib/datadog/appsec/reactive/engine.rb +1 -1
- data/lib/datadog/appsec/reactive/operation.rb +2 -2
- data/lib/datadog/appsec/reactive/subscriber.rb +1 -1
- data/lib/datadog/appsec/response.rb +18 -9
- data/lib/datadog/appsec/utils/http/media_range.rb +201 -0
- data/lib/datadog/appsec/utils/http/media_type.rb +87 -0
- data/lib/datadog/appsec/utils/http.rb +9 -0
- data/lib/datadog/appsec/utils.rb +7 -0
- data/lib/datadog/appsec.rb +1 -1
- data/lib/datadog/ci/ext/environment.rb +57 -13
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +2 -2
- data/lib/datadog/core/configuration/base.rb +3 -0
- data/lib/datadog/core/configuration/components.rb +27 -6
- data/lib/datadog/core/configuration/ext.rb +26 -0
- data/lib/datadog/core/configuration/option_definition.rb +11 -2
- data/lib/datadog/core/configuration/settings.rb +16 -341
- data/lib/datadog/core/diagnostics/environment_logger.rb +4 -3
- data/lib/datadog/core/diagnostics/health.rb +4 -22
- data/lib/datadog/core/environment/variable_helpers.rb +58 -10
- data/lib/datadog/core/metrics/client.rb +3 -2
- data/lib/datadog/core/metrics/ext.rb +0 -2
- data/lib/datadog/core/telemetry/collector.rb +1 -0
- data/lib/datadog/core/utils.rb +0 -21
- data/lib/datadog/core.rb +21 -1
- data/lib/datadog/kit/appsec/events.rb +75 -0
- data/lib/datadog/kit/enable_core_dumps.rb +1 -0
- data/lib/datadog/kit/identity.rb +8 -7
- data/lib/datadog/opentelemetry/api/context.rb +187 -0
- data/lib/datadog/opentelemetry/api/trace/span.rb +15 -0
- data/lib/datadog/opentelemetry/sdk/configurator.rb +38 -0
- data/lib/datadog/opentelemetry/sdk/id_generator.rb +27 -0
- data/lib/datadog/opentelemetry/sdk/propagator.rb +91 -0
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +92 -0
- data/lib/datadog/opentelemetry.rb +48 -0
- data/lib/datadog/opentracer/distributed_headers.rb +2 -2
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +16 -5
- data/lib/datadog/profiling/collectors/dynamic_sampling_rate.rb +14 -0
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +68 -0
- data/lib/datadog/profiling/stack_recorder.rb +14 -0
- data/lib/datadog/profiling.rb +2 -0
- data/lib/datadog/tracing/configuration/ext.rb +33 -4
- data/lib/datadog/tracing/configuration/settings.rb +433 -0
- data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +4 -1
- data/lib/datadog/tracing/contrib/aws/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +4 -1
- data/lib/datadog/tracing/contrib/dalli/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +6 -1
- data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/excon/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +6 -1
- data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +9 -4
- data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -1
- data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +10 -3
- data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +9 -4
- data/lib/datadog/tracing/contrib/http/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +3 -6
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -1
- data/lib/datadog/tracing/contrib/httpclient/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +3 -4
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -1
- data/lib/datadog/tracing/contrib/httprb/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +3 -4
- data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +4 -1
- data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +2 -2
- data/lib/datadog/tracing/contrib/patcher.rb +3 -2
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +4 -1
- data/lib/datadog/tracing/contrib/pg/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +56 -33
- data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +4 -1
- data/lib/datadog/tracing/contrib/presto/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +10 -12
- data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +4 -1
- data/lib/datadog/tracing/contrib/redis/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/redis/instrumentation.rb +30 -23
- data/lib/datadog/tracing/contrib/redis/integration.rb +34 -2
- data/lib/datadog/tracing/contrib/redis/patcher.rb +18 -14
- data/lib/datadog/tracing/contrib/redis/quantize.rb +12 -9
- data/lib/datadog/tracing/contrib/redis/tags.rb +4 -6
- data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +72 -0
- data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +6 -1
- data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +33 -0
- data/lib/datadog/tracing/contrib/stripe/ext.rb +26 -0
- data/lib/datadog/tracing/contrib/stripe/integration.rb +43 -0
- data/lib/datadog/tracing/contrib/stripe/patcher.rb +29 -0
- data/lib/datadog/tracing/contrib/stripe/request.rb +67 -0
- data/lib/datadog/tracing/contrib.rb +1 -0
- data/lib/datadog/{core → tracing}/diagnostics/ext.rb +1 -6
- data/lib/datadog/tracing/diagnostics/health.rb +40 -0
- data/lib/datadog/tracing/distributed/{b3.rb → b3_multi.rb} +2 -2
- data/lib/datadog/tracing/distributed/helpers.rb +2 -1
- data/lib/datadog/tracing/distributed/none.rb +19 -0
- data/lib/datadog/tracing/distributed/trace_context.rb +378 -0
- data/lib/datadog/tracing/metadata/ext.rb +1 -1
- data/lib/datadog/tracing/metadata/tagging.rb +6 -0
- data/lib/datadog/tracing/sampling/priority_sampler.rb +11 -0
- data/lib/datadog/tracing/sampling/rate_sampler.rb +3 -3
- data/lib/datadog/tracing/span.rb +3 -19
- data/lib/datadog/tracing/span_operation.rb +5 -4
- data/lib/datadog/tracing/trace_digest.rb +85 -2
- data/lib/datadog/tracing/trace_operation.rb +13 -4
- data/lib/datadog/tracing/utils.rb +50 -0
- data/lib/ddtrace/version.rb +1 -1
- metadata +41 -9
|
@@ -10,3 +10,8 @@
|
|
|
10
10
|
#else
|
|
11
11
|
#define DDTRACE_UNUSED
|
|
12
12
|
#endif
|
|
13
|
+
|
|
14
|
+
// @ivoanjo: After trying to read through https://stackoverflow.com/questions/3437404/min-and-max-in-c I decided I
|
|
15
|
+
// don't like C and I just implemented this as a function.
|
|
16
|
+
inline static uint64_t uint64_max_of(uint64_t a, uint64_t b) { return a > b ? a : b; }
|
|
17
|
+
inline static uint64_t uint64_min_of(uint64_t a, uint64_t b) { return a > b ? b : a; }
|
|
@@ -20,20 +20,20 @@ static VALUE http_transport_class = Qnil;
|
|
|
20
20
|
static VALUE library_version_string = Qnil;
|
|
21
21
|
|
|
22
22
|
struct call_exporter_without_gvl_arguments {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
ddog_prof_Exporter *exporter;
|
|
24
|
+
ddog_prof_Exporter_Request *request;
|
|
25
25
|
ddog_CancellationToken *cancel_token;
|
|
26
|
-
|
|
26
|
+
ddog_prof_Exporter_SendResult result;
|
|
27
27
|
bool send_ran;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
inline static ddog_ByteSlice byte_slice_from_ruby_string(VALUE string);
|
|
31
31
|
static VALUE _native_validate_exporter(VALUE self, VALUE exporter_configuration);
|
|
32
|
-
static
|
|
33
|
-
static VALUE handle_exporter_failure(
|
|
32
|
+
static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array);
|
|
33
|
+
static VALUE handle_exporter_failure(ddog_prof_Exporter_NewResult exporter_result);
|
|
34
34
|
static ddog_Endpoint endpoint_from(VALUE exporter_configuration);
|
|
35
|
-
static
|
|
36
|
-
static void safely_log_failure_to_process_tag(
|
|
35
|
+
static ddog_Vec_Tag convert_tags(VALUE tags_as_array);
|
|
36
|
+
static void safely_log_failure_to_process_tag(ddog_Vec_Tag tags, VALUE err_details);
|
|
37
37
|
static VALUE _native_do_export(
|
|
38
38
|
VALUE self,
|
|
39
39
|
VALUE exporter_configuration,
|
|
@@ -76,46 +76,46 @@ inline static ddog_ByteSlice byte_slice_from_ruby_string(VALUE string) {
|
|
|
76
76
|
|
|
77
77
|
static VALUE _native_validate_exporter(DDTRACE_UNUSED VALUE _self, VALUE exporter_configuration) {
|
|
78
78
|
ENFORCE_TYPE(exporter_configuration, T_ARRAY);
|
|
79
|
-
|
|
79
|
+
ddog_prof_Exporter_NewResult exporter_result = create_exporter(exporter_configuration, rb_ary_new());
|
|
80
80
|
|
|
81
81
|
VALUE failure_tuple = handle_exporter_failure(exporter_result);
|
|
82
82
|
if (!NIL_P(failure_tuple)) return failure_tuple;
|
|
83
83
|
|
|
84
84
|
// We don't actually need the exporter for now -- we just wanted to validate that we could create it with the
|
|
85
85
|
// settings we were given
|
|
86
|
-
|
|
86
|
+
ddog_prof_Exporter_NewResult_drop(exporter_result);
|
|
87
87
|
|
|
88
88
|
return rb_ary_new_from_args(2, ok_symbol, Qnil);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
static
|
|
91
|
+
static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array) {
|
|
92
92
|
ENFORCE_TYPE(exporter_configuration, T_ARRAY);
|
|
93
93
|
ENFORCE_TYPE(tags_as_array, T_ARRAY);
|
|
94
94
|
|
|
95
|
-
// This needs to be called BEFORE convert_tags since it can raise an exception and thus cause the
|
|
95
|
+
// This needs to be called BEFORE convert_tags since it can raise an exception and thus cause the ddog_Vec_Tag
|
|
96
96
|
// to be leaked.
|
|
97
97
|
ddog_Endpoint endpoint = endpoint_from(exporter_configuration);
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
ddog_Vec_Tag tags = convert_tags(tags_as_array);
|
|
100
100
|
|
|
101
101
|
ddog_CharSlice library_name = DDOG_CHARSLICE_C("dd-trace-rb");
|
|
102
102
|
ddog_CharSlice library_version = char_slice_from_ruby_string(library_version_string);
|
|
103
103
|
ddog_CharSlice profiling_family = DDOG_CHARSLICE_C("ruby");
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
ddog_prof_Exporter_NewResult exporter_result =
|
|
106
|
+
ddog_prof_Exporter_new(library_name, library_version, profiling_family, &tags, endpoint);
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
ddog_Vec_Tag_drop(tags);
|
|
109
109
|
|
|
110
110
|
return exporter_result;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
static VALUE handle_exporter_failure(
|
|
114
|
-
if (exporter_result.tag ==
|
|
113
|
+
static VALUE handle_exporter_failure(ddog_prof_Exporter_NewResult exporter_result) {
|
|
114
|
+
if (exporter_result.tag == DDOG_PROF_EXPORTER_NEW_RESULT_OK) return Qnil;
|
|
115
115
|
|
|
116
|
-
VALUE err_details =
|
|
116
|
+
VALUE err_details = ruby_string_from_prof_vec_u8(exporter_result.err);
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
ddog_prof_Exporter_NewResult_drop(exporter_result);
|
|
119
119
|
|
|
120
120
|
return rb_ary_new_from_args(2, error_symbol, err_details);
|
|
121
121
|
}
|
|
@@ -145,17 +145,17 @@ static ddog_Endpoint endpoint_from(VALUE exporter_configuration) {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
__attribute__((warn_unused_result))
|
|
148
|
-
static
|
|
148
|
+
static ddog_Vec_Tag convert_tags(VALUE tags_as_array) {
|
|
149
149
|
ENFORCE_TYPE(tags_as_array, T_ARRAY);
|
|
150
150
|
|
|
151
151
|
long tags_count = RARRAY_LEN(tags_as_array);
|
|
152
|
-
|
|
152
|
+
ddog_Vec_Tag tags = ddog_Vec_Tag_new();
|
|
153
153
|
|
|
154
154
|
for (long i = 0; i < tags_count; i++) {
|
|
155
155
|
VALUE name_value_pair = rb_ary_entry(tags_as_array, i);
|
|
156
156
|
|
|
157
157
|
if (!RB_TYPE_P(name_value_pair, T_ARRAY)) {
|
|
158
|
-
|
|
158
|
+
ddog_Vec_Tag_drop(tags);
|
|
159
159
|
ENFORCE_TYPE(name_value_pair, T_ARRAY);
|
|
160
160
|
}
|
|
161
161
|
|
|
@@ -164,23 +164,23 @@ static ddog_Vec_tag convert_tags(VALUE tags_as_array) {
|
|
|
164
164
|
VALUE tag_value = rb_ary_entry(name_value_pair, 1);
|
|
165
165
|
|
|
166
166
|
if (!(RB_TYPE_P(tag_name, T_STRING) && RB_TYPE_P(tag_value, T_STRING))) {
|
|
167
|
-
|
|
167
|
+
ddog_Vec_Tag_drop(tags);
|
|
168
168
|
ENFORCE_TYPE(tag_name, T_STRING);
|
|
169
169
|
ENFORCE_TYPE(tag_value, T_STRING);
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
ddog_Vec_Tag_PushResult push_result =
|
|
173
|
+
ddog_Vec_Tag_push(&tags, char_slice_from_ruby_string(tag_name), char_slice_from_ruby_string(tag_value));
|
|
174
174
|
|
|
175
|
-
if (push_result.tag ==
|
|
175
|
+
if (push_result.tag == DDOG_VEC_TAG_PUSH_RESULT_ERR) {
|
|
176
176
|
VALUE err_details = ruby_string_from_vec_u8(push_result.err);
|
|
177
|
-
|
|
177
|
+
ddog_Vec_Tag_PushResult_drop(push_result);
|
|
178
178
|
|
|
179
179
|
// libdatadog validates tags and may catch invalid tags that ddtrace didn't actually catch.
|
|
180
180
|
// We warn users about such tags, and then just ignore them.
|
|
181
181
|
safely_log_failure_to_process_tag(tags, err_details);
|
|
182
182
|
} else {
|
|
183
|
-
|
|
183
|
+
ddog_Vec_Tag_PushResult_drop(push_result);
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
@@ -193,12 +193,12 @@ static VALUE log_failure_to_process_tag(VALUE err_details) {
|
|
|
193
193
|
|
|
194
194
|
// Since we are calling into Ruby code, it may raise an exception. This method ensure that dynamically-allocated tags
|
|
195
195
|
// get cleaned before propagating the exception.
|
|
196
|
-
static void safely_log_failure_to_process_tag(
|
|
196
|
+
static void safely_log_failure_to_process_tag(ddog_Vec_Tag tags, VALUE err_details) {
|
|
197
197
|
int exception_state;
|
|
198
198
|
rb_protect(log_failure_to_process_tag, err_details, &exception_state);
|
|
199
199
|
|
|
200
200
|
if (exception_state) { // An exception was raised
|
|
201
|
-
|
|
201
|
+
ddog_Vec_Tag_drop(tags); // clean up
|
|
202
202
|
rb_jump_tag(exception_state); // "Re-raise" exception
|
|
203
203
|
}
|
|
204
204
|
}
|
|
@@ -206,17 +206,18 @@ static void safely_log_failure_to_process_tag(ddog_Vec_tag tags, VALUE err_detai
|
|
|
206
206
|
// Note: This function handles a bunch of libdatadog dynamically-allocated objects, so it MUST not use any Ruby APIs
|
|
207
207
|
// which can raise exceptions, otherwise the objects will be leaked.
|
|
208
208
|
static VALUE perform_export(
|
|
209
|
-
|
|
209
|
+
ddog_prof_Exporter_NewResult valid_exporter_result, // Must be called with a valid exporter result
|
|
210
210
|
ddog_Timespec start,
|
|
211
211
|
ddog_Timespec finish,
|
|
212
|
-
|
|
213
|
-
|
|
212
|
+
ddog_prof_Exporter_Slice_File slice_files,
|
|
213
|
+
ddog_Vec_Tag *additional_tags,
|
|
214
214
|
uint64_t timeout_milliseconds
|
|
215
215
|
) {
|
|
216
|
-
|
|
216
|
+
ddog_prof_Exporter *exporter = valid_exporter_result.ok;
|
|
217
217
|
ddog_CancellationToken *cancel_token = ddog_CancellationToken_new();
|
|
218
|
-
|
|
219
|
-
|
|
218
|
+
ddog_prof_ProfiledEndpointsStats *endpoints_stats = NULL; // Not in use yet
|
|
219
|
+
ddog_prof_Exporter_Request *request =
|
|
220
|
+
ddog_prof_Exporter_Request_build(exporter, start, finish, slice_files, additional_tags, endpoints_stats, timeout_milliseconds);
|
|
220
221
|
|
|
221
222
|
// We'll release the Global VM Lock while we're calling send, so that the Ruby VM can continue to work while this
|
|
222
223
|
// is pending
|
|
@@ -246,23 +247,23 @@ static VALUE perform_export(
|
|
|
246
247
|
|
|
247
248
|
// Cleanup exporter and token, no longer needed
|
|
248
249
|
ddog_CancellationToken_drop(cancel_token);
|
|
249
|
-
|
|
250
|
+
ddog_prof_Exporter_NewResult_drop(valid_exporter_result);
|
|
250
251
|
|
|
251
252
|
if (pending_exception) {
|
|
252
253
|
// If we got here send did not run, so we need to explicitly dispose of the request
|
|
253
|
-
|
|
254
|
+
ddog_prof_Exporter_Request_drop(request);
|
|
254
255
|
|
|
255
256
|
// Let Ruby propagate the exception. This will not return.
|
|
256
257
|
rb_jump_tag(pending_exception);
|
|
257
258
|
}
|
|
258
259
|
|
|
259
|
-
|
|
260
|
-
bool success = result.tag ==
|
|
260
|
+
ddog_prof_Exporter_SendResult result = args.result;
|
|
261
|
+
bool success = result.tag == DDOG_PROF_EXPORTER_SEND_RESULT_HTTP_RESPONSE;
|
|
261
262
|
|
|
262
263
|
VALUE ruby_status = success ? ok_symbol : error_symbol;
|
|
263
|
-
VALUE ruby_result = success ? UINT2NUM(result.http_response.code) :
|
|
264
|
+
VALUE ruby_result = success ? UINT2NUM(result.http_response.code) : ruby_string_from_prof_vec_u8(result.err);
|
|
264
265
|
|
|
265
|
-
|
|
266
|
+
ddog_prof_Exporter_SendResult_drop(args.result);
|
|
266
267
|
// The request itself does not need to be freed as libdatadog takes care of it as part of sending.
|
|
267
268
|
|
|
268
269
|
return rb_ary_new_from_args(2, ruby_status, ruby_result);
|
|
@@ -303,23 +304,23 @@ static VALUE _native_do_export(
|
|
|
303
304
|
{.seconds = NUM2LONG(finish_timespec_seconds), .nanoseconds = NUM2UINT(finish_timespec_nanoseconds)};
|
|
304
305
|
|
|
305
306
|
int files_to_report = 1 + (have_code_provenance ? 1 : 0);
|
|
306
|
-
|
|
307
|
-
|
|
307
|
+
ddog_prof_Exporter_File files[files_to_report];
|
|
308
|
+
ddog_prof_Exporter_Slice_File slice_files = {.ptr = files, .len = files_to_report};
|
|
308
309
|
|
|
309
|
-
files[0] = (
|
|
310
|
+
files[0] = (ddog_prof_Exporter_File) {
|
|
310
311
|
.name = char_slice_from_ruby_string(pprof_file_name),
|
|
311
312
|
.file = byte_slice_from_ruby_string(pprof_data)
|
|
312
313
|
};
|
|
313
314
|
if (have_code_provenance) {
|
|
314
|
-
files[1] = (
|
|
315
|
+
files[1] = (ddog_prof_Exporter_File) {
|
|
315
316
|
.name = char_slice_from_ruby_string(code_provenance_file_name),
|
|
316
317
|
.file = byte_slice_from_ruby_string(code_provenance_data)
|
|
317
318
|
};
|
|
318
319
|
}
|
|
319
320
|
|
|
320
|
-
|
|
321
|
+
ddog_Vec_Tag *null_additional_tags = NULL;
|
|
321
322
|
|
|
322
|
-
|
|
323
|
+
ddog_prof_Exporter_NewResult exporter_result = create_exporter(exporter_configuration, tags_as_array);
|
|
323
324
|
// Note: Do not add anything that can raise exceptions after this line, as otherwise the exporter memory will leak
|
|
324
325
|
|
|
325
326
|
VALUE failure_tuple = handle_exporter_failure(exporter_result);
|
|
@@ -331,7 +332,7 @@ static VALUE _native_do_export(
|
|
|
331
332
|
static void *call_exporter_without_gvl(void *call_args) {
|
|
332
333
|
struct call_exporter_without_gvl_arguments *args = (struct call_exporter_without_gvl_arguments*) call_args;
|
|
333
334
|
|
|
334
|
-
args->result =
|
|
335
|
+
args->result = ddog_prof_Exporter_send(args->exporter, args->request, args->cancel_token);
|
|
335
336
|
args->send_ran = true;
|
|
336
337
|
|
|
337
338
|
return NULL; // Unused
|
|
@@ -9,6 +9,10 @@ inline static ddog_CharSlice char_slice_from_ruby_string(VALUE string) {
|
|
|
9
9
|
return char_slice;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
inline static VALUE ruby_string_from_vec_u8(
|
|
12
|
+
inline static VALUE ruby_string_from_vec_u8(ddog_Vec_U8 string) {
|
|
13
|
+
return rb_str_new((char *) string.ptr, string.len);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
inline static VALUE ruby_string_from_prof_vec_u8(ddog_prof_Vec_U8 string) {
|
|
13
17
|
return rb_str_new((char *) string.ptr, string.len);
|
|
14
18
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# typed: ignore
|
|
4
4
|
|
|
5
|
-
require '
|
|
5
|
+
require 'rubygems'
|
|
6
6
|
require 'pathname'
|
|
7
7
|
|
|
8
8
|
module Datadog
|
|
@@ -17,6 +17,8 @@ module Datadog
|
|
|
17
17
|
# Older Rubies don't have the MJIT header, used by the JIT compiler, so we need to use a different approach
|
|
18
18
|
CAN_USE_MJIT_HEADER = RUBY_VERSION >= '2.6'
|
|
19
19
|
|
|
20
|
+
LIBDATADOG_VERSION = '~> 1.0.1.1.0'
|
|
21
|
+
|
|
20
22
|
def self.fail_install_if_missing_extension?
|
|
21
23
|
ENV[ENV_FAIL_INSTALL_IF_MISSING_EXTENSION].to_s.strip.downcase == 'true'
|
|
22
24
|
end
|
|
@@ -88,6 +90,7 @@ module Datadog
|
|
|
88
90
|
not_on_amd64_or_arm64? ||
|
|
89
91
|
on_ruby_2_1? ||
|
|
90
92
|
expected_to_use_mjit_but_mjit_is_disabled? ||
|
|
93
|
+
libdatadog_not_available? ||
|
|
91
94
|
libdatadog_not_usable?
|
|
92
95
|
end
|
|
93
96
|
|
|
@@ -142,11 +145,6 @@ module Datadog
|
|
|
142
145
|
'<https://dtdg.co/ruby-profiler-troubleshooting>.'
|
|
143
146
|
].freeze
|
|
144
147
|
|
|
145
|
-
REPORT_ISSUE = [
|
|
146
|
-
'If you needed to use this, please tell us why on',
|
|
147
|
-
'<https://github.com/DataDog/dd-trace-rb/issues/new> so we can fix it :)',
|
|
148
|
-
].freeze
|
|
149
|
-
|
|
150
148
|
GET_IN_TOUCH = [
|
|
151
149
|
"Get in touch with us if you're interested in profiling your app!"
|
|
152
150
|
].freeze
|
|
@@ -172,18 +170,31 @@ module Datadog
|
|
|
172
170
|
PKG_CONFIG_IS_MISSING = explain_issue(
|
|
173
171
|
#+-----------------------------------------------------------------------------+
|
|
174
172
|
'the `pkg-config` system tool is missing.',
|
|
175
|
-
'This issue can usually be fixed by installing:',
|
|
176
|
-
'
|
|
177
|
-
'
|
|
178
|
-
'
|
|
173
|
+
'This issue can usually be fixed by installing one of the following:',
|
|
174
|
+
'the `pkg-config` package on Homebrew and Debian/Ubuntu-based Linux;',
|
|
175
|
+
'the `pkgconf` package on Arch and Alpine-based Linux;',
|
|
176
|
+
'the `pkgconf-pkg-config` package on Fedora/Red Hat-based Linux.',
|
|
177
|
+
suggested: CONTACT_SUPPORT,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Validation for this check is done in extconf.rb because it relies on mkmf
|
|
181
|
+
COMPILER_ATOMIC_MISSING = explain_issue(
|
|
182
|
+
'your C compiler is missing support for the <stdatomic.h> header.',
|
|
183
|
+
'This issue can usually be fixed by upgrading to a later version of your',
|
|
184
|
+
'operating system image or compiler.',
|
|
179
185
|
suggested: CONTACT_SUPPORT,
|
|
180
186
|
)
|
|
181
187
|
|
|
182
188
|
private_class_method def self.disabled_via_env?
|
|
189
|
+
report_disabled = [
|
|
190
|
+
'If you needed to use this, please tell us why on',
|
|
191
|
+
'<https://github.com/DataDog/dd-trace-rb/issues/new> so we can fix it :)',
|
|
192
|
+
].freeze
|
|
193
|
+
|
|
183
194
|
disabled_via_env = explain_issue(
|
|
184
195
|
'the `DD_PROFILING_NO_EXTENSION` environment variable is/was set to',
|
|
185
196
|
'`true` during installation.',
|
|
186
|
-
suggested:
|
|
197
|
+
suggested: report_disabled,
|
|
187
198
|
)
|
|
188
199
|
|
|
189
200
|
return unless ENV[ENV_NO_EXTENSION].to_s.strip.downcase == 'true'
|
|
@@ -246,7 +257,7 @@ module Datadog
|
|
|
246
257
|
suggested: GET_IN_TOUCH,
|
|
247
258
|
)
|
|
248
259
|
|
|
249
|
-
architecture_not_supported unless RUBY_PLATFORM.start_with?('x86_64', 'aarch64')
|
|
260
|
+
architecture_not_supported unless RUBY_PLATFORM.start_with?('x86_64', 'aarch64', 'arm64')
|
|
250
261
|
end
|
|
251
262
|
|
|
252
263
|
private_class_method def self.on_ruby_2_1?
|
|
@@ -271,6 +282,25 @@ module Datadog
|
|
|
271
282
|
ruby_without_mjit if CAN_USE_MJIT_HEADER && RbConfig::CONFIG['MJIT_SUPPORT'] != 'yes'
|
|
272
283
|
end
|
|
273
284
|
|
|
285
|
+
private_class_method def self.libdatadog_not_available?
|
|
286
|
+
begin
|
|
287
|
+
gem 'libdatadog', LIBDATADOG_VERSION
|
|
288
|
+
require 'libdatadog'
|
|
289
|
+
nil
|
|
290
|
+
# rubocop:disable Lint/RescueException
|
|
291
|
+
rescue Exception => e
|
|
292
|
+
explain_issue(
|
|
293
|
+
'there was an exception during loading of the `libdatadog` gem:',
|
|
294
|
+
e.class.name,
|
|
295
|
+
*e.message.split("\n"),
|
|
296
|
+
*Array(e.backtrace),
|
|
297
|
+
'.',
|
|
298
|
+
suggested: CONTACT_SUPPORT,
|
|
299
|
+
)
|
|
300
|
+
end
|
|
301
|
+
# rubocop:enable Lint/RescueException
|
|
302
|
+
end
|
|
303
|
+
|
|
274
304
|
private_class_method def self.libdatadog_not_usable?
|
|
275
305
|
no_binaries_for_current_platform = explain_issue(
|
|
276
306
|
'the `libdatadog` gem installed on your system is missing binaries for your',
|
|
@@ -50,6 +50,100 @@ rb_nativethread_id_t pthread_id_for(VALUE thread) {
|
|
|
50
50
|
#endif
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
// Queries if the current thread is the owner of the global VM lock.
|
|
54
|
+
//
|
|
55
|
+
// @ivoanjo: Ruby has a similarly-named `ruby_thread_has_gvl_p` but that API is insufficient for our needs because it can
|
|
56
|
+
// still return `true` even when a thread DOES NOT HAVE the global VM lock.
|
|
57
|
+
// In particular, looking at the implementation, that API assumes that if a thread is not in a "blocking region" then it
|
|
58
|
+
// will have the GVL which is probably true for the situations that API was designed to be called from BUT this assumption
|
|
59
|
+
// does not hold true when calling `ruby_thread_has_gvl_p` from a signal handler. (Because the thread may have lost the
|
|
60
|
+
// GVL due to a scheduler decision, not because it decided to block.)
|
|
61
|
+
// I have also submitted https://bugs.ruby-lang.org/issues/19172 to discuss this with upstream Ruby developers.
|
|
62
|
+
//
|
|
63
|
+
// Thus we need our own gvl-checking method which actually looks at the gvl structure to determine if it is the owner.
|
|
64
|
+
bool is_current_thread_holding_the_gvl(void) {
|
|
65
|
+
current_gvl_owner owner = gvl_owner();
|
|
66
|
+
return owner.valid && pthread_equal(pthread_self(), owner.owner);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#ifndef NO_GVL_OWNER // Ruby < 2.6 doesn't have the owner/running field
|
|
70
|
+
// NOTE: Reading the owner in this is a racy read, because we're not grabbing the lock that Ruby uses to protect it.
|
|
71
|
+
//
|
|
72
|
+
// While we could potentially grab this lock, I (@ivoanjo) think we actually don't need it because:
|
|
73
|
+
// * In the case where a thread owns the GVL and calls `gvl_owner`, it will always see the correct value. That's
|
|
74
|
+
// because every thread sets itself as the owner when it grabs the GVL and unsets itself at the end.
|
|
75
|
+
// That means that `is_current_thread_holding_the_gvl` is always accurate.
|
|
76
|
+
// * In a case where we observe a different thread, then this may change by the time we do something with this value
|
|
77
|
+
// anyway. So unless we want to prevent the Ruby scheduler from switching threads, we need to deal with races here.
|
|
78
|
+
current_gvl_owner gvl_owner(void) {
|
|
79
|
+
const rb_thread_t *current_owner =
|
|
80
|
+
#ifndef NO_RB_THREAD_SCHED // Introduced in Ruby 3.2 as a replacement for struct rb_global_vm_lock_struct
|
|
81
|
+
GET_RACTOR()->threads.sched.running;
|
|
82
|
+
#elif HAVE_RUBY_RACTOR_H
|
|
83
|
+
GET_RACTOR()->threads.gvl.owner;
|
|
84
|
+
#else
|
|
85
|
+
GET_VM()->gvl.owner;
|
|
86
|
+
#endif
|
|
87
|
+
|
|
88
|
+
if (current_owner == NULL) return (current_gvl_owner) {.valid = false};
|
|
89
|
+
|
|
90
|
+
return (current_gvl_owner) {
|
|
91
|
+
.valid = true,
|
|
92
|
+
.owner =
|
|
93
|
+
#ifndef NO_RB_NATIVE_THREAD
|
|
94
|
+
current_owner->nt->thread_id
|
|
95
|
+
#else
|
|
96
|
+
current_owner->thread_id
|
|
97
|
+
#endif
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
#else
|
|
101
|
+
current_gvl_owner gvl_owner(void) {
|
|
102
|
+
rb_vm_t *vm =
|
|
103
|
+
#ifndef NO_GET_VM
|
|
104
|
+
GET_VM();
|
|
105
|
+
#else
|
|
106
|
+
thread_struct_from_object(rb_thread_current())->vm;
|
|
107
|
+
#endif
|
|
108
|
+
|
|
109
|
+
// BIG Issue: Ruby < 2.6 did not have the owner field. The really nice thing about the owner field is that it's
|
|
110
|
+
// "atomic" -- when a thread sets it, it "declares" two things in a single step
|
|
111
|
+
// * Declaration 1: Someone has the GVL
|
|
112
|
+
// * Declaration 2: That someone is the specific thread
|
|
113
|
+
//
|
|
114
|
+
// Observation 1: On older versions of Ruby, this ownership concept is actually split. Specifically, `gvl.acquired`
|
|
115
|
+
// is a boolean that represents declaration 1 above, and `vm->running_thread` (or `ruby_current_thread`/
|
|
116
|
+
// `ruby_current_execution_context_ptr`) represents declaration 2.
|
|
117
|
+
//
|
|
118
|
+
// Observation 2: In addition, when a thread releases the GVL, it only sets `gvl.acquired` back to 0 **BUT CRUCIALLY
|
|
119
|
+
// DOES NOT CHANGE THE OTHER global variables**.
|
|
120
|
+
//
|
|
121
|
+
// Observation 1+2 above lead to the following possible race:
|
|
122
|
+
// * Thread A grabs the GVL (`gvl.acquired == 1`)
|
|
123
|
+
// * Thread A sets `running_thread` (`gvl.acquired == 1` + `running_thread == Thread A`)
|
|
124
|
+
// * Thread A releases the GVL (`gvl.acquired == 0` + `running_thread == Thread A`)
|
|
125
|
+
// * Thread B grabs the GVL (`gvl.acquired == 1` + `running_thread == Thread A`)
|
|
126
|
+
// * Thread A calls gvl_owner. Due to the current state (`gvl.acquired == 1` + `running_thread == Thread A`), this
|
|
127
|
+
// function returns an incorrect result.
|
|
128
|
+
// * Thread B finally sets `running_thread` (`gvl.acquired == 1` + `running_thread == Thread B`)
|
|
129
|
+
//
|
|
130
|
+
// This is especially problematic because we use `gvl_owner` to implement `is_current_thread_holding_the_gvl` which
|
|
131
|
+
// is called in a signal handler to decide "is it safe for me to call `rb_postponed_job_register_one` or not".
|
|
132
|
+
// (See constraints in `collectors_cpu_and_wall_time_worker.c` comments for why).
|
|
133
|
+
//
|
|
134
|
+
// Thus an incorrect `is_current_thread_holding_the_gvl` result may lead to issues inside `rb_postponed_job_register_one`.
|
|
135
|
+
//
|
|
136
|
+
// For this reason we currently do not enable the new Ruby profiler on Ruby 2.5 and below by default, and we print a
|
|
137
|
+
// warning when customers force-enable it.
|
|
138
|
+
bool gvl_acquired = vm->gvl.acquired != 0;
|
|
139
|
+
rb_thread_t *current_owner = vm->running_thread;
|
|
140
|
+
|
|
141
|
+
if (!gvl_acquired || current_owner == NULL) return (current_gvl_owner) {.valid = false};
|
|
142
|
+
|
|
143
|
+
return (current_gvl_owner) {.valid = true, .owner = current_owner->thread_id};
|
|
144
|
+
}
|
|
145
|
+
#endif // NO_GVL_OWNER
|
|
146
|
+
|
|
53
147
|
// Taken from upstream vm_core.h at commit d9cf0388599a3234b9f3c06ddd006cd59a58ab8b (November 2022, Ruby 3.2 trunk)
|
|
54
148
|
// Copyright (C) 2004-2007 Koichi Sasada
|
|
55
149
|
// to support tid_for (see below)
|
|
@@ -128,7 +222,12 @@ VALUE ddtrace_thread_list(void) {
|
|
|
128
222
|
rb_ractor_t *current_ractor = GET_RACTOR();
|
|
129
223
|
ccan_list_for_each(¤t_ractor->threads.set, thread, lt_node) {
|
|
130
224
|
#else
|
|
131
|
-
rb_vm_t *vm =
|
|
225
|
+
rb_vm_t *vm =
|
|
226
|
+
#ifndef NO_GET_VM
|
|
227
|
+
GET_VM();
|
|
228
|
+
#else
|
|
229
|
+
thread_struct_from_object(rb_thread_current())->vm;
|
|
230
|
+
#endif
|
|
132
231
|
list_for_each(&vm->living_threads, thread, vmlt_node) {
|
|
133
232
|
#endif
|
|
134
233
|
switch (thread->status) {
|
|
@@ -284,9 +383,6 @@ calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
|
|
|
284
383
|
// * Add `end_cfp == NULL` and `end_cfp <= cfp` safety checks. These are used in a bunch of places in
|
|
285
384
|
// `vm_backtrace.c` (`backtrace_each`, `backtrace_size`, `rb_ec_partial_backtrace_object`) but are conspicuously
|
|
286
385
|
// absent from `rb_profile_frames`. Oversight?
|
|
287
|
-
// * Distinguish between `end_cfp == NULL` (dead thread or some other error, returns 0) and `end_cfp <= cfp`
|
|
288
|
-
// (alive thread which may just be executing native code and has not pushed anything on the Ruby stack, returns
|
|
289
|
-
// PLACEHOLDER_STACK_IN_NATIVE_CODE). See comments on `record_placeholder_stack_in_native_code` for more details.
|
|
290
386
|
// * Skip frames where `cfp->iseq && !cfp->pc`. These seem to be internal and are skipped by `backtrace_each` in
|
|
291
387
|
// `vm_backtrace.c`.
|
|
292
388
|
// * Check thread status and do not sample if thread has been killed.
|
|
@@ -294,6 +390,7 @@ calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
|
|
|
294
390
|
// for iseqs created from calls to `eval` and `instance_eval`. This makes it so that `rb_profile_frame_path` on
|
|
295
391
|
// the `VALUE` returned by rb_profile_frames returns `(eval)` instead of the path of the file where the `eval`
|
|
296
392
|
// was called from.
|
|
393
|
+
// * Imported fix from https://github.com/ruby/ruby/pull/7116 to avoid sampling threads that are still being created
|
|
297
394
|
//
|
|
298
395
|
// **IMPORTANT: WHEN CHANGING THIS FUNCTION, CONSIDER IF THE SAME CHANGE ALSO NEEDS TO BE MADE TO THE VARIANT FOR
|
|
299
396
|
// RUBY 2.2 AND BELOW WHICH IS ALSO PRESENT ON THIS FILE**
|
|
@@ -340,13 +437,17 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, VALUE *buff, i
|
|
|
340
437
|
const rb_control_frame_t *cfp = ec->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
|
|
341
438
|
const rb_callable_method_entry_t *cme;
|
|
342
439
|
|
|
343
|
-
//
|
|
344
|
-
//
|
|
345
|
-
if (
|
|
440
|
+
// This should not happen for ddtrace (it can only happen when a thread is still being created), but I've imported
|
|
441
|
+
// it from https://github.com/ruby/ruby/pull/7116 in a "just in case" kind of mindset.
|
|
442
|
+
if (cfp == NULL) return 0;
|
|
346
443
|
|
|
347
444
|
// Avoid sampling dead threads
|
|
348
445
|
if (th->status == THREAD_KILLED) return 0;
|
|
349
446
|
|
|
447
|
+
// `vm_backtrace.c` includes this check in several methods. This happens on newly-created threads, and may
|
|
448
|
+
// also (not entirely sure) happen on dead threads
|
|
449
|
+
if (end_cfp == NULL) return PLACEHOLDER_STACK_IN_NATIVE_CODE;
|
|
450
|
+
|
|
350
451
|
// Fix: Skip dummy frame that shows up in main thread.
|
|
351
452
|
//
|
|
352
453
|
// According to a comment in `backtrace_each` (`vm_backtrace.c`), there's two dummy frames that we should ignore
|
|
@@ -650,10 +751,8 @@ calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
|
|
|
650
751
|
// * Add `end_cfp == NULL` and `end_cfp <= cfp` safety checks. These are used in a bunch of places in
|
|
651
752
|
// `vm_backtrace.c` (`backtrace_each`, `backtrace_size`, `rb_ec_partial_backtrace_object`) but are conspicuously
|
|
652
753
|
// absent from `rb_profile_frames`. Oversight?
|
|
653
|
-
// * Distinguish between `end_cfp == NULL` (dead thread or some other error, returns 0) and `end_cfp <= cfp`
|
|
654
|
-
// (alive thread which may just be executing native code and has not pushed anything on the Ruby stack, returns
|
|
655
|
-
// PLACEHOLDER_STACK_IN_NATIVE_CODE). See comments on `record_placeholder_stack_in_native_code` for more details.
|
|
656
754
|
// * Check thread status and do not sample if thread has been killed.
|
|
755
|
+
// * Imported fix from https://github.com/ruby/ruby/pull/7116 to avoid sampling threads that are still being created
|
|
657
756
|
//
|
|
658
757
|
// The `rb_profile_frames` function changed quite a bit between Ruby 2.2 and 2.3. Since the change was quite complex
|
|
659
758
|
// I opted not to try to extend support to Ruby 2.2 using the same custom function, and instead I started
|
|
@@ -667,13 +766,17 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, VALUE *buff, i
|
|
|
667
766
|
rb_thread_t *th = thread_struct_from_object(thread);
|
|
668
767
|
rb_control_frame_t *cfp = th->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(th);
|
|
669
768
|
|
|
670
|
-
//
|
|
671
|
-
//
|
|
672
|
-
if (
|
|
769
|
+
// This should not happen for ddtrace (it can only happen when a thread is still being created), but I've imported
|
|
770
|
+
// it from https://github.com/ruby/ruby/pull/7116 in a "just in case" kind of mindset.
|
|
771
|
+
if (cfp == NULL) return 0;
|
|
673
772
|
|
|
674
773
|
// Avoid sampling dead threads
|
|
675
774
|
if (th->status == THREAD_KILLED) return 0;
|
|
676
775
|
|
|
776
|
+
// `vm_backtrace.c` includes this check in several methods. This happens on newly-created threads, and may
|
|
777
|
+
// also (not entirely sure) happen on dead threads
|
|
778
|
+
if (end_cfp == NULL) return PLACEHOLDER_STACK_IN_NATIVE_CODE;
|
|
779
|
+
|
|
677
780
|
// Fix: Skip dummy frame that shows up in main thread.
|
|
678
781
|
//
|
|
679
782
|
// According to a comment in `backtrace_each` (`vm_backtrace.c`), there's two dummy frames that we should ignore
|
|
@@ -717,15 +820,6 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, VALUE *buff, i
|
|
|
717
820
|
|
|
718
821
|
#endif // USE_LEGACY_RB_PROFILE_FRAMES
|
|
719
822
|
|
|
720
|
-
#ifdef NO_THREAD_HAS_GVL
|
|
721
|
-
int ruby_thread_has_gvl_p(void) {
|
|
722
|
-
// TODO: The CpuAndWallTimeWorker needs this function, but Ruby 2.2 doesn't expose it... For now this placeholder
|
|
723
|
-
// will enable the profiling native extension to continue to compile on Ruby 2.2, but the CpuAndWallTimeWorker will
|
|
724
|
-
// not work properly on 2.2. Will be addressed later.
|
|
725
|
-
return 0;
|
|
726
|
-
}
|
|
727
|
-
#endif // NO_THREAD_HAS_GVL
|
|
728
|
-
|
|
729
823
|
#ifndef NO_RACTORS
|
|
730
824
|
// This API and definition are exported as a public symbol by the VM BUT the function header is not defined in any public header, so we
|
|
731
825
|
// repeat it here to be able to use in our code.
|
|
@@ -7,11 +7,20 @@
|
|
|
7
7
|
// without also dragging the incompatible includes
|
|
8
8
|
#ifndef PRIVATE_VM_API_ACCESS_SKIP_RUBY_INCLUDES
|
|
9
9
|
#include <ruby/thread_native.h>
|
|
10
|
+
#include <ruby/vm.h>
|
|
10
11
|
#endif
|
|
11
12
|
|
|
12
13
|
#include "extconf.h"
|
|
13
14
|
|
|
15
|
+
// Contains the current gvl owner, and a flag to indicate if it is valid
|
|
16
|
+
typedef struct {
|
|
17
|
+
bool valid;
|
|
18
|
+
rb_nativethread_id_t owner;
|
|
19
|
+
} current_gvl_owner;
|
|
20
|
+
|
|
14
21
|
rb_nativethread_id_t pthread_id_for(VALUE thread);
|
|
22
|
+
bool is_current_thread_holding_the_gvl(void);
|
|
23
|
+
current_gvl_owner gvl_owner(void);
|
|
15
24
|
uint64_t native_thread_id_for(VALUE thread);
|
|
16
25
|
ptrdiff_t stack_depth_for(VALUE thread);
|
|
17
26
|
VALUE ddtrace_thread_list(void);
|