datadog 2.7.1 → 2.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +353 -1
- data/ext/datadog_profiling_native_extension/clock_id.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +78 -102
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +1 -1
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +16 -16
- data/ext/datadog_profiling_native_extension/collectors_stack.c +235 -57
- data/ext/datadog_profiling_native_extension/collectors_stack.h +21 -5
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +376 -156
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +1 -4
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +10 -0
- data/ext/datadog_profiling_native_extension/encoded_profile.c +79 -0
- data/ext/datadog_profiling_native_extension/encoded_profile.h +8 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +14 -8
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +2 -0
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +0 -8
- data/ext/datadog_profiling_native_extension/heap_recorder.c +295 -532
- data/ext/datadog_profiling_native_extension/heap_recorder.h +6 -8
- data/ext/datadog_profiling_native_extension/http_transport.c +64 -98
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +22 -0
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +8 -5
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +69 -1
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +16 -4
- data/ext/datadog_profiling_native_extension/profiling.c +19 -8
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +9 -21
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +2 -10
- data/ext/datadog_profiling_native_extension/stack_recorder.c +231 -181
- data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -2
- data/ext/datadog_profiling_native_extension/time_helpers.h +1 -1
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +47 -0
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +31 -0
- data/ext/libdatadog_api/crashtracker.c +17 -15
- data/ext/libdatadog_api/crashtracker.h +5 -0
- data/ext/libdatadog_api/datadog_ruby_common.c +1 -4
- data/ext/libdatadog_api/datadog_ruby_common.h +10 -0
- data/ext/libdatadog_api/extconf.rb +2 -2
- data/ext/libdatadog_api/init.c +15 -0
- data/ext/libdatadog_api/library_config.c +164 -0
- data/ext/libdatadog_api/library_config.h +25 -0
- data/ext/libdatadog_api/macos_development.md +3 -3
- data/ext/libdatadog_api/process_discovery.c +112 -0
- data/ext/libdatadog_api/process_discovery.h +5 -0
- data/ext/libdatadog_extconf_helpers.rb +2 -2
- data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +89 -0
- data/lib/datadog/appsec/actions_handler.rb +49 -0
- data/lib/datadog/appsec/anonymizer.rb +16 -0
- data/lib/datadog/appsec/api_security/lru_cache.rb +56 -0
- data/lib/datadog/appsec/api_security/route_extractor.rb +65 -0
- data/lib/datadog/appsec/api_security/sampler.rb +59 -0
- data/lib/datadog/appsec/api_security.rb +23 -0
- data/lib/datadog/appsec/assets/waf_rules/README.md +50 -5
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +623 -253
- data/lib/datadog/appsec/assets/waf_rules/strict.json +69 -107
- data/lib/datadog/appsec/autoload.rb +1 -1
- data/lib/datadog/appsec/component.rb +49 -65
- data/lib/datadog/appsec/compressed_json.rb +40 -0
- data/lib/datadog/appsec/configuration/settings.rb +212 -27
- data/lib/datadog/appsec/context.rb +74 -0
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +92 -0
- data/lib/datadog/appsec/contrib/active_record/integration.rb +41 -0
- data/lib/datadog/appsec/contrib/active_record/patcher.rb +101 -0
- data/lib/datadog/appsec/contrib/auto_instrument.rb +1 -1
- data/lib/datadog/appsec/contrib/devise/configuration.rb +52 -0
- data/lib/datadog/appsec/contrib/devise/data_extractor.rb +78 -0
- data/lib/datadog/appsec/contrib/devise/ext.rb +22 -0
- data/lib/datadog/appsec/contrib/devise/integration.rb +1 -2
- data/lib/datadog/appsec/contrib/devise/patcher.rb +33 -25
- data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +102 -0
- data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +69 -0
- data/lib/datadog/appsec/contrib/devise/{patcher/rememberable_patch.rb → patches/skip_signin_tracking_patch.rb} +3 -3
- data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +106 -0
- data/lib/datadog/appsec/contrib/excon/integration.rb +41 -0
- data/lib/datadog/appsec/contrib/excon/patcher.rb +28 -0
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +42 -0
- data/lib/datadog/appsec/contrib/faraday/connection_patch.rb +22 -0
- data/lib/datadog/appsec/contrib/faraday/integration.rb +42 -0
- data/lib/datadog/appsec/contrib/faraday/patcher.rb +53 -0
- data/lib/datadog/appsec/contrib/faraday/rack_builder_patch.rb +22 -0
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +41 -0
- data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +1 -7
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +17 -30
- data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/graphql/patcher.rb +0 -3
- data/lib/datadog/appsec/contrib/rack/ext.rb +34 -0
- data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +78 -98
- data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/patcher.rb +0 -3
- data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +10 -11
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +73 -78
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +16 -33
- data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/patcher.rb +25 -38
- data/lib/datadog/appsec/contrib/rest_client/integration.rb +45 -0
- data/lib/datadog/appsec/contrib/rest_client/patcher.rb +28 -0
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +38 -0
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +31 -68
- data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +5 -31
- data/lib/datadog/appsec/event.rb +96 -135
- data/lib/datadog/appsec/ext.rb +12 -3
- data/lib/datadog/appsec/instrumentation/gateway/argument.rb +7 -2
- data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +24 -0
- data/lib/datadog/appsec/instrumentation/gateway.rb +17 -22
- data/lib/datadog/appsec/metrics/collector.rb +38 -0
- data/lib/datadog/appsec/metrics/exporter.rb +35 -0
- data/lib/datadog/appsec/metrics/telemetry.rb +23 -0
- data/lib/datadog/appsec/metrics.rb +13 -0
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +52 -32
- data/lib/datadog/appsec/processor/rule_loader.rb +30 -36
- data/lib/datadog/appsec/remote.rb +31 -57
- data/lib/datadog/appsec/response.rb +19 -85
- data/lib/datadog/appsec/security_engine/engine.rb +194 -0
- data/lib/datadog/appsec/security_engine/result.rb +67 -0
- data/lib/datadog/appsec/security_engine/runner.rb +87 -0
- data/lib/datadog/appsec/security_engine.rb +9 -0
- data/lib/datadog/appsec/security_event.rb +39 -0
- data/lib/datadog/appsec/utils.rb +0 -2
- data/lib/datadog/appsec.rb +22 -12
- data/lib/datadog/auto_instrument.rb +3 -0
- data/lib/datadog/core/buffer/random.rb +18 -2
- data/lib/datadog/core/configuration/agent_settings.rb +52 -0
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +4 -18
- data/lib/datadog/core/configuration/agentless_settings_resolver.rb +176 -0
- data/lib/datadog/core/configuration/components.rb +74 -32
- data/lib/datadog/core/configuration/components_state.rb +23 -0
- data/lib/datadog/core/configuration/ext.rb +5 -1
- data/lib/datadog/core/configuration/option.rb +81 -45
- data/lib/datadog/core/configuration/option_definition.rb +6 -4
- data/lib/datadog/core/configuration/options.rb +3 -3
- data/lib/datadog/core/configuration/settings.rb +121 -50
- data/lib/datadog/core/configuration/stable_config.rb +22 -0
- data/lib/datadog/core/configuration.rb +43 -11
- data/lib/datadog/{tracing → core}/contrib/rails/utils.rb +1 -3
- data/lib/datadog/core/crashtracking/component.rb +4 -13
- data/lib/datadog/core/crashtracking/tag_builder.rb +4 -22
- data/lib/datadog/core/diagnostics/environment_logger.rb +1 -1
- data/lib/datadog/core/encoding.rb +17 -1
- data/lib/datadog/core/environment/agent_info.rb +78 -0
- data/lib/datadog/core/environment/cgroup.rb +10 -12
- data/lib/datadog/core/environment/container.rb +38 -40
- data/lib/datadog/core/environment/ext.rb +6 -6
- data/lib/datadog/core/environment/git.rb +1 -0
- data/lib/datadog/core/environment/identity.rb +3 -3
- data/lib/datadog/core/environment/platform.rb +3 -3
- data/lib/datadog/core/environment/variable_helpers.rb +1 -1
- data/lib/datadog/core/error.rb +11 -9
- data/lib/datadog/core/logger.rb +2 -2
- data/lib/datadog/core/metrics/client.rb +27 -27
- data/lib/datadog/core/metrics/logging.rb +5 -5
- data/lib/datadog/core/process_discovery/tracer_memfd.rb +15 -0
- data/lib/datadog/core/process_discovery.rb +36 -0
- data/lib/datadog/core/rate_limiter.rb +4 -2
- data/lib/datadog/core/remote/client/capabilities.rb +6 -0
- data/lib/datadog/core/remote/client.rb +107 -92
- data/lib/datadog/core/remote/component.rb +18 -19
- data/lib/datadog/core/remote/configuration/digest.rb +7 -7
- data/lib/datadog/core/remote/configuration/path.rb +1 -1
- data/lib/datadog/core/remote/configuration/repository.rb +14 -1
- data/lib/datadog/core/remote/negotiation.rb +9 -9
- data/lib/datadog/core/remote/transport/config.rb +4 -3
- data/lib/datadog/core/remote/transport/http/api.rb +13 -18
- data/lib/datadog/core/remote/transport/http/client.rb +5 -4
- data/lib/datadog/core/remote/transport/http/config.rb +27 -55
- data/lib/datadog/core/remote/transport/http/negotiation.rb +8 -51
- data/lib/datadog/core/remote/transport/http.rb +25 -94
- data/lib/datadog/core/remote/transport/negotiation.rb +17 -4
- data/lib/datadog/core/remote/worker.rb +10 -7
- data/lib/datadog/core/runtime/metrics.rb +12 -5
- data/lib/datadog/core/tag_builder.rb +56 -0
- data/lib/datadog/core/telemetry/component.rb +84 -49
- data/lib/datadog/core/telemetry/emitter.rb +23 -11
- data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +66 -0
- data/lib/datadog/core/telemetry/event/app_closing.rb +18 -0
- data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +33 -0
- data/lib/datadog/core/telemetry/event/app_heartbeat.rb +18 -0
- data/lib/datadog/core/telemetry/event/app_integrations_change.rb +58 -0
- data/lib/datadog/core/telemetry/event/app_started.rb +269 -0
- data/lib/datadog/core/telemetry/event/base.rb +40 -0
- data/lib/datadog/core/telemetry/event/distributions.rb +18 -0
- data/lib/datadog/core/telemetry/event/generate_metrics.rb +43 -0
- data/lib/datadog/core/telemetry/event/log.rb +76 -0
- data/lib/datadog/core/telemetry/event/message_batch.rb +42 -0
- data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +43 -0
- data/lib/datadog/core/telemetry/event.rb +17 -383
- data/lib/datadog/core/telemetry/ext.rb +1 -0
- data/lib/datadog/core/telemetry/http/adapters/net.rb +12 -97
- data/lib/datadog/core/telemetry/logger.rb +5 -4
- data/lib/datadog/core/telemetry/logging.rb +12 -6
- data/lib/datadog/core/telemetry/metric.rb +28 -6
- data/lib/datadog/core/telemetry/request.rb +4 -4
- data/lib/datadog/core/telemetry/transport/http/api.rb +43 -0
- data/lib/datadog/core/telemetry/transport/http/client.rb +49 -0
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +92 -0
- data/lib/datadog/core/telemetry/transport/http.rb +63 -0
- data/lib/datadog/core/telemetry/transport/telemetry.rb +51 -0
- data/lib/datadog/core/telemetry/worker.rb +128 -25
- data/lib/datadog/core/transport/http/adapters/net.rb +17 -2
- data/lib/datadog/core/transport/http/adapters/test.rb +2 -1
- data/lib/datadog/core/transport/http/adapters/unix_socket.rb +1 -1
- data/lib/datadog/{tracing → core}/transport/http/api/instance.rb +18 -1
- data/lib/datadog/core/transport/http/api/spec.rb +36 -0
- data/lib/datadog/{tracing → core}/transport/http/builder.rb +53 -31
- data/lib/datadog/core/transport/http/env.rb +8 -0
- data/lib/datadog/core/transport/http.rb +75 -0
- data/lib/datadog/core/transport/response.rb +4 -0
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +6 -6
- data/lib/datadog/core/utils/duration.rb +32 -32
- data/lib/datadog/core/utils/forking.rb +2 -2
- data/lib/datadog/core/utils/network.rb +6 -6
- data/lib/datadog/core/utils/only_once_successful.rb +16 -5
- data/lib/datadog/core/utils/time.rb +20 -0
- data/lib/datadog/core/utils/truncation.rb +21 -0
- data/lib/datadog/core/utils.rb +7 -0
- data/lib/datadog/core/vendor/multipart-post/multipart/post/composite_read_io.rb +1 -1
- data/lib/datadog/core/vendor/multipart-post/multipart/post/multipartable.rb +8 -8
- data/lib/datadog/core/vendor/multipart-post/multipart/post/parts.rb +7 -7
- data/lib/datadog/core/worker.rb +1 -1
- data/lib/datadog/core/workers/async.rb +29 -12
- data/lib/datadog/core/workers/interval_loop.rb +12 -1
- data/lib/datadog/core/workers/runtime_metrics.rb +2 -2
- data/lib/datadog/core.rb +8 -0
- data/lib/datadog/di/base.rb +115 -0
- data/lib/datadog/di/boot.rb +34 -0
- data/lib/datadog/di/code_tracker.rb +26 -15
- data/lib/datadog/di/component.rb +23 -14
- data/lib/datadog/di/configuration/settings.rb +25 -1
- data/lib/datadog/di/contrib/active_record.rb +1 -0
- data/lib/datadog/di/contrib/railtie.rb +15 -0
- data/lib/datadog/di/contrib.rb +28 -0
- data/lib/datadog/di/error.rb +5 -0
- data/lib/datadog/di/instrumenter.rb +162 -21
- data/lib/datadog/di/logger.rb +30 -0
- data/lib/datadog/di/preload.rb +18 -0
- data/lib/datadog/di/probe.rb +14 -7
- data/lib/datadog/di/probe_builder.rb +1 -0
- data/lib/datadog/di/probe_manager.rb +11 -5
- data/lib/datadog/di/probe_notification_builder.rb +54 -38
- data/lib/datadog/di/probe_notifier_worker.rb +60 -26
- data/lib/datadog/di/redactor.rb +0 -1
- data/lib/datadog/di/remote.rb +147 -0
- data/lib/datadog/di/serializer.rb +19 -8
- data/lib/datadog/di/transport/diagnostics.rb +62 -0
- data/lib/datadog/di/transport/http/api.rb +42 -0
- data/lib/datadog/di/transport/http/client.rb +47 -0
- data/lib/datadog/di/transport/http/diagnostics.rb +65 -0
- data/lib/datadog/di/transport/http/input.rb +77 -0
- data/lib/datadog/di/transport/http.rb +57 -0
- data/lib/datadog/di/transport/input.rb +70 -0
- data/lib/datadog/di/utils.rb +103 -0
- data/lib/datadog/di.rb +14 -76
- data/lib/datadog/error_tracking/collector.rb +87 -0
- data/lib/datadog/error_tracking/component.rb +167 -0
- data/lib/datadog/error_tracking/configuration/settings.rb +63 -0
- data/lib/datadog/error_tracking/configuration.rb +11 -0
- data/lib/datadog/error_tracking/ext.rb +18 -0
- data/lib/datadog/error_tracking/extensions.rb +16 -0
- data/lib/datadog/error_tracking/filters.rb +77 -0
- data/lib/datadog/error_tracking.rb +18 -0
- data/lib/datadog/kit/appsec/events.rb +15 -3
- data/lib/datadog/kit/identity.rb +9 -5
- data/lib/datadog/opentelemetry/api/baggage.rb +90 -0
- data/lib/datadog/opentelemetry/api/baggage.rbs +26 -0
- data/lib/datadog/opentelemetry/api/context.rb +16 -2
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +1 -1
- data/lib/datadog/opentelemetry.rb +2 -1
- data/lib/datadog/profiling/collectors/code_provenance.rb +18 -9
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +4 -0
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -0
- data/lib/datadog/profiling/collectors/info.rb +3 -0
- data/lib/datadog/profiling/collectors/thread_context.rb +17 -2
- data/lib/datadog/profiling/component.rb +64 -82
- data/lib/datadog/profiling/encoded_profile.rb +11 -0
- data/lib/datadog/profiling/exporter.rb +3 -4
- data/lib/datadog/profiling/ext.rb +0 -14
- data/lib/datadog/profiling/flush.rb +5 -8
- data/lib/datadog/profiling/http_transport.rb +8 -87
- data/lib/datadog/profiling/load_native_extension.rb +1 -33
- data/lib/datadog/profiling/profiler.rb +2 -0
- data/lib/datadog/profiling/scheduler.rb +10 -2
- data/lib/datadog/profiling/stack_recorder.rb +9 -9
- data/lib/datadog/profiling/tag_builder.rb +5 -41
- data/lib/datadog/profiling/tasks/setup.rb +2 -0
- data/lib/datadog/profiling.rb +6 -2
- data/lib/datadog/tracing/analytics.rb +1 -1
- data/lib/datadog/tracing/component.rb +16 -12
- data/lib/datadog/tracing/configuration/ext.rb +8 -1
- data/lib/datadog/tracing/configuration/settings.rb +22 -10
- data/lib/datadog/tracing/context_provider.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/action_mailer/integration.rb +6 -2
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +15 -0
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +19 -12
- data/lib/datadog/tracing/contrib/action_pack/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/action_pack/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/action_view/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/active_job/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/active_record/integration.rb +7 -3
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +7 -2
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +36 -1
- data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +4 -0
- data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +14 -4
- data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +10 -0
- data/lib/datadog/tracing/contrib/active_support/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/auto_instrument.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +10 -0
- data/lib/datadog/tracing/contrib/aws/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/aws/parsed_context.rb +5 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +4 -0
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -5
- data/lib/datadog/tracing/contrib/excon/middleware.rb +5 -3
- data/lib/datadog/tracing/contrib/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/extensions.rb +29 -3
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +5 -3
- data/lib/datadog/tracing/contrib/graphql/configuration/error_extension_env_parser.rb +21 -0
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +11 -0
- data/lib/datadog/tracing/contrib/graphql/ext.rb +5 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +102 -11
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +7 -1
- data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +3 -0
- data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +0 -15
- data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +4 -1
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +6 -10
- data/lib/datadog/tracing/contrib/http/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -16
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +7 -15
- data/lib/datadog/tracing/contrib/httprb/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/kafka/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +27 -0
- data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +48 -0
- data/lib/datadog/tracing/contrib/karafka/ext.rb +27 -0
- data/lib/datadog/tracing/contrib/karafka/integration.rb +45 -0
- data/lib/datadog/tracing/contrib/karafka/monitor.rb +66 -0
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +71 -0
- data/lib/datadog/tracing/contrib/karafka.rb +37 -0
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +4 -2
- data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +8 -0
- data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mongodb/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +18 -1
- data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +17 -0
- data/lib/datadog/tracing/contrib/opensearch/ext.rb +9 -0
- data/lib/datadog/tracing/contrib/opensearch/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +5 -1
- data/lib/datadog/tracing/contrib/patcher.rb +5 -2
- data/lib/datadog/tracing/contrib/presto/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/rack/header_collection.rb +11 -1
- data/lib/datadog/tracing/contrib/rack/integration.rb +2 -2
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +1 -1
- data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
- data/lib/datadog/tracing/contrib/rails/framework.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/rest_client/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +5 -3
- data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +6 -1
- data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +3 -0
- data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +5 -2
- data/lib/datadog/tracing/contrib/span_attribute_schema.rb +6 -1
- data/lib/datadog/tracing/contrib/support.rb +28 -0
- data/lib/datadog/tracing/contrib.rb +1 -0
- data/lib/datadog/tracing/correlation.rb +9 -2
- data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
- data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
- data/lib/datadog/tracing/distributed/baggage.rb +131 -0
- data/lib/datadog/tracing/distributed/datadog.rb +4 -2
- data/lib/datadog/tracing/distributed/propagation.rb +25 -4
- data/lib/datadog/tracing/distributed/propagation_policy.rb +42 -0
- data/lib/datadog/tracing/metadata/errors.rb +4 -4
- data/lib/datadog/tracing/metadata/ext.rb +5 -0
- data/lib/datadog/tracing/metadata/metastruct.rb +36 -0
- data/lib/datadog/tracing/metadata/metastruct_tagging.rb +42 -0
- data/lib/datadog/tracing/metadata.rb +2 -0
- data/lib/datadog/tracing/sampling/rate_sampler.rb +2 -1
- data/lib/datadog/tracing/sampling/span/rule.rb +0 -1
- data/lib/datadog/tracing/span.rb +22 -5
- data/lib/datadog/tracing/span_event.rb +124 -4
- data/lib/datadog/tracing/span_operation.rb +52 -16
- data/lib/datadog/tracing/sync_writer.rb +10 -6
- data/lib/datadog/tracing/trace_digest.rb +9 -2
- data/lib/datadog/tracing/trace_operation.rb +55 -27
- data/lib/datadog/tracing/trace_segment.rb +6 -4
- data/lib/datadog/tracing/tracer.rb +66 -14
- data/lib/datadog/tracing/transport/http/api.rb +5 -4
- data/lib/datadog/tracing/transport/http/client.rb +5 -4
- data/lib/datadog/tracing/transport/http/traces.rb +13 -44
- data/lib/datadog/tracing/transport/http.rb +13 -70
- data/lib/datadog/tracing/transport/serializable_trace.rb +31 -7
- data/lib/datadog/tracing/transport/trace_formatter.rb +7 -0
- data/lib/datadog/tracing/transport/traces.rb +47 -13
- data/lib/datadog/tracing/utils.rb +1 -1
- data/lib/datadog/tracing/workers/trace_writer.rb +8 -5
- data/lib/datadog/tracing/workers.rb +5 -4
- data/lib/datadog/tracing/writer.rb +10 -6
- data/lib/datadog/tracing.rb +16 -3
- data/lib/datadog/version.rb +2 -2
- data/lib/datadog.rb +2 -0
- metadata +149 -54
- data/ext/datadog_profiling_loader/datadog_profiling_loader.c +0 -142
- data/ext/datadog_profiling_loader/extconf.rb +0 -60
- data/lib/datadog/appsec/assets/waf_rules/processors.json +0 -92
- data/lib/datadog/appsec/assets/waf_rules/scanners.json +0 -114
- data/lib/datadog/appsec/contrib/devise/event.rb +0 -57
- data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +0 -77
- data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +0 -54
- data/lib/datadog/appsec/contrib/devise/resource.rb +0 -35
- data/lib/datadog/appsec/contrib/devise/tracking.rb +0 -57
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +0 -46
- data/lib/datadog/appsec/contrib/patcher.rb +0 -12
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +0 -69
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +0 -47
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +0 -53
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +0 -53
- data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -14
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +0 -48
- data/lib/datadog/appsec/monitor/reactive/set_user.rb +0 -45
- data/lib/datadog/appsec/processor/actions.rb +0 -49
- data/lib/datadog/appsec/processor/context.rb +0 -107
- data/lib/datadog/appsec/processor/rule_merger.rb +0 -170
- data/lib/datadog/appsec/processor.rb +0 -106
- data/lib/datadog/appsec/reactive/address_hash.rb +0 -22
- data/lib/datadog/appsec/reactive/engine.rb +0 -47
- data/lib/datadog/appsec/reactive/operation.rb +0 -68
- data/lib/datadog/appsec/reactive/subscriber.rb +0 -19
- data/lib/datadog/appsec/scope.rb +0 -58
- data/lib/datadog/appsec/utils/trace_operation.rb +0 -15
- data/lib/datadog/core/crashtracking/agent_base_url.rb +0 -21
- data/lib/datadog/core/remote/transport/http/api/instance.rb +0 -39
- data/lib/datadog/core/remote/transport/http/api/spec.rb +0 -21
- data/lib/datadog/core/remote/transport/http/builder.rb +0 -219
- data/lib/datadog/core/telemetry/http/env.rb +0 -20
- data/lib/datadog/core/telemetry/http/ext.rb +0 -28
- data/lib/datadog/core/telemetry/http/response.rb +0 -70
- data/lib/datadog/core/telemetry/http/transport.rb +0 -90
- data/lib/datadog/di/transport.rb +0 -81
- data/lib/datadog/tracing/transport/http/api/spec.rb +0 -19
@@ -1,5 +1,6 @@
|
|
1
1
|
#include <ruby.h>
|
2
2
|
|
3
|
+
#include "datadog_ruby_common.h"
|
3
4
|
#include "collectors_thread_context.h"
|
4
5
|
#include "clock_id.h"
|
5
6
|
#include "collectors_stack.h"
|
@@ -9,6 +10,8 @@
|
|
9
10
|
#include "private_vm_api_access.h"
|
10
11
|
#include "stack_recorder.h"
|
11
12
|
#include "time_helpers.h"
|
13
|
+
#include "unsafe_api_calls_check.h"
|
14
|
+
#include "extconf.h"
|
12
15
|
|
13
16
|
// Used to trigger sampling of threads, based on external "events", such as:
|
14
17
|
// * periodic timer for cpu-time and wall-time
|
@@ -101,6 +104,7 @@ static ID at_kind_id; // id of :@kind in Ruby
|
|
101
104
|
static ID at_name_id; // id of :@name in Ruby
|
102
105
|
static ID server_id; // id of :server in Ruby
|
103
106
|
static ID otel_context_storage_id; // id of :__opentelemetry_context_storage__ in Ruby
|
107
|
+
static ID otel_fiber_context_storage_id; // id of :@opentelemetry_context in Ruby
|
104
108
|
|
105
109
|
// This is used by `thread_context_collector_on_gvl_running`. Because when that method gets called we're not sure if
|
106
110
|
// it's safe to access the state of the thread context collector, we store this setting as a global value. This does
|
@@ -110,16 +114,18 @@ static ID otel_context_storage_id; // id of :__opentelemetry_context_storage__ i
|
|
110
114
|
static uint32_t global_waiting_for_gvl_threshold_ns = MILLIS_AS_NS(10);
|
111
115
|
|
112
116
|
typedef enum { OTEL_CONTEXT_ENABLED_FALSE, OTEL_CONTEXT_ENABLED_ONLY, OTEL_CONTEXT_ENABLED_BOTH } otel_context_enabled;
|
117
|
+
typedef enum { OTEL_CONTEXT_SOURCE_UNKNOWN, OTEL_CONTEXT_SOURCE_FIBER_IVAR, OTEL_CONTEXT_SOURCE_FIBER_LOCAL } otel_context_source;
|
113
118
|
|
114
119
|
// Contains state for a single ThreadContext instance
|
115
|
-
struct
|
120
|
+
typedef struct {
|
116
121
|
// Note: Places in this file that usually need to be changed when this struct is changed are tagged with
|
117
122
|
// "Update this when modifying state struct"
|
118
123
|
|
119
124
|
// Required by Datadog::Profiling::Collectors::Stack as a scratch buffer during sampling
|
120
125
|
ddog_prof_Location *locations;
|
121
126
|
uint16_t max_frames;
|
122
|
-
// Hashmap <Thread Object,
|
127
|
+
// Hashmap <Thread Object, per_thread_context>
|
128
|
+
// Note: Be very careful when mutating this map, as it gets read e.g. in the middle of GC and signal handlers.
|
123
129
|
st_table *hash_map_per_thread_context;
|
124
130
|
// Datadog::Profiling::StackRecorder instance
|
125
131
|
VALUE recorder_instance;
|
@@ -139,6 +145,8 @@ struct thread_context_collector_state {
|
|
139
145
|
bool timeline_enabled;
|
140
146
|
// Used to control context collection
|
141
147
|
otel_context_enabled otel_context_enabled;
|
148
|
+
// Used to remember where otel context is being stored after we observe it the first time
|
149
|
+
otel_context_source otel_context_source;
|
142
150
|
// Used when calling monotonic_to_system_epoch_ns
|
143
151
|
monotonic_to_system_epoch_state time_converter_state;
|
144
152
|
// Used to identify the main thread, to give it a fallback name
|
@@ -147,6 +155,10 @@ struct thread_context_collector_state {
|
|
147
155
|
// Qtrue serves as a marker we've not yet extracted it; when we try to extract it, we set it to an object if
|
148
156
|
// successful and Qnil if not.
|
149
157
|
VALUE otel_current_span_key;
|
158
|
+
// Used to enable native filenames in stack traces
|
159
|
+
bool native_filenames_enabled;
|
160
|
+
// Used to cache native filename lookup results (Map[void *function_pointer, char *filename])
|
161
|
+
st_table *native_filenames_cache;
|
150
162
|
|
151
163
|
struct stats {
|
152
164
|
// Track how many garbage collection samples we've taken.
|
@@ -162,11 +174,11 @@ struct thread_context_collector_state {
|
|
162
174
|
long wall_time_at_previous_gc_ns; // Will be INVALID_TIME unless there's accumulated time above
|
163
175
|
long wall_time_at_last_flushed_gc_event_ns; // Starts at 0 and then will always be valid
|
164
176
|
} gc_tracking;
|
165
|
-
};
|
177
|
+
} thread_context_collector_state;
|
166
178
|
|
167
179
|
// Tracks per-thread state
|
168
|
-
struct
|
169
|
-
sampling_buffer
|
180
|
+
typedef struct {
|
181
|
+
sampling_buffer sampling_buffer;
|
170
182
|
char thread_id[THREAD_ID_LIMIT_CHARS];
|
171
183
|
ddog_CharSlice thread_id_char_slice;
|
172
184
|
char thread_invoke_location[THREAD_INVOKE_LOCATION_LIMIT_CHARS];
|
@@ -181,92 +193,99 @@ struct per_thread_context {
|
|
181
193
|
long cpu_time_at_start_ns;
|
182
194
|
long wall_time_at_start_ns;
|
183
195
|
} gc_tracking;
|
184
|
-
};
|
196
|
+
} per_thread_context;
|
185
197
|
|
186
198
|
// Used to correlate profiles with traces
|
187
|
-
struct
|
199
|
+
typedef struct {
|
188
200
|
bool valid;
|
189
201
|
uint64_t local_root_span_id;
|
190
202
|
uint64_t span_id;
|
191
203
|
VALUE trace_endpoint;
|
192
|
-
};
|
204
|
+
} trace_identifiers;
|
193
205
|
|
194
|
-
struct
|
206
|
+
typedef struct {
|
195
207
|
VALUE span;
|
196
208
|
VALUE span_id;
|
197
209
|
VALUE trace_id;
|
198
|
-
};
|
210
|
+
} otel_span;
|
199
211
|
|
200
212
|
static void thread_context_collector_typed_data_mark(void *state_ptr);
|
201
213
|
static void thread_context_collector_typed_data_free(void *state_ptr);
|
202
|
-
static int hash_map_per_thread_context_mark(st_data_t key_thread, st_data_t
|
214
|
+
static int hash_map_per_thread_context_mark(st_data_t key_thread, st_data_t value_thread_context, DDTRACE_UNUSED st_data_t _argument);
|
203
215
|
static int hash_map_per_thread_context_free_values(st_data_t _thread, st_data_t value_per_thread_context, st_data_t _argument);
|
204
216
|
static VALUE _native_new(VALUE klass);
|
205
217
|
static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
|
206
|
-
static VALUE _native_sample(VALUE self, VALUE collector_instance, VALUE profiler_overhead_stack_thread);
|
218
|
+
static VALUE _native_sample(VALUE self, VALUE collector_instance, VALUE profiler_overhead_stack_thread, VALUE allow_exception);
|
207
219
|
static VALUE _native_on_gc_start(VALUE self, VALUE collector_instance);
|
208
220
|
static VALUE _native_on_gc_finish(VALUE self, VALUE collector_instance);
|
209
|
-
static VALUE _native_sample_after_gc(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE
|
221
|
+
static VALUE _native_sample_after_gc(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE allow_exception);
|
210
222
|
static void update_metrics_and_sample(
|
211
|
-
|
223
|
+
thread_context_collector_state *state,
|
212
224
|
VALUE thread_being_sampled,
|
213
225
|
VALUE stack_from_thread,
|
214
|
-
|
226
|
+
per_thread_context *thread_context,
|
215
227
|
sampling_buffer* sampling_buffer,
|
216
228
|
long current_cpu_time_ns,
|
217
229
|
long current_monotonic_wall_time_ns
|
218
230
|
);
|
219
231
|
static void trigger_sample_for_thread(
|
220
|
-
|
232
|
+
thread_context_collector_state *state,
|
221
233
|
VALUE thread,
|
222
234
|
VALUE stack_from_thread,
|
223
|
-
|
235
|
+
per_thread_context *thread_context,
|
224
236
|
sampling_buffer* sampling_buffer,
|
225
237
|
sample_values values,
|
226
238
|
long current_monotonic_wall_time_ns,
|
227
239
|
ddog_CharSlice *ruby_vm_type,
|
228
240
|
ddog_CharSlice *class_name,
|
229
|
-
bool is_gvl_waiting_state
|
241
|
+
bool is_gvl_waiting_state,
|
242
|
+
bool is_safe_to_allocate_objects
|
230
243
|
);
|
231
244
|
static VALUE _native_thread_list(VALUE self);
|
232
|
-
static
|
233
|
-
static
|
234
|
-
static void initialize_context(VALUE thread,
|
235
|
-
static void free_context(
|
245
|
+
static per_thread_context *get_or_create_context_for(VALUE thread, thread_context_collector_state *state);
|
246
|
+
static per_thread_context *get_context_for(VALUE thread, thread_context_collector_state *state);
|
247
|
+
static void initialize_context(VALUE thread, per_thread_context *thread_context, thread_context_collector_state *state);
|
248
|
+
static void free_context(per_thread_context* thread_context);
|
236
249
|
static VALUE _native_inspect(VALUE self, VALUE collector_instance);
|
237
|
-
static VALUE per_thread_context_st_table_as_ruby_hash(
|
250
|
+
static VALUE per_thread_context_st_table_as_ruby_hash(thread_context_collector_state *state);
|
238
251
|
static int per_thread_context_as_ruby_hash(st_data_t key_thread, st_data_t value_context, st_data_t result_hash);
|
239
|
-
static VALUE stats_as_ruby_hash(
|
240
|
-
static VALUE gc_tracking_as_ruby_hash(
|
241
|
-
static void remove_context_for_dead_threads(
|
252
|
+
static VALUE stats_as_ruby_hash(thread_context_collector_state *state);
|
253
|
+
static VALUE gc_tracking_as_ruby_hash(thread_context_collector_state *state);
|
254
|
+
static void remove_context_for_dead_threads(thread_context_collector_state *state);
|
242
255
|
static int remove_if_dead_thread(st_data_t key_thread, st_data_t value_context, st_data_t _argument);
|
243
256
|
static VALUE _native_per_thread_context(VALUE self, VALUE collector_instance);
|
244
257
|
static long update_time_since_previous_sample(long *time_at_previous_sample_ns, long current_time_ns, long gc_start_time_ns, bool is_wall_time);
|
245
|
-
static long cpu_time_now_ns(
|
258
|
+
static long cpu_time_now_ns(per_thread_context *thread_context);
|
246
259
|
static long thread_id_for(VALUE thread);
|
247
260
|
static VALUE _native_stats(VALUE self, VALUE collector_instance);
|
248
261
|
static VALUE _native_gc_tracking(VALUE self, VALUE collector_instance);
|
249
|
-
static void trace_identifiers_for(
|
262
|
+
static void trace_identifiers_for(
|
263
|
+
thread_context_collector_state *state,
|
264
|
+
VALUE thread,
|
265
|
+
trace_identifiers *trace_identifiers_result,
|
266
|
+
bool is_safe_to_allocate_objects
|
267
|
+
);
|
250
268
|
static bool should_collect_resource(VALUE root_span);
|
251
269
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
252
|
-
static VALUE thread_list(
|
270
|
+
static VALUE thread_list(thread_context_collector_state *state);
|
253
271
|
static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object);
|
254
272
|
static VALUE _native_new_empty_thread(VALUE self);
|
255
273
|
static ddog_CharSlice ruby_value_type_to_class_name(enum ruby_value_type type);
|
256
274
|
static void ddtrace_otel_trace_identifiers_for(
|
257
|
-
|
275
|
+
thread_context_collector_state *state,
|
258
276
|
VALUE *active_trace,
|
259
277
|
VALUE *root_span,
|
260
278
|
VALUE *numeric_span_id,
|
261
279
|
VALUE active_span,
|
262
|
-
VALUE otel_values
|
280
|
+
VALUE otel_values,
|
281
|
+
bool is_safe_to_allocate_objects
|
263
282
|
);
|
264
283
|
static VALUE _native_sample_skipped_allocation_samples(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE skipped_samples);
|
265
284
|
static bool handle_gvl_waiting(
|
266
|
-
|
285
|
+
thread_context_collector_state *state,
|
267
286
|
VALUE thread_being_sampled,
|
268
287
|
VALUE stack_from_thread,
|
269
|
-
|
288
|
+
per_thread_context *thread_context,
|
270
289
|
sampling_buffer* sampling_buffer,
|
271
290
|
long current_cpu_time_ns
|
272
291
|
);
|
@@ -276,12 +295,16 @@ static VALUE _native_on_gvl_running(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
276
295
|
static VALUE _native_sample_after_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread);
|
277
296
|
static VALUE _native_apply_delta_to_cpu_time_at_previous_sample_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread, VALUE delta_ns);
|
278
297
|
static void otel_without_ddtrace_trace_identifiers_for(
|
279
|
-
|
298
|
+
thread_context_collector_state *state,
|
280
299
|
VALUE thread,
|
281
|
-
|
300
|
+
trace_identifiers *trace_identifiers_result,
|
301
|
+
bool is_safe_to_allocate_objects
|
282
302
|
);
|
283
|
-
static
|
303
|
+
static otel_span otel_span_from(VALUE otel_context, VALUE otel_current_span_key);
|
284
304
|
static uint64_t otel_span_id_to_uint(VALUE otel_span_id);
|
305
|
+
static VALUE safely_lookup_hash_without_going_into_ruby_code(VALUE hash, VALUE key);
|
306
|
+
static VALUE _native_system_epoch_time_now_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
307
|
+
static VALUE _native_prepare_sample_inside_signal_handler(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
285
308
|
|
286
309
|
void collectors_thread_context_init(VALUE profiling_module) {
|
287
310
|
VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
|
@@ -302,7 +325,7 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
302
325
|
rb_define_singleton_method(collectors_thread_context_class, "_native_initialize", _native_initialize, -1);
|
303
326
|
rb_define_singleton_method(collectors_thread_context_class, "_native_inspect", _native_inspect, 1);
|
304
327
|
rb_define_singleton_method(collectors_thread_context_class, "_native_reset_after_fork", _native_reset_after_fork, 1);
|
305
|
-
rb_define_singleton_method(testing_module, "_native_sample", _native_sample,
|
328
|
+
rb_define_singleton_method(testing_module, "_native_sample", _native_sample, 3);
|
306
329
|
rb_define_singleton_method(testing_module, "_native_sample_allocation", _native_sample_allocation, 3);
|
307
330
|
rb_define_singleton_method(testing_module, "_native_on_gc_start", _native_on_gc_start, 1);
|
308
331
|
rb_define_singleton_method(testing_module, "_native_on_gc_finish", _native_on_gc_finish, 1);
|
@@ -313,6 +336,8 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
313
336
|
rb_define_singleton_method(testing_module, "_native_gc_tracking", _native_gc_tracking, 1);
|
314
337
|
rb_define_singleton_method(testing_module, "_native_new_empty_thread", _native_new_empty_thread, 0);
|
315
338
|
rb_define_singleton_method(testing_module, "_native_sample_skipped_allocation_samples", _native_sample_skipped_allocation_samples, 2);
|
339
|
+
rb_define_singleton_method(testing_module, "_native_system_epoch_time_now_ns", _native_system_epoch_time_now_ns, 1);
|
340
|
+
rb_define_singleton_method(testing_module, "_native_prepare_sample_inside_signal_handler", _native_prepare_sample_inside_signal_handler, 1);
|
316
341
|
#ifndef NO_GVL_INSTRUMENTATION
|
317
342
|
rb_define_singleton_method(testing_module, "_native_on_gvl_waiting", _native_on_gvl_waiting, 1);
|
318
343
|
rb_define_singleton_method(testing_module, "_native_gvl_waiting_at_for", _native_gvl_waiting_at_for, 1);
|
@@ -338,6 +363,7 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
338
363
|
at_name_id = rb_intern_const("@name");
|
339
364
|
server_id = rb_intern_const("server");
|
340
365
|
otel_context_storage_id = rb_intern_const("__opentelemetry_context_storage__");
|
366
|
+
otel_fiber_context_storage_id = rb_intern_const("@opentelemetry_context");
|
341
367
|
|
342
368
|
#ifndef NO_GVL_INSTRUMENTATION
|
343
369
|
// This will raise if Ruby already ran out of thread-local keys
|
@@ -347,7 +373,7 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
347
373
|
gc_profiling_init();
|
348
374
|
}
|
349
375
|
|
350
|
-
// This structure is used to define a Ruby object that stores a pointer to a
|
376
|
+
// This structure is used to define a Ruby object that stores a pointer to a thread_context_collector_state
|
351
377
|
// See also https://github.com/ruby/ruby/blob/master/doc/extension.rdoc for how this works
|
352
378
|
static const rb_data_type_t thread_context_collector_typed_data = {
|
353
379
|
.wrap_struct_name = "Datadog::Profiling::Collectors::ThreadContext",
|
@@ -363,7 +389,7 @@ static const rb_data_type_t thread_context_collector_typed_data = {
|
|
363
389
|
// This function is called by the Ruby GC to give us a chance to mark any Ruby objects that we're holding on to,
|
364
390
|
// so that they don't get garbage collected
|
365
391
|
static void thread_context_collector_typed_data_mark(void *state_ptr) {
|
366
|
-
|
392
|
+
thread_context_collector_state *state = (thread_context_collector_state *) state_ptr;
|
367
393
|
|
368
394
|
// Update this when modifying state struct
|
369
395
|
rb_gc_mark(state->recorder_instance);
|
@@ -374,7 +400,7 @@ static void thread_context_collector_typed_data_mark(void *state_ptr) {
|
|
374
400
|
}
|
375
401
|
|
376
402
|
static void thread_context_collector_typed_data_free(void *state_ptr) {
|
377
|
-
|
403
|
+
thread_context_collector_state *state = (thread_context_collector_state *) state_ptr;
|
378
404
|
|
379
405
|
// Update this when modifying state struct
|
380
406
|
|
@@ -387,25 +413,33 @@ static void thread_context_collector_typed_data_free(void *state_ptr) {
|
|
387
413
|
// ...and then the map
|
388
414
|
st_free_table(state->hash_map_per_thread_context);
|
389
415
|
|
416
|
+
st_free_table(state->native_filenames_cache);
|
417
|
+
|
390
418
|
ruby_xfree(state);
|
391
419
|
}
|
392
420
|
|
393
421
|
// Mark Ruby thread references we keep as keys in hash_map_per_thread_context
|
394
|
-
static int hash_map_per_thread_context_mark(st_data_t key_thread,
|
422
|
+
static int hash_map_per_thread_context_mark(st_data_t key_thread, st_data_t value_thread_context, DDTRACE_UNUSED st_data_t _argument) {
|
395
423
|
VALUE thread = (VALUE) key_thread;
|
424
|
+
per_thread_context *thread_context = (per_thread_context *) value_thread_context;
|
425
|
+
|
396
426
|
rb_gc_mark(thread);
|
427
|
+
if (sampling_buffer_needs_marking(&thread_context->sampling_buffer)) {
|
428
|
+
sampling_buffer_mark(&thread_context->sampling_buffer);
|
429
|
+
}
|
430
|
+
|
397
431
|
return ST_CONTINUE;
|
398
432
|
}
|
399
433
|
|
400
434
|
// Used to clear each of the per_thread_contexts inside the hash_map_per_thread_context
|
401
435
|
static int hash_map_per_thread_context_free_values(DDTRACE_UNUSED st_data_t _thread, st_data_t value_per_thread_context, DDTRACE_UNUSED st_data_t _argument) {
|
402
|
-
|
436
|
+
per_thread_context *thread_context = (per_thread_context*) value_per_thread_context;
|
403
437
|
free_context(thread_context);
|
404
438
|
return ST_CONTINUE;
|
405
439
|
}
|
406
440
|
|
407
441
|
static VALUE _native_new(VALUE klass) {
|
408
|
-
|
442
|
+
thread_context_collector_state *state = ruby_xcalloc(1, sizeof(thread_context_collector_state));
|
409
443
|
|
410
444
|
// Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
|
411
445
|
// being leaked.
|
@@ -422,7 +456,10 @@ static VALUE _native_new(VALUE klass) {
|
|
422
456
|
state->thread_list_buffer = thread_list_buffer;
|
423
457
|
state->endpoint_collection_enabled = true;
|
424
458
|
state->timeline_enabled = true;
|
459
|
+
state->native_filenames_enabled = false;
|
460
|
+
state->native_filenames_cache = st_init_numtable();
|
425
461
|
state->otel_context_enabled = OTEL_CONTEXT_ENABLED_FALSE;
|
462
|
+
state->otel_context_source = OTEL_CONTEXT_SOURCE_UNKNOWN;
|
426
463
|
state->time_converter_state = (monotonic_to_system_epoch_state) MONOTONIC_TO_SYSTEM_EPOCH_INITIALIZER;
|
427
464
|
VALUE main_thread = rb_thread_main();
|
428
465
|
state->main_thread = main_thread;
|
@@ -455,14 +492,16 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
455
492
|
VALUE timeline_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("timeline_enabled")));
|
456
493
|
VALUE waiting_for_gvl_threshold_ns = rb_hash_fetch(options, ID2SYM(rb_intern("waiting_for_gvl_threshold_ns")));
|
457
494
|
VALUE otel_context_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("otel_context_enabled")));
|
495
|
+
VALUE native_filenames_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("native_filenames_enabled")));
|
458
496
|
|
459
497
|
ENFORCE_TYPE(max_frames, T_FIXNUM);
|
460
498
|
ENFORCE_BOOLEAN(endpoint_collection_enabled);
|
461
499
|
ENFORCE_BOOLEAN(timeline_enabled);
|
462
500
|
ENFORCE_TYPE(waiting_for_gvl_threshold_ns, T_FIXNUM);
|
501
|
+
ENFORCE_BOOLEAN(native_filenames_enabled);
|
463
502
|
|
464
|
-
|
465
|
-
TypedData_Get_Struct(self_instance,
|
503
|
+
thread_context_collector_state *state;
|
504
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
466
505
|
|
467
506
|
// Update this when modifying state struct
|
468
507
|
state->max_frames = sampling_buffer_check_max_frames(NUM2INT(max_frames));
|
@@ -471,6 +510,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
471
510
|
state->recorder_instance = enforce_recorder_instance(recorder_instance);
|
472
511
|
state->endpoint_collection_enabled = (endpoint_collection_enabled == Qtrue);
|
473
512
|
state->timeline_enabled = (timeline_enabled == Qtrue);
|
513
|
+
state->native_filenames_enabled = (native_filenames_enabled == Qtrue);
|
474
514
|
if (otel_context_enabled == Qfalse || otel_context_enabled == Qnil) {
|
475
515
|
state->otel_context_enabled = OTEL_CONTEXT_ENABLED_FALSE;
|
476
516
|
} else if (otel_context_enabled == ID2SYM(rb_intern("only"))) {
|
@@ -496,40 +536,53 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
496
536
|
|
497
537
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
498
538
|
// It SHOULD NOT be used for other purposes.
|
499
|
-
static VALUE _native_sample(DDTRACE_UNUSED VALUE _self, VALUE collector_instance, VALUE profiler_overhead_stack_thread) {
|
539
|
+
static VALUE _native_sample(DDTRACE_UNUSED VALUE _self, VALUE collector_instance, VALUE profiler_overhead_stack_thread, VALUE allow_exception) {
|
540
|
+
ENFORCE_BOOLEAN(allow_exception);
|
541
|
+
|
500
542
|
if (!is_thread_alive(profiler_overhead_stack_thread)) rb_raise(rb_eArgError, "Unexpected: profiler_overhead_stack_thread is not alive");
|
501
543
|
|
544
|
+
if (allow_exception == Qfalse) debug_enter_unsafe_context();
|
545
|
+
|
502
546
|
thread_context_collector_sample(collector_instance, monotonic_wall_time_now_ns(RAISE_ON_FAILURE), profiler_overhead_stack_thread);
|
547
|
+
|
548
|
+
if (allow_exception == Qfalse) debug_leave_unsafe_context();
|
549
|
+
|
503
550
|
return Qtrue;
|
504
551
|
}
|
505
552
|
|
506
553
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
507
554
|
// It SHOULD NOT be used for other purposes.
|
508
555
|
static VALUE _native_on_gc_start(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
|
556
|
+
debug_enter_unsafe_context();
|
557
|
+
|
509
558
|
thread_context_collector_on_gc_start(collector_instance);
|
559
|
+
|
560
|
+
debug_leave_unsafe_context();
|
561
|
+
|
510
562
|
return Qtrue;
|
511
563
|
}
|
512
564
|
|
513
565
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
514
566
|
// It SHOULD NOT be used for other purposes.
|
515
567
|
static VALUE _native_on_gc_finish(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
|
568
|
+
debug_enter_unsafe_context();
|
569
|
+
|
516
570
|
(void) !thread_context_collector_on_gc_finish(collector_instance);
|
571
|
+
|
572
|
+
debug_leave_unsafe_context();
|
573
|
+
|
517
574
|
return Qtrue;
|
518
575
|
}
|
519
576
|
|
520
|
-
|
521
|
-
|
522
|
-
static VALUE _native_sample_after_gc(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE reset_monotonic_to_system_state) {
|
523
|
-
ENFORCE_BOOLEAN(reset_monotonic_to_system_state);
|
524
|
-
|
525
|
-
struct thread_context_collector_state *state;
|
526
|
-
TypedData_Get_Struct(collector_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
|
577
|
+
static VALUE _native_sample_after_gc(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE allow_exception) {
|
578
|
+
ENFORCE_BOOLEAN(allow_exception);
|
527
579
|
|
528
|
-
if (
|
529
|
-
state->time_converter_state = (monotonic_to_system_epoch_state) MONOTONIC_TO_SYSTEM_EPOCH_INITIALIZER;
|
530
|
-
}
|
580
|
+
if (allow_exception == Qfalse) debug_enter_unsafe_context();
|
531
581
|
|
532
582
|
thread_context_collector_sample_after_gc(collector_instance);
|
583
|
+
|
584
|
+
if (allow_exception == Qfalse) debug_leave_unsafe_context();
|
585
|
+
|
533
586
|
return Qtrue;
|
534
587
|
}
|
535
588
|
|
@@ -544,11 +597,11 @@ static VALUE _native_sample_after_gc(DDTRACE_UNUSED VALUE self, VALUE collector_
|
|
544
597
|
// The `profiler_overhead_stack_thread` is used to attribute the profiler overhead to a stack borrowed from a different thread
|
545
598
|
// (belonging to ddtrace), so that the overhead is visible in the profile rather than blamed on user code.
|
546
599
|
void thread_context_collector_sample(VALUE self_instance, long current_monotonic_wall_time_ns, VALUE profiler_overhead_stack_thread) {
|
547
|
-
|
548
|
-
TypedData_Get_Struct(self_instance,
|
600
|
+
thread_context_collector_state *state;
|
601
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
549
602
|
|
550
603
|
VALUE current_thread = rb_thread_current();
|
551
|
-
|
604
|
+
per_thread_context *current_thread_context = get_or_create_context_for(current_thread, state);
|
552
605
|
long cpu_time_at_sample_start_for_current_thread = cpu_time_now_ns(current_thread_context);
|
553
606
|
|
554
607
|
VALUE threads = thread_list(state);
|
@@ -556,7 +609,7 @@ void thread_context_collector_sample(VALUE self_instance, long current_monotonic
|
|
556
609
|
const long thread_count = RARRAY_LEN(threads);
|
557
610
|
for (long i = 0; i < thread_count; i++) {
|
558
611
|
VALUE thread = RARRAY_AREF(threads, i);
|
559
|
-
|
612
|
+
per_thread_context *thread_context = get_or_create_context_for(thread, state);
|
560
613
|
|
561
614
|
// We account for cpu-time for the current thread in a different way -- we use the cpu-time at sampling start, to avoid
|
562
615
|
// blaming the time the profiler took on whatever's running on the thread right now
|
@@ -567,7 +620,7 @@ void thread_context_collector_sample(VALUE self_instance, long current_monotonic
|
|
567
620
|
/* thread_being_sampled: */ thread,
|
568
621
|
/* stack_from_thread: */ thread,
|
569
622
|
thread_context,
|
570
|
-
thread_context->sampling_buffer,
|
623
|
+
&thread_context->sampling_buffer,
|
571
624
|
current_cpu_time_ns,
|
572
625
|
current_monotonic_wall_time_ns
|
573
626
|
);
|
@@ -585,17 +638,17 @@ void thread_context_collector_sample(VALUE self_instance, long current_monotonic
|
|
585
638
|
/* stack_from_thread: */ profiler_overhead_stack_thread,
|
586
639
|
current_thread_context,
|
587
640
|
// Here we use the overhead thread's sampling buffer so as to not invalidate the cache in the buffer of the thread being sampled
|
588
|
-
get_or_create_context_for(profiler_overhead_stack_thread, state)->sampling_buffer,
|
641
|
+
&get_or_create_context_for(profiler_overhead_stack_thread, state)->sampling_buffer,
|
589
642
|
cpu_time_now_ns(current_thread_context),
|
590
643
|
monotonic_wall_time_now_ns(RAISE_ON_FAILURE)
|
591
644
|
);
|
592
645
|
}
|
593
646
|
|
594
647
|
static void update_metrics_and_sample(
|
595
|
-
|
648
|
+
thread_context_collector_state *state,
|
596
649
|
VALUE thread_being_sampled,
|
597
650
|
VALUE stack_from_thread, // This can be different when attributing profiler overhead using a different stack
|
598
|
-
|
651
|
+
per_thread_context *thread_context,
|
599
652
|
sampling_buffer* sampling_buffer,
|
600
653
|
long current_cpu_time_ns,
|
601
654
|
long current_monotonic_wall_time_ns
|
@@ -647,7 +700,8 @@ static void update_metrics_and_sample(
|
|
647
700
|
current_monotonic_wall_time_ns,
|
648
701
|
NULL,
|
649
702
|
NULL,
|
650
|
-
is_gvl_waiting_state
|
703
|
+
is_gvl_waiting_state,
|
704
|
+
/* is_safe_to_allocate_objects: */ true // We called from a context that's safe to run any regular code, including allocations
|
651
705
|
);
|
652
706
|
}
|
653
707
|
|
@@ -662,12 +716,12 @@ static void update_metrics_and_sample(
|
|
662
716
|
// Assumption 1: This function is called in a thread that is holding the Global VM Lock. Caller is responsible for enforcing this.
|
663
717
|
// Assumption 2: This function is called from the main Ractor (if Ruby has support for Ractors).
|
664
718
|
void thread_context_collector_on_gc_start(VALUE self_instance) {
|
665
|
-
|
719
|
+
thread_context_collector_state *state;
|
666
720
|
if (!rb_typeddata_is_kind_of(self_instance, &thread_context_collector_typed_data)) return;
|
667
721
|
// This should never fail the the above check passes
|
668
|
-
TypedData_Get_Struct(self_instance,
|
722
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
669
723
|
|
670
|
-
|
724
|
+
per_thread_context *thread_context = get_context_for(rb_thread_current(), state);
|
671
725
|
|
672
726
|
// If there was no previously-existing context for this thread, we won't allocate one (see safety). For now we just drop
|
673
727
|
// the GC sample, under the assumption that "a thread that is so new that we never sampled it even once before it triggers
|
@@ -695,12 +749,12 @@ void thread_context_collector_on_gc_start(VALUE self_instance) {
|
|
695
749
|
// Assumption 2: This function is called from the main Ractor (if Ruby has support for Ractors).
|
696
750
|
__attribute__((warn_unused_result))
|
697
751
|
bool thread_context_collector_on_gc_finish(VALUE self_instance) {
|
698
|
-
|
752
|
+
thread_context_collector_state *state;
|
699
753
|
if (!rb_typeddata_is_kind_of(self_instance, &thread_context_collector_typed_data)) return false;
|
700
754
|
// This should never fail the the above check passes
|
701
|
-
TypedData_Get_Struct(self_instance,
|
755
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
702
756
|
|
703
|
-
|
757
|
+
per_thread_context *thread_context = get_context_for(rb_thread_current(), state);
|
704
758
|
|
705
759
|
// If there was no previously-existing context for this thread, we won't allocate one (see safety). We keep a metric for
|
706
760
|
// how often this happens -- see on_gc_start.
|
@@ -773,8 +827,8 @@ bool thread_context_collector_on_gc_finish(VALUE self_instance) {
|
|
773
827
|
// Assumption 3: Unlike `on_gc_start` and `on_gc_finish`, this method is allowed to allocate memory as needed.
|
774
828
|
// Assumption 4: This function is called from the main Ractor (if Ruby has support for Ractors).
|
775
829
|
VALUE thread_context_collector_sample_after_gc(VALUE self_instance) {
|
776
|
-
|
777
|
-
TypedData_Get_Struct(self_instance,
|
830
|
+
thread_context_collector_state *state;
|
831
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
778
832
|
|
779
833
|
if (state->gc_tracking.wall_time_at_previous_gc_ns == INVALID_TIME) {
|
780
834
|
rb_raise(rb_eRuntimeError, "BUG: Unexpected call to sample_after_gc without valid GC information available");
|
@@ -823,17 +877,20 @@ VALUE thread_context_collector_sample_after_gc(VALUE self_instance) {
|
|
823
877
|
}
|
824
878
|
|
825
879
|
static void trigger_sample_for_thread(
|
826
|
-
|
880
|
+
thread_context_collector_state *state,
|
827
881
|
VALUE thread,
|
828
882
|
VALUE stack_from_thread, // This can be different when attributing profiler overhead using a different stack
|
829
|
-
|
883
|
+
per_thread_context *thread_context,
|
830
884
|
sampling_buffer* sampling_buffer,
|
831
885
|
sample_values values,
|
832
886
|
long current_monotonic_wall_time_ns,
|
833
887
|
// These two labels are only used for allocation profiling; @ivoanjo: may want to refactor this at some point?
|
834
888
|
ddog_CharSlice *ruby_vm_type,
|
835
889
|
ddog_CharSlice *class_name,
|
836
|
-
bool is_gvl_waiting_state
|
890
|
+
bool is_gvl_waiting_state,
|
891
|
+
// If the Ruby VM is at a state that can allocate objects safely, or not. Added for allocation profiling: we're not
|
892
|
+
// allowed to allocate objects (or raise exceptions) when inside the NEWOBJ tracepoint.
|
893
|
+
bool is_safe_to_allocate_objects
|
837
894
|
) {
|
838
895
|
int max_label_count =
|
839
896
|
1 + // thread id
|
@@ -871,12 +928,12 @@ static void trigger_sample_for_thread(
|
|
871
928
|
};
|
872
929
|
}
|
873
930
|
|
874
|
-
|
875
|
-
trace_identifiers_for(state, thread, &trace_identifiers_result);
|
931
|
+
trace_identifiers trace_identifiers_result = {.valid = false, .trace_endpoint = Qnil};
|
932
|
+
trace_identifiers_for(state, thread, &trace_identifiers_result, is_safe_to_allocate_objects);
|
876
933
|
|
877
934
|
if (!trace_identifiers_result.valid && state->otel_context_enabled != OTEL_CONTEXT_ENABLED_FALSE) {
|
878
935
|
// If we couldn't get something with ddtrace, let's see if we can get some trace identifiers from opentelemetry directly
|
879
|
-
otel_without_ddtrace_trace_identifiers_for(state, thread, &trace_identifiers_result);
|
936
|
+
otel_without_ddtrace_trace_identifiers_for(state, thread, &trace_identifiers_result, is_safe_to_allocate_objects);
|
880
937
|
}
|
881
938
|
|
882
939
|
if (trace_identifiers_result.valid) {
|
@@ -962,7 +1019,9 @@ static void trigger_sample_for_thread(
|
|
962
1019
|
.state_label = state_label,
|
963
1020
|
.end_timestamp_ns = end_timestamp_ns,
|
964
1021
|
.is_gvl_waiting_state = is_gvl_waiting_state,
|
965
|
-
}
|
1022
|
+
},
|
1023
|
+
state->native_filenames_enabled,
|
1024
|
+
state->native_filenames_cache
|
966
1025
|
);
|
967
1026
|
}
|
968
1027
|
|
@@ -970,18 +1029,24 @@ static void trigger_sample_for_thread(
|
|
970
1029
|
// It SHOULD NOT be used for other purposes.
|
971
1030
|
static VALUE _native_thread_list(DDTRACE_UNUSED VALUE _self) {
|
972
1031
|
VALUE result = rb_ary_new();
|
1032
|
+
|
1033
|
+
debug_enter_unsafe_context();
|
1034
|
+
|
973
1035
|
ddtrace_thread_list(result);
|
1036
|
+
|
1037
|
+
debug_leave_unsafe_context();
|
1038
|
+
|
974
1039
|
return result;
|
975
1040
|
}
|
976
1041
|
|
977
|
-
static
|
978
|
-
|
1042
|
+
static per_thread_context *get_or_create_context_for(VALUE thread, thread_context_collector_state *state) {
|
1043
|
+
per_thread_context* thread_context = NULL;
|
979
1044
|
st_data_t value_context = 0;
|
980
1045
|
|
981
1046
|
if (st_lookup(state->hash_map_per_thread_context, (st_data_t) thread, &value_context)) {
|
982
|
-
thread_context = (
|
1047
|
+
thread_context = (per_thread_context*) value_context;
|
983
1048
|
} else {
|
984
|
-
thread_context =
|
1049
|
+
thread_context = calloc(1, sizeof(per_thread_context)); // See "note on calloc vs ruby_xcalloc use" in heap_recorder.c
|
985
1050
|
initialize_context(thread, thread_context, state);
|
986
1051
|
st_insert(state->hash_map_per_thread_context, (st_data_t) thread, (st_data_t) thread_context);
|
987
1052
|
}
|
@@ -989,12 +1054,12 @@ static struct per_thread_context *get_or_create_context_for(VALUE thread, struct
|
|
989
1054
|
return thread_context;
|
990
1055
|
}
|
991
1056
|
|
992
|
-
static
|
993
|
-
|
1057
|
+
static per_thread_context *get_context_for(VALUE thread, thread_context_collector_state *state) {
|
1058
|
+
per_thread_context* thread_context = NULL;
|
994
1059
|
st_data_t value_context = 0;
|
995
1060
|
|
996
1061
|
if (st_lookup(state->hash_map_per_thread_context, (st_data_t) thread, &value_context)) {
|
997
|
-
thread_context = (
|
1062
|
+
thread_context = (per_thread_context*) value_context;
|
998
1063
|
}
|
999
1064
|
|
1000
1065
|
return thread_context;
|
@@ -1021,8 +1086,8 @@ static bool is_logging_gem_monkey_patch(VALUE invoke_file_location) {
|
|
1021
1086
|
return strncmp(invoke_file + invoke_file_len - logging_gem_path_len, LOGGING_GEM_PATH, logging_gem_path_len) == 0;
|
1022
1087
|
}
|
1023
1088
|
|
1024
|
-
static void initialize_context(VALUE thread,
|
1025
|
-
thread_context->sampling_buffer
|
1089
|
+
static void initialize_context(VALUE thread, per_thread_context *thread_context, thread_context_collector_state *state) {
|
1090
|
+
sampling_buffer_initialize(&thread_context->sampling_buffer, state->max_frames, state->locations);
|
1026
1091
|
|
1027
1092
|
snprintf(thread_context->thread_id, THREAD_ID_LIMIT_CHARS, "%"PRIu64" (%lu)", native_thread_id_for(thread), (unsigned long) thread_id_for(thread));
|
1028
1093
|
thread_context->thread_id_char_slice = (ddog_CharSlice) {.ptr = thread_context->thread_id, .len = strlen(thread_context->thread_id)};
|
@@ -1078,14 +1143,14 @@ static void initialize_context(VALUE thread, struct per_thread_context *thread_c
|
|
1078
1143
|
#endif
|
1079
1144
|
}
|
1080
1145
|
|
1081
|
-
static void free_context(
|
1082
|
-
sampling_buffer_free(thread_context->sampling_buffer);
|
1083
|
-
|
1146
|
+
static void free_context(per_thread_context* thread_context) {
|
1147
|
+
sampling_buffer_free(&thread_context->sampling_buffer);
|
1148
|
+
free(thread_context); // See "note on calloc vs ruby_xcalloc use" in heap_recorder.c
|
1084
1149
|
}
|
1085
1150
|
|
1086
1151
|
static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
1087
|
-
|
1088
|
-
TypedData_Get_Struct(collector_instance,
|
1152
|
+
thread_context_collector_state *state;
|
1153
|
+
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1089
1154
|
|
1090
1155
|
VALUE result = rb_str_new2(" (native state)");
|
1091
1156
|
|
@@ -1099,6 +1164,9 @@ static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instanc
|
|
1099
1164
|
rb_str_concat(result, rb_sprintf(" stats=%"PRIsVALUE, stats_as_ruby_hash(state)));
|
1100
1165
|
rb_str_concat(result, rb_sprintf(" endpoint_collection_enabled=%"PRIsVALUE, state->endpoint_collection_enabled ? Qtrue : Qfalse));
|
1101
1166
|
rb_str_concat(result, rb_sprintf(" timeline_enabled=%"PRIsVALUE, state->timeline_enabled ? Qtrue : Qfalse));
|
1167
|
+
rb_str_concat(result, rb_sprintf(" native_filenames_enabled=%"PRIsVALUE, state->native_filenames_enabled ? Qtrue : Qfalse));
|
1168
|
+
// Note: `st_table_size()` is available from Ruby 3.2+ but not before
|
1169
|
+
rb_str_concat(result, rb_sprintf(" native_filenames_cache_size=%zu", state->native_filenames_cache->num_entries));
|
1102
1170
|
rb_str_concat(result, rb_sprintf(" otel_context_enabled=%d", state->otel_context_enabled));
|
1103
1171
|
rb_str_concat(result, rb_sprintf(
|
1104
1172
|
" time_converter_state={.system_epoch_ns_reference=%ld, .delta_to_epoch_ns=%ld}",
|
@@ -1113,7 +1181,7 @@ static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instanc
|
|
1113
1181
|
return result;
|
1114
1182
|
}
|
1115
1183
|
|
1116
|
-
static VALUE per_thread_context_st_table_as_ruby_hash(
|
1184
|
+
static VALUE per_thread_context_st_table_as_ruby_hash(thread_context_collector_state *state) {
|
1117
1185
|
VALUE result = rb_hash_new();
|
1118
1186
|
st_foreach(state->hash_map_per_thread_context, per_thread_context_as_ruby_hash, result);
|
1119
1187
|
return result;
|
@@ -1121,7 +1189,7 @@ static VALUE per_thread_context_st_table_as_ruby_hash(struct thread_context_coll
|
|
1121
1189
|
|
1122
1190
|
static int per_thread_context_as_ruby_hash(st_data_t key_thread, st_data_t value_context, st_data_t result_hash) {
|
1123
1191
|
VALUE thread = (VALUE) key_thread;
|
1124
|
-
|
1192
|
+
per_thread_context *thread_context = (per_thread_context*) value_context;
|
1125
1193
|
VALUE result = (VALUE) result_hash;
|
1126
1194
|
VALUE context_as_hash = rb_hash_new();
|
1127
1195
|
rb_hash_aset(result, thread, context_as_hash);
|
@@ -1146,7 +1214,7 @@ static int per_thread_context_as_ruby_hash(st_data_t key_thread, st_data_t value
|
|
1146
1214
|
return ST_CONTINUE;
|
1147
1215
|
}
|
1148
1216
|
|
1149
|
-
static VALUE stats_as_ruby_hash(
|
1217
|
+
static VALUE stats_as_ruby_hash(thread_context_collector_state *state) {
|
1150
1218
|
// Update this when modifying state struct (stats inner struct)
|
1151
1219
|
VALUE stats_as_hash = rb_hash_new();
|
1152
1220
|
VALUE arguments[] = {
|
@@ -1157,7 +1225,7 @@ static VALUE stats_as_ruby_hash(struct thread_context_collector_state *state) {
|
|
1157
1225
|
return stats_as_hash;
|
1158
1226
|
}
|
1159
1227
|
|
1160
|
-
static VALUE gc_tracking_as_ruby_hash(
|
1228
|
+
static VALUE gc_tracking_as_ruby_hash(thread_context_collector_state *state) {
|
1161
1229
|
// Update this when modifying state struct (gc_tracking inner struct)
|
1162
1230
|
VALUE result = rb_hash_new();
|
1163
1231
|
VALUE arguments[] = {
|
@@ -1170,13 +1238,13 @@ static VALUE gc_tracking_as_ruby_hash(struct thread_context_collector_state *sta
|
|
1170
1238
|
return result;
|
1171
1239
|
}
|
1172
1240
|
|
1173
|
-
static void remove_context_for_dead_threads(
|
1241
|
+
static void remove_context_for_dead_threads(thread_context_collector_state *state) {
|
1174
1242
|
st_foreach(state->hash_map_per_thread_context, remove_if_dead_thread, 0 /* unused */);
|
1175
1243
|
}
|
1176
1244
|
|
1177
1245
|
static int remove_if_dead_thread(st_data_t key_thread, st_data_t value_context, DDTRACE_UNUSED st_data_t _argument) {
|
1178
1246
|
VALUE thread = (VALUE) key_thread;
|
1179
|
-
|
1247
|
+
per_thread_context* thread_context = (per_thread_context*) value_context;
|
1180
1248
|
|
1181
1249
|
if (is_thread_alive(thread)) return ST_CONTINUE;
|
1182
1250
|
|
@@ -1189,8 +1257,8 @@ static int remove_if_dead_thread(st_data_t key_thread, st_data_t value_context,
|
|
1189
1257
|
//
|
1190
1258
|
// Returns the whole contents of the per_thread_context structs being tracked.
|
1191
1259
|
static VALUE _native_per_thread_context(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
1192
|
-
|
1193
|
-
TypedData_Get_Struct(collector_instance,
|
1260
|
+
thread_context_collector_state *state;
|
1261
|
+
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1194
1262
|
|
1195
1263
|
return per_thread_context_st_table_as_ruby_hash(state);
|
1196
1264
|
}
|
@@ -1235,7 +1303,7 @@ static long update_time_since_previous_sample(long *time_at_previous_sample_ns,
|
|
1235
1303
|
}
|
1236
1304
|
|
1237
1305
|
// Safety: This function is assumed never to raise exceptions by callers
|
1238
|
-
static long cpu_time_now_ns(
|
1306
|
+
static long cpu_time_now_ns(per_thread_context *thread_context) {
|
1239
1307
|
thread_cpu_time cpu_time = thread_cpu_time_for(thread_context->thread_cpu_time_id);
|
1240
1308
|
|
1241
1309
|
if (!cpu_time.valid) {
|
@@ -1266,15 +1334,15 @@ static long thread_id_for(VALUE thread) {
|
|
1266
1334
|
}
|
1267
1335
|
|
1268
1336
|
VALUE enforce_thread_context_collector_instance(VALUE object) {
|
1269
|
-
|
1337
|
+
ENFORCE_TYPED_DATA(object, &thread_context_collector_typed_data);
|
1270
1338
|
return object;
|
1271
1339
|
}
|
1272
1340
|
|
1273
1341
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
1274
1342
|
// It SHOULD NOT be used for other purposes.
|
1275
1343
|
static VALUE _native_stats(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
1276
|
-
|
1277
|
-
TypedData_Get_Struct(collector_instance,
|
1344
|
+
thread_context_collector_state *state;
|
1345
|
+
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1278
1346
|
|
1279
1347
|
return stats_as_ruby_hash(state);
|
1280
1348
|
}
|
@@ -1282,14 +1350,19 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE _self, VALUE collector_instance)
|
|
1282
1350
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
1283
1351
|
// It SHOULD NOT be used for other purposes.
|
1284
1352
|
static VALUE _native_gc_tracking(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
1285
|
-
|
1286
|
-
TypedData_Get_Struct(collector_instance,
|
1353
|
+
thread_context_collector_state *state;
|
1354
|
+
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1287
1355
|
|
1288
1356
|
return gc_tracking_as_ruby_hash(state);
|
1289
1357
|
}
|
1290
1358
|
|
1291
1359
|
// Assumption 1: This function is called in a thread that is holding the Global VM Lock. Caller is responsible for enforcing this.
|
1292
|
-
static void trace_identifiers_for(
|
1360
|
+
static void trace_identifiers_for(
|
1361
|
+
thread_context_collector_state *state,
|
1362
|
+
VALUE thread,
|
1363
|
+
trace_identifiers *trace_identifiers_result,
|
1364
|
+
bool is_safe_to_allocate_objects
|
1365
|
+
) {
|
1293
1366
|
if (state->otel_context_enabled == OTEL_CONTEXT_ENABLED_ONLY) return;
|
1294
1367
|
if (state->tracer_context_key == MISSING_TRACER_CONTEXT_KEY) return;
|
1295
1368
|
|
@@ -1308,7 +1381,9 @@ static void trace_identifiers_for(struct thread_context_collector_state *state,
|
|
1308
1381
|
|
1309
1382
|
VALUE numeric_span_id = Qnil;
|
1310
1383
|
|
1311
|
-
if (otel_values != Qnil)
|
1384
|
+
if (otel_values != Qnil) {
|
1385
|
+
ddtrace_otel_trace_identifiers_for(state, &active_trace, &root_span, &numeric_span_id, active_span, otel_values, is_safe_to_allocate_objects);
|
1386
|
+
}
|
1312
1387
|
|
1313
1388
|
if (root_span == Qnil || (active_span == Qnil && numeric_span_id == Qnil)) return;
|
1314
1389
|
|
@@ -1365,8 +1440,8 @@ static bool should_collect_resource(VALUE root_span) {
|
|
1365
1440
|
// Assumption: This method gets called BEFORE restarting profiling -- e.g. there are no components attempting to
|
1366
1441
|
// trigger samples at the same time.
|
1367
1442
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
|
1368
|
-
|
1369
|
-
TypedData_Get_Struct(collector_instance,
|
1443
|
+
thread_context_collector_state *state;
|
1444
|
+
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1370
1445
|
|
1371
1446
|
// Release all context memory before clearing the existing context
|
1372
1447
|
st_foreach(state->hash_map_per_thread_context, hash_map_per_thread_context_free_values, 0 /* unused */);
|
@@ -1380,16 +1455,36 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector
|
|
1380
1455
|
return Qtrue;
|
1381
1456
|
}
|
1382
1457
|
|
1383
|
-
static VALUE thread_list(
|
1458
|
+
static VALUE thread_list(thread_context_collector_state *state) {
|
1384
1459
|
VALUE result = state->thread_list_buffer;
|
1385
1460
|
rb_ary_clear(result);
|
1386
1461
|
ddtrace_thread_list(result);
|
1387
1462
|
return result;
|
1388
1463
|
}
|
1389
1464
|
|
1465
|
+
// Inside a signal handler, we don't want to do the whole work of recording a sample, but we only record the stack of
|
1466
|
+
// the current thread.
|
1467
|
+
//
|
1468
|
+
// Assumptions for this function are same as for `thread_context_collector_sample` except that this function is
|
1469
|
+
// expected to be called from a signal handler and to be async-signal-safe.
|
1470
|
+
//
|
1471
|
+
// Also, no allocation (Ruby or malloc) can happen.
|
1472
|
+
void thread_context_collector_prepare_sample_inside_signal_handler(VALUE self_instance) {
|
1473
|
+
thread_context_collector_state *state;
|
1474
|
+
if (!rb_typeddata_is_kind_of(self_instance, &thread_context_collector_typed_data)) return;
|
1475
|
+
// This should never fail if the above check passes
|
1476
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1477
|
+
|
1478
|
+
VALUE current_thread = rb_thread_current();
|
1479
|
+
per_thread_context *thread_context = get_context_for(current_thread, state);
|
1480
|
+
if (thread_context == NULL) return;
|
1481
|
+
|
1482
|
+
prepare_sample_thread(current_thread, &thread_context->sampling_buffer);
|
1483
|
+
}
|
1484
|
+
|
1390
1485
|
void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object) {
|
1391
|
-
|
1392
|
-
TypedData_Get_Struct(self_instance,
|
1486
|
+
thread_context_collector_state *state;
|
1487
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1393
1488
|
|
1394
1489
|
VALUE current_thread = rb_thread_current();
|
1395
1490
|
|
@@ -1400,11 +1495,8 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
1400
1495
|
|
1401
1496
|
// Since this is stack allocated, be careful about moving it
|
1402
1497
|
ddog_CharSlice class_name;
|
1403
|
-
ddog_CharSlice *optional_class_name = NULL;
|
1404
1498
|
char imemo_type[100];
|
1405
1499
|
|
1406
|
-
optional_class_name = &class_name;
|
1407
|
-
|
1408
1500
|
if (
|
1409
1501
|
type == RUBY_T_OBJECT ||
|
1410
1502
|
type == RUBY_T_CLASS ||
|
@@ -1460,28 +1552,34 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
1460
1552
|
class_name = ruby_vm_type; // For other weird internal things we just use the VM type
|
1461
1553
|
}
|
1462
1554
|
|
1463
|
-
track_object(state->recorder_instance, new_object, sample_weight,
|
1555
|
+
track_object(state->recorder_instance, new_object, sample_weight, class_name);
|
1464
1556
|
|
1465
|
-
|
1557
|
+
per_thread_context *thread_context = get_or_create_context_for(current_thread, state);
|
1466
1558
|
|
1467
1559
|
trigger_sample_for_thread(
|
1468
1560
|
state,
|
1469
1561
|
/* thread: */ current_thread,
|
1470
1562
|
/* stack_from_thread: */ current_thread,
|
1471
1563
|
thread_context,
|
1472
|
-
thread_context->sampling_buffer,
|
1564
|
+
&thread_context->sampling_buffer,
|
1473
1565
|
(sample_values) {.alloc_samples = sample_weight, .alloc_samples_unscaled = 1, .heap_sample = true},
|
1474
1566
|
INVALID_TIME, // For now we're not collecting timestamps for allocation events, as per profiling team internal discussions
|
1475
1567
|
&ruby_vm_type,
|
1476
|
-
|
1477
|
-
false
|
1568
|
+
&class_name,
|
1569
|
+
/* is_gvl_waiting_state: */ false,
|
1570
|
+
/* is_safe_to_allocate_objects: */ false // Not safe to allocate further inside the NEWOBJ tracepoint
|
1478
1571
|
);
|
1479
1572
|
}
|
1480
1573
|
|
1481
1574
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
1482
1575
|
// It SHOULD NOT be used for other purposes.
|
1483
1576
|
static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object) {
|
1577
|
+
debug_enter_unsafe_context();
|
1578
|
+
|
1484
1579
|
thread_context_collector_sample_allocation(collector_instance, NUM2UINT(sample_weight), new_object);
|
1580
|
+
|
1581
|
+
debug_leave_unsafe_context();
|
1582
|
+
|
1485
1583
|
return Qtrue;
|
1486
1584
|
}
|
1487
1585
|
|
@@ -1529,11 +1627,18 @@ static VALUE read_otel_current_span_key_const(DDTRACE_UNUSED VALUE _unused) {
|
|
1529
1627
|
return rb_const_get(trace_module, rb_intern("CURRENT_SPAN_KEY"));
|
1530
1628
|
}
|
1531
1629
|
|
1532
|
-
static VALUE get_otel_current_span_key(
|
1630
|
+
static VALUE get_otel_current_span_key(thread_context_collector_state *state, bool is_safe_to_allocate_objects) {
|
1533
1631
|
if (state->otel_current_span_key == Qtrue) { // Qtrue means we haven't tried to extract it yet
|
1632
|
+
if (!is_safe_to_allocate_objects) {
|
1633
|
+
// Calling read_otel_current_span_key_const below can trigger exceptions and arbitrary Ruby code running (e.g.
|
1634
|
+
// `const_missing`, etc). Not safe to call in this situation, so we just skip otel info for this sample.
|
1635
|
+
return Qnil;
|
1636
|
+
}
|
1637
|
+
|
1534
1638
|
// If this fails, we want to fail gracefully, rather than raise an exception (e.g. if the opentelemetry gem
|
1535
1639
|
// gets refactored, we should not fall on our face)
|
1536
1640
|
VALUE span_key = rb_protect(read_otel_current_span_key_const, Qnil, NULL);
|
1641
|
+
rb_set_errinfo(Qnil); // **Clear any pending exception after ignoring it**
|
1537
1642
|
|
1538
1643
|
// Note that this gets set to Qnil if we failed to extract the correct value, and thus we won't try to extract it again
|
1539
1644
|
state->otel_current_span_key = span_key;
|
@@ -1545,12 +1650,13 @@ static VALUE get_otel_current_span_key(struct thread_context_collector_state *st
|
|
1545
1650
|
// This method gets used when ddtrace is being used indirectly via the opentelemetry APIs. Information gets stored slightly
|
1546
1651
|
// differently, and this codepath handles it.
|
1547
1652
|
static void ddtrace_otel_trace_identifiers_for(
|
1548
|
-
|
1653
|
+
thread_context_collector_state *state,
|
1549
1654
|
VALUE *active_trace,
|
1550
1655
|
VALUE *root_span,
|
1551
1656
|
VALUE *numeric_span_id,
|
1552
1657
|
VALUE active_span,
|
1553
|
-
VALUE otel_values
|
1658
|
+
VALUE otel_values,
|
1659
|
+
bool is_safe_to_allocate_objects
|
1554
1660
|
) {
|
1555
1661
|
VALUE resolved_numeric_span_id =
|
1556
1662
|
active_span == Qnil ?
|
@@ -1561,7 +1667,7 @@ static void ddtrace_otel_trace_identifiers_for(
|
|
1561
1667
|
|
1562
1668
|
if (resolved_numeric_span_id == Qnil) return;
|
1563
1669
|
|
1564
|
-
VALUE otel_current_span_key = get_otel_current_span_key(state);
|
1670
|
+
VALUE otel_current_span_key = get_otel_current_span_key(state, is_safe_to_allocate_objects);
|
1565
1671
|
if (otel_current_span_key == Qnil) return;
|
1566
1672
|
VALUE current_trace = *active_trace;
|
1567
1673
|
|
@@ -1569,7 +1675,7 @@ static void ddtrace_otel_trace_identifiers_for(
|
|
1569
1675
|
// trace and span representing it. Each ddtrace trace is then connected to the previous otel span, forming a linked
|
1570
1676
|
// list. The local root span is going to be the trace/span we find at the end of this linked list.
|
1571
1677
|
while (otel_values != Qnil) {
|
1572
|
-
VALUE otel_span =
|
1678
|
+
VALUE otel_span = safely_lookup_hash_without_going_into_ruby_code(otel_values, otel_current_span_key);
|
1573
1679
|
if (otel_span == Qnil) break;
|
1574
1680
|
VALUE next_trace = rb_ivar_get(otel_span, at_datadog_trace_id);
|
1575
1681
|
if (next_trace == Qnil) break;
|
@@ -1588,8 +1694,8 @@ static void ddtrace_otel_trace_identifiers_for(
|
|
1588
1694
|
}
|
1589
1695
|
|
1590
1696
|
void thread_context_collector_sample_skipped_allocation_samples(VALUE self_instance, unsigned int skipped_samples) {
|
1591
|
-
|
1592
|
-
TypedData_Get_Struct(self_instance,
|
1697
|
+
thread_context_collector_state *state;
|
1698
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1593
1699
|
|
1594
1700
|
ddog_prof_Label labels[] = {
|
1595
1701
|
// Providing .num = 0 should not be needed but the tracer-2.7 docker image ships a buggy gcc that complains about this
|
@@ -1612,10 +1718,51 @@ void thread_context_collector_sample_skipped_allocation_samples(VALUE self_insta
|
|
1612
1718
|
}
|
1613
1719
|
|
1614
1720
|
static VALUE _native_sample_skipped_allocation_samples(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE skipped_samples) {
|
1721
|
+
debug_enter_unsafe_context();
|
1722
|
+
|
1615
1723
|
thread_context_collector_sample_skipped_allocation_samples(collector_instance, NUM2UINT(skipped_samples));
|
1724
|
+
|
1725
|
+
debug_leave_unsafe_context();
|
1726
|
+
|
1616
1727
|
return Qtrue;
|
1617
1728
|
}
|
1618
1729
|
|
1730
|
+
#ifndef NO_CURRENT_FIBER_FOR // Ruby 3.1+
|
1731
|
+
static VALUE otel_context_storage_for(thread_context_collector_state *state, VALUE thread) {
|
1732
|
+
if (state->otel_context_source == OTEL_CONTEXT_SOURCE_FIBER_IVAR) { // otel-api 1.5+
|
1733
|
+
VALUE current_fiber = current_fiber_for(thread);
|
1734
|
+
return current_fiber == Qnil ? Qnil : rb_ivar_get(current_fiber, otel_fiber_context_storage_id /* @opentelemetry_context */);
|
1735
|
+
}
|
1736
|
+
|
1737
|
+
if (state->otel_context_source == OTEL_CONTEXT_SOURCE_FIBER_LOCAL) { // otel-api < 1.5
|
1738
|
+
return rb_thread_local_aref(thread, otel_context_storage_id /* __opentelemetry_context_storage__ */);
|
1739
|
+
}
|
1740
|
+
|
1741
|
+
// If we got here, it means we never observed a context being set. Let's probe which one to use.
|
1742
|
+
VALUE current_fiber = current_fiber_for(thread);
|
1743
|
+
if (current_fiber != Qnil) {
|
1744
|
+
VALUE context_storage = rb_ivar_get(current_fiber, otel_fiber_context_storage_id /* @opentelemetry_context */);
|
1745
|
+
if (context_storage != Qnil) {
|
1746
|
+
state->otel_context_source = OTEL_CONTEXT_SOURCE_FIBER_IVAR; // Remember for next time
|
1747
|
+
return context_storage;
|
1748
|
+
}
|
1749
|
+
} else {
|
1750
|
+
VALUE context_storage = rb_thread_local_aref(thread, otel_context_storage_id /* __opentelemetry_context_storage__ */);
|
1751
|
+
if (context_storage != Qnil) {
|
1752
|
+
state->otel_context_source = OTEL_CONTEXT_SOURCE_FIBER_LOCAL; // Remember for next time
|
1753
|
+
return context_storage;
|
1754
|
+
}
|
1755
|
+
}
|
1756
|
+
|
1757
|
+
// There's no context storage attached to the current thread
|
1758
|
+
return Qnil;
|
1759
|
+
}
|
1760
|
+
#else
|
1761
|
+
static inline VALUE otel_context_storage_for(DDTRACE_UNUSED thread_context_collector_state *state, VALUE thread) {
|
1762
|
+
return rb_thread_local_aref(thread, otel_context_storage_id /* __opentelemetry_context_storage__ */);
|
1763
|
+
}
|
1764
|
+
#endif
|
1765
|
+
|
1619
1766
|
// This method differs from trace_identifiers_for/ddtrace_otel_trace_identifiers_for to support the situation where
|
1620
1767
|
// the opentelemetry ruby library is being used for tracing AND the ddtrace tracing bits are not involved at all.
|
1621
1768
|
//
|
@@ -1638,29 +1785,30 @@ static VALUE _native_sample_skipped_allocation_samples(DDTRACE_UNUSED VALUE self
|
|
1638
1785
|
// root span id.
|
1639
1786
|
// This matches the semantics of how ddtrace tracing creates a TraceOperation and assigns a local root span to it.
|
1640
1787
|
static void otel_without_ddtrace_trace_identifiers_for(
|
1641
|
-
|
1788
|
+
thread_context_collector_state *state,
|
1642
1789
|
VALUE thread,
|
1643
|
-
|
1790
|
+
trace_identifiers *trace_identifiers_result,
|
1791
|
+
bool is_safe_to_allocate_objects
|
1644
1792
|
) {
|
1645
|
-
VALUE context_storage =
|
1793
|
+
VALUE context_storage = otel_context_storage_for(state, thread);
|
1646
1794
|
|
1647
1795
|
// If it exists, context_storage is expected to be an Array[OpenTelemetry::Context]
|
1648
1796
|
if (context_storage == Qnil || !RB_TYPE_P(context_storage, T_ARRAY)) return;
|
1649
1797
|
|
1650
|
-
VALUE otel_current_span_key = get_otel_current_span_key(state);
|
1798
|
+
VALUE otel_current_span_key = get_otel_current_span_key(state, is_safe_to_allocate_objects);
|
1651
1799
|
if (otel_current_span_key == Qnil) return;
|
1652
1800
|
|
1653
1801
|
int active_context_index = RARRAY_LEN(context_storage) - 1;
|
1654
1802
|
if (active_context_index < 0) return;
|
1655
1803
|
|
1656
|
-
|
1804
|
+
otel_span active_span = otel_span_from(rb_ary_entry(context_storage, active_context_index), otel_current_span_key);
|
1657
1805
|
if (active_span.span == Qnil) return;
|
1658
1806
|
|
1659
|
-
|
1807
|
+
otel_span local_root_span = active_span;
|
1660
1808
|
|
1661
1809
|
// Now find the oldest span starting from the active span that still has the same trace id as the active span
|
1662
1810
|
for (int i = active_context_index - 1; i >= 0; i--) {
|
1663
|
-
|
1811
|
+
otel_span checking_span = otel_span_from(rb_ary_entry(context_storage, i), otel_current_span_key);
|
1664
1812
|
if (checking_span.span == Qnil) return;
|
1665
1813
|
|
1666
1814
|
if (rb_str_equal(active_span.trace_id, checking_span.trace_id) == Qfalse) break;
|
@@ -1680,7 +1828,7 @@ static void otel_without_ddtrace_trace_identifiers_for(
|
|
1680
1828
|
|
1681
1829
|
VALUE root_span_type = rb_ivar_get(local_root_span.span, at_kind_id /* @kind */);
|
1682
1830
|
// We filter out spans that don't have `kind: :server`
|
1683
|
-
if (root_span_type == Qnil || !RB_TYPE_P(root_span_type, T_SYMBOL) || SYM2ID(root_span_type) != server_id) return;
|
1831
|
+
if (root_span_type == Qnil || !RB_TYPE_P(root_span_type, T_SYMBOL) || !RB_STATIC_SYM_P(root_span_type) || SYM2ID(root_span_type) != server_id) return;
|
1684
1832
|
|
1685
1833
|
VALUE trace_resource = rb_ivar_get(local_root_span.span, at_name_id /* @name */);
|
1686
1834
|
if (!RB_TYPE_P(trace_resource, T_STRING)) return;
|
@@ -1688,8 +1836,8 @@ static void otel_without_ddtrace_trace_identifiers_for(
|
|
1688
1836
|
trace_identifiers_result->trace_endpoint = trace_resource;
|
1689
1837
|
}
|
1690
1838
|
|
1691
|
-
static
|
1692
|
-
|
1839
|
+
static otel_span otel_span_from(VALUE otel_context, VALUE otel_current_span_key) {
|
1840
|
+
otel_span failed = {.span = Qnil, .span_id = Qnil, .trace_id = Qnil};
|
1693
1841
|
|
1694
1842
|
if (otel_context == Qnil) return failed;
|
1695
1843
|
|
@@ -1697,7 +1845,7 @@ static struct otel_span otel_span_from(VALUE otel_context, VALUE otel_current_sp
|
|
1697
1845
|
if (context_entries == Qnil || !RB_TYPE_P(context_entries, T_HASH)) return failed;
|
1698
1846
|
|
1699
1847
|
// If it exists, context_entries is expected to be a Hash[OpenTelemetry::Context::Key, OpenTelemetry::Trace::Span]
|
1700
|
-
VALUE span =
|
1848
|
+
VALUE span = safely_lookup_hash_without_going_into_ruby_code(context_entries, otel_current_span_key);
|
1701
1849
|
if (span == Qnil) return failed;
|
1702
1850
|
|
1703
1851
|
// If it exists, span_context is expected to be a OpenTelemetry::Trace::SpanContext (don't confuse it with OpenTelemetry::Context)
|
@@ -1708,7 +1856,7 @@ static struct otel_span otel_span_from(VALUE otel_context, VALUE otel_current_sp
|
|
1708
1856
|
VALUE trace_id = rb_ivar_get(span_context, at_trace_id_id /* @trace_id */);
|
1709
1857
|
if (span_id == Qnil || trace_id == Qnil || !RB_TYPE_P(span_id, T_STRING) || !RB_TYPE_P(trace_id, T_STRING)) return failed;
|
1710
1858
|
|
1711
|
-
return (
|
1859
|
+
return (otel_span) {.span = span, .span_id = span_id, .trace_id = trace_id};
|
1712
1860
|
}
|
1713
1861
|
|
1714
1862
|
// Otel span ids are represented as a big-endian 8-byte string
|
@@ -1810,8 +1958,8 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
1810
1958
|
// NOTE: In normal use, current_thread is expected to be == rb_thread_current(); the `current_thread` parameter only
|
1811
1959
|
// exists to enable testing.
|
1812
1960
|
VALUE thread_context_collector_sample_after_gvl_running(VALUE self_instance, VALUE current_thread, long current_monotonic_wall_time_ns) {
|
1813
|
-
|
1814
|
-
TypedData_Get_Struct(self_instance,
|
1961
|
+
thread_context_collector_state *state;
|
1962
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1815
1963
|
|
1816
1964
|
if (!state->timeline_enabled) rb_raise(rb_eRuntimeError, "GVL profiling requires timeline to be enabled");
|
1817
1965
|
|
@@ -1825,7 +1973,7 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
1825
1973
|
return Qfalse;
|
1826
1974
|
}
|
1827
1975
|
|
1828
|
-
|
1976
|
+
per_thread_context *thread_context = get_or_create_context_for(current_thread, state);
|
1829
1977
|
|
1830
1978
|
// We don't actually account for cpu-time during Waiting for GVL. BUT, we may chose to push an
|
1831
1979
|
// extra sample to represent the period prior to Waiting for GVL. To support that, we retrieve the current
|
@@ -1839,7 +1987,7 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
1839
1987
|
/* thread_being_sampled: */ current_thread,
|
1840
1988
|
/* stack_from_thread: */ current_thread,
|
1841
1989
|
thread_context,
|
1842
|
-
thread_context->sampling_buffer,
|
1990
|
+
&thread_context->sampling_buffer,
|
1843
1991
|
cpu_time_for_thread,
|
1844
1992
|
current_monotonic_wall_time_ns
|
1845
1993
|
);
|
@@ -1851,10 +1999,10 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
1851
1999
|
// need to take when sampling cpu/wall-time for a thread that's in the "Waiting for GVL" state.
|
1852
2000
|
__attribute__((warn_unused_result))
|
1853
2001
|
static bool handle_gvl_waiting(
|
1854
|
-
|
2002
|
+
thread_context_collector_state *state,
|
1855
2003
|
VALUE thread_being_sampled,
|
1856
2004
|
VALUE stack_from_thread,
|
1857
|
-
|
2005
|
+
per_thread_context *thread_context,
|
1858
2006
|
sampling_buffer* sampling_buffer,
|
1859
2007
|
long current_cpu_time_ns
|
1860
2008
|
) {
|
@@ -1939,7 +2087,8 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
1939
2087
|
gvl_waiting_started_wall_time_ns,
|
1940
2088
|
NULL,
|
1941
2089
|
NULL,
|
1942
|
-
false // This is the extra sample before the wait begun; only the next sample will be in the gvl waiting state
|
2090
|
+
/* is_gvl_waiting_state: */ false, // This is the extra sample before the wait begun; only the next sample will be in the gvl waiting state
|
2091
|
+
/* is_safe_to_allocate_objects: */ true // This is similar to a regular cpu/wall sample, so it's also safe
|
1943
2092
|
);
|
1944
2093
|
}
|
1945
2094
|
|
@@ -1949,40 +2098,62 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
1949
2098
|
static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
1950
2099
|
ENFORCE_THREAD(thread);
|
1951
2100
|
|
2101
|
+
debug_enter_unsafe_context();
|
2102
|
+
|
1952
2103
|
thread_context_collector_on_gvl_waiting(thread_from_thread_object(thread));
|
2104
|
+
|
2105
|
+
debug_leave_unsafe_context();
|
2106
|
+
|
1953
2107
|
return Qnil;
|
1954
2108
|
}
|
1955
2109
|
|
1956
2110
|
static VALUE _native_gvl_waiting_at_for(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
1957
2111
|
ENFORCE_THREAD(thread);
|
1958
2112
|
|
2113
|
+
debug_enter_unsafe_context();
|
2114
|
+
|
1959
2115
|
intptr_t gvl_waiting_at = gvl_profiling_state_thread_object_get(thread);
|
2116
|
+
|
2117
|
+
debug_leave_unsafe_context();
|
2118
|
+
|
1960
2119
|
return LONG2NUM(gvl_waiting_at);
|
1961
2120
|
}
|
1962
2121
|
|
1963
2122
|
static VALUE _native_on_gvl_running(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
1964
2123
|
ENFORCE_THREAD(thread);
|
1965
2124
|
|
1966
|
-
|
2125
|
+
debug_enter_unsafe_context();
|
2126
|
+
|
2127
|
+
VALUE result = thread_context_collector_on_gvl_running(thread_from_thread_object(thread)) == ON_GVL_RUNNING_SAMPLE ? Qtrue : Qfalse;
|
2128
|
+
|
2129
|
+
debug_leave_unsafe_context();
|
2130
|
+
|
2131
|
+
return result;
|
1967
2132
|
}
|
1968
2133
|
|
1969
2134
|
static VALUE _native_sample_after_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread) {
|
1970
2135
|
ENFORCE_THREAD(thread);
|
1971
2136
|
|
1972
|
-
|
2137
|
+
debug_enter_unsafe_context();
|
2138
|
+
|
2139
|
+
VALUE result = thread_context_collector_sample_after_gvl_running(
|
1973
2140
|
collector_instance,
|
1974
2141
|
thread,
|
1975
2142
|
monotonic_wall_time_now_ns(RAISE_ON_FAILURE)
|
1976
2143
|
);
|
2144
|
+
|
2145
|
+
debug_leave_unsafe_context();
|
2146
|
+
|
2147
|
+
return result;
|
1977
2148
|
}
|
1978
2149
|
|
1979
2150
|
static VALUE _native_apply_delta_to_cpu_time_at_previous_sample_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread, VALUE delta_ns) {
|
1980
2151
|
ENFORCE_THREAD(thread);
|
1981
2152
|
|
1982
|
-
|
1983
|
-
TypedData_Get_Struct(collector_instance,
|
2153
|
+
thread_context_collector_state *state;
|
2154
|
+
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1984
2155
|
|
1985
|
-
|
2156
|
+
per_thread_context *thread_context = get_context_for(thread, state);
|
1986
2157
|
if (thread_context == NULL) rb_raise(rb_eArgError, "Unexpected: This method cannot be used unless the per-thread context for the thread already exists");
|
1987
2158
|
|
1988
2159
|
thread_context->cpu_time_at_previous_sample_ns += NUM2LONG(delta_ns);
|
@@ -1992,11 +2163,60 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
1992
2163
|
|
1993
2164
|
#else
|
1994
2165
|
static bool handle_gvl_waiting(
|
1995
|
-
DDTRACE_UNUSED
|
2166
|
+
DDTRACE_UNUSED thread_context_collector_state *state,
|
1996
2167
|
DDTRACE_UNUSED VALUE thread_being_sampled,
|
1997
2168
|
DDTRACE_UNUSED VALUE stack_from_thread,
|
1998
|
-
DDTRACE_UNUSED
|
2169
|
+
DDTRACE_UNUSED per_thread_context *thread_context,
|
1999
2170
|
DDTRACE_UNUSED sampling_buffer* sampling_buffer,
|
2000
2171
|
DDTRACE_UNUSED long current_cpu_time_ns
|
2001
2172
|
) { return false; }
|
2002
2173
|
#endif // NO_GVL_INSTRUMENTATION
|
2174
|
+
|
2175
|
+
#define MAX_SAFE_LOOKUP_SIZE 16
|
2176
|
+
|
2177
|
+
typedef struct { VALUE lookup_key; VALUE result; } safe_lookup_hash_state;
|
2178
|
+
|
2179
|
+
static int safe_lookup_hash_iterate(VALUE key, VALUE value, VALUE state_ptr) {
|
2180
|
+
safe_lookup_hash_state *state = (safe_lookup_hash_state *) state_ptr;
|
2181
|
+
|
2182
|
+
if (key == state->lookup_key) {
|
2183
|
+
state->result = value;
|
2184
|
+
return ST_STOP;
|
2185
|
+
}
|
2186
|
+
|
2187
|
+
return ST_CONTINUE;
|
2188
|
+
}
|
2189
|
+
|
2190
|
+
// This method exists because we need to look up a hash during sampling, but we don't want to invoke any
|
2191
|
+
// Ruby code as a side effect. To be able to look up by hash, `rb_hash_lookup` calls `#hash` on the key,
|
2192
|
+
// which we want to avoid.
|
2193
|
+
// Thus, instead, we opt to just iterate through the hash and check if we can find what we're looking for.
|
2194
|
+
//
|
2195
|
+
// To avoid having too much overhead here we only iterate in hashes up to MAX_SAFE_LOOKUP_SIZE.
|
2196
|
+
// Note that we don't even try to iterate if the hash is bigger -- this is to avoid flaky behavior where
|
2197
|
+
// depending on the internal storage order of the hash we may or not find the key, and instead we always
|
2198
|
+
// enforce the size.
|
2199
|
+
static VALUE safely_lookup_hash_without_going_into_ruby_code(VALUE hash, VALUE key) {
|
2200
|
+
if (!RB_TYPE_P(hash, T_HASH) || RHASH_SIZE(hash) > MAX_SAFE_LOOKUP_SIZE) return Qnil;
|
2201
|
+
|
2202
|
+
safe_lookup_hash_state state = {.lookup_key = key, .result = Qnil};
|
2203
|
+
|
2204
|
+
rb_hash_foreach(hash, safe_lookup_hash_iterate, (VALUE) &state);
|
2205
|
+
|
2206
|
+
return state.result;
|
2207
|
+
}
|
2208
|
+
|
2209
|
+
static VALUE _native_system_epoch_time_now_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
|
2210
|
+
thread_context_collector_state *state;
|
2211
|
+
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
2212
|
+
|
2213
|
+
long current_monotonic_wall_time_ns = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
2214
|
+
long system_epoch_time_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, current_monotonic_wall_time_ns);
|
2215
|
+
|
2216
|
+
return LONG2NUM(system_epoch_time_ns);
|
2217
|
+
}
|
2218
|
+
|
2219
|
+
static VALUE _native_prepare_sample_inside_signal_handler(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
|
2220
|
+
thread_context_collector_prepare_sample_inside_signal_handler(collector_instance);
|
2221
|
+
return Qtrue;
|
2222
|
+
}
|