datadog 2.9.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +72 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +2 -2
- data/ext/datadog_profiling_native_extension/collectors_stack.c +3 -3
- data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +46 -6
- data/ext/datadog_profiling_native_extension/extconf.rb +4 -0
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +2 -0
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +0 -8
- data/ext/datadog_profiling_native_extension/heap_recorder.c +51 -93
- data/ext/datadog_profiling_native_extension/heap_recorder.h +1 -1
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +56 -0
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +7 -0
- data/ext/datadog_profiling_native_extension/profiling.c +7 -0
- data/ext/datadog_profiling_native_extension/stack_recorder.c +9 -22
- data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -1
- data/ext/libdatadog_api/crashtracker.c +4 -4
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/actions_handler.rb +27 -0
- data/lib/datadog/appsec/component.rb +14 -8
- data/lib/datadog/appsec/configuration/settings.rb +73 -11
- data/lib/datadog/appsec/context.rb +28 -8
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +6 -2
- data/lib/datadog/appsec/contrib/active_record/patcher.rb +0 -3
- data/lib/datadog/appsec/contrib/devise/configuration.rb +76 -0
- data/lib/datadog/appsec/contrib/devise/event.rb +4 -7
- data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +16 -21
- data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +8 -15
- data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +1 -1
- data/lib/datadog/appsec/contrib/devise/patcher.rb +0 -3
- data/lib/datadog/appsec/contrib/devise/tracking.rb +1 -1
- data/lib/datadog/appsec/contrib/excon/integration.rb +41 -0
- data/lib/datadog/appsec/contrib/excon/patcher.rb +28 -0
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +43 -0
- data/lib/datadog/appsec/contrib/faraday/connection_patch.rb +22 -0
- data/lib/datadog/appsec/contrib/faraday/integration.rb +42 -0
- data/lib/datadog/appsec/contrib/faraday/patcher.rb +53 -0
- data/lib/datadog/appsec/contrib/faraday/rack_builder_patch.rb +22 -0
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +42 -0
- data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +1 -7
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +11 -14
- data/lib/datadog/appsec/contrib/graphql/patcher.rb +0 -3
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +65 -70
- data/lib/datadog/appsec/contrib/rack/patcher.rb +0 -3
- data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +3 -3
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +11 -22
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +20 -24
- data/lib/datadog/appsec/contrib/rails/patcher.rb +3 -16
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +38 -47
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +3 -29
- data/lib/datadog/appsec/ext.rb +6 -1
- data/lib/datadog/appsec/metrics/collector.rb +38 -0
- data/lib/datadog/appsec/metrics/exporter.rb +35 -0
- data/lib/datadog/appsec/metrics/telemetry.rb +23 -0
- data/lib/datadog/appsec/metrics.rb +13 -0
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +19 -24
- data/lib/datadog/appsec/processor.rb +4 -3
- data/lib/datadog/appsec/remote.rb +4 -0
- data/lib/datadog/appsec/response.rb +18 -80
- data/lib/datadog/appsec/security_engine/result.rb +67 -0
- data/lib/datadog/appsec/security_engine/runner.rb +88 -0
- data/lib/datadog/appsec/security_engine.rb +9 -0
- data/lib/datadog/appsec.rb +16 -5
- data/lib/datadog/core/configuration/components.rb +7 -1
- data/lib/datadog/core/configuration/ext.rb +1 -1
- data/lib/datadog/core/configuration/option_definition.rb +2 -0
- data/lib/datadog/core/configuration/settings.rb +22 -6
- data/lib/datadog/core/encoding.rb +16 -0
- data/lib/datadog/core/environment/agent_info.rb +77 -0
- data/lib/datadog/core/remote/transport/http/api.rb +13 -18
- data/lib/datadog/core/remote/transport/http/config.rb +0 -18
- data/lib/datadog/core/remote/transport/http/negotiation.rb +1 -18
- data/lib/datadog/core/remote/transport/http.rb +7 -12
- data/lib/datadog/core/remote/transport/negotiation.rb +13 -1
- data/lib/datadog/core/telemetry/event.rb +5 -0
- data/lib/datadog/core/transport/http/adapters/unix_socket.rb +1 -1
- data/lib/datadog/{tracing → core}/transport/http/api/instance.rb +1 -1
- data/lib/datadog/{tracing → core}/transport/http/api/spec.rb +1 -1
- data/lib/datadog/{tracing → core}/transport/http/builder.rb +37 -17
- data/lib/datadog/core/transport/response.rb +4 -0
- data/lib/datadog/di/code_tracker.rb +15 -8
- data/lib/datadog/di/component.rb +3 -0
- data/lib/datadog/di/configuration/settings.rb +14 -0
- data/lib/datadog/di/contrib.rb +2 -0
- data/lib/datadog/di/logger.rb +30 -0
- data/lib/datadog/di/probe.rb +3 -6
- data/lib/datadog/di/probe_manager.rb +5 -2
- data/lib/datadog/di/probe_notification_builder.rb +6 -0
- data/lib/datadog/di/probe_notifier_worker.rb +15 -4
- data/lib/datadog/di/redactor.rb +0 -1
- data/lib/datadog/di/remote.rb +29 -8
- data/lib/datadog/di/utils.rb +91 -0
- data/lib/datadog/di.rb +3 -0
- data/lib/datadog/profiling/component.rb +2 -8
- data/lib/datadog/profiling/load_native_extension.rb +1 -33
- data/lib/datadog/tracing/configuration/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/aws/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/extensions.rb +29 -3
- data/lib/datadog/tracing/contrib/graphql/configuration/error_extension_env_parser.rb +21 -0
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +11 -0
- data/lib/datadog/tracing/contrib/graphql/ext.rb +5 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +102 -11
- data/lib/datadog/tracing/contrib/http/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/rack/header_collection.rb +11 -1
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +1 -1
- data/lib/datadog/tracing/contrib/span_attribute_schema.rb +6 -1
- data/lib/datadog/tracing/transport/http/api.rb +11 -2
- data/lib/datadog/tracing/transport/http/traces.rb +0 -3
- data/lib/datadog/tracing/transport/http.rb +12 -7
- data/lib/datadog/tracing/transport/serializable_trace.rb +8 -4
- data/lib/datadog/tracing/transport/traces.rb +25 -8
- data/lib/datadog/version.rb +1 -1
- metadata +51 -42
- data/ext/datadog_profiling_loader/datadog_profiling_loader.c +0 -142
- data/ext/datadog_profiling_loader/extconf.rb +0 -60
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +0 -46
- data/lib/datadog/appsec/contrib/patcher.rb +0 -12
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +0 -69
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +0 -47
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +0 -53
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +0 -53
- data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -14
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +0 -48
- data/lib/datadog/appsec/monitor/reactive/set_user.rb +0 -45
- data/lib/datadog/appsec/processor/context.rb +0 -107
- data/lib/datadog/appsec/reactive/address_hash.rb +0 -22
- data/lib/datadog/appsec/reactive/engine.rb +0 -47
- data/lib/datadog/appsec/reactive/subscriber.rb +0 -19
- data/lib/datadog/core/remote/transport/http/api/instance.rb +0 -39
- data/lib/datadog/core/remote/transport/http/api/spec.rb +0 -21
- data/lib/datadog/core/remote/transport/http/builder.rb +0 -219
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae1f5db840520c849d4a21b02232137e5272619ddd7fb46126f263c71c8a5849
|
4
|
+
data.tar.gz: bd1b16b752c9e457722965ee29cf8af770ba1dcf3b42cac412c206f6245dd333
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 072e88d7ba0698a8ef6b080b6b1ab6a5be18b4a69fdb142b42494bc0afc6184aadbe7c4f52e54e2373a37f40fe439215a8a9a4594653d4d6453ef614142ca8e7
|
7
|
+
data.tar.gz: 88cb6087f42bc450a517d53338772b4fb10b00fe40024843fec3df3849bbc14f6efd25defaf1dcadb9224c9513878c75e2ee33fb0d3d24bb063ca1d2dd36dcb6
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,53 @@
|
|
2
2
|
|
3
3
|
## [Unreleased]
|
4
4
|
|
5
|
+
## [2.11.0] - 2025-02-24
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
* Tracing: Support `graphql` multiple query errors report via Span Events ([#4177][])
|
10
|
+
* Profiling: Enable GVL profiling by default on Ruby 3.2+ ([#4406][])
|
11
|
+
* Profiling: Support correlating profiling with OTel API 1.5+ ([#4425][])
|
12
|
+
* AppSec: Add detection of Server-Side Request Forgery attacks for `excon` ([#4399][])
|
13
|
+
* AppSec: Add detection of Server-Side Request Forgery attacks for `faraday` ([#4391][])
|
14
|
+
* AppSec: Deprecate `appsec.track_user_events` configuration setting in favor of `appsec.auto_user_instrumentation` ([#4352][])
|
15
|
+
* Dynamic Instrumentation: Add optional trace logging ([#4283][])
|
16
|
+
|
17
|
+
### Changed
|
18
|
+
|
19
|
+
* Increase default timeout for unix domain socket to 30 seconds ([#4411][])
|
20
|
+
* Upgrade `libdatadog` to `16.0.1` ([#4353][])
|
21
|
+
* Dynamic Instrumentation: Improve path matching with prefixes of probe paths ([#4346][])
|
22
|
+
* Dynamic Instrumentation: Improve event reporting with combing status and snapshot events ([#4360][])
|
23
|
+
|
24
|
+
### Fixed
|
25
|
+
|
26
|
+
* Tracing: Fix `rack` to continue trace if only distributed trace is present ([#4398][])
|
27
|
+
* AppSec: Fix a memory leak issue for RASP ([#4422][])
|
28
|
+
* Dynamic Instrumentation: Fix event submission on forked servers ([#4363][])
|
29
|
+
|
30
|
+
### Removed
|
31
|
+
|
32
|
+
* Tracing: Remove peer services by default ([#3846][])
|
33
|
+
|
34
|
+
## [2.10.0] - 2025-02-04
|
35
|
+
|
36
|
+
### Added
|
37
|
+
|
38
|
+
* AppSec: Add configuration option(`Datadog.configuration.appsec.rasp_enabled`) to enable/disable Runtime Application Self-Protection checks ([#4311][])
|
39
|
+
* AppSec: Add stack trace when SQL Injection attack is detected ([#4321][])
|
40
|
+
|
41
|
+
### Changed
|
42
|
+
|
43
|
+
* Add `logger` gem as dependency ([#4257][])
|
44
|
+
* Bump minimum version of `datadog-ruby_core_source` to 3.4 ([#4323][])
|
45
|
+
|
46
|
+
### Fixed
|
47
|
+
|
48
|
+
* Dynamic instrumentation: Fix report probe status when dynamic instrumentation probes fail to instrument ([#4301][])
|
49
|
+
* Dynamic instrumentation: Include variables named `env` in probe snapshots ([#4292][])
|
50
|
+
* Fix a concurrency issue during application boot ([#4303][])
|
51
|
+
|
5
52
|
## [2.9.0] - 2025-01-15
|
6
53
|
|
7
54
|
### Added
|
@@ -3079,7 +3126,9 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
|
|
3079
3126
|
Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
3080
3127
|
|
3081
3128
|
|
3082
|
-
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.
|
3129
|
+
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.11.0...master
|
3130
|
+
[2.11.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.10.0...v2.11.0
|
3131
|
+
[2.10.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.9.0...v2.10.0
|
3083
3132
|
[2.9.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.8.0...v2.9.0
|
3084
3133
|
[2.8.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.7.1...v2.8.0
|
3085
3134
|
[2.7.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.6.0...v2.7.0
|
@@ -4506,6 +4555,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
4506
4555
|
[#3837]: https://github.com/DataDog/dd-trace-rb/issues/3837
|
4507
4556
|
[#3839]: https://github.com/DataDog/dd-trace-rb/issues/3839
|
4508
4557
|
[#3841]: https://github.com/DataDog/dd-trace-rb/issues/3841
|
4558
|
+
[#3846]: https://github.com/DataDog/dd-trace-rb/issues/3846
|
4509
4559
|
[#3849]: https://github.com/DataDog/dd-trace-rb/issues/3849
|
4510
4560
|
[#3852]: https://github.com/DataDog/dd-trace-rb/issues/3852
|
4511
4561
|
[#3857]: https://github.com/DataDog/dd-trace-rb/issues/3857
|
@@ -4539,6 +4589,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
4539
4589
|
[#4161]: https://github.com/DataDog/dd-trace-rb/issues/4161
|
4540
4590
|
[#4164]: https://github.com/DataDog/dd-trace-rb/issues/4164
|
4541
4591
|
[#4167]: https://github.com/DataDog/dd-trace-rb/issues/4167
|
4592
|
+
[#4177]: https://github.com/DataDog/dd-trace-rb/issues/4177
|
4542
4593
|
[#4178]: https://github.com/DataDog/dd-trace-rb/issues/4178
|
4543
4594
|
[#4195]: https://github.com/DataDog/dd-trace-rb/issues/4195
|
4544
4595
|
[#4196]: https://github.com/DataDog/dd-trace-rb/issues/4196
|
@@ -4550,10 +4601,30 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
4550
4601
|
[#4239]: https://github.com/DataDog/dd-trace-rb/issues/4239
|
4551
4602
|
[#4240]: https://github.com/DataDog/dd-trace-rb/issues/4240
|
4552
4603
|
[#4249]: https://github.com/DataDog/dd-trace-rb/issues/4249
|
4604
|
+
[#4257]: https://github.com/DataDog/dd-trace-rb/issues/4257
|
4553
4605
|
[#4266]: https://github.com/DataDog/dd-trace-rb/issues/4266
|
4554
4606
|
[#4272]: https://github.com/DataDog/dd-trace-rb/issues/4272
|
4607
|
+
[#4283]: https://github.com/DataDog/dd-trace-rb/issues/4283
|
4555
4608
|
[#4285]: https://github.com/DataDog/dd-trace-rb/issues/4285
|
4556
4609
|
[#4288]: https://github.com/DataDog/dd-trace-rb/issues/4288
|
4610
|
+
[#4292]: https://github.com/DataDog/dd-trace-rb/issues/4292
|
4611
|
+
[#4301]: https://github.com/DataDog/dd-trace-rb/issues/4301
|
4612
|
+
[#4303]: https://github.com/DataDog/dd-trace-rb/issues/4303
|
4613
|
+
[#4311]: https://github.com/DataDog/dd-trace-rb/issues/4311
|
4614
|
+
[#4321]: https://github.com/DataDog/dd-trace-rb/issues/4321
|
4615
|
+
[#4323]: https://github.com/DataDog/dd-trace-rb/issues/4323
|
4616
|
+
[#4346]: https://github.com/DataDog/dd-trace-rb/issues/4346
|
4617
|
+
[#4352]: https://github.com/DataDog/dd-trace-rb/issues/4352
|
4618
|
+
[#4353]: https://github.com/DataDog/dd-trace-rb/issues/4353
|
4619
|
+
[#4360]: https://github.com/DataDog/dd-trace-rb/issues/4360
|
4620
|
+
[#4363]: https://github.com/DataDog/dd-trace-rb/issues/4363
|
4621
|
+
[#4391]: https://github.com/DataDog/dd-trace-rb/issues/4391
|
4622
|
+
[#4398]: https://github.com/DataDog/dd-trace-rb/issues/4398
|
4623
|
+
[#4399]: https://github.com/DataDog/dd-trace-rb/issues/4399
|
4624
|
+
[#4406]: https://github.com/DataDog/dd-trace-rb/issues/4406
|
4625
|
+
[#4411]: https://github.com/DataDog/dd-trace-rb/issues/4411
|
4626
|
+
[#4422]: https://github.com/DataDog/dd-trace-rb/issues/4422
|
4627
|
+
[#4425]: https://github.com/DataDog/dd-trace-rb/issues/4425
|
4557
4628
|
[@AdrianLC]: https://github.com/AdrianLC
|
4558
4629
|
[@Azure7111]: https://github.com/Azure7111
|
4559
4630
|
[@BabyGroot]: https://github.com/BabyGroot
|
@@ -17,7 +17,7 @@
|
|
17
17
|
#include "setup_signal_handler.h"
|
18
18
|
#include "time_helpers.h"
|
19
19
|
|
20
|
-
// Used to trigger the execution of Collectors::
|
20
|
+
// Used to trigger the execution of Collectors::ThreadContext, which implements all of the sampling logic
|
21
21
|
// itself; this class only implements the "when to do it" part.
|
22
22
|
//
|
23
23
|
// This file implements the native bits of the Datadog::Profiling::Collectors::CpuAndWallTimeWorker class
|
@@ -33,7 +33,7 @@
|
|
33
33
|
// Currently, sampling Ruby threads requires calling Ruby VM APIs that are only safe to call while holding on to the
|
34
34
|
// global VM lock (and are not async-signal safe -- cannot be called from a signal handler).
|
35
35
|
//
|
36
|
-
// @ivoanjo: As a note, I don't think we should think of this constraint as set in stone. Since can reach
|
36
|
+
// @ivoanjo: As a note, I don't think we should think of this constraint as set in stone. Since we can reach inside the Ruby
|
37
37
|
// internals, we may be able to figure out a way of overcoming it. But it's definitely going to be hard so for now
|
38
38
|
// we're considering it as a given.
|
39
39
|
//
|
@@ -300,7 +300,7 @@ void sample_thread(
|
|
300
300
|
}
|
301
301
|
|
302
302
|
buffer->locations[i] = (ddog_prof_Location) {
|
303
|
-
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C("")},
|
303
|
+
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C(""), .build_id_id = {}},
|
304
304
|
.function = (ddog_prof_Function) {.name = name_slice, .filename = filename_slice},
|
305
305
|
.line = line,
|
306
306
|
};
|
@@ -379,7 +379,7 @@ static void maybe_add_placeholder_frames_omitted(VALUE thread, sampling_buffer*
|
|
379
379
|
ddog_CharSlice function_name = DDOG_CHARSLICE_C("");
|
380
380
|
ddog_CharSlice function_filename = {.ptr = frames_omitted_message, .len = strlen(frames_omitted_message)};
|
381
381
|
buffer->locations[buffer->max_frames - 1] = (ddog_prof_Location) {
|
382
|
-
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C("")},
|
382
|
+
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C(""), .build_id_id = {}},
|
383
383
|
.function = (ddog_prof_Function) {.name = function_name, .filename = function_filename},
|
384
384
|
.line = 0,
|
385
385
|
};
|
@@ -426,7 +426,7 @@ void record_placeholder_stack(
|
|
426
426
|
ddog_CharSlice placeholder_stack
|
427
427
|
) {
|
428
428
|
ddog_prof_Location placeholder_location = {
|
429
|
-
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C("")},
|
429
|
+
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C(""), .build_id_id = {}},
|
430
430
|
.function = {.name = DDOG_CHARSLICE_C(""), .filename = placeholder_stack},
|
431
431
|
.line = 0,
|
432
432
|
};
|
@@ -1,5 +1,6 @@
|
|
1
1
|
#include <ruby.h>
|
2
2
|
|
3
|
+
#include "datadog_ruby_common.h"
|
3
4
|
#include "collectors_thread_context.h"
|
4
5
|
#include "clock_id.h"
|
5
6
|
#include "collectors_stack.h"
|
@@ -102,6 +103,7 @@ static ID at_kind_id; // id of :@kind in Ruby
|
|
102
103
|
static ID at_name_id; // id of :@name in Ruby
|
103
104
|
static ID server_id; // id of :server in Ruby
|
104
105
|
static ID otel_context_storage_id; // id of :__opentelemetry_context_storage__ in Ruby
|
106
|
+
static ID otel_fiber_context_storage_id; // id of :@opentelemetry_context in Ruby
|
105
107
|
|
106
108
|
// This is used by `thread_context_collector_on_gvl_running`. Because when that method gets called we're not sure if
|
107
109
|
// it's safe to access the state of the thread context collector, we store this setting as a global value. This does
|
@@ -111,6 +113,7 @@ static ID otel_context_storage_id; // id of :__opentelemetry_context_storage__ i
|
|
111
113
|
static uint32_t global_waiting_for_gvl_threshold_ns = MILLIS_AS_NS(10);
|
112
114
|
|
113
115
|
typedef enum { OTEL_CONTEXT_ENABLED_FALSE, OTEL_CONTEXT_ENABLED_ONLY, OTEL_CONTEXT_ENABLED_BOTH } otel_context_enabled;
|
116
|
+
typedef enum { OTEL_CONTEXT_SOURCE_UNKNOWN, OTEL_CONTEXT_SOURCE_FIBER_IVAR, OTEL_CONTEXT_SOURCE_FIBER_LOCAL } otel_context_source;
|
114
117
|
|
115
118
|
// Contains state for a single ThreadContext instance
|
116
119
|
typedef struct {
|
@@ -140,6 +143,8 @@ typedef struct {
|
|
140
143
|
bool timeline_enabled;
|
141
144
|
// Used to control context collection
|
142
145
|
otel_context_enabled otel_context_enabled;
|
146
|
+
// Used to remember where otel context is being stored after we observe it the first time
|
147
|
+
otel_context_source otel_context_source;
|
143
148
|
// Used when calling monotonic_to_system_epoch_ns
|
144
149
|
monotonic_to_system_epoch_state time_converter_state;
|
145
150
|
// Used to identify the main thread, to give it a fallback name
|
@@ -348,6 +353,7 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
348
353
|
at_name_id = rb_intern_const("@name");
|
349
354
|
server_id = rb_intern_const("server");
|
350
355
|
otel_context_storage_id = rb_intern_const("__opentelemetry_context_storage__");
|
356
|
+
otel_fiber_context_storage_id = rb_intern_const("@opentelemetry_context");
|
351
357
|
|
352
358
|
#ifndef NO_GVL_INSTRUMENTATION
|
353
359
|
// This will raise if Ruby already ran out of thread-local keys
|
@@ -433,6 +439,7 @@ static VALUE _native_new(VALUE klass) {
|
|
433
439
|
state->endpoint_collection_enabled = true;
|
434
440
|
state->timeline_enabled = true;
|
435
441
|
state->otel_context_enabled = OTEL_CONTEXT_ENABLED_FALSE;
|
442
|
+
state->otel_context_source = OTEL_CONTEXT_SOURCE_UNKNOWN;
|
436
443
|
state->time_converter_state = (monotonic_to_system_epoch_state) MONOTONIC_TO_SYSTEM_EPOCH_INITIALIZER;
|
437
444
|
VALUE main_thread = rb_thread_main();
|
438
445
|
state->main_thread = main_thread;
|
@@ -1450,11 +1457,8 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
1450
1457
|
|
1451
1458
|
// Since this is stack allocated, be careful about moving it
|
1452
1459
|
ddog_CharSlice class_name;
|
1453
|
-
ddog_CharSlice *optional_class_name = NULL;
|
1454
1460
|
char imemo_type[100];
|
1455
1461
|
|
1456
|
-
optional_class_name = &class_name;
|
1457
|
-
|
1458
1462
|
if (
|
1459
1463
|
type == RUBY_T_OBJECT ||
|
1460
1464
|
type == RUBY_T_CLASS ||
|
@@ -1510,7 +1514,7 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
1510
1514
|
class_name = ruby_vm_type; // For other weird internal things we just use the VM type
|
1511
1515
|
}
|
1512
1516
|
|
1513
|
-
track_object(state->recorder_instance, new_object, sample_weight,
|
1517
|
+
track_object(state->recorder_instance, new_object, sample_weight, class_name);
|
1514
1518
|
|
1515
1519
|
per_thread_context *thread_context = get_or_create_context_for(current_thread, state);
|
1516
1520
|
|
@@ -1523,7 +1527,7 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
1523
1527
|
(sample_values) {.alloc_samples = sample_weight, .alloc_samples_unscaled = 1, .heap_sample = true},
|
1524
1528
|
INVALID_TIME, // For now we're not collecting timestamps for allocation events, as per profiling team internal discussions
|
1525
1529
|
&ruby_vm_type,
|
1526
|
-
|
1530
|
+
&class_name,
|
1527
1531
|
/* is_gvl_waiting_state: */ false,
|
1528
1532
|
/* is_safe_to_allocate_objects: */ false // Not safe to allocate further inside the NEWOBJ tracepoint
|
1529
1533
|
);
|
@@ -1685,6 +1689,42 @@ static VALUE _native_sample_skipped_allocation_samples(DDTRACE_UNUSED VALUE self
|
|
1685
1689
|
return Qtrue;
|
1686
1690
|
}
|
1687
1691
|
|
1692
|
+
#ifndef NO_CURRENT_FIBER_FOR // Ruby 3.1+
|
1693
|
+
static VALUE otel_context_storage_for(thread_context_collector_state *state, VALUE thread) {
|
1694
|
+
if (state->otel_context_source == OTEL_CONTEXT_SOURCE_FIBER_IVAR) { // otel-api 1.5+
|
1695
|
+
VALUE current_fiber = current_fiber_for(thread);
|
1696
|
+
return current_fiber == Qnil ? Qnil : rb_ivar_get(current_fiber, otel_fiber_context_storage_id /* @opentelemetry_context */);
|
1697
|
+
}
|
1698
|
+
|
1699
|
+
if (state->otel_context_source == OTEL_CONTEXT_SOURCE_FIBER_LOCAL) { // otel-api < 1.5
|
1700
|
+
return rb_thread_local_aref(thread, otel_context_storage_id /* __opentelemetry_context_storage__ */);
|
1701
|
+
}
|
1702
|
+
|
1703
|
+
// If we got here, it means we never observed a context being set. Let's probe which one to use.
|
1704
|
+
VALUE current_fiber = current_fiber_for(thread);
|
1705
|
+
if (current_fiber != Qnil) {
|
1706
|
+
VALUE context_storage = rb_ivar_get(current_fiber, otel_fiber_context_storage_id /* @opentelemetry_context */);
|
1707
|
+
if (context_storage != Qnil) {
|
1708
|
+
state->otel_context_source = OTEL_CONTEXT_SOURCE_FIBER_IVAR; // Remember for next time
|
1709
|
+
return context_storage;
|
1710
|
+
}
|
1711
|
+
} else {
|
1712
|
+
VALUE context_storage = rb_thread_local_aref(thread, otel_context_storage_id /* __opentelemetry_context_storage__ */);
|
1713
|
+
if (context_storage != Qnil) {
|
1714
|
+
state->otel_context_source = OTEL_CONTEXT_SOURCE_FIBER_LOCAL; // Remember for next time
|
1715
|
+
return context_storage;
|
1716
|
+
}
|
1717
|
+
}
|
1718
|
+
|
1719
|
+
// There's no context storage attached to the current thread
|
1720
|
+
return Qnil;
|
1721
|
+
}
|
1722
|
+
#else
|
1723
|
+
static inline VALUE otel_context_storage_for(DDTRACE_UNUSED thread_context_collector_state *state, VALUE thread) {
|
1724
|
+
return rb_thread_local_aref(thread, otel_context_storage_id /* __opentelemetry_context_storage__ */);
|
1725
|
+
}
|
1726
|
+
#endif
|
1727
|
+
|
1688
1728
|
// This method differs from trace_identifiers_for/ddtrace_otel_trace_identifiers_for to support the situation where
|
1689
1729
|
// the opentelemetry ruby library is being used for tracing AND the ddtrace tracing bits are not involved at all.
|
1690
1730
|
//
|
@@ -1712,7 +1752,7 @@ static void otel_without_ddtrace_trace_identifiers_for(
|
|
1712
1752
|
trace_identifiers *trace_identifiers_result,
|
1713
1753
|
bool is_safe_to_allocate_objects
|
1714
1754
|
) {
|
1715
|
-
VALUE context_storage =
|
1755
|
+
VALUE context_storage = otel_context_storage_for(state, thread);
|
1716
1756
|
|
1717
1757
|
// If it exists, context_storage is expected to be an Array[OpenTelemetry::Context]
|
1718
1758
|
if (context_storage == Qnil || !RB_TYPE_P(context_storage, T_ARRAY)) return;
|
@@ -164,6 +164,10 @@ $defs << "-DNO_INT_FIRST_LINENO" if RUBY_VERSION < "3.2"
|
|
164
164
|
# On older Rubies, "pop" was not a primitive operation
|
165
165
|
$defs << "-DNO_PRIMITIVE_POP" if RUBY_VERSION < "3.2"
|
166
166
|
|
167
|
+
# We could support this for older Rubies, but since this only gets used by the OTEL context extraction, and that
|
168
|
+
# use-case is only for 3.1+, we didn't bother supporting it farther back yet.
|
169
|
+
$defs << "-DNO_CURRENT_FIBER_FOR" if RUBY_VERSION < "3.1"
|
170
|
+
|
167
171
|
# On older Rubies, there was no tid member in the internal thread structure
|
168
172
|
$defs << "-DNO_THREAD_TID" if RUBY_VERSION < "3.1"
|
169
173
|
|
@@ -39,14 +39,6 @@
|
|
39
39
|
|
40
40
|
static inline void gvl_profiling_init(void) { }
|
41
41
|
|
42
|
-
// This header gets included in private_vm_access.c which can't include datadog_ruby_common.h so we replicate this
|
43
|
-
// helper here
|
44
|
-
#ifdef __GNUC__
|
45
|
-
#define DDTRACE_UNUSED __attribute__((unused))
|
46
|
-
#else
|
47
|
-
#define DDTRACE_UNUSED
|
48
|
-
#endif
|
49
|
-
|
50
42
|
// NOTE: This is a hack that relies on the knowledge that on Ruby 3.2 the
|
51
43
|
// RUBY_INTERNAL_THREAD_EVENT_READY and RUBY_INTERNAL_THREAD_EVENT_RESUMED events always get called on the thread they
|
52
44
|
// are about. Thus, we can use our thread local storage hack to get this data, even though the event doesn't include it.
|
@@ -1,8 +1,6 @@
|
|
1
1
|
#include "heap_recorder.h"
|
2
|
-
#include <pthread.h>
|
3
2
|
#include "ruby/st.h"
|
4
3
|
#include "ruby_helpers.h"
|
5
|
-
#include <errno.h>
|
6
4
|
#include "collectors_stack.h"
|
7
5
|
#include "libdatadog_helpers.h"
|
8
6
|
#include "time_helpers.h"
|
@@ -30,7 +28,6 @@ typedef struct {
|
|
30
28
|
char *filename;
|
31
29
|
int32_t line;
|
32
30
|
} heap_frame;
|
33
|
-
static st_index_t heap_frame_hash(heap_frame*, st_index_t seed);
|
34
31
|
|
35
32
|
// A compact representation of a stacktrace for a heap allocation.
|
36
33
|
//
|
@@ -113,14 +110,6 @@ static void object_record_free(object_record*);
|
|
113
110
|
static VALUE object_record_inspect(object_record*);
|
114
111
|
static object_record SKIPPED_RECORD = {0};
|
115
112
|
|
116
|
-
// A wrapper around an object record that is in the process of being recorded and was not
|
117
|
-
// yet committed.
|
118
|
-
typedef struct {
|
119
|
-
// Pointer to the (potentially partial) object_record containing metadata about an ongoing recording.
|
120
|
-
// When NULL, this symbolizes an unstarted/invalid recording.
|
121
|
-
object_record *object_record;
|
122
|
-
} recording;
|
123
|
-
|
124
113
|
struct heap_recorder {
|
125
114
|
// Config
|
126
115
|
// Whether the recorder should try to determine approximate sizes for tracked objects.
|
@@ -140,6 +129,9 @@ struct heap_recorder {
|
|
140
129
|
// outside the GVL.
|
141
130
|
// NOTE: This table has ownership of its object_records. The keys are longs and so are
|
142
131
|
// passed as values.
|
132
|
+
//
|
133
|
+
// TODO: @ivoanjo We've evolved to actually never need to look up on object_records (we only insert and iterate),
|
134
|
+
// so right now this seems to be just a really really fancy self-resizing list/set.
|
143
135
|
st_table *object_records;
|
144
136
|
|
145
137
|
// Map[obj_id: long, record: object_record*]
|
@@ -162,7 +154,7 @@ struct heap_recorder {
|
|
162
154
|
long last_update_ns;
|
163
155
|
|
164
156
|
// Data for a heap recording that was started but not yet ended
|
165
|
-
|
157
|
+
object_record *active_recording;
|
166
158
|
|
167
159
|
// Reusable location array, implementing a flyweight pattern for things like iteration.
|
168
160
|
ddog_prof_Location *reusable_locations;
|
@@ -207,7 +199,7 @@ static int st_object_record_update(st_data_t, st_data_t, st_data_t);
|
|
207
199
|
static int st_object_records_iterate(st_data_t, st_data_t, st_data_t);
|
208
200
|
static int st_object_records_debug(st_data_t key, st_data_t value, st_data_t extra);
|
209
201
|
static int update_object_record_entry(st_data_t*, st_data_t*, st_data_t, int);
|
210
|
-
static void commit_recording(heap_recorder*, heap_record*,
|
202
|
+
static void commit_recording(heap_recorder *, heap_record *, object_record *active_recording);
|
211
203
|
static VALUE end_heap_allocation_recording(VALUE end_heap_allocation_args);
|
212
204
|
static void heap_recorder_update(heap_recorder *heap_recorder, bool full_update);
|
213
205
|
static inline double ewma_stat(double previous, double current);
|
@@ -228,7 +220,7 @@ heap_recorder* heap_recorder_new(void) {
|
|
228
220
|
recorder->object_records = st_init_numtable();
|
229
221
|
recorder->object_records_snapshot = NULL;
|
230
222
|
recorder->reusable_locations = ruby_xcalloc(MAX_FRAMES_LIMIT, sizeof(ddog_prof_Location));
|
231
|
-
recorder->active_recording =
|
223
|
+
recorder->active_recording = NULL;
|
232
224
|
recorder->size_enabled = true;
|
233
225
|
recorder->sample_rate = 1; // By default do no sampling on top of what allocation profiling already does
|
234
226
|
|
@@ -254,9 +246,9 @@ void heap_recorder_free(heap_recorder *heap_recorder) {
|
|
254
246
|
st_foreach(heap_recorder->heap_records, st_heap_record_entry_free, 0);
|
255
247
|
st_free_table(heap_recorder->heap_records);
|
256
248
|
|
257
|
-
if (heap_recorder->active_recording
|
249
|
+
if (heap_recorder->active_recording != NULL && heap_recorder->active_recording != &SKIPPED_RECORD) {
|
258
250
|
// If there's a partial object record, clean it up as well
|
259
|
-
object_record_free(heap_recorder->active_recording
|
251
|
+
object_record_free(heap_recorder->active_recording);
|
260
252
|
}
|
261
253
|
|
262
254
|
ruby_xfree(heap_recorder->reusable_locations);
|
@@ -301,7 +293,7 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder) {
|
|
301
293
|
//
|
302
294
|
// There is one small caveat though: fork only preserves one thread and in a Ruby app, that
|
303
295
|
// will be the thread holding on to the GVL. Since we support iteration on the heap recorder
|
304
|
-
// outside of the GVL, any state specific to that interaction may be
|
296
|
+
// outside of the GVL, any state specific to that interaction may be inconsistent after fork
|
305
297
|
// (e.g. an acquired lock for thread safety). Iteration operates on object_records_snapshot
|
306
298
|
// though and that one will be updated on next heap_recorder_prepare_iteration so we really
|
307
299
|
// only need to finish any iteration that might have been left unfinished.
|
@@ -313,18 +305,17 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder) {
|
|
313
305
|
heap_recorder->stats_lifetime = (struct stats_lifetime) {0};
|
314
306
|
}
|
315
307
|
|
316
|
-
void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice
|
308
|
+
void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice alloc_class) {
|
317
309
|
if (heap_recorder == NULL) {
|
318
310
|
return;
|
319
311
|
}
|
320
312
|
|
321
|
-
if (heap_recorder->active_recording
|
313
|
+
if (heap_recorder->active_recording != NULL) {
|
322
314
|
rb_raise(rb_eRuntimeError, "Detected consecutive heap allocation recording starts without end.");
|
323
315
|
}
|
324
316
|
|
325
|
-
if (heap_recorder->num_recordings_skipped
|
326
|
-
heap_recorder->active_recording
|
327
|
-
heap_recorder->num_recordings_skipped++;
|
317
|
+
if (++heap_recorder->num_recordings_skipped < heap_recorder->sample_rate) {
|
318
|
+
heap_recorder->active_recording = &SKIPPED_RECORD;
|
328
319
|
return;
|
329
320
|
}
|
330
321
|
|
@@ -335,13 +326,15 @@ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj
|
|
335
326
|
rb_raise(rb_eRuntimeError, "Detected a bignum object id. These are not supported by heap profiling.");
|
336
327
|
}
|
337
328
|
|
338
|
-
heap_recorder->active_recording = (
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
329
|
+
heap_recorder->active_recording = object_record_new(
|
330
|
+
FIX2LONG(ruby_obj_id),
|
331
|
+
NULL,
|
332
|
+
(live_object_data) {
|
333
|
+
.weight = weight * heap_recorder->sample_rate,
|
334
|
+
.class = string_from_char_slice(alloc_class),
|
335
|
+
.alloc_gen = rb_gc_count(),
|
336
|
+
}
|
337
|
+
);
|
345
338
|
}
|
346
339
|
|
347
340
|
// end_heap_allocation_recording_with_rb_protect gets called while the stack_recorder is holding one of the profile
|
@@ -349,6 +342,10 @@ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj
|
|
349
342
|
// with an rb_protect.
|
350
343
|
__attribute__((warn_unused_result))
|
351
344
|
int end_heap_allocation_recording_with_rb_protect(struct heap_recorder *heap_recorder, ddog_prof_Slice_Location locations) {
|
345
|
+
if (heap_recorder == NULL) {
|
346
|
+
return 0;
|
347
|
+
}
|
348
|
+
|
352
349
|
int exception_state;
|
353
350
|
struct end_heap_allocation_args end_heap_allocation_args = {
|
354
351
|
.heap_recorder = heap_recorder,
|
@@ -364,22 +361,18 @@ static VALUE end_heap_allocation_recording(VALUE end_heap_allocation_args) {
|
|
364
361
|
struct heap_recorder *heap_recorder = args->heap_recorder;
|
365
362
|
ddog_prof_Slice_Location locations = args->locations;
|
366
363
|
|
367
|
-
|
368
|
-
return Qnil;
|
369
|
-
}
|
364
|
+
object_record *active_recording = heap_recorder->active_recording;
|
370
365
|
|
371
|
-
|
372
|
-
|
373
|
-
if (active_recording.object_record == NULL) {
|
366
|
+
if (active_recording == NULL) {
|
374
367
|
// Recording ended without having been started?
|
375
368
|
rb_raise(rb_eRuntimeError, "Ended a heap recording that was not started");
|
376
369
|
}
|
377
370
|
// From now on, mark the global active recording as invalid so we can short-circuit at any point
|
378
371
|
// and not end up with a still active recording. the local active_recording still holds the
|
379
372
|
// data required for committing though.
|
380
|
-
heap_recorder->active_recording =
|
373
|
+
heap_recorder->active_recording = NULL;
|
381
374
|
|
382
|
-
if (active_recording
|
375
|
+
if (active_recording == &SKIPPED_RECORD) { // special marker when we decided to skip due to sampling
|
383
376
|
return Qnil;
|
384
377
|
}
|
385
378
|
|
@@ -685,7 +678,7 @@ static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t val
|
|
685
678
|
for (uint16_t i = 0; i < stack->frames_len; i++) {
|
686
679
|
const heap_frame *frame = &stack->frames[i];
|
687
680
|
locations[i] = (ddog_prof_Location) {
|
688
|
-
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C("")},
|
681
|
+
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C(""), .build_id_id = {}},
|
689
682
|
.function = {
|
690
683
|
.name = {.ptr = frame->name, .len = strlen(frame->name)},
|
691
684
|
.filename = {.ptr = frame->filename, .len = strlen(frame->filename)},
|
@@ -698,6 +691,7 @@ static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t val
|
|
698
691
|
iteration_data.object_data = record->object_data;
|
699
692
|
iteration_data.locations = (ddog_prof_Slice_Location) {.ptr = locations, .len = stack->frames_len};
|
700
693
|
|
694
|
+
// This is expected to be StackRecorder's add_heap_sample_to_active_profile_without_gvl
|
701
695
|
if (!context->for_each_callback(iteration_data, context->for_each_callback_extra_arg)) {
|
702
696
|
return ST_STOP;
|
703
697
|
}
|
@@ -715,49 +709,35 @@ static int st_object_records_debug(DDTRACE_UNUSED st_data_t key, st_data_t value
|
|
715
709
|
return ST_CONTINUE;
|
716
710
|
}
|
717
711
|
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
// [in] The heap recorder where the update is happening.
|
725
|
-
heap_recorder *heap_recorder;
|
726
|
-
} object_record_update_data;
|
727
|
-
|
728
|
-
static int update_object_record_entry(DDTRACE_UNUSED st_data_t *key, st_data_t *value, st_data_t data, int existing) {
|
729
|
-
object_record_update_data *update_data = (object_record_update_data*) data;
|
730
|
-
recording recording = update_data->recording;
|
731
|
-
object_record *new_object_record = recording.object_record;
|
732
|
-
if (existing) {
|
733
|
-
object_record *existing_record = (object_record*) (*value);
|
734
|
-
|
735
|
-
// This is not supposed to happen, raising...
|
736
|
-
VALUE existing_inspect = object_record_inspect(existing_record);
|
737
|
-
VALUE new_inspect = object_record_inspect(new_object_record);
|
738
|
-
rb_raise(rb_eRuntimeError, "Object ids are supposed to be unique. We got 2 allocation recordings with "
|
739
|
-
"the same id. previous=%"PRIsVALUE" new=%"PRIsVALUE, existing_inspect, new_inspect);
|
712
|
+
static int update_object_record_entry(DDTRACE_UNUSED st_data_t *key, st_data_t *value, st_data_t new_object_record, int existing) {
|
713
|
+
if (!existing) {
|
714
|
+
(*value) = (st_data_t) new_object_record; // Expected to be a `object_record *`
|
715
|
+
} else {
|
716
|
+
// If key already existed, we don't touch the existing value, so it can be used for diagnostics
|
740
717
|
}
|
741
|
-
// Always carry on with the update, we want the new record to be there at the end
|
742
|
-
(*value) = (st_data_t) new_object_record;
|
743
718
|
return ST_CONTINUE;
|
744
719
|
}
|
745
720
|
|
746
|
-
static void commit_recording(heap_recorder *heap_recorder, heap_record *heap_record,
|
721
|
+
static void commit_recording(heap_recorder *heap_recorder, heap_record *heap_record, object_record *active_recording) {
|
747
722
|
// Link the object record with the corresponding heap record. This was the last remaining thing we
|
748
723
|
// needed to fully build the object_record.
|
749
|
-
|
724
|
+
active_recording->heap_record = heap_record;
|
750
725
|
if (heap_record->num_tracked_objects == UINT32_MAX) {
|
751
726
|
rb_raise(rb_eRuntimeError, "Reached maximum number of tracked objects for heap record");
|
752
727
|
}
|
753
728
|
heap_record->num_tracked_objects++;
|
754
729
|
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
730
|
+
int existing_error = st_update(heap_recorder->object_records, active_recording->obj_id, update_object_record_entry, (st_data_t) active_recording);
|
731
|
+
if (existing_error) {
|
732
|
+
object_record *existing_record = NULL;
|
733
|
+
st_lookup(heap_recorder->object_records, active_recording->obj_id, (st_data_t *) &existing_record);
|
734
|
+
if (existing_record == NULL) rb_raise(rb_eRuntimeError, "Unexpected NULL when reading existing record");
|
735
|
+
|
736
|
+
VALUE existing_inspect = object_record_inspect(existing_record);
|
737
|
+
VALUE new_inspect = object_record_inspect(active_recording);
|
738
|
+
rb_raise(rb_eRuntimeError, "Object ids are supposed to be unique. We got 2 allocation recordings with "
|
739
|
+
"the same id. previous={%"PRIsVALUE"} new={%"PRIsVALUE"}", existing_inspect, new_inspect);
|
740
|
+
}
|
761
741
|
}
|
762
742
|
|
763
743
|
// Struct holding data required for an update operation on heap_records
|
@@ -867,7 +847,6 @@ void heap_record_free(heap_record *record) {
|
|
867
847
|
ruby_xfree(record);
|
868
848
|
}
|
869
849
|
|
870
|
-
|
871
850
|
// =================
|
872
851
|
// Object Record API
|
873
852
|
// =================
|
@@ -917,25 +896,6 @@ VALUE object_record_inspect(object_record *record) {
|
|
917
896
|
// ==============
|
918
897
|
// Heap Frame API
|
919
898
|
// ==============
|
920
|
-
int heap_frame_cmp(heap_frame *f1, heap_frame *f2) {
|
921
|
-
int line_diff = (int) (f1->line - f2->line);
|
922
|
-
if (line_diff != 0) {
|
923
|
-
return line_diff;
|
924
|
-
}
|
925
|
-
int cmp = strcmp(f1->name, f2->name);
|
926
|
-
if (cmp != 0) {
|
927
|
-
return cmp;
|
928
|
-
}
|
929
|
-
return strcmp(f1->filename, f2->filename);
|
930
|
-
}
|
931
|
-
|
932
|
-
// TODO: Research potential performance improvements around hashing stuff here
|
933
|
-
// once we have a benchmarking suite.
|
934
|
-
// Example: Each call to st_hash is calling murmur_finish and we may want
|
935
|
-
// to only finish once per structure, not per field?
|
936
|
-
// Example: There may be a more efficient hashing for line that is not the
|
937
|
-
// generic st_hash algorithm?
|
938
|
-
|
939
899
|
// WARN: Must be kept in-sync with ::char_slice_hash
|
940
900
|
st_index_t string_hash(char *str, st_index_t seed) {
|
941
901
|
return st_hash(str, strlen(str), seed);
|
@@ -971,9 +931,7 @@ st_index_t ddog_location_hash(ddog_prof_Location location, st_index_t seed) {
|
|
971
931
|
heap_stack* heap_stack_new(ddog_prof_Slice_Location locations) {
|
972
932
|
uint16_t frames_len = locations.len;
|
973
933
|
if (frames_len > MAX_FRAMES_LIMIT) {
|
974
|
-
// This
|
975
|
-
// the stacktrace construction mechanism. If it happens, lets just raise. This should
|
976
|
-
// be safe since only allocate with the GVL anyway.
|
934
|
+
// This is not expected as MAX_FRAMES_LIMIT is shared with the stacktrace construction mechanism
|
977
935
|
rb_raise(rb_eRuntimeError, "Found stack with more than %d frames (%d)", MAX_FRAMES_LIMIT, frames_len);
|
978
936
|
}
|
979
937
|
heap_stack *stack = ruby_xcalloc(1, sizeof(heap_stack) + frames_len * sizeof(heap_frame));
|
@@ -105,7 +105,7 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder);
|
|
105
105
|
// The sampling weight of this object.
|
106
106
|
//
|
107
107
|
// WARN: It needs to be paired with a ::end_heap_allocation_recording call.
|
108
|
-
void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice
|
108
|
+
void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice alloc_class);
|
109
109
|
|
110
110
|
// End a previously started heap allocation recording on the heap recorder.
|
111
111
|
//
|