diff --git a/.circleci/images/primary/Dockerfile-3.3.0 b/.circleci/images/primary/Dockerfile-3.3.0 index 0439e1d453..50fac52623 100644 --- a/.circleci/images/primary/Dockerfile-3.3.0 +++ b/.circleci/images/primary/Dockerfile-3.3.0 @@ -1,6 +1,6 @@ # Note: See the "Publishing updates to images" note in ./README.md for how to publish new builds of this container image -FROM ruby:3.3-rc-bullseye +FROM ruby:3.3.0-bullseye # Make apt non-interactive RUN echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/90circleci \ diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 43a3fe9ade..b768003909 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -1145,8 +1145,8 @@ client.query("SELECT * FROM users WHERE group='x'") |-----------------------|--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------| | `service_name` | `DD_TRACE_MYSQL2_SERVICE_NAME` | Name of application running the `mysql2` instrumentation. May be overridden by `global_default_service_name`. [See *Additional Configuration* for more details](#additional-configuration) | `mysql2` | | `peer_service` | `DD_TRACE_MYSQL2_PEER_SERVICE` | Name of external service the application connects to | `nil` | -| `comment_propagation` | `DD_DBM_PROPAGATION_MODE` | SQL comment propagation mode for database monitoring.
(example: `disabled` \| `service`\| `full`).

**Important**: *Note that enabling sql comment propagation results in potentially confidential data (service names) being stored in the databases which can then be accessed by other 3rd parties that have been granted access to the database.* | `'disabled'` | - +| `comment_propagation` | `DD_DBM_PROPAGATION_MODE` | SQL comment propagation mode for database monitoring.
(example: `disabled` \| `service`\| `full`).

