datadog 2.8.0 → 2.9.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 +36 -1
- data/ext/datadog_profiling_native_extension/clock_id.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +64 -54
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +1 -1
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +16 -16
- data/ext/datadog_profiling_native_extension/collectors_stack.c +7 -7
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +219 -122
- data/ext/datadog_profiling_native_extension/heap_recorder.h +1 -1
- data/ext/datadog_profiling_native_extension/http_transport.c +4 -4
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +3 -0
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +3 -1
- data/ext/datadog_profiling_native_extension/profiling.c +10 -8
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +8 -8
- data/ext/datadog_profiling_native_extension/stack_recorder.c +54 -54
- data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -1
- data/ext/datadog_profiling_native_extension/time_helpers.h +1 -1
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +47 -0
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +31 -0
- data/ext/libdatadog_api/crashtracker.c +3 -0
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +355 -157
- data/lib/datadog/appsec/assets/waf_rules/strict.json +62 -32
- data/lib/datadog/appsec/context.rb +54 -0
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +7 -7
- data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +6 -6
- data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +4 -4
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +19 -28
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +5 -5
- data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +64 -96
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +10 -10
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +5 -5
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +6 -6
- data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +10 -11
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -49
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +21 -32
- data/lib/datadog/appsec/contrib/rails/patcher.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +6 -6
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +41 -63
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +2 -2
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +5 -5
- data/lib/datadog/appsec/event.rb +6 -6
- data/lib/datadog/appsec/ext.rb +3 -1
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +22 -32
- data/lib/datadog/appsec/monitor/reactive/set_user.rb +5 -5
- data/lib/datadog/appsec/processor/rule_loader.rb +0 -3
- data/lib/datadog/appsec.rb +3 -3
- data/lib/datadog/auto_instrument.rb +3 -0
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +39 -11
- data/lib/datadog/core/configuration/components.rb +4 -2
- data/lib/datadog/core/configuration.rb +1 -1
- data/lib/datadog/{tracing → core}/contrib/rails/utils.rb +1 -3
- data/lib/datadog/core/crashtracking/component.rb +1 -3
- data/lib/datadog/core/telemetry/event.rb +87 -3
- data/lib/datadog/core/telemetry/logging.rb +2 -2
- data/lib/datadog/core/telemetry/metric.rb +22 -0
- data/lib/datadog/core/telemetry/worker.rb +33 -0
- data/lib/datadog/di/base.rb +115 -0
- data/lib/datadog/di/code_tracker.rb +7 -4
- data/lib/datadog/di/component.rb +17 -11
- data/lib/datadog/di/configuration/settings.rb +11 -1
- data/lib/datadog/di/contrib/railtie.rb +15 -0
- data/lib/datadog/di/contrib.rb +26 -0
- data/lib/datadog/di/error.rb +5 -0
- data/lib/datadog/di/instrumenter.rb +39 -18
- data/lib/datadog/di/{init.rb → preload.rb} +2 -4
- data/lib/datadog/di/probe_manager.rb +4 -4
- data/lib/datadog/di/probe_notification_builder.rb +16 -2
- data/lib/datadog/di/probe_notifier_worker.rb +5 -6
- data/lib/datadog/di/remote.rb +4 -4
- data/lib/datadog/di/transport.rb +2 -4
- data/lib/datadog/di.rb +5 -108
- data/lib/datadog/kit/appsec/events.rb +3 -3
- data/lib/datadog/kit/identity.rb +4 -4
- data/lib/datadog/profiling/component.rb +55 -53
- data/lib/datadog/profiling/http_transport.rb +1 -26
- data/lib/datadog/tracing/contrib/action_cable/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/action_mailer/integration.rb +6 -2
- data/lib/datadog/tracing/contrib/action_pack/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/action_view/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/active_job/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/active_record/integration.rb +6 -2
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +3 -1
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +3 -1
- data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +10 -0
- data/lib/datadog/tracing/contrib/active_support/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/auto_instrument.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/httprb/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/kafka/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/mongodb/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/opensearch/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/presto/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/rack/integration.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/framework.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/rest_client/integration.rb +3 -0
- data/lib/datadog/tracing/span.rb +12 -4
- data/lib/datadog/tracing/span_event.rb +123 -3
- data/lib/datadog/tracing/span_operation.rb +6 -0
- data/lib/datadog/tracing/transport/serializable_trace.rb +24 -6
- data/lib/datadog/version.rb +1 -1
- metadata +19 -10
- data/lib/datadog/appsec/reactive/operation.rb +0 -68
- data/lib/datadog/appsec/scope.rb +0 -58
- data/lib/datadog/core/crashtracking/agent_base_url.rb +0 -21
| @@ -17,7 +17,7 @@ | |
| 17 17 | 
             
            typedef struct heap_recorder heap_recorder;
         | 
| 18 18 |  | 
| 19 19 | 
             
            // Extra data associated with each live object being tracked.
         | 
| 20 | 
            -
            typedef struct  | 
| 20 | 
            +
            typedef struct {
         | 
| 21 21 | 
             
              // The weight of this object from a sampling perspective.
         | 
| 22 22 | 
             
              //
         | 
| 23 23 | 
             
              // A notion of weight is preserved for each tracked object to allow for an approximate
         | 
| @@ -13,13 +13,13 @@ static VALUE error_symbol = Qnil; // :error in Ruby | |
| 13 13 |  | 
| 14 14 | 
             
            static VALUE library_version_string = Qnil;
         | 
| 15 15 |  | 
| 16 | 
            -
            struct  | 
| 16 | 
            +
            typedef struct {
         | 
| 17 17 | 
             
              ddog_prof_Exporter *exporter;
         | 
| 18 18 | 
             
              ddog_prof_Exporter_Request_BuildResult *build_result;
         | 
| 19 19 | 
             
              ddog_CancellationToken *cancel_token;
         | 
| 20 20 | 
             
              ddog_prof_Exporter_SendResult result;
         | 
| 21 21 | 
             
              bool send_ran;
         | 
| 22 | 
            -
            };
         | 
| 22 | 
            +
            } call_exporter_without_gvl_arguments;
         | 
