ddtrace 1.5.2 → 1.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +56 -2
- data/ext/ddtrace_profiling_loader/ddtrace_profiling_loader.c +9 -2
- data/ext/ddtrace_profiling_loader/extconf.rb +17 -0
- data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +38 -2
- data/ext/ddtrace_profiling_native_extension/clock_id.h +1 -0
- data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +1 -0
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.c +517 -42
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.h +3 -0
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +208 -30
- data/ext/ddtrace_profiling_native_extension/collectors_stack.c +156 -46
- data/ext/ddtrace_profiling_native_extension/collectors_stack.h +11 -2
- data/ext/ddtrace_profiling_native_extension/extconf.rb +11 -1
- data/ext/ddtrace_profiling_native_extension/http_transport.c +83 -64
- data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +4 -4
- data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +3 -2
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +59 -0
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +3 -0
- data/ext/ddtrace_profiling_native_extension/profiling.c +10 -0
- data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +0 -1
- data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +4 -2
- data/ext/ddtrace_profiling_native_extension/stack_recorder.c +45 -29
- data/ext/ddtrace_profiling_native_extension/stack_recorder.h +7 -7
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +4 -0
- data/lib/datadog/appsec/event.rb +6 -0
- data/lib/datadog/core/configuration/components.rb +20 -14
- data/lib/datadog/core/configuration/settings.rb +42 -4
- data/lib/datadog/core/diagnostics/environment_logger.rb +5 -1
- data/lib/datadog/core/utils/compression.rb +5 -1
- data/lib/datadog/core.rb +0 -54
- data/lib/datadog/profiling/collectors/cpu_and_wall_time.rb +12 -2
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +5 -3
- data/lib/datadog/profiling/exporter.rb +2 -4
- data/lib/datadog/profiling/http_transport.rb +1 -1
- data/lib/datadog/tracing/configuration/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +2 -0
- data/lib/datadog/tracing/contrib/dalli/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +4 -0
- data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +3 -0
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +2 -0
- data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +2 -0
- data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -0
- data/lib/datadog/tracing/contrib/ext.rb +6 -0
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +2 -0
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +5 -0
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +7 -1
- data/lib/datadog/tracing/contrib/grpc/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +47 -0
- data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +22 -0
- data/lib/datadog/tracing/contrib/hanami/ext.rb +24 -0
- data/lib/datadog/tracing/contrib/hanami/integration.rb +44 -0
- data/lib/datadog/tracing/contrib/hanami/patcher.rb +33 -0
- data/lib/datadog/tracing/contrib/hanami/plugin.rb +23 -0
- data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +41 -0
- data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +44 -0
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +2 -0
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +2 -0
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +2 -0
- data/lib/datadog/tracing/contrib/mongodb/ext.rb +7 -0
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +4 -0
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +12 -0
- data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +16 -0
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +12 -0
- data/lib/datadog/tracing/contrib/pg/ext.rb +2 -1
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +34 -18
- data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +43 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +32 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +28 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +49 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +11 -5
- data/lib/datadog/tracing/contrib/redis/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/redis/instrumentation.rb +4 -2
- data/lib/datadog/tracing/contrib/redis/integration.rb +2 -1
- data/lib/datadog/tracing/contrib/redis/patcher.rb +40 -0
- data/lib/datadog/tracing/contrib/redis/tags.rb +5 -0
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +2 -0
- data/lib/datadog/tracing/contrib/sinatra/env.rb +12 -23
- data/lib/datadog/tracing/contrib/sinatra/ext.rb +7 -3
- data/lib/datadog/tracing/contrib/sinatra/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +8 -80
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +14 -9
- data/lib/datadog/tracing/contrib.rb +1 -0
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +84 -0
- data/lib/datadog/tracing/distributed/headers/datadog.rb +122 -30
- data/lib/datadog/tracing/distributed/headers/ext.rb +2 -0
- data/lib/datadog/tracing/flush.rb +1 -1
- data/lib/datadog/tracing/metadata/ext.rb +8 -0
- data/lib/datadog/tracing/propagation/http.rb +9 -1
- data/lib/datadog/tracing/sampling/ext.rb +31 -0
- data/lib/datadog/tracing/sampling/priority_sampler.rb +46 -4
- data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +8 -9
- data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +29 -5
- data/lib/datadog/tracing/sampling/rate_sampler.rb +10 -3
- data/lib/datadog/tracing/sampling/rule_sampler.rb +4 -3
- data/lib/datadog/tracing/sampling/span/ext.rb +0 -4
- data/lib/datadog/tracing/sampling/span/rule.rb +1 -1
- data/lib/datadog/tracing/sampling/span/sampler.rb +14 -3
- data/lib/datadog/tracing/trace_digest.rb +3 -0
- data/lib/datadog/tracing/trace_operation.rb +10 -0
- data/lib/datadog/tracing/trace_segment.rb +6 -0
- data/lib/datadog/tracing/tracer.rb +3 -1
- data/lib/datadog/tracing/writer.rb +7 -0
- data/lib/ddtrace/transport/trace_formatter.rb +7 -0
- data/lib/ddtrace/transport/traces.rb +1 -1
- data/lib/ddtrace/version.rb +2 -2
- metadata +18 -14
- data/lib/datadog/profiling/old_ext.rb +0 -42
- data/lib/datadog/profiling/transport/http/api/endpoint.rb +0 -85
- data/lib/datadog/profiling/transport/http/api/instance.rb +0 -38
- data/lib/datadog/profiling/transport/http/api/spec.rb +0 -42
- data/lib/datadog/profiling/transport/http/api.rb +0 -45
- data/lib/datadog/profiling/transport/http/builder.rb +0 -30
- data/lib/datadog/profiling/transport/http/client.rb +0 -37
- data/lib/datadog/profiling/transport/http/response.rb +0 -21
- data/lib/datadog/profiling/transport/http.rb +0 -118
@@ -1,6 +1,6 @@
|
|
1
1
|
#include <ruby.h>
|
2
2
|
#include <ruby/thread.h>
|
3
|
-
#include <
|
3
|
+
#include <datadog/profiling.h>
|
4
4
|
#include "helpers.h"
|
5
5
|
#include "libdatadog_helpers.h"
|
6
6
|
#include "ruby_helpers.h"
|
@@ -17,22 +17,23 @@ static ID agent_id; // id of :agent in Ruby
|
|
17
17
|
static ID log_failure_to_process_tag_id; // id of :log_failure_to_process_tag in Ruby
|
18
18
|
|
19
19
|
static VALUE http_transport_class = Qnil;
|
20
|
+
static VALUE library_version_string = Qnil;
|
20
21
|
|
21
22
|
struct call_exporter_without_gvl_arguments {
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
ddog_ProfileExporter *exporter;
|
24
|
+
ddog_Request *request;
|
25
|
+
ddog_CancellationToken *cancel_token;
|
26
|
+
ddog_SendResult result;
|
26
27
|
bool send_ran;
|
27
28
|
};
|
28
29
|
|
29
|
-
inline static
|
30
|
+
inline static ddog_ByteSlice byte_slice_from_ruby_string(VALUE string);
|
30
31
|
static VALUE _native_validate_exporter(VALUE self, VALUE exporter_configuration);
|
31
|
-
static
|
32
|
-
static VALUE handle_exporter_failure(
|
33
|
-
static
|
34
|
-
static
|
35
|
-
static void safely_log_failure_to_process_tag(
|
32
|
+
static ddog_NewProfileExporterResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array);
|
33
|
+
static VALUE handle_exporter_failure(ddog_NewProfileExporterResult exporter_result);
|
34
|
+
static ddog_Endpoint endpoint_from(VALUE exporter_configuration);
|
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);
|
36
37
|
static VALUE _native_do_export(
|
37
38
|
VALUE self,
|
38
39
|
VALUE exporter_configuration,
|
@@ -49,6 +50,7 @@ static VALUE _native_do_export(
|
|
49
50
|
);
|
50
51
|
static void *call_exporter_without_gvl(void *call_args);
|
51
52
|
static void interrupt_exporter_call(void *cancel_token);
|
53
|
+
static VALUE ddtrace_version(void);
|
52
54
|
|
53
55
|
void http_transport_init(VALUE profiling_module) {
|
54
56
|
http_transport_class = rb_define_class_under(profiling_module, "HttpTransport", rb_cObject);
|
@@ -61,57 +63,64 @@ void http_transport_init(VALUE profiling_module) {
|
|
61
63
|
agentless_id = rb_intern_const("agentless");
|
62
64
|
agent_id = rb_intern_const("agent");
|
63
65
|
log_failure_to_process_tag_id = rb_intern_const("log_failure_to_process_tag");
|
66
|
+
|
67
|
+
library_version_string = ddtrace_version();
|
68
|
+
rb_global_variable(&library_version_string);
|
64
69
|
}
|
65
70
|
|
66
|
-
inline static
|
71
|
+
inline static ddog_ByteSlice byte_slice_from_ruby_string(VALUE string) {
|
67
72
|
ENFORCE_TYPE(string, T_STRING);
|
68
|
-
|
73
|
+
ddog_ByteSlice byte_slice = {.ptr = (uint8_t *) StringValuePtr(string), .len = RSTRING_LEN(string)};
|
69
74
|
return byte_slice;
|
70
75
|
}
|
71
76
|
|
72
77
|
static VALUE _native_validate_exporter(DDTRACE_UNUSED VALUE _self, VALUE exporter_configuration) {
|
73
78
|
ENFORCE_TYPE(exporter_configuration, T_ARRAY);
|
74
|
-
|
79
|
+
ddog_NewProfileExporterResult exporter_result = create_exporter(exporter_configuration, rb_ary_new());
|
75
80
|
|
76
81
|
VALUE failure_tuple = handle_exporter_failure(exporter_result);
|
77
82
|
if (!NIL_P(failure_tuple)) return failure_tuple;
|
78
83
|
|
79
84
|
// We don't actually need the exporter for now -- we just wanted to validate that we could create it with the
|
80
85
|
// settings we were given
|
81
|
-
|
86
|
+
ddog_NewProfileExporterResult_drop(exporter_result);
|
82
87
|
|
83
88
|
return rb_ary_new_from_args(2, ok_symbol, Qnil);
|
84
89
|
}
|
85
90
|
|
86
|
-
static
|
91
|
+
static ddog_NewProfileExporterResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array) {
|
87
92
|
ENFORCE_TYPE(exporter_configuration, T_ARRAY);
|
88
93
|
ENFORCE_TYPE(tags_as_array, T_ARRAY);
|
89
94
|
|
90
|
-
// 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
|
91
96
|
// to be leaked.
|
92
|
-
|
97
|
+
ddog_Endpoint endpoint = endpoint_from(exporter_configuration);
|
98
|
+
|
99
|
+
ddog_Vec_tag tags = convert_tags(tags_as_array);
|
93
100
|
|
94
|
-
|
101
|
+
ddog_CharSlice library_name = DDOG_CHARSLICE_C("dd-trace-rb");
|
102
|
+
ddog_CharSlice library_version = char_slice_from_ruby_string(library_version_string);
|
103
|
+
ddog_CharSlice profiling_family = DDOG_CHARSLICE_C("ruby");
|
95
104
|
|
96
|
-
|
97
|
-
|
105
|
+
ddog_NewProfileExporterResult exporter_result =
|
106
|
+
ddog_ProfileExporter_new(library_name, library_version, profiling_family, &tags, endpoint);
|
98
107
|
|
99
|
-
|
108
|
+
ddog_Vec_tag_drop(tags);
|
100
109
|
|
101
110
|
return exporter_result;
|
102
111
|
}
|
103
112
|
|
104
|
-
static VALUE handle_exporter_failure(
|
105
|
-
if (exporter_result.tag ==
|
113
|
+
static VALUE handle_exporter_failure(ddog_NewProfileExporterResult exporter_result) {
|
114
|
+
if (exporter_result.tag == DDOG_NEW_PROFILE_EXPORTER_RESULT_OK) return Qnil;
|
106
115
|
|
107
116
|
VALUE err_details = ruby_string_from_vec_u8(exporter_result.err);
|
108
117
|
|
109
|
-
|
118
|
+
ddog_NewProfileExporterResult_drop(exporter_result);
|
110
119
|
|
111
120
|
return rb_ary_new_from_args(2, error_symbol, err_details);
|
112
121
|
}
|
113
122
|
|
114
|
-
static
|
123
|
+
static ddog_Endpoint endpoint_from(VALUE exporter_configuration) {
|
115
124
|
ENFORCE_TYPE(exporter_configuration, T_ARRAY);
|
116
125
|
|
117
126
|
ID working_mode = SYM2ID(rb_ary_entry(exporter_configuration, 0)); // SYM2ID verifies its input so we can do this safely
|
@@ -126,27 +135,27 @@ static ddprof_ffi_EndpointV3 endpoint_from(VALUE exporter_configuration) {
|
|
126
135
|
ENFORCE_TYPE(site, T_STRING);
|
127
136
|
ENFORCE_TYPE(api_key, T_STRING);
|
128
137
|
|
129
|
-
return
|
138
|
+
return ddog_Endpoint_agentless(char_slice_from_ruby_string(site), char_slice_from_ruby_string(api_key));
|
130
139
|
} else { // agent_id
|
131
140
|
VALUE base_url = rb_ary_entry(exporter_configuration, 1);
|
132
141
|
ENFORCE_TYPE(base_url, T_STRING);
|
133
142
|
|
134
|
-
return
|
143
|
+
return ddog_Endpoint_agent(char_slice_from_ruby_string(base_url));
|
135
144
|
}
|
136
145
|
}
|
137
146
|
|
138
147
|
__attribute__((warn_unused_result))
|
139
|
-
static
|
148
|
+
static ddog_Vec_tag convert_tags(VALUE tags_as_array) {
|
140
149
|
ENFORCE_TYPE(tags_as_array, T_ARRAY);
|
141
150
|
|
142
151
|
long tags_count = RARRAY_LEN(tags_as_array);
|
143
|
-
|
152
|
+
ddog_Vec_tag tags = ddog_Vec_tag_new();
|
144
153
|
|
145
154
|
for (long i = 0; i < tags_count; i++) {
|
146
155
|
VALUE name_value_pair = rb_ary_entry(tags_as_array, i);
|
147
156
|
|
148
157
|
if (!RB_TYPE_P(name_value_pair, T_ARRAY)) {
|
149
|
-
|
158
|
+
ddog_Vec_tag_drop(tags);
|
150
159
|
ENFORCE_TYPE(name_value_pair, T_ARRAY);
|
151
160
|
}
|
152
161
|
|
@@ -155,23 +164,23 @@ static ddprof_ffi_Vec_tag convert_tags(VALUE tags_as_array) {
|
|
155
164
|
VALUE tag_value = rb_ary_entry(name_value_pair, 1);
|
156
165
|
|
157
166
|
if (!(RB_TYPE_P(tag_name, T_STRING) && RB_TYPE_P(tag_value, T_STRING))) {
|
158
|
-
|
167
|
+
ddog_Vec_tag_drop(tags);
|
159
168
|
ENFORCE_TYPE(tag_name, T_STRING);
|
160
169
|
ENFORCE_TYPE(tag_value, T_STRING);
|
161
170
|
}
|
162
171
|
|
163
|
-
|
164
|
-
|
172
|
+
ddog_PushTagResult push_result =
|
173
|
+
ddog_Vec_tag_push(&tags, char_slice_from_ruby_string(tag_name), char_slice_from_ruby_string(tag_value));
|
165
174
|
|
166
|
-
if (push_result.tag ==
|
175
|
+
if (push_result.tag == DDOG_PUSH_TAG_RESULT_ERR) {
|
167
176
|
VALUE err_details = ruby_string_from_vec_u8(push_result.err);
|
168
|
-
|
177
|
+
ddog_PushTagResult_drop(push_result);
|
169
178
|
|
170
179
|
// libdatadog validates tags and may catch invalid tags that ddtrace didn't actually catch.
|
171
180
|
// We warn users about such tags, and then just ignore them.
|
172
181
|
safely_log_failure_to_process_tag(tags, err_details);
|
173
182
|
} else {
|
174
|
-
|
183
|
+
ddog_PushTagResult_drop(push_result);
|
175
184
|
}
|
176
185
|
}
|
177
186
|
|
@@ -184,12 +193,12 @@ static VALUE log_failure_to_process_tag(VALUE err_details) {
|
|
184
193
|
|
185
194
|
// Since we are calling into Ruby code, it may raise an exception. This method ensure that dynamically-allocated tags
|
186
195
|
// get cleaned before propagating the exception.
|
187
|
-
static void safely_log_failure_to_process_tag(
|
196
|
+
static void safely_log_failure_to_process_tag(ddog_Vec_tag tags, VALUE err_details) {
|
188
197
|
int exception_state;
|
189
198
|
rb_protect(log_failure_to_process_tag, err_details, &exception_state);
|
190
199
|
|
191
200
|
if (exception_state) { // An exception was raised
|
192
|
-
|
201
|
+
ddog_Vec_tag_drop(tags); // clean up
|
193
202
|
rb_jump_tag(exception_state); // "Re-raise" exception
|
194
203
|
}
|
195
204
|
}
|
@@ -197,17 +206,17 @@ static void safely_log_failure_to_process_tag(ddprof_ffi_Vec_tag tags, VALUE err
|
|
197
206
|
// Note: This function handles a bunch of libdatadog dynamically-allocated objects, so it MUST not use any Ruby APIs
|
198
207
|
// which can raise exceptions, otherwise the objects will be leaked.
|
199
208
|
static VALUE perform_export(
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
209
|
+
ddog_NewProfileExporterResult valid_exporter_result, // Must be called with a valid exporter result
|
210
|
+
ddog_Timespec start,
|
211
|
+
ddog_Timespec finish,
|
212
|
+
ddog_Slice_file slice_files,
|
213
|
+
ddog_Vec_tag *additional_tags,
|
205
214
|
uint64_t timeout_milliseconds
|
206
215
|
) {
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
216
|
+
ddog_ProfileExporter *exporter = valid_exporter_result.ok;
|
217
|
+
ddog_CancellationToken *cancel_token = ddog_CancellationToken_new();
|
218
|
+
ddog_Request *request =
|
219
|
+
ddog_ProfileExporter_build(exporter, start, finish, slice_files, additional_tags, timeout_milliseconds);
|
211
220
|
|
212
221
|
// We'll release the Global VM Lock while we're calling send, so that the Ruby VM can continue to work while this
|
213
222
|
// is pending
|
@@ -236,24 +245,24 @@ static VALUE perform_export(
|
|
236
245
|
}
|
237
246
|
|
238
247
|
// Cleanup exporter and token, no longer needed
|
239
|
-
|
240
|
-
|
248
|
+
ddog_CancellationToken_drop(cancel_token);
|
249
|
+
ddog_NewProfileExporterResult_drop(valid_exporter_result);
|
241
250
|
|
242
251
|
if (pending_exception) {
|
243
252
|
// If we got here send did not run, so we need to explicitly dispose of the request
|
244
|
-
|
253
|
+
ddog_Request_drop(request);
|
245
254
|
|
246
255
|
// Let Ruby propagate the exception. This will not return.
|
247
256
|
rb_jump_tag(pending_exception);
|
248
257
|
}
|
249
258
|
|
250
|
-
|
251
|
-
bool success = result.tag ==
|
259
|
+
ddog_SendResult result = args.result;
|
260
|
+
bool success = result.tag == DDOG_SEND_RESULT_HTTP_RESPONSE;
|
252
261
|
|
253
262
|
VALUE ruby_status = success ? ok_symbol : error_symbol;
|
254
263
|
VALUE ruby_result = success ? UINT2NUM(result.http_response.code) : ruby_string_from_vec_u8(result.err);
|
255
264
|
|
256
|
-
|
265
|
+
ddog_SendResult_drop(args.result);
|
257
266
|
// The request itself does not need to be freed as libdatadog takes care of it as part of sending.
|
258
267
|
|
259
268
|
return rb_ary_new_from_args(2, ruby_status, ruby_result);
|
@@ -288,29 +297,29 @@ static VALUE _native_do_export(
|
|
288
297
|
|
289
298
|
uint64_t timeout_milliseconds = NUM2ULONG(upload_timeout_milliseconds);
|
290
299
|
|
291
|
-
|
300
|
+
ddog_Timespec start =
|
292
301
|
{.seconds = NUM2LONG(start_timespec_seconds), .nanoseconds = NUM2UINT(start_timespec_nanoseconds)};
|
293
|
-
|
302
|
+
ddog_Timespec finish =
|
294
303
|
{.seconds = NUM2LONG(finish_timespec_seconds), .nanoseconds = NUM2UINT(finish_timespec_nanoseconds)};
|
295
304
|
|
296
305
|
int files_to_report = 1 + (have_code_provenance ? 1 : 0);
|
297
|
-
|
298
|
-
|
306
|
+
ddog_File files[files_to_report];
|
307
|
+
ddog_Slice_file slice_files = {.ptr = files, .len = files_to_report};
|
299
308
|
|
300
|
-
files[0] = (
|
309
|
+
files[0] = (ddog_File) {
|
301
310
|
.name = char_slice_from_ruby_string(pprof_file_name),
|
302
311
|
.file = byte_slice_from_ruby_string(pprof_data)
|
303
312
|
};
|
304
313
|
if (have_code_provenance) {
|
305
|
-
files[1] = (
|
314
|
+
files[1] = (ddog_File) {
|
306
315
|
.name = char_slice_from_ruby_string(code_provenance_file_name),
|
307
316
|
.file = byte_slice_from_ruby_string(code_provenance_data)
|
308
317
|
};
|
309
318
|
}
|
310
319
|
|
311
|
-
|
320
|
+
ddog_Vec_tag *null_additional_tags = NULL;
|
312
321
|
|
313
|
-
|
322
|
+
ddog_NewProfileExporterResult exporter_result = create_exporter(exporter_configuration, tags_as_array);
|
314
323
|
// Note: Do not add anything that can raise exceptions after this line, as otherwise the exporter memory will leak
|
315
324
|
|
316
325
|
VALUE failure_tuple = handle_exporter_failure(exporter_result);
|
@@ -322,7 +331,7 @@ static VALUE _native_do_export(
|
|
322
331
|
static void *call_exporter_without_gvl(void *call_args) {
|
323
332
|
struct call_exporter_without_gvl_arguments *args = (struct call_exporter_without_gvl_arguments*) call_args;
|
324
333
|
|
325
|
-
args->result =
|
334
|
+
args->result = ddog_ProfileExporter_send(args->exporter, args->request, args->cancel_token);
|
326
335
|
args->send_ran = true;
|
327
336
|
|
328
337
|
return NULL; // Unused
|
@@ -330,5 +339,15 @@ static void *call_exporter_without_gvl(void *call_args) {
|
|
330
339
|
|
331
340
|
// Called by Ruby when it wants to interrupt call_exporter_without_gvl above, e.g. when the app wants to exit cleanly
|
332
341
|
static void interrupt_exporter_call(void *cancel_token) {
|
333
|
-
|
342
|
+
ddog_CancellationToken_cancel((ddog_CancellationToken *) cancel_token);
|
343
|
+
}
|
344
|
+
|
345
|
+
static VALUE ddtrace_version(void) {
|
346
|
+
VALUE ddtrace_module = rb_const_get(rb_cObject, rb_intern("DDTrace"));
|
347
|
+
ENFORCE_TYPE(ddtrace_module, T_MODULE);
|
348
|
+
VALUE version_module = rb_const_get(ddtrace_module, rb_intern("VERSION"));
|
349
|
+
ENFORCE_TYPE(version_module, T_MODULE);
|
350
|
+
VALUE version_string = rb_const_get(version_module, rb_intern("STRING"));
|
351
|
+
ENFORCE_TYPE(version_string, T_STRING);
|
352
|
+
return version_string;
|
334
353
|
}
|
@@ -1,14 +1,14 @@
|
|
1
1
|
#pragma once
|
2
2
|
|
3
|
-
#include <
|
3
|
+
#include <datadog/profiling.h>
|
4
4
|
#include "ruby_helpers.h"
|
5
5
|
|
6
|
-
inline static
|
6
|
+
inline static ddog_CharSlice char_slice_from_ruby_string(VALUE string) {
|
7
7
|
ENFORCE_TYPE(string, T_STRING);
|
8
|
-
|
8
|
+
ddog_CharSlice char_slice = {.ptr = StringValuePtr(string), .len = RSTRING_LEN(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
13
|
return rb_str_new((char *) string.ptr, string.len);
|
14
14
|
}
|
@@ -31,8 +31,9 @@ module Datadog
|
|
31
31
|
# This runpath gets hardcoded at native library linking time. You can look at it using the `readelf` tool in
|
32
32
|
# Linux: e.g. `readelf -d ddtrace_profiling_native_extension.2.7.3_x86_64-linux.so`.
|
33
33
|
#
|
34
|
-
# In ddtrace
|
35
|
-
#
|
34
|
+
# In older versions of ddtrace, we only set as runpath an absolute path to libdatadog.
|
35
|
+
# (This gets set automatically by the call
|
36
|
+
# to `pkg_config('datadog_profiling_with_rpath')` in `extconf.rb`). This worked fine as long as libdatadog was **NOT**
|
36
37
|
# moved from the folder it was present at ddtrace installation/linking time.
|
37
38
|
#
|
38
39
|
# Unfortunately, environments such as Heroku and AWS Elastic Beanstalk move gems around in the filesystem after
|
@@ -49,6 +49,40 @@ rb_nativethread_id_t pthread_id_for(VALUE thread) {
|
|
49
49
|
#endif
|
50
50
|
}
|
51
51
|
|
52
|
+
// Taken from upstream vm_core.h at commit d9cf0388599a3234b9f3c06ddd006cd59a58ab8b (November 2022, Ruby 3.2 trunk)
|
53
|
+
// Copyright (C) 2004-2007 Koichi Sasada
|
54
|
+
// to support tid_for (see below)
|
55
|
+
// Modifications: None
|
56
|
+
#if defined(__linux__) || defined(__FreeBSD__)
|
57
|
+
# define RB_THREAD_T_HAS_NATIVE_ID
|
58
|
+
#endif
|
59
|
+
|
60
|
+
uint64_t native_thread_id_for(VALUE thread) {
|
61
|
+
// The tid is only available on Ruby >= 3.1 + Linux (and FreeBSD). It's the same as `gettid()` aka the task id as seen in /proc
|
62
|
+
#if !defined(NO_THREAD_TID) && defined(RB_THREAD_T_HAS_NATIVE_ID)
|
63
|
+
#ifndef NO_RB_NATIVE_THREAD
|
64
|
+
return thread_struct_from_object(thread)->nt->tid;
|
65
|
+
#else
|
66
|
+
return thread_struct_from_object(thread)->tid;
|
67
|
+
#endif
|
68
|
+
#else
|
69
|
+
rb_nativethread_id_t pthread_id = pthread_id_for(thread);
|
70
|
+
|
71
|
+
#ifdef __APPLE__
|
72
|
+
uint64_t result;
|
73
|
+
// On macOS, this gives us the same identifier that shows up in activity monitor
|
74
|
+
int error = pthread_threadid_np(pthread_id, &result);
|
75
|
+
if (error) rb_syserr_fail(error, "Unexpected failure in pthread_threadid_np");
|
76
|
+
return result;
|
77
|
+
#else
|
78
|
+
// Fallback, when we have nothing better (e.g. on Ruby < 3.1 on Linux)
|
79
|
+
// @ivoanjo: In the future we may want to explore some potential hacks to get the actual tid on linux
|
80
|
+
// (e.g. https://stackoverflow.com/questions/558469/how-do-i-get-a-thread-id-from-an-arbitrary-pthread-t )
|
81
|
+
return (uint64_t) pthread_id;
|
82
|
+
#endif
|
83
|
+
#endif
|
84
|
+
}
|
85
|
+
|
52
86
|
// Returns the stack depth by using the same approach as rb_profile_frames and backtrace_each: get the positions
|
53
87
|
// of the end and current frame pointers and subtracting them.
|
54
88
|
ptrdiff_t stack_depth_for(VALUE thread) {
|
@@ -690,3 +724,28 @@ int ruby_thread_has_gvl_p(void) {
|
|
690
724
|
return 0;
|
691
725
|
}
|
692
726
|
#endif // NO_THREAD_HAS_GVL
|
727
|
+
|
728
|
+
#ifndef NO_RACTORS
|
729
|
+
// 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
|
730
|
+
// repeat it here to be able to use in our code.
|
731
|
+
bool rb_ractor_main_p_(void);
|
732
|
+
extern struct rb_ractor_struct *ruby_single_main_ractor;
|
733
|
+
|
734
|
+
// Taken from upstream ractor_core.h at commit d9cf0388599a3234b9f3c06ddd006cd59a58ab8b (November 2022, Ruby 3.2 trunk)
|
735
|
+
// to allow us to ensure that we're always operating on the main ractor (if Ruby has ractors)
|
736
|
+
// Modifications:
|
737
|
+
// * None
|
738
|
+
bool ddtrace_rb_ractor_main_p(void)
|
739
|
+
{
|
740
|
+
if (ruby_single_main_ractor) {
|
741
|
+
return true;
|
742
|
+
}
|
743
|
+
else {
|
744
|
+
return rb_ractor_main_p_();
|
745
|
+
}
|
746
|
+
}
|
747
|
+
#else
|
748
|
+
// Simplify callers on older Rubies, instead of having them probe if the VM supports Ractors we just tell them that yes
|
749
|
+
// they're always on the main Ractor
|
750
|
+
bool ddtrace_rb_ractor_main_p(void) { return true; }
|
751
|
+
#endif // NO_RACTORS
|
@@ -12,12 +12,15 @@
|
|
12
12
|
#include "extconf.h"
|
13
13
|
|
14
14
|
rb_nativethread_id_t pthread_id_for(VALUE thread);
|
15
|
+
uint64_t native_thread_id_for(VALUE thread);
|
15
16
|
ptrdiff_t stack_depth_for(VALUE thread);
|
16
17
|
VALUE ddtrace_thread_list(void);
|
17
18
|
bool is_thread_alive(VALUE thread);
|
18
19
|
VALUE thread_name_for(VALUE thread);
|
19
20
|
|
20
21
|
int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, VALUE *buff, int *lines, bool* is_ruby_frame);
|
22
|
+
// Returns true if the current thread belongs to the main Ractor or if Ruby has no Ractor support
|
23
|
+
bool ddtrace_rb_ractor_main_p(void);
|
21
24
|
|
22
25
|
// Ruby 3.0 finally added support for showing CFUNC frames (frames for methods written using native code)
|
23
26
|
// in stack traces gathered via `rb_profile_frames` (https://github.com/ruby/ruby/pull/3299).
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
#include "clock_id.h"
|
4
4
|
#include "helpers.h"
|
5
|
+
#include "private_vm_api_access.h"
|
5
6
|
|
6
7
|
// Each class/module here is implemented in their separate file
|
7
8
|
void collectors_cpu_and_wall_time_init(VALUE profiling_module);
|
@@ -11,6 +12,7 @@ void http_transport_init(VALUE profiling_module);
|
|
11
12
|
void stack_recorder_init(VALUE profiling_module);
|
12
13
|
|
13
14
|
static VALUE native_working_p(VALUE self);
|
15
|
+
static VALUE _native_ddtrace_rb_ractor_main_p(DDTRACE_UNUSED VALUE _self);
|
14
16
|
|
15
17
|
void DDTRACE_EXPORT Init_ddtrace_profiling_native_extension(void) {
|
16
18
|
VALUE datadog_module = rb_define_module("Datadog");
|
@@ -27,6 +29,10 @@ void DDTRACE_EXPORT Init_ddtrace_profiling_native_extension(void) {
|
|
27
29
|
collectors_stack_init(profiling_module);
|
28
30
|
http_transport_init(profiling_module);
|
29
31
|
stack_recorder_init(profiling_module);
|
32
|
+
|
33
|
+
// Hosts methods used for testing the native code using RSpec
|
34
|
+
VALUE testing_module = rb_define_module_under(native_extension_module, "Testing");
|
35
|
+
rb_define_singleton_method(testing_module, "_native_ddtrace_rb_ractor_main_p", _native_ddtrace_rb_ractor_main_p, 0);
|
30
36
|
}
|
31
37
|
|
32
38
|
static VALUE native_working_p(DDTRACE_UNUSED VALUE _self) {
|
@@ -34,3 +40,7 @@ static VALUE native_working_p(DDTRACE_UNUSED VALUE _self) {
|
|
34
40
|
|
35
41
|
return Qtrue;
|
36
42
|
}
|
43
|
+
|
44
|
+
static VALUE _native_ddtrace_rb_ractor_main_p(DDTRACE_UNUSED VALUE _self) {
|
45
|
+
return ddtrace_rb_ractor_main_p() ? Qtrue : Qfalse;
|
46
|
+
}
|
@@ -45,12 +45,14 @@ static inline int check_if_pending_exception(void) {
|
|
45
45
|
// Ruby has a Check_Type(value, type) that is roughly equivalent to this BUT Ruby's version is rather cryptic when it fails
|
46
46
|
// e.g. "wrong argument type nil (expected String)". This is a replacement that prints more information to help debugging.
|
47
47
|
#define ENFORCE_TYPE(value, type) \
|
48
|
-
{ if (RB_UNLIKELY(!RB_TYPE_P(value, type))) raise_unexpected_type(value,
|
48
|
+
{ if (RB_UNLIKELY(!RB_TYPE_P(value, type))) raise_unexpected_type(value, ADD_QUOTES(value), ADD_QUOTES(type), __FILE__, __LINE__, __func__); }
|
49
|
+
|
50
|
+
#define ENFORCE_BOOLEAN(value) \
|
51
|
+
{ if (RB_UNLIKELY(value != Qtrue && value != Qfalse)) raise_unexpected_type(value, ADD_QUOTES(value), "true or false", __FILE__, __LINE__, __func__); }
|
49
52
|
|
50
53
|
// Called by ENFORCE_TYPE; should not be used directly
|
51
54
|
NORETURN(void raise_unexpected_type(
|
52
55
|
VALUE value,
|
53
|
-
enum ruby_value_type type,
|
54
56
|
const char *value_name,
|
55
57
|
const char *type_name,
|
56
58
|
const char *file,
|