ddtrace 1.12.1 → 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 +613 -9
- 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}/NativeExtensionDesign.md +3 -5
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id.h +0 -3
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id_from_pthread.c +3 -22
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id_noop.c +0 -1
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_cpu_and_wall_time_worker.c +338 -108
- 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_idle_sampling_helper.c +3 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_stack.c +111 -118
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_stack.h +11 -4
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_thread_context.c +545 -144
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_thread_context.h +3 -2
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/extconf.rb +68 -17
- 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 +60 -32
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +62 -0
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +42 -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 +155 -32
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/private_vm_api_access.h +16 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/profiling.c +19 -3
- 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/datadog_profiling_native_extension/stack_recorder.c +1040 -0
- data/ext/datadog_profiling_native_extension/stack_recorder.h +27 -0
- data/ext/datadog_profiling_native_extension/time_helpers.c +53 -0
- data/ext/datadog_profiling_native_extension/time_helpers.h +26 -0
- data/lib/datadog/appsec/assets/waf_rules/processors.json +92 -0
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +698 -75
- data/lib/datadog/appsec/assets/waf_rules/scanners.json +114 -0
- data/lib/datadog/appsec/assets/waf_rules/strict.json +98 -8
- data/lib/datadog/appsec/assets.rb +8 -0
- data/lib/datadog/appsec/component.rb +21 -2
- data/lib/datadog/appsec/configuration/settings.rb +167 -189
- data/lib/datadog/appsec/configuration.rb +0 -79
- data/lib/datadog/appsec/contrib/auto_instrument.rb +2 -4
- data/lib/datadog/appsec/contrib/devise/event.rb +57 -0
- data/lib/datadog/appsec/contrib/devise/ext.rb +13 -0
- data/lib/datadog/appsec/contrib/devise/integration.rb +42 -0
- data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +76 -0
- data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +54 -0
- data/lib/datadog/appsec/contrib/devise/patcher.rb +45 -0
- data/lib/datadog/appsec/contrib/devise/resource.rb +35 -0
- data/lib/datadog/appsec/contrib/devise/tracking.rb +57 -0
- data/lib/datadog/appsec/contrib/rack/ext.rb +2 -1
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +6 -2
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +8 -6
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +3 -8
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +3 -6
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +3 -6
- data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +3 -2
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +77 -27
- data/lib/datadog/appsec/contrib/rails/ext.rb +3 -2
- data/lib/datadog/appsec/contrib/rails/framework.rb +1 -3
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +3 -2
- data/lib/datadog/appsec/contrib/rails/patcher.rb +17 -11
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +3 -6
- data/lib/datadog/appsec/contrib/sinatra/ext.rb +2 -1
- data/lib/datadog/appsec/contrib/sinatra/framework.rb +1 -3
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +6 -4
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +13 -7
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +3 -6
- data/lib/datadog/appsec/event.rb +106 -50
- data/lib/datadog/appsec/extensions.rb +1 -130
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +3 -3
- data/lib/datadog/appsec/monitor/reactive/set_user.rb +3 -6
- data/lib/datadog/appsec/processor/actions.rb +49 -0
- data/lib/datadog/appsec/processor/rule_loader.rb +60 -0
- data/lib/datadog/appsec/processor/rule_merger.rb +22 -2
- data/lib/datadog/appsec/processor.rb +35 -7
- data/lib/datadog/appsec/rate_limiter.rb +1 -1
- data/lib/datadog/appsec/remote.rb +17 -11
- data/lib/datadog/appsec/response.rb +82 -4
- data/lib/datadog/appsec/sample_rate.rb +21 -0
- data/lib/datadog/appsec.rb +3 -4
- data/lib/datadog/auto_instrument.rb +3 -0
- data/lib/datadog/core/backport.rb +51 -0
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +38 -29
- data/lib/datadog/core/configuration/base.rb +6 -16
- data/lib/datadog/core/configuration/components.rb +20 -7
- data/lib/datadog/core/configuration/ext.rb +28 -5
- data/lib/datadog/core/configuration/option.rb +271 -21
- data/lib/datadog/core/configuration/option_definition.rb +73 -32
- data/lib/datadog/core/configuration/options.rb +27 -15
- data/lib/datadog/core/configuration/settings.rb +398 -119
- data/lib/datadog/core/configuration.rb +24 -4
- data/lib/datadog/core/diagnostics/environment_logger.rb +132 -235
- data/lib/datadog/core/environment/class_count.rb +6 -6
- data/lib/datadog/core/environment/execution.rb +103 -0
- data/lib/datadog/core/environment/ext.rb +13 -11
- 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/environment/variable_helpers.rb +0 -69
- data/lib/datadog/core/environment/yjit.rb +58 -0
- data/lib/datadog/core/error.rb +1 -0
- data/lib/datadog/core/git/ext.rb +6 -23
- data/lib/datadog/core/logging/ext.rb +3 -1
- data/lib/datadog/core/metrics/ext.rb +7 -5
- data/lib/datadog/core/remote/client/capabilities.rb +7 -2
- data/lib/datadog/core/remote/client.rb +3 -0
- data/lib/datadog/core/remote/component.rb +52 -48
- data/lib/datadog/core/remote/configuration/content.rb +28 -1
- data/lib/datadog/core/remote/configuration/repository.rb +3 -1
- data/lib/datadog/core/remote/ext.rb +2 -1
- data/lib/datadog/core/remote/negotiation.rb +20 -7
- 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/config.rb +60 -0
- data/lib/datadog/core/remote/transport/http/api/instance.rb +39 -0
- data/lib/datadog/core/remote/transport/http/api/spec.rb +21 -0
- data/lib/datadog/core/remote/transport/http/api.rb +58 -0
- data/lib/datadog/core/remote/transport/http/builder.rb +219 -0
- data/lib/datadog/core/remote/transport/http/client.rb +48 -0
- data/lib/datadog/core/remote/transport/http/config.rb +280 -0
- data/lib/datadog/core/remote/transport/http/negotiation.rb +146 -0
- data/lib/datadog/core/remote/transport/http.rb +179 -0
- data/lib/datadog/core/{transport → remote/transport}/negotiation.rb +25 -23
- data/lib/datadog/core/remote/worker.rb +11 -5
- data/lib/datadog/core/runtime/ext.rb +22 -12
- data/lib/datadog/core/runtime/metrics.rb +43 -0
- data/lib/datadog/core/telemetry/client.rb +28 -10
- data/lib/datadog/core/telemetry/emitter.rb +9 -11
- data/lib/datadog/core/telemetry/event.rb +250 -44
- data/lib/datadog/core/telemetry/ext.rb +8 -1
- data/lib/datadog/core/telemetry/heartbeat.rb +3 -7
- data/lib/datadog/core/telemetry/http/ext.rb +13 -8
- data/lib/datadog/core/telemetry/http/response.rb +4 -0
- data/lib/datadog/core/telemetry/http/transport.rb +10 -3
- data/lib/datadog/core/telemetry/request.rb +59 -0
- data/lib/datadog/core/transport/ext.rb +49 -0
- data/lib/datadog/core/transport/http/adapters/net.rb +168 -0
- data/lib/datadog/core/transport/http/adapters/registry.rb +29 -0
- data/lib/datadog/core/transport/http/adapters/test.rb +89 -0
- data/lib/datadog/core/transport/http/adapters/unix_socket.rb +83 -0
- data/lib/datadog/core/transport/http/api/endpoint.rb +31 -0
- data/lib/datadog/core/transport/http/api/fallbacks.rb +26 -0
- data/lib/datadog/core/transport/http/api/map.rb +18 -0
- data/lib/datadog/core/transport/http/env.rb +62 -0
- data/lib/datadog/core/transport/http/response.rb +60 -0
- data/lib/datadog/core/transport/parcel.rb +22 -0
- data/lib/datadog/core/transport/request.rb +17 -0
- data/lib/datadog/core/transport/response.rb +64 -0
- data/lib/datadog/core/utils/duration.rb +52 -0
- data/lib/datadog/core/utils/hash.rb +47 -0
- data/lib/datadog/core/utils/network.rb +1 -1
- data/lib/datadog/core/utils/safe_dup.rb +27 -20
- data/lib/datadog/core/utils/url.rb +25 -0
- data/lib/datadog/core/utils.rb +1 -1
- data/lib/datadog/core/workers/async.rb +3 -2
- data/lib/datadog/core/workers/polling.rb +2 -2
- data/lib/datadog/kit/appsec/events.rb +139 -89
- data/lib/datadog/kit/enable_core_dumps.rb +5 -6
- data/lib/datadog/kit/identity.rb +80 -65
- data/lib/datadog/opentelemetry/api/context.rb +10 -3
- data/lib/datadog/opentelemetry/sdk/propagator.rb +5 -3
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +48 -5
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +167 -0
- data/lib/datadog/opentelemetry/trace.rb +58 -0
- data/lib/datadog/opentelemetry.rb +4 -0
- data/lib/datadog/opentracer/text_map_propagator.rb +2 -1
- data/lib/datadog/opentracer.rb +9 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +10 -4
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +43 -20
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +3 -1
- data/lib/datadog/profiling/collectors/info.rb +101 -0
- data/lib/datadog/profiling/collectors/thread_context.rb +17 -2
- data/lib/datadog/profiling/component.rb +248 -97
- data/lib/datadog/profiling/exporter.rb +26 -5
- data/lib/datadog/profiling/ext.rb +2 -12
- data/lib/datadog/profiling/flush.rb +10 -5
- data/lib/datadog/profiling/http_transport.rb +23 -6
- data/lib/datadog/profiling/load_native_extension.rb +25 -6
- data/lib/datadog/profiling/native_extension.rb +1 -22
- data/lib/datadog/profiling/profiler.rb +36 -13
- data/lib/datadog/profiling/scheduler.rb +20 -15
- 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 +28 -79
- data/lib/datadog/tracing/component.rb +70 -11
- data/lib/datadog/tracing/configuration/agent_settings_resolver.rb +13 -0
- data/lib/datadog/tracing/configuration/dynamic/option.rb +71 -0
- data/lib/datadog/tracing/configuration/dynamic.rb +64 -0
- data/lib/datadog/tracing/configuration/ext.rb +40 -33
- data/lib/datadog/tracing/configuration/http.rb +74 -0
- data/lib/datadog/tracing/configuration/settings.rb +136 -99
- data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +10 -6
- data/lib/datadog/tracing/contrib/action_cable/ext.rb +21 -18
- data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +10 -6
- data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +1 -1
- data/lib/datadog/tracing/contrib/action_mailer/ext.rb +21 -18
- data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +10 -7
- data/lib/datadog/tracing/contrib/action_pack/ext.rb +11 -8
- data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +10 -6
- data/lib/datadog/tracing/contrib/action_view/ext.rb +13 -10
- data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +14 -7
- data/lib/datadog/tracing/contrib/active_job/ext.rb +26 -23
- data/lib/datadog/tracing/contrib/active_job/log_injection.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +10 -6
- data/lib/datadog/tracing/contrib/active_model_serializers/ext.rb +13 -10
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +29 -15
- data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +10 -7
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +2 -6
- data/lib/datadog/tracing/contrib/active_record/ext.rb +18 -15
- data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +106 -202
- data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +3 -0
- data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +10 -7
- data/lib/datadog/tracing/contrib/active_support/ext.rb +19 -16
- data/lib/datadog/tracing/contrib/analytics.rb +0 -1
- data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +15 -7
- data/lib/datadog/tracing/contrib/aws/ext.rb +38 -24
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +16 -5
- data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +20 -0
- data/lib/datadog/tracing/contrib/concurrent_ruby/configuration/settings.rb +3 -2
- data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +14 -14
- data/lib/datadog/tracing/contrib/concurrent_ruby/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +3 -10
- data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +2 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +19 -2
- data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +22 -0
- data/lib/datadog/tracing/contrib/configurable.rb +1 -1
- data/lib/datadog/tracing/contrib/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +21 -7
- data/lib/datadog/tracing/contrib/dalli/ext.rb +27 -11
- data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +17 -8
- data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +14 -7
- data/lib/datadog/tracing/contrib/delayed_job/ext.rb +17 -14
- data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +15 -7
- data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +22 -15
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +104 -99
- data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +17 -9
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +48 -3
- data/lib/datadog/tracing/contrib/ethon/ext.rb +20 -11
- data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +6 -3
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +20 -10
- data/lib/datadog/tracing/contrib/excon/ext.rb +17 -8
- data/lib/datadog/tracing/contrib/excon/middleware.rb +25 -5
- data/lib/datadog/tracing/contrib/ext.rb +26 -1
- data/lib/datadog/tracing/contrib/extensions.rb +38 -2
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +27 -10
- data/lib/datadog/tracing/contrib/faraday/ext.rb +17 -8
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +22 -6
- data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +9 -6
- data/lib/datadog/tracing/contrib/grape/ext.rb +17 -14
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +9 -6
- data/lib/datadog/tracing/contrib/graphql/ext.rb +8 -5
- data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +40 -9
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +39 -20
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +37 -18
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +0 -4
- data/lib/datadog/tracing/contrib/grpc/ext.rb +17 -13
- data/lib/datadog/tracing/contrib/grpc/formatting.rb +127 -0
- data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +3 -2
- data/lib/datadog/tracing/contrib/hanami/ext.rb +10 -8
- data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +5 -8
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +34 -11
- data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +2 -2
- data/lib/datadog/tracing/contrib/http/ext.rb +17 -9
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +27 -7
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +34 -11
- data/lib/datadog/tracing/contrib/httpclient/ext.rb +18 -9
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +22 -5
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +34 -11
- data/lib/datadog/tracing/contrib/httprb/ext.rb +17 -9
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +22 -5
- data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +10 -6
- data/lib/datadog/tracing/contrib/kafka/ext.rb +43 -39
- data/lib/datadog/tracing/contrib/lograge/configuration/settings.rb +3 -2
- data/lib/datadog/tracing/contrib/lograge/ext.rb +3 -1
- data/lib/datadog/tracing/contrib/lograge/instrumentation.rb +2 -17
- data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +15 -7
- data/lib/datadog/tracing/contrib/mongodb/ext.rb +21 -16
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +16 -5
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +22 -14
- data/lib/datadog/tracing/contrib/mysql2/ext.rb +16 -10
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +22 -7
- data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +53 -0
- data/lib/datadog/tracing/contrib/opensearch/ext.rb +38 -0
- data/lib/datadog/tracing/contrib/opensearch/integration.rb +44 -0
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +135 -0
- data/lib/datadog/tracing/contrib/opensearch/quantize.rb +81 -0
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +23 -14
- data/lib/datadog/tracing/contrib/pg/ext.rb +23 -19
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +49 -9
- data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +15 -7
- data/lib/datadog/tracing/contrib/presto/ext.rb +26 -20
- data/lib/datadog/tracing/contrib/presto/instrumentation.rb +14 -5
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +12 -10
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +1 -1
- data/lib/datadog/tracing/contrib/qless/configuration/settings.rb +13 -8
- data/lib/datadog/tracing/contrib/qless/ext.rb +15 -12
- data/lib/datadog/tracing/contrib/que/configuration/settings.rb +22 -12
- data/lib/datadog/tracing/contrib/que/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +10 -7
- data/lib/datadog/tracing/contrib/racecar/event.rb +5 -5
- data/lib/datadog/tracing/contrib/racecar/ext.rb +21 -18
- data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +17 -12
- data/lib/datadog/tracing/contrib/rack/ext.rb +19 -16
- data/lib/datadog/tracing/contrib/rack/header_collection.rb +3 -0
- data/lib/datadog/tracing/contrib/rack/header_tagging.rb +63 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +16 -50
- data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +0 -2
- data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +20 -15
- data/lib/datadog/tracing/contrib/rails/ext.rb +8 -5
- data/lib/datadog/tracing/contrib/rails/log_injection.rb +7 -10
- data/lib/datadog/tracing/contrib/rails/patcher.rb +10 -41
- data/lib/datadog/tracing/contrib/rails/railtie.rb +3 -3
- data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +14 -10
- data/lib/datadog/tracing/contrib/rake/ext.rb +15 -12
- data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +18 -9
- data/lib/datadog/tracing/contrib/redis/ext.rb +23 -15
- data/lib/datadog/tracing/contrib/redis/instrumentation.rb +5 -40
- data/lib/datadog/tracing/contrib/redis/patcher.rb +34 -21
- data/lib/datadog/tracing/contrib/redis/tags.rb +16 -7
- data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +46 -33
- data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +14 -7
- data/lib/datadog/tracing/contrib/resque/ext.rb +10 -7
- data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +17 -9
- data/lib/datadog/tracing/contrib/rest_client/ext.rb +16 -8
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +25 -5
- data/lib/datadog/tracing/contrib/roda/configuration/settings.rb +10 -6
- data/lib/datadog/tracing/contrib/roda/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/semantic_logger/configuration/settings.rb +3 -2
- data/lib/datadog/tracing/contrib/semantic_logger/ext.rb +3 -1
- data/lib/datadog/tracing/contrib/semantic_logger/instrumentation.rb +4 -20
- data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +10 -6
- data/lib/datadog/tracing/contrib/sequel/ext.rb +11 -8
- data/lib/datadog/tracing/contrib/sequel/utils.rb +7 -7
- data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +15 -8
- data/lib/datadog/tracing/contrib/shoryuken/ext.rb +15 -12
- data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +19 -11
- data/lib/datadog/tracing/contrib/sidekiq/ext.rb +33 -30
- data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +12 -9
- data/lib/datadog/tracing/contrib/sinatra/env.rb +0 -17
- data/lib/datadog/tracing/contrib/sinatra/ext.rb +22 -19
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -14
- data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +15 -8
- data/lib/datadog/tracing/contrib/sneakers/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/sneakers/tracer.rb +1 -1
- data/lib/datadog/tracing/contrib/span_attribute_schema.rb +74 -10
- data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +10 -6
- data/lib/datadog/tracing/contrib/stripe/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +10 -6
- data/lib/datadog/tracing/contrib/sucker_punch/ext.rb +16 -13
- 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/{ci/contrib/cucumber → tracing/contrib/trilogy}/patcher.rb +10 -6
- data/lib/datadog/tracing/contrib/utils/database.rb +5 -3
- data/lib/datadog/tracing/contrib/utils/quantization/http.rb +11 -11
- data/lib/datadog/tracing/contrib.rb +2 -0
- data/lib/datadog/tracing/correlation.rb +29 -12
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +165 -0
- data/lib/datadog/tracing/diagnostics/ext.rb +21 -19
- data/lib/datadog/tracing/distributed/b3_multi.rb +2 -2
- data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
- data/lib/datadog/tracing/distributed/datadog.rb +0 -1
- data/lib/datadog/tracing/distributed/propagation.rb +35 -34
- data/lib/datadog/tracing/distributed/trace_context.rb +52 -17
- data/lib/datadog/tracing/metadata/ext.rb +9 -6
- data/lib/datadog/tracing/metadata/tagging.rb +3 -3
- data/lib/datadog/tracing/remote.rb +78 -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 +31 -0
- data/lib/datadog/tracing/span_operation.rb +3 -15
- data/lib/datadog/tracing/sync_writer.rb +3 -3
- data/lib/datadog/tracing/trace_digest.rb +31 -0
- data/lib/datadog/tracing/trace_operation.rb +17 -5
- data/lib/datadog/tracing/trace_segment.rb +5 -2
- data/lib/datadog/tracing/tracer.rb +12 -1
- data/lib/datadog/{core → tracing}/transport/http/api/instance.rb +1 -1
- data/lib/datadog/{core → tracing}/transport/http/api/spec.rb +1 -1
- data/lib/datadog/tracing/transport/http/api.rb +43 -0
- data/lib/datadog/{core → tracing}/transport/http/builder.rb +13 -68
- data/lib/datadog/tracing/transport/http/client.rb +57 -0
- data/lib/datadog/tracing/transport/http/statistics.rb +47 -0
- data/lib/datadog/tracing/transport/http/traces.rb +152 -0
- data/lib/datadog/tracing/transport/http.rb +125 -0
- data/lib/datadog/tracing/transport/io/client.rb +89 -0
- data/lib/datadog/tracing/transport/io/response.rb +27 -0
- data/lib/datadog/tracing/transport/io/traces.rb +101 -0
- data/lib/datadog/tracing/transport/io.rb +30 -0
- data/lib/datadog/tracing/transport/serializable_trace.rb +126 -0
- data/lib/datadog/tracing/transport/statistics.rb +77 -0
- data/lib/datadog/tracing/transport/trace_formatter.rb +240 -0
- data/lib/datadog/tracing/transport/traces.rb +224 -0
- data/lib/datadog/tracing/workers/trace_writer.rb +6 -4
- data/lib/datadog/tracing/workers.rb +4 -2
- data/lib/datadog/tracing/writer.rb +5 -2
- data/lib/datadog/tracing.rb +8 -2
- data/lib/ddtrace/transport/ext.rb +22 -14
- data/lib/ddtrace/version.rb +9 -12
- data/lib/ddtrace.rb +1 -1
- metadata +157 -139
- data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +0 -25
- data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +0 -110
- data/ext/ddtrace_profiling_native_extension/stack_recorder.c +0 -591
- data/ext/ddtrace_profiling_native_extension/stack_recorder.h +0 -14
- data/ext/ddtrace_profiling_native_extension/time_helpers.c +0 -17
- data/ext/ddtrace_profiling_native_extension/time_helpers.h +0 -10
- data/lib/datadog/ci/configuration/components.rb +0 -32
- data/lib/datadog/ci/configuration/settings.rb +0 -53
- data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +0 -33
- data/lib/datadog/ci/contrib/cucumber/ext.rb +0 -20
- data/lib/datadog/ci/contrib/cucumber/formatter.rb +0 -94
- data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +0 -28
- data/lib/datadog/ci/contrib/cucumber/integration.rb +0 -47
- data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +0 -33
- data/lib/datadog/ci/contrib/rspec/example.rb +0 -68
- data/lib/datadog/ci/contrib/rspec/ext.rb +0 -19
- data/lib/datadog/ci/contrib/rspec/integration.rb +0 -48
- data/lib/datadog/ci/contrib/rspec/patcher.rb +0 -27
- data/lib/datadog/ci/ext/app_types.rb +0 -9
- data/lib/datadog/ci/ext/environment.rb +0 -575
- data/lib/datadog/ci/ext/settings.rb +0 -10
- data/lib/datadog/ci/ext/test.rb +0 -35
- data/lib/datadog/ci/extensions.rb +0 -19
- data/lib/datadog/ci/flush.rb +0 -38
- data/lib/datadog/ci/test.rb +0 -81
- data/lib/datadog/ci.rb +0 -20
- data/lib/datadog/core/configuration/dependency_resolver.rb +0 -28
- data/lib/datadog/core/configuration/option_definition_set.rb +0 -22
- data/lib/datadog/core/configuration/option_set.rb +0 -10
- data/lib/datadog/core/telemetry/collector.rb +0 -231
- 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/transport/config.rb +0 -58
- data/lib/datadog/core/transport/http/api.rb +0 -57
- data/lib/datadog/core/transport/http/client.rb +0 -45
- data/lib/datadog/core/transport/http/config.rb +0 -268
- data/lib/datadog/core/transport/http/negotiation.rb +0 -144
- data/lib/datadog/core/transport/http.rb +0 -169
- data/lib/datadog/core/utils/object_set.rb +0 -43
- data/lib/datadog/core/utils/string_table.rb +0 -47
- data/lib/datadog/profiling/backtrace_location.rb +0 -34
- data/lib/datadog/profiling/buffer.rb +0 -43
- data/lib/datadog/profiling/collectors/old_stack.rb +0 -301
- data/lib/datadog/profiling/encoding/profile.rb +0 -41
- data/lib/datadog/profiling/event.rb +0 -15
- data/lib/datadog/profiling/events/stack.rb +0 -82
- data/lib/datadog/profiling/old_recorder.rb +0 -107
- data/lib/datadog/profiling/pprof/builder.rb +0 -125
- data/lib/datadog/profiling/pprof/converter.rb +0 -102
- data/lib/datadog/profiling/pprof/message_set.rb +0 -16
- data/lib/datadog/profiling/pprof/payload.rb +0 -20
- data/lib/datadog/profiling/pprof/pprof.proto +0 -212
- data/lib/datadog/profiling/pprof/pprof_pb.rb +0 -81
- data/lib/datadog/profiling/pprof/stack_sample.rb +0 -139
- data/lib/datadog/profiling/pprof/string_table.rb +0 -12
- data/lib/datadog/profiling/pprof/template.rb +0 -118
- data/lib/datadog/profiling/trace_identifiers/ddtrace.rb +0 -43
- data/lib/datadog/profiling/trace_identifiers/helper.rb +0 -45
- data/lib/datadog/tracing/contrib/sinatra/headers.rb +0 -35
- data/lib/ddtrace/transport/http/adapters/net.rb +0 -168
- data/lib/ddtrace/transport/http/adapters/registry.rb +0 -27
- data/lib/ddtrace/transport/http/adapters/test.rb +0 -85
- data/lib/ddtrace/transport/http/adapters/unix_socket.rb +0 -77
- data/lib/ddtrace/transport/http/api/endpoint.rb +0 -29
- data/lib/ddtrace/transport/http/api/fallbacks.rb +0 -24
- data/lib/ddtrace/transport/http/api/instance.rb +0 -35
- data/lib/ddtrace/transport/http/api/map.rb +0 -16
- data/lib/ddtrace/transport/http/api/spec.rb +0 -17
- data/lib/ddtrace/transport/http/api.rb +0 -39
- data/lib/ddtrace/transport/http/builder.rb +0 -176
- data/lib/ddtrace/transport/http/client.rb +0 -52
- data/lib/ddtrace/transport/http/env.rb +0 -58
- data/lib/ddtrace/transport/http/response.rb +0 -58
- data/lib/ddtrace/transport/http/statistics.rb +0 -43
- data/lib/ddtrace/transport/http/traces.rb +0 -144
- data/lib/ddtrace/transport/http.rb +0 -117
- data/lib/ddtrace/transport/io/client.rb +0 -85
- data/lib/ddtrace/transport/io/response.rb +0 -25
- data/lib/ddtrace/transport/io/traces.rb +0 -99
- data/lib/ddtrace/transport/io.rb +0 -28
- data/lib/ddtrace/transport/parcel.rb +0 -20
- data/lib/ddtrace/transport/request.rb +0 -15
- data/lib/ddtrace/transport/response.rb +0 -60
- data/lib/ddtrace/transport/serializable_trace.rb +0 -122
- data/lib/ddtrace/transport/statistics.rb +0 -75
- data/lib/ddtrace/transport/trace_formatter.rb +0 -198
- data/lib/ddtrace/transport/traces.rb +0 -216
- /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
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include "collectors_thread_context.h"
|
|
4
4
|
#include "clock_id.h"
|
|
5
5
|
#include "collectors_stack.h"
|
|
6
|
+
#include "collectors_gc_profiling_helper.h"
|
|
6
7
|
#include "helpers.h"
|
|
7
8
|
#include "libdatadog_helpers.h"
|
|
8
9
|
#include "private_vm_api_access.h"
|
|
@@ -37,24 +38,29 @@
|
|
|
37
38
|
// When `thread_context_collector_on_gc_start` gets called, the current cpu and wall-time get recorded to the thread
|
|
38
39
|
// context: `cpu_time_at_gc_start_ns` and `wall_time_at_gc_start_ns`.
|
|
39
40
|
//
|
|
40
|
-
// While
|
|
41
|
-
//
|
|
41
|
+
// While `cpu_time_at_gc_start_ns` is set, regular samples (if any) do not account for cpu-time any time that passes
|
|
42
|
+
// after this timestamp. The idea is that this cpu-time will be blamed separately on GC, and not on the user thread.
|
|
43
|
+
// Wall-time accounting is not affected by this (e.g. we still record 60 seconds every 60 seconds).
|
|
42
44
|
//
|
|
43
|
-
// (Regular samples can still account for the time between the previous sample and the start of GC.)
|
|
45
|
+
// (Regular samples can still account for the cpu-time between the previous sample and the start of GC.)
|
|
44
46
|
//
|
|
45
|
-
// When `thread_context_collector_on_gc_finish` gets called, the
|
|
46
|
-
//
|
|
47
|
+
// When `thread_context_collector_on_gc_finish` gets called, the cpu-time and wall-time spent during GC gets recorded
|
|
48
|
+
// into the global gc_tracking structure, and further samples are not affected. (The `cpu_time_at_previous_sample_ns`
|
|
49
|
+
// of the thread that did GC also gets adjusted to avoid double-accounting.)
|
|
47
50
|
//
|
|
48
|
-
// Finally, when `thread_context_collector_sample_after_gc` gets called,
|
|
51
|
+
// Finally, when `thread_context_collector_sample_after_gc` gets called, a sample gets recorded with a stack having
|
|
52
|
+
// a single placeholder `Garbage Collection` frame. This sample gets
|
|
53
|
+
// assigned the cpu-time and wall-time that was recorded between calls to `on_gc_start` and `on_gc_finish`, as well
|
|
54
|
+
// as metadata for the last GC.
|
|
49
55
|
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
56
|
+
// Note that the Ruby GC does not usually do all of the GC work in one go. Instead, it breaks it up into smaller steps
|
|
57
|
+
// so that the application can keep doing user work in between GC steps.
|
|
58
|
+
// The `on_gc_start` / `on_gc_finish` will trigger each time the VM executes these smaller steps, and on a benchmark
|
|
59
|
+
// that executes `Object.new` in a loop, I measured more than 50k of this steps per second (!!).
|
|
60
|
+
// Creating these many events for every GC step is a lot of overhead, so instead `on_gc_finish` coalesces time
|
|
61
|
+
// spent in GC and only flushes it at most every 10 ms/every complete GC collection. This reduces the amount of
|
|
62
|
+
// individual GC events we need to record. We use the latest GC metadata for this event, reflecting the last GC that
|
|
63
|
+
// happened in the coalesced period.
|
|
58
64
|
//
|
|
59
65
|
// In an earlier attempt at implementing this functionality (https://github.com/DataDog/dd-trace-rb/pull/2308), we
|
|
60
66
|
// discovered that we needed to factor the sampling work away from `thread_context_collector_on_gc_finish` and into a
|
|
@@ -63,11 +69,12 @@
|
|
|
63
69
|
// allowed to happen during Ruby's garbage collection start/finish hooks.
|
|
64
70
|
// ---
|
|
65
71
|
|
|
66
|
-
#define INVALID_TIME -1
|
|
67
72
|
#define THREAD_ID_LIMIT_CHARS 44 // Why 44? "#{2**64} (#{2**64})".size + 1 for \0
|
|
73
|
+
#define THREAD_INVOKE_LOCATION_LIMIT_CHARS 512
|
|
68
74
|
#define IS_WALL_TIME true
|
|
69
75
|
#define IS_NOT_WALL_TIME false
|
|
70
76
|
#define MISSING_TRACER_CONTEXT_KEY 0
|
|
77
|
+
#define TIME_BETWEEN_GC_EVENTS_NS MILLIS_AS_NS(10)
|
|
71
78
|
|
|
72
79
|
static ID at_active_span_id; // id of :@active_span in Ruby
|
|
73
80
|
static ID at_active_trace_id; // id of :@active_trace in Ruby
|
|
@@ -75,6 +82,9 @@ static ID at_id_id; // id of :@id in Ruby
|
|
|
75
82
|
static ID at_resource_id; // id of :@resource in Ruby
|
|
76
83
|
static ID at_root_span_id; // id of :@root_span in Ruby
|
|
77
84
|
static ID at_type_id; // id of :@type in Ruby
|
|
85
|
+
static ID at_otel_values_id; // id of :@otel_values in Ruby
|
|
86
|
+
static ID at_parent_span_id_id; // id of :@parent_span_id in Ruby
|
|
87
|
+
static ID at_datadog_trace_id; // id of :@datadog_trace in Ruby
|
|
78
88
|
|
|
79
89
|
// Contains state for a single ThreadContext instance
|
|
80
90
|
struct thread_context_collector_state {
|
|
@@ -99,6 +109,16 @@ struct thread_context_collector_state {
|
|
|
99
109
|
VALUE thread_list_buffer;
|
|
100
110
|
// Used to omit endpoint names (retrieved from tracer) from collected data
|
|
101
111
|
bool endpoint_collection_enabled;
|
|
112
|
+
// Used to omit timestamps / timeline events from collected data
|
|
113
|
+
bool timeline_enabled;
|
|
114
|
+
// Used to omit class information from collected allocation data
|
|
115
|
+
bool allocation_type_enabled;
|
|
116
|
+
// Used when calling monotonic_to_system_epoch_ns
|
|
117
|
+
monotonic_to_system_epoch_state time_converter_state;
|
|
118
|
+
// Used to identify the main thread, to give it a fallback name
|
|
119
|
+
VALUE main_thread;
|
|
120
|
+
// Used when extracting trace identifiers from otel spans. Lazily initialized.
|
|
121
|
+
VALUE otel_current_span_key;
|
|
102
122
|
|
|
103
123
|
struct stats {
|
|
104
124
|
// Track how many garbage collection samples we've taken.
|
|
@@ -106,26 +126,31 @@ struct thread_context_collector_state {
|
|
|
106
126
|
// See thread_context_collector_on_gc_start for details
|
|
107
127
|
unsigned int gc_samples_missed_due_to_missing_context;
|
|
108
128
|
} stats;
|
|
129
|
+
|
|
130
|
+
struct {
|
|
131
|
+
unsigned long accumulated_cpu_time_ns;
|
|
132
|
+
unsigned long accumulated_wall_time_ns;
|
|
133
|
+
|
|
134
|
+
long wall_time_at_previous_gc_ns; // Will be INVALID_TIME unless there's accumulated time above
|
|
135
|
+
long wall_time_at_last_flushed_gc_event_ns; // Starts at 0 and then will always be valid
|
|
136
|
+
} gc_tracking;
|
|
109
137
|
};
|
|
110
138
|
|
|
111
139
|
// Tracks per-thread state
|
|
112
140
|
struct per_thread_context {
|
|
113
141
|
char thread_id[THREAD_ID_LIMIT_CHARS];
|
|
114
142
|
ddog_CharSlice thread_id_char_slice;
|
|
143
|
+
char thread_invoke_location[THREAD_INVOKE_LOCATION_LIMIT_CHARS];
|
|
144
|
+
ddog_CharSlice thread_invoke_location_char_slice;
|
|
115
145
|
thread_cpu_time_id thread_cpu_time_id;
|
|
116
146
|
long cpu_time_at_previous_sample_ns; // Can be INVALID_TIME until initialized or if getting it fails for another reason
|
|
117
147
|
long wall_time_at_previous_sample_ns; // Can be INVALID_TIME until initialized
|
|
118
148
|
|
|
119
149
|
struct {
|
|
120
|
-
// Both of these fields are set by on_gc_start and kept until
|
|
150
|
+
// Both of these fields are set by on_gc_start and kept until on_gc_finish is called.
|
|
121
151
|
// Outside of this window, they will be INVALID_TIME.
|
|
122
152
|
long cpu_time_at_start_ns;
|
|
123
153
|
long wall_time_at_start_ns;
|
|
124
|
-
|
|
125
|
-
// Both of these fields are set by on_gc_finish and kept until sample_after_gc is called.
|
|
126
|
-
// Outside of this window, they will be INVALID_TIME.
|
|
127
|
-
long cpu_time_at_finish_ns;
|
|
128
|
-
long wall_time_at_finish_ns;
|
|
129
154
|
} gc_tracking;
|
|
130
155
|
};
|
|
131
156
|
|
|
@@ -148,7 +173,9 @@ static VALUE _native_initialize(
|
|
|
148
173
|
VALUE recorder_instance,
|
|
149
174
|
VALUE max_frames,
|
|
150
175
|
VALUE tracer_context_key,
|
|
151
|
-
VALUE endpoint_collection_enabled
|
|
176
|
+
VALUE endpoint_collection_enabled,
|
|
177
|
+
VALUE timeline_enabled,
|
|
178
|
+
VALUE allocation_type_enabled
|
|
152
179
|
);
|
|
153
180
|
static VALUE _native_sample(VALUE self, VALUE collector_instance, VALUE profiler_overhead_stack_thread);
|
|
154
181
|
static VALUE _native_on_gc_start(VALUE self, VALUE collector_instance);
|
|
@@ -168,16 +195,19 @@ static void trigger_sample_for_thread(
|
|
|
168
195
|
VALUE stack_from_thread,
|
|
169
196
|
struct per_thread_context *thread_context,
|
|
170
197
|
sample_values values,
|
|
171
|
-
|
|
198
|
+
long current_monotonic_wall_time_ns,
|
|
199
|
+
ddog_CharSlice *ruby_vm_type,
|
|
200
|
+
ddog_CharSlice *class_name
|
|
172
201
|
);
|
|
173
202
|
static VALUE _native_thread_list(VALUE self);
|
|
174
203
|
static struct per_thread_context *get_or_create_context_for(VALUE thread, struct thread_context_collector_state *state);
|
|
175
204
|
static struct per_thread_context *get_context_for(VALUE thread, struct thread_context_collector_state *state);
|
|
176
|
-
static void initialize_context(VALUE thread, struct per_thread_context *thread_context);
|
|
205
|
+
static void initialize_context(VALUE thread, struct per_thread_context *thread_context, struct thread_context_collector_state *state);
|
|
177
206
|
static VALUE _native_inspect(VALUE self, VALUE collector_instance);
|
|
178
207
|
static VALUE per_thread_context_st_table_as_ruby_hash(struct thread_context_collector_state *state);
|
|
179
208
|
static int per_thread_context_as_ruby_hash(st_data_t key_thread, st_data_t value_context, st_data_t result_hash);
|
|
180
209
|
static VALUE stats_as_ruby_hash(struct thread_context_collector_state *state);
|
|
210
|
+
static VALUE gc_tracking_as_ruby_hash(struct thread_context_collector_state *state);
|
|
181
211
|
static void remove_context_for_dead_threads(struct thread_context_collector_state *state);
|
|
182
212
|
static int remove_if_dead_thread(st_data_t key_thread, st_data_t value_context, st_data_t _argument);
|
|
183
213
|
static VALUE _native_per_thread_context(VALUE self, VALUE collector_instance);
|
|
@@ -185,11 +215,22 @@ static long update_time_since_previous_sample(long *time_at_previous_sample_ns,
|
|
|
185
215
|
static long cpu_time_now_ns(struct per_thread_context *thread_context);
|
|
186
216
|
static long thread_id_for(VALUE thread);
|
|
187
217
|
static VALUE _native_stats(VALUE self, VALUE collector_instance);
|
|
218
|
+
static VALUE _native_gc_tracking(VALUE self, VALUE collector_instance);
|
|
188
219
|
static void trace_identifiers_for(struct thread_context_collector_state *state, VALUE thread, struct trace_identifiers *trace_identifiers_result);
|
|
189
|
-
static bool
|
|
220
|
+
static bool should_collect_resource(VALUE root_span);
|
|
190
221
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
|
191
222
|
static VALUE thread_list(struct thread_context_collector_state *state);
|
|
192
|
-
static VALUE _native_sample_allocation(VALUE self, VALUE collector_instance, VALUE sample_weight);
|
|
223
|
+
static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object);
|
|
224
|
+
static VALUE _native_new_empty_thread(VALUE self);
|
|
225
|
+
static ddog_CharSlice ruby_value_type_to_class_name(enum ruby_value_type type);
|
|
226
|
+
static void ddtrace_otel_trace_identifiers_for(
|
|
227
|
+
struct thread_context_collector_state *state,
|
|
228
|
+
VALUE *active_trace,
|
|
229
|
+
VALUE *root_span,
|
|
230
|
+
VALUE *numeric_span_id,
|
|
231
|
+
VALUE active_span,
|
|
232
|
+
VALUE otel_values
|
|
233
|
+
);
|
|
193
234
|
|
|
194
235
|
void collectors_thread_context_init(VALUE profiling_module) {
|
|
195
236
|
VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
|
|
@@ -207,17 +248,19 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
|
207
248
|
// https://bugs.ruby-lang.org/issues/18007 for a discussion around this.
|
|
208
249
|
rb_define_alloc_func(collectors_thread_context_class, _native_new);
|
|
209
250
|
|
|
210
|
-
rb_define_singleton_method(collectors_thread_context_class, "_native_initialize", _native_initialize,
|
|
251
|
+
rb_define_singleton_method(collectors_thread_context_class, "_native_initialize", _native_initialize, 7);
|
|
211
252
|
rb_define_singleton_method(collectors_thread_context_class, "_native_inspect", _native_inspect, 1);
|
|
212
253
|
rb_define_singleton_method(collectors_thread_context_class, "_native_reset_after_fork", _native_reset_after_fork, 1);
|
|
213
254
|
rb_define_singleton_method(testing_module, "_native_sample", _native_sample, 2);
|
|
214
|
-
rb_define_singleton_method(testing_module, "_native_sample_allocation", _native_sample_allocation,
|
|
255
|
+
rb_define_singleton_method(testing_module, "_native_sample_allocation", _native_sample_allocation, 3);
|
|
215
256
|
rb_define_singleton_method(testing_module, "_native_on_gc_start", _native_on_gc_start, 1);
|
|
216
257
|
rb_define_singleton_method(testing_module, "_native_on_gc_finish", _native_on_gc_finish, 1);
|
|
217
258
|
rb_define_singleton_method(testing_module, "_native_sample_after_gc", _native_sample_after_gc, 1);
|
|
218
259
|
rb_define_singleton_method(testing_module, "_native_thread_list", _native_thread_list, 0);
|
|
219
260
|
rb_define_singleton_method(testing_module, "_native_per_thread_context", _native_per_thread_context, 1);
|
|
220
261
|
rb_define_singleton_method(testing_module, "_native_stats", _native_stats, 1);
|
|
262
|
+
rb_define_singleton_method(testing_module, "_native_gc_tracking", _native_gc_tracking, 1);
|
|
263
|
+
rb_define_singleton_method(testing_module, "_native_new_empty_thread", _native_new_empty_thread, 0);
|
|
221
264
|
|
|
222
265
|
at_active_span_id = rb_intern_const("@active_span");
|
|
223
266
|
at_active_trace_id = rb_intern_const("@active_trace");
|
|
@@ -225,6 +268,11 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
|
225
268
|
at_resource_id = rb_intern_const("@resource");
|
|
226
269
|
at_root_span_id = rb_intern_const("@root_span");
|
|
227
270
|
at_type_id = rb_intern_const("@type");
|
|
271
|
+
at_otel_values_id = rb_intern_const("@otel_values");
|
|
272
|
+
at_parent_span_id_id = rb_intern_const("@parent_span_id");
|
|
273
|
+
at_datadog_trace_id = rb_intern_const("@datadog_trace");
|
|
274
|
+
|
|
275
|
+
gc_profiling_init();
|
|
228
276
|
}
|
|
229
277
|
|
|
230
278
|
// This structure is used to define a Ruby object that stores a pointer to a struct thread_context_collector_state
|
|
@@ -249,6 +297,8 @@ static void thread_context_collector_typed_data_mark(void *state_ptr) {
|
|
|
249
297
|
rb_gc_mark(state->recorder_instance);
|
|
250
298
|
st_foreach(state->hash_map_per_thread_context, hash_map_per_thread_context_mark, 0 /* unused */);
|
|
251
299
|
rb_gc_mark(state->thread_list_buffer);
|
|
300
|
+
rb_gc_mark(state->main_thread);
|
|
301
|
+
rb_gc_mark(state->otel_current_span_key);
|
|
252
302
|
}
|
|
253
303
|
|
|
254
304
|
static void thread_context_collector_typed_data_free(void *state_ptr) {
|
|
@@ -285,6 +335,9 @@ static int hash_map_per_thread_context_free_values(DDTRACE_UNUSED st_data_t _thr
|
|
|
285
335
|
static VALUE _native_new(VALUE klass) {
|
|
286
336
|
struct thread_context_collector_state *state = ruby_xcalloc(1, sizeof(struct thread_context_collector_state));
|
|
287
337
|
|
|
338
|
+
// Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
|
|
339
|
+
// being leaked.
|
|
340
|
+
|
|
288
341
|
// Update this when modifying state struct
|
|
289
342
|
state->sampling_buffer = NULL;
|
|
290
343
|
state->hash_map_per_thread_context =
|
|
@@ -294,6 +347,13 @@ static VALUE _native_new(VALUE klass) {
|
|
|
294
347
|
state->tracer_context_key = MISSING_TRACER_CONTEXT_KEY;
|
|
295
348
|
state->thread_list_buffer = rb_ary_new();
|
|
296
349
|
state->endpoint_collection_enabled = true;
|
|
350
|
+
state->timeline_enabled = true;
|
|
351
|
+
state->allocation_type_enabled = true;
|
|
352
|
+
state->time_converter_state = (monotonic_to_system_epoch_state) MONOTONIC_TO_SYSTEM_EPOCH_INITIALIZER;
|
|
353
|
+
state->main_thread = rb_thread_main();
|
|
354
|
+
state->otel_current_span_key = Qnil;
|
|
355
|
+
state->gc_tracking.wall_time_at_previous_gc_ns = INVALID_TIME;
|
|
356
|
+
state->gc_tracking.wall_time_at_last_flushed_gc_event_ns = 0;
|
|
297
357
|
|
|
298
358
|
return TypedData_Wrap_Struct(klass, &thread_context_collector_typed_data, state);
|
|
299
359
|
}
|
|
@@ -304,9 +364,13 @@ static VALUE _native_initialize(
|
|
|
304
364
|
VALUE recorder_instance,
|
|
305
365
|
VALUE max_frames,
|
|
306
366
|
VALUE tracer_context_key,
|
|
307
|
-
VALUE endpoint_collection_enabled
|
|
367
|
+
VALUE endpoint_collection_enabled,
|
|
368
|
+
VALUE timeline_enabled,
|
|
369
|
+
VALUE allocation_type_enabled
|
|
308
370
|
) {
|
|
309
371
|
ENFORCE_BOOLEAN(endpoint_collection_enabled);
|
|
372
|
+
ENFORCE_BOOLEAN(timeline_enabled);
|
|
373
|
+
ENFORCE_BOOLEAN(allocation_type_enabled);
|
|
310
374
|
|
|
311
375
|
struct thread_context_collector_state *state;
|
|
312
376
|
TypedData_Get_Struct(collector_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
@@ -319,6 +383,8 @@ static VALUE _native_initialize(
|
|
|
319
383
|
// hash_map_per_thread_context is already initialized, nothing to do here
|
|
320
384
|
state->recorder_instance = enforce_recorder_instance(recorder_instance);
|
|
321
385
|
state->endpoint_collection_enabled = (endpoint_collection_enabled == Qtrue);
|
|
386
|
+
state->timeline_enabled = (timeline_enabled == Qtrue);
|
|
387
|
+
state->allocation_type_enabled = (allocation_type_enabled == Qtrue);
|
|
322
388
|
|
|
323
389
|
if (RTEST(tracer_context_key)) {
|
|
324
390
|
ENFORCE_TYPE(tracer_context_key, T_SYMBOL);
|
|
@@ -433,7 +499,11 @@ void update_metrics_and_sample(
|
|
|
433
499
|
long wall_time_elapsed_ns = update_time_since_previous_sample(
|
|
434
500
|
&thread_context->wall_time_at_previous_sample_ns,
|
|
435
501
|
current_monotonic_wall_time_ns,
|
|
436
|
-
|
|
502
|
+
// We explicitly pass in `INVALID_TIME` as an argument for `gc_start_time_ns` here because we don't want wall-time
|
|
503
|
+
// accounting to change during GC.
|
|
504
|
+
// E.g. if 60 seconds pass in the real world, 60 seconds of wall-time are recorded, regardless of the thread doing
|
|
505
|
+
// GC or not.
|
|
506
|
+
INVALID_TIME,
|
|
437
507
|
IS_WALL_TIME
|
|
438
508
|
);
|
|
439
509
|
|
|
@@ -442,14 +512,16 @@ void update_metrics_and_sample(
|
|
|
442
512
|
thread_being_sampled,
|
|
443
513
|
stack_from_thread,
|
|
444
514
|
thread_context,
|
|
445
|
-
(sample_values) {.cpu_time_ns = cpu_time_elapsed_ns, .
|
|
446
|
-
|
|
515
|
+
(sample_values) {.cpu_time_ns = cpu_time_elapsed_ns, .cpu_or_wall_samples = 1, .wall_time_ns = wall_time_elapsed_ns},
|
|
516
|
+
current_monotonic_wall_time_ns,
|
|
517
|
+
NULL,
|
|
518
|
+
NULL
|
|
447
519
|
);
|
|
448
520
|
}
|
|
449
521
|
|
|
450
522
|
// This function gets called when Ruby is about to start running the Garbage Collector on the current thread.
|
|
451
523
|
// It updates the per_thread_context of the current thread to include the current cpu/wall times, to be used to later
|
|
452
|
-
// create
|
|
524
|
+
// create an event including the cpu/wall time spent in garbage collector work.
|
|
453
525
|
//
|
|
454
526
|
// Safety: This function gets called while Ruby is doing garbage collection. While Ruby is doing garbage collection,
|
|
455
527
|
// *NO ALLOCATION* is allowed. This function, and any it calls must never trigger memory or object allocation.
|
|
@@ -474,27 +546,14 @@ void thread_context_collector_on_gc_start(VALUE self_instance) {
|
|
|
474
546
|
return;
|
|
475
547
|
}
|
|
476
548
|
|
|
477
|
-
//
|
|
478
|
-
//
|
|
479
|
-
// When can this happen? Because we don't have precise control over when `sample_after_gc` gets called (it will be
|
|
480
|
-
// called sometime after GC finishes), there is no way to guarantee that Ruby will not trigger more than one GC cycle
|
|
481
|
-
// before we can actually run that method.
|
|
482
|
-
//
|
|
483
|
-
// We handle this by collapsing multiple GC cycles into one. That is, if the following happens:
|
|
484
|
-
// `on_gc_start` (time=0) -> `on_gc_finish` (time=1) -> `on_gc_start` (time=2) -> `on_gc_finish` (time=3) -> `sample_after_gc`
|
|
485
|
-
// then we just use time=0 from the first on_gc_start and time=3 from the last on_gc_finish, e.g. we behave as if
|
|
486
|
-
// there was a single, longer GC period.
|
|
487
|
-
if (thread_context->gc_tracking.cpu_time_at_finish_ns != INVALID_TIME &&
|
|
488
|
-
thread_context->gc_tracking.wall_time_at_finish_ns != INVALID_TIME) return;
|
|
489
|
-
|
|
490
|
-
// Here we record the wall-time first and in on_gc_finish we record it second to avoid having wall-time be slightly < cpu-time
|
|
549
|
+
// Here we record the wall-time first and in on_gc_finish we record it second to try to avoid having wall-time be slightly < cpu-time
|
|
491
550
|
thread_context->gc_tracking.wall_time_at_start_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
|
|
492
551
|
thread_context->gc_tracking.cpu_time_at_start_ns = cpu_time_now_ns(thread_context);
|
|
493
552
|
}
|
|
494
553
|
|
|
495
554
|
// This function gets called when Ruby has finished running the Garbage Collector on the current thread.
|
|
496
|
-
// It
|
|
497
|
-
// create
|
|
555
|
+
// It records the cpu/wall-time observed during GC, which will be used to later
|
|
556
|
+
// create an event including the cpu/wall time spent from the start of garbage collector work until now.
|
|
498
557
|
//
|
|
499
558
|
// Safety: This function gets called while Ruby is doing garbage collection. While Ruby is doing garbage collection,
|
|
500
559
|
// *NO ALLOCATION* is allowed. This function, and any it calls must never trigger memory or object allocation.
|
|
@@ -502,9 +561,9 @@ void thread_context_collector_on_gc_start(VALUE self_instance) {
|
|
|
502
561
|
//
|
|
503
562
|
// Assumption 1: This function is called in a thread that is holding the Global VM Lock. Caller is responsible for enforcing this.
|
|
504
563
|
// Assumption 2: This function is called from the main Ractor (if Ruby has support for Ractors).
|
|
505
|
-
|
|
564
|
+
bool thread_context_collector_on_gc_finish(VALUE self_instance) {
|
|
506
565
|
struct thread_context_collector_state *state;
|
|
507
|
-
if (!rb_typeddata_is_kind_of(self_instance, &thread_context_collector_typed_data)) return;
|
|
566
|
+
if (!rb_typeddata_is_kind_of(self_instance, &thread_context_collector_typed_data)) return false;
|
|
508
567
|
// This should never fail the the above check passes
|
|
509
568
|
TypedData_Get_Struct(self_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
510
569
|
|
|
@@ -512,29 +571,70 @@ void thread_context_collector_on_gc_finish(VALUE self_instance) {
|
|
|
512
571
|
|
|
513
572
|
// If there was no previously-existing context for this thread, we won't allocate one (see safety). We keep a metric for
|
|
514
573
|
// how often this happens -- see on_gc_start.
|
|
515
|
-
if (thread_context == NULL) return;
|
|
574
|
+
if (thread_context == NULL) return false;
|
|
516
575
|
|
|
517
|
-
|
|
518
|
-
|
|
576
|
+
long cpu_time_at_start_ns = thread_context->gc_tracking.cpu_time_at_start_ns;
|
|
577
|
+
long wall_time_at_start_ns = thread_context->gc_tracking.wall_time_at_start_ns;
|
|
578
|
+
|
|
579
|
+
if (cpu_time_at_start_ns == INVALID_TIME && wall_time_at_start_ns == INVALID_TIME) {
|
|
519
580
|
// If this happened, it means that on_gc_start was either never called for the thread OR it was called but no thread
|
|
520
581
|
// context existed at the time. The former can be the result of a bug, but since we can't distinguish them, we just
|
|
521
582
|
// do nothing.
|
|
522
|
-
return;
|
|
583
|
+
return false;
|
|
523
584
|
}
|
|
524
585
|
|
|
525
|
-
//
|
|
526
|
-
thread_context->gc_tracking.
|
|
527
|
-
thread_context->gc_tracking.
|
|
586
|
+
// Mark thread as no longer in GC
|
|
587
|
+
thread_context->gc_tracking.cpu_time_at_start_ns = INVALID_TIME;
|
|
588
|
+
thread_context->gc_tracking.wall_time_at_start_ns = INVALID_TIME;
|
|
589
|
+
|
|
590
|
+
// Here we record the wall-time second and in on_gc_start we record it first to try to avoid having wall-time be slightly < cpu-time
|
|
591
|
+
long cpu_time_at_finish_ns = cpu_time_now_ns(thread_context);
|
|
592
|
+
long wall_time_at_finish_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
|
|
593
|
+
|
|
594
|
+
// If our end timestamp is not OK, we bail out
|
|
595
|
+
if (wall_time_at_finish_ns == 0) return false;
|
|
596
|
+
|
|
597
|
+
long gc_cpu_time_elapsed_ns = cpu_time_at_finish_ns - cpu_time_at_start_ns;
|
|
598
|
+
long gc_wall_time_elapsed_ns = wall_time_at_finish_ns - wall_time_at_start_ns;
|
|
599
|
+
|
|
600
|
+
// Wall-time can go backwards if the system clock gets changed (and we observed spurious jumps back on macOS as well)
|
|
601
|
+
// so let's ensure we don't get negative values for time deltas.
|
|
602
|
+
gc_cpu_time_elapsed_ns = long_max_of(gc_cpu_time_elapsed_ns, 0);
|
|
603
|
+
gc_wall_time_elapsed_ns = long_max_of(gc_wall_time_elapsed_ns, 0);
|
|
604
|
+
|
|
605
|
+
if (state->gc_tracking.wall_time_at_previous_gc_ns == INVALID_TIME) {
|
|
606
|
+
state->gc_tracking.accumulated_cpu_time_ns = 0;
|
|
607
|
+
state->gc_tracking.accumulated_wall_time_ns = 0;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
state->gc_tracking.accumulated_cpu_time_ns += gc_cpu_time_elapsed_ns;
|
|
611
|
+
state->gc_tracking.accumulated_wall_time_ns += gc_wall_time_elapsed_ns;
|
|
612
|
+
state->gc_tracking.wall_time_at_previous_gc_ns = wall_time_at_finish_ns;
|
|
613
|
+
|
|
614
|
+
// Update cpu-time accounting so it doesn't include the cpu-time spent in GC during the next sample
|
|
615
|
+
// We don't update the wall-time because we don't subtract the wall-time spent in GC (see call to
|
|
616
|
+
// `update_time_since_previous_sample` for wall-time in `update_metrics_and_sample`).
|
|
617
|
+
if (thread_context->cpu_time_at_previous_sample_ns != INVALID_TIME) {
|
|
618
|
+
thread_context->cpu_time_at_previous_sample_ns += gc_cpu_time_elapsed_ns;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Let the caller know if it should schedule a flush or not. Returning true every time would cause a lot of overhead
|
|
622
|
+
// on the application (see GC tracking introduction at the top of the file), so instead we try to accumulate a few
|
|
623
|
+
// samples first.
|
|
624
|
+
bool over_flush_time_treshold =
|
|
625
|
+
(wall_time_at_finish_ns - state->gc_tracking.wall_time_at_last_flushed_gc_event_ns) >= TIME_BETWEEN_GC_EVENTS_NS;
|
|
626
|
+
|
|
627
|
+
if (over_flush_time_treshold) {
|
|
628
|
+
return true;
|
|
629
|
+
} else {
|
|
630
|
+
return gc_profiling_has_major_gc_finished();
|
|
631
|
+
}
|
|
528
632
|
}
|
|
529
633
|
|
|
530
|
-
// This function gets called
|
|
634
|
+
// This function gets called after one or more GC work steps (calls to on_gc_start/on_gc_finish).
|
|
531
635
|
// It creates a new sample including the cpu and wall-time spent by the garbage collector work, and resets any
|
|
532
636
|
// GC-related tracking.
|
|
533
637
|
//
|
|
534
|
-
// Specifically, it will search for thread(s) which have gone through a cycle of on_gc_start/on_gc_finish
|
|
535
|
-
// and thus have cpu_time_at_start_ns, cpu_time_at_finish_ns, wall_time_at_start_ns, wall_time_at_finish_ns
|
|
536
|
-
// set on their context.
|
|
537
|
-
//
|
|
538
638
|
// Assumption 1: This function is called in a thread that is holding the Global VM Lock. Caller is responsible for enforcing this.
|
|
539
639
|
// Assumption 2: This function is allowed to raise exceptions. Caller is responsible for handling them, if needed.
|
|
540
640
|
// Assumption 3: Unlike `on_gc_start` and `on_gc_finish`, this method is allowed to allocate memory as needed.
|
|
@@ -543,67 +643,45 @@ VALUE thread_context_collector_sample_after_gc(VALUE self_instance) {
|
|
|
543
643
|
struct thread_context_collector_state *state;
|
|
544
644
|
TypedData_Get_Struct(self_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
545
645
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const long thread_count = RARRAY_LEN(threads);
|
|
550
|
-
for (long i = 0; i < thread_count; i++) {
|
|
551
|
-
VALUE thread = RARRAY_AREF(threads, i);
|
|
552
|
-
struct per_thread_context *thread_context = get_or_create_context_for(thread, state);
|
|
646
|
+
if (state->gc_tracking.wall_time_at_previous_gc_ns == INVALID_TIME) {
|
|
647
|
+
rb_raise(rb_eRuntimeError, "BUG: Unexpected call to sample_after_gc without valid GC information available");
|
|
648
|
+
}
|
|
553
649
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
thread_context->gc_tracking.wall_time_at_start_ns == INVALID_TIME ||
|
|
558
|
-
thread_context->gc_tracking.wall_time_at_finish_ns == INVALID_TIME
|
|
559
|
-
) continue; // Ignore threads with no/incomplete garbage collection data
|
|
560
|
-
|
|
561
|
-
sampled_any_thread = true;
|
|
562
|
-
|
|
563
|
-
long gc_cpu_time_elapsed_ns =
|
|
564
|
-
thread_context->gc_tracking.cpu_time_at_finish_ns - thread_context->gc_tracking.cpu_time_at_start_ns;
|
|
565
|
-
long gc_wall_time_elapsed_ns =
|
|
566
|
-
thread_context->gc_tracking.wall_time_at_finish_ns - thread_context->gc_tracking.wall_time_at_start_ns;
|
|
567
|
-
|
|
568
|
-
// We don't expect non-wall time to go backwards, so let's flag this as a bug
|
|
569
|
-
if (gc_cpu_time_elapsed_ns < 0) rb_raise(rb_eRuntimeError, "BUG: Unexpected negative gc_cpu_time_elapsed_ns between samples");
|
|
570
|
-
// Wall-time can actually go backwards (e.g. when the system clock gets set) so we can't assume time going backwards
|
|
571
|
-
// was a bug.
|
|
572
|
-
// @ivoanjo: I've also observed time going backwards spuriously on macOS, see discussion on
|
|
573
|
-
// https://github.com/DataDog/dd-trace-rb/pull/2336.
|
|
574
|
-
if (gc_wall_time_elapsed_ns < 0) gc_wall_time_elapsed_ns = 0;
|
|
575
|
-
|
|
576
|
-
if (thread_context->gc_tracking.wall_time_at_start_ns == 0 && thread_context->gc_tracking.wall_time_at_finish_ns != 0) {
|
|
577
|
-
// Avoid using wall-clock if we got 0 for a start (meaning there was an error) but not 0 for finish so we don't
|
|
578
|
-
// come up with a crazy value for the frame
|
|
579
|
-
rb_raise(rb_eRuntimeError, "BUG: Unexpected zero value for gc_tracking.wall_time_at_start_ns");
|
|
580
|
-
}
|
|
650
|
+
int max_labels_needed_for_gc = 7; // Magic number gets validated inside gc_profiling_set_metadata
|
|
651
|
+
ddog_prof_Label labels[max_labels_needed_for_gc];
|
|
652
|
+
uint8_t label_pos = gc_profiling_set_metadata(labels, max_labels_needed_for_gc);
|
|
581
653
|
|
|
582
|
-
|
|
583
|
-
state,
|
|
584
|
-
/* thread: */ thread,
|
|
585
|
-
/* stack_from_thread: */ thread,
|
|
586
|
-
thread_context,
|
|
587
|
-
(sample_values) {.cpu_time_ns = gc_cpu_time_elapsed_ns, .cpu_samples = 1, .wall_time_ns = gc_wall_time_elapsed_ns},
|
|
588
|
-
SAMPLE_IN_GC
|
|
589
|
-
);
|
|
654
|
+
ddog_prof_Slice_Label slice_labels = {.ptr = labels, .len = label_pos};
|
|
590
655
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
thread_context->gc_tracking.cpu_time_at_finish_ns = INVALID_TIME;
|
|
594
|
-
thread_context->gc_tracking.wall_time_at_start_ns = INVALID_TIME;
|
|
595
|
-
thread_context->gc_tracking.wall_time_at_finish_ns = INVALID_TIME;
|
|
656
|
+
// The end_timestamp_ns is treated specially by libdatadog and that's why it's not added as a ddog_prof_Label
|
|
657
|
+
int64_t end_timestamp_ns = 0;
|
|
596
658
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
thread_context->cpu_time_at_previous_sample_ns += gc_cpu_time_elapsed_ns;
|
|
600
|
-
}
|
|
601
|
-
if (thread_context->wall_time_at_previous_sample_ns != INVALID_TIME) {
|
|
602
|
-
thread_context->wall_time_at_previous_sample_ns += gc_wall_time_elapsed_ns;
|
|
603
|
-
}
|
|
659
|
+
if (state->timeline_enabled) {
|
|
660
|
+
end_timestamp_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, state->gc_tracking.wall_time_at_previous_gc_ns);
|
|
604
661
|
}
|
|
605
662
|
|
|
606
|
-
|
|
663
|
+
record_placeholder_stack(
|
|
664
|
+
state->sampling_buffer,
|
|
665
|
+
state->recorder_instance,
|
|
666
|
+
(sample_values) {
|
|
667
|
+
// This event gets both a regular cpu/wall-time duration, as a normal cpu/wall-time sample would, as well as a
|
|
668
|
+
// timeline duration.
|
|
669
|
+
// This is done to enable two use-cases:
|
|
670
|
+
// * regular cpu/wall-time makes this event show up as a regular stack in the flamegraph
|
|
671
|
+
// * the timeline duration is used when the event shows up in the timeline
|
|
672
|
+
.cpu_time_ns = state->gc_tracking.accumulated_cpu_time_ns,
|
|
673
|
+
.cpu_or_wall_samples = 1,
|
|
674
|
+
.wall_time_ns = state->gc_tracking.accumulated_wall_time_ns,
|
|
675
|
+
.timeline_wall_time_ns = state->gc_tracking.accumulated_wall_time_ns,
|
|
676
|
+
},
|
|
677
|
+
(sample_labels) {.labels = slice_labels, .state_label = NULL, .end_timestamp_ns = end_timestamp_ns},
|
|
678
|
+
DDOG_CHARSLICE_C("Garbage Collection")
|
|
679
|
+
);
|
|
680
|
+
|
|
681
|
+
state->gc_tracking.wall_time_at_last_flushed_gc_event_ns = state->gc_tracking.wall_time_at_previous_gc_ns;
|
|
682
|
+
state->gc_tracking.wall_time_at_previous_gc_ns = INVALID_TIME;
|
|
683
|
+
|
|
684
|
+
state->stats.gc_samples++;
|
|
607
685
|
|
|
608
686
|
// Return a VALUE to make it easier to call this function from Ruby APIs that expect a return value (such as rb_rescue2)
|
|
609
687
|
return Qnil;
|
|
@@ -615,12 +693,17 @@ static void trigger_sample_for_thread(
|
|
|
615
693
|
VALUE stack_from_thread, // This can be different when attributing profiler overhead using a different stack
|
|
616
694
|
struct per_thread_context *thread_context,
|
|
617
695
|
sample_values values,
|
|
618
|
-
|
|
696
|
+
long current_monotonic_wall_time_ns,
|
|
697
|
+
// These two labels are only used for allocation profiling; @ivoanjo: may want to refactor this at some point?
|
|
698
|
+
ddog_CharSlice *ruby_vm_type,
|
|
699
|
+
ddog_CharSlice *class_name
|
|
619
700
|
) {
|
|
620
701
|
int max_label_count =
|
|
621
702
|
1 + // thread id
|
|
622
703
|
1 + // thread name
|
|
623
704
|
1 + // profiler overhead
|
|
705
|
+
2 + // ruby vm type and allocation class
|
|
706
|
+
1 + // state (only set for cpu/wall-time samples)
|
|
624
707
|
2; // local root span id and span id
|
|
625
708
|
ddog_prof_Label labels[max_label_count];
|
|
626
709
|
int label_pos = 0;
|
|
@@ -636,6 +719,19 @@ static void trigger_sample_for_thread(
|
|
|
636
719
|
.key = DDOG_CHARSLICE_C("thread name"),
|
|
637
720
|
.str = char_slice_from_ruby_string(thread_name)
|
|
638
721
|
};
|
|
722
|
+
} else if (thread == state->main_thread) { // Threads are often not named, but we can have a nice fallback for this special thread
|
|
723
|
+
ddog_CharSlice main_thread_name = DDOG_CHARSLICE_C("main");
|
|
724
|
+
labels[label_pos++] = (ddog_prof_Label) {
|
|
725
|
+
.key = DDOG_CHARSLICE_C("thread name"),
|
|
726
|
+
.str = main_thread_name
|
|
727
|
+
};
|
|
728
|
+
} else {
|
|
729
|
+
// For other threads without name, we use the "invoke location" (first file:line of the block used to start the thread), if any.
|
|
730
|
+
// This is what Ruby shows in `Thread#to_s`.
|
|
731
|
+
labels[label_pos++] = (ddog_prof_Label) {
|
|
732
|
+
.key = DDOG_CHARSLICE_C("thread name"),
|
|
733
|
+
.str = thread_context->thread_invoke_location_char_slice // This is an empty string if no invoke location was available
|
|
734
|
+
};
|
|
639
735
|
}
|
|
640
736
|
|
|
641
737
|
struct trace_identifiers trace_identifiers_result = {.valid = false, .trace_endpoint = Qnil};
|
|
@@ -670,21 +766,56 @@ static void trigger_sample_for_thread(
|
|
|
670
766
|
};
|
|
671
767
|
}
|
|
672
768
|
|
|
769
|
+
if (ruby_vm_type != NULL) {
|
|
770
|
+
labels[label_pos++] = (ddog_prof_Label) {
|
|
771
|
+
.key = DDOG_CHARSLICE_C("ruby vm type"),
|
|
772
|
+
.str = *ruby_vm_type
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if (class_name != NULL) {
|
|
777
|
+
labels[label_pos++] = (ddog_prof_Label) {
|
|
778
|
+
.key = DDOG_CHARSLICE_C("allocation class"),
|
|
779
|
+
.str = *class_name
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// This label is handled specially:
|
|
784
|
+
// 1. It's only set for cpu/wall-time samples
|
|
785
|
+
// 2. We set it here to its default state of "unknown", but the `Collectors::Stack` may choose to override it with
|
|
786
|
+
// something more interesting.
|
|
787
|
+
ddog_prof_Label *state_label = NULL;
|
|
788
|
+
if (values.cpu_or_wall_samples > 0) {
|
|
789
|
+
state_label = &labels[label_pos++];
|
|
790
|
+
*state_label = (ddog_prof_Label) {
|
|
791
|
+
.key = DDOG_CHARSLICE_C("state"),
|
|
792
|
+
.str = DDOG_CHARSLICE_C("unknown"),
|
|
793
|
+
.num = 0, // This shouldn't be needed but the tracer-2.7 docker image ships a buggy gcc that complains about this
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
|
|
673
797
|
// The number of times `label_pos++` shows up in this function needs to match `max_label_count`. To avoid "oops I
|
|
674
798
|
// forgot to update max_label_count" in the future, we've also added this validation.
|
|
675
|
-
// @ivoanjo: I wonder if C compilers are smart enough to statically prove
|
|
676
|
-
// remove it entirely
|
|
799
|
+
// @ivoanjo: I wonder if C compilers are smart enough to statically prove this check never triggers unless someone
|
|
800
|
+
// changes the code erroneously and remove it entirely?
|
|
677
801
|
if (label_pos > max_label_count) {
|
|
678
802
|
rb_raise(rb_eRuntimeError, "BUG: Unexpected label_pos (%d) > max_label_count (%d)", label_pos, max_label_count);
|
|
679
803
|
}
|
|
680
804
|
|
|
805
|
+
ddog_prof_Slice_Label slice_labels = {.ptr = labels, .len = label_pos};
|
|
806
|
+
|
|
807
|
+
// The end_timestamp_ns is treated specially by libdatadog and that's why it's not added as a ddog_prof_Label
|
|
808
|
+
int64_t end_timestamp_ns = 0;
|
|
809
|
+
if (state->timeline_enabled && current_monotonic_wall_time_ns != INVALID_TIME) {
|
|
810
|
+
end_timestamp_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, current_monotonic_wall_time_ns);
|
|
811
|
+
}
|
|
812
|
+
|
|
681
813
|
sample_thread(
|
|
682
814
|
stack_from_thread,
|
|
683
815
|
state->sampling_buffer,
|
|
684
816
|
state->recorder_instance,
|
|
685
817
|
values,
|
|
686
|
-
(
|
|
687
|
-
type
|
|
818
|
+
(sample_labels) {.labels = slice_labels, .state_label = state_label, .end_timestamp_ns = end_timestamp_ns}
|
|
688
819
|
);
|
|
689
820
|
}
|
|
690
821
|
|
|
@@ -704,7 +835,7 @@ static struct per_thread_context *get_or_create_context_for(VALUE thread, struct
|
|
|
704
835
|
thread_context = (struct per_thread_context*) value_context;
|
|
705
836
|
} else {
|
|
706
837
|
thread_context = ruby_xcalloc(1, sizeof(struct per_thread_context));
|
|
707
|
-
initialize_context(thread, thread_context);
|
|
838
|
+
initialize_context(thread, thread_context, state);
|
|
708
839
|
st_insert(state->hash_map_per_thread_context, (st_data_t) thread, (st_data_t) thread_context);
|
|
709
840
|
}
|
|
710
841
|
|
|
@@ -722,10 +853,57 @@ static struct per_thread_context *get_context_for(VALUE thread, struct thread_co
|
|
|
722
853
|
return thread_context;
|
|
723
854
|
}
|
|
724
855
|
|
|
725
|
-
|
|
856
|
+
#define LOGGING_GEM_PATH "/lib/logging/diagnostic_context.rb"
|
|
857
|
+
|
|
858
|
+
// The `logging` gem monkey patches thread creation, which makes the `invoke_location_for` useless, since every thread
|
|
859
|
+
// will point to the `logging` gem. When that happens, we avoid using the invoke location.
|
|
860
|
+
//
|
|
861
|
+
// TODO: This approach is a bit brittle, since it matches on the specific gem path, and only works for the `logging`
|
|
862
|
+
// gem.
|
|
863
|
+
// In the future we should probably explore a more generic fix (e.g. using Thread.method(:new).source_location or
|
|
864
|
+
// something like that to detect redefinition of the `Thread` methods). One difficulty of doing it is that we need
|
|
865
|
+
// to either run Ruby code during sampling (not great), or otherwise use some of the VM private APIs to detect this.
|
|
866
|
+
//
|
|
867
|
+
static bool is_logging_gem_monkey_patch(VALUE invoke_file_location) {
|
|
868
|
+
int logging_gem_path_len = strlen(LOGGING_GEM_PATH);
|
|
869
|
+
char *invoke_file = StringValueCStr(invoke_file_location);
|
|
870
|
+
int invoke_file_len = strlen(invoke_file);
|
|
871
|
+
|
|
872
|
+
if (invoke_file_len < logging_gem_path_len) return false;
|
|
873
|
+
|
|
874
|
+
return strncmp(invoke_file + invoke_file_len - logging_gem_path_len, LOGGING_GEM_PATH, logging_gem_path_len) == 0;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
static void initialize_context(VALUE thread, struct per_thread_context *thread_context, struct thread_context_collector_state *state) {
|
|
726
878
|
snprintf(thread_context->thread_id, THREAD_ID_LIMIT_CHARS, "%"PRIu64" (%lu)", native_thread_id_for(thread), (unsigned long) thread_id_for(thread));
|
|
727
879
|
thread_context->thread_id_char_slice = (ddog_CharSlice) {.ptr = thread_context->thread_id, .len = strlen(thread_context->thread_id)};
|
|
728
880
|
|
|
881
|
+
int invoke_line_location;
|
|
882
|
+
VALUE invoke_file_location = invoke_location_for(thread, &invoke_line_location);
|
|
883
|
+
if (invoke_file_location != Qnil) {
|
|
884
|
+
if (!is_logging_gem_monkey_patch(invoke_file_location)) {
|
|
885
|
+
snprintf(
|
|
886
|
+
thread_context->thread_invoke_location,
|
|
887
|
+
THREAD_INVOKE_LOCATION_LIMIT_CHARS,
|
|
888
|
+
"%s:%d",
|
|
889
|
+
StringValueCStr(invoke_file_location),
|
|
890
|
+
invoke_line_location
|
|
891
|
+
);
|
|
892
|
+
} else {
|
|
893
|
+
snprintf(thread_context->thread_invoke_location, THREAD_INVOKE_LOCATION_LIMIT_CHARS, "%s", "(Unnamed thread)");
|
|
894
|
+
}
|
|
895
|
+
} else if (thread != state->main_thread) {
|
|
896
|
+
// If the first function of a thread is native code, there won't be an invoke location, so we use this fallback.
|
|
897
|
+
// NOTE: In the future, I wonder if we could take the pointer to the native function, and try to see if there's a native
|
|
898
|
+
// symbol attached to it.
|
|
899
|
+
snprintf(thread_context->thread_invoke_location, THREAD_INVOKE_LOCATION_LIMIT_CHARS, "%s", "(Unnamed thread from native code)");
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
thread_context->thread_invoke_location_char_slice = (ddog_CharSlice) {
|
|
903
|
+
.ptr = thread_context->thread_invoke_location,
|
|
904
|
+
.len = strlen(thread_context->thread_invoke_location)
|
|
905
|
+
};
|
|
906
|
+
|
|
729
907
|
thread_context->thread_cpu_time_id = thread_cpu_time_id_for(thread);
|
|
730
908
|
|
|
731
909
|
// These will get initialized during actual sampling
|
|
@@ -734,9 +912,7 @@ static void initialize_context(VALUE thread, struct per_thread_context *thread_c
|
|
|
734
912
|
|
|
735
913
|
// These will only be used during a GC operation
|
|
736
914
|
thread_context->gc_tracking.cpu_time_at_start_ns = INVALID_TIME;
|
|
737
|
-
thread_context->gc_tracking.cpu_time_at_finish_ns = INVALID_TIME;
|
|
738
915
|
thread_context->gc_tracking.wall_time_at_start_ns = INVALID_TIME;
|
|
739
|
-
thread_context->gc_tracking.wall_time_at_finish_ns = INVALID_TIME;
|
|
740
916
|
}
|
|
741
917
|
|
|
742
918
|
static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
|
@@ -753,6 +929,16 @@ static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instanc
|
|
|
753
929
|
rb_str_concat(result, rb_sprintf(" sample_count=%u", state->sample_count));
|
|
754
930
|
rb_str_concat(result, rb_sprintf(" stats=%"PRIsVALUE, stats_as_ruby_hash(state)));
|
|
755
931
|
rb_str_concat(result, rb_sprintf(" endpoint_collection_enabled=%"PRIsVALUE, state->endpoint_collection_enabled ? Qtrue : Qfalse));
|
|
932
|
+
rb_str_concat(result, rb_sprintf(" timeline_enabled=%"PRIsVALUE, state->timeline_enabled ? Qtrue : Qfalse));
|
|
933
|
+
rb_str_concat(result, rb_sprintf(" allocation_type_enabled=%"PRIsVALUE, state->allocation_type_enabled ? Qtrue : Qfalse));
|
|
934
|
+
rb_str_concat(result, rb_sprintf(
|
|
935
|
+
" time_converter_state={.system_epoch_ns_reference=%ld, .delta_to_epoch_ns=%ld}",
|
|
936
|
+
state->time_converter_state.system_epoch_ns_reference,
|
|
937
|
+
state->time_converter_state.delta_to_epoch_ns
|
|
938
|
+
));
|
|
939
|
+
rb_str_concat(result, rb_sprintf(" main_thread=%"PRIsVALUE, state->main_thread));
|
|
940
|
+
rb_str_concat(result, rb_sprintf(" gc_tracking=%"PRIsVALUE, gc_tracking_as_ruby_hash(state)));
|
|
941
|
+
rb_str_concat(result, rb_sprintf(" otel_current_span_key=%"PRIsVALUE, state->otel_current_span_key));
|
|
756
942
|
|
|
757
943
|
return result;
|
|
758
944
|
}
|
|
@@ -772,15 +958,14 @@ static int per_thread_context_as_ruby_hash(st_data_t key_thread, st_data_t value
|
|
|
772
958
|
|
|
773
959
|
VALUE arguments[] = {
|
|
774
960
|
ID2SYM(rb_intern("thread_id")), /* => */ rb_str_new2(thread_context->thread_id),
|
|
961
|
+
ID2SYM(rb_intern("thread_invoke_location")), /* => */ rb_str_new2(thread_context->thread_invoke_location),
|
|
775
962
|
ID2SYM(rb_intern("thread_cpu_time_id_valid?")), /* => */ thread_context->thread_cpu_time_id.valid ? Qtrue : Qfalse,
|
|
776
963
|
ID2SYM(rb_intern("thread_cpu_time_id")), /* => */ CLOCKID2NUM(thread_context->thread_cpu_time_id.clock_id),
|
|
777
964
|
ID2SYM(rb_intern("cpu_time_at_previous_sample_ns")), /* => */ LONG2NUM(thread_context->cpu_time_at_previous_sample_ns),
|
|
778
965
|
ID2SYM(rb_intern("wall_time_at_previous_sample_ns")), /* => */ LONG2NUM(thread_context->wall_time_at_previous_sample_ns),
|
|
779
966
|
|
|
780
967
|
ID2SYM(rb_intern("gc_tracking.cpu_time_at_start_ns")), /* => */ LONG2NUM(thread_context->gc_tracking.cpu_time_at_start_ns),
|
|
781
|
-
ID2SYM(rb_intern("gc_tracking.cpu_time_at_finish_ns")), /* => */ LONG2NUM(thread_context->gc_tracking.cpu_time_at_finish_ns),
|
|
782
968
|
ID2SYM(rb_intern("gc_tracking.wall_time_at_start_ns")), /* => */ LONG2NUM(thread_context->gc_tracking.wall_time_at_start_ns),
|
|
783
|
-
ID2SYM(rb_intern("gc_tracking.wall_time_at_finish_ns")), /* => */ LONG2NUM(thread_context->gc_tracking.wall_time_at_finish_ns)
|
|
784
969
|
};
|
|
785
970
|
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(context_as_hash, arguments[i], arguments[i+1]);
|
|
786
971
|
|
|
@@ -798,6 +983,19 @@ static VALUE stats_as_ruby_hash(struct thread_context_collector_state *state) {
|
|
|
798
983
|
return stats_as_hash;
|
|
799
984
|
}
|
|
800
985
|
|
|
986
|
+
static VALUE gc_tracking_as_ruby_hash(struct thread_context_collector_state *state) {
|
|
987
|
+
// Update this when modifying state struct (gc_tracking inner struct)
|
|
988
|
+
VALUE result = rb_hash_new();
|
|
989
|
+
VALUE arguments[] = {
|
|
990
|
+
ID2SYM(rb_intern("accumulated_cpu_time_ns")), /* => */ ULONG2NUM(state->gc_tracking.accumulated_cpu_time_ns),
|
|
991
|
+
ID2SYM(rb_intern("accumulated_wall_time_ns")), /* => */ ULONG2NUM(state->gc_tracking.accumulated_wall_time_ns),
|
|
992
|
+
ID2SYM(rb_intern("wall_time_at_previous_gc_ns")), /* => */ LONG2NUM(state->gc_tracking.wall_time_at_previous_gc_ns),
|
|
993
|
+
ID2SYM(rb_intern("wall_time_at_last_flushed_gc_event_ns")), /* => */ LONG2NUM(state->gc_tracking.wall_time_at_last_flushed_gc_event_ns),
|
|
994
|
+
};
|
|
995
|
+
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(result, arguments[i], arguments[i+1]);
|
|
996
|
+
return result;
|
|
997
|
+
}
|
|
998
|
+
|
|
801
999
|
static void remove_context_for_dead_threads(struct thread_context_collector_state *state) {
|
|
802
1000
|
st_foreach(state->hash_map_per_thread_context, remove_if_dead_thread, 0 /* unused */);
|
|
803
1001
|
}
|
|
@@ -900,8 +1098,6 @@ VALUE enforce_thread_context_collector_instance(VALUE object) {
|
|
|
900
1098
|
|
|
901
1099
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
|
902
1100
|
// It SHOULD NOT be used for other purposes.
|
|
903
|
-
//
|
|
904
|
-
// Returns the whole contents of the per_thread_context structs being tracked.
|
|
905
1101
|
static VALUE _native_stats(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
|
906
1102
|
struct thread_context_collector_state *state;
|
|
907
1103
|
TypedData_Get_Struct(collector_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
@@ -909,6 +1105,15 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE _self, VALUE collector_instance)
|
|
|
909
1105
|
return stats_as_ruby_hash(state);
|
|
910
1106
|
}
|
|
911
1107
|
|
|
1108
|
+
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
|
1109
|
+
// It SHOULD NOT be used for other purposes.
|
|
1110
|
+
static VALUE _native_gc_tracking(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
|
1111
|
+
struct thread_context_collector_state *state;
|
|
1112
|
+
TypedData_Get_Struct(collector_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1113
|
+
|
|
1114
|
+
return gc_tracking_as_ruby_hash(state);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
912
1117
|
// Assumption 1: This function is called in a thread that is holding the Global VM Lock. Caller is responsible for enforcing this.
|
|
913
1118
|
static void trace_identifiers_for(struct thread_context_collector_state *state, VALUE thread, struct trace_identifiers *trace_identifiers_result) {
|
|
914
1119
|
if (state->tracer_context_key == MISSING_TRACER_CONTEXT_KEY) return;
|
|
@@ -921,10 +1126,19 @@ static void trace_identifiers_for(struct thread_context_collector_state *state,
|
|
|
921
1126
|
|
|
922
1127
|
VALUE root_span = rb_ivar_get(active_trace, at_root_span_id /* @root_span */);
|
|
923
1128
|
VALUE active_span = rb_ivar_get(active_trace, at_active_span_id /* @active_span */);
|
|
924
|
-
|
|
1129
|
+
// Note: On Ruby 3.x `rb_attr_get` is exactly the same as `rb_ivar_get`. For Ruby 2.x, the difference is that
|
|
1130
|
+
// `rb_ivar_get` can trigger "warning: instance variable @otel_values not initialized" if warnings are enabled and
|
|
1131
|
+
// opentelemetry is not in use, whereas `rb_attr_get` does the lookup without generating the warning.
|
|
1132
|
+
VALUE otel_values = rb_attr_get(active_trace, at_otel_values_id /* @otel_values */);
|
|
1133
|
+
|
|
1134
|
+
VALUE numeric_span_id = Qnil;
|
|
1135
|
+
|
|
1136
|
+
if (otel_values != Qnil) ddtrace_otel_trace_identifiers_for(state, &active_trace, &root_span, &numeric_span_id, active_span, otel_values);
|
|
1137
|
+
|
|
1138
|
+
if (root_span == Qnil || (active_span == Qnil && numeric_span_id == Qnil)) return;
|
|
925
1139
|
|
|
926
1140
|
VALUE numeric_local_root_span_id = rb_ivar_get(root_span, at_id_id /* @id */);
|
|
927
|
-
|
|
1141
|
+
if (active_span != Qnil && numeric_span_id == Qnil) numeric_span_id = rb_ivar_get(active_span, at_id_id /* @id */);
|
|
928
1142
|
if (numeric_local_root_span_id == Qnil || numeric_span_id == Qnil) return;
|
|
929
1143
|
|
|
930
1144
|
trace_identifiers_result->local_root_span_id = NUM2ULL(numeric_local_root_span_id);
|
|
@@ -932,10 +1146,7 @@ static void trace_identifiers_for(struct thread_context_collector_state *state,
|
|
|
932
1146
|
|
|
933
1147
|
trace_identifiers_result->valid = true;
|
|
934
1148
|
|
|
935
|
-
if (!state->endpoint_collection_enabled) return;
|
|
936
|
-
|
|
937
|
-
VALUE root_span_type = rb_ivar_get(root_span, at_type_id /* @type */);
|
|
938
|
-
if (root_span_type == Qnil || !is_type_web(root_span_type)) return;
|
|
1149
|
+
if (!state->endpoint_collection_enabled || !should_collect_resource(root_span)) return;
|
|
939
1150
|
|
|
940
1151
|
VALUE trace_resource = rb_ivar_get(active_trace, at_resource_id /* @resource */);
|
|
941
1152
|
if (RB_TYPE_P(trace_resource, T_STRING)) {
|
|
@@ -946,11 +1157,32 @@ static void trace_identifiers_for(struct thread_context_collector_state *state,
|
|
|
946
1157
|
}
|
|
947
1158
|
}
|
|
948
1159
|
|
|
949
|
-
|
|
1160
|
+
// We opt-in to collecting the resource for spans of types:
|
|
1161
|
+
// * 'web', for web requests
|
|
1162
|
+
// * 'proxy', used by the rack integration with request_queuing: true (e.g. also represents a web request)
|
|
1163
|
+
// * 'worker', used for sidekiq and similar background job processors
|
|
1164
|
+
//
|
|
1165
|
+
// Over time, this list may be expanded.
|
|
1166
|
+
// Resources MUST NOT include personal identifiable information (PII); this should not be the case with
|
|
1167
|
+
// ddtrace integrations, but worth mentioning just in case :)
|
|
1168
|
+
static bool should_collect_resource(VALUE root_span) {
|
|
1169
|
+
VALUE root_span_type = rb_ivar_get(root_span, at_type_id /* @type */);
|
|
1170
|
+
if (root_span_type == Qnil) return false;
|
|
950
1171
|
ENFORCE_TYPE(root_span_type, T_STRING);
|
|
951
1172
|
|
|
952
|
-
|
|
953
|
-
|
|
1173
|
+
int root_span_type_length = RSTRING_LEN(root_span_type);
|
|
1174
|
+
const char *root_span_type_value = StringValuePtr(root_span_type);
|
|
1175
|
+
|
|
1176
|
+
bool is_web_request =
|
|
1177
|
+
(root_span_type_length == strlen("web") && (memcmp("web", root_span_type_value, strlen("web")) == 0)) ||
|
|
1178
|
+
(root_span_type_length == strlen("proxy") && (memcmp("proxy", root_span_type_value, strlen("proxy")) == 0));
|
|
1179
|
+
|
|
1180
|
+
if (is_web_request) return true;
|
|
1181
|
+
|
|
1182
|
+
bool is_worker_request =
|
|
1183
|
+
(root_span_type_length == strlen("worker") && (memcmp("worker", root_span_type_value, strlen("worker")) == 0));
|
|
1184
|
+
|
|
1185
|
+
return is_worker_request;
|
|
954
1186
|
}
|
|
955
1187
|
|
|
956
1188
|
// After the Ruby VM forks, this method gets called in the child process to clean up any leftover state from the parent.
|
|
@@ -977,25 +1209,194 @@ static VALUE thread_list(struct thread_context_collector_state *state) {
|
|
|
977
1209
|
return result;
|
|
978
1210
|
}
|
|
979
1211
|
|
|
980
|
-
void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight) {
|
|
1212
|
+
void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object) {
|
|
981
1213
|
struct thread_context_collector_state *state;
|
|
982
1214
|
TypedData_Get_Struct(self_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
983
1215
|
|
|
984
1216
|
VALUE current_thread = rb_thread_current();
|
|
985
1217
|
|
|
1218
|
+
enum ruby_value_type type = rb_type(new_object);
|
|
1219
|
+
|
|
1220
|
+
// Tag samples with the VM internal types
|
|
1221
|
+
ddog_CharSlice ruby_vm_type = ruby_value_type_to_char_slice(type);
|
|
1222
|
+
|
|
1223
|
+
// Since this is stack allocated, be careful about moving it
|
|
1224
|
+
ddog_CharSlice class_name;
|
|
1225
|
+
ddog_CharSlice *optional_class_name = NULL;
|
|
1226
|
+
char imemo_type[100];
|
|
1227
|
+
|
|
1228
|
+
if (state->allocation_type_enabled) {
|
|
1229
|
+
optional_class_name = &class_name;
|
|
1230
|
+
|
|
1231
|
+
if (
|
|
1232
|
+
type == RUBY_T_OBJECT ||
|
|
1233
|
+
type == RUBY_T_CLASS ||
|
|
1234
|
+
type == RUBY_T_MODULE ||
|
|
1235
|
+
type == RUBY_T_FLOAT ||
|
|
1236
|
+
type == RUBY_T_STRING ||
|
|
1237
|
+
type == RUBY_T_REGEXP ||
|
|
1238
|
+
type == RUBY_T_ARRAY ||
|
|
1239
|
+
type == RUBY_T_HASH ||
|
|
1240
|
+
type == RUBY_T_STRUCT ||
|
|
1241
|
+
type == RUBY_T_BIGNUM ||
|
|
1242
|
+
type == RUBY_T_FILE ||
|
|
1243
|
+
type == RUBY_T_DATA ||
|
|
1244
|
+
type == RUBY_T_MATCH ||
|
|
1245
|
+
type == RUBY_T_COMPLEX ||
|
|
1246
|
+
type == RUBY_T_RATIONAL ||
|
|
1247
|
+
type == RUBY_T_NIL ||
|
|
1248
|
+
type == RUBY_T_TRUE ||
|
|
1249
|
+
type == RUBY_T_FALSE ||
|
|
1250
|
+
type == RUBY_T_SYMBOL ||
|
|
1251
|
+
type == RUBY_T_FIXNUM
|
|
1252
|
+
) {
|
|
1253
|
+
VALUE klass = rb_class_of(new_object);
|
|
1254
|
+
|
|
1255
|
+
// Ruby sometimes plays a bit fast and loose with some of its internal objects, e.g.
|
|
1256
|
+
// `rb_str_tmp_frozen_acquire` allocates a string with no class (klass=0).
|
|
1257
|
+
// Thus, we need to make sure there's actually a class before getting its name.
|
|
1258
|
+
|
|
1259
|
+
if (klass != 0) {
|
|
1260
|
+
const char *name = rb_obj_classname(new_object);
|
|
1261
|
+
size_t name_length = name != NULL ? strlen(name) : 0;
|
|
1262
|
+
|
|
1263
|
+
if (name_length > 0) {
|
|
1264
|
+
class_name = (ddog_CharSlice) {.ptr = name, .len = name_length};
|
|
1265
|
+
} else {
|
|
1266
|
+
// @ivoanjo: I'm not sure this can ever happen, but just-in-case
|
|
1267
|
+
class_name = ruby_value_type_to_class_name(type);
|
|
1268
|
+
}
|
|
1269
|
+
} else {
|
|
1270
|
+
// Fallback for objects with no class
|
|
1271
|
+
class_name = ruby_value_type_to_class_name(type);
|
|
1272
|
+
}
|
|
1273
|
+
} else if (type == RUBY_T_IMEMO) {
|
|
1274
|
+
const char *imemo_string = imemo_kind(new_object);
|
|
1275
|
+
if (imemo_string != NULL) {
|
|
1276
|
+
snprintf(imemo_type, 100, "(VM Internal, T_IMEMO, %s)", imemo_string);
|
|
1277
|
+
class_name = (ddog_CharSlice) {.ptr = imemo_type, .len = strlen(imemo_type)};
|
|
1278
|
+
} else { // Ruby < 3
|
|
1279
|
+
class_name = DDOG_CHARSLICE_C("(VM Internal, T_IMEMO)");
|
|
1280
|
+
}
|
|
1281
|
+
} else {
|
|
1282
|
+
class_name = ruby_vm_type; // For other weird internal things we just use the VM type
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
track_object(state->recorder_instance, new_object, sample_weight, optional_class_name);
|
|
1287
|
+
|
|
986
1288
|
trigger_sample_for_thread(
|
|
987
1289
|
state,
|
|
988
1290
|
/* thread: */ current_thread,
|
|
989
1291
|
/* stack_from_thread: */ current_thread,
|
|
990
1292
|
get_or_create_context_for(current_thread, state),
|
|
991
1293
|
(sample_values) {.alloc_samples = sample_weight},
|
|
992
|
-
|
|
1294
|
+
INVALID_TIME, // For now we're not collecting timestamps for allocation events, as per profiling team internal discussions
|
|
1295
|
+
&ruby_vm_type,
|
|
1296
|
+
optional_class_name
|
|
993
1297
|
);
|
|
994
1298
|
}
|
|
995
1299
|
|
|
996
1300
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
|
997
1301
|
// It SHOULD NOT be used for other purposes.
|
|
998
|
-
static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight) {
|
|
999
|
-
thread_context_collector_sample_allocation(collector_instance, NUM2UINT(sample_weight));
|
|
1302
|
+
static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object) {
|
|
1303
|
+
thread_context_collector_sample_allocation(collector_instance, NUM2UINT(sample_weight), new_object);
|
|
1000
1304
|
return Qtrue;
|
|
1001
1305
|
}
|
|
1306
|
+
|
|
1307
|
+
static VALUE new_empty_thread_inner(DDTRACE_UNUSED void *arg) { return Qnil; }
|
|
1308
|
+
|
|
1309
|
+
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
|
1310
|
+
// It SHOULD NOT be used for other purposes.
|
|
1311
|
+
// (It creates an empty native thread, so we can test our native thread naming fallback)
|
|
1312
|
+
static VALUE _native_new_empty_thread(DDTRACE_UNUSED VALUE self) {
|
|
1313
|
+
return rb_thread_create(new_empty_thread_inner, NULL);
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
static ddog_CharSlice ruby_value_type_to_class_name(enum ruby_value_type type) {
|
|
1317
|
+
switch (type) {
|
|
1318
|
+
case(RUBY_T_OBJECT ): return DDOG_CHARSLICE_C("Object");
|
|
1319
|
+
case(RUBY_T_CLASS ): return DDOG_CHARSLICE_C("Class");
|
|
1320
|
+
case(RUBY_T_MODULE ): return DDOG_CHARSLICE_C("Module");
|
|
1321
|
+
case(RUBY_T_FLOAT ): return DDOG_CHARSLICE_C("Float");
|
|
1322
|
+
case(RUBY_T_STRING ): return DDOG_CHARSLICE_C("String");
|
|
1323
|
+
case(RUBY_T_REGEXP ): return DDOG_CHARSLICE_C("Regexp");
|
|
1324
|
+
case(RUBY_T_ARRAY ): return DDOG_CHARSLICE_C("Array");
|
|
1325
|
+
case(RUBY_T_HASH ): return DDOG_CHARSLICE_C("Hash");
|
|
1326
|
+
case(RUBY_T_STRUCT ): return DDOG_CHARSLICE_C("Struct");
|
|
1327
|
+
case(RUBY_T_BIGNUM ): return DDOG_CHARSLICE_C("Integer");
|
|
1328
|
+
case(RUBY_T_FILE ): return DDOG_CHARSLICE_C("File");
|
|
1329
|
+
case(RUBY_T_DATA ): return DDOG_CHARSLICE_C("(VM Internal, T_DATA)");
|
|
1330
|
+
case(RUBY_T_MATCH ): return DDOG_CHARSLICE_C("MatchData");
|
|
1331
|
+
case(RUBY_T_COMPLEX ): return DDOG_CHARSLICE_C("Complex");
|
|
1332
|
+
case(RUBY_T_RATIONAL): return DDOG_CHARSLICE_C("Rational");
|
|
1333
|
+
case(RUBY_T_NIL ): return DDOG_CHARSLICE_C("NilClass");
|
|
1334
|
+
case(RUBY_T_TRUE ): return DDOG_CHARSLICE_C("TrueClass");
|
|
1335
|
+
case(RUBY_T_FALSE ): return DDOG_CHARSLICE_C("FalseClass");
|
|
1336
|
+
case(RUBY_T_SYMBOL ): return DDOG_CHARSLICE_C("Symbol");
|
|
1337
|
+
case(RUBY_T_FIXNUM ): return DDOG_CHARSLICE_C("Integer");
|
|
1338
|
+
default: return DDOG_CHARSLICE_C("(VM Internal, Missing class)");
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
static VALUE get_otel_current_span_key(struct thread_context_collector_state *state) {
|
|
1343
|
+
if (state->otel_current_span_key == Qnil) {
|
|
1344
|
+
VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
|
|
1345
|
+
VALUE opentelemetry_module = rb_const_get(datadog_module, rb_intern("OpenTelemetry"));
|
|
1346
|
+
VALUE api_module = rb_const_get(opentelemetry_module, rb_intern("API"));
|
|
1347
|
+
VALUE context_module = rb_const_get(api_module, rb_intern_const("Context"));
|
|
1348
|
+
VALUE current_span_key = rb_const_get(context_module, rb_intern_const("CURRENT_SPAN_KEY"));
|
|
1349
|
+
|
|
1350
|
+
if (current_span_key == Qnil) {
|
|
1351
|
+
rb_raise(rb_eRuntimeError, "Unexpected: Missing Datadog::OpenTelemetry::API::Context::CURRENT_SPAN_KEY");
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
state->otel_current_span_key = current_span_key;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
return state->otel_current_span_key;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
// This method gets used when ddtrace is being used indirectly via the otel APIs. Information gets stored slightly
|
|
1361
|
+
// differently, and this codepath handles it.
|
|
1362
|
+
static void ddtrace_otel_trace_identifiers_for(
|
|
1363
|
+
struct thread_context_collector_state *state,
|
|
1364
|
+
VALUE *active_trace,
|
|
1365
|
+
VALUE *root_span,
|
|
1366
|
+
VALUE *numeric_span_id,
|
|
1367
|
+
VALUE active_span,
|
|
1368
|
+
VALUE otel_values
|
|
1369
|
+
) {
|
|
1370
|
+
VALUE resolved_numeric_span_id =
|
|
1371
|
+
active_span == Qnil ?
|
|
1372
|
+
// For traces started from otel spans, the span id will be empty, and the @parent_span_id has the right value
|
|
1373
|
+
rb_ivar_get(*active_trace, at_parent_span_id_id /* @parent_span_id */) :
|
|
1374
|
+
// Regular span created by ddtrace
|
|
1375
|
+
rb_ivar_get(active_span, at_id_id /* @id */);
|
|
1376
|
+
|
|
1377
|
+
if (resolved_numeric_span_id == Qnil) return;
|
|
1378
|
+
|
|
1379
|
+
VALUE otel_current_span_key = get_otel_current_span_key(state);
|
|
1380
|
+
VALUE current_trace = *active_trace;
|
|
1381
|
+
|
|
1382
|
+
// ddtrace uses a different structure when spans are created from otel, where each otel span will have a unique ddtrace
|
|
1383
|
+
// trace and span representing it. Each ddtrace trace is then connected to the previous otel span, forming a linked
|
|
1384
|
+
// list. The local root span is going to be the trace/span we find at the end of this linked list.
|
|
1385
|
+
while (otel_values != Qnil) {
|
|
1386
|
+
VALUE otel_span = rb_hash_lookup(otel_values, otel_current_span_key);
|
|
1387
|
+
if (otel_span == Qnil) break;
|
|
1388
|
+
VALUE next_trace = rb_ivar_get(otel_span, at_datadog_trace_id);
|
|
1389
|
+
if (next_trace == Qnil) break;
|
|
1390
|
+
|
|
1391
|
+
current_trace = next_trace;
|
|
1392
|
+
otel_values = rb_ivar_get(current_trace, at_otel_values_id /* @otel_values */);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
// We found the last trace in the linked list. This contains the local root span
|
|
1396
|
+
VALUE resolved_root_span = rb_ivar_get(current_trace, at_root_span_id /* @root_span */);
|
|
1397
|
+
if (resolved_root_span == Qnil) return;
|
|
1398
|
+
|
|
1399
|
+
*root_span = resolved_root_span;
|
|
1400
|
+
*active_trace = current_trace;
|
|
1401
|
+
*numeric_span_id = resolved_numeric_span_id;
|
|
1402
|
+
}
|