ddtrace 1.18.0 → 1.23.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +228 -2
- data/LICENSE-3rdparty.csv +1 -1
- data/bin/ddprofrb +15 -0
- data/bin/ddtracerb +3 -1
- data/ext/{ddtrace_profiling_loader/ddtrace_profiling_loader.c → datadog_profiling_loader/datadog_profiling_loader.c} +2 -2
- data/ext/{ddtrace_profiling_loader → datadog_profiling_loader}/extconf.rb +3 -3
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_cpu_and_wall_time_worker.c +312 -117
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +422 -0
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +101 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_dynamic_sampling_rate.c +22 -14
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_dynamic_sampling_rate.h +4 -0
- data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +156 -0
- data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.h +5 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_stack.c +43 -102
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_stack.h +10 -3
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_thread_context.c +272 -136
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_thread_context.h +2 -1
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/extconf.rb +28 -7
- data/ext/datadog_profiling_native_extension/heap_recorder.c +1047 -0
- data/ext/datadog_profiling_native_extension/heap_recorder.h +166 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/helpers.h +6 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/http_transport.c +15 -19
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/libdatadog_helpers.c +20 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/libdatadog_helpers.h +11 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/native_extension_helpers.rb +50 -4
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/private_vm_api_access.c +19 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/private_vm_api_access.h +4 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/profiling.c +18 -1
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +267 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/ruby_helpers.h +33 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/stack_recorder.c +476 -58
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/stack_recorder.h +3 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/time_helpers.h +2 -0
- data/lib/datadog/appsec/contrib/devise/tracking.rb +8 -0
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +45 -14
- data/lib/datadog/appsec/event.rb +1 -1
- data/lib/datadog/auto_instrument.rb +3 -0
- data/lib/datadog/core/configuration/components.rb +7 -6
- data/lib/datadog/core/configuration/option.rb +8 -6
- data/lib/datadog/core/configuration/settings.rb +259 -60
- data/lib/datadog/core/configuration.rb +20 -4
- data/lib/datadog/core/diagnostics/environment_logger.rb +4 -3
- data/lib/datadog/core/environment/class_count.rb +6 -6
- data/lib/datadog/core/environment/git.rb +25 -0
- data/lib/datadog/core/environment/identity.rb +18 -48
- data/lib/datadog/core/environment/platform.rb +7 -1
- data/lib/datadog/core/git/ext.rb +2 -23
- data/lib/datadog/core/remote/client/capabilities.rb +1 -1
- data/lib/datadog/core/remote/component.rb +25 -12
- data/lib/datadog/core/remote/ext.rb +1 -0
- data/lib/datadog/core/remote/negotiation.rb +2 -2
- data/lib/datadog/core/remote/tie/tracing.rb +39 -0
- data/lib/datadog/core/remote/tie.rb +27 -0
- data/lib/datadog/core/remote/transport/http/config.rb +1 -1
- data/lib/datadog/core/remote/worker.rb +7 -4
- data/lib/datadog/core/telemetry/client.rb +18 -10
- data/lib/datadog/core/telemetry/emitter.rb +9 -13
- data/lib/datadog/core/telemetry/event.rb +247 -56
- data/lib/datadog/core/telemetry/ext.rb +4 -0
- data/lib/datadog/core/telemetry/heartbeat.rb +1 -3
- data/lib/datadog/core/telemetry/http/ext.rb +4 -1
- data/lib/datadog/core/telemetry/http/response.rb +4 -0
- data/lib/datadog/core/telemetry/http/transport.rb +9 -4
- data/lib/datadog/core/telemetry/request.rb +59 -0
- data/lib/datadog/core/transport/ext.rb +2 -0
- data/lib/datadog/core/utils/url.rb +25 -0
- data/lib/datadog/opentelemetry/sdk/propagator.rb +3 -2
- data/lib/datadog/opentelemetry.rb +3 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +10 -4
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +36 -12
- data/lib/datadog/profiling/collectors/info.rb +101 -0
- data/lib/datadog/profiling/component.rb +210 -34
- data/lib/datadog/profiling/exporter.rb +23 -6
- data/lib/datadog/profiling/ext.rb +2 -0
- data/lib/datadog/profiling/flush.rb +6 -3
- data/lib/datadog/profiling/http_transport.rb +5 -1
- data/lib/datadog/profiling/load_native_extension.rb +19 -6
- data/lib/datadog/profiling/native_extension.rb +1 -1
- data/lib/datadog/profiling/scheduler.rb +4 -6
- data/lib/datadog/profiling/stack_recorder.rb +19 -4
- data/lib/datadog/profiling/tag_builder.rb +5 -0
- data/lib/datadog/profiling/tasks/exec.rb +3 -3
- data/lib/datadog/profiling/tasks/help.rb +3 -3
- data/lib/datadog/profiling.rb +13 -2
- data/lib/datadog/tracing/configuration/ext.rb +0 -1
- data/lib/datadog/tracing/configuration/settings.rb +2 -1
- data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/action_cable/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +1 -1
- data/lib/datadog/tracing/contrib/action_mailer/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/action_pack/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/action_view/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/active_job/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/active_model_serializers/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +11 -4
- data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/active_record/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/active_support/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/analytics.rb +0 -1
- data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/aws/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +20 -0
- data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +11 -1
- data/lib/datadog/tracing/contrib/configurable.rb +1 -1
- data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/dalli/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/delayed_job/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/excon/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/extensions.rb +6 -2
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +7 -0
- data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +1 -1
- data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/grape/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +2 -2
- data/lib/datadog/tracing/contrib/http/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/httpclient/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/httprb/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/kafka/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +2 -1
- data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/opensearch/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/pg/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +11 -4
- data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/presto/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/qless/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/qless/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/que/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/que/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/racecar/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +9 -2
- data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +0 -2
- data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/rails/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/rake/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/redis/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/redis/instrumentation.rb +2 -2
- data/lib/datadog/tracing/contrib/redis/patcher.rb +34 -21
- data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/resque/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/roda/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/roda/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/sequel/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/shoryuken/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/sneakers/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/stripe/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/sucker_punch/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +58 -0
- data/lib/datadog/tracing/contrib/trilogy/ext.rb +27 -0
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +94 -0
- data/lib/datadog/tracing/contrib/trilogy/integration.rb +43 -0
- data/lib/datadog/tracing/contrib/trilogy/patcher.rb +31 -0
- data/lib/datadog/tracing/contrib.rb +1 -0
- data/lib/datadog/tracing/sampling/matcher.rb +23 -3
- data/lib/datadog/tracing/sampling/rule.rb +7 -2
- data/lib/datadog/tracing/sampling/rule_sampler.rb +2 -0
- data/lib/datadog/tracing/trace_operation.rb +1 -2
- data/lib/datadog/tracing/transport/http.rb +1 -0
- data/lib/datadog/tracing/transport/trace_formatter.rb +31 -0
- data/lib/datadog/tracing.rb +8 -2
- data/lib/ddtrace/version.rb +2 -2
- metadata +71 -61
- data/ext/ddtrace_profiling_native_extension/pid_controller.c +0 -57
- data/ext/ddtrace_profiling_native_extension/pid_controller.h +0 -45
- data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +0 -110
- data/lib/datadog/core/telemetry/collector.rb +0 -240
- data/lib/datadog/core/telemetry/v1/app_event.rb +0 -52
- data/lib/datadog/core/telemetry/v1/application.rb +0 -92
- data/lib/datadog/core/telemetry/v1/configuration.rb +0 -25
- data/lib/datadog/core/telemetry/v1/dependency.rb +0 -43
- data/lib/datadog/core/telemetry/v1/host.rb +0 -59
- data/lib/datadog/core/telemetry/v1/integration.rb +0 -64
- data/lib/datadog/core/telemetry/v1/product.rb +0 -36
- data/lib/datadog/core/telemetry/v1/telemetry_request.rb +0 -106
- data/lib/datadog/core/telemetry/v2/app_client_configuration_change.rb +0 -41
- data/lib/datadog/core/telemetry/v2/request.rb +0 -29
- data/lib/datadog/profiling/diagnostics/environment_logger.rb +0 -39
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/NativeExtensionDesign.md +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id_from_pthread.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id_noop.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_idle_sampling_helper.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_idle_sampling_helper.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/setup_signal_handler.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/setup_signal_handler.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/time_helpers.c +0 -0
data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/stack_recorder.c
RENAMED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include "libdatadog_helpers.h"
|
|
8
8
|
#include "ruby_helpers.h"
|
|
9
9
|
#include "time_helpers.h"
|
|
10
|
+
#include "heap_recorder.h"
|
|
10
11
|
|
|
11
12
|
// Used to wrap a ddog_prof_Profile in a Ruby object and expose Ruby-level serialization APIs
|
|
12
13
|
// This file implements the native bits of the Datadog::Profiling::StackRecorder class
|
|
@@ -150,35 +151,69 @@ static VALUE error_symbol = Qnil; // :error in Ruby
|
|
|
150
151
|
#define WALL_TIME_VALUE_ID 2
|
|
151
152
|
#define ALLOC_SAMPLES_VALUE {.type_ = VALUE_STRING("alloc-samples"), .unit = VALUE_STRING("count")}
|
|
152
153
|
#define ALLOC_SAMPLES_VALUE_ID 3
|
|
154
|
+
#define HEAP_SAMPLES_VALUE {.type_ = VALUE_STRING("heap-live-samples"), .unit = VALUE_STRING("count")}
|
|
155
|
+
#define HEAP_SAMPLES_VALUE_ID 4
|
|
156
|
+
#define HEAP_SIZE_VALUE {.type_ = VALUE_STRING("heap-live-size"), .unit = VALUE_STRING("bytes")}
|
|
157
|
+
#define HEAP_SIZE_VALUE_ID 5
|
|
158
|
+
#define TIMELINE_VALUE {.type_ = VALUE_STRING("timeline"), .unit = VALUE_STRING("nanoseconds")}
|
|
159
|
+
#define TIMELINE_VALUE_ID 6
|
|
153
160
|
|
|
154
|
-
static const ddog_prof_ValueType all_value_types[] =
|
|
161
|
+
static const ddog_prof_ValueType all_value_types[] =
|
|
162
|
+
{CPU_TIME_VALUE, CPU_SAMPLES_VALUE, WALL_TIME_VALUE, ALLOC_SAMPLES_VALUE, HEAP_SAMPLES_VALUE, HEAP_SIZE_VALUE, TIMELINE_VALUE};
|
|
155
163
|
|
|
156
164
|
// This array MUST be kept in sync with all_value_types above and is intended to act as a "hashmap" between VALUE_ID and the position it
|
|
157
165
|
// occupies on the all_value_types array.
|
|
158
166
|
// E.g. all_value_types_positions[CPU_TIME_VALUE_ID] => 0, means that CPU_TIME_VALUE was declared at position 0 of all_value_types.
|
|
159
|
-
static const uint8_t all_value_types_positions[] =
|
|
167
|
+
static const uint8_t all_value_types_positions[] =
|
|
168
|
+
{CPU_TIME_VALUE_ID, CPU_SAMPLES_VALUE_ID, WALL_TIME_VALUE_ID, ALLOC_SAMPLES_VALUE_ID, HEAP_SAMPLES_VALUE_ID, HEAP_SIZE_VALUE_ID, TIMELINE_VALUE_ID};
|
|
160
169
|
|
|
161
170
|
#define ALL_VALUE_TYPES_COUNT (sizeof(all_value_types) / sizeof(ddog_prof_ValueType))
|
|
162
171
|
|
|
172
|
+
// Struct for storing stats related to a profile in a particular slot.
|
|
173
|
+
// These stats will share the same lifetime as the data in that profile slot.
|
|
174
|
+
typedef struct slot_stats {
|
|
175
|
+
// How many individual samples were recorded into this slot (un-weighted)
|
|
176
|
+
uint64_t recorded_samples;
|
|
177
|
+
} stats_slot;
|
|
178
|
+
|
|
179
|
+
typedef struct profile_slot {
|
|
180
|
+
ddog_prof_Profile profile;
|
|
181
|
+
stats_slot stats;
|
|
182
|
+
} profile_slot;
|
|
183
|
+
|
|
163
184
|
// Contains native state for each instance
|
|
164
185
|
struct stack_recorder_state {
|
|
165
|
-
|
|
166
|
-
|
|
186
|
+
// Heap recorder instance
|
|
187
|
+
heap_recorder *heap_recorder;
|
|
167
188
|
|
|
168
|
-
pthread_mutex_t
|
|
169
|
-
|
|
189
|
+
pthread_mutex_t mutex_slot_one;
|
|
190
|
+
profile_slot profile_slot_one;
|
|
191
|
+
pthread_mutex_t mutex_slot_two;
|
|
192
|
+
profile_slot profile_slot_two;
|
|
170
193
|
|
|
171
194
|
short active_slot; // MUST NEVER BE ACCESSED FROM record_sample; this is NOT for the sampler thread to use.
|
|
172
195
|
|
|
173
196
|
uint8_t position_for[ALL_VALUE_TYPES_COUNT];
|
|
174
197
|
uint8_t enabled_values_count;
|
|
198
|
+
|
|
199
|
+
// Struct for storing stats related to behaviour of a stack recorder instance during its entire lifetime.
|
|
200
|
+
struct lifetime_stats {
|
|
201
|
+
// How many profiles have we serialized successfully so far
|
|
202
|
+
uint64_t serialization_successes;
|
|
203
|
+
// How many profiles have we serialized unsuccessfully so far
|
|
204
|
+
uint64_t serialization_failures;
|
|
205
|
+
// Stats on profile serialization time
|
|
206
|
+
long serialization_time_ns_min;
|
|
207
|
+
long serialization_time_ns_max;
|
|
208
|
+
uint64_t serialization_time_ns_total;
|
|
209
|
+
} stats_lifetime;
|
|
175
210
|
};
|
|
176
211
|
|
|
177
|
-
// Used to
|
|
178
|
-
struct
|
|
212
|
+
// Used to group mutex and the corresponding profile slot for easy unlocking after work is done.
|
|
213
|
+
typedef struct locked_profile_slot {
|
|
179
214
|
pthread_mutex_t *mutex;
|
|
180
|
-
|
|
181
|
-
};
|
|
215
|
+
profile_slot *data;
|
|
216
|
+
} locked_profile_slot;
|
|
182
217
|
|
|
183
218
|
struct call_serialize_without_gvl_arguments {
|
|
184
219
|
// Set by caller
|
|
@@ -186,8 +221,10 @@ struct call_serialize_without_gvl_arguments {
|
|
|
186
221
|
ddog_Timespec finish_timestamp;
|
|
187
222
|
|
|
188
223
|
// Set by callee
|
|
189
|
-
|
|
224
|
+
profile_slot *slot;
|
|
190
225
|
ddog_prof_Profile_SerializeResult result;
|
|
226
|
+
long heap_profile_build_time_ns;
|
|
227
|
+
long serialize_no_gvl_time_ns;
|
|
191
228
|
|
|
192
229
|
// Set by both
|
|
193
230
|
bool serialize_ran;
|
|
@@ -197,13 +234,22 @@ static VALUE _native_new(VALUE klass);
|
|
|
197
234
|
static void initialize_slot_concurrency_control(struct stack_recorder_state *state);
|
|
198
235
|
static void initialize_profiles(struct stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types);
|
|
199
236
|
static void stack_recorder_typed_data_free(void *data);
|
|
200
|
-
static VALUE _native_initialize(
|
|
237
|
+
static VALUE _native_initialize(
|
|
238
|
+
DDTRACE_UNUSED VALUE _self,
|
|
239
|
+
VALUE recorder_instance,
|
|
240
|
+
VALUE cpu_time_enabled,
|
|
241
|
+
VALUE alloc_samples_enabled,
|
|
242
|
+
VALUE heap_samples_enabled,
|
|
243
|
+
VALUE heap_size_enabled,
|
|
244
|
+
VALUE heap_sample_every,
|
|
245
|
+
VALUE timeline_enabled
|
|
246
|
+
);
|
|
201
247
|
static VALUE _native_serialize(VALUE self, VALUE recorder_instance);
|
|
202
248
|
static VALUE ruby_time_from(ddog_Timespec ddprof_time);
|
|
203
249
|
static void *call_serialize_without_gvl(void *call_args);
|
|
204
|
-
static struct
|
|
205
|
-
static void sampler_unlock_active_profile(
|
|
206
|
-
static
|
|
250
|
+
static locked_profile_slot sampler_lock_active_profile(struct stack_recorder_state *state);
|
|
251
|
+
static void sampler_unlock_active_profile(locked_profile_slot active_slot);
|
|
252
|
+
static profile_slot* serializer_flip_active_and_inactive_slots(struct stack_recorder_state *state);
|
|
207
253
|
static VALUE _native_active_slot(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
|
208
254
|
static VALUE _native_is_slot_one_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
|
209
255
|
static VALUE _native_is_slot_two_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
|
@@ -212,7 +258,17 @@ static ddog_Timespec system_epoch_now_timespec(void);
|
|
|
212
258
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_instance);
|
|
213
259
|
static void serializer_set_start_timestamp_for_next_profile(struct stack_recorder_state *state, ddog_Timespec start_time);
|
|
214
260
|
static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE local_root_span_id, VALUE endpoint);
|
|
215
|
-
static void
|
|
261
|
+
static void reset_profile_slot(profile_slot *slot, ddog_Timespec *start_time /* Can be null */);
|
|
262
|
+
static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE new_obj, VALUE weight, VALUE alloc_class);
|
|
263
|
+
static VALUE _native_check_heap_hashes(DDTRACE_UNUSED VALUE _self, VALUE locations);
|
|
264
|
+
static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
|
265
|
+
static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
|
266
|
+
static VALUE _native_debug_heap_recorder(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
|
267
|
+
static VALUE _native_gc_force_recycle(DDTRACE_UNUSED VALUE _self, VALUE obj);
|
|
268
|
+
static VALUE _native_has_seen_id_flag(DDTRACE_UNUSED VALUE _self, VALUE obj);
|
|
269
|
+
static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
270
|
+
static VALUE build_profile_stats(profile_slot *slot, long serialization_time_ns, long heap_iteration_prep_time_ns, long heap_profile_build_time_ns);
|
|
271
|
+
|
|
216
272
|
|
|
217
273
|
void stack_recorder_init(VALUE profiling_module) {
|
|
218
274
|
VALUE stack_recorder_class = rb_define_class_under(profiling_module, "StackRecorder", rb_cObject);
|
|
@@ -229,13 +285,26 @@ void stack_recorder_init(VALUE profiling_module) {
|
|
|
229
285
|
// https://bugs.ruby-lang.org/issues/18007 for a discussion around this.
|
|
230
286
|
rb_define_alloc_func(stack_recorder_class, _native_new);
|
|
231
287
|
|
|
232
|
-
rb_define_singleton_method(stack_recorder_class, "_native_initialize", _native_initialize,
|
|
288
|
+
rb_define_singleton_method(stack_recorder_class, "_native_initialize", _native_initialize, 7);
|
|
233
289
|
rb_define_singleton_method(stack_recorder_class, "_native_serialize", _native_serialize, 1);
|
|
234
290
|
rb_define_singleton_method(stack_recorder_class, "_native_reset_after_fork", _native_reset_after_fork, 1);
|
|
291
|
+
rb_define_singleton_method(stack_recorder_class, "_native_stats", _native_stats, 1);
|
|
235
292
|
rb_define_singleton_method(testing_module, "_native_active_slot", _native_active_slot, 1);
|
|
236
293
|
rb_define_singleton_method(testing_module, "_native_slot_one_mutex_locked?", _native_is_slot_one_mutex_locked, 1);
|
|
237
294
|
rb_define_singleton_method(testing_module, "_native_slot_two_mutex_locked?", _native_is_slot_two_mutex_locked, 1);
|
|
238
295
|
rb_define_singleton_method(testing_module, "_native_record_endpoint", _native_record_endpoint, 3);
|
|
296
|
+
rb_define_singleton_method(testing_module, "_native_track_object", _native_track_object, 4);
|
|
297
|
+
rb_define_singleton_method(testing_module, "_native_check_heap_hashes", _native_check_heap_hashes, 1);
|
|
298
|
+
rb_define_singleton_method(testing_module, "_native_start_fake_slow_heap_serialization",
|
|
299
|
+
_native_start_fake_slow_heap_serialization, 1);
|
|
300
|
+
rb_define_singleton_method(testing_module, "_native_end_fake_slow_heap_serialization",
|
|
301
|
+
_native_end_fake_slow_heap_serialization, 1);
|
|
302
|
+
rb_define_singleton_method(testing_module, "_native_debug_heap_recorder",
|
|
303
|
+
_native_debug_heap_recorder, 1);
|
|
304
|
+
rb_define_singleton_method(testing_module, "_native_gc_force_recycle",
|
|
305
|
+
_native_gc_force_recycle, 1);
|
|
306
|
+
rb_define_singleton_method(testing_module, "_native_has_seen_id_flag",
|
|
307
|
+
_native_has_seen_id_flag, 1);
|
|
239
308
|
|
|
240
309
|
ok_symbol = ID2SYM(rb_intern_const("ok"));
|
|
241
310
|
error_symbol = ID2SYM(rb_intern_const("error"));
|
|
@@ -264,12 +333,21 @@ static VALUE _native_new(VALUE klass) {
|
|
|
264
333
|
initialize_slot_concurrency_control(state);
|
|
265
334
|
for (uint8_t i = 0; i < ALL_VALUE_TYPES_COUNT; i++) { state->position_for[i] = all_value_types_positions[i]; }
|
|
266
335
|
state->enabled_values_count = ALL_VALUE_TYPES_COUNT;
|
|
336
|
+
state->stats_lifetime = (struct lifetime_stats) {
|
|
337
|
+
.serialization_time_ns_min = INT64_MAX,
|
|
338
|
+
};
|
|
267
339
|
|
|
268
340
|
// Note: At this point, slot_one_profile and slot_two_profile contain null pointers. Libdatadog validates pointers
|
|
269
341
|
// before using them so it's ok for us to go ahead and create the StackRecorder object.
|
|
270
342
|
|
|
271
343
|
VALUE stack_recorder = TypedData_Wrap_Struct(klass, &stack_recorder_typed_data, state);
|
|
272
344
|
|
|
345
|
+
// NOTE: We initialize this because we want a new recorder to be operational even without initialization and our
|
|
346
|
+
// default is everything enabled. However, if during recording initialization it turns out we don't want
|
|
347
|
+
// heap samples, we will free and reset heap_recorder to NULL, effectively disabling all behaviour specific
|
|
348
|
+
// to heap profiling (all calls to heap_recorder_* with a NULL heap recorder are noops).
|
|
349
|
+
state->heap_recorder = heap_recorder_new();
|
|
350
|
+
|
|
273
351
|
// Note: Don't raise exceptions after this point, since it'll lead to libdatadog memory leaking!
|
|
274
352
|
|
|
275
353
|
initialize_profiles(state, sample_types);
|
|
@@ -278,11 +356,11 @@ static VALUE _native_new(VALUE klass) {
|
|
|
278
356
|
}
|
|
279
357
|
|
|
280
358
|
static void initialize_slot_concurrency_control(struct stack_recorder_state *state) {
|
|
281
|
-
state->
|
|
282
|
-
state->
|
|
359
|
+
state->mutex_slot_one = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
|
|
360
|
+
state->mutex_slot_two = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
|
|
283
361
|
|
|
284
362
|
// A newly-created StackRecorder starts with slot one being active for samples, so let's lock slot two
|
|
285
|
-
ENFORCE_SUCCESS_GVL(pthread_mutex_lock(&state->
|
|
363
|
+
ENFORCE_SUCCESS_GVL(pthread_mutex_lock(&state->mutex_slot_two));
|
|
286
364
|
|
|
287
365
|
state->active_slot = 1;
|
|
288
366
|
}
|
|
@@ -305,40 +383,68 @@ static void initialize_profiles(struct stack_recorder_state *state, ddog_prof_Sl
|
|
|
305
383
|
rb_raise(rb_eRuntimeError, "Failed to initialize slot two profile: %"PRIsVALUE, get_error_details_and_drop(&slot_two_profile_result.err));
|
|
306
384
|
}
|
|
307
385
|
|
|
308
|
-
state->
|
|
309
|
-
|
|
386
|
+
state->profile_slot_one = (profile_slot) {
|
|
387
|
+
.profile = slot_one_profile_result.ok,
|
|
388
|
+
};
|
|
389
|
+
state->profile_slot_two = (profile_slot) {
|
|
390
|
+
.profile = slot_two_profile_result.ok,
|
|
391
|
+
};
|
|
310
392
|
}
|
|
311
393
|
|
|
312
394
|
static void stack_recorder_typed_data_free(void *state_ptr) {
|
|
313
395
|
struct stack_recorder_state *state = (struct stack_recorder_state *) state_ptr;
|
|
314
396
|
|
|
315
|
-
pthread_mutex_destroy(&state->
|
|
316
|
-
ddog_prof_Profile_drop(&state->
|
|
397
|
+
pthread_mutex_destroy(&state->mutex_slot_one);
|
|
398
|
+
ddog_prof_Profile_drop(&state->profile_slot_one.profile);
|
|
399
|
+
|
|
400
|
+
pthread_mutex_destroy(&state->mutex_slot_two);
|
|
401
|
+
ddog_prof_Profile_drop(&state->profile_slot_two.profile);
|
|
317
402
|
|
|
318
|
-
|
|
319
|
-
ddog_prof_Profile_drop(&state->slot_two_profile);
|
|
403
|
+
heap_recorder_free(state->heap_recorder);
|
|
320
404
|
|
|
321
405
|
ruby_xfree(state);
|
|
322
406
|
}
|
|
323
407
|
|
|
324
|
-
static VALUE _native_initialize(
|
|
408
|
+
static VALUE _native_initialize(
|
|
409
|
+
DDTRACE_UNUSED VALUE _self,
|
|
410
|
+
VALUE recorder_instance,
|
|
411
|
+
VALUE cpu_time_enabled,
|
|
412
|
+
VALUE alloc_samples_enabled,
|
|
413
|
+
VALUE heap_samples_enabled,
|
|
414
|
+
VALUE heap_size_enabled,
|
|
415
|
+
VALUE heap_sample_every,
|
|
416
|
+
VALUE timeline_enabled
|
|
417
|
+
) {
|
|
325
418
|
ENFORCE_BOOLEAN(cpu_time_enabled);
|
|
326
419
|
ENFORCE_BOOLEAN(alloc_samples_enabled);
|
|
420
|
+
ENFORCE_BOOLEAN(heap_samples_enabled);
|
|
421
|
+
ENFORCE_BOOLEAN(heap_size_enabled);
|
|
422
|
+
ENFORCE_TYPE(heap_sample_every, T_FIXNUM);
|
|
423
|
+
ENFORCE_BOOLEAN(timeline_enabled);
|
|
327
424
|
|
|
328
425
|
struct stack_recorder_state *state;
|
|
329
426
|
TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
|
|
330
427
|
|
|
331
|
-
|
|
428
|
+
heap_recorder_set_sample_rate(state->heap_recorder, NUM2INT(heap_sample_every));
|
|
429
|
+
|
|
430
|
+
uint8_t requested_values_count = ALL_VALUE_TYPES_COUNT -
|
|
431
|
+
(cpu_time_enabled == Qtrue ? 0 : 1) -
|
|
432
|
+
(alloc_samples_enabled == Qtrue? 0 : 1) -
|
|
433
|
+
(heap_samples_enabled == Qtrue ? 0 : 1) -
|
|
434
|
+
(heap_size_enabled == Qtrue ? 0 : 1) -
|
|
435
|
+
(timeline_enabled == Qtrue ? 0 : 1);
|
|
436
|
+
|
|
437
|
+
if (requested_values_count == ALL_VALUE_TYPES_COUNT) return Qtrue; // Nothing to do, this is the default
|
|
332
438
|
|
|
333
439
|
// When some sample types are disabled, we need to reconfigure libdatadog to record less types,
|
|
334
440
|
// as well as reconfigure the position_for array to push the disabled types to the end so they don't get recorded.
|
|
335
441
|
// See record_sample for details on the use of position_for.
|
|
336
442
|
|
|
337
|
-
state->enabled_values_count =
|
|
443
|
+
state->enabled_values_count = requested_values_count;
|
|
338
444
|
|
|
339
445
|
ddog_prof_ValueType enabled_value_types[ALL_VALUE_TYPES_COUNT];
|
|
340
446
|
uint8_t next_enabled_pos = 0;
|
|
341
|
-
uint8_t next_disabled_pos =
|
|
447
|
+
uint8_t next_disabled_pos = requested_values_count;
|
|
342
448
|
|
|
343
449
|
// CPU_SAMPLES_VALUE is always enabled
|
|
344
450
|
enabled_value_types[next_enabled_pos] = (ddog_prof_ValueType) CPU_SAMPLES_VALUE;
|
|
@@ -362,8 +468,37 @@ static VALUE _native_initialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_insta
|
|
|
362
468
|
state->position_for[ALLOC_SAMPLES_VALUE_ID] = next_disabled_pos++;
|
|
363
469
|
}
|
|
364
470
|
|
|
365
|
-
|
|
366
|
-
|
|
471
|
+
if (heap_samples_enabled == Qtrue) {
|
|
472
|
+
enabled_value_types[next_enabled_pos] = (ddog_prof_ValueType) HEAP_SAMPLES_VALUE;
|
|
473
|
+
state->position_for[HEAP_SAMPLES_VALUE_ID] = next_enabled_pos++;
|
|
474
|
+
} else {
|
|
475
|
+
state->position_for[HEAP_SAMPLES_VALUE_ID] = next_disabled_pos++;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (heap_size_enabled == Qtrue) {
|
|
479
|
+
enabled_value_types[next_enabled_pos] = (ddog_prof_ValueType) HEAP_SIZE_VALUE;
|
|
480
|
+
state->position_for[HEAP_SIZE_VALUE_ID] = next_enabled_pos++;
|
|
481
|
+
} else {
|
|
482
|
+
state->position_for[HEAP_SIZE_VALUE_ID] = next_disabled_pos++;
|
|
483
|
+
}
|
|
484
|
+
heap_recorder_set_size_enabled(state->heap_recorder, heap_size_enabled);
|
|
485
|
+
|
|
486
|
+
if (heap_samples_enabled == Qfalse && heap_size_enabled == Qfalse) {
|
|
487
|
+
// Turns out heap sampling is disabled but we initialized everything in _native_new
|
|
488
|
+
// assuming all samples were enabled. We need to deinitialize the heap recorder.
|
|
489
|
+
heap_recorder_free(state->heap_recorder);
|
|
490
|
+
state->heap_recorder = NULL;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (timeline_enabled == Qtrue) {
|
|
494
|
+
enabled_value_types[next_enabled_pos] = (ddog_prof_ValueType) TIMELINE_VALUE;
|
|
495
|
+
state->position_for[TIMELINE_VALUE_ID] = next_enabled_pos++;
|
|
496
|
+
} else {
|
|
497
|
+
state->position_for[TIMELINE_VALUE_ID] = next_disabled_pos++;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
ddog_prof_Profile_drop(&state->profile_slot_one.profile);
|
|
501
|
+
ddog_prof_Profile_drop(&state->profile_slot_two.profile);
|
|
367
502
|
|
|
368
503
|
ddog_prof_Slice_ValueType sample_types = {.ptr = enabled_value_types, .len = state->enabled_values_count};
|
|
369
504
|
initialize_profiles(state, sample_types);
|
|
@@ -379,9 +514,19 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan
|
|
|
379
514
|
// Need to do this while still holding on to the Global VM Lock; see comments on method for why
|
|
380
515
|
serializer_set_start_timestamp_for_next_profile(state, finish_timestamp);
|
|
381
516
|
|
|
517
|
+
long heap_iteration_prep_start_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
|
|
518
|
+
// Prepare the iteration on heap recorder we'll be doing outside the GVL. The preparation needs to
|
|
519
|
+
// happen while holding on to the GVL.
|
|
520
|
+
heap_recorder_prepare_iteration(state->heap_recorder);
|
|
521
|
+
long heap_iteration_prep_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE) - heap_iteration_prep_start_time_ns;
|
|
522
|
+
|
|
382
523
|
// We'll release the Global VM Lock while we're calling serialize, so that the Ruby VM can continue to work while this
|
|
383
524
|
// is pending
|
|
384
|
-
struct call_serialize_without_gvl_arguments args = {
|
|
525
|
+
struct call_serialize_without_gvl_arguments args = {
|
|
526
|
+
.state = state,
|
|
527
|
+
.finish_timestamp = finish_timestamp,
|
|
528
|
+
.serialize_ran = false
|
|
529
|
+
};
|
|
385
530
|
|
|
386
531
|
while (!args.serialize_ran) {
|
|
387
532
|
// Give the Ruby VM an opportunity to process any pending interruptions (including raising exceptions).
|
|
@@ -397,12 +542,30 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan
|
|
|
397
542
|
rb_thread_call_without_gvl2(call_serialize_without_gvl, &args, NULL /* No interruption function needed in this case */, NULL /* Not needed */);
|
|
398
543
|
}
|
|
399
544
|
|
|
545
|
+
// Cleanup after heap recorder iteration. This needs to happen while holding on to the GVL.
|
|
546
|
+
heap_recorder_finish_iteration(state->heap_recorder);
|
|
547
|
+
|
|
548
|
+
// NOTE: We are focusing on the serialization time outside of the GVL in this stat here. This doesn't
|
|
549
|
+
// really cover the full serialization process but it gives a more useful number since it bypasses
|
|
550
|
+
// the noise of acquiring GVLs and dealing with interruptions which is highly specific to runtime
|
|
551
|
+
// conditions and over which we really have no control about.
|
|
552
|
+
long serialization_time_ns = args.serialize_no_gvl_time_ns;
|
|
553
|
+
if (serialization_time_ns >= 0) {
|
|
554
|
+
// Only update stats if our serialization time is valid.
|
|
555
|
+
state->stats_lifetime.serialization_time_ns_max = long_max_of(state->stats_lifetime.serialization_time_ns_max, serialization_time_ns);
|
|
556
|
+
state->stats_lifetime.serialization_time_ns_min = long_min_of(state->stats_lifetime.serialization_time_ns_min, serialization_time_ns);
|
|
557
|
+
state->stats_lifetime.serialization_time_ns_total += serialization_time_ns;
|
|
558
|
+
}
|
|
559
|
+
|
|
400
560
|
ddog_prof_Profile_SerializeResult serialized_profile = args.result;
|
|
401
561
|
|
|
402
562
|
if (serialized_profile.tag == DDOG_PROF_PROFILE_SERIALIZE_RESULT_ERR) {
|
|
563
|
+
state->stats_lifetime.serialization_failures++;
|
|
403
564
|
return rb_ary_new_from_args(2, error_symbol, get_error_details_and_drop(&serialized_profile.err));
|
|
404
565
|
}
|
|
405
566
|
|
|
567
|
+
state->stats_lifetime.serialization_successes++;
|
|
568
|
+
|
|
406
569
|
VALUE encoded_pprof = ruby_string_from_vec_u8(serialized_profile.ok.buffer);
|
|
407
570
|
|
|
408
571
|
ddog_Timespec ddprof_start = serialized_profile.ok.start;
|
|
@@ -412,8 +575,9 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan
|
|
|
412
575
|
|
|
413
576
|
VALUE start = ruby_time_from(ddprof_start);
|
|
414
577
|
VALUE finish = ruby_time_from(ddprof_finish);
|
|
578
|
+
VALUE profile_stats = build_profile_stats(args.slot, serialization_time_ns, heap_iteration_prep_time_ns, args.heap_profile_build_time_ns);
|
|
415
579
|
|
|
416
|
-
return rb_ary_new_from_args(2, ok_symbol, rb_ary_new_from_args(
|
|
580
|
+
return rb_ary_new_from_args(2, ok_symbol, rb_ary_new_from_args(4, start, finish, encoded_pprof, profile_stats));
|
|
417
581
|
}
|
|
418
582
|
|
|
419
583
|
static VALUE ruby_time_from(ddog_Timespec ddprof_time) {
|
|
@@ -426,7 +590,7 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations,
|
|
|
426
590
|
struct stack_recorder_state *state;
|
|
427
591
|
TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
|
|
428
592
|
|
|
429
|
-
|
|
593
|
+
locked_profile_slot active_slot = sampler_lock_active_profile(state);
|
|
430
594
|
|
|
431
595
|
// Note: We initialize this array to have ALL_VALUE_TYPES_COUNT but only tell libdatadog to use the first
|
|
432
596
|
// state->enabled_values_count values. This simplifies handling disabled value types -- we still put them on the
|
|
@@ -439,9 +603,18 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations,
|
|
|
439
603
|
metric_values[position_for[CPU_SAMPLES_VALUE_ID]] = values.cpu_or_wall_samples;
|
|
440
604
|
metric_values[position_for[WALL_TIME_VALUE_ID]] = values.wall_time_ns;
|
|
441
605
|
metric_values[position_for[ALLOC_SAMPLES_VALUE_ID]] = values.alloc_samples;
|
|
606
|
+
metric_values[position_for[TIMELINE_VALUE_ID]] = values.timeline_wall_time_ns;
|
|
607
|
+
|
|
608
|
+
if (values.alloc_samples != 0) {
|
|
609
|
+
// If we got an allocation sample end the heap allocation recording to commit the heap sample.
|
|
610
|
+
// FIXME: Heap sampling currently has to be done in 2 parts because the construction of locations is happening
|
|
611
|
+
// very late in the allocation-sampling path (which is shared with the cpu sampling path). This can
|
|
612
|
+
// be fixed with some refactoring but for now this leads to a less impactful change.
|
|
613
|
+
end_heap_allocation_recording(state->heap_recorder, locations);
|
|
614
|
+
}
|
|
442
615
|
|
|
443
616
|
ddog_prof_Profile_Result result = ddog_prof_Profile_add(
|
|
444
|
-
active_slot.profile,
|
|
617
|
+
&active_slot.data->profile,
|
|
445
618
|
(ddog_prof_Sample) {
|
|
446
619
|
.locations = locations,
|
|
447
620
|
.values = (ddog_Slice_I64) {.ptr = metric_values, .len = state->enabled_values_count},
|
|
@@ -450,6 +623,8 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations,
|
|
|
450
623
|
labels.end_timestamp_ns
|
|
451
624
|
);
|
|
452
625
|
|
|
626
|
+
active_slot.data->stats.recorded_samples++;
|
|
627
|
+
|
|
453
628
|
sampler_unlock_active_profile(active_slot);
|
|
454
629
|
|
|
455
630
|
if (result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
|
|
@@ -457,13 +632,22 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations,
|
|
|
457
632
|
}
|
|
458
633
|
}
|
|
459
634
|
|
|
635
|
+
void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample_weight, ddog_CharSlice *alloc_class) {
|
|
636
|
+
struct stack_recorder_state *state;
|
|
637
|
+
TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
|
|
638
|
+
// FIXME: Heap sampling currently has to be done in 2 parts because the construction of locations is happening
|
|
639
|
+
// very late in the allocation-sampling path (which is shared with the cpu sampling path). This can
|
|
640
|
+
// be fixed with some refactoring but for now this leads to a less impactful change.
|
|
641
|
+
start_heap_allocation_recording(state->heap_recorder, new_object, sample_weight, alloc_class);
|
|
642
|
+
}
|
|
643
|
+
|
|
460
644
|
void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_CharSlice endpoint) {
|
|
461
645
|
struct stack_recorder_state *state;
|
|
462
646
|
TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
|
|
463
647
|
|
|
464
|
-
|
|
648
|
+
locked_profile_slot active_slot = sampler_lock_active_profile(state);
|
|
465
649
|
|
|
466
|
-
ddog_prof_Profile_Result result = ddog_prof_Profile_set_endpoint(active_slot.profile, local_root_span_id, endpoint);
|
|
650
|
+
ddog_prof_Profile_Result result = ddog_prof_Profile_set_endpoint(&active_slot.data->profile, local_root_span_id, endpoint);
|
|
467
651
|
|
|
468
652
|
sampler_unlock_active_profile(active_slot);
|
|
469
653
|
|
|
@@ -472,13 +656,111 @@ void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_
|
|
|
472
656
|
}
|
|
473
657
|
}
|
|
474
658
|
|
|
659
|
+
#define MAX_LEN_HEAP_ITERATION_ERROR_MSG 256
|
|
660
|
+
|
|
661
|
+
// Heap recorder iteration context allows us access to stack recorder state and profile being serialized
|
|
662
|
+
// during iteration of heap recorder live objects.
|
|
663
|
+
typedef struct heap_recorder_iteration_context {
|
|
664
|
+
struct stack_recorder_state *state;
|
|
665
|
+
profile_slot *slot;
|
|
666
|
+
|
|
667
|
+
bool error;
|
|
668
|
+
char error_msg[MAX_LEN_HEAP_ITERATION_ERROR_MSG];
|
|
669
|
+
} heap_recorder_iteration_context;
|
|
670
|
+
|
|
671
|
+
static bool add_heap_sample_to_active_profile_without_gvl(heap_recorder_iteration_data iteration_data, void *extra_arg) {
|
|
672
|
+
heap_recorder_iteration_context *context = (heap_recorder_iteration_context*) extra_arg;
|
|
673
|
+
|
|
674
|
+
live_object_data *object_data = &iteration_data.object_data;
|
|
675
|
+
|
|
676
|
+
int64_t metric_values[ALL_VALUE_TYPES_COUNT] = {0};
|
|
677
|
+
uint8_t *position_for = context->state->position_for;
|
|
678
|
+
|
|
679
|
+
metric_values[position_for[HEAP_SAMPLES_VALUE_ID]] = object_data->weight;
|
|
680
|
+
metric_values[position_for[HEAP_SIZE_VALUE_ID]] = object_data->size * object_data->weight;
|
|
681
|
+
|
|
682
|
+
ddog_prof_Label labels[2];
|
|
683
|
+
size_t label_offset = 0;
|
|
684
|
+
|
|
685
|
+
if (object_data->class != NULL) {
|
|
686
|
+
labels[label_offset++] = (ddog_prof_Label) {
|
|
687
|
+
.key = DDOG_CHARSLICE_C("allocation class"),
|
|
688
|
+
.str = (ddog_CharSlice) {
|
|
689
|
+
.ptr = object_data->class,
|
|
690
|
+
.len = strlen(object_data->class),
|
|
691
|
+
},
|
|
692
|
+
.num = 0, // This shouldn't be needed but the tracer-2.7 docker image ships a buggy gcc that complains about this
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
labels[label_offset++] = (ddog_prof_Label) {
|
|
696
|
+
.key = DDOG_CHARSLICE_C("gc gen age"),
|
|
697
|
+
.num = object_data->gen_age,
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
ddog_prof_Profile_Result result = ddog_prof_Profile_add(
|
|
701
|
+
&context->slot->profile,
|
|
702
|
+
(ddog_prof_Sample) {
|
|
703
|
+
.locations = iteration_data.locations,
|
|
704
|
+
.values = (ddog_Slice_I64) {.ptr = metric_values, .len = context->state->enabled_values_count},
|
|
705
|
+
.labels = (ddog_prof_Slice_Label) {
|
|
706
|
+
.ptr = labels,
|
|
707
|
+
.len = label_offset,
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
0
|
|
711
|
+
);
|
|
712
|
+
|
|
713
|
+
context->slot->stats.recorded_samples++;
|
|
714
|
+
|
|
715
|
+
if (result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
|
|
716
|
+
read_ddogerr_string_and_drop(&result.err, context->error_msg, MAX_LEN_HEAP_ITERATION_ERROR_MSG);
|
|
717
|
+
context->error = true;
|
|
718
|
+
// By returning false we cancel the iteration
|
|
719
|
+
return false;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// Keep on iterating to next item!
|
|
723
|
+
return true;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
static void build_heap_profile_without_gvl(struct stack_recorder_state *state, profile_slot *slot) {
|
|
727
|
+
heap_recorder_iteration_context iteration_context = {
|
|
728
|
+
.state = state,
|
|
729
|
+
.slot = slot,
|
|
730
|
+
.error = false,
|
|
731
|
+
.error_msg = {0},
|
|
732
|
+
};
|
|
733
|
+
bool iterated = heap_recorder_for_each_live_object(state->heap_recorder, add_heap_sample_to_active_profile_without_gvl, (void*) &iteration_context);
|
|
734
|
+
// We wait until we're out of the iteration to grab the gvl and raise. This is important because during
|
|
735
|
+
// iteration we may potentially acquire locks in the heap recorder and we could reach a deadlock if the
|
|
736
|
+
// same locks are acquired by the heap recorder while holding the gvl (since we'd be operating on the
|
|
737
|
+
// same locks but acquiring them in different order).
|
|
738
|
+
if (!iterated) {
|
|
739
|
+
grab_gvl_and_raise(rb_eRuntimeError, "Failure during heap profile building: iteration cancelled");
|
|
740
|
+
}
|
|
741
|
+
else if (iteration_context.error) {
|
|
742
|
+
grab_gvl_and_raise(rb_eRuntimeError, "Failure during heap profile building: %s", iteration_context.error_msg);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
475
746
|
static void *call_serialize_without_gvl(void *call_args) {
|
|
476
747
|
struct call_serialize_without_gvl_arguments *args = (struct call_serialize_without_gvl_arguments *) call_args;
|
|
477
748
|
|
|
478
|
-
|
|
749
|
+
long serialize_no_gvl_start_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
|
|
750
|
+
|
|
751
|
+
profile_slot *slot_now_inactive = serializer_flip_active_and_inactive_slots(args->state);
|
|
752
|
+
|
|
753
|
+
args->slot = slot_now_inactive;
|
|
754
|
+
|
|
755
|
+
// Now that we have the inactive profile with all but heap samples, lets fill it with heap data
|
|
756
|
+
// without needing to race with the active sampler
|
|
757
|
+
build_heap_profile_without_gvl(args->state, args->slot);
|
|
758
|
+
args->heap_profile_build_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE) - serialize_no_gvl_start_time_ns;
|
|
759
|
+
|
|
479
760
|
// Note: The profile gets reset by the serialize call
|
|
480
|
-
args->result = ddog_prof_Profile_serialize(args->profile, &args->finish_timestamp, NULL /* duration_nanos is optional */, NULL /* start_time is optional */);
|
|
761
|
+
args->result = ddog_prof_Profile_serialize(&args->slot->profile, &args->finish_timestamp, NULL /* duration_nanos is optional */, NULL /* start_time is optional */);
|
|
481
762
|
args->serialize_ran = true;
|
|
763
|
+
args->serialize_no_gvl_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE) - serialize_no_gvl_start_time_ns;
|
|
482
764
|
|
|
483
765
|
return NULL; // Unused
|
|
484
766
|
}
|
|
@@ -488,42 +770,42 @@ VALUE enforce_recorder_instance(VALUE object) {
|
|
|
488
770
|
return object;
|
|
489
771
|
}
|
|
490
772
|
|
|
491
|
-
static
|
|
773
|
+
static locked_profile_slot sampler_lock_active_profile(struct stack_recorder_state *state) {
|
|
492
774
|
int error;
|
|
493
775
|
|
|
494
776
|
for (int attempts = 0; attempts < 2; attempts++) {
|
|
495
|
-
error = pthread_mutex_trylock(&state->
|
|
777
|
+
error = pthread_mutex_trylock(&state->mutex_slot_one);
|
|
496
778
|
if (error && error != EBUSY) ENFORCE_SUCCESS_GVL(error);
|
|
497
779
|
|
|
498
780
|
// Slot one is active
|
|
499
|
-
if (!error) return (
|
|
781
|
+
if (!error) return (locked_profile_slot) {.mutex = &state->mutex_slot_one, .data = &state->profile_slot_one};
|
|
500
782
|
|
|
501
783
|
// If we got here, slot one was not active, let's try slot two
|
|
502
784
|
|
|
503
|
-
error = pthread_mutex_trylock(&state->
|
|
785
|
+
error = pthread_mutex_trylock(&state->mutex_slot_two);
|
|
504
786
|
if (error && error != EBUSY) ENFORCE_SUCCESS_GVL(error);
|
|
505
787
|
|
|
506
788
|
// Slot two is active
|
|
507
|
-
if (!error) return (
|
|
789
|
+
if (!error) return (locked_profile_slot) {.mutex = &state->mutex_slot_two, .data = &state->profile_slot_two};
|
|
508
790
|
}
|
|
509
791
|
|
|
510
792
|
// We already tried both multiple times, and we did not succeed. This is not expected to happen. Let's stop sampling.
|
|
511
793
|
rb_raise(rb_eRuntimeError, "Failed to grab either mutex in sampler_lock_active_profile");
|
|
512
794
|
}
|
|
513
795
|
|
|
514
|
-
static void sampler_unlock_active_profile(
|
|
796
|
+
static void sampler_unlock_active_profile(locked_profile_slot active_slot) {
|
|
515
797
|
ENFORCE_SUCCESS_GVL(pthread_mutex_unlock(active_slot.mutex));
|
|
516
798
|
}
|
|
517
799
|
|
|
518
|
-
static
|
|
800
|
+
static profile_slot* serializer_flip_active_and_inactive_slots(struct stack_recorder_state *state) {
|
|
519
801
|
int previously_active_slot = state->active_slot;
|
|
520
802
|
|
|
521
803
|
if (previously_active_slot != 1 && previously_active_slot != 2) {
|
|
522
804
|
grab_gvl_and_raise(rb_eRuntimeError, "Unexpected active_slot state %d in serializer_flip_active_and_inactive_slots", previously_active_slot);
|
|
523
805
|
}
|
|
524
806
|
|
|
525
|
-
pthread_mutex_t *previously_active = (previously_active_slot == 1) ? &state->
|
|
526
|
-
pthread_mutex_t *previously_inactive = (previously_active_slot == 1) ? &state->
|
|
807
|
+
pthread_mutex_t *previously_active = (previously_active_slot == 1) ? &state->mutex_slot_one : &state->mutex_slot_two;
|
|
808
|
+
pthread_mutex_t *previously_inactive = (previously_active_slot == 1) ? &state->mutex_slot_two : &state->mutex_slot_one;
|
|
527
809
|
|
|
528
810
|
// Release the lock, thus making this slot active
|
|
529
811
|
ENFORCE_SUCCESS_NO_GVL(pthread_mutex_unlock(previously_inactive));
|
|
@@ -534,8 +816,8 @@ static ddog_prof_Profile *serializer_flip_active_and_inactive_slots(struct stack
|
|
|
534
816
|
// Update active_slot
|
|
535
817
|
state->active_slot = (previously_active_slot == 1) ? 2 : 1;
|
|
536
818
|
|
|
537
|
-
// Return
|
|
538
|
-
return (previously_active_slot == 1) ? &state->
|
|
819
|
+
// Return pointer to previously active slot (now inactive)
|
|
820
|
+
return (previously_active_slot == 1) ? &state->profile_slot_one : &state->profile_slot_two;
|
|
539
821
|
}
|
|
540
822
|
|
|
541
823
|
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
|
@@ -559,7 +841,7 @@ static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot) {
|
|
|
559
841
|
struct stack_recorder_state *state;
|
|
560
842
|
TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
|
|
561
843
|
|
|
562
|
-
pthread_mutex_t *slot_mutex = (slot == 1) ? &state->
|
|
844
|
+
pthread_mutex_t *slot_mutex = (slot == 1) ? &state->mutex_slot_one : &state->mutex_slot_two;
|
|
563
845
|
|
|
564
846
|
// Like Heisenberg's uncertainty principle, we can't observe without affecting...
|
|
565
847
|
int error = pthread_mutex_trylock(slot_mutex);
|
|
@@ -594,8 +876,10 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_
|
|
|
594
876
|
// resulting state is inconsistent, we make sure to reset it back to the initial state.
|
|
595
877
|
initialize_slot_concurrency_control(state);
|
|
596
878
|
|
|
597
|
-
|
|
598
|
-
|
|
879
|
+
reset_profile_slot(&state->profile_slot_one, /* start_time: */ NULL);
|
|
880
|
+
reset_profile_slot(&state->profile_slot_two, /* start_time: */ NULL);
|
|
881
|
+
|
|
882
|
+
heap_recorder_after_fork(state->heap_recorder);
|
|
599
883
|
|
|
600
884
|
return Qtrue;
|
|
601
885
|
}
|
|
@@ -604,8 +888,8 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_
|
|
|
604
888
|
// not be interrupted part-way through by a VM fork.
|
|
605
889
|
static void serializer_set_start_timestamp_for_next_profile(struct stack_recorder_state *state, ddog_Timespec start_time) {
|
|
606
890
|
// Before making this profile active, we reset it so that it uses the correct start_time for its start
|
|
607
|
-
|
|
608
|
-
|
|
891
|
+
profile_slot *next_profile_slot = (state->active_slot == 1) ? &state->profile_slot_two : &state->profile_slot_one;
|
|
892
|
+
reset_profile_slot(next_profile_slot, &start_time);
|
|
609
893
|
}
|
|
610
894
|
|
|
611
895
|
static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE local_root_span_id, VALUE endpoint) {
|
|
@@ -614,9 +898,143 @@ static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_
|
|
|
614
898
|
return Qtrue;
|
|
615
899
|
}
|
|
616
900
|
|
|
617
|
-
static
|
|
618
|
-
|
|
901
|
+
static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE new_obj, VALUE weight, VALUE alloc_class) {
|
|
902
|
+
ENFORCE_TYPE(weight, T_FIXNUM);
|
|
903
|
+
ddog_CharSlice alloc_class_slice = char_slice_from_ruby_string(alloc_class);
|
|
904
|
+
track_object(recorder_instance, new_obj, NUM2UINT(weight), &alloc_class_slice);
|
|
905
|
+
return Qtrue;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
static VALUE _native_check_heap_hashes(DDTRACE_UNUSED VALUE _self, VALUE locations) {
|
|
909
|
+
ENFORCE_TYPE(locations, T_ARRAY);
|
|
910
|
+
size_t locations_len = rb_array_len(locations);
|
|
911
|
+
ddog_prof_Location locations_arr[locations_len];
|
|
912
|
+
for (size_t i = 0; i < locations_len; i++) {
|
|
913
|
+
VALUE location = rb_ary_entry(locations, i);
|
|
914
|
+
ENFORCE_TYPE(location, T_ARRAY);
|
|
915
|
+
VALUE name = rb_ary_entry(location, 0);
|
|
916
|
+
VALUE filename = rb_ary_entry(location, 1);
|
|
917
|
+
VALUE line = rb_ary_entry(location, 2);
|
|
918
|
+
ENFORCE_TYPE(name, T_STRING);
|
|
919
|
+
ENFORCE_TYPE(filename, T_STRING);
|
|
920
|
+
ENFORCE_TYPE(line, T_FIXNUM);
|
|
921
|
+
locations_arr[i] = (ddog_prof_Location) {
|
|
922
|
+
.line = line,
|
|
923
|
+
.function = (ddog_prof_Function) {
|
|
924
|
+
.name = char_slice_from_ruby_string(name),
|
|
925
|
+
.filename = char_slice_from_ruby_string(filename),
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
ddog_prof_Slice_Location ddog_locations = {
|
|
930
|
+
.len = locations_len,
|
|
931
|
+
.ptr = locations_arr,
|
|
932
|
+
};
|
|
933
|
+
heap_recorder_testonly_assert_hash_matches(ddog_locations);
|
|
934
|
+
|
|
935
|
+
return Qnil;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
static void reset_profile_slot(profile_slot *slot, ddog_Timespec *start_time /* Can be null */) {
|
|
939
|
+
ddog_prof_Profile_Result reset_result = ddog_prof_Profile_reset(&slot->profile, start_time);
|
|
619
940
|
if (reset_result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
|
|
620
941
|
rb_raise(rb_eRuntimeError, "Failed to reset profile: %"PRIsVALUE, get_error_details_and_drop(&reset_result.err));
|
|
621
942
|
}
|
|
943
|
+
slot->stats = (stats_slot) {};
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
|
947
|
+
// It SHOULD NOT be used for other purposes.
|
|
948
|
+
static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
|
949
|
+
struct stack_recorder_state *state;
|
|
950
|
+
TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
|
|
951
|
+
|
|
952
|
+
heap_recorder_prepare_iteration(state->heap_recorder);
|
|
953
|
+
|
|
954
|
+
return Qnil;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
|
958
|
+
// It SHOULD NOT be used for other purposes.
|
|
959
|
+
static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
|
960
|
+
struct stack_recorder_state *state;
|
|
961
|
+
TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
|
|
962
|
+
|
|
963
|
+
heap_recorder_finish_iteration(state->heap_recorder);
|
|
964
|
+
|
|
965
|
+
return Qnil;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
|
969
|
+
// It SHOULD NOT be used for other purposes.
|
|
970
|
+
static VALUE _native_debug_heap_recorder(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
|
971
|
+
struct stack_recorder_state *state;
|
|
972
|
+
TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
|
|
973
|
+
|
|
974
|
+
return heap_recorder_testonly_debug(state->heap_recorder);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
#pragma GCC diagnostic push
|
|
978
|
+
// rb_gc_force_recycle was deprecated in latest versions of Ruby and is a noop.
|
|
979
|
+
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
980
|
+
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
981
|
+
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
|
982
|
+
// It SHOULD NOT be used for other purposes.
|
|
983
|
+
static VALUE _native_gc_force_recycle(DDTRACE_UNUSED VALUE _self, VALUE obj) {
|
|
984
|
+
#ifdef HAVE_WORKING_RB_GC_FORCE_RECYCLE
|
|
985
|
+
rb_gc_force_recycle(obj);
|
|
986
|
+
#endif
|
|
987
|
+
return Qnil;
|
|
988
|
+
}
|
|
989
|
+
#pragma GCC diagnostic pop
|
|
990
|
+
|
|
991
|
+
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
|
992
|
+
// It SHOULD NOT be used for other purposes.
|
|
993
|
+
static VALUE _native_has_seen_id_flag(DDTRACE_UNUSED VALUE _self, VALUE obj) {
|
|
994
|
+
#ifndef NO_SEEN_OBJ_ID_FLAG
|
|
995
|
+
if (RB_FL_TEST(obj, RUBY_FL_SEEN_OBJ_ID)) {
|
|
996
|
+
return Qtrue;
|
|
997
|
+
} else {
|
|
998
|
+
return Qfalse;
|
|
999
|
+
}
|
|
1000
|
+
#else
|
|
1001
|
+
return Qfalse;
|
|
1002
|
+
#endif
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE recorder_instance) {
|
|
1006
|
+
struct stack_recorder_state *state;
|
|
1007
|
+
TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
|
|
1008
|
+
|
|
1009
|
+
uint64_t total_serializations = state->stats_lifetime.serialization_successes + state->stats_lifetime.serialization_failures;
|
|
1010
|
+
|
|
1011
|
+
VALUE heap_recorder_snapshot = state->heap_recorder ?
|
|
1012
|
+
heap_recorder_state_snapshot(state->heap_recorder) : Qnil;
|
|
1013
|
+
|
|
1014
|
+
VALUE stats_as_hash = rb_hash_new();
|
|
1015
|
+
VALUE arguments[] = {
|
|
1016
|
+
ID2SYM(rb_intern("serialization_successes")), /* => */ ULL2NUM(state->stats_lifetime.serialization_successes),
|
|
1017
|
+
ID2SYM(rb_intern("serialization_failures")), /* => */ ULL2NUM(state->stats_lifetime.serialization_failures),
|
|
1018
|
+
|
|
1019
|
+
ID2SYM(rb_intern("serialization_time_ns_min")), /* => */ RUBY_NUM_OR_NIL(state->stats_lifetime.serialization_time_ns_min, != INT64_MAX, LONG2NUM),
|
|
1020
|
+
ID2SYM(rb_intern("serialization_time_ns_max")), /* => */ RUBY_NUM_OR_NIL(state->stats_lifetime.serialization_time_ns_max, > 0, LONG2NUM),
|
|
1021
|
+
ID2SYM(rb_intern("serialization_time_ns_total")), /* => */ RUBY_NUM_OR_NIL(state->stats_lifetime.serialization_time_ns_total, > 0, LONG2NUM),
|
|
1022
|
+
ID2SYM(rb_intern("serialization_time_ns_avg")), /* => */ RUBY_AVG_OR_NIL(state->stats_lifetime.serialization_time_ns_total, total_serializations),
|
|
1023
|
+
|
|
1024
|
+
ID2SYM(rb_intern("heap_recorder_snapshot")), /* => */ heap_recorder_snapshot,
|
|
1025
|
+
};
|
|
1026
|
+
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(stats_as_hash, arguments[i], arguments[i+1]);
|
|
1027
|
+
return stats_as_hash;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
static VALUE build_profile_stats(profile_slot *slot, long serialization_time_ns, long heap_iteration_prep_time_ns, long heap_profile_build_time_ns) {
|
|
1031
|
+
VALUE stats_as_hash = rb_hash_new();
|
|
1032
|
+
VALUE arguments[] = {
|
|
1033
|
+
ID2SYM(rb_intern("recorded_samples")), /* => */ ULL2NUM(slot->stats.recorded_samples),
|
|
1034
|
+
ID2SYM(rb_intern("serialization_time_ns")), /* => */ LONG2NUM(serialization_time_ns),
|
|
1035
|
+
ID2SYM(rb_intern("heap_iteration_prep_time_ns")), /* => */ LONG2NUM(heap_iteration_prep_time_ns),
|
|
1036
|
+
ID2SYM(rb_intern("heap_profile_build_time_ns")), /* => */ LONG2NUM(heap_profile_build_time_ns),
|
|
1037
|
+
};
|
|
1038
|
+
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(stats_as_hash, arguments[i], arguments[i+1]);
|
|
1039
|
+
return stats_as_hash;
|
|
622
1040
|
}
|