datadog 2.0.0.beta2 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +57 -1
- data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +1 -1
- 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/crashtracker.c +108 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +9 -23
- 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/http_transport.c +0 -93
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +86 -0
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +4 -0
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +2 -12
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +25 -86
- data/ext/datadog_profiling_native_extension/profiling.c +2 -0
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +3 -5
- 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/settings.rb +10 -79
- data/lib/datadog/core/remote/client.rb +1 -5
- data/lib/datadog/core/remote/configuration/repository.rb +1 -1
- data/lib/datadog/core/remote/dispatcher.rb +3 -3
- data/lib/datadog/core/telemetry/emitter.rb +1 -1
- data/lib/datadog/core/telemetry/http/response.rb +4 -0
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +18 -1
- data/lib/datadog/profiling/component.rb +26 -2
- data/lib/datadog/profiling/crashtracker.rb +91 -0
- data/lib/datadog/profiling/exporter.rb +6 -3
- data/lib/datadog/profiling/http_transport.rb +7 -11
- data/lib/datadog/profiling/profiler.rb +9 -2
- data/lib/datadog/profiling/stack_recorder.rb +6 -2
- data/lib/datadog/profiling.rb +1 -0
- data/lib/datadog/tracing/component.rb +5 -1
- data/lib/datadog/tracing/configuration/dynamic.rb +39 -1
- data/lib/datadog/tracing/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +1 -0
- data/lib/datadog/tracing/contrib/active_record/integration.rb +10 -0
- data/lib/datadog/tracing/contrib/configuration/resolver.rb +43 -0
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +1 -1
- data/lib/datadog/tracing/remote.rb +5 -1
- data/lib/datadog/tracing/sampling/ext.rb +5 -1
- data/lib/datadog/tracing/sampling/matcher.rb +60 -31
- data/lib/datadog/tracing/sampling/rule.rb +12 -5
- data/lib/datadog/tracing/sampling/rule_sampler.rb +17 -1
- data/lib/datadog/tracing/sampling/span/matcher.rb +13 -41
- data/lib/datadog/tracing/span_link.rb +12 -6
- data/lib/datadog/tracing/span_operation.rb +6 -4
- data/lib/datadog/version.rb +1 -1
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c8f4fef81d3a238299c70c27f6ece67dc31e76e4b6ad2180a2d1126e4c9545a
|
4
|
+
data.tar.gz: 17bc14574f265b2fdba65d4030e37e513f6af33af6ce8be77e1bbb535d4f6ae4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fff160e4a8e316ec82381e5ae6ef5687a434aa0798debcc689c37e127d688e3d0c0f1009befb6871096c77032caeed348df777ba9baab4c8f7d6f4582d02c8bd
|
7
|
+
data.tar.gz: 6fb4d250c8838a101a489cc10ee0672c23dd4a9793ad850b17bc2c375881c0e3e4cf2c7c3bf5645a6ce8bce1e59c06cbb82609dffe489101fb3ba158d655f034
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,44 @@
|
|
2
2
|
|
3
3
|
## [Unreleased]
|
4
4
|
|
5
|
+
## [2.0.0.rc1] - 2024-05-24
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
* Core: Add libdatadog crash tracker ([#3384][])
|
10
|
+
* OpenTelemetry: Add support for Span Links ([#3572][])
|
11
|
+
* Profiling: Enable endpoint profiling for Sidekiq and other background job processors ([#3610][])
|
12
|
+
* Tracing: Add dynamically configurable sampling rules ([#3598][])
|
13
|
+
* Tracing: Add sampling rule glob pattern matching ([#3616][])
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
|
17
|
+
* Appsec: Fix undefined method error when Tracing disabled ([#3645][])
|
18
|
+
* Profiling: Upgrade to libdatadog 9 ([#3627][])
|
19
|
+
* Tracing: Cache ActiveRecord configuration resolver ([#3630][])
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
|
23
|
+
* Core: Fix error during telemetry debug logging attempt ([#3617][])
|
24
|
+
* OpenTelemetry: Fix attribute merge with Datadog tags ([#3651][])
|
25
|
+
* Tracing: Fix environment logger repeated entries ([#3624][])
|
26
|
+
|
27
|
+
### Removed
|
28
|
+
|
29
|
+
* Profiling: Remove profiler support for Ruby 2.3 and 2.4 ([#3621][])
|
30
|
+
* Profiling: Remove deprecated profiler settings ([#3597][])
|
31
|
+
|
32
|
+
## [1.23.0] - 2024-05-09
|
33
|
+
|
34
|
+
### Added
|
35
|
+
|
36
|
+
* Profiling: Enable endpoint profiling for Sidekiq and similar background job processors ([#3619][])
|
37
|
+
|
38
|
+
### Fixed
|
39
|
+
|
40
|
+
* Fix no such file or directory issue when using single step instrumentation ([#3623][])
|
41
|
+
* Fix error during telemetry debug logging attempt ([#3618][])
|
42
|
+
|
5
43
|
## [2.0.0.beta2] - 2024-04-18
|
6
44
|
|
7
45
|
### Added
|
@@ -2850,7 +2888,9 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
|
|
2850
2888
|
Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
2851
2889
|
|
2852
2890
|
|
2853
|
-
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/
|
2891
|
+
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0.rc1...master
|
2892
|
+
[2.0.0.rc1]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0.beta2...v2.0.0.rc1
|
2893
|
+
[1.23.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.22.0...v1.23.0
|
2854
2894
|
[2.0.0.beta2]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0.beta1...v2.0.0.beta2
|
2855
2895
|
[1.22.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.21.1...v1.22.0
|
2856
2896
|
[2.0.0.beta1]: https://github.com/DataDog/dd-trace-rb/compare/v1.21.1...v2.0.0.beta1
|
@@ -4145,6 +4185,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
4145
4185
|
[#3370]: https://github.com/DataDog/dd-trace-rb/issues/3370
|
4146
4186
|
[#3373]: https://github.com/DataDog/dd-trace-rb/issues/3373
|
4147
4187
|
[#3374]: https://github.com/DataDog/dd-trace-rb/issues/3374
|
4188
|
+
[#3384]: https://github.com/DataDog/dd-trace-rb/issues/3384
|
4148
4189
|
[#3386]: https://github.com/DataDog/dd-trace-rb/issues/3386
|
4149
4190
|
[#3388]: https://github.com/DataDog/dd-trace-rb/issues/3388
|
4150
4191
|
[#3392]: https://github.com/DataDog/dd-trace-rb/issues/3392
|
@@ -4202,10 +4243,25 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
4202
4243
|
[#3551]: https://github.com/DataDog/dd-trace-rb/issues/3551
|
4203
4244
|
[#3558]: https://github.com/DataDog/dd-trace-rb/issues/3558
|
4204
4245
|
[#3565]: https://github.com/DataDog/dd-trace-rb/issues/3565
|
4246
|
+
[#3572]: https://github.com/DataDog/dd-trace-rb/issues/3572
|
4205
4247
|
[#3573]: https://github.com/DataDog/dd-trace-rb/issues/3573
|
4206
4248
|
[#3582]: https://github.com/DataDog/dd-trace-rb/issues/3582
|
4207
4249
|
[#3585]: https://github.com/DataDog/dd-trace-rb/issues/3585
|
4208
4250
|
[#3587]: https://github.com/DataDog/dd-trace-rb/issues/3587
|
4251
|
+
[#3597]: https://github.com/DataDog/dd-trace-rb/issues/3597
|
4252
|
+
[#3598]: https://github.com/DataDog/dd-trace-rb/issues/3598
|
4253
|
+
[#3610]: https://github.com/DataDog/dd-trace-rb/issues/3610
|
4254
|
+
[#3616]: https://github.com/DataDog/dd-trace-rb/issues/3616
|
4255
|
+
[#3617]: https://github.com/DataDog/dd-trace-rb/issues/3617
|
4256
|
+
[#3618]: https://github.com/DataDog/dd-trace-rb/issues/3618
|
4257
|
+
[#3619]: https://github.com/DataDog/dd-trace-rb/issues/3619
|
4258
|
+
[#3621]: https://github.com/DataDog/dd-trace-rb/issues/3621
|
4259
|
+
[#3623]: https://github.com/DataDog/dd-trace-rb/issues/3623
|
4260
|
+
[#3624]: https://github.com/DataDog/dd-trace-rb/issues/3624
|
4261
|
+
[#3627]: https://github.com/DataDog/dd-trace-rb/issues/3627
|
4262
|
+
[#3630]: https://github.com/DataDog/dd-trace-rb/issues/3630
|
4263
|
+
[#3645]: https://github.com/DataDog/dd-trace-rb/issues/3645
|
4264
|
+
[#3651]: https://github.com/DataDog/dd-trace-rb/issues/3651
|
4209
4265
|
[@AdrianLC]: https://github.com/AdrianLC
|
4210
4266
|
[@Azure7111]: https://github.com/Azure7111
|
4211
4267
|
[@BabyGroot]: https://github.com/BabyGroot
|
@@ -28,7 +28,7 @@ documentation.**
|
|
28
28
|
The profiling native extension is (and must always be) designed to **not cause failures** during gem installation, even
|
29
29
|
if some features, Ruby versions, or operating systems are not supported.
|
30
30
|
|
31
|
-
E.g. the extension must not break installation on Ruby 2.
|
31
|
+
E.g. the extension must not break installation on Ruby 2.5 (or the oldest Ruby version we support at the time) on 64-bit ARM macOS,
|
32
32
|
even if at run time it will effectively do nothing for such a setup.
|
33
33
|
|
34
34
|
We have a CI setup to help validate this, but this is really important to keep in mind when adding to or changing the
|
@@ -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.
|
@@ -0,0 +1,108 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <datadog/common.h>
|
3
|
+
#include <libdatadog_helpers.h>
|
4
|
+
|
5
|
+
static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
|
6
|
+
static VALUE _native_stop(DDTRACE_UNUSED VALUE _self);
|
7
|
+
|
8
|
+
// Used to report Ruby VM crashes.
|
9
|
+
// Once initialized, segfaults will be reported automatically using libdatadog.
|
10
|
+
|
11
|
+
void crashtracker_init(VALUE profiling_module) {
|
12
|
+
VALUE crashtracker_class = rb_define_class_under(profiling_module, "Crashtracker", rb_cObject);
|
13
|
+
|
14
|
+
rb_define_singleton_method(crashtracker_class, "_native_start_or_update_on_fork", _native_start_or_update_on_fork, -1);
|
15
|
+
rb_define_singleton_method(crashtracker_class, "_native_stop", _native_stop, 0);
|
16
|
+
}
|
17
|
+
|
18
|
+
static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self) {
|
19
|
+
VALUE options;
|
20
|
+
rb_scan_args(argc, argv, "0:", &options);
|
21
|
+
|
22
|
+
VALUE exporter_configuration = rb_hash_fetch(options, ID2SYM(rb_intern("exporter_configuration")));
|
23
|
+
VALUE path_to_crashtracking_receiver_binary = rb_hash_fetch(options, ID2SYM(rb_intern("path_to_crashtracking_receiver_binary")));
|
24
|
+
VALUE ld_library_path = rb_hash_fetch(options, ID2SYM(rb_intern("ld_library_path")));
|
25
|
+
VALUE tags_as_array = rb_hash_fetch(options, ID2SYM(rb_intern("tags_as_array")));
|
26
|
+
VALUE action = rb_hash_fetch(options, ID2SYM(rb_intern("action")));
|
27
|
+
VALUE upload_timeout_seconds = rb_hash_fetch(options, ID2SYM(rb_intern("upload_timeout_seconds")));
|
28
|
+
|
29
|
+
VALUE start_action = ID2SYM(rb_intern("start"));
|
30
|
+
VALUE update_on_fork_action = ID2SYM(rb_intern("update_on_fork"));
|
31
|
+
|
32
|
+
ENFORCE_TYPE(exporter_configuration, T_ARRAY);
|
33
|
+
ENFORCE_TYPE(tags_as_array, T_ARRAY);
|
34
|
+
ENFORCE_TYPE(path_to_crashtracking_receiver_binary, T_STRING);
|
35
|
+
ENFORCE_TYPE(ld_library_path, T_STRING);
|
36
|
+
ENFORCE_TYPE(action, T_SYMBOL);
|
37
|
+
ENFORCE_TYPE(upload_timeout_seconds, T_FIXNUM);
|
38
|
+
|
39
|
+
if (action != start_action && action != update_on_fork_action) rb_raise(rb_eArgError, "Unexpected action: %+"PRIsVALUE, action);
|
40
|
+
|
41
|
+
VALUE version = ddtrace_version();
|
42
|
+
ddog_prof_Endpoint endpoint = endpoint_from(exporter_configuration);
|
43
|
+
|
44
|
+
// Tags are heap-allocated, so after here we can't raise exceptions otherwise we'll leak this memory
|
45
|
+
// Start of exception-free zone to prevent leaks {{
|
46
|
+
ddog_Vec_Tag tags = convert_tags(tags_as_array);
|
47
|
+
|
48
|
+
ddog_prof_CrashtrackerConfiguration config = {
|
49
|
+
.additional_files = {},
|
50
|
+
// The Ruby VM already uses an alt stack to detect stack overflows so the crash handler must not overwrite it.
|
51
|
+
//
|
52
|
+
// @ivoanjo: Specifically, with `create_alt_stack = true` I saw a segfault, such as Ruby 2.6's bug with
|
53
|
+
// "Process.detach(fork { exit! }).instance_variable_get(:@foo)" being turned into a
|
54
|
+
// "-e:1:in `instance_variable_get': stack level too deep (SystemStackError)" by Ruby.
|
55
|
+
//
|
56
|
+
// The Ruby crash handler also seems to get confused when this option is enabled and
|
57
|
+
// "Process.kill('SEGV', Process.pid)" gets run.
|
58
|
+
.create_alt_stack = false,
|
59
|
+
.endpoint = endpoint,
|
60
|
+
.resolve_frames = DDOG_PROF_STACKTRACE_COLLECTION_ENABLED,
|
61
|
+
.timeout_secs = FIX2INT(upload_timeout_seconds),
|
62
|
+
};
|
63
|
+
|
64
|
+
ddog_prof_CrashtrackerMetadata metadata = {
|
65
|
+
.profiling_library_name = DDOG_CHARSLICE_C("dd-trace-rb"),
|
66
|
+
.profiling_library_version = char_slice_from_ruby_string(version),
|
67
|
+
.family = DDOG_CHARSLICE_C("ruby"),
|
68
|
+
.tags = &tags,
|
69
|
+
};
|
70
|
+
|
71
|
+
ddog_prof_EnvVar ld_library_path_env = {
|
72
|
+
.key = DDOG_CHARSLICE_C("LD_LIBRARY_PATH"),
|
73
|
+
.val = char_slice_from_ruby_string(ld_library_path),
|
74
|
+
};
|
75
|
+
|
76
|
+
ddog_prof_CrashtrackerReceiverConfig receiver_config = {
|
77
|
+
.args = {},
|
78
|
+
.env = {.ptr = &ld_library_path_env, .len = 1},
|
79
|
+
.path_to_receiver_binary = char_slice_from_ruby_string(path_to_crashtracking_receiver_binary),
|
80
|
+
.optional_stderr_filename = {},
|
81
|
+
.optional_stdout_filename = {},
|
82
|
+
};
|
83
|
+
|
84
|
+
ddog_prof_CrashtrackerResult result =
|
85
|
+
action == start_action ?
|
86
|
+
ddog_prof_Crashtracker_init(config, receiver_config, metadata) :
|
87
|
+
ddog_prof_Crashtracker_update_on_fork(config, receiver_config, metadata);
|
88
|
+
|
89
|
+
// Clean up before potentially raising any exceptions
|
90
|
+
ddog_Vec_Tag_drop(tags);
|
91
|
+
// }} End of exception-free zone to prevent leaks
|
92
|
+
|
93
|
+
if (result.tag == DDOG_PROF_CRASHTRACKER_RESULT_ERR) {
|
94
|
+
rb_raise(rb_eRuntimeError, "Failed to start/update the crash tracker: %"PRIsVALUE, get_error_details_and_drop(&result.err));
|
95
|
+
}
|
96
|
+
|
97
|
+
return Qtrue;
|
98
|
+
}
|
99
|
+
|
100
|
+
static VALUE _native_stop(DDTRACE_UNUSED VALUE _self) {
|
101
|
+
ddog_prof_CrashtrackerResult result = ddog_prof_Crashtracker_shutdown();
|
102
|
+
|
103
|
+
if (result.tag == DDOG_PROF_CRASHTRACKER_RESULT_ERR) {
|
104
|
+
rb_raise(rb_eRuntimeError, "Failed to stop the crash tracker: %"PRIsVALUE, get_error_details_and_drop(&result.err));
|
105
|
+
}
|
106
|
+
|
107
|
+
return Qtrue;
|
108
|
+
}
|
@@ -126,7 +126,7 @@ if RUBY_PLATFORM.include?('linux')
|
|
126
126
|
# have_library 'pthread'
|
127
127
|
# have_func 'pthread_getcpuclockid'
|
128
128
|
# ```
|
129
|
-
# but
|
129
|
+
# but it's slower to build
|
130
130
|
# so instead we just assume that we have the function we need on Linux, and nowhere else
|
131
131
|
$defs << '-DHAVE_PTHREAD_GETCPUCLOCKID'
|
132
132
|
end
|
@@ -163,6 +163,11 @@ $defs << '-DNO_THREAD_TID' if RUBY_VERSION < '3.1'
|
|
163
163
|
# On older Rubies, there was no jit_return member on the rb_control_frame_t struct
|
164
164
|
$defs << '-DNO_JIT_RETURN' if RUBY_VERSION < '3.1'
|
165
165
|
|
166
|
+
# On older Rubies, rb_gc_force_recycle allowed to free objects in a way that
|
167
|
+
# would be invisible to free tracepoints, finalizers and without cleaning
|
168
|
+
# obj_to_id_tbl mappings.
|
169
|
+
$defs << '-DHAVE_WORKING_RB_GC_FORCE_RECYCLE' if RUBY_VERSION < '3.1'
|
170
|
+
|
166
171
|
# On older Rubies, we need to use a backported version of this function. See private_vm_api_access.h for details.
|
167
172
|
$defs << '-DUSE_BACKPORTED_RB_PROFILE_FRAME_METHOD_NAME' if RUBY_VERSION < '3'
|
168
173
|
|
@@ -175,34 +180,15 @@ $defs << '-DNO_IMEMO_NAME' if RUBY_VERSION < '3'
|
|
175
180
|
# On older Rubies, objects would not move
|
176
181
|
$defs << '-DNO_T_MOVED' if RUBY_VERSION < '2.7'
|
177
182
|
|
183
|
+
# On older Rubies, there was no RUBY_SEEN_OBJ_ID flag
|
184
|
+
$defs << '-DNO_SEEN_OBJ_ID_FLAG' if RUBY_VERSION < '2.7'
|
185
|
+
|
178
186
|
# On older Rubies, rb_global_vm_lock_struct did not include the owner field
|
179
187
|
$defs << '-DNO_GVL_OWNER' if RUBY_VERSION < '2.6'
|
180
188
|
|
181
189
|
# On older Rubies, there was no thread->invoke_arg
|
182
190
|
$defs << '-DNO_THREAD_INVOKE_ARG' if RUBY_VERSION < '2.6'
|
183
191
|
|
184
|
-
# On older Rubies, we need to use rb_thread_t instead of rb_execution_context_t
|
185
|
-
$defs << '-DUSE_THREAD_INSTEAD_OF_EXECUTION_CONTEXT' if RUBY_VERSION < '2.5'
|
186
|
-
|
187
|
-
# On older Rubies, extensions can't use GET_VM()
|
188
|
-
$defs << '-DNO_GET_VM' if RUBY_VERSION < '2.5'
|
189
|
-
|
190
|
-
# On older Rubies...
|
191
|
-
if RUBY_VERSION < '2.4'
|
192
|
-
# ...we need to use RUBY_VM_NORMAL_ISEQ_P instead of VM_FRAME_RUBYFRAME_P
|
193
|
-
$defs << '-DUSE_ISEQ_P_INSTEAD_OF_RUBYFRAME_P'
|
194
|
-
# ...we use a legacy copy of rb_vm_frame_method_entry
|
195
|
-
$defs << '-DUSE_LEGACY_RB_VM_FRAME_METHOD_ENTRY'
|
196
|
-
end
|
197
|
-
|
198
|
-
# On older Rubies, rb_gc_force_recycle allowed to free objects in a way that
|
199
|
-
# would be invisible to free tracepoints, finalizers and without cleaning
|
200
|
-
# obj_to_id_tbl mappings.
|
201
|
-
$defs << '-DHAVE_WORKING_RB_GC_FORCE_RECYCLE' if RUBY_VERSION < '3.1'
|
202
|
-
|
203
|
-
# On older Rubies, there was no RUBY_SEEN_OBJ_ID flag
|
204
|
-
$defs << '-DNO_SEEN_OBJ_ID_FLAG' if RUBY_VERSION < '2.7'
|
205
|
-
|
206
192
|
# If we got here, libdatadog is available and loaded
|
207
193
|
ENV['PKG_CONFIG_PATH'] = "#{ENV['PKG_CONFIG_PATH']}:#{Libdatadog.pkgconfig_folder}"
|
208
194
|
Logging.message("[datadog] PKG_CONFIG_PATH set to #{ENV['PKG_CONFIG_PATH'].inspect}\n")
|
@@ -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
|
@@ -11,11 +11,6 @@
|
|
11
11
|
static VALUE ok_symbol = Qnil; // :ok in Ruby
|
12
12
|
static VALUE error_symbol = Qnil; // :error in Ruby
|
13
13
|
|
14
|
-
static ID agentless_id; // id of :agentless in Ruby
|
15
|
-
static ID agent_id; // id of :agent in Ruby
|
16
|
-
|
17
|
-
static ID log_failure_to_process_tag_id; // id of :log_failure_to_process_tag in Ruby
|
18
|
-
|
19
14
|
static VALUE library_version_string = Qnil;
|
20
15
|
|
21
16
|
struct call_exporter_without_gvl_arguments {
|
@@ -30,9 +25,6 @@ inline static ddog_ByteSlice byte_slice_from_ruby_string(VALUE string);
|
|
30
25
|
static VALUE _native_validate_exporter(VALUE self, VALUE exporter_configuration);
|
31
26
|
static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array);
|
32
27
|
static VALUE handle_exporter_failure(ddog_prof_Exporter_NewResult exporter_result);
|
33
|
-
static ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration);
|
34
|
-
static ddog_Vec_Tag convert_tags(VALUE tags_as_array);
|
35
|
-
static void safely_log_failure_to_process_tag(ddog_Vec_Tag tags, VALUE err_details);
|
36
28
|
static VALUE _native_do_export(
|
37
29
|
VALUE self,
|
38
30
|
VALUE exporter_configuration,
|
@@ -60,9 +52,6 @@ void http_transport_init(VALUE profiling_module) {
|
|
60
52
|
|
61
53
|
ok_symbol = ID2SYM(rb_intern_const("ok"));
|
62
54
|
error_symbol = ID2SYM(rb_intern_const("error"));
|
63
|
-
agentless_id = rb_intern_const("agentless");
|
64
|
-
agent_id = rb_intern_const("agent");
|
65
|
-
log_failure_to_process_tag_id = rb_intern_const("log_failure_to_process_tag");
|
66
55
|
|
67
56
|
library_version_string = ddtrace_version();
|
68
57
|
rb_global_variable(&library_version_string);
|
@@ -116,88 +105,6 @@ static VALUE handle_exporter_failure(ddog_prof_Exporter_NewResult exporter_resul
|
|
116
105
|
rb_ary_new_from_args(2, error_symbol, get_error_details_and_drop(&exporter_result.err));
|
117
106
|
}
|
118
107
|
|
119
|
-
static ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration) {
|
120
|
-
ENFORCE_TYPE(exporter_configuration, T_ARRAY);
|
121
|
-
|
122
|
-
ID working_mode = SYM2ID(rb_ary_entry(exporter_configuration, 0)); // SYM2ID verifies its input so we can do this safely
|
123
|
-
|
124
|
-
if (working_mode != agentless_id && working_mode != agent_id) {
|
125
|
-
rb_raise(rb_eArgError, "Failed to initialize transport: Unexpected working mode, expected :agentless or :agent");
|
126
|
-
}
|
127
|
-
|
128
|
-
if (working_mode == agentless_id) {
|
129
|
-
VALUE site = rb_ary_entry(exporter_configuration, 1);
|
130
|
-
VALUE api_key = rb_ary_entry(exporter_configuration, 2);
|
131
|
-
ENFORCE_TYPE(site, T_STRING);
|
132
|
-
ENFORCE_TYPE(api_key, T_STRING);
|
133
|
-
|
134
|
-
return ddog_prof_Endpoint_agentless(char_slice_from_ruby_string(site), char_slice_from_ruby_string(api_key));
|
135
|
-
} else { // agent_id
|
136
|
-
VALUE base_url = rb_ary_entry(exporter_configuration, 1);
|
137
|
-
ENFORCE_TYPE(base_url, T_STRING);
|
138
|
-
|
139
|
-
return ddog_prof_Endpoint_agent(char_slice_from_ruby_string(base_url));
|
140
|
-
}
|
141
|
-
}
|
142
|
-
|
143
|
-
__attribute__((warn_unused_result))
|
144
|
-
static ddog_Vec_Tag convert_tags(VALUE tags_as_array) {
|
145
|
-
ENFORCE_TYPE(tags_as_array, T_ARRAY);
|
146
|
-
|
147
|
-
long tags_count = RARRAY_LEN(tags_as_array);
|
148
|
-
ddog_Vec_Tag tags = ddog_Vec_Tag_new();
|
149
|
-
|
150
|
-
for (long i = 0; i < tags_count; i++) {
|
151
|
-
VALUE name_value_pair = rb_ary_entry(tags_as_array, i);
|
152
|
-
|
153
|
-
if (!RB_TYPE_P(name_value_pair, T_ARRAY)) {
|
154
|
-
ddog_Vec_Tag_drop(tags);
|
155
|
-
ENFORCE_TYPE(name_value_pair, T_ARRAY);
|
156
|
-
}
|
157
|
-
|
158
|
-
// Note: We can index the array without checking its size first because rb_ary_entry returns Qnil if out of bounds
|
159
|
-
VALUE tag_name = rb_ary_entry(name_value_pair, 0);
|
160
|
-
VALUE tag_value = rb_ary_entry(name_value_pair, 1);
|
161
|
-
|
162
|
-
if (!(RB_TYPE_P(tag_name, T_STRING) && RB_TYPE_P(tag_value, T_STRING))) {
|
163
|
-
ddog_Vec_Tag_drop(tags);
|
164
|
-
ENFORCE_TYPE(tag_name, T_STRING);
|
165
|
-
ENFORCE_TYPE(tag_value, T_STRING);
|
166
|
-
}
|
167
|
-
|
168
|
-
ddog_Vec_Tag_PushResult push_result =
|
169
|
-
ddog_Vec_Tag_push(&tags, char_slice_from_ruby_string(tag_name), char_slice_from_ruby_string(tag_value));
|
170
|
-
|
171
|
-
if (push_result.tag == DDOG_VEC_TAG_PUSH_RESULT_ERR) {
|
172
|
-
// libdatadog validates tags and may catch invalid tags that ddtrace didn't actually catch.
|
173
|
-
// We warn users about such tags, and then just ignore them.
|
174
|
-
safely_log_failure_to_process_tag(tags, get_error_details_and_drop(&push_result.err));
|
175
|
-
}
|
176
|
-
}
|
177
|
-
|
178
|
-
return tags;
|
179
|
-
}
|
180
|
-
|
181
|
-
static VALUE log_failure_to_process_tag(VALUE err_details) {
|
182
|
-
VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
|
183
|
-
VALUE profiling_module = rb_const_get(datadog_module, rb_intern("Profiling"));
|
184
|
-
VALUE http_transport_class = rb_const_get(profiling_module, rb_intern("HttpTransport"));
|
185
|
-
|
186
|
-
return rb_funcall(http_transport_class, log_failure_to_process_tag_id, 1, err_details);
|
187
|
-
}
|
188
|
-
|
189
|
-
// Since we are calling into Ruby code, it may raise an exception. This method ensure that dynamically-allocated tags
|
190
|
-
// get cleaned before propagating the exception.
|
191
|
-
static void safely_log_failure_to_process_tag(ddog_Vec_Tag tags, VALUE err_details) {
|
192
|
-
int exception_state;
|
193
|
-
rb_protect(log_failure_to_process_tag, err_details, &exception_state);
|
194
|
-
|
195
|
-
if (exception_state) { // An exception was raised
|
196
|
-
ddog_Vec_Tag_drop(tags); // clean up
|
197
|
-
rb_jump_tag(exception_state); // "Re-raise" exception
|
198
|
-
}
|
199
|
-
}
|
200
|
-
|
201
108
|
// Note: This function handles a bunch of libdatadog dynamically-allocated objects, so it MUST not use any Ruby APIs
|
202
109
|
// which can raise exceptions, otherwise the objects will be leaked.
|
203
110
|
static VALUE perform_export(
|