datadog 2.7.1 → 2.17.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 +310 -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 +66 -56
- 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 +10 -10
- data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +314 -145
- 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 +7 -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 +61 -174
- data/ext/datadog_profiling_native_extension/heap_recorder.h +2 -2
- data/ext/datadog_profiling_native_extension/http_transport.c +64 -98
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +68 -1
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +10 -1
- data/ext/datadog_profiling_native_extension/profiling.c +19 -8
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +8 -8
- data/ext/datadog_profiling_native_extension/stack_recorder.c +84 -131
- 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/init.c +15 -0
- data/ext/libdatadog_api/library_config.c +122 -0
- data/ext/libdatadog_api/library_config.h +19 -0
- data/ext/libdatadog_api/macos_development.md +3 -3
- data/ext/libdatadog_api/process_discovery.c +117 -0
- data/ext/libdatadog_api/process_discovery.h +5 -0
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- 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 +49 -0
- data/lib/datadog/appsec/api_security.rb +9 -0
- data/lib/datadog/appsec/assets/waf_rules/README.md +50 -5
- data/lib/datadog/appsec/assets/waf_rules/processors.json +239 -10
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +355 -157
- data/lib/datadog/appsec/assets/waf_rules/scanners.json +926 -17
- data/lib/datadog/appsec/assets/waf_rules/strict.json +62 -32
- data/lib/datadog/appsec/autoload.rb +1 -1
- data/lib/datadog/appsec/component.rb +41 -33
- data/lib/datadog/appsec/compressed_json.rb +40 -0
- data/lib/datadog/appsec/configuration/settings.rb +152 -25
- 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 +52 -68
- 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 +26 -31
- data/lib/datadog/appsec/processor/rule_merger.rb +7 -6
- data/lib/datadog/appsec/processor.rb +5 -4
- data/lib/datadog/appsec/remote.rb +26 -12
- data/lib/datadog/appsec/response.rb +19 -85
- data/lib/datadog/appsec/security_engine/result.rb +67 -0
- data/lib/datadog/appsec/security_engine/runner.rb +88 -0
- data/lib/datadog/appsec/security_engine.rb +9 -0
- data/lib/datadog/appsec/security_event.rb +39 -0
- data/lib/datadog/appsec/utils.rb +0 -2
- data/lib/datadog/appsec.rb +23 -10
- data/lib/datadog/auto_instrument.rb +3 -0
- data/lib/datadog/core/buffer/random.rb +18 -2
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +42 -14
- data/lib/datadog/core/configuration/agentless_settings_resolver.rb +176 -0
- data/lib/datadog/core/configuration/components.rb +76 -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 +79 -43
- 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 +100 -41
- data/lib/datadog/core/configuration/stable_config.rb +23 -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/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.rb +32 -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 +2 -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/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 +65 -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 +179 -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 +1 -1
- data/lib/datadog/core/telemetry/logging.rb +2 -2
- 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/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.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/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 +111 -20
- 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 +34 -8
- data/lib/datadog/di/probe_notifier_worker.rb +52 -26
- data/lib/datadog/di/redactor.rb +0 -1
- data/lib/datadog/di/remote.rb +147 -0
- data/lib/datadog/di/serializer.rb +14 -7
- 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 +67 -0
- data/lib/datadog/di/transport/http.rb +57 -0
- data/lib/datadog/di/transport/input.rb +62 -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 +1 -1
- data/lib/datadog/profiling/collectors/info.rb +3 -0
- data/lib/datadog/profiling/collectors/thread_context.rb +1 -1
- data/lib/datadog/profiling/component.rb +60 -76
- data/lib/datadog/profiling/encoded_profile.rb +11 -0
- data/lib/datadog/profiling/exporter.rb +3 -4
- data/lib/datadog/profiling/ext.rb +0 -2
- data/lib/datadog/profiling/flush.rb +5 -8
- data/lib/datadog/profiling/http_transport.rb +6 -85
- data/lib/datadog/profiling/load_native_extension.rb +1 -33
- data/lib/datadog/profiling/scheduler.rb +8 -1
- data/lib/datadog/profiling/stack_recorder.rb +4 -4
- data/lib/datadog/profiling/tag_builder.rb +1 -5
- 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/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/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/server_tracer.rb +1 -1
- 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 +9 -5
- data/lib/datadog/tracing/trace_digest.rb +9 -2
- data/lib/datadog/tracing/trace_operation.rb +44 -24
- data/lib/datadog/tracing/trace_segment.rb +6 -4
- data/lib/datadog/tracing/tracer.rb +60 -12
- 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 +143 -50
- data/ext/datadog_profiling_loader/datadog_profiling_loader.c +0 -142
- data/ext/datadog_profiling_loader/extconf.rb +0 -60
- data/lib/datadog/appsec/contrib/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/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,7 @@
|
|
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"
|
12
14
|
|
13
15
|
// Used to trigger sampling of threads, based on external "events", such as:
|
14
16
|
// * periodic timer for cpu-time and wall-time
|
@@ -101,6 +103,7 @@ static ID at_kind_id; // id of :@kind in Ruby
|
|
101
103
|
static ID at_name_id; // id of :@name in Ruby
|
102
104
|
static ID server_id; // id of :server in Ruby
|
103
105
|
static ID otel_context_storage_id; // id of :__opentelemetry_context_storage__ in Ruby
|
106
|
+
static ID otel_fiber_context_storage_id; // id of :@opentelemetry_context in Ruby
|
104
107
|
|
105
108
|
// This is used by `thread_context_collector_on_gvl_running`. Because when that method gets called we're not sure if
|
106
109
|
// it's safe to access the state of the thread context collector, we store this setting as a global value. This does
|
@@ -110,16 +113,17 @@ static ID otel_context_storage_id; // id of :__opentelemetry_context_storage__ i
|
|
110
113
|
static uint32_t global_waiting_for_gvl_threshold_ns = MILLIS_AS_NS(10);
|
111
114
|
|
112
115
|
typedef enum { OTEL_CONTEXT_ENABLED_FALSE, OTEL_CONTEXT_ENABLED_ONLY, OTEL_CONTEXT_ENABLED_BOTH } otel_context_enabled;
|
116
|
+
typedef enum { OTEL_CONTEXT_SOURCE_UNKNOWN, OTEL_CONTEXT_SOURCE_FIBER_IVAR, OTEL_CONTEXT_SOURCE_FIBER_LOCAL } otel_context_source;
|
113
117
|
|
114
118
|
// Contains state for a single ThreadContext instance
|
115
|
-
struct
|
119
|
+
typedef struct {
|
116
120
|
// Note: Places in this file that usually need to be changed when this struct is changed are tagged with
|
117
121
|
// "Update this when modifying state struct"
|
118
122
|
|
119
123
|
// Required by Datadog::Profiling::Collectors::Stack as a scratch buffer during sampling
|
120
124
|
ddog_prof_Location *locations;
|
121
125
|
uint16_t max_frames;
|
122
|
-
// Hashmap <Thread Object,
|
126
|
+
// Hashmap <Thread Object, per_thread_context>
|
123
127
|
st_table *hash_map_per_thread_context;
|
124
128
|
// Datadog::Profiling::StackRecorder instance
|
125
129
|
VALUE recorder_instance;
|
@@ -139,6 +143,8 @@ struct thread_context_collector_state {
|
|
139
143
|
bool timeline_enabled;
|
140
144
|
// Used to control context collection
|
141
145
|
otel_context_enabled otel_context_enabled;
|
146
|
+
// Used to remember where otel context is being stored after we observe it the first time
|
147
|
+
otel_context_source otel_context_source;
|
142
148
|
// Used when calling monotonic_to_system_epoch_ns
|
143
149
|
monotonic_to_system_epoch_state time_converter_state;
|
144
150
|
// Used to identify the main thread, to give it a fallback name
|
@@ -162,10 +168,10 @@ struct thread_context_collector_state {
|
|
162
168
|
long wall_time_at_previous_gc_ns; // Will be INVALID_TIME unless there's accumulated time above
|
163
169
|
long wall_time_at_last_flushed_gc_event_ns; // Starts at 0 and then will always be valid
|
164
170
|
} gc_tracking;
|
165
|
-
};
|
171
|
+
} thread_context_collector_state;
|
166
172
|
|
167
173
|
// Tracks per-thread state
|
168
|
-
struct
|
174
|
+
typedef struct {
|
169
175
|
sampling_buffer *sampling_buffer;
|
170
176
|
char thread_id[THREAD_ID_LIMIT_CHARS];
|
171
177
|
ddog_CharSlice thread_id_char_slice;
|
@@ -181,21 +187,21 @@ struct per_thread_context {
|
|
181
187
|
long cpu_time_at_start_ns;
|
182
188
|
long wall_time_at_start_ns;
|
183
189
|
} gc_tracking;
|
184
|
-
};
|
190
|
+
} per_thread_context;
|
185
191
|
|
186
192
|
// Used to correlate profiles with traces
|
187
|
-
struct
|
193
|
+
typedef struct {
|
188
194
|
bool valid;
|
189
195
|
uint64_t local_root_span_id;
|
190
196
|
uint64_t span_id;
|
191
197
|
VALUE trace_endpoint;
|
192
|
-
};
|
198
|
+
} trace_identifiers;
|
193
199
|
|
194
|
-
struct
|
200
|
+
typedef struct {
|
195
201
|
VALUE span;
|
196
202
|
VALUE span_id;
|
197
203
|
VALUE trace_id;
|
198
|
-
};
|
204
|
+
} otel_span;
|
199
205
|
|
200
206
|
static void thread_context_collector_typed_data_mark(void *state_ptr);
|
201
207
|
static void thread_context_collector_typed_data_free(void *state_ptr);
|
@@ -203,70 +209,77 @@ static int hash_map_per_thread_context_mark(st_data_t key_thread, st_data_t _val
|
|
203
209
|
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
210
|
static VALUE _native_new(VALUE klass);
|
205
211
|
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);
|
212
|
+
static VALUE _native_sample(VALUE self, VALUE collector_instance, VALUE profiler_overhead_stack_thread, VALUE allow_exception);
|
207
213
|
static VALUE _native_on_gc_start(VALUE self, VALUE collector_instance);
|
208
214
|
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
|
215
|
+
static VALUE _native_sample_after_gc(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE allow_exception);
|
210
216
|
static void update_metrics_and_sample(
|
211
|
-
|
217
|
+
thread_context_collector_state *state,
|
212
218
|
VALUE thread_being_sampled,
|
213
219
|
VALUE stack_from_thread,
|
214
|
-
|
220
|
+
per_thread_context *thread_context,
|
215
221
|
sampling_buffer* sampling_buffer,
|
216
222
|
long current_cpu_time_ns,
|
217
223
|
long current_monotonic_wall_time_ns
|
218
224
|
);
|
219
225
|
static void trigger_sample_for_thread(
|
220
|
-
|
226
|
+
thread_context_collector_state *state,
|
221
227
|
VALUE thread,
|
222
228
|
VALUE stack_from_thread,
|
223
|
-
|
229
|
+
per_thread_context *thread_context,
|
224
230
|
sampling_buffer* sampling_buffer,
|
225
231
|
sample_values values,
|
226
232
|
long current_monotonic_wall_time_ns,
|
227
233
|
ddog_CharSlice *ruby_vm_type,
|
228
234
|
ddog_CharSlice *class_name,
|
229
|
-
bool is_gvl_waiting_state
|
235
|
+
bool is_gvl_waiting_state,
|
236
|
+
bool is_safe_to_allocate_objects
|
230
237
|
);
|
231
238
|
static VALUE _native_thread_list(VALUE self);
|
232
|
-
static
|
233
|
-
static
|
234
|
-
static void initialize_context(VALUE thread,
|
235
|
-
static void free_context(
|
239
|
+
static per_thread_context *get_or_create_context_for(VALUE thread, thread_context_collector_state *state);
|
240
|
+
static per_thread_context *get_context_for(VALUE thread, thread_context_collector_state *state);
|
241
|
+
static void initialize_context(VALUE thread, per_thread_context *thread_context, thread_context_collector_state *state);
|
242
|
+
static void free_context(per_thread_context* thread_context);
|
236
243
|
static VALUE _native_inspect(VALUE self, VALUE collector_instance);
|
237
|
-
static VALUE per_thread_context_st_table_as_ruby_hash(
|
244
|
+
static VALUE per_thread_context_st_table_as_ruby_hash(thread_context_collector_state *state);
|
238
245
|
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(
|
246
|
+
static VALUE stats_as_ruby_hash(thread_context_collector_state *state);
|
247
|
+
static VALUE gc_tracking_as_ruby_hash(thread_context_collector_state *state);
|
248
|
+
static void remove_context_for_dead_threads(thread_context_collector_state *state);
|
242
249
|
static int remove_if_dead_thread(st_data_t key_thread, st_data_t value_context, st_data_t _argument);
|
243
250
|
static VALUE _native_per_thread_context(VALUE self, VALUE collector_instance);
|
244
251
|
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(
|
252
|
+
static long cpu_time_now_ns(per_thread_context *thread_context);
|
246
253
|
static long thread_id_for(VALUE thread);
|
247
254
|
static VALUE _native_stats(VALUE self, VALUE collector_instance);
|
248
255
|
static VALUE _native_gc_tracking(VALUE self, VALUE collector_instance);
|
249
|
-
static void trace_identifiers_for(
|
256
|
+
static void trace_identifiers_for(
|
257
|
+
thread_context_collector_state *state,
|
258
|
+
VALUE thread,
|
259
|
+
trace_identifiers *trace_identifiers_result,
|
260
|
+
bool is_safe_to_allocate_objects
|
261
|
+
);
|
250
262
|
static bool should_collect_resource(VALUE root_span);
|
251
263
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
252
|
-
static VALUE thread_list(
|
264
|
+
static VALUE thread_list(thread_context_collector_state *state);
|
253
265
|
static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object);
|
254
266
|
static VALUE _native_new_empty_thread(VALUE self);
|
255
267
|
static ddog_CharSlice ruby_value_type_to_class_name(enum ruby_value_type type);
|
256
268
|
static void ddtrace_otel_trace_identifiers_for(
|
257
|
-
|
269
|
+
thread_context_collector_state *state,
|
258
270
|
VALUE *active_trace,
|
259
271
|
VALUE *root_span,
|
260
272
|
VALUE *numeric_span_id,
|
261
273
|
VALUE active_span,
|
262
|
-
VALUE otel_values
|
274
|
+
VALUE otel_values,
|
275
|
+
bool is_safe_to_allocate_objects
|
263
276
|
);
|
264
277
|
static VALUE _native_sample_skipped_allocation_samples(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE skipped_samples);
|
265
278
|
static bool handle_gvl_waiting(
|
266
|
-
|
279
|
+
thread_context_collector_state *state,
|
267
280
|
VALUE thread_being_sampled,
|
268
281
|
VALUE stack_from_thread,
|
269
|
-
|
282
|
+
per_thread_context *thread_context,
|
270
283
|
sampling_buffer* sampling_buffer,
|
271
284
|
long current_cpu_time_ns
|
272
285
|
);
|
@@ -276,12 +289,15 @@ static VALUE _native_on_gvl_running(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
276
289
|
static VALUE _native_sample_after_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread);
|
277
290
|
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
291
|
static void otel_without_ddtrace_trace_identifiers_for(
|
279
|
-
|
292
|
+
thread_context_collector_state *state,
|
280
293
|
VALUE thread,
|
281
|
-
|
294
|
+
trace_identifiers *trace_identifiers_result,
|
295
|
+
bool is_safe_to_allocate_objects
|
282
296
|
);
|
283
|
-
static
|
297
|
+
static otel_span otel_span_from(VALUE otel_context, VALUE otel_current_span_key);
|
284
298
|
static uint64_t otel_span_id_to_uint(VALUE otel_span_id);
|
299
|
+
static VALUE safely_lookup_hash_without_going_into_ruby_code(VALUE hash, VALUE key);
|
300
|
+
static VALUE _native_system_epoch_time_now_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
285
301
|
|
286
302
|
void collectors_thread_context_init(VALUE profiling_module) {
|
287
303
|
VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
|
@@ -302,7 +318,7 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
302
318
|
rb_define_singleton_method(collectors_thread_context_class, "_native_initialize", _native_initialize, -1);
|
303
319
|
rb_define_singleton_method(collectors_thread_context_class, "_native_inspect", _native_inspect, 1);
|
304
320
|
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,
|
321
|
+
rb_define_singleton_method(testing_module, "_native_sample", _native_sample, 3);
|
306
322
|
rb_define_singleton_method(testing_module, "_native_sample_allocation", _native_sample_allocation, 3);
|
307
323
|
rb_define_singleton_method(testing_module, "_native_on_gc_start", _native_on_gc_start, 1);
|
308
324
|
rb_define_singleton_method(testing_module, "_native_on_gc_finish", _native_on_gc_finish, 1);
|
@@ -313,6 +329,7 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
313
329
|
rb_define_singleton_method(testing_module, "_native_gc_tracking", _native_gc_tracking, 1);
|
314
330
|
rb_define_singleton_method(testing_module, "_native_new_empty_thread", _native_new_empty_thread, 0);
|
315
331
|
rb_define_singleton_method(testing_module, "_native_sample_skipped_allocation_samples", _native_sample_skipped_allocation_samples, 2);
|
332
|
+
rb_define_singleton_method(testing_module, "_native_system_epoch_time_now_ns", _native_system_epoch_time_now_ns, 1);
|
316
333
|
#ifndef NO_GVL_INSTRUMENTATION
|
317
334
|
rb_define_singleton_method(testing_module, "_native_on_gvl_waiting", _native_on_gvl_waiting, 1);
|
318
335
|
rb_define_singleton_method(testing_module, "_native_gvl_waiting_at_for", _native_gvl_waiting_at_for, 1);
|
@@ -338,6 +355,7 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
338
355
|
at_name_id = rb_intern_const("@name");
|
339
356
|
server_id = rb_intern_const("server");
|
340
357
|
otel_context_storage_id = rb_intern_const("__opentelemetry_context_storage__");
|
358
|
+
otel_fiber_context_storage_id = rb_intern_const("@opentelemetry_context");
|
341
359
|
|
342
360
|
#ifndef NO_GVL_INSTRUMENTATION
|
343
361
|
// This will raise if Ruby already ran out of thread-local keys
|
@@ -347,7 +365,7 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
347
365
|
gc_profiling_init();
|
348
366
|
}
|
349
367
|
|
350
|
-
// This structure is used to define a Ruby object that stores a pointer to a
|
368
|
+
// This structure is used to define a Ruby object that stores a pointer to a thread_context_collector_state
|
351
369
|
// See also https://github.com/ruby/ruby/blob/master/doc/extension.rdoc for how this works
|
352
370
|
static const rb_data_type_t thread_context_collector_typed_data = {
|
353
371
|
.wrap_struct_name = "Datadog::Profiling::Collectors::ThreadContext",
|
@@ -363,7 +381,7 @@ static const rb_data_type_t thread_context_collector_typed_data = {
|
|
363
381
|
// 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
382
|
// so that they don't get garbage collected
|
365
383
|
static void thread_context_collector_typed_data_mark(void *state_ptr) {
|
366
|
-
|
384
|
+
thread_context_collector_state *state = (thread_context_collector_state *) state_ptr;
|
367
385
|
|
368
386
|
// Update this when modifying state struct
|
369
387
|
rb_gc_mark(state->recorder_instance);
|
@@ -374,7 +392,7 @@ static void thread_context_collector_typed_data_mark(void *state_ptr) {
|
|
374
392
|
}
|
375
393
|
|
376
394
|
static void thread_context_collector_typed_data_free(void *state_ptr) {
|
377
|
-
|
395
|
+
thread_context_collector_state *state = (thread_context_collector_state *) state_ptr;
|
378
396
|
|
379
397
|
// Update this when modifying state struct
|
380
398
|
|
@@ -399,13 +417,13 @@ static int hash_map_per_thread_context_mark(st_data_t key_thread, DDTRACE_UNUSED
|
|
399
417
|
|
400
418
|
// Used to clear each of the per_thread_contexts inside the hash_map_per_thread_context
|
401
419
|
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
|
-
|
420
|
+
per_thread_context *thread_context = (per_thread_context*) value_per_thread_context;
|
403
421
|
free_context(thread_context);
|
404
422
|
return ST_CONTINUE;
|
405
423
|
}
|
406
424
|
|
407
425
|
static VALUE _native_new(VALUE klass) {
|
408
|
-
|
426
|
+
thread_context_collector_state *state = ruby_xcalloc(1, sizeof(thread_context_collector_state));
|
409
427
|
|
410
428
|
// Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
|
411
429
|
// being leaked.
|
@@ -423,6 +441,7 @@ static VALUE _native_new(VALUE klass) {
|
|
423
441
|
state->endpoint_collection_enabled = true;
|
424
442
|
state->timeline_enabled = true;
|
425
443
|
state->otel_context_enabled = OTEL_CONTEXT_ENABLED_FALSE;
|
444
|
+
state->otel_context_source = OTEL_CONTEXT_SOURCE_UNKNOWN;
|
426
445
|
state->time_converter_state = (monotonic_to_system_epoch_state) MONOTONIC_TO_SYSTEM_EPOCH_INITIALIZER;
|
427
446
|
VALUE main_thread = rb_thread_main();
|
428
447
|
state->main_thread = main_thread;
|
@@ -461,8 +480,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
461
480
|
ENFORCE_BOOLEAN(timeline_enabled);
|
462
481
|
ENFORCE_TYPE(waiting_for_gvl_threshold_ns, T_FIXNUM);
|
463
482
|
|
464
|
-
|
465
|
-
TypedData_Get_Struct(self_instance,
|
483
|
+
thread_context_collector_state *state;
|
484
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
466
485
|
|
467
486
|
// Update this when modifying state struct
|
468
487
|
state->max_frames = sampling_buffer_check_max_frames(NUM2INT(max_frames));
|
@@ -496,40 +515,53 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
496
515
|
|
497
516
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
498
517
|
// 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) {
|
518
|
+
static VALUE _native_sample(DDTRACE_UNUSED VALUE _self, VALUE collector_instance, VALUE profiler_overhead_stack_thread, VALUE allow_exception) {
|
519
|
+
ENFORCE_BOOLEAN(allow_exception);
|
520
|
+
|
500
521
|
if (!is_thread_alive(profiler_overhead_stack_thread)) rb_raise(rb_eArgError, "Unexpected: profiler_overhead_stack_thread is not alive");
|
501
522
|
|
523
|
+
if (allow_exception == Qfalse) debug_enter_unsafe_context();
|
524
|
+
|
502
525
|
thread_context_collector_sample(collector_instance, monotonic_wall_time_now_ns(RAISE_ON_FAILURE), profiler_overhead_stack_thread);
|
526
|
+
|
527
|
+
if (allow_exception == Qfalse) debug_leave_unsafe_context();
|
528
|
+
|
503
529
|
return Qtrue;
|
504
530
|
}
|
505
531
|
|
506
532
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
507
533
|
// It SHOULD NOT be used for other purposes.
|
508
534
|
static VALUE _native_on_gc_start(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
|
535
|
+
debug_enter_unsafe_context();
|
536
|
+
|
509
537
|
thread_context_collector_on_gc_start(collector_instance);
|
538
|
+
|
539
|
+
debug_leave_unsafe_context();
|
540
|
+
|
510
541
|
return Qtrue;
|
511
542
|
}
|
512
543
|
|
513
544
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
514
545
|
// It SHOULD NOT be used for other purposes.
|
515
546
|
static VALUE _native_on_gc_finish(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
|
547
|
+
debug_enter_unsafe_context();
|
548
|
+
|
516
549
|
(void) !thread_context_collector_on_gc_finish(collector_instance);
|
550
|
+
|
551
|
+
debug_leave_unsafe_context();
|
552
|
+
|
517
553
|
return Qtrue;
|
518
554
|
}
|
519
555
|
|
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);
|
556
|
+
static VALUE _native_sample_after_gc(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE allow_exception) {
|
557
|
+
ENFORCE_BOOLEAN(allow_exception);
|
524
558
|
|
525
|
-
|
526
|
-
TypedData_Get_Struct(collector_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
|
527
|
-
|
528
|
-
if (reset_monotonic_to_system_state == Qtrue) {
|
529
|
-
state->time_converter_state = (monotonic_to_system_epoch_state) MONOTONIC_TO_SYSTEM_EPOCH_INITIALIZER;
|
530
|
-
}
|
559
|
+
if (allow_exception == Qfalse) debug_enter_unsafe_context();
|
531
560
|
|
532
561
|
thread_context_collector_sample_after_gc(collector_instance);
|
562
|
+
|
563
|
+
if (allow_exception == Qfalse) debug_leave_unsafe_context();
|
564
|
+
|
533
565
|
return Qtrue;
|
534
566
|
}
|
535
567
|
|
@@ -544,11 +576,11 @@ static VALUE _native_sample_after_gc(DDTRACE_UNUSED VALUE self, VALUE collector_
|
|
544
576
|
// The `profiler_overhead_stack_thread` is used to attribute the profiler overhead to a stack borrowed from a different thread
|
545
577
|
// (belonging to ddtrace), so that the overhead is visible in the profile rather than blamed on user code.
|
546
578
|
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,
|
579
|
+
thread_context_collector_state *state;
|
580
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
549
581
|
|
550
582
|
VALUE current_thread = rb_thread_current();
|
551
|
-
|
583
|
+
per_thread_context *current_thread_context = get_or_create_context_for(current_thread, state);
|
552
584
|
long cpu_time_at_sample_start_for_current_thread = cpu_time_now_ns(current_thread_context);
|
553
585
|
|
554
586
|
VALUE threads = thread_list(state);
|
@@ -556,7 +588,7 @@ void thread_context_collector_sample(VALUE self_instance, long current_monotonic
|
|
556
588
|
const long thread_count = RARRAY_LEN(threads);
|
557
589
|
for (long i = 0; i < thread_count; i++) {
|
558
590
|
VALUE thread = RARRAY_AREF(threads, i);
|
559
|
-
|
591
|
+
per_thread_context *thread_context = get_or_create_context_for(thread, state);
|
560
592
|
|
561
593
|
// We account for cpu-time for the current thread in a different way -- we use the cpu-time at sampling start, to avoid
|
562
594
|
// blaming the time the profiler took on whatever's running on the thread right now
|
@@ -592,10 +624,10 @@ void thread_context_collector_sample(VALUE self_instance, long current_monotonic
|
|
592
624
|
}
|
593
625
|
|
594
626
|
static void update_metrics_and_sample(
|
595
|
-
|
627
|
+
thread_context_collector_state *state,
|
596
628
|
VALUE thread_being_sampled,
|
597
629
|
VALUE stack_from_thread, // This can be different when attributing profiler overhead using a different stack
|
598
|
-
|
630
|
+
per_thread_context *thread_context,
|
599
631
|
sampling_buffer* sampling_buffer,
|
600
632
|
long current_cpu_time_ns,
|
601
633
|
long current_monotonic_wall_time_ns
|
@@ -647,7 +679,8 @@ static void update_metrics_and_sample(
|
|
647
679
|
current_monotonic_wall_time_ns,
|
648
680
|
NULL,
|
649
681
|
NULL,
|
650
|
-
is_gvl_waiting_state
|
682
|
+
is_gvl_waiting_state,
|
683
|
+
/* is_safe_to_allocate_objects: */ true // We called from a context that's safe to run any regular code, including allocations
|
651
684
|
);
|
652
685
|
}
|
653
686
|
|
@@ -662,12 +695,12 @@ static void update_metrics_and_sample(
|
|
662
695
|
// Assumption 1: This function is called in a thread that is holding the Global VM Lock. Caller is responsible for enforcing this.
|
663
696
|
// Assumption 2: This function is called from the main Ractor (if Ruby has support for Ractors).
|
664
697
|
void thread_context_collector_on_gc_start(VALUE self_instance) {
|
665
|
-
|
698
|
+
thread_context_collector_state *state;
|
666
699
|
if (!rb_typeddata_is_kind_of(self_instance, &thread_context_collector_typed_data)) return;
|
667
700
|
// This should never fail the the above check passes
|
668
|
-
TypedData_Get_Struct(self_instance,
|
701
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
669
702
|
|
670
|
-
|
703
|
+
per_thread_context *thread_context = get_context_for(rb_thread_current(), state);
|
671
704
|
|
672
705
|
// If there was no previously-existing context for this thread, we won't allocate one (see safety). For now we just drop
|
673
706
|
// 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 +728,12 @@ void thread_context_collector_on_gc_start(VALUE self_instance) {
|
|
695
728
|
// Assumption 2: This function is called from the main Ractor (if Ruby has support for Ractors).
|
696
729
|
__attribute__((warn_unused_result))
|
697
730
|
bool thread_context_collector_on_gc_finish(VALUE self_instance) {
|
698
|
-
|
731
|
+
thread_context_collector_state *state;
|
699
732
|
if (!rb_typeddata_is_kind_of(self_instance, &thread_context_collector_typed_data)) return false;
|
700
733
|
// This should never fail the the above check passes
|
701
|
-
TypedData_Get_Struct(self_instance,
|
734
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
702
735
|
|
703
|
-
|
736
|
+
per_thread_context *thread_context = get_context_for(rb_thread_current(), state);
|
704
737
|
|
705
738
|
// If there was no previously-existing context for this thread, we won't allocate one (see safety). We keep a metric for
|
706
739
|
// how often this happens -- see on_gc_start.
|
@@ -773,8 +806,8 @@ bool thread_context_collector_on_gc_finish(VALUE self_instance) {
|
|
773
806
|
// Assumption 3: Unlike `on_gc_start` and `on_gc_finish`, this method is allowed to allocate memory as needed.
|
774
807
|
// Assumption 4: This function is called from the main Ractor (if Ruby has support for Ractors).
|
775
808
|
VALUE thread_context_collector_sample_after_gc(VALUE self_instance) {
|
776
|
-
|
777
|
-
TypedData_Get_Struct(self_instance,
|
809
|
+
thread_context_collector_state *state;
|
810
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
778
811
|
|
779
812
|
if (state->gc_tracking.wall_time_at_previous_gc_ns == INVALID_TIME) {
|
780
813
|
rb_raise(rb_eRuntimeError, "BUG: Unexpected call to sample_after_gc without valid GC information available");
|
@@ -823,17 +856,20 @@ VALUE thread_context_collector_sample_after_gc(VALUE self_instance) {
|
|
823
856
|
}
|
824
857
|
|
825
858
|
static void trigger_sample_for_thread(
|
826
|
-
|
859
|
+
thread_context_collector_state *state,
|
827
860
|
VALUE thread,
|
828
861
|
VALUE stack_from_thread, // This can be different when attributing profiler overhead using a different stack
|
829
|
-
|
862
|
+
per_thread_context *thread_context,
|
830
863
|
sampling_buffer* sampling_buffer,
|
831
864
|
sample_values values,
|
832
865
|
long current_monotonic_wall_time_ns,
|
833
866
|
// These two labels are only used for allocation profiling; @ivoanjo: may want to refactor this at some point?
|
834
867
|
ddog_CharSlice *ruby_vm_type,
|
835
868
|
ddog_CharSlice *class_name,
|
836
|
-
bool is_gvl_waiting_state
|
869
|
+
bool is_gvl_waiting_state,
|
870
|
+
// If the Ruby VM is at a state that can allocate objects safely, or not. Added for allocation profiling: we're not
|
871
|
+
// allowed to allocate objects (or raise exceptions) when inside the NEWOBJ tracepoint.
|
872
|
+
bool is_safe_to_allocate_objects
|
837
873
|
) {
|
838
874
|
int max_label_count =
|
839
875
|
1 + // thread id
|
@@ -871,12 +907,12 @@ static void trigger_sample_for_thread(
|
|
871
907
|
};
|
872
908
|
}
|
873
909
|
|
874
|
-
|
875
|
-
trace_identifiers_for(state, thread, &trace_identifiers_result);
|
910
|
+
trace_identifiers trace_identifiers_result = {.valid = false, .trace_endpoint = Qnil};
|
911
|
+
trace_identifiers_for(state, thread, &trace_identifiers_result, is_safe_to_allocate_objects);
|
876
912
|
|
877
913
|
if (!trace_identifiers_result.valid && state->otel_context_enabled != OTEL_CONTEXT_ENABLED_FALSE) {
|
878
914
|
// 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);
|
915
|
+
otel_without_ddtrace_trace_identifiers_for(state, thread, &trace_identifiers_result, is_safe_to_allocate_objects);
|
880
916
|
}
|
881
917
|
|
882
918
|
if (trace_identifiers_result.valid) {
|
@@ -970,18 +1006,24 @@ static void trigger_sample_for_thread(
|
|
970
1006
|
// It SHOULD NOT be used for other purposes.
|
971
1007
|
static VALUE _native_thread_list(DDTRACE_UNUSED VALUE _self) {
|
972
1008
|
VALUE result = rb_ary_new();
|
1009
|
+
|
1010
|
+
debug_enter_unsafe_context();
|
1011
|
+
|
973
1012
|
ddtrace_thread_list(result);
|
1013
|
+
|
1014
|
+
debug_leave_unsafe_context();
|
1015
|
+
|
974
1016
|
return result;
|
975
1017
|
}
|
976
1018
|
|
977
|
-
static
|
978
|
-
|
1019
|
+
static per_thread_context *get_or_create_context_for(VALUE thread, thread_context_collector_state *state) {
|
1020
|
+
per_thread_context* thread_context = NULL;
|
979
1021
|
st_data_t value_context = 0;
|
980
1022
|
|
981
1023
|
if (st_lookup(state->hash_map_per_thread_context, (st_data_t) thread, &value_context)) {
|
982
|
-
thread_context = (
|
1024
|
+
thread_context = (per_thread_context*) value_context;
|
983
1025
|
} else {
|
984
|
-
thread_context = ruby_xcalloc(1, sizeof(
|
1026
|
+
thread_context = ruby_xcalloc(1, sizeof(per_thread_context));
|
985
1027
|
initialize_context(thread, thread_context, state);
|
986
1028
|
st_insert(state->hash_map_per_thread_context, (st_data_t) thread, (st_data_t) thread_context);
|
987
1029
|
}
|
@@ -989,12 +1031,12 @@ static struct per_thread_context *get_or_create_context_for(VALUE thread, struct
|
|
989
1031
|
return thread_context;
|
990
1032
|
}
|
991
1033
|
|
992
|
-
static
|
993
|
-
|
1034
|
+
static per_thread_context *get_context_for(VALUE thread, thread_context_collector_state *state) {
|
1035
|
+
per_thread_context* thread_context = NULL;
|
994
1036
|
st_data_t value_context = 0;
|
995
1037
|
|
996
1038
|
if (st_lookup(state->hash_map_per_thread_context, (st_data_t) thread, &value_context)) {
|
997
|
-
thread_context = (
|
1039
|
+
thread_context = (per_thread_context*) value_context;
|
998
1040
|
}
|
999
1041
|
|
1000
1042
|
return thread_context;
|
@@ -1021,7 +1063,7 @@ static bool is_logging_gem_monkey_patch(VALUE invoke_file_location) {
|
|
1021
1063
|
return strncmp(invoke_file + invoke_file_len - logging_gem_path_len, LOGGING_GEM_PATH, logging_gem_path_len) == 0;
|
1022
1064
|
}
|
1023
1065
|
|
1024
|
-
static void initialize_context(VALUE thread,
|
1066
|
+
static void initialize_context(VALUE thread, per_thread_context *thread_context, thread_context_collector_state *state) {
|
1025
1067
|
thread_context->sampling_buffer = sampling_buffer_new(state->max_frames, state->locations);
|
1026
1068
|
|
1027
1069
|
snprintf(thread_context->thread_id, THREAD_ID_LIMIT_CHARS, "%"PRIu64" (%lu)", native_thread_id_for(thread), (unsigned long) thread_id_for(thread));
|
@@ -1078,14 +1120,14 @@ static void initialize_context(VALUE thread, struct per_thread_context *thread_c
|
|
1078
1120
|
#endif
|
1079
1121
|
}
|
1080
1122
|
|
1081
|
-
static void free_context(
|
1123
|
+
static void free_context(per_thread_context* thread_context) {
|
1082
1124
|
sampling_buffer_free(thread_context->sampling_buffer);
|
1083
1125
|
ruby_xfree(thread_context);
|
1084
1126
|
}
|
1085
1127
|
|
1086
1128
|
static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
1087
|
-
|
1088
|
-
TypedData_Get_Struct(collector_instance,
|
1129
|
+
thread_context_collector_state *state;
|
1130
|
+
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1089
1131
|
|
1090
1132
|
VALUE result = rb_str_new2(" (native state)");
|
1091
1133
|
|
@@ -1113,7 +1155,7 @@ static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instanc
|
|
1113
1155
|
return result;
|
1114
1156
|
}
|
1115
1157
|
|
1116
|
-
static VALUE per_thread_context_st_table_as_ruby_hash(
|
1158
|
+
static VALUE per_thread_context_st_table_as_ruby_hash(thread_context_collector_state *state) {
|
1117
1159
|
VALUE result = rb_hash_new();
|
1118
1160
|
st_foreach(state->hash_map_per_thread_context, per_thread_context_as_ruby_hash, result);
|
1119
1161
|
return result;
|
@@ -1121,7 +1163,7 @@ static VALUE per_thread_context_st_table_as_ruby_hash(struct thread_context_coll
|
|
1121
1163
|
|
1122
1164
|
static int per_thread_context_as_ruby_hash(st_data_t key_thread, st_data_t value_context, st_data_t result_hash) {
|
1123
1165
|
VALUE thread = (VALUE) key_thread;
|
1124
|
-
|
1166
|
+
per_thread_context *thread_context = (per_thread_context*) value_context;
|
1125
1167
|
VALUE result = (VALUE) result_hash;
|
1126
1168
|
VALUE context_as_hash = rb_hash_new();
|
1127
1169
|
rb_hash_aset(result, thread, context_as_hash);
|
@@ -1146,7 +1188,7 @@ static int per_thread_context_as_ruby_hash(st_data_t key_thread, st_data_t value
|
|
1146
1188
|
return ST_CONTINUE;
|
1147
1189
|
}
|
1148
1190
|
|
1149
|
-
static VALUE stats_as_ruby_hash(
|
1191
|
+
static VALUE stats_as_ruby_hash(thread_context_collector_state *state) {
|
1150
1192
|
// Update this when modifying state struct (stats inner struct)
|
1151
1193
|
VALUE stats_as_hash = rb_hash_new();
|
1152
1194
|
VALUE arguments[] = {
|
@@ -1157,7 +1199,7 @@ static VALUE stats_as_ruby_hash(struct thread_context_collector_state *state) {
|
|
1157
1199
|
return stats_as_hash;
|
1158
1200
|
}
|
1159
1201
|
|
1160
|
-
static VALUE gc_tracking_as_ruby_hash(
|
1202
|
+
static VALUE gc_tracking_as_ruby_hash(thread_context_collector_state *state) {
|
1161
1203
|
// Update this when modifying state struct (gc_tracking inner struct)
|
1162
1204
|
VALUE result = rb_hash_new();
|
1163
1205
|
VALUE arguments[] = {
|
@@ -1170,13 +1212,13 @@ static VALUE gc_tracking_as_ruby_hash(struct thread_context_collector_state *sta
|
|
1170
1212
|
return result;
|
1171
1213
|
}
|
1172
1214
|
|
1173
|
-
static void remove_context_for_dead_threads(
|
1215
|
+
static void remove_context_for_dead_threads(thread_context_collector_state *state) {
|
1174
1216
|
st_foreach(state->hash_map_per_thread_context, remove_if_dead_thread, 0 /* unused */);
|
1175
1217
|
}
|
1176
1218
|
|
1177
1219
|
static int remove_if_dead_thread(st_data_t key_thread, st_data_t value_context, DDTRACE_UNUSED st_data_t _argument) {
|
1178
1220
|
VALUE thread = (VALUE) key_thread;
|
1179
|
-
|
1221
|
+
per_thread_context* thread_context = (per_thread_context*) value_context;
|
1180
1222
|
|
1181
1223
|
if (is_thread_alive(thread)) return ST_CONTINUE;
|
1182
1224
|
|
@@ -1189,8 +1231,8 @@ static int remove_if_dead_thread(st_data_t key_thread, st_data_t value_context,
|
|
1189
1231
|
//
|
1190
1232
|
// Returns the whole contents of the per_thread_context structs being tracked.
|
1191
1233
|
static VALUE _native_per_thread_context(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
1192
|
-
|
1193
|
-
TypedData_Get_Struct(collector_instance,
|
1234
|
+
thread_context_collector_state *state;
|
1235
|
+
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1194
1236
|
|
1195
1237
|
return per_thread_context_st_table_as_ruby_hash(state);
|
1196
1238
|
}
|
@@ -1235,7 +1277,7 @@ static long update_time_since_previous_sample(long *time_at_previous_sample_ns,
|
|
1235
1277
|
}
|
1236
1278
|
|
1237
1279
|
// Safety: This function is assumed never to raise exceptions by callers
|
1238
|
-
static long cpu_time_now_ns(
|
1280
|
+
static long cpu_time_now_ns(per_thread_context *thread_context) {
|
1239
1281
|
thread_cpu_time cpu_time = thread_cpu_time_for(thread_context->thread_cpu_time_id);
|
1240
1282
|
|
1241
1283
|
if (!cpu_time.valid) {
|
@@ -1266,15 +1308,15 @@ static long thread_id_for(VALUE thread) {
|
|
1266
1308
|
}
|
1267
1309
|
|
1268
1310
|
VALUE enforce_thread_context_collector_instance(VALUE object) {
|
1269
|
-
|
1311
|
+
ENFORCE_TYPED_DATA(object, &thread_context_collector_typed_data);
|
1270
1312
|
return object;
|
1271
1313
|
}
|
1272
1314
|
|
1273
1315
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
1274
1316
|
// It SHOULD NOT be used for other purposes.
|
1275
1317
|
static VALUE _native_stats(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
1276
|
-
|
1277
|
-
TypedData_Get_Struct(collector_instance,
|
1318
|
+
thread_context_collector_state *state;
|
1319
|
+
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1278
1320
|
|
1279
1321
|
return stats_as_ruby_hash(state);
|
1280
1322
|
}
|
@@ -1282,14 +1324,19 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE _self, VALUE collector_instance)
|
|
1282
1324
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
1283
1325
|
// It SHOULD NOT be used for other purposes.
|
1284
1326
|
static VALUE _native_gc_tracking(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
1285
|
-
|
1286
|
-
TypedData_Get_Struct(collector_instance,
|
1327
|
+
thread_context_collector_state *state;
|
1328
|
+
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1287
1329
|
|
1288
1330
|
return gc_tracking_as_ruby_hash(state);
|
1289
1331
|
}
|
1290
1332
|
|
1291
1333
|
// 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(
|
1334
|
+
static void trace_identifiers_for(
|
1335
|
+
thread_context_collector_state *state,
|
1336
|
+
VALUE thread,
|
1337
|
+
trace_identifiers *trace_identifiers_result,
|
1338
|
+
bool is_safe_to_allocate_objects
|
1339
|
+
) {
|
1293
1340
|
if (state->otel_context_enabled == OTEL_CONTEXT_ENABLED_ONLY) return;
|
1294
1341
|
if (state->tracer_context_key == MISSING_TRACER_CONTEXT_KEY) return;
|
1295
1342
|
|
@@ -1308,7 +1355,9 @@ static void trace_identifiers_for(struct thread_context_collector_state *state,
|
|
1308
1355
|
|
1309
1356
|
VALUE numeric_span_id = Qnil;
|
1310
1357
|
|
1311
|
-
if (otel_values != Qnil)
|
1358
|
+
if (otel_values != Qnil) {
|
1359
|
+
ddtrace_otel_trace_identifiers_for(state, &active_trace, &root_span, &numeric_span_id, active_span, otel_values, is_safe_to_allocate_objects);
|
1360
|
+
}
|
1312
1361
|
|
1313
1362
|
if (root_span == Qnil || (active_span == Qnil && numeric_span_id == Qnil)) return;
|
1314
1363
|
|
@@ -1365,8 +1414,8 @@ static bool should_collect_resource(VALUE root_span) {
|
|
1365
1414
|
// Assumption: This method gets called BEFORE restarting profiling -- e.g. there are no components attempting to
|
1366
1415
|
// trigger samples at the same time.
|
1367
1416
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
|
1368
|
-
|
1369
|
-
TypedData_Get_Struct(collector_instance,
|
1417
|
+
thread_context_collector_state *state;
|
1418
|
+
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1370
1419
|
|
1371
1420
|
// Release all context memory before clearing the existing context
|
1372
1421
|
st_foreach(state->hash_map_per_thread_context, hash_map_per_thread_context_free_values, 0 /* unused */);
|
@@ -1380,7 +1429,7 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector
|
|
1380
1429
|
return Qtrue;
|
1381
1430
|
}
|
1382
1431
|
|
1383
|
-
static VALUE thread_list(
|
1432
|
+
static VALUE thread_list(thread_context_collector_state *state) {
|
1384
1433
|
VALUE result = state->thread_list_buffer;
|
1385
1434
|
rb_ary_clear(result);
|
1386
1435
|
ddtrace_thread_list(result);
|
@@ -1388,8 +1437,8 @@ static VALUE thread_list(struct thread_context_collector_state *state) {
|
|
1388
1437
|
}
|
1389
1438
|
|
1390
1439
|
void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object) {
|
1391
|
-
|
1392
|
-
TypedData_Get_Struct(self_instance,
|
1440
|
+
thread_context_collector_state *state;
|
1441
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1393
1442
|
|
1394
1443
|
VALUE current_thread = rb_thread_current();
|
1395
1444
|
|
@@ -1400,11 +1449,8 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
1400
1449
|
|
1401
1450
|
// Since this is stack allocated, be careful about moving it
|
1402
1451
|
ddog_CharSlice class_name;
|
1403
|
-
ddog_CharSlice *optional_class_name = NULL;
|
1404
1452
|
char imemo_type[100];
|
1405
1453
|
|
1406
|
-
optional_class_name = &class_name;
|
1407
|
-
|
1408
1454
|
if (
|
1409
1455
|
type == RUBY_T_OBJECT ||
|
1410
1456
|
type == RUBY_T_CLASS ||
|
@@ -1460,9 +1506,9 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
1460
1506
|
class_name = ruby_vm_type; // For other weird internal things we just use the VM type
|
1461
1507
|
}
|
1462
1508
|
|
1463
|
-
track_object(state->recorder_instance, new_object, sample_weight,
|
1509
|
+
track_object(state->recorder_instance, new_object, sample_weight, class_name);
|
1464
1510
|
|
1465
|
-
|
1511
|
+
per_thread_context *thread_context = get_or_create_context_for(current_thread, state);
|
1466
1512
|
|
1467
1513
|
trigger_sample_for_thread(
|
1468
1514
|
state,
|
@@ -1473,15 +1519,21 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
1473
1519
|
(sample_values) {.alloc_samples = sample_weight, .alloc_samples_unscaled = 1, .heap_sample = true},
|
1474
1520
|
INVALID_TIME, // For now we're not collecting timestamps for allocation events, as per profiling team internal discussions
|
1475
1521
|
&ruby_vm_type,
|
1476
|
-
|
1477
|
-
false
|
1522
|
+
&class_name,
|
1523
|
+
/* is_gvl_waiting_state: */ false,
|
1524
|
+
/* is_safe_to_allocate_objects: */ false // Not safe to allocate further inside the NEWOBJ tracepoint
|
1478
1525
|
);
|
1479
1526
|
}
|
1480
1527
|
|
1481
1528
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
1482
1529
|
// It SHOULD NOT be used for other purposes.
|
1483
1530
|
static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object) {
|
1531
|
+
debug_enter_unsafe_context();
|
1532
|
+
|
1484
1533
|
thread_context_collector_sample_allocation(collector_instance, NUM2UINT(sample_weight), new_object);
|
1534
|
+
|
1535
|
+
debug_leave_unsafe_context();
|
1536
|
+
|
1485
1537
|
return Qtrue;
|
1486
1538
|
}
|
1487
1539
|
|
@@ -1529,11 +1581,18 @@ static VALUE read_otel_current_span_key_const(DDTRACE_UNUSED VALUE _unused) {
|
|
1529
1581
|
return rb_const_get(trace_module, rb_intern("CURRENT_SPAN_KEY"));
|
1530
1582
|
}
|
1531
1583
|
|
1532
|
-
static VALUE get_otel_current_span_key(
|
1584
|
+
static VALUE get_otel_current_span_key(thread_context_collector_state *state, bool is_safe_to_allocate_objects) {
|
1533
1585
|
if (state->otel_current_span_key == Qtrue) { // Qtrue means we haven't tried to extract it yet
|
1586
|
+
if (!is_safe_to_allocate_objects) {
|
1587
|
+
// Calling read_otel_current_span_key_const below can trigger exceptions and arbitrary Ruby code running (e.g.
|
1588
|
+
// `const_missing`, etc). Not safe to call in this situation, so we just skip otel info for this sample.
|
1589
|
+
return Qnil;
|
1590
|
+
}
|
1591
|
+
|
1534
1592
|
// If this fails, we want to fail gracefully, rather than raise an exception (e.g. if the opentelemetry gem
|
1535
1593
|
// gets refactored, we should not fall on our face)
|
1536
1594
|
VALUE span_key = rb_protect(read_otel_current_span_key_const, Qnil, NULL);
|
1595
|
+
rb_set_errinfo(Qnil); // **Clear any pending exception after ignoring it**
|
1537
1596
|
|
1538
1597
|
// 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
1598
|
state->otel_current_span_key = span_key;
|
@@ -1545,12 +1604,13 @@ static VALUE get_otel_current_span_key(struct thread_context_collector_state *st
|
|
1545
1604
|
// This method gets used when ddtrace is being used indirectly via the opentelemetry APIs. Information gets stored slightly
|
1546
1605
|
// differently, and this codepath handles it.
|
1547
1606
|
static void ddtrace_otel_trace_identifiers_for(
|
1548
|
-
|
1607
|
+
thread_context_collector_state *state,
|
1549
1608
|
VALUE *active_trace,
|
1550
1609
|
VALUE *root_span,
|
1551
1610
|
VALUE *numeric_span_id,
|
1552
1611
|
VALUE active_span,
|
1553
|
-
VALUE otel_values
|
1612
|
+
VALUE otel_values,
|
1613
|
+
bool is_safe_to_allocate_objects
|
1554
1614
|
) {
|
1555
1615
|
VALUE resolved_numeric_span_id =
|
1556
1616
|
active_span == Qnil ?
|
@@ -1561,7 +1621,7 @@ static void ddtrace_otel_trace_identifiers_for(
|
|
1561
1621
|
|
1562
1622
|
if (resolved_numeric_span_id == Qnil) return;
|
1563
1623
|
|
1564
|
-
VALUE otel_current_span_key = get_otel_current_span_key(state);
|
1624
|
+
VALUE otel_current_span_key = get_otel_current_span_key(state, is_safe_to_allocate_objects);
|
1565
1625
|
if (otel_current_span_key == Qnil) return;
|
1566
1626
|
VALUE current_trace = *active_trace;
|
1567
1627
|
|
@@ -1569,7 +1629,7 @@ static void ddtrace_otel_trace_identifiers_for(
|
|
1569
1629
|
// trace and span representing it. Each ddtrace trace is then connected to the previous otel span, forming a linked
|
1570
1630
|
// list. The local root span is going to be the trace/span we find at the end of this linked list.
|
1571
1631
|
while (otel_values != Qnil) {
|
1572
|
-
VALUE otel_span =
|
1632
|
+
VALUE otel_span = safely_lookup_hash_without_going_into_ruby_code(otel_values, otel_current_span_key);
|
1573
1633
|
if (otel_span == Qnil) break;
|
1574
1634
|
VALUE next_trace = rb_ivar_get(otel_span, at_datadog_trace_id);
|
1575
1635
|
if (next_trace == Qnil) break;
|
@@ -1588,8 +1648,8 @@ static void ddtrace_otel_trace_identifiers_for(
|
|
1588
1648
|
}
|
1589
1649
|
|
1590
1650
|
void thread_context_collector_sample_skipped_allocation_samples(VALUE self_instance, unsigned int skipped_samples) {
|
1591
|
-
|
1592
|
-
TypedData_Get_Struct(self_instance,
|
1651
|
+
thread_context_collector_state *state;
|
1652
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1593
1653
|
|
1594
1654
|
ddog_prof_Label labels[] = {
|
1595
1655
|
// Providing .num = 0 should not be needed but the tracer-2.7 docker image ships a buggy gcc that complains about this
|
@@ -1612,10 +1672,51 @@ void thread_context_collector_sample_skipped_allocation_samples(VALUE self_insta
|
|
1612
1672
|
}
|
1613
1673
|
|
1614
1674
|
static VALUE _native_sample_skipped_allocation_samples(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE skipped_samples) {
|
1675
|
+
debug_enter_unsafe_context();
|
1676
|
+
|
1615
1677
|
thread_context_collector_sample_skipped_allocation_samples(collector_instance, NUM2UINT(skipped_samples));
|
1678
|
+
|
1679
|
+
debug_leave_unsafe_context();
|
1680
|
+
|
1616
1681
|
return Qtrue;
|
1617
1682
|
}
|
1618
1683
|
|
1684
|
+
#ifndef NO_CURRENT_FIBER_FOR // Ruby 3.1+
|
1685
|
+
static VALUE otel_context_storage_for(thread_context_collector_state *state, VALUE thread) {
|
1686
|
+
if (state->otel_context_source == OTEL_CONTEXT_SOURCE_FIBER_IVAR) { // otel-api 1.5+
|
1687
|
+
VALUE current_fiber = current_fiber_for(thread);
|
1688
|
+
return current_fiber == Qnil ? Qnil : rb_ivar_get(current_fiber, otel_fiber_context_storage_id /* @opentelemetry_context */);
|
1689
|
+
}
|
1690
|
+
|
1691
|
+
if (state->otel_context_source == OTEL_CONTEXT_SOURCE_FIBER_LOCAL) { // otel-api < 1.5
|
1692
|
+
return rb_thread_local_aref(thread, otel_context_storage_id /* __opentelemetry_context_storage__ */);
|
1693
|
+
}
|
1694
|
+
|
1695
|
+
// If we got here, it means we never observed a context being set. Let's probe which one to use.
|
1696
|
+
VALUE current_fiber = current_fiber_for(thread);
|
1697
|
+
if (current_fiber != Qnil) {
|
1698
|
+
VALUE context_storage = rb_ivar_get(current_fiber, otel_fiber_context_storage_id /* @opentelemetry_context */);
|
1699
|
+
if (context_storage != Qnil) {
|
1700
|
+
state->otel_context_source = OTEL_CONTEXT_SOURCE_FIBER_IVAR; // Remember for next time
|
1701
|
+
return context_storage;
|
1702
|
+
}
|
1703
|
+
} else {
|
1704
|
+
VALUE context_storage = rb_thread_local_aref(thread, otel_context_storage_id /* __opentelemetry_context_storage__ */);
|
1705
|
+
if (context_storage != Qnil) {
|
1706
|
+
state->otel_context_source = OTEL_CONTEXT_SOURCE_FIBER_LOCAL; // Remember for next time
|
1707
|
+
return context_storage;
|
1708
|
+
}
|
1709
|
+
}
|
1710
|
+
|
1711
|
+
// There's no context storage attached to the current thread
|
1712
|
+
return Qnil;
|
1713
|
+
}
|
1714
|
+
#else
|
1715
|
+
static inline VALUE otel_context_storage_for(DDTRACE_UNUSED thread_context_collector_state *state, VALUE thread) {
|
1716
|
+
return rb_thread_local_aref(thread, otel_context_storage_id /* __opentelemetry_context_storage__ */);
|
1717
|
+
}
|
1718
|
+
#endif
|
1719
|
+
|
1619
1720
|
// This method differs from trace_identifiers_for/ddtrace_otel_trace_identifiers_for to support the situation where
|
1620
1721
|
// the opentelemetry ruby library is being used for tracing AND the ddtrace tracing bits are not involved at all.
|
1621
1722
|
//
|
@@ -1638,29 +1739,30 @@ static VALUE _native_sample_skipped_allocation_samples(DDTRACE_UNUSED VALUE self
|
|
1638
1739
|
// root span id.
|
1639
1740
|
// This matches the semantics of how ddtrace tracing creates a TraceOperation and assigns a local root span to it.
|
1640
1741
|
static void otel_without_ddtrace_trace_identifiers_for(
|
1641
|
-
|
1742
|
+
thread_context_collector_state *state,
|
1642
1743
|
VALUE thread,
|
1643
|
-
|
1744
|
+
trace_identifiers *trace_identifiers_result,
|
1745
|
+
bool is_safe_to_allocate_objects
|
1644
1746
|
) {
|
1645
|
-
VALUE context_storage =
|
1747
|
+
VALUE context_storage = otel_context_storage_for(state, thread);
|
1646
1748
|
|
1647
1749
|
// If it exists, context_storage is expected to be an Array[OpenTelemetry::Context]
|
1648
1750
|
if (context_storage == Qnil || !RB_TYPE_P(context_storage, T_ARRAY)) return;
|
1649
1751
|
|
1650
|
-
VALUE otel_current_span_key = get_otel_current_span_key(state);
|
1752
|
+
VALUE otel_current_span_key = get_otel_current_span_key(state, is_safe_to_allocate_objects);
|
1651
1753
|
if (otel_current_span_key == Qnil) return;
|
1652
1754
|
|
1653
1755
|
int active_context_index = RARRAY_LEN(context_storage) - 1;
|
1654
1756
|
if (active_context_index < 0) return;
|
1655
1757
|
|
1656
|
-
|
1758
|
+
otel_span active_span = otel_span_from(rb_ary_entry(context_storage, active_context_index), otel_current_span_key);
|
1657
1759
|
if (active_span.span == Qnil) return;
|
1658
1760
|
|
1659
|
-
|
1761
|
+
otel_span local_root_span = active_span;
|
1660
1762
|
|
1661
1763
|
// Now find the oldest span starting from the active span that still has the same trace id as the active span
|
1662
1764
|
for (int i = active_context_index - 1; i >= 0; i--) {
|
1663
|
-
|
1765
|
+
otel_span checking_span = otel_span_from(rb_ary_entry(context_storage, i), otel_current_span_key);
|
1664
1766
|
if (checking_span.span == Qnil) return;
|
1665
1767
|
|
1666
1768
|
if (rb_str_equal(active_span.trace_id, checking_span.trace_id) == Qfalse) break;
|
@@ -1680,7 +1782,7 @@ static void otel_without_ddtrace_trace_identifiers_for(
|
|
1680
1782
|
|
1681
1783
|
VALUE root_span_type = rb_ivar_get(local_root_span.span, at_kind_id /* @kind */);
|
1682
1784
|
// 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;
|
1785
|
+
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
1786
|
|
1685
1787
|
VALUE trace_resource = rb_ivar_get(local_root_span.span, at_name_id /* @name */);
|
1686
1788
|
if (!RB_TYPE_P(trace_resource, T_STRING)) return;
|
@@ -1688,8 +1790,8 @@ static void otel_without_ddtrace_trace_identifiers_for(
|
|
1688
1790
|
trace_identifiers_result->trace_endpoint = trace_resource;
|
1689
1791
|
}
|
1690
1792
|
|
1691
|
-
static
|
1692
|
-
|
1793
|
+
static otel_span otel_span_from(VALUE otel_context, VALUE otel_current_span_key) {
|
1794
|
+
otel_span failed = {.span = Qnil, .span_id = Qnil, .trace_id = Qnil};
|
1693
1795
|
|
1694
1796
|
if (otel_context == Qnil) return failed;
|
1695
1797
|
|
@@ -1697,7 +1799,7 @@ static struct otel_span otel_span_from(VALUE otel_context, VALUE otel_current_sp
|
|
1697
1799
|
if (context_entries == Qnil || !RB_TYPE_P(context_entries, T_HASH)) return failed;
|
1698
1800
|
|
1699
1801
|
// If it exists, context_entries is expected to be a Hash[OpenTelemetry::Context::Key, OpenTelemetry::Trace::Span]
|
1700
|
-
VALUE span =
|
1802
|
+
VALUE span = safely_lookup_hash_without_going_into_ruby_code(context_entries, otel_current_span_key);
|
1701
1803
|
if (span == Qnil) return failed;
|
1702
1804
|
|
1703
1805
|
// If it exists, span_context is expected to be a OpenTelemetry::Trace::SpanContext (don't confuse it with OpenTelemetry::Context)
|
@@ -1708,7 +1810,7 @@ static struct otel_span otel_span_from(VALUE otel_context, VALUE otel_current_sp
|
|
1708
1810
|
VALUE trace_id = rb_ivar_get(span_context, at_trace_id_id /* @trace_id */);
|
1709
1811
|
if (span_id == Qnil || trace_id == Qnil || !RB_TYPE_P(span_id, T_STRING) || !RB_TYPE_P(trace_id, T_STRING)) return failed;
|
1710
1812
|
|
1711
|
-
return (
|
1813
|
+
return (otel_span) {.span = span, .span_id = span_id, .trace_id = trace_id};
|
1712
1814
|
}
|
1713
1815
|
|
1714
1816
|
// Otel span ids are represented as a big-endian 8-byte string
|
@@ -1810,8 +1912,8 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
1810
1912
|
// NOTE: In normal use, current_thread is expected to be == rb_thread_current(); the `current_thread` parameter only
|
1811
1913
|
// exists to enable testing.
|
1812
1914
|
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,
|
1915
|
+
thread_context_collector_state *state;
|
1916
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1815
1917
|
|
1816
1918
|
if (!state->timeline_enabled) rb_raise(rb_eRuntimeError, "GVL profiling requires timeline to be enabled");
|
1817
1919
|
|
@@ -1825,7 +1927,7 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
1825
1927
|
return Qfalse;
|
1826
1928
|
}
|
1827
1929
|
|
1828
|
-
|
1930
|
+
per_thread_context *thread_context = get_or_create_context_for(current_thread, state);
|
1829
1931
|
|
1830
1932
|
// We don't actually account for cpu-time during Waiting for GVL. BUT, we may chose to push an
|
1831
1933
|
// extra sample to represent the period prior to Waiting for GVL. To support that, we retrieve the current
|
@@ -1851,10 +1953,10 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
1851
1953
|
// need to take when sampling cpu/wall-time for a thread that's in the "Waiting for GVL" state.
|
1852
1954
|
__attribute__((warn_unused_result))
|
1853
1955
|
static bool handle_gvl_waiting(
|
1854
|
-
|
1956
|
+
thread_context_collector_state *state,
|
1855
1957
|
VALUE thread_being_sampled,
|
1856
1958
|
VALUE stack_from_thread,
|
1857
|
-
|
1959
|
+
per_thread_context *thread_context,
|
1858
1960
|
sampling_buffer* sampling_buffer,
|
1859
1961
|
long current_cpu_time_ns
|
1860
1962
|
) {
|
@@ -1939,7 +2041,8 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
1939
2041
|
gvl_waiting_started_wall_time_ns,
|
1940
2042
|
NULL,
|
1941
2043
|
NULL,
|
1942
|
-
false // This is the extra sample before the wait begun; only the next sample will be in the gvl waiting state
|
2044
|
+
/* 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
|
2045
|
+
/* is_safe_to_allocate_objects: */ true // This is similar to a regular cpu/wall sample, so it's also safe
|
1943
2046
|
);
|
1944
2047
|
}
|
1945
2048
|
|
@@ -1949,40 +2052,62 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
1949
2052
|
static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
1950
2053
|
ENFORCE_THREAD(thread);
|
1951
2054
|
|
2055
|
+
debug_enter_unsafe_context();
|
2056
|
+
|
1952
2057
|
thread_context_collector_on_gvl_waiting(thread_from_thread_object(thread));
|
2058
|
+
|
2059
|
+
debug_leave_unsafe_context();
|
2060
|
+
|
1953
2061
|
return Qnil;
|
1954
2062
|
}
|
1955
2063
|
|
1956
2064
|
static VALUE _native_gvl_waiting_at_for(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
1957
2065
|
ENFORCE_THREAD(thread);
|
1958
2066
|
|
2067
|
+
debug_enter_unsafe_context();
|
2068
|
+
|
1959
2069
|
intptr_t gvl_waiting_at = gvl_profiling_state_thread_object_get(thread);
|
2070
|
+
|
2071
|
+
debug_leave_unsafe_context();
|
2072
|
+
|
1960
2073
|
return LONG2NUM(gvl_waiting_at);
|
1961
2074
|
}
|
1962
2075
|
|
1963
2076
|
static VALUE _native_on_gvl_running(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
1964
2077
|
ENFORCE_THREAD(thread);
|
1965
2078
|
|
1966
|
-
|
2079
|
+
debug_enter_unsafe_context();
|
2080
|
+
|
2081
|
+
VALUE result = thread_context_collector_on_gvl_running(thread_from_thread_object(thread)) == ON_GVL_RUNNING_SAMPLE ? Qtrue : Qfalse;
|
2082
|
+
|
2083
|
+
debug_leave_unsafe_context();
|
2084
|
+
|
2085
|
+
return result;
|
1967
2086
|
}
|
1968
2087
|
|
1969
2088
|
static VALUE _native_sample_after_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread) {
|
1970
2089
|
ENFORCE_THREAD(thread);
|
1971
2090
|
|
1972
|
-
|
2091
|
+
debug_enter_unsafe_context();
|
2092
|
+
|
2093
|
+
VALUE result = thread_context_collector_sample_after_gvl_running(
|
1973
2094
|
collector_instance,
|
1974
2095
|
thread,
|
1975
2096
|
monotonic_wall_time_now_ns(RAISE_ON_FAILURE)
|
1976
2097
|
);
|
2098
|
+
|
2099
|
+
debug_leave_unsafe_context();
|
2100
|
+
|
2101
|
+
return result;
|
1977
2102
|
}
|
1978
2103
|
|
1979
2104
|
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
2105
|
ENFORCE_THREAD(thread);
|
1981
2106
|
|
1982
|
-
|
1983
|
-
TypedData_Get_Struct(collector_instance,
|
2107
|
+
thread_context_collector_state *state;
|
2108
|
+
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1984
2109
|
|
1985
|
-
|
2110
|
+
per_thread_context *thread_context = get_context_for(thread, state);
|
1986
2111
|
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
2112
|
|
1988
2113
|
thread_context->cpu_time_at_previous_sample_ns += NUM2LONG(delta_ns);
|
@@ -1992,11 +2117,55 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
1992
2117
|
|
1993
2118
|
#else
|
1994
2119
|
static bool handle_gvl_waiting(
|
1995
|
-
DDTRACE_UNUSED
|
2120
|
+
DDTRACE_UNUSED thread_context_collector_state *state,
|
1996
2121
|
DDTRACE_UNUSED VALUE thread_being_sampled,
|
1997
2122
|
DDTRACE_UNUSED VALUE stack_from_thread,
|
1998
|
-
DDTRACE_UNUSED
|
2123
|
+
DDTRACE_UNUSED per_thread_context *thread_context,
|
1999
2124
|
DDTRACE_UNUSED sampling_buffer* sampling_buffer,
|
2000
2125
|
DDTRACE_UNUSED long current_cpu_time_ns
|
2001
2126
|
) { return false; }
|
2002
2127
|
#endif // NO_GVL_INSTRUMENTATION
|
2128
|
+
|
2129
|
+
#define MAX_SAFE_LOOKUP_SIZE 16
|
2130
|
+
|
2131
|
+
typedef struct { VALUE lookup_key; VALUE result; } safe_lookup_hash_state;
|
2132
|
+
|
2133
|
+
static int safe_lookup_hash_iterate(VALUE key, VALUE value, VALUE state_ptr) {
|
2134
|
+
safe_lookup_hash_state *state = (safe_lookup_hash_state *) state_ptr;
|
2135
|
+
|
2136
|
+
if (key == state->lookup_key) {
|
2137
|
+
state->result = value;
|
2138
|
+
return ST_STOP;
|
2139
|
+
}
|
2140
|
+
|
2141
|
+
return ST_CONTINUE;
|
2142
|
+
}
|
2143
|
+
|
2144
|
+
// This method exists because we need to look up a hash during sampling, but we don't want to invoke any
|
2145
|
+
// Ruby code as a side effect. To be able to look up by hash, `rb_hash_lookup` calls `#hash` on the key,
|
2146
|
+
// which we want to avoid.
|
2147
|
+
// Thus, instead, we opt to just iterate through the hash and check if we can find what we're looking for.
|
2148
|
+
//
|
2149
|
+
// To avoid having too much overhead here we only iterate in hashes up to MAX_SAFE_LOOKUP_SIZE.
|
2150
|
+
// Note that we don't even try to iterate if the hash is bigger -- this is to avoid flaky behavior where
|
2151
|
+
// depending on the internal storage order of the hash we may or not find the key, and instead we always
|
2152
|
+
// enforce the size.
|
2153
|
+
static VALUE safely_lookup_hash_without_going_into_ruby_code(VALUE hash, VALUE key) {
|
2154
|
+
if (!RB_TYPE_P(hash, T_HASH) || RHASH_SIZE(hash) > MAX_SAFE_LOOKUP_SIZE) return Qnil;
|
2155
|
+
|
2156
|
+
safe_lookup_hash_state state = {.lookup_key = key, .result = Qnil};
|
2157
|
+
|
2158
|
+
rb_hash_foreach(hash, safe_lookup_hash_iterate, (VALUE) &state);
|
2159
|
+
|
2160
|
+
return state.result;
|
2161
|
+
}
|
2162
|
+
|
2163
|
+
static VALUE _native_system_epoch_time_now_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
|
2164
|
+
thread_context_collector_state *state;
|
2165
|
+
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
2166
|
+
|
2167
|
+
long current_monotonic_wall_time_ns = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
2168
|
+
long system_epoch_time_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, current_monotonic_wall_time_ns);
|
2169
|
+
|
2170
|
+
return LONG2NUM(system_epoch_time_ns);
|
2171
|
+
}
|