ddtrace 1.22.0 → 1.23.3
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 +46 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +8 -20
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +18 -10
- data/ext/datadog_profiling_native_extension/extconf.rb +7 -5
- data/ext/datadog_profiling_native_extension/heap_recorder.c +38 -3
- data/ext/datadog_profiling_native_extension/heap_recorder.h +5 -0
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +46 -0
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +3 -0
- data/ext/datadog_profiling_native_extension/stack_recorder.c +156 -55
- data/lib/datadog/appsec/contrib/devise/tracking.rb +8 -0
- data/lib/datadog/core/configuration/components.rb +4 -3
- data/lib/datadog/core/configuration.rb +3 -17
- data/lib/datadog/core/telemetry/component.rb +66 -0
- data/lib/datadog/core/telemetry/emitter.rb +1 -1
- data/lib/datadog/core/telemetry/event.rb +1 -0
- data/lib/datadog/core/telemetry/http/adapters/net.rb +1 -1
- data/lib/datadog/core/telemetry/http/response.rb +4 -0
- data/lib/datadog/core/telemetry/worker.rb +158 -0
- data/lib/datadog/core/utils/only_once_successful.rb +76 -0
- data/lib/datadog/profiling/exporter.rb +6 -3
- data/lib/datadog/profiling/stack_recorder.rb +6 -2
- data/lib/ddtrace/version.rb +2 -2
- metadata +18 -7
- data/lib/datadog/core/telemetry/client.rb +0 -95
- data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e52d825fdd7cb0391c1e529a979862f24f8ec0412cbd1952f0ae21f3129b83f
|
4
|
+
data.tar.gz: 27ab67ddd6bd0c21a0f7de60c022c143ae2367051b4b1a744f33f01453c1d8e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0c94e18051c3fe9522493daf7321ae77246d9e5cf6d5888fd3b9c565668c882e684d3156d58867104743c6769f07437c4bc68f69f4884d0609c92279d1ea3bb
|
7
|
+
data.tar.gz: 83bab49e211de5749555906b2f59b0ee8cb8a501c2363ea625a52e6f2c318714be26dc0226e51aabfd8461b96f3036b15a856eb372729ce69bd29df7baa06ef8
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,40 @@
|
|
2
2
|
|
3
3
|
## [Unreleased]
|
4
4
|
|
5
|
+
## [1.23.3] - 2024-07-01
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
* Add post install message about 2.x upgrade ([#3723][])
|
10
|
+
|
11
|
+
### Fixed
|
12
|
+
|
13
|
+
* Fix telemetry events blocking main thread ([#3740][])
|
14
|
+
* Fix deadlock from telemetry threads ([#3745][])
|
15
|
+
|
16
|
+
## [1.23.2] - 2024-06-13
|
17
|
+
|
18
|
+
### Fixed
|
19
|
+
|
20
|
+
* Profiling: Fix rpath for linking to libdatadog when loading from extension dir ([#3683][])
|
21
|
+
|
22
|
+
## [1.23.1] - 2024-06-06
|
23
|
+
|
24
|
+
### Fixed
|
25
|
+
|
26
|
+
* AppSec: Fix undefined method error when Tracing disabled ([#3650][])
|
27
|
+
|
28
|
+
## [1.23.0] - 2024-05-09
|
29
|
+
|
30
|
+
### Added
|
31
|
+
|
32
|
+
* Profiling: Enable endpoint profiling for Sidekiq and similar background job processors ([#3619][])
|
33
|
+
|
34
|
+
### Fixed
|
35
|
+
|
36
|
+
* Fix no such file or directory issue when using single step instrumentation ([#3623][])
|
37
|
+
* Fix error during telemetry debug logging attempt ([#3618][])
|
38
|
+
|
5
39
|
## [1.22.0] - 2024-04-16
|
6
40
|
|
7
41
|
### Added
|
@@ -2803,7 +2837,11 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
|
|
2803
2837
|
Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
2804
2838
|
|
2805
2839
|
|
2806
|
-
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v1.
|
2840
|
+
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v1.23.3...1.x-stable
|
2841
|
+
[1.23.3]: https://github.com/DataDog/dd-trace-rb/compare/v1.23.2...v1.23.3
|
2842
|
+
[1.23.2]: https://github.com/DataDog/dd-trace-rb/compare/v1.23.1...v1.23.2
|
2843
|
+
[1.23.1]: https://github.com/DataDog/dd-trace-rb/compare/v1.23.0...v1.23.1
|
2844
|
+
[1.23.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.22.0...v1.23.0
|
2807
2845
|
[1.22.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.21.1...v1.22.0
|
2808
2846
|
[2.0.0.beta1]: https://github.com/DataDog/dd-trace-rb/compare/v1.21.1...v2.0.0.beta1
|
2809
2847
|
[1.21.1]: https://github.com/DataDog/dd-trace-rb/compare/v1.21.0...v1.21.1
|
@@ -4111,6 +4149,12 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
4111
4149
|
[#3582]: https://github.com/DataDog/dd-trace-rb/issues/3582
|
4112
4150
|
[#3585]: https://github.com/DataDog/dd-trace-rb/issues/3585
|
4113
4151
|
[#3587]: https://github.com/DataDog/dd-trace-rb/issues/3587
|
4152
|
+
[#3618]: https://github.com/DataDog/dd-trace-rb/issues/3618
|
4153
|
+
[#3619]: https://github.com/DataDog/dd-trace-rb/issues/3619
|
4154
|
+
[#3623]: https://github.com/DataDog/dd-trace-rb/issues/3623
|
4155
|
+
[#3650]: https://github.com/DataDog/dd-trace-rb/issues/3650
|
4156
|
+
[#3683]: https://github.com/DataDog/dd-trace-rb/issues/3683
|
4157
|
+
[#3745]: https://github.com/DataDog/dd-trace-rb/issues/3745
|
4114
4158
|
[@AdrianLC]: https://github.com/AdrianLC
|
4115
4159
|
[@Azure7111]: https://github.com/Azure7111
|
4116
4160
|
[@BabyGroot]: https://github.com/BabyGroot
|
@@ -4262,4 +4306,4 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
4262
4306
|
[@y-yagi]: https://github.com/y-yagi
|
4263
4307
|
[@yujideveloper]: https://github.com/yujideveloper
|
4264
4308
|
[@yukimurasawa]: https://github.com/yukimurasawa
|
4265
|
-
[@zachmccormick]: https://github.com/zachmccormick
|
4309
|
+
[@zachmccormick]: https://github.com/zachmccormick
|
@@ -929,18 +929,6 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
929
929
|
struct cpu_and_wall_time_worker_state *state;
|
930
930
|
TypedData_Get_Struct(instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
931
931
|
|
932
|
-
VALUE pretty_cpu_sampling_time_ns_min = state->stats.cpu_sampling_time_ns_min == UINT64_MAX ? Qnil : ULL2NUM(state->stats.cpu_sampling_time_ns_min);
|
933
|
-
VALUE pretty_cpu_sampling_time_ns_max = state->stats.cpu_sampling_time_ns_max == 0 ? Qnil : ULL2NUM(state->stats.cpu_sampling_time_ns_max);
|
934
|
-
VALUE pretty_cpu_sampling_time_ns_total = state->stats.cpu_sampling_time_ns_total == 0 ? Qnil : ULL2NUM(state->stats.cpu_sampling_time_ns_total);
|
935
|
-
VALUE pretty_cpu_sampling_time_ns_avg =
|
936
|
-
state->stats.cpu_sampled == 0 ? Qnil : DBL2NUM(((double) state->stats.cpu_sampling_time_ns_total) / state->stats.cpu_sampled);
|
937
|
-
|
938
|
-
VALUE pretty_allocation_sampling_time_ns_min = state->stats.allocation_sampling_time_ns_min == UINT64_MAX ? Qnil : ULL2NUM(state->stats.allocation_sampling_time_ns_min);
|
939
|
-
VALUE pretty_allocation_sampling_time_ns_max = state->stats.allocation_sampling_time_ns_max == 0 ? Qnil : ULL2NUM(state->stats.allocation_sampling_time_ns_max);
|
940
|
-
VALUE pretty_allocation_sampling_time_ns_total = state->stats.allocation_sampling_time_ns_total == 0 ? Qnil : ULL2NUM(state->stats.allocation_sampling_time_ns_total);
|
941
|
-
VALUE pretty_allocation_sampling_time_ns_avg =
|
942
|
-
state->stats.allocation_sampled == 0 ? Qnil : DBL2NUM(((double) state->stats.allocation_sampling_time_ns_total) / state->stats.allocation_sampled);
|
943
|
-
|
944
932
|
unsigned long total_cpu_samples_attempted = state->stats.cpu_sampled + state->stats.cpu_skipped;
|
945
933
|
VALUE effective_cpu_sample_rate =
|
946
934
|
total_cpu_samples_attempted == 0 ? Qnil : DBL2NUM(((double) state->stats.cpu_sampled) / total_cpu_samples_attempted);
|
@@ -968,19 +956,19 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
968
956
|
ID2SYM(rb_intern("cpu_sampled")), /* => */ UINT2NUM(state->stats.cpu_sampled),
|
969
957
|
ID2SYM(rb_intern("cpu_skipped")), /* => */ UINT2NUM(state->stats.cpu_skipped),
|
970
958
|
ID2SYM(rb_intern("cpu_effective_sample_rate")), /* => */ effective_cpu_sample_rate,
|
971
|
-
ID2SYM(rb_intern("cpu_sampling_time_ns_min")), /* => */
|
972
|
-
ID2SYM(rb_intern("cpu_sampling_time_ns_max")), /* => */
|
973
|
-
ID2SYM(rb_intern("cpu_sampling_time_ns_total")), /* => */
|
974
|
-
ID2SYM(rb_intern("cpu_sampling_time_ns_avg")), /* => */
|
959
|
+
ID2SYM(rb_intern("cpu_sampling_time_ns_min")), /* => */ RUBY_NUM_OR_NIL(state->stats.cpu_sampling_time_ns_min, != UINT64_MAX, ULL2NUM),
|
960
|
+
ID2SYM(rb_intern("cpu_sampling_time_ns_max")), /* => */ RUBY_NUM_OR_NIL(state->stats.cpu_sampling_time_ns_max, > 0, ULL2NUM),
|
961
|
+
ID2SYM(rb_intern("cpu_sampling_time_ns_total")), /* => */ RUBY_NUM_OR_NIL(state->stats.cpu_sampling_time_ns_total, > 0, ULL2NUM),
|
962
|
+
ID2SYM(rb_intern("cpu_sampling_time_ns_avg")), /* => */ RUBY_AVG_OR_NIL(state->stats.cpu_sampling_time_ns_total, state->stats.cpu_sampled),
|
975
963
|
|
976
964
|
// Allocation stats
|
977
965
|
ID2SYM(rb_intern("allocation_sampled")), /* => */ state->allocation_profiling_enabled ? ULONG2NUM(state->stats.allocation_sampled) : Qnil,
|
978
966
|
ID2SYM(rb_intern("allocation_skipped")), /* => */ state->allocation_profiling_enabled ? ULONG2NUM(state->stats.allocation_skipped) : Qnil,
|
979
967
|
ID2SYM(rb_intern("allocation_effective_sample_rate")), /* => */ effective_allocation_sample_rate,
|
980
|
-
ID2SYM(rb_intern("allocation_sampling_time_ns_min")), /* => */
|
981
|
-
ID2SYM(rb_intern("allocation_sampling_time_ns_max")), /* => */
|
982
|
-
ID2SYM(rb_intern("allocation_sampling_time_ns_total")), /* => */
|
983
|
-
ID2SYM(rb_intern("allocation_sampling_time_ns_avg")), /* => */
|
968
|
+
ID2SYM(rb_intern("allocation_sampling_time_ns_min")), /* => */ RUBY_NUM_OR_NIL(state->stats.allocation_sampling_time_ns_min, != UINT64_MAX, ULL2NUM),
|
969
|
+
ID2SYM(rb_intern("allocation_sampling_time_ns_max")), /* => */ RUBY_NUM_OR_NIL(state->stats.allocation_sampling_time_ns_max, > 0, ULL2NUM),
|
970
|
+
ID2SYM(rb_intern("allocation_sampling_time_ns_total")), /* => */ RUBY_NUM_OR_NIL(state->stats.allocation_sampling_time_ns_total, > 0, ULL2NUM),
|
971
|
+
ID2SYM(rb_intern("allocation_sampling_time_ns_avg")), /* => */ RUBY_AVG_OR_NIL(state->stats.allocation_sampling_time_ns_total, state->stats.allocation_sampled),
|
984
972
|
ID2SYM(rb_intern("allocation_sampler_snapshot")), /* => */ allocation_sampler_snapshot,
|
985
973
|
ID2SYM(rb_intern("allocations_during_sample")), /* => */ state->allocation_profiling_enabled ? UINT2NUM(state->stats.allocations_during_sample) : Qnil,
|
986
974
|
};
|
@@ -217,7 +217,7 @@ static long thread_id_for(VALUE thread);
|
|
217
217
|
static VALUE _native_stats(VALUE self, VALUE collector_instance);
|
218
218
|
static VALUE _native_gc_tracking(VALUE self, VALUE collector_instance);
|
219
219
|
static void trace_identifiers_for(struct thread_context_collector_state *state, VALUE thread, struct trace_identifiers *trace_identifiers_result);
|
220
|
-
static bool should_collect_resource(VALUE
|
220
|
+
static bool should_collect_resource(VALUE root_span);
|
221
221
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
222
222
|
static VALUE thread_list(struct thread_context_collector_state *state);
|
223
223
|
static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object);
|
@@ -1146,10 +1146,7 @@ static void trace_identifiers_for(struct thread_context_collector_state *state,
|
|
1146
1146
|
|
1147
1147
|
trace_identifiers_result->valid = true;
|
1148
1148
|
|
1149
|
-
if (!state->endpoint_collection_enabled) return;
|
1150
|
-
|
1151
|
-
VALUE root_span_type = rb_ivar_get(root_span, at_type_id /* @type */);
|
1152
|
-
if (root_span_type == Qnil || !should_collect_resource(root_span_type)) return;
|
1149
|
+
if (!state->endpoint_collection_enabled || !should_collect_resource(root_span)) return;
|
1153
1150
|
|
1154
1151
|
VALUE trace_resource = rb_ivar_get(active_trace, at_resource_id /* @resource */);
|
1155
1152
|
if (RB_TYPE_P(trace_resource, T_STRING)) {
|
@@ -1160,21 +1157,32 @@ static void trace_identifiers_for(struct thread_context_collector_state *state,
|
|
1160
1157
|
}
|
1161
1158
|
}
|
1162
1159
|
|
1163
|
-
// We
|
1160
|
+
// We opt-in to collecting the resource for spans of types:
|
1164
1161
|
// * 'web', for web requests
|
1165
|
-
// * proxy', used by the rack integration with request_queuing: true (e.g. also represents a web request)
|
1162
|
+
// * 'proxy', used by the rack integration with request_queuing: true (e.g. also represents a web request)
|
1163
|
+
// * 'worker', used for sidekiq and similar background job processors
|
1166
1164
|
//
|
1167
|
-
//
|
1165
|
+
// Over time, this list may be expanded.
|
1168
1166
|
// Resources MUST NOT include personal identifiable information (PII); this should not be the case with
|
1169
1167
|
// ddtrace integrations, but worth mentioning just in case :)
|
1170
|
-
static bool should_collect_resource(VALUE
|
1168
|
+
static bool should_collect_resource(VALUE root_span) {
|
1169
|
+
VALUE root_span_type = rb_ivar_get(root_span, at_type_id /* @type */);
|
1170
|
+
if (root_span_type == Qnil) return false;
|
1171
1171
|
ENFORCE_TYPE(root_span_type, T_STRING);
|
1172
1172
|
|
1173
1173
|
int root_span_type_length = RSTRING_LEN(root_span_type);
|
1174
1174
|
const char *root_span_type_value = StringValuePtr(root_span_type);
|
1175
1175
|
|
1176
|
-
|
1176
|
+
bool is_web_request =
|
1177
|
+
(root_span_type_length == strlen("web") && (memcmp("web", root_span_type_value, strlen("web")) == 0)) ||
|
1177
1178
|
(root_span_type_length == strlen("proxy") && (memcmp("proxy", root_span_type_value, strlen("proxy")) == 0));
|
1179
|
+
|
1180
|
+
if (is_web_request) return true;
|
1181
|
+
|
1182
|
+
bool is_worker_request =
|
1183
|
+
(root_span_type_length == strlen("worker") && (memcmp("worker", root_span_type_value, strlen("worker")) == 0));
|
1184
|
+
|
1185
|
+
return is_worker_request;
|
1178
1186
|
}
|
1179
1187
|
|
1180
1188
|
// After the Ruby VM forks, this method gets called in the child process to clean up any leftover state from the parent.
|
@@ -225,13 +225,15 @@ unless have_type('atomic_int', ['stdatomic.h'])
|
|
225
225
|
skip_building_extension!(Datadog::Profiling::NativeExtensionHelpers::Supported::COMPILER_ATOMIC_MISSING)
|
226
226
|
end
|
227
227
|
|
228
|
-
# See comments on the helper
|
228
|
+
# See comments on the helper methods being used for why we need to additionally set this.
|
229
229
|
# The extremely excessive escaping around ORIGIN below seems to be correct and was determined after a lot of
|
230
230
|
# experimentation. We need to get these special characters across a lot of tools untouched...
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
231
|
+
extra_relative_rpaths = [
|
232
|
+
Datadog::Profiling::NativeExtensionHelpers.libdatadog_folder_relative_to_native_lib_folder,
|
233
|
+
*Datadog::Profiling::NativeExtensionHelpers.libdatadog_folder_relative_to_ruby_extensions_folders,
|
234
|
+
]
|
235
|
+
extra_relative_rpaths.each { |folder| $LDFLAGS += " -Wl,-rpath,$$$\\\\{ORIGIN\\}/#{folder.to_str}" }
|
236
|
+
Logging.message("[datadog] After pkg-config $LDFLAGS were set to: #{$LDFLAGS.inspect}\n")
|
235
237
|
|
236
238
|
# Tag the native extension library with the Ruby version and Ruby platform.
|
237
239
|
# This makes it easier for development (avoids "oops I forgot to rebuild when I switched my Ruby") and ensures that
|
@@ -158,6 +158,13 @@ struct heap_recorder {
|
|
158
158
|
|
159
159
|
// Sampling state
|
160
160
|
uint num_recordings_skipped;
|
161
|
+
|
162
|
+
struct stats_last_update {
|
163
|
+
size_t objects_alive;
|
164
|
+
size_t objects_dead;
|
165
|
+
size_t objects_skipped;
|
166
|
+
size_t objects_frozen;
|
167
|
+
} stats_last_update;
|
161
168
|
};
|
162
169
|
static heap_record* get_or_create_heap_record(heap_recorder*, ddog_prof_Slice_Location);
|
163
170
|
static void cleanup_heap_record_if_unused(heap_recorder*, heap_record*);
|
@@ -372,6 +379,9 @@ void heap_recorder_prepare_iteration(heap_recorder *heap_recorder) {
|
|
372
379
|
rb_raise(rb_eRuntimeError, "New heap recorder iteration prepared without the previous one having been finished.");
|
373
380
|
}
|
374
381
|
|
382
|
+
// Reset last update stats, we'll be building them from scratch during the st_foreach call below
|
383
|
+
heap_recorder->stats_last_update = (struct stats_last_update) {};
|
384
|
+
|
375
385
|
st_foreach(heap_recorder->object_records, st_object_record_update, (st_data_t) heap_recorder);
|
376
386
|
|
377
387
|
heap_recorder->object_records_snapshot = st_copy(heap_recorder->object_records);
|
@@ -427,6 +437,22 @@ bool heap_recorder_for_each_live_object(
|
|
427
437
|
return true;
|
428
438
|
}
|
429
439
|
|
440
|
+
VALUE heap_recorder_state_snapshot(heap_recorder *heap_recorder) {
|
441
|
+
VALUE arguments[] = {
|
442
|
+
ID2SYM(rb_intern("num_object_records")), /* => */ LONG2NUM(heap_recorder->object_records->num_entries),
|
443
|
+
ID2SYM(rb_intern("num_heap_records")), /* => */ LONG2NUM(heap_recorder->heap_records->num_entries),
|
444
|
+
|
445
|
+
// Stats as of last update
|
446
|
+
ID2SYM(rb_intern("last_update_objects_alive")), /* => */ LONG2NUM(heap_recorder->stats_last_update.objects_alive),
|
447
|
+
ID2SYM(rb_intern("last_update_objects_dead")), /* => */ LONG2NUM(heap_recorder->stats_last_update.objects_dead),
|
448
|
+
ID2SYM(rb_intern("last_update_objects_skipped")), /* => */ LONG2NUM(heap_recorder->stats_last_update.objects_skipped),
|
449
|
+
ID2SYM(rb_intern("last_update_objects_frozen")), /* => */ LONG2NUM(heap_recorder->stats_last_update.objects_frozen),
|
450
|
+
};
|
451
|
+
VALUE hash = rb_hash_new();
|
452
|
+
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(hash, arguments[i], arguments[i+1]);
|
453
|
+
return hash;
|
454
|
+
}
|
455
|
+
|
430
456
|
void heap_recorder_testonly_assert_hash_matches(ddog_prof_Slice_Location locations) {
|
431
457
|
heap_stack *stack = heap_stack_new(locations);
|
432
458
|
heap_record_key stack_based_key = (heap_record_key) {
|
@@ -497,12 +523,14 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
|
|
497
523
|
// no point checking for liveness or updating its size, so exit early.
|
498
524
|
// NOTE: This means that there should be an equivalent check during actual
|
499
525
|
// iteration otherwise we'd iterate/expose stale object data.
|
526
|
+
recorder->stats_last_update.objects_skipped++;
|
500
527
|
return ST_CONTINUE;
|
501
528
|
}
|
502
529
|
|
503
530
|
if (!ruby_ref_from_id(LONG2NUM(obj_id), &ref)) {
|
504
531
|
// Id no longer associated with a valid ref. Need to delete this object record!
|
505
532
|
on_committed_object_record_cleanup(recorder, record);
|
533
|
+
recorder->stats_last_update.objects_dead++;
|
506
534
|
return ST_DELETE;
|
507
535
|
}
|
508
536
|
|
@@ -537,6 +565,7 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
|
|
537
565
|
RB_FL_SET(ref, RUBY_FL_SEEN_OBJ_ID);
|
538
566
|
|
539
567
|
on_committed_object_record_cleanup(recorder, record);
|
568
|
+
recorder->stats_last_update.objects_dead++;
|
540
569
|
return ST_DELETE;
|
541
570
|
}
|
542
571
|
|
@@ -550,6 +579,11 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
|
|
550
579
|
record->object_data.is_frozen = RB_OBJ_FROZEN(ref);
|
551
580
|
}
|
552
581
|
|
582
|
+
recorder->stats_last_update.objects_alive++;
|
583
|
+
if (record->object_data.is_frozen) {
|
584
|
+
recorder->stats_last_update.objects_frozen++;
|
585
|
+
}
|
586
|
+
|
553
587
|
return ST_CONTINUE;
|
554
588
|
}
|
555
589
|
|
@@ -767,9 +801,10 @@ void object_record_free(object_record *record) {
|
|
767
801
|
|
768
802
|
VALUE object_record_inspect(object_record *record) {
|
769
803
|
heap_frame top_frame = record->heap_record->stack->frames[0];
|
770
|
-
|
771
|
-
|
772
|
-
|
804
|
+
live_object_data object_data = record->object_data;
|
805
|
+
VALUE inspect = rb_sprintf("obj_id=%ld weight=%d size=%zu location=%s:%d alloc_gen=%zu gen_age=%zu frozen=%d ",
|
806
|
+
record->obj_id, object_data.weight, object_data.size, top_frame.filename,
|
807
|
+
(int) top_frame.line, object_data.alloc_gen, object_data.gen_age, object_data.is_frozen);
|
773
808
|
|
774
809
|
const char *class = record->object_data.class;
|
775
810
|
if (class != NULL) {
|
@@ -150,6 +150,11 @@ bool heap_recorder_for_each_live_object(
|
|
150
150
|
bool (*for_each_callback)(heap_recorder_iteration_data data, void* extra_arg),
|
151
151
|
void *for_each_callback_extra_arg);
|
152
152
|
|
153
|
+
// Return a Ruby hash containing a snapshot of this recorder's interesting state at calling time.
|
154
|
+
// WARN: This allocates in the Ruby VM and therefore should not be called without the
|
155
|
+
// VM lock or during GC.
|
156
|
+
VALUE heap_recorder_state_snapshot(heap_recorder *heap_recorder);
|
157
|
+
|
153
158
|
// v--- TEST-ONLY APIs ---v
|
154
159
|
|
155
160
|
// Assert internal hashing logic is valid for the provided locations and its
|
@@ -67,6 +67,52 @@ module Datadog
|
|
67
67
|
Pathname.new(libdatadog_lib_folder).relative_path_from(Pathname.new(profiling_native_lib_folder)).to_s
|
68
68
|
end
|
69
69
|
|
70
|
+
# In https://github.com/DataDog/dd-trace-rb/pull/3582 we got a report of a customer for which the native extension
|
71
|
+
# only got installed into the extensions folder.
|
72
|
+
#
|
73
|
+
# But then this fix was not enough to fully get them moving because then they started to see the issue from
|
74
|
+
# https://github.com/DataDog/dd-trace-rb/issues/2067 / https://github.com/DataDog/dd-trace-rb/pull/2125 :
|
75
|
+
#
|
76
|
+
# > Profiling was requested but is not supported, profiling disabled: There was an error loading the profiling
|
77
|
+
# > native extension due to 'RuntimeError Failure to load datadog_profiling_native_extension.3.2.2_x86_64-linux
|
78
|
+
# > due to libdatadog_profiling.so: cannot open shared object file: No such file or directory
|
79
|
+
#
|
80
|
+
# The problem is that when loading the native extension from the extensions directory, the relative rpath we add
|
81
|
+
# with the #libdatadog_folder_relative_to_native_lib_folder helper above is not correct, we need to add a relative
|
82
|
+
# rpath to the extensions directory.
|
83
|
+
#
|
84
|
+
# So how do we find the full path where the native extension is placed?
|
85
|
+
# * From https://github.com/ruby/ruby/blob/83f02d42e0a3c39661dc99c049ab9a70ff227d5b/lib/bundler/runtime.rb#L166
|
86
|
+
# `extension_dirs = Dir["#{Gem.dir}/extensions/*/*/*"] + Dir["#{Gem.dir}/bundler/gems/extensions/*/*/*"]`
|
87
|
+
# we get that's in one of two fixed subdirectories of `Gem.dir`
|
88
|
+
# * From https://github.com/ruby/ruby/blob/83f02d42e0a3c39661dc99c049ab9a70ff227d5b/lib/rubygems/basic_specification.rb#L111-L115
|
89
|
+
# we get the structure of the subdirectory (platform/extension_api_version/gem_and_version)
|
90
|
+
#
|
91
|
+
# Thus, `Gem.dir` of `/var/app/current/vendor/bundle/ruby/3.2.0` becomes (for instance)
|
92
|
+
# `/var/app/current/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/datadog-2.0.0/` or
|
93
|
+
# `/var/app/current/vendor/bundle/ruby/3.2.0/bundler/gems/extensions/x86_64-linux/3.2.0/datadog-2.0.0/`
|
94
|
+
#
|
95
|
+
# We then compute the relative path between these folders and the libdatadog folder, and use that as a relative path.
|
96
|
+
def self.libdatadog_folder_relative_to_ruby_extensions_folders(
|
97
|
+
gem_dir: Gem.dir,
|
98
|
+
libdatadog_pkgconfig_folder: Libdatadog.pkgconfig_folder
|
99
|
+
)
|
100
|
+
return unless libdatadog_pkgconfig_folder
|
101
|
+
|
102
|
+
# For the purposes of calculating a folder relative to the other, we don't actually NEED to fill in the
|
103
|
+
# platform, extension_api_version and gem version. We're basically just after how many folders it is deep from
|
104
|
+
# the Gem.dir.
|
105
|
+
expected_ruby_extensions_folders = [
|
106
|
+
"#{gem_dir}/extensions/platform/extension_api_version/datadog_version/",
|
107
|
+
"#{gem_dir}/bundler/gems/extensions/platform/extension_api_version/datadog_version/",
|
108
|
+
]
|
109
|
+
libdatadog_lib_folder = "#{libdatadog_pkgconfig_folder}/../"
|
110
|
+
|
111
|
+
expected_ruby_extensions_folders.map do |folder|
|
112
|
+
Pathname.new(libdatadog_lib_folder).relative_path_from(Pathname.new(folder)).to_s
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
70
116
|
# Used to check if profiler is supported, including user-visible clear messages explaining why their
|
71
117
|
# system may not be supported.
|
72
118
|
module Supported
|
@@ -82,6 +82,9 @@ NORETURN(
|
|
82
82
|
#define ENFORCE_SUCCESS_HELPER(expression, have_gvl) \
|
83
83
|
{ int result_syserr_errno = expression; if (RB_UNLIKELY(result_syserr_errno)) raise_syserr(result_syserr_errno, have_gvl, ADD_QUOTES(expression), __FILE__, __LINE__, __func__); }
|
84
84
|
|
85
|
+
#define RUBY_NUM_OR_NIL(val, condition, conv) ((val condition) ? conv(val) : Qnil)
|
86
|
+
#define RUBY_AVG_OR_NIL(total, count) ((count == 0) ? Qnil : DBL2NUM(((double) total) / count))
|
87
|
+
|
85
88
|
// Called by ENFORCE_SUCCESS_HELPER; should not be used directly
|
86
89
|
NORETURN(void raise_syserr(
|
87
90
|
int syserr_errno,
|