datadog 2.0.0.beta2 → 2.0.0
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 +65 -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 +23 -1
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +3 -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/correlation.rb +10 -6
- 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 -4
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 5008d4cb7f37cf4c4e1ed7956198b1fba9837c52f379b5d892898506ce4b2640
         | 
| 4 | 
            +
              data.tar.gz: 684c2b438ef7230e7f30ebd69b43041b2eea81413bbe2d4aedfe8140af01e955
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 1582a1f36cdf990613dc46e6febadb29f45275a363d32f4213237b5c8dc5be145218226639a590cb16e2fe7881a91b05dfb714c018e85fbb99f407376f68d71c
         | 
| 7 | 
            +
              data.tar.gz: e08d4d1645d47bd4fb8e4c6d03f088571bca24df622bc8e9da0d5ca696b33602fe28dc855e34da9f1783c62e4797916e1a0736f5d8fa29850d71d57a5508ec4e
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -2,6 +2,50 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            ## [Unreleased]
         | 
| 4 4 |  | 
| 5 | 
            +
            ## [2.0.0] - 2024-06-06
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ### Added
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            * Tracing: Remap http status code tag for otel span for trace metrics ([#3664][])
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            ## [2.0.0.rc1] - 2024-05-24
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            ### Added
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            * Core: Add libdatadog crash tracker ([#3384][])
         | 
| 16 | 
            +
            * OpenTelemetry: Add support for Span Links ([#3572][])
         | 
| 17 | 
            +
            * Profiling: Enable endpoint profiling for Sidekiq and other background job processors ([#3610][])
         | 
| 18 | 
            +
            * Tracing: Add dynamically configurable sampling rules ([#3598][])
         | 
| 19 | 
            +
            * Tracing: Add sampling rule glob pattern matching ([#3616][])
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            ### Changed
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            * Appsec: Fix undefined method error when Tracing disabled ([#3645][])
         | 
| 24 | 
            +
            * Profiling: Upgrade to libdatadog 9 ([#3627][])
         | 
| 25 | 
            +
            * Tracing: Cache ActiveRecord configuration resolver ([#3630][])
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            ### Fixed
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            * Core: Fix error during telemetry debug logging attempt ([#3617][])
         | 
| 30 | 
            +
            * OpenTelemetry: Fix attribute merge with Datadog tags ([#3651][])
         | 
| 31 | 
            +
            * Tracing: Fix environment logger repeated entries ([#3624][])
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            ### Removed
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            * Profiling: Remove profiler support for Ruby 2.3 and 2.4 ([#3621][])
         | 
| 36 | 
            +
            * Profiling: Remove deprecated profiler settings ([#3597][])
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            ## [1.23.0] - 2024-05-09
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            ### Added
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            * Profiling: Enable endpoint profiling for Sidekiq and similar background job processors ([#3619][])
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            ### Fixed
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            * Fix no such file or directory issue when using single step instrumentation ([#3623][])
         | 
| 47 | 
            +
            * Fix error during telemetry debug logging attempt ([#3618][])
         | 
| 48 | 
            +
             | 
| 5 49 | 
             
            ## [2.0.0.beta2] - 2024-04-18
         | 
| 6 50 |  | 
| 7 51 | 
             
            ### Added
         | 
| @@ -2850,7 +2894,10 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1 | |
| 2850 2894 | 
             
            Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
         | 
| 2851 2895 |  | 
| 2852 2896 |  | 
| 2853 | 
            -
            [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/ | 
| 2897 | 
            +
            [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0...master
         | 
| 2898 | 
            +
            [2.0.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0.rc1...v2.0.0
         | 
| 2899 | 
            +
            [2.0.0.rc1]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0.beta2...v2.0.0.rc1
         | 
| 2900 | 
            +
            [1.23.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.22.0...v1.23.0
         | 
| 2854 2901 | 
             
            [2.0.0.beta2]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0.beta1...v2.0.0.beta2
         | 
| 2855 2902 | 
             
            [1.22.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.21.1...v1.22.0
         | 
| 2856 2903 | 
             
            [2.0.0.beta1]: https://github.com/DataDog/dd-trace-rb/compare/v1.21.1...v2.0.0.beta1
         | 
| @@ -4145,6 +4192,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1 | |
| 4145 4192 | 
             
            [#3370]: https://github.com/DataDog/dd-trace-rb/issues/3370
         | 
| 4146 4193 | 
             
            [#3373]: https://github.com/DataDog/dd-trace-rb/issues/3373
         | 
| 4147 4194 | 
             
            [#3374]: https://github.com/DataDog/dd-trace-rb/issues/3374
         | 
| 4195 | 
            +
            [#3384]: https://github.com/DataDog/dd-trace-rb/issues/3384
         | 
| 4148 4196 | 
             
            [#3386]: https://github.com/DataDog/dd-trace-rb/issues/3386
         | 
| 4149 4197 | 
             
            [#3388]: https://github.com/DataDog/dd-trace-rb/issues/3388
         | 
| 4150 4198 | 
             
            [#3392]: https://github.com/DataDog/dd-trace-rb/issues/3392
         | 
| @@ -4202,10 +4250,26 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1 | |
| 4202 4250 | 
             
            [#3551]: https://github.com/DataDog/dd-trace-rb/issues/3551
         | 
| 4203 4251 | 
             
            [#3558]: https://github.com/DataDog/dd-trace-rb/issues/3558
         | 
| 4204 4252 | 
             
            [#3565]: https://github.com/DataDog/dd-trace-rb/issues/3565
         | 
| 4253 | 
            +
            [#3572]: https://github.com/DataDog/dd-trace-rb/issues/3572
         | 
| 4205 4254 | 
             
            [#3573]: https://github.com/DataDog/dd-trace-rb/issues/3573
         | 
| 4206 4255 | 
             
            [#3582]: https://github.com/DataDog/dd-trace-rb/issues/3582
         | 
| 4207 4256 | 
             
            [#3585]: https://github.com/DataDog/dd-trace-rb/issues/3585
         | 
| 4208 4257 | 
             
            [#3587]: https://github.com/DataDog/dd-trace-rb/issues/3587
         | 
| 4258 | 
            +
            [#3597]: https://github.com/DataDog/dd-trace-rb/issues/3597
         | 
| 4259 | 
            +
            [#3598]: https://github.com/DataDog/dd-trace-rb/issues/3598
         | 
| 4260 | 
            +
            [#3610]: https://github.com/DataDog/dd-trace-rb/issues/3610
         | 
| 4261 | 
            +
            [#3616]: https://github.com/DataDog/dd-trace-rb/issues/3616
         | 
| 4262 | 
            +
            [#3617]: https://github.com/DataDog/dd-trace-rb/issues/3617
         | 
| 4263 | 
            +
            [#3618]: https://github.com/DataDog/dd-trace-rb/issues/3618
         | 
| 4264 | 
            +
            [#3619]: https://github.com/DataDog/dd-trace-rb/issues/3619
         | 
| 4265 | 
            +
            [#3621]: https://github.com/DataDog/dd-trace-rb/issues/3621
         | 
| 4266 | 
            +
            [#3623]: https://github.com/DataDog/dd-trace-rb/issues/3623
         | 
| 4267 | 
            +
            [#3624]: https://github.com/DataDog/dd-trace-rb/issues/3624
         | 
| 4268 | 
            +
            [#3627]: https://github.com/DataDog/dd-trace-rb/issues/3627
         | 
| 4269 | 
            +
            [#3630]: https://github.com/DataDog/dd-trace-rb/issues/3630
         | 
| 4270 | 
            +
            [#3645]: https://github.com/DataDog/dd-trace-rb/issues/3645
         | 
| 4271 | 
            +
            [#3651]: https://github.com/DataDog/dd-trace-rb/issues/3651
         | 
| 4272 | 
            +
            [#3664]: https://github.com/DataDog/dd-trace-rb/issues/3664
         | 
| 4209 4273 | 
             
            [@AdrianLC]: https://github.com/AdrianLC
         | 
| 4210 4274 | 
             
            [@Azure7111]: https://github.com/Azure7111
         | 
| 4211 4275 | 
             
            [@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(
         |