| 23 23 |  | 
| 24 24 | 
             
            static inline ddog_ByteSlice byte_slice_from_ruby_string(VALUE string);
         | 
| 25 25 | 
             
            static VALUE _native_validate_exporter(VALUE self, VALUE exporter_configuration);
         | 
| @@ -165,7 +165,7 @@ static VALUE perform_export( | |
| 165 165 |  | 
| 166 166 | 
             
              // We'll release the Global VM Lock while we're calling send, so that the Ruby VM can continue to work while this
         | 
| 167 167 | 
             
              // is pending
         | 
| 168 | 
            -
               | 
| 168 | 
            +
              call_exporter_without_gvl_arguments args =
         | 
| 169 169 | 
             
                {.exporter = exporter, .build_result = &build_result, .cancel_token = cancel_token, .send_ran = false};
         | 
| 170 170 |  | 
| 171 171 | 
             
              // We use rb_thread_call_without_gvl2 instead of rb_thread_call_without_gvl as the gvl2 variant never raises any
         | 
| @@ -300,7 +300,7 @@ static VALUE _native_do_export( | |
| 300 300 | 
             
            }
         | 
| 301 301 |  | 
| 302 302 | 
             
            static void *call_exporter_without_gvl(void *call_args) {
         | 
| 303 | 
            -
               | 
| 303 | 
            +
              call_exporter_without_gvl_arguments *args = (call_exporter_without_gvl_arguments*) call_args;
         | 
| 304 304 |  | 
| 305 305 | 
             
              args->result = ddog_prof_Exporter_send(args->exporter, &args->build_result->ok, args->cancel_token);
         | 
| 306 306 | 
             
              args->send_ran = true;
         | 
| @@ -800,3 +800,6 @@ static inline int ddtrace_imemo_type(VALUE imemo) { | |
| 800 800 | 
             
                return current_thread;
         | 
| 801 801 | 
             
              }
         | 
| 802 802 | 
             
            #endif
         | 
| 803 | 
            +
             | 
| 804 | 
            +
            // Is the VM smack in the middle of raising an exception?
         | 
| 805 | 
            +
            bool is_raised_flag_set(VALUE thread) { return thread_struct_from_object(thread)->ec->raised_flag > 0; }
         | 
| @@ -18,7 +18,7 @@ typedef struct { | |
| 18 18 | 
             
              rb_nativethread_id_t owner;
         | 
| 19 19 | 
             
            } current_gvl_owner;
         | 
| 20 20 |  | 
| 21 | 
            -
            typedef struct  | 
| 21 | 
            +
            typedef struct {
         | 
| 22 22 | 
             
              union {
         | 
| 23 23 | 
             
                struct {
         | 
| 24 24 | 
             
                  VALUE iseq;
         | 
| @@ -68,3 +68,5 @@ const char *imemo_kind(VALUE imemo); | |
| 68 68 |  | 
| 69 69 | 
             
            #define ENFORCE_THREAD(value) \
         | 
| 70 70 | 
             
              { if (RB_UNLIKELY(!rb_typeddata_is_kind_of(value, RTYPEDDATA_TYPE(rb_thread_current())))) raise_unexpected_type(value, ADD_QUOTES(value), "Thread", __FILE__, __LINE__, __func__); }
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            bool is_raised_flag_set(VALUE thread);
         | 
| @@ -11,6 +11,7 @@ | |
| 11 11 | 
             
            #include "ruby_helpers.h"
         | 
| 12 12 | 
             
            #include "setup_signal_handler.h"
         | 
| 13 13 | 
             
            #include "time_helpers.h"
         | 
| 14 | 
            +
            #include "unsafe_api_calls_check.h"
         | 
| 14 15 |  | 
| 15 16 | 
             
            // Each class/module here is implemented in their separate file
         | 
| 16 17 | 
             
            void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module);
         | 
| @@ -56,6 +57,7 @@ void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) { | |
| 56 57 | 
             
              collectors_thread_context_init(profiling_module);
         | 
| 57 58 | 
             
              http_transport_init(profiling_module);
         | 
| 58 59 | 
             
              stack_recorder_init(profiling_module);
         | 
| 60 | 
            +
              unsafe_api_calls_check_init();
         | 
| 59 61 |  | 
| 60 62 | 
             
              // Hosts methods used for testing the native code using RSpec
         | 
| 61 63 | 
             
              VALUE testing_module = rb_define_module_under(native_extension_module, "Testing");
         | 
| @@ -83,16 +85,16 @@ static VALUE native_working_p(DDTRACE_UNUSED VALUE _self) { | |
| 83 85 | 
             
              return Qtrue;
         | 
| 84 86 | 
             
            }
         | 
| 85 87 |  | 
| 86 | 
            -
            struct  | 
| 88 | 
            +
            typedef struct {
         | 
| 87 89 | 
             
              VALUE exception_class;
         | 
| 88 90 | 
             
              char *test_message;
         | 
| 89 91 | 
             
              int test_message_arg;
         | 
| 90 | 
            -
            };
         | 
| 92 | 
            +
            } trigger_grab_gvl_and_raise_arguments;
         | 
| 91 93 |  | 
| 92 94 | 
             
            static VALUE _native_grab_gvl_and_raise(DDTRACE_UNUSED VALUE _self, VALUE exception_class, VALUE test_message, VALUE test_message_arg, VALUE release_gvl) {
         | 
| 93 95 | 
             
              ENFORCE_TYPE(test_message, T_STRING);
         | 
| 94 96 |  | 
| 95 | 
            -
               | 
| 97 | 
            +
              trigger_grab_gvl_and_raise_arguments args;
         | 
| 96 98 |  | 
| 97 99 | 
             
              args.exception_class = exception_class;
         | 
| 98 100 | 
             
              args.test_message = StringValueCStr(test_message);
         | 
| @@ -108,7 +110,7 @@ static VALUE _native_grab_gvl_and_raise(DDTRACE_UNUSED VALUE _self, VALUE except | |
| 108 110 | 
             
            }
         | 
| 109 111 |  | 
| 110 112 | 
             
            static void *trigger_grab_gvl_and_raise(void *trigger_args) {
         | 
| 111 | 
            -
               | 
| 113 | 
            +
              trigger_grab_gvl_and_raise_arguments *args = (trigger_grab_gvl_and_raise_arguments *) trigger_args;
         | 
| 112 114 |  | 
| 113 115 | 
             
              if (args->test_message_arg >= 0) {
         | 
| 114 116 | 
             
                grab_gvl_and_raise(args->exception_class, "%s%d", args->test_message, args->test_message_arg);
         | 
| @@ -119,16 +121,16 @@ static void *trigger_grab_gvl_and_raise(void *trigger_args) { | |
| 119 121 | 
             
              return NULL;
         | 
| 120 122 | 
             
            }
         | 
| 121 123 |  | 
| 122 | 
            -
            struct  | 
| 124 | 
            +
            typedef struct {
         | 
| 123 125 | 
             
              int syserr_errno;
         | 
| 124 126 | 
             
              char *test_message;
         | 
| 125 127 | 
             
              int test_message_arg;
         | 
| 126 | 
            -
            };
         | 
| 128 | 
            +
            } trigger_grab_gvl_and_raise_syserr_arguments;
         | 
| 127 129 |  | 
| 128 130 | 
             
            static VALUE _native_grab_gvl_and_raise_syserr(DDTRACE_UNUSED VALUE _self, VALUE syserr_errno, VALUE test_message, VALUE test_message_arg, VALUE release_gvl) {
         | 
| 129 131 | 
             
              ENFORCE_TYPE(test_message, T_STRING);
         | 
| 130 132 |  | 
| 131 | 
            -
               | 
| 133 | 
            +
              trigger_grab_gvl_and_raise_syserr_arguments args;
         | 
| 132 134 |  | 
| 133 135 | 
             
              args.syserr_errno = NUM2INT(syserr_errno);
         | 
| 134 136 | 
             
              args.test_message = StringValueCStr(test_message);
         | 
| @@ -144,7 +146,7 @@ static VALUE _native_grab_gvl_and_raise_syserr(DDTRACE_UNUSED VALUE _self, VALUE | |
| 144 146 | 
             
            }
         | 
| 145 147 |  | 
| 146 148 | 
             
            static void *trigger_grab_gvl_and_raise_syserr(void *trigger_args) {
         | 
| 147 | 
            -
               | 
| 149 | 
            +
              trigger_grab_gvl_and_raise_syserr_arguments *args = (trigger_grab_gvl_and_raise_syserr_arguments *) trigger_args;
         | 
| 148 150 |  | 
| 149 151 | 
             
              if (args->test_message_arg >= 0) {
         | 
| 150 152 | 
             
                grab_gvl_and_raise_syserr(args->syserr_errno, "%s%d", args->test_message, args->test_message_arg);
         | 
| @@ -23,18 +23,18 @@ void ruby_helpers_init(void) { | |
| 23 23 |  | 
| 24 24 | 
             
            #define MAX_RAISE_MESSAGE_SIZE 256
         | 
| 25 25 |  | 
| 26 | 
            -
            struct  | 
| 26 | 
            +
            typedef struct {
         | 
| 27 27 | 
             
              VALUE exception_class;
         | 
| 28 28 | 
             
              char exception_message[MAX_RAISE_MESSAGE_SIZE];
         | 
| 29 | 
            -
            };
         | 
| 29 | 
            +
            } raise_args;
         | 
| 30 30 |  | 
| 31 31 | 
             
            static void *trigger_raise(void *raise_arguments) {
         | 
| 32 | 
            -
               | 
| 32 | 
            +
              raise_args *args = (raise_args *) raise_arguments;
         | 
| 33 33 | 
             
              rb_raise(args->exception_class, "%s", args->exception_message);
         | 
| 34 34 | 
             
            }
         | 
| 35 35 |  | 
| 36 36 | 
             
            void grab_gvl_and_raise(VALUE exception_class, const char *format_string, ...) {
         | 
| 37 | 
            -
               | 
| 37 | 
            +
              raise_args args;
         | 
| 38 38 |  | 
| 39 39 | 
             
              args.exception_class = exception_class;
         | 
| 40 40 |  | 
| @@ -55,18 +55,18 @@ void grab_gvl_and_raise(VALUE exception_class, const char *format_string, ...) { | |
| 55 55 | 
             
              rb_bug("[ddtrace] Unexpected: Reached the end of grab_gvl_and_raise while raising '%s'\n", args.exception_message);
         | 
| 56 56 | 
             
            }
         | 
| 57 57 |  | 
| 58 | 
            -
            struct  | 
| 58 | 
            +
            typedef struct {
         | 
| 59 59 | 
             
              int syserr_errno;
         | 
| 60 60 | 
             
              char exception_message[MAX_RAISE_MESSAGE_SIZE];
         | 
| 61 | 
            -
            };
         | 
| 61 | 
            +
            } syserr_raise_args;
         | 
| 62 62 |  | 
| 63 63 | 
             
            static void *trigger_syserr_raise(void *syserr_raise_arguments) {
         | 
| 64 | 
            -
               | 
| 64 | 
            +
              syserr_raise_args *args = (syserr_raise_args *) syserr_raise_arguments;
         | 
| 65 65 | 
             
              rb_syserr_fail(args->syserr_errno, args->exception_message);
         | 
| 66 66 | 
             
            }
         | 
| 67 67 |  | 
| 68 68 | 
             
            void grab_gvl_and_raise_syserr(int syserr_errno, const char *format_string, ...) {
         | 
| 69 | 
            -
               | 
| 69 | 
            +
              syserr_raise_args args;
         | 
| 70 70 |  | 
| 71 71 | 
             
              args.syserr_errno = syserr_errno;
         | 
| 72 72 |  | 
| @@ -173,18 +173,18 @@ static const uint8_t all_value_types_positions[] = | |
| 173 173 |  | 
| 174 174 | 
             
            // Struct for storing stats related to a profile in a particular slot.
         | 
| 175 175 | 
             
            // These stats will share the same lifetime as the data in that profile slot.
         | 
| 176 | 
            -
            typedef struct  | 
| 176 | 
            +
            typedef struct {
         | 
| 177 177 | 
             
              // How many individual samples were recorded into this slot (un-weighted)
         | 
| 178 178 | 
             
              uint64_t recorded_samples;
         | 
| 179 179 | 
             
            } stats_slot;
         | 
| 180 180 |  | 
| 181 | 
            -
            typedef struct  | 
| 181 | 
            +
            typedef struct {
         | 
| 182 182 | 
             
              ddog_prof_Profile profile;
         | 
| 183 183 | 
             
              stats_slot stats;
         | 
| 184 184 | 
             
            } profile_slot;
         | 
| 185 185 |  | 
| 186 186 | 
             
            // Contains native state for each instance
         | 
| 187 | 
            -
            struct  | 
| 187 | 
            +
            typedef struct {
         | 
| 188 188 | 
             
              // Heap recorder instance
         | 
| 189 189 | 
             
              heap_recorder *heap_recorder;
         | 
| 190 190 | 
             
              bool heap_clean_after_gc_enabled;
         | 
| @@ -210,17 +210,17 @@ struct stack_recorder_state { | |
| 210 210 | 
             
                long serialization_time_ns_max;
         | 
| 211 211 | 
             
                uint64_t serialization_time_ns_total;
         | 
| 212 212 | 
             
              } stats_lifetime;
         | 
| 213 | 
            -
            };
         | 
| 213 | 
            +
            } stack_recorder_state;
         | 
| 214 214 |  | 
| 215 215 | 
             
            // Used to group mutex and the corresponding profile slot for easy unlocking after work is done.
         | 
| 216 | 
            -
            typedef struct  | 
| 216 | 
            +
            typedef struct {
         | 
| 217 217 | 
             
              pthread_mutex_t *mutex;
         | 
| 218 218 | 
             
              profile_slot *data;
         | 
| 219 219 | 
             
            } locked_profile_slot;
         | 
| 220 220 |  | 
| 221 | 
            -
            struct  | 
| 221 | 
            +
            typedef struct {
         | 
| 222 222 | 
             
              // Set by caller
         | 
| 223 | 
            -
               | 
| 223 | 
            +
              stack_recorder_state *state;
         | 
| 224 224 | 
             
              ddog_Timespec finish_timestamp;
         | 
| 225 225 |  | 
| 226 226 | 
             
              // Set by callee
         | 
| @@ -231,26 +231,26 @@ struct call_serialize_without_gvl_arguments { | |
| 231 231 |  | 
| 232 232 | 
             
              // Set by both
         | 
| 233 233 | 
             
              bool serialize_ran;
         | 
| 234 | 
            -
            };
         | 
| 234 | 
            +
            } call_serialize_without_gvl_arguments;
         | 
| 235 235 |  | 
| 236 236 | 
             
            static VALUE _native_new(VALUE klass);
         | 
| 237 | 
            -
            static void initialize_slot_concurrency_control( | 
| 238 | 
            -
            static void initialize_profiles( | 
| 237 | 
            +
            static void initialize_slot_concurrency_control(stack_recorder_state *state);
         | 
| 238 | 
            +
            static void initialize_profiles(stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types);
         | 
| 239 239 | 
             
            static void stack_recorder_typed_data_free(void *data);
         | 
| 240 240 | 
             
            static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
         | 
| 241 241 | 
             
            static VALUE _native_serialize(VALUE self, VALUE recorder_instance);
         | 
| 242 242 | 
             
            static VALUE ruby_time_from(ddog_Timespec ddprof_time);
         | 
| 243 243 | 
             
            static void *call_serialize_without_gvl(void *call_args);
         | 
| 244 | 
            -
            static locked_profile_slot sampler_lock_active_profile( | 
| 244 | 
            +
            static locked_profile_slot sampler_lock_active_profile(stack_recorder_state *state);
         | 
| 245 245 | 
             
            static void sampler_unlock_active_profile(locked_profile_slot active_slot);
         | 
| 246 | 
            -
            static profile_slot* serializer_flip_active_and_inactive_slots( | 
| 246 | 
            +
            static profile_slot* serializer_flip_active_and_inactive_slots(stack_recorder_state *state);
         | 
| 247 247 | 
             
            static VALUE _native_active_slot(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
         | 
| 248 248 | 
             
            static VALUE _native_is_slot_one_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
         | 
| 249 249 | 
             
            static VALUE _native_is_slot_two_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
         | 
| 250 250 | 
             
            static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot);
         | 
| 251 251 | 
             
            static ddog_Timespec system_epoch_now_timespec(void);
         | 
| 252 252 | 
             
            static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_instance);
         | 
| 253 | 
            -
            static void serializer_set_start_timestamp_for_next_profile( | 
| 253 | 
            +
            static void serializer_set_start_timestamp_for_next_profile(stack_recorder_state *state, ddog_Timespec start_time);
         | 
| 254 254 | 
             
            static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE local_root_span_id, VALUE endpoint);
         | 
| 255 255 | 
             
            static void reset_profile_slot(profile_slot *slot, ddog_Timespec *start_time /* Can be null */);
         | 
| 256 256 | 
             
            static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE new_obj, VALUE weight, VALUE alloc_class);
         | 
| @@ -316,7 +316,7 @@ static const rb_data_type_t stack_recorder_typed_data = { | |
| 316 316 | 
             
            };
         | 
| 317 317 |  | 
| 318 318 | 
             
            static VALUE _native_new(VALUE klass) {
         | 
| 319 | 
            -
               | 
| 319 | 
            +
              stack_recorder_state *state = ruby_xcalloc(1, sizeof(stack_recorder_state));
         | 
| 320 320 |  | 
| 321 321 | 
             
              // Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
         | 
| 322 322 | 
             
              // being leaked.
         | 
| @@ -354,7 +354,7 @@ static VALUE _native_new(VALUE klass) { | |
| 354 354 | 
             
              return stack_recorder;
         | 
| 355 355 | 
             
            }
         | 
| 356 356 |  | 
| 357 | 
            -
            static void initialize_slot_concurrency_control( | 
| 357 | 
            +
            static void initialize_slot_concurrency_control(stack_recorder_state *state) {
         | 
| 358 358 | 
             
              state->mutex_slot_one = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
         | 
| 359 359 | 
             
              state->mutex_slot_two = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
         | 
| 360 360 |  | 
| @@ -364,7 +364,7 @@ static void initialize_slot_concurrency_control(struct stack_recorder_state *sta | |
| 364 364 | 
             
              state->active_slot = 1;
         | 
| 365 365 | 
             
            }
         | 
| 366 366 |  | 
| 367 | 
            -
            static void initialize_profiles( | 
| 367 | 
            +
            static void initialize_profiles(stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types) {
         | 
| 368 368 | 
             
              ddog_prof_Profile_NewResult slot_one_profile_result =
         | 
| 369 369 | 
             
                ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
         | 
| 370 370 |  | 
| @@ -391,7 +391,7 @@ static void initialize_profiles(struct stack_recorder_state *state, ddog_prof_Sl | |
| 391 391 | 
             
            }
         | 
| 392 392 |  | 
| 393 393 | 
             
            static void stack_recorder_typed_data_free(void *state_ptr) {
         | 
| 394 | 
            -
               | 
| 394 | 
            +
              stack_recorder_state *state = (stack_recorder_state *) state_ptr;
         | 
| 395 395 |  | 
| 396 396 | 
             
              pthread_mutex_destroy(&state->mutex_slot_one);
         | 
| 397 397 | 
             
              ddog_prof_Profile_drop(&state->profile_slot_one.profile);
         | 
| @@ -426,8 +426,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel | |
| 426 426 | 
             
              ENFORCE_BOOLEAN(timeline_enabled);
         | 
| 427 427 | 
             
              ENFORCE_BOOLEAN(heap_clean_after_gc_enabled);
         | 
| 428 428 |  | 
| 429 | 
            -
               | 
| 430 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 429 | 
            +
              stack_recorder_state *state;
         | 
| 430 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 431 431 |  | 
| 432 432 | 
             
              state->heap_clean_after_gc_enabled = (heap_clean_after_gc_enabled == Qtrue);
         | 
| 433 433 |  | 
| @@ -517,8 +517,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel | |
| 517 517 | 
             
            }
         | 
| 518 518 |  | 
| 519 519 | 
             
            static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
         | 
| 520 | 
            -
               | 
| 521 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 520 | 
            +
              stack_recorder_state *state;
         | 
| 521 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 522 522 |  | 
| 523 523 | 
             
              ddog_Timespec finish_timestamp = system_epoch_now_timespec();
         | 
| 524 524 | 
             
              // Need to do this while still holding on to the Global VM Lock; see comments on method for why
         | 
| @@ -532,7 +532,7 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan | |
| 532 532 |  | 
| 533 533 | 
             
              // We'll release the Global VM Lock while we're calling serialize, so that the Ruby VM can continue to work while this
         | 
| 534 534 | 
             
              // is pending
         | 
| 535 | 
            -
               | 
| 535 | 
            +
              call_serialize_without_gvl_arguments args = {
         | 
| 536 536 | 
             
                .state = state,
         | 
| 537 537 | 
             
                .finish_timestamp = finish_timestamp,
         | 
| 538 538 | 
             
                .serialize_ran = false
         | 
| @@ -597,8 +597,8 @@ static VALUE ruby_time_from(ddog_Timespec ddprof_time) { | |
| 597 597 | 
             
            }
         | 
| 598 598 |  | 
| 599 599 | 
             
            void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations, sample_values values, sample_labels labels) {
         | 
| 600 | 
            -
               | 
| 601 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 600 | 
            +
              stack_recorder_state *state;
         | 
| 601 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 602 602 |  | 
| 603 603 | 
             
              locked_profile_slot active_slot = sampler_lock_active_profile(state);
         | 
| 604 604 |  | 
| @@ -652,8 +652,8 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations, | |
| 652 652 | 
             
            }
         | 
| 653 653 |  | 
| 654 654 | 
             
            void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample_weight, ddog_CharSlice *alloc_class) {
         | 
| 655 | 
            -
               | 
| 656 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 655 | 
            +
              stack_recorder_state *state;
         | 
| 656 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 657 657 | 
             
              // FIXME: Heap sampling currently has to be done in 2 parts because the construction of locations is happening
         | 
| 658 658 | 
             
              //        very late in the allocation-sampling path (which is shared with the cpu sampling path). This can
         | 
| 659 659 | 
             
              //        be fixed with some refactoring but for now this leads to a less impactful change.
         | 
| @@ -661,8 +661,8 @@ void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample | |
| 661 661 | 
             
            }
         | 
| 662 662 |  | 
| 663 663 | 
             
            void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_CharSlice endpoint) {
         | 
| 664 | 
            -
               | 
| 665 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 664 | 
            +
              stack_recorder_state *state;
         | 
| 665 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 666 666 |  | 
| 667 667 | 
             
              locked_profile_slot active_slot = sampler_lock_active_profile(state);
         | 
| 668 668 |  | 
| @@ -676,8 +676,8 @@ void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_ | |
| 676 676 | 
             
            }
         | 
| 677 677 |  | 
| 678 678 | 
             
            void recorder_after_gc_step(VALUE recorder_instance) {
         | 
| 679 | 
            -
               | 
| 680 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 679 | 
            +
              stack_recorder_state *state;
         | 
| 680 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 681 681 |  | 
| 682 682 | 
             
              if (state->heap_clean_after_gc_enabled) heap_recorder_update_young_objects(state->heap_recorder);
         | 
| 683 683 | 
             
            }
         | 
| @@ -687,7 +687,7 @@ void recorder_after_gc_step(VALUE recorder_instance) { | |
| 687 687 | 
             
            // Heap recorder iteration context allows us access to stack recorder state and profile being serialized
         | 
| 688 688 | 
             
            // during iteration of heap recorder live objects.
         | 
| 689 689 | 
             
            typedef struct heap_recorder_iteration_context {
         | 
| 690 | 
            -
               | 
| 690 | 
            +
              stack_recorder_state *state;
         | 
| 691 691 | 
             
              profile_slot *slot;
         | 
| 692 692 |  | 
| 693 693 | 
             
              bool error;
         | 
| @@ -749,7 +749,7 @@ static bool add_heap_sample_to_active_profile_without_gvl(heap_recorder_iteratio | |
| 749 749 | 
             
              return true;
         | 
| 750 750 | 
             
            }
         | 
| 751 751 |  | 
| 752 | 
            -
            static void build_heap_profile_without_gvl( | 
| 752 | 
            +
            static void build_heap_profile_without_gvl(stack_recorder_state *state, profile_slot *slot) {
         | 
| 753 753 | 
             
              heap_recorder_iteration_context iteration_context = {
         | 
| 754 754 | 
             
                .state = state,
         | 
| 755 755 | 
             
                .slot = slot,
         | 
| @@ -770,7 +770,7 @@ static void build_heap_profile_without_gvl(struct stack_recorder_state *state, p | |
| 770 770 | 
             
            }
         | 
| 771 771 |  | 
| 772 772 | 
             
            static void *call_serialize_without_gvl(void *call_args) {
         | 
| 773 | 
            -
               | 
| 773 | 
            +
              call_serialize_without_gvl_arguments *args = (call_serialize_without_gvl_arguments *) call_args;
         | 
| 774 774 |  | 
| 775 775 | 
             
              long serialize_no_gvl_start_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
         | 
| 776 776 |  | 
| @@ -796,7 +796,7 @@ VALUE enforce_recorder_instance(VALUE object) { | |
| 796 796 | 
             
              return object;
         | 
| 797 797 | 
             
            }
         | 
| 798 798 |  | 
| 799 | 
            -
            static locked_profile_slot sampler_lock_active_profile( | 
| 799 | 
            +
            static locked_profile_slot sampler_lock_active_profile(stack_recorder_state *state) {
         | 
| 800 800 | 
             
              int error;
         | 
| 801 801 |  | 
| 802 802 | 
             
              for (int attempts = 0; attempts < 2; attempts++) {
         | 
| @@ -823,7 +823,7 @@ static void sampler_unlock_active_profile(locked_profile_slot active_slot) { | |
| 823 823 | 
             
              ENFORCE_SUCCESS_GVL(pthread_mutex_unlock(active_slot.mutex));
         | 
| 824 824 | 
             
            }
         | 
| 825 825 |  | 
| 826 | 
            -
            static profile_slot* serializer_flip_active_and_inactive_slots( | 
| 826 | 
            +
            static profile_slot* serializer_flip_active_and_inactive_slots(stack_recorder_state *state) {
         | 
| 827 827 | 
             
              int previously_active_slot = state->active_slot;
         | 
| 828 828 |  | 
| 829 829 | 
             
              if (previously_active_slot != 1 && previously_active_slot != 2) {
         | 
| @@ -849,8 +849,8 @@ static profile_slot* serializer_flip_active_and_inactive_slots(struct stack_reco | |
| 849 849 | 
             
            // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
         | 
| 850 850 | 
             
            // It SHOULD NOT be used for other purposes.
         | 
| 851 851 | 
             
            static VALUE _native_active_slot(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
         | 
| 852 | 
            -
               | 
| 853 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 852 | 
            +
              stack_recorder_state *state;
         | 
| 853 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 854 854 |  | 
| 855 855 | 
             
              return INT2NUM(state->active_slot);
         | 
| 856 856 | 
             
            }
         | 
| @@ -864,8 +864,8 @@ static VALUE _native_is_slot_one_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE | |
| 864 864 | 
             
            static VALUE _native_is_slot_two_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) { return test_slot_mutex_state(recorder_instance, 2); }
         | 
| 865 865 |  | 
| 866 866 | 
             
            static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot) {
         | 
| 867 | 
            -
               | 
| 868 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 867 | 
            +
              stack_recorder_state *state;
         | 
| 868 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 869 869 |  | 
| 870 870 | 
             
              pthread_mutex_t *slot_mutex = (slot == 1) ? &state->mutex_slot_one : &state->mutex_slot_two;
         | 
| 871 871 |  | 
| @@ -895,8 +895,8 @@ static ddog_Timespec system_epoch_now_timespec(void) { | |
| 895 895 | 
             
            // Assumption: This method gets called BEFORE restarting profiling -- e.g. there are no components attempting to
         | 
| 896 896 | 
             
            // trigger samples at the same time.
         | 
| 897 897 | 
             
            static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_instance) {
         | 
| 898 | 
            -
               | 
| 899 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 898 | 
            +
              stack_recorder_state *state;
         | 
| 899 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 900 900 |  | 
| 901 901 | 
             
              // In case the fork happened halfway through `serializer_flip_active_and_inactive_slots` execution and the
         | 
| 902 902 | 
             
              // resulting state is inconsistent, we make sure to reset it back to the initial state.
         | 
| @@ -912,7 +912,7 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_ | |
| 912 912 |  | 
| 913 913 | 
             
            // Assumption 1: This method is called with the GVL being held, because `ddog_prof_Profile_reset` mutates the profile and must
         | 
| 914 914 | 
             
            // not be interrupted part-way through by a VM fork.
         | 
| 915 | 
            -
            static void serializer_set_start_timestamp_for_next_profile( | 
| 915 | 
            +
            static void serializer_set_start_timestamp_for_next_profile(stack_recorder_state *state, ddog_Timespec start_time) {
         | 
| 916 916 | 
             
              // Before making this profile active, we reset it so that it uses the correct start_time for its start
         | 
| 917 917 | 
             
              profile_slot *next_profile_slot = (state->active_slot == 1) ? &state->profile_slot_two : &state->profile_slot_one;
         | 
| 918 918 | 
             
              reset_profile_slot(next_profile_slot, &start_time);
         | 
| @@ -972,8 +972,8 @@ static void reset_profile_slot(profile_slot *slot, ddog_Timespec *start_time /* | |
| 972 972 | 
             
            // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
         | 
| 973 973 | 
             
            // It SHOULD NOT be used for other purposes.
         | 
| 974 974 | 
             
            static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
         | 
| 975 | 
            -
               | 
| 976 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 975 | 
            +
              stack_recorder_state *state;
         | 
| 976 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 977 977 |  | 
| 978 978 | 
             
              heap_recorder_prepare_iteration(state->heap_recorder);
         | 
| 979 979 |  | 
| @@ -983,8 +983,8 @@ static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _se | |
| 983 983 | 
             
            // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
         | 
| 984 984 | 
             
            // It SHOULD NOT be used for other purposes.
         | 
| 985 985 | 
             
            static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
         | 
| 986 | 
            -
               | 
| 987 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 986 | 
            +
              stack_recorder_state *state;
         | 
| 987 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 988 988 |  | 
| 989 989 | 
             
              heap_recorder_finish_iteration(state->heap_recorder);
         | 
| 990 990 |  | 
| @@ -994,15 +994,15 @@ static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self | |
| 994 994 | 
             
            // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
         | 
| 995 995 | 
             
            // It SHOULD NOT be used for other purposes.
         | 
| 996 996 | 
             
            static VALUE _native_debug_heap_recorder(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
         | 
| 997 | 
            -
               | 
| 998 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 997 | 
            +
              stack_recorder_state *state;
         | 
| 998 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 999 999 |  | 
| 1000 1000 | 
             
              return heap_recorder_testonly_debug(state->heap_recorder);
         | 
| 1001 1001 | 
             
            }
         | 
| 1002 1002 |  | 
| 1003 1003 | 
             
            static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE recorder_instance) {
         | 
| 1004 | 
            -
               | 
| 1005 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 1004 | 
            +
              stack_recorder_state *state;
         | 
| 1005 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 1006 1006 |  | 
| 1007 1007 | 
             
              uint64_t total_serializations = state->stats_lifetime.serialization_successes + state->stats_lifetime.serialization_failures;
         | 
| 1008 1008 |  | 
| @@ -1040,15 +1040,15 @@ static VALUE build_profile_stats(profile_slot *slot, long serialization_time_ns, | |
| 1040 1040 | 
             
            static VALUE _native_is_object_recorded(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE obj_id) {
         | 
| 1041 1041 | 
             
              ENFORCE_TYPE(obj_id, T_FIXNUM);
         | 
| 1042 1042 |  | 
| 1043 | 
            -
               | 
| 1044 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 1043 | 
            +
              stack_recorder_state *state;
         | 
| 1044 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 1045 1045 |  | 
| 1046 1046 | 
             
              return heap_recorder_testonly_is_object_recorded(state->heap_recorder, obj_id);
         | 
| 1047 1047 | 
             
            }
         | 
| 1048 1048 |  | 
| 1049 1049 | 
             
            static VALUE _native_heap_recorder_reset_last_update(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
         | 
| 1050 | 
            -
               | 
| 1051 | 
            -
              TypedData_Get_Struct(recorder_instance,  | 
| 1050 | 
            +
              stack_recorder_state *state;
         | 
| 1051 | 
            +
              TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
         | 
| 1052 1052 |  | 
| 1053 1053 | 
             
              heap_recorder_testonly_reset_last_update(state->heap_recorder);
         | 
| 1054 1054 |  | 
| @@ -13,7 +13,7 @@ typedef struct { | |
| 13 13 | 
             
              int64_t timeline_wall_time_ns;
         | 
| 14 14 | 
             
            } sample_values;
         | 
| 15 15 |  | 
| 16 | 
            -
            typedef struct  | 
| 16 | 
            +
            typedef struct {
         | 
| 17 17 | 
             
              ddog_prof_Slice_Label labels;
         | 
| 18 18 |  | 
| 19 19 | 
             
              // This is used to allow the `Collectors::Stack` to modify the existing label, if any. This MUST be NULL or point
         | 
| @@ -39,7 +39,7 @@ static inline long system_epoch_time_now_ns(raise_on_failure_setting raise_on_fa | |
| 39 39 | 
             
            // https://docs.redhat.com/en/documentation/red_hat_enterprise_linux_for_real_time/7/html/reference_guide/sect-posix_clocks#Using_clock_getres_to_compare_clock_resolution
         | 
| 40 40 | 
             
            // We introduce here a separate type for it, so as to make it harder to misuse/more explicit when these timestamps are used
         | 
| 41 41 |  | 
| 42 | 
            -
            typedef struct  | 
| 42 | 
            +
            typedef struct {
         | 
| 43 43 | 
             
              long timestamp_ns;
         | 
| 44 44 | 
             
            } coarse_instant;
         | 
| 45 45 |  | 
| @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            #include <ruby.h>
         | 
| 2 | 
            +
            #include <ruby/debug.h>
         | 
| 3 | 
            +
            #include <stdbool.h>
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            #include "datadog_ruby_common.h"
         | 
| 6 | 
            +
            #include "unsafe_api_calls_check.h"
         | 
| 7 | 
            +
            #include "extconf.h"
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            static bool inside_unsafe_context = false;
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            #ifndef NO_POSTPONED_TRIGGER
         | 
| 12 | 
            +
              static rb_postponed_job_handle_t check_for_unsafe_api_calls_handle;
         | 
| 13 | 
            +
            #endif
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            static void check_for_unsafe_api_calls(DDTRACE_UNUSED void *_unused);
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            void unsafe_api_calls_check_init(void) {
         | 
| 18 | 
            +
              #ifndef NO_POSTPONED_TRIGGER
         | 
| 19 | 
            +
                int unused_flags = 0;
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                check_for_unsafe_api_calls_handle = rb_postponed_job_preregister(unused_flags, check_for_unsafe_api_calls, NULL);
         | 
| 22 | 
            +
             | 
| 23 | 
            +
               if (check_for_unsafe_api_calls_handle == POSTPONED_JOB_HANDLE_INVALID) {
         | 
| 24 | 
            +
                 rb_raise(rb_eRuntimeError, "Failed to register check_for_unsafe_api_calls_handle postponed job (got POSTPONED_JOB_HANDLE_INVALID)");
         | 
| 25 | 
            +
               }
         | 
| 26 | 
            +
              #endif
         | 
| 27 | 
            +
            }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            void debug_enter_unsafe_context(void) {
         | 
| 30 | 
            +
              inside_unsafe_context = true;
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              #ifndef NO_POSTPONED_TRIGGER
         | 
| 33 | 
            +
                rb_postponed_job_trigger(check_for_unsafe_api_calls_handle);
         | 
| 34 | 
            +
              #else
         | 
| 35 | 
            +
                rb_postponed_job_register(0, check_for_unsafe_api_calls, NULL);
         | 
| 36 | 
            +
              #endif
         | 
| 37 | 
            +
            }
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            void debug_leave_unsafe_context(void) {
         | 
| 40 | 
            +
              inside_unsafe_context = false;
         | 
| 41 | 
            +
            }
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            static void check_for_unsafe_api_calls(DDTRACE_UNUSED void *_unused) {
         | 
| 44 | 
            +
              if (inside_unsafe_context) rb_bug(
         | 
| 45 | 
            +
                "Datadog Ruby profiler detected callback nested inside sample. Please report this at https://github.com/datadog/dd-trace-rb/blob/master/CONTRIBUTING.md#found-a-bug"
         | 
| 46 | 
            +
              );
         | 
| 47 | 
            +
            }
         | 
| @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            #pragma once
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            // This checker is used to detect accidental thread scheduling switching points happening during profiling sampling.
         | 
| 4 | 
            +
            //
         | 
| 5 | 
            +
            // Specifically, when the profiler is sampling, we're never supposed to call into Ruby code (e.g. methods
         | 
| 6 | 
            +
            // implemented using Ruby code) or allocate Ruby objects.
         | 
| 7 | 
            +
            // That's because those events introduce thread switch points, and really we don't the VM switching between threads
         | 
| 8 | 
            +
            // in the middle of the profiler sampling.
         | 
| 9 | 
            +
            // This includes raising exceptions, unless we're trying to stop the profiler, and even then we must be careful.
         | 
| 10 | 
            +
            //
         | 
| 11 | 
            +
            // The above is especially true in situations such as GC profiling or allocation/heap profiling, as in those situations
         | 
| 12 | 
            +
            // we can even crash the Ruby VM if we switch away at the wrong time.
         | 
| 13 | 
            +
            //
         | 
| 14 | 
            +
            // The below APIs can be used to detect these situations. They work by relying on the following observation:
         | 
| 15 | 
            +
            // in most (all?) thread switch points, Ruby will check for interrupts and run the postponed jobs.
         | 
| 16 | 
            +
            //
         | 
| 17 | 
            +
            // Thus, if we set a flag while we're sampling (inside_unsafe_context), trigger the postponed job, and then only unset
         | 
| 18 | 
            +
            // the flag after sampling, he correct thing to happen is that the postponed job should never see the flag.
         | 
| 19 | 
            +
            //
         | 
| 20 | 
            +
            // If, however, we have a bug and there's a thread switch point, our postponed job will see the flag and immediately
         | 
| 21 | 
            +
            // stop the Ruby VM before further damage happens (and hopefully giving us a stack trace clearly pointing to the culprit).
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            void unsafe_api_calls_check_init(void);
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            // IMPORTANT: This method **MUST** only be called from test code, as it causes an immediate hard-crash on the Ruby VM
         | 
| 26 | 
            +
            // when it detects a potential issue, and that's not something we want for production apps.
         | 
| 27 | 
            +
            //
         | 
| 28 | 
            +
            // In the future we may introduce some kind of setting (off by default) to also allow this to be safely be used
         | 
| 29 | 
            +
            // in production code if needed.
         | 
| 30 | 
            +
            void debug_enter_unsafe_context(void);
         | 
| 31 | 
            +
            void debug_leave_unsafe_context(void);
         | 
| @@ -54,6 +54,9 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS | |
| 54 54 | 
             
              // Tags and endpoint are heap-allocated, so after here we can't raise exceptions otherwise we'll leak this memory
         | 
| 55 55 | 
             
              // Start of exception-free zone to prevent leaks {{
         | 
| 56 56 | 
             
              ddog_Endpoint *endpoint = ddog_endpoint_from_url(char_slice_from_ruby_string(agent_base_url));
         | 
| 57 | 
            +
              if (endpoint == NULL) {
         | 
| 58 | 
            +
                rb_raise(rb_eRuntimeError, "Failed to create endpoint from agent_base_url: %"PRIsVALUE, agent_base_url);
         | 
| 59 | 
            +
              }
         | 
| 57 60 | 
             
              ddog_Vec_Tag tags = convert_tags(tags_as_array);
         | 
| 58 61 |  | 
| 59 62 | 
             
              ddog_crasht_Config config = {
         |