ddtrace 1.18.0 → 1.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -1
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +67 -52
- data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.c +22 -14
- data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.h +4 -0
- data/ext/ddtrace_profiling_native_extension/collectors_gc_profiling_helper.c +156 -0
- data/ext/ddtrace_profiling_native_extension/collectors_gc_profiling_helper.h +5 -0
- data/ext/ddtrace_profiling_native_extension/collectors_stack.c +43 -102
- data/ext/ddtrace_profiling_native_extension/collectors_stack.h +10 -3
- data/ext/ddtrace_profiling_native_extension/collectors_thread_context.c +159 -124
- data/ext/ddtrace_profiling_native_extension/collectors_thread_context.h +2 -1
- data/ext/ddtrace_profiling_native_extension/extconf.rb +16 -0
- data/ext/ddtrace_profiling_native_extension/heap_recorder.c +970 -0
- data/ext/ddtrace_profiling_native_extension/heap_recorder.h +155 -0
- data/ext/ddtrace_profiling_native_extension/helpers.h +2 -0
- data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.c +20 -0
- data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +11 -0
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +5 -0
- data/ext/ddtrace_profiling_native_extension/profiling.c +1 -0
- data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +147 -0
- data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +28 -0
- data/ext/ddtrace_profiling_native_extension/stack_recorder.c +329 -10
- data/ext/ddtrace_profiling_native_extension/stack_recorder.h +3 -0
- data/lib/datadog/core/configuration/settings.rb +139 -22
- data/lib/datadog/core/telemetry/collector.rb +10 -0
- data/lib/datadog/core/telemetry/event.rb +2 -1
- data/lib/datadog/core/telemetry/ext.rb +3 -0
- data/lib/datadog/core/telemetry/v1/app_event.rb +8 -1
- data/lib/datadog/core/telemetry/v1/install_signature.rb +38 -0
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +6 -11
- data/lib/datadog/profiling/component.rb +197 -13
- data/lib/datadog/profiling/scheduler.rb +4 -6
- data/lib/datadog/profiling/stack_recorder.rb +13 -2
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +4 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +2 -1
- data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +0 -2
- data/lib/ddtrace/version.rb +1 -1
- metadata +12 -7
@@ -11,9 +11,6 @@
|
|
11
11
|
// Gathers stack traces from running threads, storing them in a StackRecorder instance
|
12
12
|
// This file implements the native bits of the Datadog::Profiling::Collectors::Stack class
|
13
13
|
|
14
|
-
#define MAX_FRAMES_LIMIT 10000
|
15
|
-
#define MAX_FRAMES_LIMIT_AS_STRING "10000"
|
16
|
-
|
17
14
|
static VALUE missing_string = Qnil;
|
18
15
|
|
19
16
|
// Used as scratch space during sampling
|
@@ -36,23 +33,7 @@ static VALUE _native_sample(
|
|
36
33
|
VALUE in_gc
|
37
34
|
);
|
38
35
|
static void maybe_add_placeholder_frames_omitted(VALUE thread, sampling_buffer* buffer, char *frames_omitted_message, int frames_omitted_message_size);
|
39
|
-
static void record_placeholder_stack_in_native_code(
|
40
|
-
sampling_buffer* buffer,
|
41
|
-
VALUE recorder_instance,
|
42
|
-
sample_values values,
|
43
|
-
sample_labels labels,
|
44
|
-
sampling_buffer *record_buffer,
|
45
|
-
int extra_frames_in_record_buffer
|
46
|
-
);
|
47
|
-
static void sample_thread_internal(
|
48
|
-
VALUE thread,
|
49
|
-
sampling_buffer* buffer,
|
50
|
-
VALUE recorder_instance,
|
51
|
-
sample_values values,
|
52
|
-
sample_labels labels,
|
53
|
-
sampling_buffer *record_buffer,
|
54
|
-
int extra_frames_in_record_buffer
|
55
|
-
);
|
36
|
+
static void record_placeholder_stack_in_native_code(sampling_buffer* buffer, VALUE recorder_instance, sample_values values, sample_labels labels);
|
56
37
|
|
57
38
|
void collectors_stack_init(VALUE profiling_module) {
|
58
39
|
VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
|
@@ -88,6 +69,7 @@ static VALUE _native_sample(
|
|
88
69
|
.cpu_or_wall_samples = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("cpu-samples"), zero)),
|
89
70
|
.wall_time_ns = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("wall-time"), zero)),
|
90
71
|
.alloc_samples = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("alloc-samples"), zero)),
|
72
|
+
.timeline_wall_time_ns = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("timeline"), zero)),
|
91
73
|
};
|
92
74
|
|
93
75
|
long labels_count = RARRAY_LEN(labels_array) + RARRAY_LEN(numeric_labels_array);
|
@@ -122,62 +104,29 @@ static VALUE _native_sample(
|
|
122
104
|
|
123
105
|
ddog_prof_Slice_Label slice_labels = {.ptr = labels, .len = labels_count};
|
124
106
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
107
|
+
if (in_gc == Qtrue) {
|
108
|
+
record_placeholder_stack(
|
109
|
+
buffer,
|
110
|
+
recorder_instance,
|
111
|
+
values,
|
112
|
+
(sample_labels) {.labels = slice_labels, .state_label = state_label},
|
113
|
+
DDOG_CHARSLICE_C("Garbage Collection")
|
114
|
+
);
|
115
|
+
} else {
|
116
|
+
sample_thread(
|
117
|
+
thread,
|
118
|
+
buffer,
|
119
|
+
recorder_instance,
|
120
|
+
values,
|
121
|
+
(sample_labels) {.labels = slice_labels, .state_label = state_label}
|
122
|
+
);
|
123
|
+
}
|
133
124
|
|
134
125
|
sampling_buffer_free(buffer);
|
135
126
|
|
136
127
|
return Qtrue;
|
137
128
|
}
|
138
129
|
|
139
|
-
void sample_thread(
|
140
|
-
VALUE thread,
|
141
|
-
sampling_buffer* buffer,
|
142
|
-
VALUE recorder_instance,
|
143
|
-
sample_values values,
|
144
|
-
sample_labels labels,
|
145
|
-
sample_type type
|
146
|
-
) {
|
147
|
-
// Samples thread into recorder
|
148
|
-
if (type == SAMPLE_REGULAR) {
|
149
|
-
sampling_buffer *record_buffer = buffer;
|
150
|
-
int extra_frames_in_record_buffer = 0;
|
151
|
-
sample_thread_internal(thread, buffer, recorder_instance, values, labels, record_buffer, extra_frames_in_record_buffer);
|
152
|
-
return;
|
153
|
-
}
|
154
|
-
|
155
|
-
// Samples thread into recorder, including as a top frame in the stack a frame named "Garbage Collection"
|
156
|
-
if (type == SAMPLE_IN_GC) {
|
157
|
-
ddog_CharSlice function_name = DDOG_CHARSLICE_C("");
|
158
|
-
ddog_CharSlice function_filename = DDOG_CHARSLICE_C("Garbage Collection");
|
159
|
-
buffer->locations[0] = (ddog_prof_Location) {
|
160
|
-
.function = (ddog_prof_Function) {.name = function_name, .filename = function_filename},
|
161
|
-
.line = 0
|
162
|
-
};
|
163
|
-
// To avoid changing sample_thread_internal, we just prepare a new buffer struct that uses the same underlying storage as the
|
164
|
-
// original buffer, but has capacity one less, so that we can keep the above Garbage Collection frame untouched.
|
165
|
-
sampling_buffer thread_in_gc_buffer = (struct sampling_buffer) {
|
166
|
-
.max_frames = buffer->max_frames - 1,
|
167
|
-
.stack_buffer = buffer->stack_buffer + 1,
|
168
|
-
.lines_buffer = buffer->lines_buffer + 1,
|
169
|
-
.is_ruby_frame = buffer->is_ruby_frame + 1,
|
170
|
-
.locations = buffer->locations + 1,
|
171
|
-
};
|
172
|
-
sampling_buffer *record_buffer = buffer; // We pass in the original buffer as the record_buffer, but not as the regular buffer
|
173
|
-
int extra_frames_in_record_buffer = 1;
|
174
|
-
sample_thread_internal(thread, &thread_in_gc_buffer, recorder_instance, values, labels, record_buffer, extra_frames_in_record_buffer);
|
175
|
-
return;
|
176
|
-
}
|
177
|
-
|
178
|
-
rb_raise(rb_eArgError, "Unexpected value for sample_type: %d", type);
|
179
|
-
}
|
180
|
-
|
181
130
|
#define CHARSLICE_EQUALS(must_be_a_literal, charslice) (strlen("" must_be_a_literal) == charslice.len && strncmp(must_be_a_literal, charslice.ptr, charslice.len) == 0)
|
182
131
|
|
183
132
|
// Idea: Should we release the global vm lock (GVL) after we get the data from `rb_profile_frames`? That way other Ruby threads
|
@@ -189,24 +138,12 @@ void sample_thread(
|
|
189
138
|
// * Should we move this into a different thread entirely?
|
190
139
|
// * If we don't move it into a different thread, does releasing the GVL on a Ruby thread mean that we're introducing
|
191
140
|
// a new thread switch point where there previously was none?
|
192
|
-
|
193
|
-
// ---
|
194
|
-
//
|
195
|
-
// Why the weird extra record_buffer and extra_frames_in_record_buffer?
|
196
|
-
// The answer is: to support both sample_thread() and sample_thread_in_gc().
|
197
|
-
//
|
198
|
-
// For sample_thread(), buffer == record_buffer and extra_frames_in_record_buffer == 0, so it's a no-op.
|
199
|
-
// For sample_thread_in_gc(), the buffer is a special buffer that is the same as the record_buffer, but with every
|
200
|
-
// pointer shifted forward extra_frames_in_record_buffer elements, so that the caller can actually inject those extra
|
201
|
-
// frames, and this function doesn't have to care about it.
|
202
|
-
static void sample_thread_internal(
|
141
|
+
void sample_thread(
|
203
142
|
VALUE thread,
|
204
143
|
sampling_buffer* buffer,
|
205
144
|
VALUE recorder_instance,
|
206
145
|
sample_values values,
|
207
|
-
sample_labels labels
|
208
|
-
sampling_buffer *record_buffer,
|
209
|
-
int extra_frames_in_record_buffer
|
146
|
+
sample_labels labels
|
210
147
|
) {
|
211
148
|
int captured_frames = ddtrace_rb_profile_frames(
|
212
149
|
thread,
|
@@ -218,14 +155,7 @@ static void sample_thread_internal(
|
|
218
155
|
);
|
219
156
|
|
220
157
|
if (captured_frames == PLACEHOLDER_STACK_IN_NATIVE_CODE) {
|
221
|
-
record_placeholder_stack_in_native_code(
|
222
|
-
buffer,
|
223
|
-
recorder_instance,
|
224
|
-
values,
|
225
|
-
labels,
|
226
|
-
record_buffer,
|
227
|
-
extra_frames_in_record_buffer
|
228
|
-
);
|
158
|
+
record_placeholder_stack_in_native_code(buffer, recorder_instance, values, labels);
|
229
159
|
return;
|
230
160
|
}
|
231
161
|
|
@@ -332,7 +262,7 @@ static void sample_thread_internal(
|
|
332
262
|
|
333
263
|
record_sample(
|
334
264
|
recorder_instance,
|
335
|
-
(ddog_prof_Slice_Location) {.ptr =
|
265
|
+
(ddog_prof_Slice_Location) {.ptr = buffer->locations, .len = captured_frames},
|
336
266
|
values,
|
337
267
|
labels
|
338
268
|
);
|
@@ -379,24 +309,35 @@ static void maybe_add_placeholder_frames_omitted(VALUE thread, sampling_buffer*
|
|
379
309
|
//
|
380
310
|
// To give customers visibility into these threads, rather than reporting an empty stack, we replace the empty stack
|
381
311
|
// with one containing a placeholder frame, so that these threads are properly represented in the UX.
|
312
|
+
|
382
313
|
static void record_placeholder_stack_in_native_code(
|
314
|
+
sampling_buffer* buffer,
|
315
|
+
VALUE recorder_instance,
|
316
|
+
sample_values values,
|
317
|
+
sample_labels labels
|
318
|
+
) {
|
319
|
+
record_placeholder_stack(
|
320
|
+
buffer,
|
321
|
+
recorder_instance,
|
322
|
+
values,
|
323
|
+
labels,
|
324
|
+
DDOG_CHARSLICE_C("In native code")
|
325
|
+
);
|
326
|
+
}
|
327
|
+
|
328
|
+
void record_placeholder_stack(
|
383
329
|
sampling_buffer* buffer,
|
384
330
|
VALUE recorder_instance,
|
385
331
|
sample_values values,
|
386
332
|
sample_labels labels,
|
387
|
-
|
388
|
-
int extra_frames_in_record_buffer
|
333
|
+
ddog_CharSlice placeholder_stack
|
389
334
|
) {
|
390
|
-
|
391
|
-
|
392
|
-
buffer->locations[0] = (ddog_prof_Location) {
|
393
|
-
.function = (ddog_prof_Function) {.name = function_name, .filename = function_filename},
|
394
|
-
.line = 0
|
395
|
-
};
|
335
|
+
ddog_prof_Function placeholder = {.name = DDOG_CHARSLICE_C(""), .filename = placeholder_stack};
|
336
|
+
buffer->locations[0] = (ddog_prof_Location) {.function = placeholder, .line = 0};
|
396
337
|
|
397
338
|
record_sample(
|
398
339
|
recorder_instance,
|
399
|
-
(ddog_prof_Slice_Location) {.ptr =
|
340
|
+
(ddog_prof_Slice_Location) {.ptr = buffer->locations, .len = 1},
|
400
341
|
values,
|
401
342
|
labels
|
402
343
|
);
|
@@ -4,17 +4,24 @@
|
|
4
4
|
|
5
5
|
#include "stack_recorder.h"
|
6
6
|
|
7
|
-
|
7
|
+
#define MAX_FRAMES_LIMIT 10000
|
8
|
+
#define MAX_FRAMES_LIMIT_AS_STRING "10000"
|
8
9
|
|
9
|
-
typedef
|
10
|
+
typedef struct sampling_buffer sampling_buffer;
|
10
11
|
|
11
12
|
void sample_thread(
|
12
13
|
VALUE thread,
|
14
|
+
sampling_buffer* buffer,
|
15
|
+
VALUE recorder_instance,
|
16
|
+
sample_values values,
|
17
|
+
sample_labels labels
|
18
|
+
);
|
19
|
+
void record_placeholder_stack(
|
13
20
|
sampling_buffer* buffer,
|
14
21
|
VALUE recorder_instance,
|
15
22
|
sample_values values,
|
16
23
|
sample_labels labels,
|
17
|
-
|
24
|
+
ddog_CharSlice placeholder_stack
|
18
25
|
);
|
19
26
|
sampling_buffer *sampling_buffer_new(unsigned int max_frames);
|
20
27
|
void sampling_buffer_free(sampling_buffer *buffer);
|