ddtrace 1.5.2 → 1.6.1
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 +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,
|