**Important**: *Note that enabling SQL comment propagation results in potentially confidential data (service names) being stored in the databases which can then be accessed by other third parties that have been granted access to the database.* | `'disabled'` | +| `on_error` | | Custom error handler invoked when MySQL raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring errors that are handled at the application level. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | ### Net/HTTP The Net/HTTP integration will trace any HTTP call using the standard lib Net::HTTP module. diff --git a/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c b/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c index 876dbd4afe..181cdd43b4 100644 --- a/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +++ b/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c @@ -75,6 +75,13 @@ // // --- +#ifndef NO_POSTPONED_TRIGGER + // Used to call the rb_postponed_job_trigger from Ruby 3.3+. These get initialized in + // `collectors_cpu_and_wall_time_worker_init` below and always get reused after that. + static rb_postponed_job_handle_t sample_from_postponed_job_handle; + static rb_postponed_job_handle_t after_gc_from_postponed_job_handle; +#endif + // Contains state for a single CpuAndWallTimeWorker instance struct cpu_and_wall_time_worker_state { // These are immutable after initialization @@ -212,6 +219,16 @@ __thread uint64_t allocation_count = 0; void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) { rb_global_variable(&active_sampler_instance); + #ifndef NO_POSTPONED_TRIGGER + int unused_flags = 0; + sample_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, sample_from_postponed_job, NULL); + after_gc_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gc_from_postponed_job, NULL); + + if (sample_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID || after_gc_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID) { + rb_raise(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)"); + } + #endif + VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors"); VALUE collectors_cpu_and_wall_time_worker_class = rb_define_class_under(collectors_module, "CpuAndWallTimeWorker", rb_cObject); // Hosts methods used for testing the native code using RSpec @@ -476,20 +493,25 @@ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED si // Note: If we ever want to get rid of rb_postponed_job_register_one, remember not to clobber Ruby exceptions, as // this function does this helpful job for us now -- https://github.com/ruby/ruby/commit/a98e343d39c4d7bf1e2190b076720f32d9f298b3. - int result = rb_postponed_job_register_one(0, sample_from_postponed_job, NULL); - - // Officially, the result of rb_postponed_job_register_one is documented as being opaque, but in practice it does not - // seem to have changed between Ruby 2.3 and 3.2, and so we track it as a debugging mechanism - switch (result) { - case 0: - state->stats.postponed_job_full++; break; - case 1: - state->stats.postponed_job_success++; break; - case 2: - state->stats.postponed_job_skipped_already_existed++; break; - default: - state->stats.postponed_job_unknown_result++; - } + #ifndef NO_POSTPONED_TRIGGER // Ruby 3.3+ + rb_postponed_job_trigger(sample_from_postponed_job_handle); + state->stats.postponed_job_success++; // Always succeeds + #else + int result = rb_postponed_job_register_one(0, sample_from_postponed_job, NULL); + + // Officially, the result of rb_postponed_job_register_one is documented as being opaque, but in practice it does not + // seem to have changed between Ruby 2.3 and 3.2, and so we track it as a debugging mechanism + switch (result) { + case 0: + state->stats.postponed_job_full++; break; + case 1: + state->stats.postponed_job_success++; break; + case 2: + state->stats.postponed_job_skipped_already_existed++; break; + default: + state->stats.postponed_job_unknown_result++; + } + #endif } // The actual sampling trigger loop always runs **without** the global vm lock. @@ -722,7 +744,13 @@ static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused) { // We use rb_postponed_job_register_one to ask Ruby to run thread_context_collector_sample_after_gc when the // thread collector flags it's time to flush. - if (should_flush) rb_postponed_job_register_one(0, after_gc_from_postponed_job, NULL); + if (should_flush) { + #ifndef NO_POSTPONED_TRIGGER // Ruby 3.3+ + rb_postponed_job_trigger(after_gc_from_postponed_job_handle); + #else + rb_postponed_job_register_one(0, after_gc_from_postponed_job, NULL); + #endif + } } } diff --git a/ext/ddtrace_profiling_native_extension/collectors_stack.c b/ext/ddtrace_profiling_native_extension/collectors_stack.c index 9bc0d11e0c..7cb073928e 100644 --- a/ext/ddtrace_profiling_native_extension/collectors_stack.c +++ b/ext/ddtrace_profiling_native_extension/collectors_stack.c @@ -11,9 +11,6 @@ // Gathers stack traces from running threads, storing them in a StackRecorder instance // This file implements the native bits of the Datadog::Profiling::Collectors::Stack class -#define MAX_FRAMES_LIMIT 10000 -#define MAX_FRAMES_LIMIT_AS_STRING "10000" - static VALUE missing_string = Qnil; // Used as scratch space during sampling @@ -37,13 +34,6 @@ static VALUE _native_sample( ); static void maybe_add_placeholder_frames_omitted(VALUE thread, sampling_buffer* buffer, char *frames_omitted_message, int frames_omitted_message_size); static void record_placeholder_stack_in_native_code(sampling_buffer* buffer, VALUE recorder_instance, sample_values values, sample_labels labels); -static void sample_thread_internal( - VALUE thread, - sampling_buffer* buffer, - VALUE recorder_instance, - sample_values values, - sample_labels labels -); void collectors_stack_init(VALUE profiling_module) { VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors"); diff --git a/ext/ddtrace_profiling_native_extension/collectors_stack.h b/ext/ddtrace_profiling_native_extension/collectors_stack.h index b026bbbdeb..816407ff8c 100644 --- a/ext/ddtrace_profiling_native_extension/collectors_stack.h +++ b/ext/ddtrace_profiling_native_extension/collectors_stack.h @@ -4,6 +4,9 @@ #include "stack_recorder.h" +#define MAX_FRAMES_LIMIT 10000 +#define MAX_FRAMES_LIMIT_AS_STRING "10000" + typedef struct sampling_buffer sampling_buffer; void sample_thread( diff --git a/ext/ddtrace_profiling_native_extension/collectors_thread_context.c b/ext/ddtrace_profiling_native_extension/collectors_thread_context.c index a86ce1dba9..cbfb06c3e5 100644 --- a/ext/ddtrace_profiling_native_extension/collectors_thread_context.c +++ b/ext/ddtrace_profiling_native_extension/collectors_thread_context.c @@ -1244,7 +1244,7 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in } } - track_object(state->recorder_instance, new_object, sample_weight); + track_object(state->recorder_instance, new_object, sample_weight, optional_class_name); trigger_sample_for_thread( state, diff --git a/ext/ddtrace_profiling_native_extension/extconf.rb b/ext/ddtrace_profiling_native_extension/extconf.rb index 10ceafdb4f..f1399ba38c 100644 --- a/ext/ddtrace_profiling_native_extension/extconf.rb +++ b/ext/ddtrace_profiling_native_extension/extconf.rb @@ -130,6 +130,9 @@ def add_compiler_flag(flag) $defs << '-DHAVE_PTHREAD_GETCPUCLOCKID' end +# On older Rubies, rb_postponed_job_preregister/rb_postponed_job_trigger did not exist +$defs << '-DNO_POSTPONED_TRIGGER' if RUBY_VERSION < '3.3' + # On older Rubies, M:N threads were not available $defs << '-DNO_MN_THREADS_AVAILABLE' if RUBY_VERSION < '3.3' diff --git a/ext/ddtrace_profiling_native_extension/heap_recorder.c b/ext/ddtrace_profiling_native_extension/heap_recorder.c index 627567c388..608d16b070 100644 --- a/ext/ddtrace_profiling_native_extension/heap_recorder.c +++ b/ext/ddtrace_profiling_native_extension/heap_recorder.c @@ -1,23 +1,136 @@ #include "heap_recorder.h" #include #include "ruby/st.h" -#include "ruby/util.h" #include "ruby_helpers.h" #include +#include "collectors_stack.h" +#include "libdatadog_helpers.h" -// Allows storing data passed to ::start_heap_allocation_recording to make it accessible to -// ::end_heap_allocation_recording. +// A compact representation of a stacktrace frame for a heap allocation. +typedef struct { + char *name; + char *filename; + int32_t line; +} heap_frame; +static st_index_t heap_frame_hash(heap_frame*, st_index_t seed); + +// A compact representation of a stacktrace for a heap allocation. // -// obj != Qnil flags this struct as holding a valid partial heap recording. +// We could use a ddog_prof_Slice_Location instead but it has a lot of +// unused fields. Because we have to keep these stacks around for at +// least the lifetime of the objects allocated therein, we would be +// incurring a non-negligible memory overhead for little purpose. typedef struct { - VALUE obj; + uint16_t frames_len; + heap_frame frames[]; +} heap_stack; +static heap_stack* heap_stack_new(ddog_prof_Slice_Location); +static void heap_stack_free(heap_stack*); +static st_index_t heap_stack_hash(heap_stack*, st_index_t); + +#if MAX_FRAMES_LIMIT > UINT16_MAX + #error Frames len type not compatible with MAX_FRAMES_LIMIT +#endif + +enum heap_record_key_type { + HEAP_STACK, + LOCATION_SLICE +}; +// This struct allows us to use two different types of stacks when +// interacting with a heap_record hash. +// +// The idea is that we'll always want to use heap_stack-keys when +// adding new entries to the hash since that's the compact stack +// representation we rely on internally. +// +// However, when querying for an existing heap record, we'd save a +// lot of allocations if we could query with the +// ddog_prof_Slice_Location we receive in our external API. +// +// To allow this interchange, we need a union and need to ensure +// that whatever shape of the union, the heap_record_key_cmp_st +// and heap_record_hash_st functions return the same results for +// equivalent stacktraces. +typedef struct { + enum heap_record_key_type type; + union { + // key never owns this if set + heap_stack *heap_stack; + // key never owns this if set + ddog_prof_Slice_Location *location_slice; + }; +} heap_record_key; +static heap_record_key* heap_record_key_new(heap_stack*); +static void heap_record_key_free(heap_record_key*); +static int heap_record_key_cmp_st(st_data_t, st_data_t); +static st_index_t heap_record_key_hash_st(st_data_t); +static const struct st_hash_type st_hash_type_heap_record_key = { + heap_record_key_cmp_st, + heap_record_key_hash_st, +}; + +// Need to implement these functions to support the location-slice based keys +static st_index_t ddog_location_hash(ddog_prof_Location, st_index_t seed); +static st_index_t ddog_location_slice_hash(ddog_prof_Slice_Location, st_index_t seed); + +// A heap record is used for deduping heap allocation stacktraces across multiple +// objects sharing the same allocation location. +typedef struct { + // How many objects are currently tracked by the heap recorder for this heap record. + uint32_t num_tracked_objects; + // stack is owned by the associated record and gets cleaned up alongside it + heap_stack *stack; +} heap_record; +static heap_record* heap_record_new(heap_stack*); +static void heap_record_free(heap_record*); + +// An object record is used for storing data about currently tracked live objects +typedef struct { + long obj_id; + heap_record *heap_record; live_object_data object_data; -} partial_heap_recording; +} object_record; +static object_record* object_record_new(long, heap_record*, live_object_data); +static void object_record_free(object_record*); struct heap_recorder { + // Map[key: heap_record_key*, record: heap_record*] + // NOTE: We always use heap_record_key.type == HEAP_STACK for storage but support lookups + // via heap_record_key.type == LOCATION_SLICE to allow for allocation-free fast-paths. + // NOTE: This table is currently only protected by the GVL since we never interact with it + // outside the GVL. + // NOTE: This table has ownership of both its heap_record_keys and heap_records. + st_table *heap_records; + + // Map[obj_id: long, record: object_record*] + // NOTE: This table is currently only protected by the GVL since we never interact with it + // outside the GVL. + // NOTE: This table has ownership of its object_records. The keys are longs and so are + // passed as values. + st_table *object_records; + + // Map[obj_id: long, record: object_record*] + // NOTE: This is a snapshot of object_records built ahead of a iteration. Outside of an + // iteration context, this table will be NULL. During an iteration, there will be no + // mutation of the data so iteration can occur without acquiring a lock. + // NOTE: Contrary to object_records, this table has no ownership of its data. + st_table *object_records_snapshot; + // Data for a heap recording that was started but not yet ended - partial_heap_recording active_recording; + object_record *partial_object_record; + + // Reusable location array, implementing a flyweight pattern for things like iteration. + ddog_prof_Location *reusable_locations; }; +static heap_record* get_or_create_heap_record(heap_recorder*, ddog_prof_Slice_Location); +static void cleanup_heap_record_if_unused(heap_recorder*, heap_record*); +static int st_heap_record_entry_free(st_data_t, st_data_t, st_data_t); +static int st_object_record_entry_free(st_data_t, st_data_t, st_data_t); +static int st_object_record_entry_free_if_invalid(st_data_t, st_data_t, st_data_t); +static int st_object_records_iterate(st_data_t, st_data_t, st_data_t); +static int st_object_records_debug(st_data_t key, st_data_t value, st_data_t extra); +static int update_object_record_entry(st_data_t*, st_data_t*, st_data_t, int); +static void commit_allocation(heap_recorder*, heap_record*, object_record*); // ========================== // Heap Recorder External API @@ -29,47 +142,90 @@ struct heap_recorder { // // ========================== heap_recorder* heap_recorder_new(void) { - heap_recorder* recorder = ruby_xmalloc(sizeof(heap_recorder)); + heap_recorder *recorder = ruby_xcalloc(1, sizeof(heap_recorder)); - recorder->active_recording = (partial_heap_recording) { - .obj = Qnil, - .object_data = {0}, - }; + recorder->heap_records = st_init_table(&st_hash_type_heap_record_key); + recorder->object_records = st_init_numtable(); + recorder->object_records_snapshot = NULL; + recorder->reusable_locations = ruby_xcalloc(MAX_FRAMES_LIMIT, sizeof(ddog_prof_Location)); + recorder->partial_object_record = NULL; return recorder; } -void heap_recorder_free(struct heap_recorder* recorder) { - if (recorder == NULL) { +void heap_recorder_free(heap_recorder *heap_recorder) { + if (heap_recorder == NULL) { return; } - ruby_xfree(recorder); -} + if (heap_recorder->object_records_snapshot != NULL) { + // if there's an unfinished iteration, clean it up now + // before we clean up any other state it might depend on + heap_recorder_finish_iteration(heap_recorder); + } + + // Clean-up all object records + st_foreach(heap_recorder->object_records, st_object_record_entry_free, 0); + st_free_table(heap_recorder->object_records); -// TODO: Remove when things get implemented -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" + // Clean-up all heap records (this includes those only referred to by queued_samples) + st_foreach(heap_recorder->heap_records, st_heap_record_entry_free, 0); + st_free_table(heap_recorder->heap_records); + if (heap_recorder->partial_object_record != NULL) { + // If there's a partial object record, clean it up as well + object_record_free(heap_recorder->partial_object_record); + } + + ruby_xfree(heap_recorder->reusable_locations); + + ruby_xfree(heap_recorder); +} + +// WARN: Assumes this gets called before profiler is reinitialized on the fork void heap_recorder_after_fork(heap_recorder *heap_recorder) { if (heap_recorder == NULL) { return; } - // TODO: Implement + // When forking, the child process gets a copy of the entire state of the parent process, minus + // threads. + // + // This means anything the heap recorder is tracking will still be alive after the fork and + // should thus be kept. Because this heap recorder implementation does not rely on free + // tracepoints to track liveness, any frees that happen until we fully reinitialize, will + // simply be noticed on next heap_recorder_prepare_iteration. + // + // There is one small caveat though: fork only preserves one thread and in a Ruby app, that + // will be the thread holding on to the GVL. Since we support iteration on the heap recorder + // outside of the GVL, any state specific to that interaction may be incosistent after fork + // (e.g. an acquired lock for thread safety). Iteration operates on object_records_snapshot + // though and that one will be updated on next heap_recorder_prepare_iteration so we really + // only need to finish any iteration that might have been left unfinished. + if (heap_recorder->object_records_snapshot != NULL) { + heap_recorder_finish_iteration(heap_recorder); + } } -void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight) { +void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice *alloc_class) { if (heap_recorder == NULL) { return; } - heap_recorder->active_recording = (partial_heap_recording) { - .obj = new_obj, - .object_data = (live_object_data) { - .weight = weight, - }, - }; + VALUE ruby_obj_id = rb_obj_id(new_obj); + if (!FIXNUM_P(ruby_obj_id)) { + rb_raise(rb_eRuntimeError, "Detected a bignum object id. These are not supported by heap profiling."); + } + + if (heap_recorder->partial_object_record != NULL) { + rb_raise(rb_eRuntimeError, "Detected consecutive heap allocation recording starts without end."); + } + + heap_recorder->partial_object_record = object_record_new(FIX2LONG(ruby_obj_id), NULL, (live_object_data) { + .weight = weight, + .class = alloc_class != NULL ? string_from_char_slice(*alloc_class) : NULL, + .alloc_gen = rb_gc_count(), + }); } void end_heap_allocation_recording(struct heap_recorder *heap_recorder, ddog_prof_Slice_Location locations) { @@ -77,41 +233,568 @@ void end_heap_allocation_recording(struct heap_recorder *heap_recorder, ddog_pro return; } - partial_heap_recording *active_recording = &heap_recorder->active_recording; + object_record *partial_object_record = heap_recorder->partial_object_record; - VALUE new_obj = active_recording->obj; - if (new_obj == Qnil) { + if (partial_object_record == NULL) { // Recording ended without having been started? rb_raise(rb_eRuntimeError, "Ended a heap recording that was not started"); } // From now on, mark active recording as invalid so we can short-circuit at any point and - // not end up with a still active recording. new_obj still holds the object for this recording - active_recording->obj = Qnil; + // not end up with a still active recording. partial_object_record still holds the object for this recording + heap_recorder->partial_object_record = NULL; + + heap_record *heap_record = get_or_create_heap_record(heap_recorder, locations); - // TODO: Implement + // And then commit the new allocation. + commit_allocation(heap_recorder, heap_record, partial_object_record); } -void heap_recorder_flush(heap_recorder *heap_recorder) { +void heap_recorder_prepare_iteration(heap_recorder *heap_recorder) { if (heap_recorder == NULL) { return; } - // TODO: Implement + if (heap_recorder->object_records_snapshot != NULL) { + // we could trivially handle this but we raise to highlight and catch unexpected usages. + rb_raise(rb_eRuntimeError, "New heap recorder iteration prepared without the previous one having been finished."); + } + + st_foreach(heap_recorder->object_records, st_object_record_entry_free_if_invalid, (st_data_t) heap_recorder); + + heap_recorder->object_records_snapshot = st_copy(heap_recorder->object_records); + if (heap_recorder->object_records_snapshot == NULL) { + rb_raise(rb_eRuntimeError, "Failed to create heap snapshot."); + } +} + +void heap_recorder_finish_iteration(heap_recorder *heap_recorder) { + if (heap_recorder == NULL) { + return; + } + + if (heap_recorder->object_records_snapshot == NULL) { + // we could trivially handle this but we raise to highlight and catch unexpected usages. + rb_raise(rb_eRuntimeError, "Heap recorder iteration finished without having been prepared."); + } + + st_free_table(heap_recorder->object_records_snapshot); + heap_recorder->object_records_snapshot = NULL; } -// WARN: If with_gvl = False, NO HEAP ALLOCATIONS, EXCEPTIONS or RUBY CALLS ARE ALLOWED. -void heap_recorder_for_each_live_object( +// Internal data we need while performing iteration over live objects. +typedef struct { + // The callback we need to call for each object. + bool (*for_each_callback)(heap_recorder_iteration_data stack_data, void *extra_arg); + // The extra arg to pass as the second parameter to the callback. + void *for_each_callback_extra_arg; + // A reference to the heap recorder so we can access extra stuff like reusable_locations. + heap_recorder *heap_recorder; +} iteration_context; + +// WARN: Assume iterations can run without the GVL for performance reasons. Do not raise, allocate or +// do NoGVL-unsafe interactions with the Ruby runtime. Any such interactions should be done during +// heap_recorder_prepare_iteration or heap_recorder_finish_iteration. +bool heap_recorder_for_each_live_object( heap_recorder *heap_recorder, bool (*for_each_callback)(heap_recorder_iteration_data stack_data, void *extra_arg), - void *for_each_callback_extra_arg, - bool with_gvl) { + void *for_each_callback_extra_arg) { if (heap_recorder == NULL) { + return true; + } + + if (heap_recorder->object_records_snapshot == NULL) { + return false; + } + + iteration_context context; + context.for_each_callback = for_each_callback; + context.for_each_callback_extra_arg = for_each_callback_extra_arg; + context.heap_recorder = heap_recorder; + st_foreach(heap_recorder->object_records_snapshot, st_object_records_iterate, (st_data_t) &context); + return true; +} + +void heap_recorder_testonly_assert_hash_matches(ddog_prof_Slice_Location locations) { + heap_stack *stack = heap_stack_new(locations); + heap_record_key stack_based_key = (heap_record_key) { + .type = HEAP_STACK, + .heap_stack = stack, + }; + heap_record_key location_based_key = (heap_record_key) { + .type = LOCATION_SLICE, + .location_slice = &locations, + }; + + st_index_t stack_hash = heap_record_key_hash_st((st_data_t) &stack_based_key); + st_index_t location_hash = heap_record_key_hash_st((st_data_t) &location_based_key); + + heap_stack_free(stack); + + if (stack_hash != location_hash) { + rb_raise(rb_eRuntimeError, "Heap record key hashes built from the same locations differ. stack_based_hash=%"PRI_VALUE_PREFIX"u location_based_hash=%"PRI_VALUE_PREFIX"u", stack_hash, location_hash); + } +} + +VALUE heap_recorder_testonly_debug(heap_recorder *heap_recorder) { + if (heap_recorder == NULL) { + return rb_str_new2("NULL heap_recorder"); + } + + VALUE debug_str = rb_str_new2("object records:\n"); + st_foreach(heap_recorder->object_records, st_object_records_debug, (st_data_t) debug_str); + return debug_str; +} + +// ========================== +// Heap Recorder Internal API +// ========================== +static int st_heap_record_entry_free(st_data_t key, st_data_t value, DDTRACE_UNUSED st_data_t extra_arg) { + heap_record_key *record_key = (heap_record_key*) key; + heap_record_key_free(record_key); + heap_record_free((heap_record *) value); + return ST_DELETE; +} + +static int st_object_record_entry_free(DDTRACE_UNUSED st_data_t key, st_data_t value, DDTRACE_UNUSED st_data_t extra_arg) { + object_record_free((object_record *) value); + return ST_DELETE; +} + +static int st_object_record_entry_free_if_invalid(st_data_t key, st_data_t value, st_data_t extra_arg) { + long obj_id = (long) key; + object_record *record = (object_record*) value; + heap_recorder *recorder = (heap_recorder*) extra_arg; + + if (!ruby_ref_from_id(LONG2NUM(obj_id), NULL)) { + // Id no longer associated with a valid ref. Need to clean things up! + + // Starting with the associated heap record. There will now be one less tracked object pointing to it + heap_record *heap_record = record->heap_record; + heap_record->num_tracked_objects--; + + // One less object using this heap record, it may have become unused... + cleanup_heap_record_if_unused(recorder, heap_record); + + object_record_free(record); + return ST_DELETE; + } + + return ST_CONTINUE; +} + +// WARN: This can get called outside the GVL. NO HEAP ALLOCATIONS OR EXCEPTIONS ARE ALLOWED. +static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t value, st_data_t extra) { + object_record *record = (object_record*) value; + const heap_stack *stack = record->heap_record->stack; + iteration_context *context = (iteration_context*) extra; + + ddog_prof_Location *locations = context->heap_recorder->reusable_locations; + + for (uint16_t i = 0; i < stack->frames_len; i++) { + const heap_frame *frame = &stack->frames[i]; + ddog_prof_Location *location = &locations[i]; + location->function.name.ptr = frame->name; + location->function.name.len = strlen(frame->name); + location->function.filename.ptr = frame->filename; + location->function.filename.len = strlen(frame->filename); + location->line = frame->line; + } + + heap_recorder_iteration_data iteration_data; + iteration_data.object_data = record->object_data; + iteration_data.locations = (ddog_prof_Slice_Location) {.ptr = locations, .len = stack->frames_len}; + + if (!context->for_each_callback(iteration_data, context->for_each_callback_extra_arg)) { + return ST_STOP; + } + + return ST_CONTINUE; +} + +static int st_object_records_debug(DDTRACE_UNUSED st_data_t key, st_data_t value, st_data_t extra) { + VALUE debug_str = (VALUE) extra; + + object_record *record = (object_record*) value; + + heap_frame top_frame = record->heap_record->stack->frames[0]; + rb_str_catf(debug_str, "obj_id=%ld weight=%d location=%s:%d alloc_gen=%zu ", record->obj_id, record->object_data.weight, top_frame.filename, (int) top_frame.line, record->object_data.alloc_gen); + + const char *class = record->object_data.class; + if (class != NULL) { + rb_str_catf(debug_str, "class=%s ", class); + } + + VALUE ref; + if (!ruby_ref_from_id(LONG2NUM(record->obj_id), &ref)) { + rb_str_catf(debug_str, "object="); + } else { + rb_str_catf(debug_str, "object=%+"PRIsVALUE, ref); + } + + rb_str_catf(debug_str, "\n"); + + return ST_CONTINUE; +} + +// Struct holding data required for an update operation on heap_records +typedef struct { + // [in] The new object record we want to add. + // NOTE: Transfer of ownership is assumed, do not re-use it after call to ::update_object_record_entry + object_record *new_object_record; + + // [in] The heap recorder where the update is happening. + heap_recorder *heap_recorder; +} object_record_update_data; + +static int update_object_record_entry(DDTRACE_UNUSED st_data_t *key, st_data_t *value, st_data_t data, int existing) { + object_record_update_data *update_data = (object_record_update_data*) data; + if (existing) { + rb_raise(rb_eRuntimeError, "Object ids are supposed to be unique. We got 2 allocation recordings with the same id"); + } + // Always carry on with the update, we want the new record to be there at the end + (*value) = (st_data_t) update_data->new_object_record; + return ST_CONTINUE; +} + +static void commit_allocation(heap_recorder *heap_recorder, heap_record *heap_record, object_record *object_record) { + // Link the object record with the corresponding heap record. + object_record->heap_record = heap_record; + + // Update object_records + object_record_update_data update_data = (object_record_update_data) { + .heap_recorder = heap_recorder, + .new_object_record = object_record, + }; + if (!st_update(heap_recorder->object_records, object_record->obj_id, update_object_record_entry, (st_data_t) &update_data)) { + // We are sure there was no previous record for this id so let the heap record know there now is one + // extra record associated with this stack. + if (heap_record->num_tracked_objects == UINT32_MAX) { + rb_raise(rb_eRuntimeError, "Reached maximum number of tracked objects for heap record"); + } + heap_record->num_tracked_objects++; + }; +} + +// Struct holding data required for an update operation on heap_records +typedef struct { + // [in] The locations we did this update with + ddog_prof_Slice_Location locations; + // [out] Pointer that will be updated to the updated heap record to prevent having to do + // another lookup to access the updated heap record. + heap_record **record; +} heap_record_update_data; + +// This function assumes ownership of stack_data is passed on to it so it'll either transfer ownership or clean-up. +static int update_heap_record_entry_with_new_allocation(st_data_t *key, st_data_t *value, st_data_t data, int existing) { + heap_record_update_data *update_data = (heap_record_update_data*) data; + + if (!existing) { + // there was no matching heap record so lets create a new one... + // we need to initialize a heap_record_key with a new stack and use that for the key storage. We can't use the + // locations-based key we used for the update call because we don't own its lifecycle. So we create a new + // heap stack and will pass ownership of it to the heap_record. + heap_stack *stack = heap_stack_new(update_data->locations); + (*key) = (st_data_t) heap_record_key_new(stack); + (*value) = (st_data_t) heap_record_new(stack); + } + + heap_record *record = (heap_record*) (*value); + (*update_data->record) = record; + + return ST_CONTINUE; +} + +static heap_record* get_or_create_heap_record(heap_recorder *heap_recorder, ddog_prof_Slice_Location locations) { + // For performance reasons we use a stack-allocated location-slice based key. This allows us + // to do allocation-free lookups and reuse of a matching existing heap record. + // NOTE: If we end up creating a new record, we'll create a heap-allocated key we own and use that for storage + // instead of this one. + heap_record_key lookup_key = (heap_record_key) { + .type = LOCATION_SLICE, + .location_slice = &locations, + }; + + heap_record *heap_record = NULL; + heap_record_update_data update_data = (heap_record_update_data) { + .locations = locations, + .record = &heap_record, + }; + st_update(heap_recorder->heap_records, (st_data_t) &lookup_key, update_heap_record_entry_with_new_allocation, (st_data_t) &update_data); + + return heap_record; +} + +static void cleanup_heap_record_if_unused(heap_recorder *heap_recorder, heap_record *heap_record) { + if (heap_record->num_tracked_objects > 0) { + // still being used! do nothing... return; } - // TODO: Implement + heap_record_key heap_key = (heap_record_key) { + .type = HEAP_STACK, + .heap_stack = heap_record->stack, + }; + // We need to access the deleted key to free it since we gave ownership of the keys to the hash. + // st_delete will change this pointer to point to the removed key if one is found. + heap_record_key *deleted_key = &heap_key; + if (!st_delete(heap_recorder->heap_records, (st_data_t*) &deleted_key, NULL)) { + rb_raise(rb_eRuntimeError, "Attempted to cleanup an untracked heap_record"); + }; + heap_record_key_free(deleted_key); + heap_record_free(heap_record); +} + +// =============== +// Heap Record API +// =============== +heap_record* heap_record_new(heap_stack *stack) { + heap_record *record = ruby_xcalloc(1, sizeof(heap_record)); + record->num_tracked_objects = 0; + record->stack = stack; + return record; +} + +void heap_record_free(heap_record *record) { + heap_stack_free(record->stack); + ruby_xfree(record); +} + + +// ================= +// Object Record API +// ================= +object_record* object_record_new(long obj_id, heap_record *heap_record, live_object_data object_data) { + object_record *record = ruby_xcalloc(1, sizeof(object_record)); + record->obj_id = obj_id; + record->heap_record = heap_record; + record->object_data = object_data; + return record; +} + +void object_record_free(object_record *record) { + if (record->object_data.class != NULL) { + ruby_xfree(record->object_data.class); + } + ruby_xfree(record); +} + +// ============== +// Heap Frame API +// ============== +int heap_frame_cmp(heap_frame *f1, heap_frame *f2) { + int line_diff = (int) (f1->line - f2->line); + if (line_diff != 0) { + return line_diff; + } + int cmp = strcmp(f1->name, f2->name); + if (cmp != 0) { + return cmp; + } + return strcmp(f1->filename, f2->filename); +} + +// TODO: Research potential performance improvements around hashing stuff here +// once we have a benchmarking suite. +// Example: Each call to st_hash is calling murmur_finish and we may want +// to only finish once per structure, not per field? +// Example: There may be a more efficient hashing for line that is not the +// generic st_hash algorithm? + +// WARN: Must be kept in-sync with ::char_slice_hash +st_index_t string_hash(char *str, st_index_t seed) { + return st_hash(str, strlen(str), seed); } -// TODO: Remove when things get implemented -#pragma GCC diagnostic pop +// WARN: Must be kept in-sync with ::string_hash +st_index_t char_slice_hash(ddog_CharSlice char_slice, st_index_t seed) { + return st_hash(char_slice.ptr, char_slice.len, seed); +} + +// WARN: Must be kept in-sync with ::ddog_location_hash +st_index_t heap_frame_hash(heap_frame *frame, st_index_t seed) { + st_index_t hash = string_hash(frame->name, seed); + hash = string_hash(frame->filename, hash); + hash = st_hash(&frame->line, sizeof(frame->line), hash); + return hash; +} + +// WARN: Must be kept in-sync with ::heap_frame_hash +st_index_t ddog_location_hash(ddog_prof_Location location, st_index_t seed) { + st_index_t hash = char_slice_hash(location.function.name, seed); + hash = char_slice_hash(location.function.filename, hash); + // Convert ddog_prof line type to the same type we use for our heap_frames to + // ensure we have compatible hashes + int32_t line_as_int32 = (int32_t) location.line; + hash = st_hash(&line_as_int32, sizeof(line_as_int32), hash); + return hash; +} + +// ============== +// Heap Stack API +// ============== +heap_stack* heap_stack_new(ddog_prof_Slice_Location locations) { + uint16_t frames_len = locations.len; + if (frames_len > MAX_FRAMES_LIMIT) { + // This should not be happening anyway since MAX_FRAMES_LIMIT should be shared with + // the stacktrace construction mechanism. If it happens, lets just raise. This should + // be safe since only allocate with the GVL anyway. + rb_raise(rb_eRuntimeError, "Found stack with more than %d frames (%d)", MAX_FRAMES_LIMIT, frames_len); + } + heap_stack *stack = ruby_xcalloc(1, sizeof(heap_stack) + frames_len * sizeof(heap_frame)); + stack->frames_len = frames_len; + for (uint16_t i = 0; i < stack->frames_len; i++) { + const ddog_prof_Location *location = &locations.ptr[i]; + stack->frames[i] = (heap_frame) { + .name = string_from_char_slice(location->function.name), + .filename = string_from_char_slice(location->function.filename), + // ddog_prof_Location is a int64_t. We don't expect to have to profile files with more than + // 2M lines so this cast should be fairly safe? + .line = (int32_t) location->line, + }; + } + return stack; +} + +void heap_stack_free(heap_stack *stack) { + for (uint64_t i = 0; i < stack->frames_len; i++) { + heap_frame *frame = &stack->frames[i]; + ruby_xfree(frame->name); + ruby_xfree(frame->filename); + } + ruby_xfree(stack); +} + +// WARN: Must be kept in-sync with ::ddog_location_slice_hash +st_index_t heap_stack_hash(heap_stack *stack, st_index_t seed) { + st_index_t hash = seed; + for (uint64_t i = 0; i < stack->frames_len; i++) { + hash = heap_frame_hash(&stack->frames[i], hash); + } + return hash; +} + +// WARN: Must be kept in-sync with ::heap_stack_hash +st_index_t ddog_location_slice_hash(ddog_prof_Slice_Location locations, st_index_t seed) { + st_index_t hash = seed; + for (uint64_t i = 0; i < locations.len; i++) { + hash = ddog_location_hash(locations.ptr[i], hash); + } + return hash; +} + +// =================== +// Heap Record Key API +// =================== +heap_record_key* heap_record_key_new(heap_stack *stack) { + heap_record_key *key = ruby_xmalloc(sizeof(heap_record_key)); + key->type = HEAP_STACK; + key->heap_stack = stack; + return key; +} + +void heap_record_key_free(heap_record_key *key) { + ruby_xfree(key); +} + +static inline size_t heap_record_key_len(heap_record_key *key) { + if (key->type == HEAP_STACK) { + return key->heap_stack->frames_len; + } else { + return key->location_slice->len; + } +} + +static inline int64_t heap_record_key_entry_line(heap_record_key *key, size_t entry_i) { + if (key->type == HEAP_STACK) { + return key->heap_stack->frames[entry_i].line; + } else { + return key->location_slice->ptr[entry_i].line; + } +} + +static inline size_t heap_record_key_entry_name(heap_record_key *key, size_t entry_i, const char **name_ptr) { + if (key->type == HEAP_STACK) { + char *name = key->heap_stack->frames[entry_i].name; + (*name_ptr) = name; + return strlen(name); + } else { + ddog_CharSlice name = key->location_slice->ptr[entry_i].function.name; + (*name_ptr) = name.ptr; + return name.len; + } +} + +static inline size_t heap_record_key_entry_filename(heap_record_key *key, size_t entry_i, const char **filename_ptr) { + if (key->type == HEAP_STACK) { + char *filename = key->heap_stack->frames[entry_i].filename; + (*filename_ptr) = filename; + return strlen(filename); + } else { + ddog_CharSlice filename = key->location_slice->ptr[entry_i].function.filename; + (*filename_ptr) = filename.ptr; + return filename.len; + } +} + +int heap_record_key_cmp_st(st_data_t key1, st_data_t key2) { + heap_record_key *key_record1 = (heap_record_key*) key1; + heap_record_key *key_record2 = (heap_record_key*) key2; + + // Fast path, check if lengths differ + size_t key_record1_len = heap_record_key_len(key_record1); + size_t key_record2_len = heap_record_key_len(key_record2); + + if (key_record1_len != key_record2_len) { + return ((int) key_record1_len) - ((int) key_record2_len); + } + + // If we got this far, we have same lengths so need to check item-by-item + for (size_t i = 0; i < key_record1_len; i++) { + // Lines are faster to compare, lets do that first + size_t line1 = heap_record_key_entry_line(key_record1, i); + size_t line2 = heap_record_key_entry_line(key_record2, i); + if (line1 != line2) { + return ((int) line1) - ((int)line2); + } + + // Then come names, they are usually smaller than filenames + const char *name1, *name2; + size_t name1_len = heap_record_key_entry_name(key_record1, i, &name1); + size_t name2_len = heap_record_key_entry_name(key_record2, i, &name2); + if (name1_len != name2_len) { + return ((int) name1_len) - ((int) name2_len); + } + int name_cmp_result = strncmp(name1, name2, name1_len); + if (name_cmp_result != 0) { + return name_cmp_result; + } + + // Then come filenames + const char *filename1, *filename2; + int64_t filename1_len = heap_record_key_entry_filename(key_record1, i, &filename1); + int64_t filename2_len = heap_record_key_entry_filename(key_record2, i, &filename2); + if (filename1_len != filename2_len) { + return ((int) filename1_len) - ((int) filename2_len); + } + int filename_cmp_result = strncmp(filename1, filename2, filename1_len); + if (filename_cmp_result != 0) { + return filename_cmp_result; + } + } + + // If we survived the above for, then everything matched + return 0; +} + +// Initial seed for hash functions +#define FNV1_32A_INIT 0x811c9dc5 + +st_index_t heap_record_key_hash_st(st_data_t key) { + heap_record_key *record_key = (heap_record_key*) key; + if (record_key->type == HEAP_STACK) { + return heap_stack_hash(record_key->heap_stack, FNV1_32A_INIT); + } else { + return ddog_location_slice_hash(*record_key->location_slice, FNV1_32A_INIT); + } +} diff --git a/ext/ddtrace_profiling_native_extension/heap_recorder.h b/ext/ddtrace_profiling_native_extension/heap_recorder.h index add47d4673..768b24522f 100644 --- a/ext/ddtrace_profiling_native_extension/heap_recorder.h +++ b/ext/ddtrace_profiling_native_extension/heap_recorder.h @@ -26,6 +26,15 @@ typedef struct live_object_data { // Example: If we were sampling every 50 objects, then each sampled object // could be seen as being representative of 50 objects. unsigned int weight; + + // The class of the object that we're tracking. + // NOTE: This is optional and will be set to NULL if not set. + char* class; + + // The GC allocation gen in which we saw this object being allocated. + // + // This enables us to calculate the age of this object in terms of GC executions. + size_t alloc_gen; } live_object_data; // Data that is made available to iterators of heap recorder data for each live object @@ -42,6 +51,7 @@ heap_recorder* heap_recorder_new(void); void heap_recorder_free(heap_recorder *heap_recorder); // Do any cleanup needed after forking. +// WARN: Assumes this gets called before profiler is reinitialized on the fork void heap_recorder_after_fork(heap_recorder *heap_recorder); // Start a heap allocation recording on the heap recorder for a new object. @@ -55,7 +65,7 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder); // The sampling weight of this object. // // WARN: It needs to be paired with a ::end_heap_allocation_recording call. -void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight); +void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice *alloc_class); // End a previously started heap allocation recording on the heap recorder. // @@ -66,13 +76,25 @@ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj // WARN: It is illegal to call this without previously having called ::start_heap_allocation_recording. void end_heap_allocation_recording(heap_recorder *heap_recorder, ddog_prof_Slice_Location locations); -// Flush any intermediate state that might be queued inside the heap recorder. +// Update the heap recorder to reflect the latest state of the VM and prepare internal structures +// for efficient iteration. +// +// WARN: This must be called strictly before iteration. Failing to do so will result in exceptions. +void heap_recorder_prepare_iteration(heap_recorder *heap_recorder); + +// Optimize the heap recorder by cleaning up any data that might have been prepared specifically +// for the purpose of iterating over the heap recorder data. // -// NOTE: This should usually be called before iteration to ensure data is as little stale as possible. -void heap_recorder_flush(heap_recorder *heap_recorder); +// WARN: This must be called strictly after iteration to ensure proper cleanup and to keep the memory +// profile of the heap recorder low. +void heap_recorder_finish_iteration(heap_recorder *heap_recorder); // Iterate over each live object being tracked by the heap recorder. // +// NOTE: Iteration can be called without holding the Ruby Global VM lock. +// WARN: This must be called strictly after heap_recorder_prepare_iteration and before +// heap_recorder_finish_iteration. +// // @param for_each_callback // A callback function that shall be called for each live object being tracked // by the heap recorder. Alongside the iteration_data for each live object, @@ -82,10 +104,18 @@ void heap_recorder_flush(heap_recorder *heap_recorder); // @param for_each_callback_extra_arg // Optional (NULL if empty) extra data that should be passed to the // callback function alongside the data for each live tracked object. -// @param with_gvl -// True if we're calling this while holding the GVL, false otherwise. -void heap_recorder_for_each_live_object( +// @return true if iteration ran, false if something prevented it from running. +bool heap_recorder_for_each_live_object( heap_recorder *heap_recorder, bool (*for_each_callback)(heap_recorder_iteration_data data, void* extra_arg), - void *for_each_callback_extra_arg, - bool with_gvl); + void *for_each_callback_extra_arg); + +// v--- TEST-ONLY APIs ---v + +// Assert internal hashing logic is valid for the provided locations and its +// corresponding internal representations in heap recorder. +void heap_recorder_testonly_assert_hash_matches(ddog_prof_Slice_Location locations); + +// Returns a Ruby string with a representation of internal data helpful to +// troubleshoot issues such as unexpected test failures. +VALUE heap_recorder_testonly_debug(heap_recorder *heap_recorder); diff --git a/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h b/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h index 7d4593fa55..307d4d0fb3 100644 --- a/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +++ b/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h @@ -34,3 +34,9 @@ size_t read_ddogerr_string_and_drop(ddog_Error *error, char *string, size_t capa // ruby_value_type that Ruby uses so that we can also use this for debugging. const char *ruby_value_type_to_string(enum ruby_value_type type); ddog_CharSlice ruby_value_type_to_char_slice(enum ruby_value_type type); + +// Returns a dynamically allocated string from the provided char slice. +// WARN: The returned string must be explicitly freed with ruby_xfree. +inline static char* string_from_char_slice(ddog_CharSlice slice) { + return ruby_strndup(slice.ptr, slice.len); +} diff --git a/ext/ddtrace_profiling_native_extension/profiling.c b/ext/ddtrace_profiling_native_extension/profiling.c index 9dc9952b4a..d1d39397cd 100644 --- a/ext/ddtrace_profiling_native_extension/profiling.c +++ b/ext/ddtrace_profiling_native_extension/profiling.c @@ -41,6 +41,7 @@ void DDTRACE_EXPORT Init_ddtrace_profiling_native_extension(void) { rb_define_singleton_method(native_extension_module, "native_working?", native_working_p, 0); rb_funcall(native_extension_module, rb_intern("private_class_method"), 1, ID2SYM(rb_intern("native_working?"))); + ruby_helpers_init(); collectors_cpu_and_wall_time_worker_init(profiling_module); collectors_dynamic_sampling_rate_init(profiling_module); collectors_idle_sampling_helper_init(profiling_module); diff --git a/ext/ddtrace_profiling_native_extension/ruby_helpers.c b/ext/ddtrace_profiling_native_extension/ruby_helpers.c index b874d1f249..99dc6097d6 100644 --- a/ext/ddtrace_profiling_native_extension/ruby_helpers.c +++ b/ext/ddtrace_profiling_native_extension/ruby_helpers.c @@ -4,6 +4,18 @@ #include "ruby_helpers.h" #include "private_vm_api_access.h" +// The following global variables are initialized at startup to save expensive lookups later. +// They are not expected to be mutated outside of init. +static VALUE module_object_space = Qnil; +static ID _id2ref_id = Qnil; + +void ruby_helpers_init(void) { + rb_global_variable(&module_object_space); + + module_object_space = rb_const_get(rb_cObject, rb_intern("ObjectSpace")); + _id2ref_id = rb_intern("_id2ref"); +} + void raise_unexpected_type( VALUE value, const char *value_name, @@ -108,3 +120,49 @@ void raise_syserr( grab_gvl_and_raise_syserr(syserr_errno, "Failure returned by '%s' at %s:%d:in `%s'", expression, file, line, function_name); } } + +char* ruby_strndup(const char *str, size_t size) { + char *dup; + + dup = xmalloc(size + 1); + memcpy(dup, str, size); + dup[size] = '\0'; + + return dup; +} + +static VALUE _id2ref(VALUE obj_id) { + // Call ::ObjectSpace._id2ref natively. It will raise if the id is no longer valid + return rb_funcall(module_object_space, _id2ref_id, 1, obj_id); +} + +static VALUE _id2ref_failure(DDTRACE_UNUSED VALUE _unused1, DDTRACE_UNUSED VALUE _unused2) { + return Qfalse; +} + +// Native wrapper to get an object ref from an id. Returns true on success and +// writes the ref to the value pointer parameter if !NULL. False if id doesn't +// reference a valid object (in which case value is not changed). +bool ruby_ref_from_id(VALUE obj_id, VALUE *value) { + // Call ::ObjectSpace._id2ref natively. It will raise if the id is no longer valid + // so we need to call it via rb_rescue2 + // TODO: Benchmark rb_rescue2 vs rb_protect here + VALUE result = rb_rescue2( + _id2ref, + obj_id, + _id2ref_failure, + Qnil, + rb_eRangeError, // rb_eRangeError is the error used to flag invalid ids + 0 // Required by API to be the last argument + ); + + if (result == Qfalse) { + return false; + } + + if (value != NULL) { + (*value) = result; + } + + return true; +} diff --git a/ext/ddtrace_profiling_native_extension/ruby_helpers.h b/ext/ddtrace_profiling_native_extension/ruby_helpers.h index 84889fb83d..d0bf3cfcb0 100644 --- a/ext/ddtrace_profiling_native_extension/ruby_helpers.h +++ b/ext/ddtrace_profiling_native_extension/ruby_helpers.h @@ -5,6 +5,10 @@ #include "helpers.h" +// Initialize internal data needed by some ruby helpers. Should be called during start, before any actual +// usage of ruby helpers. +void ruby_helpers_init(void); + // Processes any pending interruptions, including exceptions to be raised. // If there's an exception to be raised, it raises it. In that case, this function does not return. static inline VALUE process_pending_interruptions(DDTRACE_UNUSED VALUE _) { @@ -87,3 +91,18 @@ NORETURN(void raise_syserr( int line, const char *function_name )); + +// Alternative to ruby_strdup that takes a size argument. +// Similar to C's strndup but slightly less smart as size is expected to +// be smaller or equal to the real size of str (minus null termination if it +// exists). +// A new string will be returned with size+1 bytes and last byte set to '\0'. +// The returned string must be freed explicitly. +// +// WARN: Cannot be used during GC or outside the GVL. +char* ruby_strndup(const char *str, size_t size); + +// Native wrapper to get an object ref from an id. Returns true on success and +// writes the ref to the value pointer parameter if !NULL. False if id doesn't +// reference a valid object (in which case value is not changed). +bool ruby_ref_from_id(size_t id, VALUE *value); diff --git a/ext/ddtrace_profiling_native_extension/stack_recorder.c b/ext/ddtrace_profiling_native_extension/stack_recorder.c index 55ac0b25a1..041cf4fc88 100644 --- a/ext/ddtrace_profiling_native_extension/stack_recorder.c +++ b/ext/ddtrace_profiling_native_extension/stack_recorder.c @@ -194,6 +194,7 @@ struct call_serialize_without_gvl_arguments { // Set by caller struct stack_recorder_state *state; ddog_Timespec finish_timestamp; + size_t gc_count_before_serialize; // Set by callee ddog_prof_Profile *profile; @@ -230,7 +231,11 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_ static void serializer_set_start_timestamp_for_next_profile(struct stack_recorder_state *state, ddog_Timespec start_time); static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE local_root_span_id, VALUE endpoint); static void reset_profile(ddog_prof_Profile *profile, ddog_Timespec *start_time /* Can be null */); -static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE new_obj, VALUE weight); +static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE new_obj, VALUE weight, VALUE alloc_class); +static VALUE _native_check_heap_hashes(DDTRACE_UNUSED VALUE _self, VALUE locations); +static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance); +static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance); +static VALUE _native_debug_heap_recorder(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance); void stack_recorder_init(VALUE profiling_module) { @@ -255,7 +260,14 @@ void stack_recorder_init(VALUE profiling_module) { rb_define_singleton_method(testing_module, "_native_slot_one_mutex_locked?", _native_is_slot_one_mutex_locked, 1); rb_define_singleton_method(testing_module, "_native_slot_two_mutex_locked?", _native_is_slot_two_mutex_locked, 1); rb_define_singleton_method(testing_module, "_native_record_endpoint", _native_record_endpoint, 3); - rb_define_singleton_method(testing_module, "_native_track_object", _native_track_object, 3); + rb_define_singleton_method(testing_module, "_native_track_object", _native_track_object, 4); + rb_define_singleton_method(testing_module, "_native_check_heap_hashes", _native_check_heap_hashes, 1); + rb_define_singleton_method(testing_module, "_native_start_fake_slow_heap_serialization", + _native_start_fake_slow_heap_serialization, 1); + rb_define_singleton_method(testing_module, "_native_end_fake_slow_heap_serialization", + _native_end_fake_slow_heap_serialization, 1); + rb_define_singleton_method(testing_module, "_native_debug_heap_recorder", + _native_debug_heap_recorder, 1); ok_symbol = ID2SYM(rb_intern_const("ok")); error_symbol = ID2SYM(rb_intern_const("error")); @@ -441,13 +453,18 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan // Need to do this while still holding on to the Global VM Lock; see comments on method for why serializer_set_start_timestamp_for_next_profile(state, finish_timestamp); - // Flush any pending data in the heap recorder prior to doing the iteration during serialization. - // This needs to happen while holding on to the GVL - heap_recorder_flush(state->heap_recorder); + // Prepare the iteration on heap recorder we'll be doing outside the GVL. The preparation needs to + // happen while holding on to the GVL. + heap_recorder_prepare_iteration(state->heap_recorder); // We'll release the Global VM Lock while we're calling serialize, so that the Ruby VM can continue to work while this // is pending - struct call_serialize_without_gvl_arguments args = {.state = state, .finish_timestamp = finish_timestamp, .serialize_ran = false}; + struct call_serialize_without_gvl_arguments args = { + .state = state, + .finish_timestamp = finish_timestamp, + .gc_count_before_serialize = rb_gc_count(), + .serialize_ran = false + }; while (!args.serialize_ran) { // Give the Ruby VM an opportunity to process any pending interruptions (including raising exceptions). @@ -463,6 +480,9 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan rb_thread_call_without_gvl2(call_serialize_without_gvl, &args, NULL /* No interruption function needed in this case */, NULL /* Not needed */); } + // Cleanup after heap recorder iteration. This needs to happen while holding on to the GVL. + heap_recorder_finish_iteration(state->heap_recorder); + ddog_prof_Profile_SerializeResult serialized_profile = args.result; if (serialized_profile.tag == DDOG_PROF_PROFILE_SERIALIZE_RESULT_ERR) { @@ -532,13 +552,13 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations, } } -void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample_weight) { +void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample_weight, ddog_CharSlice *alloc_class) { struct stack_recorder_state *state; TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state); // FIXME: Heap sampling currently has to be done in 2 parts because the construction of locations is happening // very late in the allocation-sampling path (which is shared with the cpu sampling path). This can // be fixed with some refactoring but for now this leads to a less impactful change. - start_heap_allocation_recording(state->heap_recorder, new_object, sample_weight); + start_heap_allocation_recording(state->heap_recorder, new_object, sample_weight, alloc_class); } void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_CharSlice endpoint) { @@ -563,24 +583,50 @@ void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_ typedef struct heap_recorder_iteration_context { struct stack_recorder_state *state; ddog_prof_Profile *profile; + bool error; char error_msg[MAX_LEN_HEAP_ITERATION_ERROR_MSG]; + + size_t profile_gen; } heap_recorder_iteration_context; static bool add_heap_sample_to_active_profile_without_gvl(heap_recorder_iteration_data iteration_data, void *extra_arg) { heap_recorder_iteration_context *context = (heap_recorder_iteration_context*) extra_arg; + live_object_data *object_data = &iteration_data.object_data; + int64_t metric_values[ALL_VALUE_TYPES_COUNT] = {0}; uint8_t *position_for = context->state->position_for; - metric_values[position_for[HEAP_SAMPLES_VALUE_ID]] = iteration_data.object_data.weight; + metric_values[position_for[HEAP_SAMPLES_VALUE_ID]] = object_data->weight; + + ddog_prof_Label labels[2]; + size_t label_offset = 0; + + if (object_data->class != NULL) { + labels[label_offset++] = (ddog_prof_Label) { + .key = DDOG_CHARSLICE_C("allocation class"), + .str = (ddog_CharSlice) { + .ptr = object_data->class, + .len = strlen(object_data->class), + }, + .num = 0, // This shouldn't be needed but the tracer-2.7 docker image ships a buggy gcc that complains about this + }; + } + labels[label_offset++] = (ddog_prof_Label) { + .key = DDOG_CHARSLICE_C("gc gen age"), + .num = context->profile_gen - object_data->alloc_gen, + }; ddog_prof_Profile_Result result = ddog_prof_Profile_add( context->profile, (ddog_prof_Sample) { .locations = iteration_data.locations, .values = (ddog_Slice_I64) {.ptr = metric_values, .len = context->state->enabled_values_count}, - .labels = {0}, + .labels = (ddog_prof_Slice_Label) { + .ptr = labels, + .len = label_offset, + } }, 0 ); @@ -596,20 +642,23 @@ static bool add_heap_sample_to_active_profile_without_gvl(heap_recorder_iteratio return true; } -static void build_heap_profile_without_gvl(struct stack_recorder_state *state, ddog_prof_Profile *profile) { +static void build_heap_profile_without_gvl(struct stack_recorder_state *state, ddog_prof_Profile *profile, size_t gc_count_before_serialize) { heap_recorder_iteration_context iteration_context = { .state = state, .profile = profile, .error = false, .error_msg = {0}, + .profile_gen = gc_count_before_serialize, }; - heap_recorder_for_each_live_object(state->heap_recorder, add_heap_sample_to_active_profile_without_gvl, (void*) &iteration_context, false); - if (iteration_context.error) { - // We wait until we're out of the iteration to grab the gvl and raise. This is important because during - // iteration we first grab the records_mutex and raising requires grabbing the GVL. When sampling, we are - // in the opposite situation: we have the GVL and may need to grab the records_mutex for mutation. This - // different ordering can lead to deadlocks. By delaying the raise here until after we no longer hold - // records_mutex, we prevent this different-lock-acquisition-order situation. + bool iterated = heap_recorder_for_each_live_object(state->heap_recorder, add_heap_sample_to_active_profile_without_gvl, (void*) &iteration_context); + // We wait until we're out of the iteration to grab the gvl and raise. This is important because during + // iteration we may potentially acquire locks in the heap recorder and we could reach a deadlock if the + // same locks are acquired by the heap recorder while holding the gvl (since we'd be operating on the + // same locks but acquiring them in different order). + if (!iterated) { + grab_gvl_and_raise(rb_eRuntimeError, "Failure during heap profile building: iteration cancelled"); + } + else if (iteration_context.error) { grab_gvl_and_raise(rb_eRuntimeError, "Failure during heap profile building: %s", iteration_context.error_msg); } } @@ -621,7 +670,7 @@ static void *call_serialize_without_gvl(void *call_args) { // Now that we have the inactive profile with all but heap samples, lets fill it with heap data // without needing to race with the active sampler - build_heap_profile_without_gvl(args->state, args->profile); + build_heap_profile_without_gvl(args->state, args->profile, args->gc_count_before_serialize); // Note: The profile gets reset by the serialize call args->result = ddog_prof_Profile_serialize(args->profile, &args->finish_timestamp, NULL /* duration_nanos is optional */, NULL /* start_time is optional */); @@ -763,15 +812,77 @@ static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_ return Qtrue; } -static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE new_obj, VALUE weight) { +static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE new_obj, VALUE weight, VALUE alloc_class) { ENFORCE_TYPE(weight, T_FIXNUM); - track_object(recorder_instance, new_obj, NUM2UINT(weight)); + ddog_CharSlice alloc_class_slice = char_slice_from_ruby_string(alloc_class); + track_object(recorder_instance, new_obj, NUM2UINT(weight), &alloc_class_slice); return Qtrue; } +static VALUE _native_check_heap_hashes(DDTRACE_UNUSED VALUE _self, VALUE locations) { + ENFORCE_TYPE(locations, T_ARRAY); + size_t locations_len = rb_array_len(locations); + ddog_prof_Location locations_arr[locations_len]; + for (size_t i = 0; i < locations_len; i++) { + VALUE location = rb_ary_entry(locations, i); + ENFORCE_TYPE(location, T_ARRAY); + VALUE name = rb_ary_entry(location, 0); + VALUE filename = rb_ary_entry(location, 1); + VALUE line = rb_ary_entry(location, 2); + ENFORCE_TYPE(name, T_STRING); + ENFORCE_TYPE(filename, T_STRING); + ENFORCE_TYPE(line, T_FIXNUM); + locations_arr[i] = (ddog_prof_Location) { + .line = line, + .function = (ddog_prof_Function) { + .name = char_slice_from_ruby_string(name), + .filename = char_slice_from_ruby_string(filename), + } + }; + } + ddog_prof_Slice_Location ddog_locations = { + .len = locations_len, + .ptr = locations_arr, + }; + heap_recorder_testonly_assert_hash_matches(ddog_locations); + + return Qnil; +} + static void reset_profile(ddog_prof_Profile *profile, ddog_Timespec *start_time /* Can be null */) { ddog_prof_Profile_Result reset_result = ddog_prof_Profile_reset(profile, start_time); if (reset_result.tag == DDOG_PROF_PROFILE_RESULT_ERR) { rb_raise(rb_eRuntimeError, "Failed to reset profile: %"PRIsVALUE, get_error_details_and_drop(&reset_result.err)); } } + +// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec. +// It SHOULD NOT be used for other purposes. +static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) { + struct stack_recorder_state *state; + TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state); + + heap_recorder_prepare_iteration(state->heap_recorder); + + return Qnil; +} + +// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec. +// It SHOULD NOT be used for other purposes. +static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) { + struct stack_recorder_state *state; + TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state); + + heap_recorder_finish_iteration(state->heap_recorder); + + return Qnil; +} + +// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec. +// It SHOULD NOT be used for other purposes. +static VALUE _native_debug_heap_recorder(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) { + struct stack_recorder_state *state; + TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state); + + return heap_recorder_testonly_debug(state->heap_recorder); +} diff --git a/ext/ddtrace_profiling_native_extension/stack_recorder.h b/ext/ddtrace_profiling_native_extension/stack_recorder.h index 4a9f2ead7b..cfb93f7a25 100644 --- a/ext/ddtrace_profiling_native_extension/stack_recorder.h +++ b/ext/ddtrace_profiling_native_extension/stack_recorder.h @@ -23,5 +23,5 @@ typedef struct sample_labels { void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations, sample_values values, sample_labels labels); void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_CharSlice endpoint); -void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample_weight); +void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample_weight, ddog_CharSlice *alloc_class); VALUE enforce_recorder_instance(VALUE object); diff --git a/gemfiles/ruby_2.1_activesupport.gemfile.lock b/gemfiles/ruby_2.1_activesupport.gemfile.lock index 404f26f81b..d0c9f49eaf 100644 --- a/gemfiles/ruby_2.1_activesupport.gemfile.lock +++ b/gemfiles/ruby_2.1_activesupport.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -36,7 +36,7 @@ GEM concurrent-ruby (1.1.9) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_aws.gemfile.lock b/gemfiles/ruby_2.1_aws.gemfile.lock index d0df147954..4839cc969e 100644 --- a/gemfiles/ruby_2.1_aws.gemfile.lock +++ b/gemfiles/ruby_2.1_aws.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -35,7 +35,7 @@ GEM concurrent-ruby (1.1.9) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_contrib.gemfile.lock b/gemfiles/ruby_2.1_contrib.gemfile.lock index b85a441fa6..0acbc80ff6 100644 --- a/gemfiles/ruby_2.1_contrib.gemfile.lock +++ b/gemfiles/ruby_2.1_contrib.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -45,7 +45,7 @@ GEM crack (0.4.5) rexml dalli (2.7.11) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_core_old.gemfile.lock b/gemfiles/ruby_2.1_core_old.gemfile.lock index ee2ef29613..aa64c76c3d 100644 --- a/gemfiles/ruby_2.1_core_old.gemfile.lock +++ b/gemfiles/ruby_2.1_core_old.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.1.9) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_elasticsearch_7.gemfile.lock b/gemfiles/ruby_2.1_elasticsearch_7.gemfile.lock index b8d949c936..db1fff3259 100644 --- a/gemfiles/ruby_2.1_elasticsearch_7.gemfile.lock +++ b/gemfiles/ruby_2.1_elasticsearch_7.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -26,7 +26,7 @@ GEM concurrent-ruby (1.1.9) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_http.gemfile.lock b/gemfiles/ruby_2.1_http.gemfile.lock index 3c8a7c1f15..104c2fdfe9 100644 --- a/gemfiles/ruby_2.1_http.gemfile.lock +++ b/gemfiles/ruby_2.1_http.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.1.9) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_opentracing.gemfile.lock b/gemfiles/ruby_2.1_opentracing.gemfile.lock index 7d39bd462d..332150ae6e 100644 --- a/gemfiles/ruby_2.1_opentracing.gemfile.lock +++ b/gemfiles/ruby_2.1_opentracing.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -26,7 +26,7 @@ GEM concurrent-ruby (1.1.9) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_rack_1.gemfile.lock b/gemfiles/ruby_2.1_rack_1.gemfile.lock index 65b964be0e..09c5fb755a 100644 --- a/gemfiles/ruby_2.1_rack_1.gemfile.lock +++ b/gemfiles/ruby_2.1_rack_1.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.1.9) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_rails32_mysql2.gemfile.lock b/gemfiles/ruby_2.1_rails32_mysql2.gemfile.lock index fe353ed015..67cfd45e5d 100644 --- a/gemfiles/ruby_2.1_rails32_mysql2.gemfile.lock +++ b/gemfiles/ruby_2.1_rails32_mysql2.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -57,7 +57,7 @@ GEM concurrent-ruby (1.1.9) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_rails32_postgres.gemfile.lock b/gemfiles/ruby_2.1_rails32_postgres.gemfile.lock index 0d6ec94e54..6b219339bc 100644 --- a/gemfiles/ruby_2.1_rails32_postgres.gemfile.lock +++ b/gemfiles/ruby_2.1_rails32_postgres.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -53,7 +53,7 @@ GEM concurrent-ruby (1.1.9) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_rails32_postgres_redis.gemfile.lock b/gemfiles/ruby_2.1_rails32_postgres_redis.gemfile.lock index d25790aabe..7724063d26 100644 --- a/gemfiles/ruby_2.1_rails32_postgres_redis.gemfile.lock +++ b/gemfiles/ruby_2.1_rails32_postgres_redis.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -53,7 +53,7 @@ GEM concurrent-ruby (1.1.9) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_rails32_postgres_sidekiq.gemfile.lock b/gemfiles/ruby_2.1_rails32_postgres_sidekiq.gemfile.lock index f41d036616..a5b38f60ef 100644 --- a/gemfiles/ruby_2.1_rails32_postgres_sidekiq.gemfile.lock +++ b/gemfiles/ruby_2.1_rails32_postgres_sidekiq.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -54,7 +54,7 @@ GEM connection_pool (2.2.3) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_rails4_mysql2.gemfile.lock b/gemfiles/ruby_2.1_rails4_mysql2.gemfile.lock index d92a7c0180..0bc4a49ffd 100644 --- a/gemfiles/ruby_2.1_rails4_mysql2.gemfile.lock +++ b/gemfiles/ruby_2.1_rails4_mysql2.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -61,7 +61,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_rails4_postgres.gemfile.lock b/gemfiles/ruby_2.1_rails4_postgres.gemfile.lock index 2b1b66c890..a40553bd5b 100644 --- a/gemfiles/ruby_2.1_rails4_postgres.gemfile.lock +++ b/gemfiles/ruby_2.1_rails4_postgres.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -61,7 +61,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_rails4_postgres_redis.gemfile.lock b/gemfiles/ruby_2.1_rails4_postgres_redis.gemfile.lock index e340af8662..af00f959f0 100644 --- a/gemfiles/ruby_2.1_rails4_postgres_redis.gemfile.lock +++ b/gemfiles/ruby_2.1_rails4_postgres_redis.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -61,7 +61,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_rails4_semantic_logger.gemfile.lock b/gemfiles/ruby_2.1_rails4_semantic_logger.gemfile.lock index 47f4f6a462..dfac098d51 100644 --- a/gemfiles/ruby_2.1_rails4_semantic_logger.gemfile.lock +++ b/gemfiles/ruby_2.1_rails4_semantic_logger.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -61,7 +61,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_redis_3.gemfile.lock b/gemfiles/ruby_2.1_redis_3.gemfile.lock index e7b7241a1f..658babd68b 100644 --- a/gemfiles/ruby_2.1_redis_3.gemfile.lock +++ b/gemfiles/ruby_2.1_redis_3.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.1.9) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.1_relational_db.gemfile.lock b/gemfiles/ruby_2.1_relational_db.gemfile.lock index 0590d67eeb..f9f9666693 100644 --- a/gemfiles/ruby_2.1_relational_db.gemfile.lock +++ b/gemfiles/ruby_2.1_relational_db.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -41,7 +41,7 @@ GEM concurrent-ruby (1.1.9) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) delayed_job (4.1.11) diff --git a/gemfiles/ruby_2.1_sinatra.gemfile.lock b/gemfiles/ruby_2.1_sinatra.gemfile.lock index 3596d3c9ce..2f50187f1f 100644 --- a/gemfiles/ruby_2.1_sinatra.gemfile.lock +++ b/gemfiles/ruby_2.1_sinatra.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.1.9) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_activesupport.gemfile.lock b/gemfiles/ruby_2.2_activesupport.gemfile.lock index 3d8b72a916..ae1efd5d88 100644 --- a/gemfiles/ruby_2.2_activesupport.gemfile.lock +++ b/gemfiles/ruby_2.2_activesupport.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -60,7 +60,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) descendants_tracker (0.0.4) diff --git a/gemfiles/ruby_2.2_aws.gemfile.lock b/gemfiles/ruby_2.2_aws.gemfile.lock index f855cd750e..e7d6312018 100644 --- a/gemfiles/ruby_2.2_aws.gemfile.lock +++ b/gemfiles/ruby_2.2_aws.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -1147,7 +1147,7 @@ GEM concurrent-ruby (1.1.10) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_contrib.gemfile.lock b/gemfiles/ruby_2.2_contrib.gemfile.lock index 47182dcfff..03eefc621e 100644 --- a/gemfiles/ruby_2.2_contrib.gemfile.lock +++ b/gemfiles/ruby_2.2_contrib.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -31,7 +31,7 @@ GEM crack (0.4.5) rexml dalli (2.7.11) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_core_old.gemfile.lock b/gemfiles/ruby_2.2_core_old.gemfile.lock index 7bb4c1a8e2..fd980c6cce 100644 --- a/gemfiles/ruby_2.2_core_old.gemfile.lock +++ b/gemfiles/ruby_2.2_core_old.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.1.10) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_elasticsearch_7.gemfile.lock b/gemfiles/ruby_2.2_elasticsearch_7.gemfile.lock index b81a44038c..c262051ee2 100644 --- a/gemfiles/ruby_2.2_elasticsearch_7.gemfile.lock +++ b/gemfiles/ruby_2.2_elasticsearch_7.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -26,7 +26,7 @@ GEM concurrent-ruby (1.1.10) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_http.gemfile.lock b/gemfiles/ruby_2.2_http.gemfile.lock index 63211e3ba2..b4f9aea9e9 100644 --- a/gemfiles/ruby_2.2_http.gemfile.lock +++ b/gemfiles/ruby_2.2_http.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.1.10) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_opentracing.gemfile.lock b/gemfiles/ruby_2.2_opentracing.gemfile.lock index eed0fb6ebc..7735e0f503 100644 --- a/gemfiles/ruby_2.2_opentracing.gemfile.lock +++ b/gemfiles/ruby_2.2_opentracing.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -26,7 +26,7 @@ GEM concurrent-ruby (1.1.10) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rack_1.gemfile.lock b/gemfiles/ruby_2.2_rack_1.gemfile.lock index f24a87f21c..613ac67249 100644 --- a/gemfiles/ruby_2.2_rack_1.gemfile.lock +++ b/gemfiles/ruby_2.2_rack_1.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.1.10) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails32_mysql2.gemfile.lock b/gemfiles/ruby_2.2_rails32_mysql2.gemfile.lock index 0ba0742971..73fa5eb64e 100644 --- a/gemfiles/ruby_2.2_rails32_mysql2.gemfile.lock +++ b/gemfiles/ruby_2.2_rails32_mysql2.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -57,7 +57,7 @@ GEM concurrent-ruby (1.1.10) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails32_postgres.gemfile.lock b/gemfiles/ruby_2.2_rails32_postgres.gemfile.lock index ee5d04518f..9d4654d06b 100644 --- a/gemfiles/ruby_2.2_rails32_postgres.gemfile.lock +++ b/gemfiles/ruby_2.2_rails32_postgres.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -53,7 +53,7 @@ GEM concurrent-ruby (1.1.10) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails32_postgres_redis.gemfile.lock b/gemfiles/ruby_2.2_rails32_postgres_redis.gemfile.lock index 771e429f7e..37cc2e9b4d 100644 --- a/gemfiles/ruby_2.2_rails32_postgres_redis.gemfile.lock +++ b/gemfiles/ruby_2.2_rails32_postgres_redis.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -53,7 +53,7 @@ GEM concurrent-ruby (1.1.10) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails32_postgres_sidekiq.gemfile.lock b/gemfiles/ruby_2.2_rails32_postgres_sidekiq.gemfile.lock index 47fe1f8684..e131803664 100644 --- a/gemfiles/ruby_2.2_rails32_postgres_sidekiq.gemfile.lock +++ b/gemfiles/ruby_2.2_rails32_postgres_sidekiq.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -54,7 +54,7 @@ GEM connection_pool (2.2.3) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails4_mysql2.gemfile.lock b/gemfiles/ruby_2.2_rails4_mysql2.gemfile.lock index 76754ea5e4..37746c3efb 100644 --- a/gemfiles/ruby_2.2_rails4_mysql2.gemfile.lock +++ b/gemfiles/ruby_2.2_rails4_mysql2.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -61,7 +61,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails4_postgres.gemfile.lock b/gemfiles/ruby_2.2_rails4_postgres.gemfile.lock index 0e40183c9f..cab753e593 100644 --- a/gemfiles/ruby_2.2_rails4_postgres.gemfile.lock +++ b/gemfiles/ruby_2.2_rails4_postgres.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -61,7 +61,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails4_postgres_redis.gemfile.lock b/gemfiles/ruby_2.2_rails4_postgres_redis.gemfile.lock index f7b7bb70e8..6d59044452 100644 --- a/gemfiles/ruby_2.2_rails4_postgres_redis.gemfile.lock +++ b/gemfiles/ruby_2.2_rails4_postgres_redis.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -61,7 +61,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails4_postgres_sidekiq.gemfile.lock b/gemfiles/ruby_2.2_rails4_postgres_sidekiq.gemfile.lock index e6be1f8469..c5581ee4b6 100644 --- a/gemfiles/ruby_2.2_rails4_postgres_sidekiq.gemfile.lock +++ b/gemfiles/ruby_2.2_rails4_postgres_sidekiq.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -62,7 +62,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails4_semantic_logger.gemfile.lock b/gemfiles/ruby_2.2_rails4_semantic_logger.gemfile.lock index 37aaa17d53..53a3a618ab 100644 --- a/gemfiles/ruby_2.2_rails4_semantic_logger.gemfile.lock +++ b/gemfiles/ruby_2.2_rails4_semantic_logger.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -61,7 +61,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails5_mysql2.gemfile.lock b/gemfiles/ruby_2.2_rails5_mysql2.gemfile.lock index ec8b91f115..ad5ef64a88 100644 --- a/gemfiles/ruby_2.2_rails5_mysql2.gemfile.lock +++ b/gemfiles/ruby_2.2_rails5_mysql2.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -68,7 +68,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails5_postgres.gemfile.lock b/gemfiles/ruby_2.2_rails5_postgres.gemfile.lock index 3af12f5591..73f4c12c6c 100644 --- a/gemfiles/ruby_2.2_rails5_postgres.gemfile.lock +++ b/gemfiles/ruby_2.2_rails5_postgres.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -68,7 +68,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails5_postgres_redis.gemfile.lock b/gemfiles/ruby_2.2_rails5_postgres_redis.gemfile.lock index ad7fce7d73..8ae30dbb01 100644 --- a/gemfiles/ruby_2.2_rails5_postgres_redis.gemfile.lock +++ b/gemfiles/ruby_2.2_rails5_postgres_redis.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -68,7 +68,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails5_postgres_redis_activesupport.gemfile.lock b/gemfiles/ruby_2.2_rails5_postgres_redis_activesupport.gemfile.lock index 92cb631cef..81a2babc75 100644 --- a/gemfiles/ruby_2.2_rails5_postgres_redis_activesupport.gemfile.lock +++ b/gemfiles/ruby_2.2_rails5_postgres_redis_activesupport.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -68,7 +68,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails5_postgres_sidekiq.gemfile.lock b/gemfiles/ruby_2.2_rails5_postgres_sidekiq.gemfile.lock index 4e76ad7946..eefaf54860 100644 --- a/gemfiles/ruby_2.2_rails5_postgres_sidekiq.gemfile.lock +++ b/gemfiles/ruby_2.2_rails5_postgres_sidekiq.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -69,7 +69,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_rails5_semantic_logger.gemfile.lock b/gemfiles/ruby_2.2_rails5_semantic_logger.gemfile.lock index 2372af73e6..097bb1293c 100644 --- a/gemfiles/ruby_2.2_rails5_semantic_logger.gemfile.lock +++ b/gemfiles/ruby_2.2_rails5_semantic_logger.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -68,7 +68,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_redis_3.gemfile.lock b/gemfiles/ruby_2.2_redis_3.gemfile.lock index 4faa312d20..26720bd04a 100644 --- a/gemfiles/ruby_2.2_redis_3.gemfile.lock +++ b/gemfiles/ruby_2.2_redis_3.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.1.10) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.2_relational_db.gemfile.lock b/gemfiles/ruby_2.2_relational_db.gemfile.lock index 04ec5e3367..3c35f05e50 100644 --- a/gemfiles/ruby_2.2_relational_db.gemfile.lock +++ b/gemfiles/ruby_2.2_relational_db.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -37,7 +37,7 @@ GEM concurrent-ruby (1.1.10) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) delayed_job (4.1.11) diff --git a/gemfiles/ruby_2.2_sinatra.gemfile.lock b/gemfiles/ruby_2.2_sinatra.gemfile.lock index e071546a7a..eb3289dfa2 100644 --- a/gemfiles/ruby_2.2_sinatra.gemfile.lock +++ b/gemfiles/ruby_2.2_sinatra.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.1.10) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_activerecord_3.gemfile.lock b/gemfiles/ruby_2.3_activerecord_3.gemfile.lock index 637290c4de..2acb335334 100644 --- a/gemfiles/ruby_2.3_activerecord_3.gemfile.lock +++ b/gemfiles/ruby_2.3_activerecord_3.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -41,7 +41,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_activesupport.gemfile.lock b/gemfiles/ruby_2.3_activesupport.gemfile.lock index fe623d2485..679a8ab682 100644 --- a/gemfiles/ruby_2.3_activesupport.gemfile.lock +++ b/gemfiles/ruby_2.3_activesupport.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -60,7 +60,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) descendants_tracker (0.0.4) diff --git a/gemfiles/ruby_2.3_aws.gemfile.lock b/gemfiles/ruby_2.3_aws.gemfile.lock index ca0e6b3995..a0f784b637 100644 --- a/gemfiles/ruby_2.3_aws.gemfile.lock +++ b/gemfiles/ruby_2.3_aws.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -1446,7 +1446,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_contrib.gemfile.lock b/gemfiles/ruby_2.3_contrib.gemfile.lock index 9db1f25a65..8fea951f73 100644 --- a/gemfiles/ruby_2.3_contrib.gemfile.lock +++ b/gemfiles/ruby_2.3_contrib.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -32,7 +32,7 @@ GEM crack (0.4.5) rexml dalli (2.7.11) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_contrib_old.gemfile.lock b/gemfiles/ruby_2.3_contrib_old.gemfile.lock index f919e09f69..95ca0c6b79 100644 --- a/gemfiles/ruby_2.3_contrib_old.gemfile.lock +++ b/gemfiles/ruby_2.3_contrib_old.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_core_old.gemfile.lock b/gemfiles/ruby_2.3_core_old.gemfile.lock index 36c092f6b0..ffb98f15e3 100644 --- a/gemfiles/ruby_2.3_core_old.gemfile.lock +++ b/gemfiles/ruby_2.3_core_old.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_elasticsearch_7.gemfile.lock b/gemfiles/ruby_2.3_elasticsearch_7.gemfile.lock index c69dd9db47..812dbac0c4 100644 --- a/gemfiles/ruby_2.3_elasticsearch_7.gemfile.lock +++ b/gemfiles/ruby_2.3_elasticsearch_7.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -26,7 +26,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_hanami_1.gemfile.lock b/gemfiles/ruby_2.3_hanami_1.gemfile.lock index 1de64a6d22..c142116e15 100644 --- a/gemfiles/ruby_2.3_hanami_1.gemfile.lock +++ b/gemfiles/ruby_2.3_hanami_1.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_http.gemfile.lock b/gemfiles/ruby_2.3_http.gemfile.lock index e1cc1f75cd..4b78a86c8e 100644 --- a/gemfiles/ruby_2.3_http.gemfile.lock +++ b/gemfiles/ruby_2.3_http.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_opentracing.gemfile.lock b/gemfiles/ruby_2.3_opentracing.gemfile.lock index f25ecbd295..c51c173af6 100644 --- a/gemfiles/ruby_2.3_opentracing.gemfile.lock +++ b/gemfiles/ruby_2.3_opentracing.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -26,7 +26,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rack_1.gemfile.lock b/gemfiles/ruby_2.3_rack_1.gemfile.lock index 22f94a5b67..8978751335 100644 --- a/gemfiles/ruby_2.3_rack_1.gemfile.lock +++ b/gemfiles/ruby_2.3_rack_1.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rack_2.gemfile.lock b/gemfiles/ruby_2.3_rack_2.gemfile.lock index 1248059e3e..ca9b435de6 100644 --- a/gemfiles/ruby_2.3_rack_2.gemfile.lock +++ b/gemfiles/ruby_2.3_rack_2.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails32_mysql2.gemfile.lock b/gemfiles/ruby_2.3_rails32_mysql2.gemfile.lock index d03d941e6c..fcafd320db 100644 --- a/gemfiles/ruby_2.3_rails32_mysql2.gemfile.lock +++ b/gemfiles/ruby_2.3_rails32_mysql2.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -57,7 +57,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails32_postgres.gemfile.lock b/gemfiles/ruby_2.3_rails32_postgres.gemfile.lock index fce7eee45a..2a98fe4286 100644 --- a/gemfiles/ruby_2.3_rails32_postgres.gemfile.lock +++ b/gemfiles/ruby_2.3_rails32_postgres.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -53,7 +53,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails32_postgres_redis.gemfile.lock b/gemfiles/ruby_2.3_rails32_postgres_redis.gemfile.lock index 1adf5da1bf..e023175bef 100644 --- a/gemfiles/ruby_2.3_rails32_postgres_redis.gemfile.lock +++ b/gemfiles/ruby_2.3_rails32_postgres_redis.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -53,7 +53,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails32_postgres_sidekiq.gemfile.lock b/gemfiles/ruby_2.3_rails32_postgres_sidekiq.gemfile.lock index 9715fae05e..50eaaedea8 100644 --- a/gemfiles/ruby_2.3_rails32_postgres_sidekiq.gemfile.lock +++ b/gemfiles/ruby_2.3_rails32_postgres_sidekiq.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -54,7 +54,7 @@ GEM connection_pool (2.2.5) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails4_mysql2.gemfile.lock b/gemfiles/ruby_2.3_rails4_mysql2.gemfile.lock index b97ca5e09e..2951afc226 100644 --- a/gemfiles/ruby_2.3_rails4_mysql2.gemfile.lock +++ b/gemfiles/ruby_2.3_rails4_mysql2.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -61,7 +61,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails4_postgres.gemfile.lock b/gemfiles/ruby_2.3_rails4_postgres.gemfile.lock index eca2f61b1c..ba047c40ce 100644 --- a/gemfiles/ruby_2.3_rails4_postgres.gemfile.lock +++ b/gemfiles/ruby_2.3_rails4_postgres.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -61,7 +61,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails4_postgres_redis.gemfile.lock b/gemfiles/ruby_2.3_rails4_postgres_redis.gemfile.lock index e094dd3b5b..001280f201 100644 --- a/gemfiles/ruby_2.3_rails4_postgres_redis.gemfile.lock +++ b/gemfiles/ruby_2.3_rails4_postgres_redis.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -61,7 +61,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails4_postgres_sidekiq.gemfile.lock b/gemfiles/ruby_2.3_rails4_postgres_sidekiq.gemfile.lock index 03fa5c2e83..6d4d86613b 100644 --- a/gemfiles/ruby_2.3_rails4_postgres_sidekiq.gemfile.lock +++ b/gemfiles/ruby_2.3_rails4_postgres_sidekiq.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -62,7 +62,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails4_semantic_logger.gemfile.lock b/gemfiles/ruby_2.3_rails4_semantic_logger.gemfile.lock index 38857a513f..11040bc350 100644 --- a/gemfiles/ruby_2.3_rails4_semantic_logger.gemfile.lock +++ b/gemfiles/ruby_2.3_rails4_semantic_logger.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -61,7 +61,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails5_mysql2.gemfile.lock b/gemfiles/ruby_2.3_rails5_mysql2.gemfile.lock index ea3deb3dc8..cfe63db464 100644 --- a/gemfiles/ruby_2.3_rails5_mysql2.gemfile.lock +++ b/gemfiles/ruby_2.3_rails5_mysql2.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -68,7 +68,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails5_postgres.gemfile.lock b/gemfiles/ruby_2.3_rails5_postgres.gemfile.lock index d13a1123bc..da6e2d3a2f 100644 --- a/gemfiles/ruby_2.3_rails5_postgres.gemfile.lock +++ b/gemfiles/ruby_2.3_rails5_postgres.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -68,7 +68,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails5_postgres_redis.gemfile.lock b/gemfiles/ruby_2.3_rails5_postgres_redis.gemfile.lock index be9f83acb8..98288a1cd5 100644 --- a/gemfiles/ruby_2.3_rails5_postgres_redis.gemfile.lock +++ b/gemfiles/ruby_2.3_rails5_postgres_redis.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -68,7 +68,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails5_postgres_redis_activesupport.gemfile.lock b/gemfiles/ruby_2.3_rails5_postgres_redis_activesupport.gemfile.lock index 52965762be..c60a1c2e71 100644 --- a/gemfiles/ruby_2.3_rails5_postgres_redis_activesupport.gemfile.lock +++ b/gemfiles/ruby_2.3_rails5_postgres_redis_activesupport.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -68,7 +68,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails5_postgres_sidekiq.gemfile.lock b/gemfiles/ruby_2.3_rails5_postgres_sidekiq.gemfile.lock index d9cdf6cafc..93f7169555 100644 --- a/gemfiles/ruby_2.3_rails5_postgres_sidekiq.gemfile.lock +++ b/gemfiles/ruby_2.3_rails5_postgres_sidekiq.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -69,7 +69,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_rails5_semantic_logger.gemfile.lock b/gemfiles/ruby_2.3_rails5_semantic_logger.gemfile.lock index f4ce2e0696..9ff746b871 100644 --- a/gemfiles/ruby_2.3_rails5_semantic_logger.gemfile.lock +++ b/gemfiles/ruby_2.3_rails5_semantic_logger.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -68,7 +68,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_redis_3.gemfile.lock b/gemfiles/ruby_2.3_redis_3.gemfile.lock index c115af0884..7ff271ad0f 100644 --- a/gemfiles/ruby_2.3_redis_3.gemfile.lock +++ b/gemfiles/ruby_2.3_redis_3.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_relational_db.gemfile.lock b/gemfiles/ruby_2.3_relational_db.gemfile.lock index 19666213c7..ea7794ae59 100644 --- a/gemfiles/ruby_2.3_relational_db.gemfile.lock +++ b/gemfiles/ruby_2.3_relational_db.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -37,7 +37,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) delayed_job (4.1.11) diff --git a/gemfiles/ruby_2.3_resque2_redis3.gemfile.lock b/gemfiles/ruby_2.3_resque2_redis3.gemfile.lock index 643443d679..aff46cc880 100644 --- a/gemfiles/ruby_2.3_resque2_redis3.gemfile.lock +++ b/gemfiles/ruby_2.3_resque2_redis3.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_resque2_redis4.gemfile.lock b/gemfiles/ruby_2.3_resque2_redis4.gemfile.lock index c2222473bd..99050218bc 100644 --- a/gemfiles/ruby_2.3_resque2_redis4.gemfile.lock +++ b/gemfiles/ruby_2.3_resque2_redis4.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.3_sinatra.gemfile.lock b/gemfiles/ruby_2.3_sinatra.gemfile.lock index 75b4e3eaa7..63cdd9494e 100644 --- a/gemfiles/ruby_2.3_sinatra.gemfile.lock +++ b/gemfiles/ruby_2.3_sinatra.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -25,7 +25,7 @@ GEM concurrent-ruby (1.2.2) crack (0.4.5) rexml - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_activerecord_4.gemfile.lock b/gemfiles/ruby_2.4_activerecord_4.gemfile.lock index 05312e29c2..4903445296 100644 --- a/gemfiles/ruby_2.4_activerecord_4.gemfile.lock +++ b/gemfiles/ruby_2.4_activerecord_4.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -40,7 +40,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_activesupport.gemfile.lock b/gemfiles/ruby_2.4_activesupport.gemfile.lock index 1e7b49c229..6dd535e453 100644 --- a/gemfiles/ruby_2.4_activesupport.gemfile.lock +++ b/gemfiles/ruby_2.4_activesupport.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -55,7 +55,7 @@ GEM rexml crass (1.0.6) cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_aws.gemfile.lock b/gemfiles/ruby_2.4_aws.gemfile.lock index d70a22ce69..a9e67423db 100644 --- a/gemfiles/ruby_2.4_aws.gemfile.lock +++ b/gemfiles/ruby_2.4_aws.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -1448,7 +1448,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_contrib.gemfile.lock b/gemfiles/ruby_2.4_contrib.gemfile.lock index 119d514e6a..4372353b1a 100644 --- a/gemfiles/ruby_2.4_contrib.gemfile.lock +++ b/gemfiles/ruby_2.4_contrib.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -34,7 +34,7 @@ GEM rexml cri (2.15.10) dalli (2.7.11) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.3) diff --git a/gemfiles/ruby_2.4_contrib_old.gemfile.lock b/gemfiles/ruby_2.4_contrib_old.gemfile.lock index 6c9c0a491c..154496f587 100644 --- a/gemfiles/ruby_2.4_contrib_old.gemfile.lock +++ b/gemfiles/ruby_2.4_contrib_old.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -27,7 +27,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_core_old.gemfile.lock b/gemfiles/ruby_2.4_core_old.gemfile.lock index 03be1c2807..7b2bdfbf88 100644 --- a/gemfiles/ruby_2.4_core_old.gemfile.lock +++ b/gemfiles/ruby_2.4_core_old.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -27,7 +27,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_elasticsearch_7.gemfile.lock b/gemfiles/ruby_2.4_elasticsearch_7.gemfile.lock index 3ee0459e75..808e575a49 100644 --- a/gemfiles/ruby_2.4_elasticsearch_7.gemfile.lock +++ b/gemfiles/ruby_2.4_elasticsearch_7.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -28,7 +28,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_hanami_1.gemfile.lock b/gemfiles/ruby_2.4_hanami_1.gemfile.lock index 7fdc89c5da..8d6d9e0458 100644 --- a/gemfiles/ruby_2.4_hanami_1.gemfile.lock +++ b/gemfiles/ruby_2.4_hanami_1.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -27,7 +27,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_http.gemfile.lock b/gemfiles/ruby_2.4_http.gemfile.lock index eb19a63c0b..45b2cfe605 100644 --- a/gemfiles/ruby_2.4_http.gemfile.lock +++ b/gemfiles/ruby_2.4_http.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -27,7 +27,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_opensearch_2.gemfile.lock b/gemfiles/ruby_2.4_opensearch_2.gemfile.lock index a0aec2451a..43cca1d8b0 100644 --- a/gemfiles/ruby_2.4_opensearch_2.gemfile.lock +++ b/gemfiles/ruby_2.4_opensearch_2.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -28,7 +28,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_opentracing.gemfile.lock b/gemfiles/ruby_2.4_opentracing.gemfile.lock index 61dd0be8fd..f7787e728b 100644 --- a/gemfiles/ruby_2.4_opentracing.gemfile.lock +++ b/gemfiles/ruby_2.4_opentracing.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -28,7 +28,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_rack_1.gemfile.lock b/gemfiles/ruby_2.4_rack_1.gemfile.lock index 991dc9f368..894b776dac 100644 --- a/gemfiles/ruby_2.4_rack_1.gemfile.lock +++ b/gemfiles/ruby_2.4_rack_1.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -27,7 +27,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_rack_2.gemfile.lock b/gemfiles/ruby_2.4_rack_2.gemfile.lock index cfbcc2705d..f015823ada 100644 --- a/gemfiles/ruby_2.4_rack_2.gemfile.lock +++ b/gemfiles/ruby_2.4_rack_2.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -27,7 +27,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_rack_3.gemfile.lock b/gemfiles/ruby_2.4_rack_3.gemfile.lock index eeac990a6b..051294fd39 100644 --- a/gemfiles/ruby_2.4_rack_3.gemfile.lock +++ b/gemfiles/ruby_2.4_rack_3.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -27,7 +27,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_rails5_mysql2.gemfile.lock b/gemfiles/ruby_2.4_rails5_mysql2.gemfile.lock index 23890d70a3..754d7926da 100644 --- a/gemfiles/ruby_2.4_rails5_mysql2.gemfile.lock +++ b/gemfiles/ruby_2.4_rails5_mysql2.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -70,7 +70,7 @@ GEM rexml crass (1.0.6) cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_rails5_postgres.gemfile.lock b/gemfiles/ruby_2.4_rails5_postgres.gemfile.lock index f802d72002..d590bacd8c 100644 --- a/gemfiles/ruby_2.4_rails5_postgres.gemfile.lock +++ b/gemfiles/ruby_2.4_rails5_postgres.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -70,7 +70,7 @@ GEM rexml crass (1.0.6) cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_rails5_postgres_redis.gemfile.lock b/gemfiles/ruby_2.4_rails5_postgres_redis.gemfile.lock index b0ded0ec68..944e9ff568 100644 --- a/gemfiles/ruby_2.4_rails5_postgres_redis.gemfile.lock +++ b/gemfiles/ruby_2.4_rails5_postgres_redis.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -70,7 +70,7 @@ GEM rexml crass (1.0.6) cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_rails5_postgres_redis_activesupport.gemfile.lock b/gemfiles/ruby_2.4_rails5_postgres_redis_activesupport.gemfile.lock index ad7b929fd3..5515e31cd4 100644 --- a/gemfiles/ruby_2.4_rails5_postgres_redis_activesupport.gemfile.lock +++ b/gemfiles/ruby_2.4_rails5_postgres_redis_activesupport.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -70,7 +70,7 @@ GEM rexml crass (1.0.6) cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_rails5_postgres_sidekiq.gemfile.lock b/gemfiles/ruby_2.4_rails5_postgres_sidekiq.gemfile.lock index 50f795b26f..9353dfadc6 100644 --- a/gemfiles/ruby_2.4_rails5_postgres_sidekiq.gemfile.lock +++ b/gemfiles/ruby_2.4_rails5_postgres_sidekiq.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -71,7 +71,7 @@ GEM rexml crass (1.0.6) cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_rails5_semantic_logger.gemfile.lock b/gemfiles/ruby_2.4_rails5_semantic_logger.gemfile.lock index 1065c8356d..45c0db9f43 100644 --- a/gemfiles/ruby_2.4_rails5_semantic_logger.gemfile.lock +++ b/gemfiles/ruby_2.4_rails5_semantic_logger.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -70,7 +70,7 @@ GEM rexml crass (1.0.6) cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_redis_3.gemfile.lock b/gemfiles/ruby_2.4_redis_3.gemfile.lock index efd58eb74b..6e0c5404d7 100644 --- a/gemfiles/ruby_2.4_redis_3.gemfile.lock +++ b/gemfiles/ruby_2.4_redis_3.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -27,7 +27,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_redis_4.gemfile.lock b/gemfiles/ruby_2.4_redis_4.gemfile.lock index ec30662fa9..c65b514455 100644 --- a/gemfiles/ruby_2.4_redis_4.gemfile.lock +++ b/gemfiles/ruby_2.4_redis_4.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -27,7 +27,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_relational_db.gemfile.lock b/gemfiles/ruby_2.4_relational_db.gemfile.lock index e6aea21e2b..f21fbd43e8 100644 --- a/gemfiles/ruby_2.4_relational_db.gemfile.lock +++ b/gemfiles/ruby_2.4_relational_db.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -39,7 +39,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) delayed_job (4.1.11) diff --git a/gemfiles/ruby_2.4_resque2_redis3.gemfile.lock b/gemfiles/ruby_2.4_resque2_redis3.gemfile.lock index 9ed487529b..4de8009e5a 100644 --- a/gemfiles/ruby_2.4_resque2_redis3.gemfile.lock +++ b/gemfiles/ruby_2.4_resque2_redis3.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -27,7 +27,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_resque2_redis4.gemfile.lock b/gemfiles/ruby_2.4_resque2_redis4.gemfile.lock index aa8e91b31c..6e30bc9211 100644 --- a/gemfiles/ruby_2.4_resque2_redis4.gemfile.lock +++ b/gemfiles/ruby_2.4_resque2_redis4.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -27,7 +27,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/gemfiles/ruby_2.4_sinatra.gemfile.lock b/gemfiles/ruby_2.4_sinatra.gemfile.lock index d526e26aa8..d9779972b1 100644 --- a/gemfiles/ruby_2.4_sinatra.gemfile.lock +++ b/gemfiles/ruby_2.4_sinatra.gemfile.lock @@ -2,7 +2,7 @@ PATH remote: .. specs: ddtrace (1.18.0) - datadog-ci (~> 0.5.0) + datadog-ci (~> 0.6.0) debase-ruby_core_source (= 3.2.3) libdatadog (~> 5.0.0.1.0) libddwaf (~> 1.14.0.0.0) @@ -27,7 +27,7 @@ GEM crack (0.4.5) rexml cri (2.15.10) - datadog-ci (0.5.0) + datadog-ci (0.6.0) msgpack debase-ruby_core_source (3.2.3) diff-lcs (1.5.0) diff --git a/integration/images/ruby/3.3/Dockerfile b/integration/images/ruby/3.3/Dockerfile index 686620c687..38e69fd213 100644 --- a/integration/images/ruby/3.3/Dockerfile +++ b/integration/images/ruby/3.3/Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.3-rc +FROM ruby:3.3 ENV DEBIAN_FRONTEND=noninteractive diff --git a/lib/datadog/core/configuration/settings.rb b/lib/datadog/core/configuration/settings.rb index 3df60e8259..8bc13981bd 100644 --- a/lib/datadog/core/configuration/settings.rb +++ b/lib/datadog/core/configuration/settings.rb @@ -591,6 +591,42 @@ def initialize(*_) o.env Core::Telemetry::Ext::ENV_HEARTBEAT_INTERVAL o.default 60.0 end + + # The install id of the application. + # + # This method is used internally, by library injection. + # + # @default `DD_INSTRUMENTATION_INSTALL_ID` environment variable, otherwise `nil`. + # @return [String,nil] + # @!visibility private + option :install_id do |o| + o.type :string, nilable: true + o.env Core::Telemetry::Ext::ENV_INSTALL_ID + end + + # The install type of the application. + # + # This method is used internally, by library injection. + # + # @default `DD_INSTRUMENTATION_INSTALL_TYPE` environment variable, otherwise `nil`. + # @return [String,nil] + # @!visibility private + option :install_type do |o| + o.type :string, nilable: true + o.env Core::Telemetry::Ext::ENV_INSTALL_TYPE + end + + # The install time of the application. + # + # This method is used internally, by library injection. + # + # @default `DD_INSTRUMENTATION_INSTALL_TIME` environment variable, otherwise `nil`. + # @return [String,nil] + # @!visibility private + option :install_time do |o| + o.type :string, nilable: true + o.env Core::Telemetry::Ext::ENV_INSTALL_TIME + end end # Remote configuration diff --git a/lib/datadog/core/telemetry/collector.rb b/lib/datadog/core/telemetry/collector.rb index f511a63819..f455026d89 100644 --- a/lib/datadog/core/telemetry/collector.rb +++ b/lib/datadog/core/telemetry/collector.rb @@ -9,6 +9,7 @@ require_relative 'v1/application' require_relative 'v1/dependency' require_relative 'v1/host' +require_relative 'v1/install_signature' require_relative 'v1/integration' require_relative 'v1/product' require_relative '../transport/ext' @@ -81,6 +82,15 @@ def host ) end + # Forms a telemetry app-started install_signature object + def install_signature + Telemetry::V1::InstallSignature.new( + install_id: Datadog.configuration.dig('telemetry', 'install_id'), + install_type: Datadog.configuration.dig('telemetry', 'install_type'), + install_time: Datadog.configuration.dig('telemetry', 'install_time'), + ) + end + # Forms a telemetry app-started integrations object def integrations Datadog.registry.map do |integration| diff --git a/lib/datadog/core/telemetry/event.rb b/lib/datadog/core/telemetry/event.rb index f28fb8995f..1fe6a49aef 100644 --- a/lib/datadog/core/telemetry/event.rb +++ b/lib/datadog/core/telemetry/event.rb @@ -60,7 +60,8 @@ def app_started dependencies: dependencies, integrations: integrations, configuration: configurations, - additional_payload: additional_payload + additional_payload: additional_payload, + install_signature: install_signature ) end diff --git a/lib/datadog/core/telemetry/ext.rb b/lib/datadog/core/telemetry/ext.rb index 28716f2de7..4ce312a525 100644 --- a/lib/datadog/core/telemetry/ext.rb +++ b/lib/datadog/core/telemetry/ext.rb @@ -6,6 +6,9 @@ module Telemetry module Ext ENV_ENABLED = 'DD_INSTRUMENTATION_TELEMETRY_ENABLED' ENV_HEARTBEAT_INTERVAL = 'DD_TELEMETRY_HEARTBEAT_INTERVAL' + ENV_INSTALL_ID = 'DD_INSTRUMENTATION_INSTALL_ID' + ENV_INSTALL_TYPE = 'DD_INSTRUMENTATION_INSTALL_TYPE' + ENV_INSTALL_TIME = 'DD_INSTRUMENTATION_INSTALL_TIME' end end end diff --git a/lib/datadog/core/telemetry/v1/app_event.rb b/lib/datadog/core/telemetry/v1/app_event.rb index 11f57e41b3..b33e5fb08b 100644 --- a/lib/datadog/core/telemetry/v1/app_event.rb +++ b/lib/datadog/core/telemetry/v1/app_event.rb @@ -10,18 +10,24 @@ class AppEvent :additional_payload, :configuration, :dependencies, + :install_signature, :integrations # @param additional_payload [Array] List of Additional payload to track (any key # value not mentioned and doesn't fit under a metric) # @param configuration [Array] List of Tracer related configuration data # @param dependencies [Array] List of all loaded modules requested by the app + # @param install_signature [Telemetry::V1::InstallSignature] Install signature data # @param integrations [Array] List of integrations that are available within the app # and applicable to be traced - def initialize(additional_payload: nil, configuration: nil, dependencies: nil, integrations: nil) + def initialize( + additional_payload: nil, configuration: nil, dependencies: nil, install_signature: nil, + integrations: nil + ) @additional_payload = additional_payload @configuration = configuration @dependencies = dependencies + @install_signature = install_signature @integrations = integrations end @@ -30,6 +36,7 @@ def to_h hash[:additional_payload] = map_hash(@additional_payload) if @additional_payload hash[:configuration] = map_hash(@configuration) if @configuration hash[:dependencies] = map_array(@dependencies) if @dependencies + hash[:install_signature] = @install_signature.to_h if @install_signature hash[:integrations] = map_array(@integrations) if @integrations end end diff --git a/lib/datadog/core/telemetry/v1/install_signature.rb b/lib/datadog/core/telemetry/v1/install_signature.rb new file mode 100644 index 0000000000..b5ad38db71 --- /dev/null +++ b/lib/datadog/core/telemetry/v1/install_signature.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Datadog + module Core + module Telemetry + module V1 + # Describes attributes for install signature + class InstallSignature + using Core::Utils::Hash::Refinement + + attr_reader \ + :install_id, + :install_type, + :install_time + + # @param id [String,nil] Install ID + # @param type [String,nil] Install type + # @param type [String,nil] Install time + def initialize(install_id:, install_type:, install_time:) + @install_id = install_id + @install_type = install_type + @install_time = install_time + end + + def to_h + hash = { + install_id: @install_id, + install_type: @install_type, + install_time: @install_time + } + hash.compact! + hash + end + end + end + end + end +end diff --git a/lib/datadog/profiling/component.rb b/lib/datadog/profiling/component.rb index d0083a583f..9eb160eaf2 100644 --- a/lib/datadog/profiling/component.rb +++ b/lib/datadog/profiling/component.rb @@ -202,17 +202,26 @@ def self.build_profiler_component(settings:, agent_settings:, optional_tracer:) private_class_method def self.enable_heap_profiling?(settings, allocation_profiling_enabled) heap_profiling_enabled = settings.profiling.advanced.experimental_heap_enabled - if heap_profiling_enabled && !allocation_profiling_enabled - raise ArgumentError, 'Heap profiling requires allocation profiling to be enabled' - end + return false unless heap_profiling_enabled - if heap_profiling_enabled + if RUBY_VERSION.start_with?('2.') && RUBY_VERSION < '2.7' Datadog.logger.warn( - 'Enabled experimental heap profiling. This is experimental, not recommended, and will increase overhead!' + 'Heap profiling currently relies on features introduced in Ruby 2.7 and will be forcibly disabled. '\ + 'Please upgrade to Ruby >= 2.7 in order to use this feature.' ) + return false + end + + unless allocation_profiling_enabled + raise ArgumentError, + 'Heap profiling requires allocation profiling to be enabled' end - heap_profiling_enabled + Datadog.logger.warn( + 'Enabled experimental heap profiling. This is experimental, not recommended, and will increase overhead!' + ) + + true end private_class_method def self.no_signals_workaround_enabled?(settings) # rubocop:disable Metrics/MethodLength @@ -322,9 +331,12 @@ def self.build_profiler_component(settings:, agent_settings:, optional_tracer:) return true unless mysql2_client_class && mysql2_client_class.respond_to?(:info) - libmysqlclient_version = Gem::Version.new(mysql2_client_class.info[:version]) + info = mysql2_client_class.info + libmysqlclient_version = Gem::Version.new(info[:version]) - compatible = libmysqlclient_version >= Gem::Version.new('8.0.0') + compatible = + libmysqlclient_version >= Gem::Version.new('8.0.0') || + looks_like_mariadb?(info, libmysqlclient_version) Datadog.logger.debug( "The `mysql2` gem is using #{compatible ? 'a compatible' : 'an incompatible'} version of " \ @@ -363,6 +375,35 @@ def self.build_profiler_component(settings:, agent_settings:, optional_tracer:) 2.0 end end + + # To add just a bit more complexity to our detection code, in https://github.com/DataDog/dd-trace-rb/issues/3334 + # a user reported that our code was incorrectly flagging the mariadb variant of libmysqlclient as being + # incompatible. In fact we have no reports of the mariadb variant needing the "no signals" workaround, + # so we flag it as compatible when it's in use. + # + # A problem is that there doesn't seem to be an obvious way to query the mysql2 gem on which kind of + # libmysqlclient it's using, so we detect it by looking at the version. + # + # The info method for mysql2 with mariadb looks something like this: + # `{:id=>30308, :version=>"3.3.8", :header_version=>"11.2.2"}` + # + # * The version seems to come from https://github.com/mariadb-corporation/mariadb-connector-c and the latest + # one is 3.x. + # * The header_version is what people usually see as the "mariadb version" + # + # As a comparison, for libmysql the info looks like: + # * `{:id=>80035, :version=>"8.0.35", :header_version=>"8.0.35"}` + # + # Thus our detection is version 4 or older, because libmysqlclient 4 is almost 20 years old so it's most probably + # not that one + header_version being 10 or newer, since according to https://endoflife.date/mariadb that's a + # sane range for modern mariadb releases. + private_class_method def self.looks_like_mariadb?(info, libmysqlclient_version) + header_version = Gem::Version.new(info[:header_version]) if info[:header_version] + + !!(header_version && + libmysqlclient_version < Gem::Version.new('5.0.0') && + header_version >= Gem::Version.new('10.0.0')) + end end end end diff --git a/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb b/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb index 2402c3c2d9..04b097b23b 100644 --- a/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +++ b/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb @@ -50,6 +50,10 @@ class Settings < Contrib::Configuration::Settings o.type :string, nilable: true o.env Ext::ENV_PEER_SERVICE end + + option :on_error do |o| + o.type :proc, nilable: true + end end end end diff --git a/lib/datadog/tracing/contrib/mysql2/instrumentation.rb b/lib/datadog/tracing/contrib/mysql2/instrumentation.rb index 7153574f67..ed9fce2aac 100644 --- a/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +++ b/lib/datadog/tracing/contrib/mysql2/instrumentation.rb @@ -21,8 +21,9 @@ def self.included(base) module InstanceMethods def query(sql, options = {}) service = Datadog.configuration_for(self, :service_name) || datadog_configuration[:service_name] + on_error = Datadog.configuration_for(self, :on_error) || datadog_configuration[:on_error] - Tracing.trace(Ext::SPAN_QUERY, service: service) do |span, trace_op| + Tracing.trace(Ext::SPAN_QUERY, service: service, on_error: on_error) do |span, trace_op| span.resource = sql span.span_type = Tracing::Metadata::Ext::SQL::TYPE diff --git a/sig/datadog/core/telemetry/v1/install_signature.rbs b/sig/datadog/core/telemetry/v1/install_signature.rbs new file mode 100644 index 0000000000..cd07bbd4bc --- /dev/null +++ b/sig/datadog/core/telemetry/v1/install_signature.rbs @@ -0,0 +1,24 @@ +module Datadog + module Core + module Telemetry + module V1 + class InstallSignature + @install_id: untyped + + @install_type: untyped + + @install_time: untyped + + attr_reader install_id: untyped + + attr_reader install_type: untyped + + attr_reader install_time: untyped + def initialize: (install_id: untyped, install_type: untyped, install_time: untyped) -> void + + def to_h: () -> untyped + end + end + end + end +end diff --git a/sig/datadog/profiling/component.rbs b/sig/datadog/profiling/component.rbs index 5de8b61eb6..25673784cb 100644 --- a/sig/datadog/profiling/component.rbs +++ b/sig/datadog/profiling/component.rbs @@ -42,6 +42,7 @@ module Datadog def self.incompatible_passenger_version?: () -> bool def self.flush_interval: (untyped settings) -> ::Numeric def self.valid_overhead_target: (::Float overhead_target_percentage) -> ::Float + def self.looks_like_mariadb?: ({ header_version: ::String? }, ::Gem::Version) -> bool end end end diff --git a/spec/datadog/core/configuration/settings_spec.rb b/spec/datadog/core/configuration/settings_spec.rb index d5fc1bf8c0..ba76eafa2d 100644 --- a/spec/datadog/core/configuration/settings_spec.rb +++ b/spec/datadog/core/configuration/settings_spec.rb @@ -1331,6 +1331,99 @@ .to change { settings.telemetry.heartbeat_interval_seconds }.from(1.1).to(2.2) end end + + describe '#install_id' do + subject(:install_id) { settings.telemetry.install_id } + let(:env_var_name) { 'DD_INSTRUMENTATION_INSTALL_ID' } + + context 'when DD_INSTRUMENTATION_INSTALL_ID' do + context 'is not defined' do + let(:env_var_value) { nil } + + it { is_expected.to eq nil } + end + + context 'is defined' do + let(:env_var_value) { '68e75c48-57ca-4a12-adfc-575c4b05fcbe' } + + it { is_expected.to eq '68e75c48-57ca-4a12-adfc-575c4b05fcbe' } + end + end + end + + describe '#install_id=' do + let(:env_var_name) { 'DD_INSTRUMENTATION_INSTALL_ID' } + let(:env_var_value) { '68e75c48-57ca-4a12-adfc-575c4b05fcbe' } + + it 'updates the #install_id setting' do + expect { settings.telemetry.install_id = 'abc123' } + .to change { settings.telemetry.install_id } + .from('68e75c48-57ca-4a12-adfc-575c4b05fcbe') + .to('abc123') + end + end + + describe '#install_type' do + subject(:install_id) { settings.telemetry.install_type } + let(:env_var_name) { 'DD_INSTRUMENTATION_INSTALL_TYPE' } + + context 'when DD_INSTRUMENTATION_INSTALL_TYPE' do + context 'is not defined' do + let(:env_var_value) { nil } + + it { is_expected.to eq nil } + end + + context 'is defined' do + let(:env_var_value) { 'k8s_single_step' } + + it { is_expected.to eq 'k8s_single_step' } + end + end + end + + describe '#install_type=' do + let(:env_var_name) { 'DD_INSTRUMENTATION_INSTALL_TYPE' } + let(:env_var_value) { 'k8s_single_step' } + + it 'updates the #install_type setting' do + expect { settings.telemetry.install_type = 'abc123' } + .to change { settings.telemetry.install_type } + .from('k8s_single_step') + .to('abc123') + end + end + + describe '#install_time' do + subject(:install_id) { settings.telemetry.install_time } + let(:env_var_name) { 'DD_INSTRUMENTATION_INSTALL_TIME' } + + context 'when DD_INSTRUMENTATION_INSTALL_TIME' do + context 'is not defined' do + let(:env_var_value) { nil } + + it { is_expected.to eq nil } + end + + context 'is defined' do + let(:env_var_value) { '1703188212' } + + it { is_expected.to eq '1703188212' } + end + end + end + + describe '#install_time=' do + let(:env_var_name) { 'DD_INSTRUMENTATION_INSTALL_TIME' } + let(:env_var_value) { '1703188212' } + + it 'updates the #install_time setting' do + expect { settings.telemetry.install_time = 'abc123' } + .to change { settings.telemetry.install_time } + .from('1703188212') + .to('abc123') + end + end end describe '#remote' do diff --git a/spec/datadog/core/telemetry/collector_spec.rb b/spec/datadog/core/telemetry/collector_spec.rb index e0a8168a86..df5c488ce3 100644 --- a/spec/datadog/core/telemetry/collector_spec.rb +++ b/spec/datadog/core/telemetry/collector_spec.rb @@ -18,6 +18,18 @@ RSpec.describe Datadog::Core::Telemetry::Collector do let(:dummy_class) { Class.new { extend(Datadog::Core::Telemetry::Collector) } } + before do + # We don't care about details of profiling initialization (which requires + # interacting with native extension) in this suite. This initialization is + # tested in other suites. Thus, mock it to nil throughout. + # NOTE: We could have used a double but that leads to messy configuration + # lifecycle as we'd need to do a full reconfiguration in an `after` block + # (which would require extra allow/expect for unrelated things). The global + # reset with `around` happens already outside of a test context where it is + # forbidden to interact with doubles (and thus we can't call shutdown! on it) + allow(Datadog::Profiling::Component).to receive(:build_profiler_component).and_return(nil) + end + describe '#application' do subject(:application) { dummy_class.application } let(:env_service) { 'default-service' } @@ -98,7 +110,6 @@ require 'datadog/appsec' before do - allow_any_instance_of(Datadog::Profiling::Profiler).to receive(:start) if PlatformHelpers.mri? Datadog.configure do |c| c.profiling.enabled = true c.appsec.enabled = true @@ -262,7 +273,6 @@ context 'when profiling is enabled' do before do stub_const('Datadog::Core::Environment::Ext::TRACER_VERSION', '4.2') - allow_any_instance_of(Datadog::Profiling::Profiler).to receive(:start) Datadog.configure do |c| c.profiling.enabled = true end @@ -348,6 +358,77 @@ class << Datadog.configuration it { is_expected.to be_a_kind_of(Datadog::Core::Telemetry::V1::Host) } end + describe '#install_signature' do + subject(:install_signature) { dummy_class.install_signature } + + it { is_expected.to be_a_kind_of(Datadog::Core::Telemetry::V1::InstallSignature) } + + describe ':install_id' do + subject(:install_id) { install_signature.install_id } + + context 'when DD_INSTRUMENTATION_INSTALL_ID not set' do + it('is nil when unset') { is_expected.to be_nil } + end + + context 'when DD_INSTRUMENTATION_INSTALL_ID set' do + let(:install_id) { '68e75c48-57ca-4a12-adfc-575c4b05fcbe' } + + before do + Datadog.configure do |c| + c.telemetry.install_id = install_id + end + end + after do + Datadog.configuration.reset! + end + + it { is_expected.to eql(install_id) } + end + end + + describe ':install_type' do + subject(:install_type) { install_signature.install_type } + + context 'when DD_INSTRUMENTATION_INSTALL_TYPE not set' do + it('is nil when unset') { is_expected.to be_nil } + end + + context 'when DD_INSTRUMENTATION_INSTALL_TYPE set' do + before do + Datadog.configure do |c| + c.telemetry.install_type = install_type + end + end + after do + Datadog.configuration.reset! + end + + it { is_expected.to eql(install_type) } + end + end + + describe ':install_time' do + subject(:install_time) { install_signature.install_time } + + context 'when DD_INSTRUMENTATION_INSTALL_TIME not set' do + it('is nil when unset') { is_expected.to be_nil } + end + + context 'when DD_INSTRUMENTATION_INSTALL_TIME set' do + before do + Datadog.configure do |c| + c.telemetry.install_time = install_time + end + end + after do + Datadog.configuration.reset! + end + + it { is_expected.to eql(install_time) } + end + end + end + describe '#integrations' do subject(:integrations) { dummy_class.integrations } diff --git a/spec/datadog/core/telemetry/v1/app_event_spec.rb b/spec/datadog/core/telemetry/v1/app_event_spec.rb index a6301bd99c..87730f05f7 100644 --- a/spec/datadog/core/telemetry/v1/app_event_spec.rb +++ b/spec/datadog/core/telemetry/v1/app_event_spec.rb @@ -10,6 +10,7 @@ additional_payload: additional_payload, configuration: configuration, dependencies: dependencies, + install_signature: install_signature, integrations: integrations, ) end @@ -17,6 +18,13 @@ let(:additional_payload) { [{ name: 'logger.level', value: 1 }] } let(:configuration) { [{ name: 'DD_TRACE_DEBUG', value: false }] } let(:dependencies) { [Datadog::Core::Telemetry::V1::Dependency.new(name: 'pg')] } + let(:install_signature) do + Datadog::Core::Telemetry::V1::InstallSignature.new( + install_id: '123', + install_type: 'docker', + install_time: '1703188212' + ) + end let(:integrations) { [Datadog::Core::Telemetry::V1::Integration.new(name: 'pg', enabled: true)] } it do @@ -24,6 +32,7 @@ additional_payload: additional_payload, configuration: configuration, dependencies: dependencies, + install_signature: install_signature, integrations: integrations, ) end @@ -98,6 +107,24 @@ end end + context 'when :install_signature' do + context 'is nil' do + let(:install_signature) { nil } + it { is_expected.to be_a_kind_of(described_class) } + end + + context 'is InstallSignature' do + let(:integrations) do + Datadog::Core::Telemetry::V1::InstallSignature.new( + install_id: '123', + install_type: 'docker', + install_time: '1703188212' + ) + end + it { is_expected.to be_a_kind_of(described_class) } + end + end + context 'when :integrations' do context 'is nil' do let(:integrations) { nil } @@ -129,6 +156,7 @@ let(:additional_payload) { nil } let(:configuration) { nil } let(:dependencies) { nil } + let(:install_signature) { nil } let(:integrations) { nil } it do @@ -140,6 +168,7 @@ let(:additional_payload) { nil } let(:configuration) { nil } let(:dependencies) { nil } + let(:install_signature) { nil } let(:integrations) { [Datadog::Core::Telemetry::V1::Integration.new(name: 'pg', enabled: true)] } it do @@ -153,6 +182,13 @@ let(:additional_payload) { { 'tracing.enabled' => true, 'profiling.enabled' => false } } let(:configuration) { { DD_AGENT_HOST: 'localhost', DD_TRACE_SAMPLE_RATE: '1' } } let(:dependencies) { [Datadog::Core::Telemetry::V1::Dependency.new(name: 'pg')] } + let(:install_signature) do + Datadog::Core::Telemetry::V1::InstallSignature.new( + install_id: '123', + install_type: 'docker', + install_time: '1703188212' + ) + end let(:integrations) { [Datadog::Core::Telemetry::V1::Integration.new(name: 'pg', enabled: true)] } it do @@ -162,6 +198,7 @@ configuration: [{ name: 'DD_AGENT_HOST', value: 'localhost' }, { name: 'DD_TRACE_SAMPLE_RATE', value: '1' }], dependencies: [{ name: 'pg' }], + install_signature: { install_id: '123', install_type: 'docker', install_time: '1703188212' }, integrations: [{ enabled: true, name: 'pg' }] ) end diff --git a/spec/datadog/core/telemetry/v1/install_signature.spec.rb b/spec/datadog/core/telemetry/v1/install_signature.spec.rb new file mode 100644 index 0000000000..ac244b21f3 --- /dev/null +++ b/spec/datadog/core/telemetry/v1/install_signature.spec.rb @@ -0,0 +1,72 @@ +require 'spec_helper' + +require 'datadog/core/telemetry/v1/product' + +RSpec.describe Datadog::Core::Telemetry::V1::InstallSignature do + subject(:install_signature) do + described_class.new(install_id: install_id, install_type: install_type, install_time: install_time) + end + + let(:install_id) { '68e75c48-57ca-4a12-adfc-575c4b05fcbe' } + let(:install_type) { 'k8s_single_step' } + let(:install_time) { '1703188212' } + + it { is_expected.to have_attributes(install_id: install_id, install_type: install_type, install_time: install_time) } + + describe '#initialize' do + context 'when :install_id' do + context 'is nil' do + let(:install_id) { nil } + it { is_expected.to have_attributes(install_id: nil, install_type: install_type, install_time: install_time) } + it { is_expected.to be_a_kind_of(described_class) } + end + + context 'is valid' do + let(:install_id) { '68e75c48-57ca-4a12-adfc-575c4b05fcbe' } + it { is_expected.to be_a_kind_of(described_class) } + end + end + + context 'when :install_type' do + context 'is nil' do + let(:install_type) { nil } + it { is_expected.to have_attributes(install_id: install_id, install_type: nil, install_time: install_time) } + it { is_expected.to be_a_kind_of(described_class) } + end + + context 'is valid' do + let(:install_type) { 'k8s_single_step' } + it { is_expected.to be_a_kind_of(described_class) } + end + end + + context 'when :install_time' do + context 'is nil' do + let(:install_time) { nil } + it { is_expected.to have_attributes(install_id: install_id, install_type: install_type, install_time: nil) } + it { is_expected.to be_a_kind_of(described_class) } + end + + context 'is valid' do + let(:install_time) { '1703188212' } + it { is_expected.to be_a_kind_of(described_class) } + end + end + end + + describe '#to_h' do + subject(:to_h) { install_signature.to_h } + + let(:install_id) { '68e75c48-57ca-4a12-adfc-575c4b05fcbe' } + let(:install_type) { 'k8s_single_step' } + let(:install_time) { '1703188212' } + + it do + is_expected.to eq( + install_id: install_id, + install_type: install_type, + install_time: install_time + ) + end + end +end diff --git a/spec/datadog/profiling/collectors/cpu_and_wall_time_worker_spec.rb b/spec/datadog/profiling/collectors/cpu_and_wall_time_worker_spec.rb index 3533b4699f..b4e18d9fa9 100644 --- a/spec/datadog/profiling/collectors/cpu_and_wall_time_worker_spec.rb +++ b/spec/datadog/profiling/collectors/cpu_and_wall_time_worker_spec.rb @@ -570,21 +570,30 @@ end it 'records live heap objects' do - pending "heap profiling isn't actually implemented just yet" - stub_const('CpuAndWallTimeWorkerSpec::TestStruct', Struct.new(:foo)) start - test_num_allocated_object.times { CpuAndWallTimeWorkerSpec::TestStruct.new } + live_objects = Array.new(test_num_allocated_object) + + test_num_allocated_object.times { |i| live_objects[i] = CpuAndWallTimeWorkerSpec::TestStruct.new } allocation_line = __LINE__ - 1 cpu_and_wall_time_worker.stop - relevant_sample = samples_for_thread(samples_from_pprof(recorder.serialize!), Thread.current) - .find { |s| s.locations.first.lineno == allocation_line && s.locations.first.path == __FILE__ } + test_struct_heap_sample = lambda { |sample| + first_frame = sample.locations.first + first_frame.lineno == allocation_line && + first_frame.path == __FILE__ && + first_frame.base_label == 'new' && + sample.labels[:'allocation class'] == 'CpuAndWallTimeWorkerSpec::TestStruct' && + (sample.values[:'heap-live-samples'] || 0) > 0 + } + + relevant_sample = samples_from_pprof(recorder.serialize!) + .find(&test_struct_heap_sample) - expect(relevant_sample.values[':heap-live-samples']).to eq test_num_allocated_object + expect(relevant_sample.values[:'heap-live-samples']).to eq test_num_allocated_object end end diff --git a/spec/datadog/profiling/component_spec.rb b/spec/datadog/profiling/component_spec.rb index efd37d42ef..4c8989b9cd 100644 --- a/spec/datadog/profiling/component_spec.rb +++ b/spec/datadog/profiling/component_spec.rb @@ -229,11 +229,26 @@ end context 'when heap profiling is enabled' do + # Universally supported ruby version for allocation profiling by default + let(:testing_version) { '2.7.2' } + before do settings.profiling.advanced.experimental_heap_enabled = true - # Universally supported ruby version for allocation profiling, we don't want to test those - # edge cases here - stub_const('RUBY_VERSION', '2.7.2') + stub_const('RUBY_VERSION', testing_version) + end + + context 'on a Ruby older than 2.7' do + let(:testing_version) { '2.6' } + + it 'initializes StackRecorder without heap sampling support and warns' do + expect(Datadog::Profiling::StackRecorder).to receive(:new) + .with(hash_including(heap_samples_enabled: false)) + .and_call_original + + expect(Datadog.logger).to receive(:warn).with(/upgrade to Ruby >= 2.7/) + + build_profiler_component + end end context 'and allocation profiling disabled' do @@ -605,6 +620,23 @@ end end + # See comments on looks_like_mariadb? for details on how this matching works + context "when mysql2 gem is linked to mariadb's version of libmysqlclient" do + before do + fake_client = double('Fake Mysql2::Client') + stub_const('Mysql2::Client', fake_client) + expect(fake_client).to receive(:info).and_return({ version: '4.9.99', header_version: '10.0.0' }) + end + + it { is_expected.to be false } + + it 'does not log any warning message' do + expect(Datadog.logger).to_not receive(:warn) + + no_signals_workaround_enabled? + end + end + context 'when mysql2 gem is using a version of libmysqlclient < 8.0.0' do before do fake_client = double('Fake Mysql2::Client') diff --git a/spec/datadog/profiling/spec_helper.rb b/spec/datadog/profiling/spec_helper.rb index fcb084106b..d35e4fa088 100644 --- a/spec/datadog/profiling/spec_helper.rb +++ b/spec/datadog/profiling/spec_helper.rb @@ -73,7 +73,10 @@ def object_id_from(thread_id) end def samples_for_thread(samples, thread) - samples.select { |sample| object_id_from(sample.labels.fetch(:'thread id')) == thread.object_id } + samples.select do |sample| + thread_id = sample.labels[:'thread id'] + thread_id && object_id_from(thread_id) == thread.object_id + end end # We disable heap_sample collection by default in tests since it requires some extra mocking/ diff --git a/spec/datadog/profiling/stack_recorder_spec.rb b/spec/datadog/profiling/stack_recorder_spec.rb index 73f73ae487..0a2289ae56 100644 --- a/spec/datadog/profiling/stack_recorder_spec.rb +++ b/spec/datadog/profiling/stack_recorder_spec.rb @@ -348,31 +348,58 @@ def sample_types_from(decoded_profile) let(:labels) { { 'label_a' => 'value_a', 'label_b' => 'value_b', 'state' => 'unknown' }.to_a } let(:a_string) { 'a beautiful string' } - let(:an_array) { [1..10] } + let(:an_array) { (1..10).to_a } let(:a_hash) { { 'a' => 1, 'b' => '2', 'c' => true } } let(:samples) { samples_from_pprof(encoded_pprof) } + def sample_allocation(obj) + # Heap sampling currently requires this 2-step process to first pass data about the allocated object... + described_class::Testing._native_track_object(stack_recorder, obj, sample_rate, obj.class.name) + Datadog::Profiling::Collectors::Stack::Testing + ._native_sample(Thread.current, stack_recorder, metric_values, labels, numeric_labels, 400, false) + end + before do - allocations = [a_string, an_array, 'a fearsome string', [-10..-1], a_hash, { 'z' => -1, 'y' => '-2', 'x' => false }] + allocations = [a_string, an_array, "a fearsome interpolated string: #{sample_rate}", (-10..-1).to_a, a_hash, + { 'z' => -1, 'y' => '-2', 'x' => false }, Object.new] @num_allocations = 0 allocations.each_with_index do |obj, i| - # Heap sampling currently requires this 2-step process to first pass data about the allocated object... - described_class::Testing._native_track_object(stack_recorder, obj, sample_rate) - # ...and then pass data about the allocation stacktrace (with 2 distinct stacktraces) + # Sample allocations with 2 distinct stacktraces if i.even? - Datadog::Profiling::Collectors::Stack::Testing - ._native_sample(Thread.current, stack_recorder, metric_values, labels, numeric_labels, 400, false) - else - # 401 used instead of 400 here just to make the branches different and appease Rubocop - Datadog::Profiling::Collectors::Stack::Testing - ._native_sample(Thread.current, stack_recorder, metric_values, labels, numeric_labels, 401, false) + sample_allocation(obj) # rubocop:disable Style/IdenticalConditionalBranches + else # rubocop:disable Lint/DuplicateBranch + sample_allocation(obj) # rubocop:disable Style/IdenticalConditionalBranches end @num_allocations += 1 + GC.start # Force each allocation to be done in its own GC epoch for interesting GC age labels end allocations.clear # The literals in the previous array are now dangling GC.start # And this will clear them, leaving only the non-literals which are still pointed to by the lets + + # NOTE: We've witnessed CI flakiness where some no longer referenced allocations may still be seen as alive + # after the previous GC. + # This might be an instance of the issues described in https://bugs.ruby-lang.org/issues/19460 + # and https://bugs.ruby-lang.org/issues/19041. We didn't get to the bottom of the + # reason but it might be that some machine context/register ends up still pointing to + # that last entry and thus manages to get it marked in the first GC. + # To reduce the likelihood of this happening we'll: + # * Allocate some more stuff and clear again + # * Do another GC + allocations = ["another fearsome interpolated string: #{sample_rate}", (-20..-10).to_a, + { 'a' => 1, 'b' => '2', 'c' => true }, Object.new] + allocations.clear + GC.start + end + + after do |example| + # This is here to facilitate troubleshooting when this test fails. Otherwise + # it's very hard to understand what may be happening. + if example.exception + puts('Heap recorder debugging info:') + puts(described_class::Testing._native_debug_heap_recorder(stack_recorder)) + end end context 'when disabled' do @@ -397,14 +424,36 @@ def sample_types_from(decoded_profile) end it 'include the stack and sample counts for the objects still left alive' do - pending 'heap_recorder implementation is currently missing' + # There should be 3 different allocation class labels so we expect 3 different heap samples + expect(heap_samples.size).to eq(3) - # We sample from 2 distinct locations - expect(heap_samples.size).to eq(2) + expect(heap_samples.map { |s| s.labels[:'allocation class'] }).to include('String', 'Array', 'Hash') + expect(heap_samples.map(&:labels)).to all(match(hash_including(:'gc gen age' => be_a(Integer).and(be >= 0)))) + end - sum_heap_samples = 0 - heap_samples.each { |s| sum_heap_samples += s.values[:'heap-live-samples'] } - expect(sum_heap_samples).to eq([a_string, an_array, a_hash].size * sample_rate) + it 'include accurate object ages' do + string_sample = heap_samples.find { |s| s.labels[:'allocation class'] == 'String' } + string_age = string_sample.labels[:'gc gen age'] + + array_sample = heap_samples.find { |s| s.labels[:'allocation class'] == 'Array' } + array_age = array_sample.labels[:'gc gen age'] + + hash_sample = heap_samples.find { |s| s.labels[:'allocation class'] == 'Hash' } + hash_age = hash_sample.labels[:'gc gen age'] + + unique_sorted_ages = [string_age, array_age, hash_age].uniq.sort + # Expect all ages to be different and to be in the reverse order of allocation + # Last to allocate => Lower age + expect(unique_sorted_ages).to match([hash_age, array_age, string_age]) + + # Validate that the age of the newest object makes sense. + # * We force a GC after each allocation and the hash sample should correspond to + # the 5th allocation in 7 (which means we expect at least 3 GC after all allocations + # are done) + # * We forced 1 extra GC at the end of our before (+1) + # * This test isn't memory intensive otherwise so lets give us an extra margin of 1 GC to account for any + # GC out of our control + expect(hash_age).to be_between(4, 5) end it 'keeps on reporting accurate samples for other profile types' do @@ -427,6 +476,32 @@ def sample_types_from(decoded_profile) expect(summed_values).to eq(expected_summed_values) end + + it "aren't lost when they happen concurrently with a long serialization" do + described_class::Testing._native_start_fake_slow_heap_serialization(stack_recorder) + + test_num_allocated_object = 123 + live_objects = Array.new(test_num_allocated_object) + + test_num_allocated_object.times do |i| + live_objects[i] = "this is string number #{i}" + sample_allocation(live_objects[i]) + end + + allocation_line = __LINE__ - 3 + + described_class::Testing._native_end_fake_slow_heap_serialization(stack_recorder) + + heap_samples_in_test_matcher = lambda { |sample| + (sample.values[:'heap-live-samples'] || 0) > 0 && sample.locations.any? do |location| + location.lineno == allocation_line && location.path == __FILE__ + end + } + + relevant_sample = heap_samples.find(&heap_samples_in_test_matcher) + expect(relevant_sample).not_to be nil + expect(relevant_sample.values[:'heap-live-samples']).to eq test_num_allocated_object * sample_rate + end end end @@ -559,4 +634,69 @@ def sample_types_from(decoded_profile) expect(stack_recorder.serialize.first).to be >= now end end + + describe 'Heap_recorder' do + context 'produces the same hash code for stack-based and location-based keys' do + it 'with empty stacks' do + described_class::Testing._native_check_heap_hashes([]) + end + + it 'with single-frame stacks' do + described_class::Testing._native_check_heap_hashes( + [ + ['a name', 'a filename', 123] + ] + ) + end + + it 'with multi-frame stacks' do + described_class::Testing._native_check_heap_hashes( + [ + ['a name', 'a filename', 123], + ['another name', 'anoter filename', 456], + ] + ) + end + + it 'with empty names' do + described_class::Testing._native_check_heap_hashes( + [ + ['', 'a filename', 123], + ] + ) + end + + it 'with empty filenames' do + described_class::Testing._native_check_heap_hashes( + [ + ['a name', '', 123], + ] + ) + end + + it 'with zero lines' do + described_class::Testing._native_check_heap_hashes( + [ + ['a name', 'a filename', 0] + ] + ) + end + + it 'with negative lines' do + described_class::Testing._native_check_heap_hashes( + [ + ['a name', 'a filename', -123] + ] + ) + end + + it 'with biiiiiiig lines' do + described_class::Testing._native_check_heap_hashes( + [ + ['a name', 'a filename', 4_000_000] + ] + ) + end + end + end end diff --git a/spec/datadog/tracing/contrib/mysql2/patcher_spec.rb b/spec/datadog/tracing/contrib/mysql2/patcher_spec.rb index b8ec2d9bc5..34341a1222 100644 --- a/spec/datadog/tracing/contrib/mysql2/patcher_spec.rb +++ b/spec/datadog/tracing/contrib/mysql2/patcher_spec.rb @@ -77,6 +77,20 @@ end it_behaves_like 'with sql comment propagation', span_op_name: 'mysql2.query' + + context 'when configured with `on_error`' do + before do + Datadog.configure_onto(client, on_error: ->(_span, _error) { false }) + end + + let(:sql_statement) { 'SELECT INVALID' } + + it 'does not mark span with error' do + expect { query }.to raise_error(Mysql2::Error) + + expect(span).not_to have_error + end + end end context 'when a successful query is made' do @@ -149,6 +163,15 @@ it_behaves_like 'configured peer service span', 'DD_TRACE_MYSQL2_PEER_SERVICE', error: Mysql2::Error do let(:configuration_options) { {} } end + + context 'when configured with `on_error`' do + let(:configuration_options) { { on_error: ->(_span, _error) { false } } } + + it 'does not mark span with error' do + expect { query }.to raise_error(Mysql2::Error) + expect(span).not_to have_error + end + end end end end diff --git a/vendor/rbs/mysql2/0/mysql2.rbs b/vendor/rbs/mysql2/0/mysql2.rbs index 6ec51ba118..884e53afa6 100644 --- a/vendor/rbs/mysql2/0/mysql2.rbs +++ b/vendor/rbs/mysql2/0/mysql2.rbs @@ -1,5 +1,5 @@ module Mysql2 class Client - def self.info: () -> { version: ::String } + def self.info: () -> { version: ::String, header_version: ::String? } end end