datadog 2.8.0 → 2.39.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 +1009 -1
- data/README.md +0 -1
- data/bin/ddprofrb +4 -4
- data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
- data/ext/datadog_profiling_native_extension/clock_id.h +11 -3
- data/ext/datadog_profiling_native_extension/clock_id_from_mach.c +66 -0
- data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +3 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +500 -188
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +5 -5
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +1 -1
- data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +3 -2
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +34 -20
- data/ext/datadog_profiling_native_extension/collectors_stack.c +319 -101
- data/ext/datadog_profiling_native_extension/collectors_stack.h +27 -5
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +985 -504
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +19 -5
- data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +66 -5
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +50 -1
- data/ext/datadog_profiling_native_extension/encoded_profile.c +80 -0
- data/ext/datadog_profiling_native_extension/encoded_profile.h +8 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +53 -35
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +6 -43
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +15 -55
- data/ext/datadog_profiling_native_extension/heap_recorder.c +487 -497
- data/ext/datadog_profiling_native_extension/heap_recorder.h +17 -8
- data/ext/datadog_profiling_native_extension/http_transport.c +108 -144
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +18 -15
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +8 -10
- data/ext/datadog_profiling_native_extension/macos_sampler_thread.h +55 -0
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +0 -13
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +136 -60
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +22 -5
- data/ext/datadog_profiling_native_extension/profiling.c +84 -27
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +67 -62
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +20 -16
- data/ext/datadog_profiling_native_extension/setup_signal_handler.c +34 -10
- data/ext/datadog_profiling_native_extension/setup_signal_handler.h +2 -1
- data/ext/datadog_profiling_native_extension/stack_recorder.c +326 -217
- data/ext/datadog_profiling_native_extension/stack_recorder.h +4 -2
- data/ext/datadog_profiling_native_extension/time_helpers.h +14 -8
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +48 -0
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +36 -0
- data/ext/libdatadog_api/crashtracker.c +27 -24
- data/ext/libdatadog_api/crashtracker.h +5 -0
- data/ext/libdatadog_api/crashtracker_report_exception.c +126 -0
- data/ext/libdatadog_api/datadog_ruby_common.c +66 -5
- data/ext/libdatadog_api/datadog_ruby_common.h +50 -1
- data/ext/libdatadog_api/ddsketch.c +102 -0
- data/ext/libdatadog_api/di.c +203 -0
- data/ext/libdatadog_api/extconf.rb +41 -45
- data/ext/libdatadog_api/feature_flags.c +580 -0
- data/ext/libdatadog_api/feature_flags.h +5 -0
- data/ext/libdatadog_api/helpers.h +27 -0
- data/ext/libdatadog_api/init.c +27 -0
- data/ext/libdatadog_api/library_config.c +173 -0
- data/ext/libdatadog_api/library_config.h +25 -0
- data/ext/libdatadog_api/process_discovery.c +118 -0
- data/ext/libdatadog_api/process_discovery.h +5 -0
- data/ext/libdatadog_extconf_helpers.rb +111 -18
- data/lib/datadog/ai_guard/api_client.rb +84 -0
- data/lib/datadog/ai_guard/autoload.rb +10 -0
- data/lib/datadog/ai_guard/component.rb +44 -0
- data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
- data/lib/datadog/ai_guard/configuration.rb +115 -0
- data/lib/datadog/ai_guard/contrib/auto_instrument.rb +24 -0
- data/lib/datadog/ai_guard/contrib/integration.rb +37 -0
- data/lib/datadog/ai_guard/contrib/rack/integration.rb +42 -0
- data/lib/datadog/ai_guard/contrib/rack/patcher.rb +26 -0
- data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +97 -0
- data/lib/datadog/ai_guard/contrib/rails/integration.rb +41 -0
- data/lib/datadog/ai_guard/contrib/rails/patcher.rb +97 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +80 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +41 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +30 -0
- data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
- data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
- data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
- data/lib/datadog/ai_guard/evaluation/no_op_result.rb +36 -0
- data/lib/datadog/ai_guard/evaluation/request.rb +86 -0
- data/lib/datadog/ai_guard/evaluation/result.rb +45 -0
- data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
- data/lib/datadog/ai_guard/evaluation.rb +108 -0
- data/lib/datadog/ai_guard/ext.rb +29 -0
- data/lib/datadog/ai_guard.rb +181 -0
- 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/endpoint_collection/grape_route_serializer.rb +26 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +66 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +36 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
- data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
- data/lib/datadog/appsec/api_security/route_extractor.rb +104 -0
- data/lib/datadog/appsec/api_security/sampler.rb +62 -0
- data/lib/datadog/appsec/api_security.rb +23 -0
- data/lib/datadog/appsec/assets/blocked.html +10 -1
- data/lib/datadog/appsec/assets/blocked.json +1 -1
- data/lib/datadog/appsec/assets/blocked.text +3 -1
- data/lib/datadog/appsec/assets/waf_rules/README.md +44 -5
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +954 -229
- data/lib/datadog/appsec/assets/waf_rules/strict.json +109 -106
- data/lib/datadog/appsec/assets.rb +8 -8
- data/lib/datadog/appsec/autoload.rb +5 -5
- data/lib/datadog/appsec/component.rb +56 -68
- data/lib/datadog/appsec/compressed_json.rb +40 -0
- data/lib/datadog/appsec/configuration.rb +427 -1
- data/lib/datadog/appsec/context.rb +135 -0
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +45 -24
- data/lib/datadog/appsec/contrib/active_record/integration.rb +6 -6
- data/lib/datadog/appsec/contrib/active_record/patcher.rb +67 -16
- data/lib/datadog/appsec/contrib/auto_instrument.rb +1 -1
- data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +75 -0
- data/lib/datadog/appsec/contrib/aws_lambda/integration.rb +39 -0
- data/lib/datadog/appsec/contrib/aws_lambda/patcher.rb +30 -0
- data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +146 -0
- 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 +5 -6
- data/lib/datadog/appsec/contrib/devise/patcher.rb +33 -25
- data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +104 -0
- data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +70 -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 +121 -0
- data/lib/datadog/appsec/contrib/excon/integration.rb +41 -0
- data/lib/datadog/appsec/contrib/excon/patcher.rb +30 -0
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +181 -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 +166 -0
- data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +4 -10
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +76 -26
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +22 -33
- data/lib/datadog/appsec/contrib/graphql/integration.rb +10 -9
- data/lib/datadog/appsec/contrib/graphql/patcher.rb +3 -6
- data/lib/datadog/appsec/contrib/rack/buffered_input.rb +83 -0
- data/lib/datadog/appsec/contrib/rack/ext.rb +34 -0
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +56 -21
- data/lib/datadog/appsec/contrib/rack/gateway/response.rb +4 -4
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +101 -100
- data/lib/datadog/appsec/contrib/rack/input_peeker.rb +79 -0
- data/lib/datadog/appsec/contrib/rack/integration.rb +7 -7
- data/lib/datadog/appsec/contrib/rack/patcher.rb +2 -5
- data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +13 -14
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +117 -115
- data/lib/datadog/appsec/contrib/rack/response_body.rb +36 -0
- data/lib/datadog/appsec/contrib/rails/gateway/request.rb +43 -4
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +58 -33
- data/lib/datadog/appsec/contrib/rails/integration.rb +6 -6
- data/lib/datadog/appsec/contrib/rails/patcher.rb +82 -69
- data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.rb +29 -0
- data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +33 -0
- data/lib/datadog/appsec/contrib/rails/request.rb +3 -3
- data/lib/datadog/appsec/contrib/rest_client/integration.rb +45 -0
- data/lib/datadog/appsec/contrib/rest_client/patcher.rb +30 -0
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +193 -0
- data/lib/datadog/appsec/contrib/sinatra/gateway/request.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/gateway/route_params.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +83 -67
- data/lib/datadog/appsec/contrib/sinatra/integration.rb +6 -6
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +25 -61
- data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +31 -0
- data/lib/datadog/appsec/counter_sampler.rb +25 -0
- data/lib/datadog/appsec/default_header_tags.rb +52 -0
- data/lib/datadog/appsec/event.rb +80 -152
- data/lib/datadog/appsec/ext.rb +16 -4
- data/lib/datadog/appsec/extensions.rb +2 -2
- data/lib/datadog/appsec/instrumentation/gateway/argument.rb +23 -2
- data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +23 -0
- data/lib/datadog/appsec/instrumentation/gateway.rb +6 -24
- data/lib/datadog/appsec/metrics/collector.rb +80 -0
- data/lib/datadog/appsec/metrics/exporter.rb +48 -0
- data/lib/datadog/appsec/metrics/telemetry.rb +40 -0
- data/lib/datadog/appsec/metrics/telemetry_exporter.rb +47 -0
- data/lib/datadog/appsec/metrics.rb +14 -0
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +60 -34
- data/lib/datadog/appsec/monitor.rb +1 -1
- data/lib/datadog/appsec/processor/rule_loader.rb +48 -54
- data/lib/datadog/appsec/rate_limiter.rb +1 -1
- data/lib/datadog/appsec/remote.rb +59 -68
- data/lib/datadog/appsec/response.rb +42 -90
- data/lib/datadog/appsec/route_normalizer/rails_route_pattern.rb +176 -0
- data/lib/datadog/appsec/route_normalizer/route_pattern.rb +378 -0
- data/lib/datadog/appsec/route_normalizer/route_text.rb +57 -0
- data/lib/datadog/appsec/route_normalizer.rb +80 -0
- data/lib/datadog/appsec/security_engine/engine.rb +197 -0
- data/lib/datadog/appsec/security_engine/result.rb +103 -0
- data/lib/datadog/appsec/security_engine/runner.rb +111 -0
- data/lib/datadog/appsec/security_engine.rb +9 -0
- data/lib/datadog/appsec/security_event.rb +37 -0
- data/lib/datadog/appsec/thread_safe_ref.rb +61 -0
- data/lib/datadog/appsec/trace_keeper.rb +36 -0
- data/lib/datadog/appsec/utils/hash_coercion.rb +23 -0
- data/lib/datadog/appsec/utils/http/body.rb +41 -0
- data/lib/datadog/appsec/utils/http/body_reader.rb +61 -0
- data/lib/datadog/appsec/utils/http/media_range.rb +17 -16
- data/lib/datadog/appsec/utils/http/media_type.rb +33 -27
- data/lib/datadog/appsec/utils/http/url_encoded.rb +107 -0
- data/lib/datadog/appsec/utils.rb +0 -2
- data/lib/datadog/appsec.rb +35 -30
- data/lib/datadog/auto_instrument.rb +5 -2
- data/lib/datadog/auto_instrument_base.rb +2 -1
- data/lib/datadog/core/buffer/cruby.rb +1 -1
- data/lib/datadog/core/buffer/random.rb +18 -2
- data/lib/datadog/core/buffer/thread_safe.rb +1 -1
- data/lib/datadog/core/configuration/agent_settings.rb +52 -0
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +22 -36
- data/lib/datadog/core/configuration/agentless_settings_resolver.rb +176 -0
- data/lib/datadog/core/configuration/base.rb +19 -7
- data/lib/datadog/core/configuration/components.rb +261 -57
- data/lib/datadog/core/configuration/components_state.rb +28 -0
- data/lib/datadog/core/configuration/config_helper.rb +109 -0
- data/lib/datadog/core/configuration/deprecations.rb +36 -0
- data/lib/datadog/core/configuration/ext.rb +15 -12
- data/lib/datadog/core/configuration/option.rb +150 -84
- data/lib/datadog/core/configuration/option_definition.rb +50 -29
- data/lib/datadog/core/configuration/options.rb +58 -19
- data/lib/datadog/core/configuration/settings.rb +287 -103
- data/lib/datadog/core/configuration/stable_config.rb +32 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +415 -0
- data/lib/datadog/core/configuration.rb +47 -23
- data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
- data/lib/datadog/core/contrib/rails/utils.rb +28 -0
- data/lib/datadog/core/crashtracking/component.rb +81 -38
- data/lib/datadog/core/crashtracking/tag_builder.rb +11 -23
- data/lib/datadog/core/ddsketch.rb +19 -0
- data/lib/datadog/core/deprecations.rb +3 -3
- data/lib/datadog/core/diagnostics/environment_logger.rb +10 -8
- data/lib/datadog/core/diagnostics/health.rb +2 -2
- data/lib/datadog/core/encoding.rb +21 -5
- data/lib/datadog/core/environment/agent_info.rb +142 -0
- data/lib/datadog/core/environment/cgroup.rb +53 -28
- data/lib/datadog/core/environment/container.rb +143 -51
- data/lib/datadog/core/environment/execution.rb +3 -3
- data/lib/datadog/core/environment/ext.rb +32 -21
- data/lib/datadog/core/environment/git.rb +5 -4
- data/lib/datadog/core/environment/identity.rb +50 -14
- data/lib/datadog/core/environment/platform.rb +8 -8
- data/lib/datadog/core/environment/process.rb +122 -0
- data/lib/datadog/core/environment/socket.rb +15 -2
- data/lib/datadog/core/environment/variable_helpers.rb +4 -4
- data/lib/datadog/core/environment/yjit.rb +2 -1
- data/lib/datadog/core/error.rb +15 -13
- data/lib/datadog/core/evp.rb +11 -0
- data/lib/datadog/core/extensions.rb +1 -1
- data/lib/datadog/core/feature_flags.rb +61 -0
- data/lib/datadog/core/git/ext.rb +4 -4
- data/lib/datadog/core/knuth_sampler.rb +59 -0
- data/lib/datadog/core/logger.rb +5 -5
- data/lib/datadog/core/logging/ext.rb +1 -1
- data/lib/datadog/core/metrics/client.rb +49 -49
- data/lib/datadog/core/metrics/ext.rb +5 -5
- data/lib/datadog/core/metrics/logging.rb +8 -8
- data/lib/datadog/core/metrics/options.rb +3 -3
- data/lib/datadog/core/pin.rb +9 -9
- data/lib/datadog/core/process_discovery/tracer_memfd.rb +13 -0
- data/lib/datadog/core/process_discovery.rb +62 -0
- data/lib/datadog/core/rate_limiter.rb +16 -4
- data/lib/datadog/core/remote/client/capabilities.rb +72 -13
- data/lib/datadog/core/remote/client.rb +68 -51
- data/lib/datadog/core/remote/component.rb +90 -55
- data/lib/datadog/core/remote/configuration/content.rb +17 -4
- data/lib/datadog/core/remote/configuration/digest.rb +22 -15
- data/lib/datadog/core/remote/configuration/path.rb +9 -9
- data/lib/datadog/core/remote/configuration/repository.rb +17 -4
- data/lib/datadog/core/remote/configuration/target.rb +15 -8
- data/lib/datadog/core/remote/configuration.rb +4 -4
- data/lib/datadog/core/remote/ext.rb +3 -3
- data/lib/datadog/core/remote/negotiation.rb +10 -10
- data/lib/datadog/core/remote/tie/tracing.rb +7 -7
- data/lib/datadog/core/remote/transport/config.rb +11 -37
- data/lib/datadog/core/remote/transport/http/config.rb +39 -107
- data/lib/datadog/core/remote/transport/http/negotiation.rb +19 -92
- data/lib/datadog/core/remote/transport/http.rb +33 -111
- data/lib/datadog/core/remote/transport/negotiation.rb +9 -21
- data/lib/datadog/core/remote/worker.rb +33 -42
- data/lib/datadog/core/runtime/ext.rb +22 -23
- data/lib/datadog/core/runtime/metrics.rb +30 -14
- data/lib/datadog/core/semaphore.rb +1 -4
- data/lib/datadog/core/tag_builder.rb +52 -0
- data/lib/datadog/core/tag_normalizer.rb +84 -0
- data/lib/datadog/core/telemetry/component.rb +142 -65
- data/lib/datadog/core/telemetry/emitter.rb +26 -14
- 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_endpoints_loaded.rb +30 -0
- data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -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 +293 -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 +66 -0
- data/lib/datadog/core/telemetry/event.rb +21 -392
- data/lib/datadog/core/telemetry/ext.rb +11 -10
- data/lib/datadog/core/telemetry/http/adapters/net.rb +12 -97
- data/lib/datadog/core/telemetry/logger.rb +6 -3
- data/lib/datadog/core/telemetry/logging.rb +27 -9
- data/lib/datadog/core/telemetry/metric.rb +35 -13
- data/lib/datadog/core/telemetry/metrics_collection.rb +2 -2
- data/lib/datadog/core/telemetry/metrics_manager.rb +10 -1
- data/lib/datadog/core/telemetry/request.rb +21 -9
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +66 -0
- data/lib/datadog/core/telemetry/transport/http.rb +68 -0
- data/lib/datadog/core/telemetry/transport/telemetry.rb +42 -0
- data/lib/datadog/core/telemetry/worker.rb +214 -35
- data/lib/datadog/core/transport/ext.rb +14 -11
- data/lib/datadog/core/transport/http/adapters/net.rb +19 -4
- data/lib/datadog/core/transport/http/adapters/test.rb +3 -2
- data/lib/datadog/core/transport/http/adapters/unix_socket.rb +5 -5
- data/lib/datadog/core/transport/http/api/endpoint.rb +10 -5
- data/lib/datadog/{tracing → core}/transport/http/api/instance.rb +5 -5
- data/lib/datadog/core/transport/http/api/map.rb +1 -1
- data/lib/datadog/{tracing → core}/transport/http/builder.rb +62 -36
- data/lib/datadog/core/transport/http/client.rb +80 -0
- data/lib/datadog/core/transport/http/env.rb +8 -0
- data/lib/datadog/core/transport/http/response.rb +5 -1
- data/lib/datadog/core/transport/http.rb +80 -0
- data/lib/datadog/core/transport/parcel.rb +61 -9
- data/lib/datadog/core/transport/response.rb +19 -1
- data/lib/datadog/core/transport/transport.rb +90 -0
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +9 -9
- data/lib/datadog/core/utils/base64_codec.rb +23 -0
- data/lib/datadog/core/utils/duration.rb +33 -33
- data/lib/datadog/core/utils/enumerable_compat.rb +29 -0
- data/lib/datadog/core/utils/fnv.rb +26 -0
- data/lib/datadog/core/utils/forking.rb +5 -3
- data/lib/datadog/core/utils/hash.rb +0 -23
- data/lib/datadog/core/utils/lru_cache.rb +45 -0
- data/lib/datadog/core/utils/network.rb +34 -13
- data/lib/datadog/core/utils/only_once.rb +1 -1
- data/lib/datadog/core/utils/only_once_successful.rb +25 -8
- data/lib/datadog/core/utils/safe_dup.rb +4 -3
- data/lib/datadog/core/utils/sequence.rb +2 -0
- data/lib/datadog/core/utils/spawn_monkey_patch.rb +68 -0
- data/lib/datadog/core/utils/time.rb +26 -2
- data/lib/datadog/core/utils/truncation.rb +21 -0
- data/lib/datadog/core/utils/url.rb +1 -1
- data/lib/datadog/core/utils.rb +14 -5
- 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 +40 -14
- data/lib/datadog/core/workers/interval_loop.rb +63 -4
- data/lib/datadog/core/workers/polling.rb +4 -2
- data/lib/datadog/core/workers/queue.rb +96 -1
- data/lib/datadog/core/workers/runtime_metrics.rb +14 -6
- data/lib/datadog/core.rb +20 -3
- data/lib/datadog/data_streams/configuration.rb +50 -0
- data/lib/datadog/data_streams/ext.rb +11 -0
- data/lib/datadog/data_streams/extensions.rb +16 -0
- data/lib/datadog/data_streams/pathway_context.rb +169 -0
- data/lib/datadog/data_streams/processor.rb +511 -0
- data/lib/datadog/data_streams/transport/http/stats.rb +58 -0
- data/lib/datadog/data_streams/transport/http.rb +36 -0
- data/lib/datadog/data_streams/transport/stats.rb +39 -0
- data/lib/datadog/data_streams.rb +100 -0
- data/lib/datadog/di/base.rb +123 -0
- data/lib/datadog/di/boot.rb +47 -0
- data/lib/datadog/di/capture_expression.rb +21 -0
- data/lib/datadog/di/capture_expression_evaluator.rb +71 -0
- data/lib/datadog/di/capture_limits.rb +41 -0
- data/lib/datadog/di/code_tracker.rb +224 -12
- data/lib/datadog/di/component.rb +157 -49
- data/lib/datadog/di/configuration.rb +241 -2
- data/lib/datadog/di/context.rb +82 -0
- data/lib/datadog/di/contrib/active_record.rb +34 -5
- data/lib/datadog/di/contrib/railtie.rb +15 -0
- data/lib/datadog/di/contrib.rb +28 -0
- data/lib/datadog/di/el/compiler.rb +168 -0
- data/lib/datadog/di/el/evaluator.rb +159 -0
- data/lib/datadog/di/el/expression.rb +42 -0
- data/lib/datadog/di/el.rb +5 -0
- data/lib/datadog/di/error.rb +49 -0
- data/lib/datadog/di/fatal_exceptions.rb +26 -0
- data/lib/datadog/di/instrumenter.rb +672 -122
- data/lib/datadog/di/logger.rb +30 -0
- data/lib/datadog/di/{init.rb → preload.rb} +2 -4
- data/lib/datadog/di/probe.rb +125 -20
- data/lib/datadog/di/probe_builder.rb +126 -5
- data/lib/datadog/di/probe_file_loader/railtie.rb +15 -0
- data/lib/datadog/di/probe_file_loader.rb +87 -0
- data/lib/datadog/di/probe_manager.rb +198 -100
- data/lib/datadog/di/probe_notification_builder.rb +349 -103
- data/lib/datadog/di/probe_notifier_worker.rb +161 -51
- data/lib/datadog/di/probe_repository.rb +198 -0
- data/lib/datadog/di/proc_responder.rb +36 -0
- data/lib/datadog/di/redactor.rb +24 -3
- data/lib/datadog/di/remote.rb +259 -73
- data/lib/datadog/di/serializer.rb +319 -30
- data/lib/datadog/di/transport/diagnostics.rb +32 -0
- data/lib/datadog/di/transport/http/diagnostics.rb +35 -0
- data/lib/datadog/di/transport/http/input.rb +45 -0
- data/lib/datadog/di/transport/http.rb +81 -0
- data/lib/datadog/di/transport/input.rb +144 -0
- data/lib/datadog/di/utils.rb +133 -9
- data/lib/datadog/di.rb +188 -121
- data/lib/datadog/error_tracking/collector.rb +88 -0
- data/lib/datadog/error_tracking/component.rb +167 -0
- data/lib/datadog/error_tracking/configuration.rb +64 -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/v2.rb +253 -0
- data/lib/datadog/kit/appsec/events.rb +39 -26
- data/lib/datadog/kit/enable_core_dumps.rb +3 -3
- data/lib/datadog/kit/identity.rb +34 -24
- data/lib/datadog/kit/tracing/method_tracer.rb +135 -0
- data/lib/datadog/kit.rb +1 -1
- data/lib/datadog/open_feature/component.rb +109 -0
- data/lib/datadog/open_feature/configuration.rb +50 -0
- data/lib/datadog/open_feature/evaluation_engine.rb +70 -0
- data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
- data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
- data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
- data/lib/datadog/open_feature/exposures/event.rb +60 -0
- data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
- data/lib/datadog/open_feature/exposures/worker.rb +116 -0
- data/lib/datadog/open_feature/ext.rb +21 -0
- data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +286 -0
- data/lib/datadog/open_feature/flag_evaluation/writer.rb +433 -0
- data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +102 -0
- data/lib/datadog/open_feature/hooks/flag_eval_metrics_hook.rb +51 -0
- data/lib/datadog/open_feature/hooks/span_enrichment_hook/codec.rb +58 -0
- data/lib/datadog/open_feature/hooks/span_enrichment_hook/span_enrichment_state.rb +96 -0
- data/lib/datadog/open_feature/hooks/span_enrichment_hook/span_enrichment_state_store.rb +49 -0
- data/lib/datadog/open_feature/hooks/span_enrichment_hook.rb +184 -0
- data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
- data/lib/datadog/open_feature/native_evaluator.rb +60 -0
- data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
- data/lib/datadog/open_feature/provider.rb +226 -0
- data/lib/datadog/open_feature/remote.rb +67 -0
- data/lib/datadog/open_feature/resolution_details.rb +37 -0
- data/lib/datadog/open_feature/transport.rb +121 -0
- data/lib/datadog/open_feature.rb +19 -0
- data/lib/datadog/opentelemetry/api/baggage.rb +90 -0
- data/lib/datadog/opentelemetry/api/context.rb +25 -10
- data/lib/datadog/opentelemetry/configuration/settings.rb +226 -0
- data/lib/datadog/opentelemetry/ext.rb +9 -0
- data/lib/datadog/opentelemetry/logs.rb +98 -0
- data/lib/datadog/opentelemetry/metrics.rb +90 -0
- data/lib/datadog/opentelemetry/sdk/configurator.rb +69 -5
- data/lib/datadog/opentelemetry/sdk/id_generator.rb +16 -10
- data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +30 -0
- data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +28 -0
- data/lib/datadog/opentelemetry/sdk/propagator.rb +20 -14
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +24 -21
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +43 -39
- data/lib/datadog/opentelemetry/sdk.rb +19 -0
- data/lib/datadog/opentelemetry/signal_configuration.rb +53 -0
- data/lib/datadog/opentelemetry/trace.rb +5 -5
- data/lib/datadog/opentelemetry.rb +11 -6
- data/lib/datadog/profiling/collectors/code_provenance.rb +84 -16
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +57 -3
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +13 -4
- data/lib/datadog/profiling/collectors/info.rb +62 -4
- data/lib/datadog/profiling/collectors/thread_context.rb +27 -7
- data/lib/datadog/profiling/component.rb +125 -88
- data/lib/datadog/profiling/encoded_profile.rb +11 -0
- data/lib/datadog/profiling/exporter.rb +51 -17
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +24 -5
- data/lib/datadog/profiling/ext/exec_monkey_patch.rb +32 -0
- data/lib/datadog/profiling/ext.rb +2 -17
- data/lib/datadog/profiling/flush.rb +26 -17
- data/lib/datadog/profiling/http_transport.rb +30 -96
- data/lib/datadog/profiling/load_native_extension.rb +3 -35
- data/lib/datadog/profiling/profiler.rb +31 -12
- data/lib/datadog/profiling/scheduler.rb +19 -10
- data/lib/datadog/profiling/sequence_tracker.rb +44 -0
- data/lib/datadog/profiling/stack_recorder.rb +9 -17
- data/lib/datadog/profiling/tag_builder.rb +41 -41
- data/lib/datadog/profiling/tasks/exec.rb +10 -5
- data/lib/datadog/profiling/tasks/help.rb +1 -0
- data/lib/datadog/profiling/tasks/setup.rb +4 -2
- data/lib/datadog/profiling.rb +38 -37
- data/lib/datadog/ruby_version.rb +25 -0
- data/lib/datadog/single_step_instrument.rb +11 -2
- data/lib/datadog/symbol_database/component.rb +734 -0
- data/lib/datadog/symbol_database/configuration.rb +73 -0
- data/lib/datadog/symbol_database/extensions.rb +19 -0
- data/lib/datadog/symbol_database/extractor.rb +1110 -0
- data/lib/datadog/symbol_database/file_hash.rb +48 -0
- data/lib/datadog/symbol_database/logger.rb +43 -0
- data/lib/datadog/symbol_database/remote.rb +195 -0
- data/lib/datadog/symbol_database/scope.rb +102 -0
- data/lib/datadog/symbol_database/scope_batcher.rb +292 -0
- data/lib/datadog/symbol_database/service_version.rb +66 -0
- data/lib/datadog/symbol_database/symbol.rb +69 -0
- data/lib/datadog/symbol_database/transport/http/endpoint.rb +28 -0
- data/lib/datadog/symbol_database/transport/http.rb +45 -0
- data/lib/datadog/symbol_database/transport.rb +54 -0
- data/lib/datadog/symbol_database/uploader.rb +230 -0
- data/lib/datadog/symbol_database.rb +71 -0
- data/lib/datadog/tracing/analytics.rb +2 -2
- data/lib/datadog/tracing/buffer.rb +15 -15
- data/lib/datadog/tracing/client_ip.rb +4 -4
- data/lib/datadog/tracing/component.rb +39 -35
- data/lib/datadog/tracing/configuration/dynamic.rb +14 -16
- data/lib/datadog/tracing/configuration/ext.rb +60 -33
- data/lib/datadog/tracing/configuration/http.rb +2 -2
- data/lib/datadog/tracing/configuration/settings.rb +156 -26
- data/lib/datadog/tracing/context.rb +3 -3
- data/lib/datadog/tracing/context_provider.rb +3 -3
- data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/action_cable/event.rb +5 -5
- data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +9 -6
- data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +8 -5
- data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +9 -6
- data/lib/datadog/tracing/contrib/action_cable/events.rb +4 -4
- data/lib/datadog/tracing/contrib/action_cable/ext.rb +18 -18
- data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -5
- data/lib/datadog/tracing/contrib/action_cable/integration.rb +10 -7
- data/lib/datadog/tracing/contrib/action_cable/patcher.rb +4 -4
- data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/action_mailer/event.rb +7 -4
- data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +8 -8
- data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +4 -4
- data/lib/datadog/tracing/contrib/action_mailer/events.rb +2 -2
- data/lib/datadog/tracing/contrib/action_mailer/ext.rb +18 -18
- data/lib/datadog/tracing/contrib/action_mailer/integration.rb +11 -7
- data/lib/datadog/tracing/contrib/action_mailer/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +37 -19
- data/lib/datadog/tracing/contrib/action_pack/action_controller/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +48 -13
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +5 -3
- data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +3 -3
- data/lib/datadog/tracing/contrib/action_pack/ext.rb +10 -8
- data/lib/datadog/tracing/contrib/action_pack/integration.rb +10 -7
- data/lib/datadog/tracing/contrib/action_pack/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/action_pack/utils.rb +2 -3
- data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +3 -3
- data/lib/datadog/tracing/contrib/action_view/event.rb +1 -1
- data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +10 -7
- data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +12 -9
- data/lib/datadog/tracing/contrib/action_view/events.rb +2 -2
- data/lib/datadog/tracing/contrib/action_view/ext.rb +10 -10
- data/lib/datadog/tracing/contrib/action_view/integration.rb +14 -11
- data/lib/datadog/tracing/contrib/action_view/patcher.rb +5 -5
- data/lib/datadog/tracing/contrib/action_view/utils.rb +2 -2
- data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/active_job/data_streams.rb +62 -0
- data/lib/datadog/tracing/contrib/active_job/event.rb +11 -11
- data/lib/datadog/tracing/contrib/active_job/events/discard.rb +11 -8
- data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +11 -8
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +11 -8
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +11 -8
- data/lib/datadog/tracing/contrib/active_job/events/perform.rb +11 -8
- data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +11 -8
- data/lib/datadog/tracing/contrib/active_job/events.rb +6 -6
- data/lib/datadog/tracing/contrib/active_job/ext.rb +23 -23
- data/lib/datadog/tracing/contrib/active_job/integration.rb +10 -7
- data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
- data/lib/datadog/tracing/contrib/active_job/patcher.rb +21 -5
- data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/active_model_serializers/event.rb +4 -4
- data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +8 -8
- data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +9 -10
- data/lib/datadog/tracing/contrib/active_model_serializers/events.rb +2 -2
- data/lib/datadog/tracing/contrib/active_model_serializers/ext.rb +10 -10
- data/lib/datadog/tracing/contrib/active_model_serializers/integration.rb +5 -6
- data/lib/datadog/tracing/contrib/active_model_serializers/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/active_record/configuration/makara_resolver.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +8 -8
- data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +4 -4
- data/lib/datadog/tracing/contrib/active_record/event.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +10 -10
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +16 -19
- data/lib/datadog/tracing/contrib/active_record/events.rb +2 -2
- data/lib/datadog/tracing/contrib/active_record/ext.rb +15 -15
- data/lib/datadog/tracing/contrib/active_record/integration.rb +16 -12
- data/lib/datadog/tracing/contrib/active_record/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/active_record/utils.rb +19 -19
- data/lib/datadog/tracing/contrib/active_support/cache/event.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +28 -20
- data/lib/datadog/tracing/contrib/active_support/cache/events.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +40 -8
- data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +8 -4
- data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +5 -7
- data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +26 -3
- data/lib/datadog/tracing/contrib/active_support/ext.rb +16 -16
- data/lib/datadog/tracing/contrib/active_support/integration.rb +11 -8
- data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +3 -2
- data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +2 -2
- data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +9 -11
- data/lib/datadog/tracing/contrib/active_support/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/analytics.rb +1 -1
- data/lib/datadog/tracing/contrib/auto_instrument.rb +5 -5
- data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +3 -3
- data/lib/datadog/tracing/contrib/aws/ext.rb +26 -26
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +21 -13
- data/lib/datadog/tracing/contrib/aws/integration.rb +11 -8
- data/lib/datadog/tracing/contrib/aws/parsed_context.rb +9 -3
- data/lib/datadog/tracing/contrib/aws/patcher.rb +10 -6
- data/lib/datadog/tracing/contrib/aws/service/base.rb +2 -1
- data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +4 -4
- data/lib/datadog/tracing/contrib/aws/service/s3.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/service/sns.rb +3 -3
- data/lib/datadog/tracing/contrib/aws/service/sqs.rb +3 -3
- data/lib/datadog/tracing/contrib/aws/service/states.rb +5 -5
- data/lib/datadog/tracing/contrib/aws/services.rb +14 -14
- data/lib/datadog/tracing/contrib/component.rb +3 -3
- data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +2 -2
- data/lib/datadog/tracing/contrib/concurrent_ruby/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +2 -2
- data/lib/datadog/tracing/contrib/concurrent_ruby/ext.rb +2 -2
- data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +2 -2
- data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +8 -5
- data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +4 -4
- data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +2 -2
- data/lib/datadog/tracing/contrib/configurable.rb +24 -9
- data/lib/datadog/tracing/contrib/configuration/resolver.rb +14 -4
- data/lib/datadog/tracing/contrib/configuration/resolvers/pattern_resolver.rb +5 -5
- data/lib/datadog/tracing/contrib/configuration/settings.rb +3 -3
- data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/dalli/ext.rb +16 -15
- data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +6 -10
- data/lib/datadog/tracing/contrib/dalli/integration.rb +10 -7
- data/lib/datadog/tracing/contrib/dalli/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/dalli/quantize.rb +3 -3
- data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/delayed_job/ext.rb +14 -14
- data/lib/datadog/tracing/contrib/delayed_job/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/delayed_job/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +7 -5
- data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +1 -0
- data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +18 -17
- data/lib/datadog/tracing/contrib/elasticsearch/integration.rb +8 -8
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +57 -63
- data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +8 -8
- data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +7 -3
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +19 -21
- data/lib/datadog/tracing/contrib/ethon/ext.rb +16 -14
- data/lib/datadog/tracing/contrib/ethon/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +4 -11
- data/lib/datadog/tracing/contrib/ethon/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +21 -10
- data/lib/datadog/tracing/contrib/excon/ext.rb +14 -12
- data/lib/datadog/tracing/contrib/excon/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/excon/middleware.rb +17 -19
- data/lib/datadog/tracing/contrib/excon/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/ext.rb +20 -17
- data/lib/datadog/tracing/contrib/extensions.rb +57 -14
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +20 -13
- data/lib/datadog/tracing/contrib/faraday/ext.rb +14 -12
- data/lib/datadog/tracing/contrib/faraday/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +19 -19
- data/lib/datadog/tracing/contrib/faraday/patcher.rb +6 -6
- data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +11 -7
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +37 -34
- data/lib/datadog/tracing/contrib/grape/ext.rb +15 -15
- data/lib/datadog/tracing/contrib/grape/instrumentation.rb +15 -10
- data/lib/datadog/tracing/contrib/grape/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/grape/patcher.rb +10 -5
- data/lib/datadog/tracing/contrib/graphql/configuration/error_extension_env_parser.rb +21 -0
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +20 -2
- data/lib/datadog/tracing/contrib/graphql/ext.rb +11 -5
- data/lib/datadog/tracing/contrib/graphql/integration.rb +9 -9
- data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -8
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +174 -41
- data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +7 -4
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +25 -23
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +13 -17
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +6 -6
- data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +2 -2
- data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +12 -7
- data/lib/datadog/tracing/contrib/grpc/ext.rb +14 -13
- data/lib/datadog/tracing/contrib/grpc/formatting.rb +7 -7
- data/lib/datadog/tracing/contrib/grpc/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/grpc/intercept_with_datadog.rb +1 -1
- data/lib/datadog/tracing/contrib/grpc/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/grpc.rb +7 -6
- data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +4 -3
- data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/hanami/ext.rb +8 -8
- data/lib/datadog/tracing/contrib/hanami/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/hanami/patcher.rb +5 -5
- data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +5 -4
- data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +12 -13
- data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +1 -16
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +20 -9
- data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +6 -6
- data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +13 -8
- data/lib/datadog/tracing/contrib/http/ext.rb +14 -12
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +20 -28
- data/lib/datadog/tracing/contrib/http/integration.rb +9 -8
- data/lib/datadog/tracing/contrib/http/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/http.rb +7 -6
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +20 -9
- data/lib/datadog/tracing/contrib/httpclient/ext.rb +14 -12
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +21 -31
- data/lib/datadog/tracing/contrib/httpclient/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/httpclient/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +20 -9
- data/lib/datadog/tracing/contrib/httprb/ext.rb +14 -12
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +20 -32
- data/lib/datadog/tracing/contrib/httprb/integration.rb +9 -6
- data/lib/datadog/tracing/contrib/httprb/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/integration.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/kafka/event.rb +5 -4
- data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +4 -4
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +5 -5
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +5 -5
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +6 -6
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/join_group.rb +6 -6
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/leave_group.rb +6 -6
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/sync_group.rb +6 -6
- data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +4 -4
- data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +4 -4
- data/lib/datadog/tracing/contrib/kafka/events.rb +9 -9
- data/lib/datadog/tracing/contrib/kafka/ext.rb +40 -40
- data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
- data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
- data/lib/datadog/tracing/contrib/kafka/integration.rb +8 -5
- data/lib/datadog/tracing/contrib/kafka/patcher.rb +17 -3
- data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +31 -0
- data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +50 -0
- data/lib/datadog/tracing/contrib/karafka/ext.rb +28 -0
- data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
- data/lib/datadog/tracing/contrib/karafka/integration.rb +45 -0
- data/lib/datadog/tracing/contrib/karafka/monitor.rb +77 -0
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +102 -0
- data/lib/datadog/tracing/contrib/karafka.rb +38 -0
- data/lib/datadog/tracing/contrib/lograge/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/lograge/ext.rb +1 -1
- data/lib/datadog/tracing/contrib/lograge/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/lograge/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +13 -11
- data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/mongodb/ext.rb +19 -18
- data/lib/datadog/tracing/contrib/mongodb/instrumentation.rb +3 -3
- data/lib/datadog/tracing/contrib/mongodb/integration.rb +9 -6
- data/lib/datadog/tracing/contrib/mongodb/parsers.rb +10 -10
- data/lib/datadog/tracing/contrib/mongodb/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +38 -24
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +9 -3
- data/lib/datadog/tracing/contrib/mysql2/ext.rb +12 -12
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +25 -18
- data/lib/datadog/tracing/contrib/mysql2/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/mysql2/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +19 -2
- data/lib/datadog/tracing/contrib/opensearch/ext.rb +31 -21
- data/lib/datadog/tracing/contrib/opensearch/integration.rb +8 -6
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +75 -77
- data/lib/datadog/tracing/contrib/opensearch/quantize.rb +9 -9
- data/lib/datadog/tracing/contrib/patcher.rb +14 -13
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +9 -3
- data/lib/datadog/tracing/contrib/pg/ext.rb +19 -19
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +21 -24
- data/lib/datadog/tracing/contrib/pg/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/pg/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/presto/ext.rb +22 -22
- data/lib/datadog/tracing/contrib/presto/instrumentation.rb +11 -13
- data/lib/datadog/tracing/contrib/presto/integration.rb +8 -5
- data/lib/datadog/tracing/contrib/presto/patcher.rb +4 -4
- data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +2 -2
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +26 -13
- data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +7 -3
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +29 -5
- data/lib/datadog/tracing/contrib/que/configuration/settings.rb +7 -4
- data/lib/datadog/tracing/contrib/que/ext.rb +19 -18
- data/lib/datadog/tracing/contrib/que/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/que/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/que/tracer.rb +2 -1
- data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +3 -3
- data/lib/datadog/tracing/contrib/racecar/event.rb +6 -10
- data/lib/datadog/tracing/contrib/racecar/events/batch.rb +5 -5
- data/lib/datadog/tracing/contrib/racecar/events/consume.rb +4 -4
- data/lib/datadog/tracing/contrib/racecar/events/message.rb +5 -5
- data/lib/datadog/tracing/contrib/racecar/events.rb +3 -3
- data/lib/datadog/tracing/contrib/racecar/ext.rb +18 -18
- data/lib/datadog/tracing/contrib/racecar/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/racecar/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +13 -3
- data/lib/datadog/tracing/contrib/rack/ext.rb +43 -15
- data/lib/datadog/tracing/contrib/rack/header_collection.rb +13 -3
- data/lib/datadog/tracing/contrib/rack/header_tagging.rb +55 -32
- data/lib/datadog/tracing/contrib/rack/integration.rb +7 -7
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +102 -63
- data/lib/datadog/tracing/contrib/rack/patcher.rb +5 -5
- data/lib/datadog/tracing/contrib/rack/request_queue.rb +8 -7
- data/lib/datadog/tracing/contrib/rack/route_inference.rb +67 -0
- data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +130 -6
- data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +1 -1
- data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +9 -6
- data/lib/datadog/tracing/contrib/rails/ext.rb +16 -14
- data/lib/datadog/tracing/contrib/rails/framework.rb +12 -12
- data/lib/datadog/tracing/contrib/rails/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/rails/log_injection.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/middlewares.rb +4 -4
- data/lib/datadog/tracing/contrib/rails/patcher.rb +11 -9
- data/lib/datadog/tracing/contrib/rails/railtie.rb +4 -4
- data/lib/datadog/tracing/contrib/rails/runner.rb +64 -40
- data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +3 -3
- data/lib/datadog/tracing/contrib/rake/ext.rb +12 -12
- data/lib/datadog/tracing/contrib/rake/instrumentation.rb +13 -11
- data/lib/datadog/tracing/contrib/rake/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/rake/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +4 -4
- data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/redis/ext.rb +19 -18
- data/lib/datadog/tracing/contrib/redis/instrumentation.rb +6 -6
- data/lib/datadog/tracing/contrib/redis/integration.rb +7 -7
- data/lib/datadog/tracing/contrib/redis/patcher.rb +12 -12
- data/lib/datadog/tracing/contrib/redis/quantize.rb +6 -6
- data/lib/datadog/tracing/contrib/redis/tags.rb +7 -12
- data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +11 -9
- data/lib/datadog/tracing/contrib/registerable.rb +11 -0
- data/lib/datadog/tracing/contrib/registry.rb +1 -1
- data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/resque/ext.rb +7 -7
- data/lib/datadog/tracing/contrib/resque/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/resque/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/resque/resque_job.rb +7 -6
- data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +7 -4
- data/lib/datadog/tracing/contrib/rest_client/ext.rb +13 -11
- data/lib/datadog/tracing/contrib/rest_client/integration.rb +8 -5
- data/lib/datadog/tracing/contrib/rest_client/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +18 -17
- data/lib/datadog/tracing/contrib/roda/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/roda/ext.rb +6 -5
- data/lib/datadog/tracing/contrib/roda/instrumentation.rb +12 -7
- data/lib/datadog/tracing/contrib/roda/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/roda/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/semantic_logger/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/semantic_logger/ext.rb +1 -1
- data/lib/datadog/tracing/contrib/semantic_logger/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/semantic_logger/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/semantic_logger/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/sequel/database.rb +12 -11
- data/lib/datadog/tracing/contrib/sequel/dataset.rb +7 -6
- data/lib/datadog/tracing/contrib/sequel/ext.rb +12 -8
- data/lib/datadog/tracing/contrib/sequel/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/sequel/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/sequel/utils.rb +115 -15
- data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/shoryuken/ext.rb +12 -12
- data/lib/datadog/tracing/contrib/shoryuken/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/shoryuken/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +3 -2
- data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +15 -9
- data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +7 -3
- data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +13 -8
- data/lib/datadog/tracing/contrib/sidekiq/ext.rb +31 -29
- data/lib/datadog/tracing/contrib/sidekiq/integration.rb +7 -7
- data/lib/datadog/tracing/contrib/sidekiq/patcher.rb +8 -8
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/heartbeat.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/job_fetch.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/redis_info.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/scheduled_poller.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/stop.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +19 -15
- data/lib/datadog/tracing/contrib/sidekiq/utils.rb +10 -10
- data/lib/datadog/tracing/contrib/sidekiq.rb +7 -6
- data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +7 -3
- data/lib/datadog/tracing/contrib/sinatra/env.rb +6 -6
- data/lib/datadog/tracing/contrib/sinatra/ext.rb +17 -16
- data/lib/datadog/tracing/contrib/sinatra/framework.rb +2 -2
- data/lib/datadog/tracing/contrib/sinatra/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/sinatra/patcher.rb +5 -5
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +11 -10
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +46 -45
- data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/sneakers/ext.rb +12 -12
- data/lib/datadog/tracing/contrib/sneakers/integration.rb +19 -8
- data/lib/datadog/tracing/contrib/sneakers/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/sneakers/tracer.rb +4 -3
- data/lib/datadog/tracing/contrib/span_attribute_schema.rb +9 -4
- data/lib/datadog/tracing/contrib/status_range_env_parser.rb +2 -2
- data/lib/datadog/tracing/contrib/status_range_matcher.rb +13 -1
- data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/stripe/ext.rb +12 -12
- data/lib/datadog/tracing/contrib/stripe/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/stripe/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/stripe/request.rb +5 -5
- data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/sucker_punch/exception_handler.rb +1 -1
- data/lib/datadog/tracing/contrib/sucker_punch/ext.rb +13 -13
- data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +5 -4
- data/lib/datadog/tracing/contrib/sucker_punch/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/sucker_punch/patcher.rb +4 -4
- data/lib/datadog/tracing/contrib/support.rb +28 -0
- data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +9 -3
- data/lib/datadog/tracing/contrib/trilogy/ext.rb +12 -12
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +10 -12
- data/lib/datadog/tracing/contrib/trilogy/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/trilogy/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/utils/database.rb +5 -5
- data/lib/datadog/tracing/contrib/utils/quantization/{hash.rb → hash_formatter.rb} +16 -14
- data/lib/datadog/tracing/contrib/utils/quantization/http.rb +16 -16
- data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +31 -0
- data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +50 -0
- data/lib/datadog/tracing/contrib/waterdrop/ext.rb +18 -0
- data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
- data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
- data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +49 -0
- data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
- data/lib/datadog/tracing/contrib/waterdrop.rb +42 -0
- data/lib/datadog/tracing/contrib.rb +62 -52
- data/lib/datadog/tracing/correlation.rb +19 -12
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +21 -13
- data/lib/datadog/tracing/diagnostics/ext.rb +19 -19
- data/lib/datadog/tracing/diagnostics/health.rb +1 -1
- data/lib/datadog/tracing/distributed/b3_multi.rb +9 -9
- data/lib/datadog/tracing/distributed/b3_single.rb +6 -6
- data/lib/datadog/tracing/distributed/baggage.rb +252 -0
- data/lib/datadog/tracing/distributed/datadog.rb +39 -36
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +12 -27
- data/lib/datadog/tracing/distributed/fetcher.rb +1 -1
- data/lib/datadog/tracing/distributed/helpers.rb +4 -4
- data/lib/datadog/tracing/distributed/none.rb +4 -2
- data/lib/datadog/tracing/distributed/propagation.rb +70 -14
- data/lib/datadog/tracing/distributed/propagation_policy.rb +42 -0
- data/lib/datadog/tracing/distributed/trace_context.rb +126 -97
- data/lib/datadog/tracing/event.rb +6 -8
- data/lib/datadog/tracing/ext.rb +9 -0
- data/lib/datadog/tracing/flush.rb +1 -1
- data/lib/datadog/tracing/metadata/analytics.rb +3 -3
- data/lib/datadog/tracing/metadata/errors.rb +8 -8
- data/lib/datadog/tracing/metadata/ext.rb +86 -59
- data/lib/datadog/tracing/metadata/metastruct.rb +36 -0
- data/lib/datadog/tracing/metadata/metastruct_tagging.rb +42 -0
- data/lib/datadog/tracing/metadata/tagging.rb +8 -8
- data/lib/datadog/tracing/metadata.rb +5 -3
- data/lib/datadog/tracing/pipeline/span_filter.rb +5 -3
- data/lib/datadog/tracing/pipeline/span_processor.rb +3 -1
- data/lib/datadog/tracing/pipeline.rb +4 -4
- data/lib/datadog/tracing/remote.rb +47 -17
- data/lib/datadog/tracing/sampling/all_sampler.rb +1 -1
- data/lib/datadog/tracing/sampling/ext.rb +9 -9
- data/lib/datadog/tracing/sampling/matcher.rb +4 -4
- data/lib/datadog/tracing/sampling/priority_sampler.rb +20 -5
- data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +3 -3
- data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +2 -2
- data/lib/datadog/tracing/sampling/rate_sampler.rb +9 -19
- data/lib/datadog/tracing/sampling/rule.rb +5 -5
- data/lib/datadog/tracing/sampling/rule_sampler.rb +88 -59
- data/lib/datadog/tracing/sampling/sampler.rb +2 -2
- data/lib/datadog/tracing/sampling/span/ext.rb +3 -3
- data/lib/datadog/tracing/sampling/span/matcher.rb +2 -2
- data/lib/datadog/tracing/sampling/span/rule.rb +1 -2
- data/lib/datadog/tracing/sampling/span/rule_parser.rb +11 -11
- data/lib/datadog/tracing/sampling/span/sampler.rb +0 -7
- data/lib/datadog/tracing/span.rb +28 -11
- data/lib/datadog/tracing/span_event.rb +125 -5
- data/lib/datadog/tracing/span_link.rb +12 -12
- data/lib/datadog/tracing/span_operation.rb +119 -39
- data/lib/datadog/tracing/sync_writer.rb +14 -11
- data/lib/datadog/tracing/trace_digest.rb +36 -24
- data/lib/datadog/tracing/trace_operation.rb +243 -112
- data/lib/datadog/tracing/trace_segment.rb +18 -16
- data/lib/datadog/tracing/tracer.rb +181 -72
- data/lib/datadog/tracing/transport/http/client.rb +17 -30
- data/lib/datadog/tracing/transport/http/statistics.rb +2 -2
- data/lib/datadog/tracing/transport/http/traces.rb +11 -90
- data/lib/datadog/tracing/transport/http.rb +26 -77
- data/lib/datadog/tracing/transport/io/client.rb +13 -16
- data/lib/datadog/tracing/transport/io/response.rb +1 -1
- data/lib/datadog/tracing/transport/io/traces.rb +32 -38
- data/lib/datadog/tracing/transport/io.rb +3 -3
- data/lib/datadog/tracing/transport/serializable_trace.rb +47 -23
- data/lib/datadog/tracing/transport/statistics.rb +3 -3
- data/lib/datadog/tracing/transport/trace_formatter.rb +36 -7
- data/lib/datadog/tracing/transport/traces.rb +56 -90
- data/lib/datadog/tracing/utils.rb +3 -3
- data/lib/datadog/tracing/workers.rb +11 -9
- data/lib/datadog/tracing/writer.rb +15 -11
- data/lib/datadog/tracing.rb +19 -6
- data/lib/datadog/version.rb +4 -4
- data/lib/datadog.rb +20 -7
- metadata +327 -69
- data/ext/datadog_profiling_loader/datadog_profiling_loader.c +0 -142
- data/ext/datadog_profiling_loader/extconf.rb +0 -60
- data/ext/datadog_profiling_native_extension/clock_id_noop.c +0 -21
- data/ext/libdatadog_api/macos_development.md +0 -26
- 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/configuration/settings.rb +0 -215
- 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/ext.rb +0 -13
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +0 -53
- data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -14
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +0 -48
- data/lib/datadog/appsec/monitor/reactive/set_user.rb +0 -45
- data/lib/datadog/appsec/processor/context.rb +0 -107
- data/lib/datadog/appsec/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/api.rb +0 -58
- data/lib/datadog/core/remote/transport/http/builder.rb +0 -219
- data/lib/datadog/core/remote/transport/http/client.rb +0 -48
- 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/core/utils/base64.rb +0 -22
- data/lib/datadog/di/configuration/settings.rb +0 -188
- data/lib/datadog/di/transport.rb +0 -81
- data/lib/datadog/tracing/contrib/rails/utils.rb +0 -26
- data/lib/datadog/tracing/transport/http/api/spec.rb +0 -19
- data/lib/datadog/tracing/transport/http/api.rb +0 -43
- data/lib/datadog/tracing/workers/trace_writer.rb +0 -196
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
#include "setup_signal_handler.h"
|
|
18
18
|
#include "time_helpers.h"
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
#ifdef __APPLE__
|
|
21
|
+
#include "macos_sampler_thread.h"
|
|
22
|
+
#endif
|
|
23
|
+
|
|
24
|
+
// Used to trigger the execution of Collectors::ThreadContext, which implements all of the sampling logic
|
|
21
25
|
// itself; this class only implements the "when to do it" part.
|
|
22
26
|
//
|
|
23
27
|
// This file implements the native bits of the Datadog::Profiling::Collectors::CpuAndWallTimeWorker class
|
|
@@ -33,7 +37,7 @@
|
|
|
33
37
|
// Currently, sampling Ruby threads requires calling Ruby VM APIs that are only safe to call while holding on to the
|
|
34
38
|
// global VM lock (and are not async-signal safe -- cannot be called from a signal handler).
|
|
35
39
|
//
|
|
36
|
-
// @ivoanjo: As a note, I don't think we should think of this constraint as set in stone. Since can reach
|
|
40
|
+
// @ivoanjo: As a note, I don't think we should think of this constraint as set in stone. Since we can reach inside the Ruby
|
|
37
41
|
// internals, we may be able to figure out a way of overcoming it. But it's definitely going to be hard so for now
|
|
38
42
|
// we're considering it as a given.
|
|
39
43
|
//
|
|
@@ -76,8 +80,6 @@
|
|
|
76
80
|
//
|
|
77
81
|
// ---
|
|
78
82
|
|
|
79
|
-
#define ERR_CLOCK_FAIL "failed to get clock time"
|
|
80
|
-
|
|
81
83
|
// Maximum allowed value for an allocation weight. Attempts to use higher values will result in clamping.
|
|
82
84
|
// See https://docs.google.com/document/d/1lWLB714wlLBBq6T4xZyAc4a5wtWhSmr4-hgiPKeErlA/edit#heading=h.ugp0zxcj5iqh
|
|
83
85
|
// (Datadog-only link) for research backing the choice of this value.
|
|
@@ -89,10 +91,11 @@ unsigned int MAX_ALLOC_WEIGHT = 10000;
|
|
|
89
91
|
static rb_postponed_job_handle_t sample_from_postponed_job_handle;
|
|
90
92
|
static rb_postponed_job_handle_t after_gc_from_postponed_job_handle;
|
|
91
93
|
static rb_postponed_job_handle_t after_gvl_running_from_postponed_job_handle;
|
|
94
|
+
static rb_postponed_job_handle_t after_allocation_from_postponed_job_handle;
|
|
92
95
|
#endif
|
|
93
96
|
|
|
94
97
|
// Contains state for a single CpuAndWallTimeWorker instance
|
|
95
|
-
struct
|
|
98
|
+
typedef struct {
|
|
96
99
|
// These are immutable after initialization
|
|
97
100
|
|
|
98
101
|
bool gc_profiling_enabled;
|
|
@@ -102,6 +105,8 @@ struct cpu_and_wall_time_worker_state {
|
|
|
102
105
|
bool allocation_counting_enabled;
|
|
103
106
|
bool gvl_profiling_enabled;
|
|
104
107
|
bool skip_idle_samples_for_testing;
|
|
108
|
+
bool sighandler_sampling_enabled;
|
|
109
|
+
uint32_t cpu_sampling_interval_ms;
|
|
105
110
|
VALUE self_instance;
|
|
106
111
|
VALUE thread_context_collector_instance;
|
|
107
112
|
VALUE idle_sampling_helper_instance;
|
|
@@ -116,13 +121,18 @@ struct cpu_and_wall_time_worker_state {
|
|
|
116
121
|
// When something goes wrong during sampling, we record the Ruby exception here, so that it can be "re-raised" on
|
|
117
122
|
// the CpuAndWallTimeWorker thread
|
|
118
123
|
VALUE failure_exception;
|
|
124
|
+
const char *failure_exception_during_operation;
|
|
119
125
|
// Used by `_native_stop` to flag the worker thread to start (see comment on `_native_sampling_loop`)
|
|
120
126
|
VALUE stop_thread;
|
|
121
127
|
|
|
122
128
|
// Others
|
|
123
129
|
|
|
124
130
|
// Used to detect/avoid nested sampling, e.g. when on_newobj_event gets triggered by a memory allocation
|
|
125
|
-
// that happens during another sample
|
|
131
|
+
// that happens during another sample, or when the signal handler gets triggered while we're already in the middle of
|
|
132
|
+
// sampling.
|
|
133
|
+
//
|
|
134
|
+
// @ivoanjo: Right now we always sample inside `safely_call`; if that ever changes, this flag may need to become
|
|
135
|
+
// volatile/atomic/have some barriers to ensure it's visible during e.g. signal handlers.
|
|
126
136
|
bool during_sample;
|
|
127
137
|
|
|
128
138
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
@@ -134,27 +144,19 @@ struct cpu_and_wall_time_worker_state {
|
|
|
134
144
|
// # Generic stats
|
|
135
145
|
// How many times we tried to trigger a sample
|
|
136
146
|
unsigned int trigger_sample_attempts;
|
|
147
|
+
// How many times extra sleep was triggered by dynamic sampling rate
|
|
148
|
+
unsigned int trigger_sample_extra_sleep;
|
|
137
149
|
// How many times we tried to simulate signal delivery
|
|
138
150
|
unsigned int trigger_simulated_signal_delivery_attempts;
|
|
139
151
|
// How many times we actually simulated signal delivery
|
|
140
152
|
unsigned int simulated_signal_delivery;
|
|
141
|
-
// How many times we actually called rb_postponed_job_register_one from
|
|
153
|
+
// How many times we actually called rb_postponed_job_register_one from the signal handler
|
|
142
154
|
unsigned int signal_handler_enqueued_sample;
|
|
143
|
-
// How many times
|
|
144
|
-
unsigned int
|
|
155
|
+
// How many times we prepared a sample (sampled directly) from the signal handler
|
|
156
|
+
unsigned int signal_handler_prepared_sample;
|
|
145
157
|
// How many times we actually tried to interrupt a thread for sampling
|
|
146
158
|
unsigned int interrupt_thread_attempts;
|
|
147
159
|
|
|
148
|
-
// # Stats for the results of calling rb_postponed_job_register_one
|
|
149
|
-
// The same function was already waiting to be executed
|
|
150
|
-
unsigned int postponed_job_skipped_already_existed;
|
|
151
|
-
// The function was added to the queue successfully
|
|
152
|
-
unsigned int postponed_job_success;
|
|
153
|
-
// The queue was full
|
|
154
|
-
unsigned int postponed_job_full;
|
|
155
|
-
// The function returned an unknown result code
|
|
156
|
-
unsigned int postponed_job_unknown_result;
|
|
157
|
-
|
|
158
160
|
// # CPU/Walltime sampling stats
|
|
159
161
|
// How many times we actually CPU/wall sampled
|
|
160
162
|
unsigned int cpu_sampled;
|
|
@@ -178,6 +180,11 @@ struct cpu_and_wall_time_worker_state {
|
|
|
178
180
|
unsigned int allocations_during_sample;
|
|
179
181
|
|
|
180
182
|
// # GVL profiling stats
|
|
183
|
+
// Note that this tracks two kind of samples from RESUMED:
|
|
184
|
+
// * samples when Waiting for GVL for more than the threshold
|
|
185
|
+
// * samples for the skip-samples-while-without-GVL optimization,
|
|
186
|
+
// which are needed to attribute the time without GVL to the correct Ruby stack
|
|
187
|
+
|
|
181
188
|
// How many times we triggered the after_gvl_running sampling
|
|
182
189
|
unsigned int after_gvl_running;
|
|
183
190
|
// How many times we skipped the after_gvl_running sampling
|
|
@@ -186,33 +193,44 @@ struct cpu_and_wall_time_worker_state {
|
|
|
186
193
|
uint64_t gvl_sampling_time_ns_min;
|
|
187
194
|
uint64_t gvl_sampling_time_ns_max;
|
|
188
195
|
uint64_t gvl_sampling_time_ns_total;
|
|
196
|
+
|
|
197
|
+
struct vm_metrics {
|
|
198
|
+
// Total time spent waiting for the GVL
|
|
199
|
+
uint64_t gvl_waiting_time_ns_total;
|
|
200
|
+
} vm_metrics;
|
|
189
201
|
} stats;
|
|
190
|
-
};
|
|
202
|
+
} cpu_and_wall_time_worker_state;
|
|
191
203
|
|
|
192
204
|
static VALUE _native_new(VALUE klass);
|
|
193
205
|
static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
|
|
194
206
|
static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr);
|
|
195
207
|
static VALUE _native_sampling_loop(VALUE self, VALUE instance);
|
|
196
208
|
static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread);
|
|
197
|
-
static VALUE stop(VALUE self_instance, VALUE optional_exception);
|
|
198
|
-
static void stop_state(
|
|
209
|
+
static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *optional_exception_during_operation);
|
|
210
|
+
static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception, const char *optional_operation_name);
|
|
199
211
|
static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
|
|
200
212
|
static void *run_sampling_trigger_loop(void *state_ptr);
|
|
201
213
|
static void interrupt_sampling_trigger_loop(void *state_ptr);
|
|
202
214
|
static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
203
215
|
static VALUE rescued_sample_from_postponed_job(VALUE self_instance);
|
|
204
|
-
static VALUE handle_sampling_failure(VALUE self_instance, VALUE exception);
|
|
205
216
|
static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self);
|
|
206
217
|
static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance);
|
|
207
218
|
static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
219
|
+
static VALUE _native_failure_exception_during_operation(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
208
220
|
static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
|
|
209
221
|
static VALUE _native_install_testing_signal_handler(DDTRACE_UNUSED VALUE self);
|
|
210
222
|
static VALUE _native_remove_testing_signal_handler(DDTRACE_UNUSED VALUE self);
|
|
223
|
+
static VALUE _native_install_sigprof_handler_on_altstack(DDTRACE_UNUSED VALUE self);
|
|
211
224
|
static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self);
|
|
212
225
|
static VALUE _native_gc_tracepoint(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
213
226
|
static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused);
|
|
214
227
|
static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
215
|
-
static VALUE safely_call(
|
|
228
|
+
static VALUE safely_call(
|
|
229
|
+
VALUE (*function_to_call_safely)(VALUE),
|
|
230
|
+
VALUE function_to_call_safely_arg,
|
|
231
|
+
VALUE instance,
|
|
232
|
+
VALUE (*handle_sampling_failure)(VALUE, VALUE)
|
|
233
|
+
);
|
|
216
234
|
static VALUE _native_simulate_handle_sampling_signal(DDTRACE_UNUSED VALUE self);
|
|
217
235
|
static VALUE _native_simulate_sample_from_postponed_job(DDTRACE_UNUSED VALUE self);
|
|
218
236
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
@@ -221,23 +239,33 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
|
221
239
|
static VALUE _native_stats_reset_not_thread_safe(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
222
240
|
void *simulate_sampling_signal_delivery(DDTRACE_UNUSED void *_unused);
|
|
223
241
|
static void grab_gvl_and_sample(void);
|
|
224
|
-
static void reset_stats_not_thread_safe(
|
|
242
|
+
static void reset_stats_not_thread_safe(cpu_and_wall_time_worker_state *state);
|
|
225
243
|
static void sleep_for(uint64_t time_ns);
|
|
226
244
|
static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self);
|
|
227
245
|
static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *unused2);
|
|
228
|
-
static void disable_tracepoints(
|
|
246
|
+
static void disable_tracepoints(cpu_and_wall_time_worker_state *state);
|
|
229
247
|
static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self);
|
|
230
248
|
static VALUE rescued_sample_allocation(VALUE tracepoint_data);
|
|
231
|
-
static
|
|
249
|
+
static VALUE rescued_after_allocation(VALUE self_instance);
|
|
250
|
+
static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error);
|
|
251
|
+
static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state);
|
|
232
252
|
static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
|
|
233
253
|
static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self);
|
|
234
254
|
static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);
|
|
235
255
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
236
|
-
static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused);
|
|
237
|
-
static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
256
|
+
static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused);
|
|
257
|
+
static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
258
|
+
static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance);
|
|
259
|
+
static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception);
|
|
238
260
|
#endif
|
|
239
|
-
static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance);
|
|
240
261
|
static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
262
|
+
static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception);
|
|
263
|
+
static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception);
|
|
264
|
+
static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception);
|
|
265
|
+
static VALUE handle_sampling_failure_rescued_after_allocation(VALUE self_instance, VALUE exception);
|
|
266
|
+
static inline void during_sample_enter(cpu_and_wall_time_worker_state* state);
|
|
267
|
+
static inline void during_sample_exit(cpu_and_wall_time_worker_state* state);
|
|
268
|
+
static void after_allocation_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
241
269
|
|
|
242
270
|
// We're using `on_newobj_event` function with `rb_add_event_hook2`, which requires in its public signature a function
|
|
243
271
|
// with signature `rb_event_hook_func_t` which doesn't match `on_newobj_event`.
|
|
@@ -262,7 +290,17 @@ static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE
|
|
|
262
290
|
// This global state is needed because a bunch of functions on this file need to access it from situations
|
|
263
291
|
// (e.g. signal handler) where it's impossible or just awkward to pass it as an argument.
|
|
264
292
|
static VALUE active_sampler_instance = Qnil;
|
|
265
|
-
static
|
|
293
|
+
static cpu_and_wall_time_worker_state *active_sampler_instance_state = NULL;
|
|
294
|
+
static VALUE clock_failure_exception_class = Qnil;
|
|
295
|
+
|
|
296
|
+
// Stats that live outside of any particular `cpu_and_wall_time_worker_state`, so that they can always be safely
|
|
297
|
+
// touched, even when we're not sure it's safe to touch the state (e.g. signal handler, no GVL, etc).
|
|
298
|
+
typedef struct {
|
|
299
|
+
// How many times we skipped sampling because we were running on the alternate signal stack (e.g. nested inside
|
|
300
|
+
// another signal handler, presumably GC compaction).
|
|
301
|
+
unsigned int signal_handler_skipped_sample_on_altstack;
|
|
302
|
+
} global_stats_t;
|
|
303
|
+
static global_stats_t global_stats;
|
|
266
304
|
|
|
267
305
|
// See handle_sampling_signal for details on what this does
|
|
268
306
|
#ifdef NO_POSTPONED_TRIGGER
|
|
@@ -284,13 +322,15 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
284
322
|
sample_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, sample_from_postponed_job, NULL);
|
|
285
323
|
after_gc_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gc_from_postponed_job, NULL);
|
|
286
324
|
after_gvl_running_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gvl_running_from_postponed_job, NULL);
|
|
325
|
+
after_allocation_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_allocation_from_postponed_job, NULL);
|
|
287
326
|
|
|
288
327
|
if (
|
|
289
328
|
sample_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
290
329
|
after_gc_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
291
|
-
after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
|
|
330
|
+
after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
331
|
+
after_allocation_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
|
|
292
332
|
) {
|
|
293
|
-
|
|
333
|
+
raise_error(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
|
|
294
334
|
}
|
|
295
335
|
#else
|
|
296
336
|
gc_finalize_deferred_workaround = objspace_ptr_for_gc_finalize_deferred_workaround();
|
|
@@ -300,6 +340,8 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
300
340
|
VALUE collectors_cpu_and_wall_time_worker_class = rb_define_class_under(collectors_module, "CpuAndWallTimeWorker", rb_cObject);
|
|
301
341
|
// Hosts methods used for testing the native code using RSpec
|
|
302
342
|
VALUE testing_module = rb_define_module_under(collectors_cpu_and_wall_time_worker_class, "Testing");
|
|
343
|
+
clock_failure_exception_class = rb_define_class_under(collectors_cpu_and_wall_time_worker_class, "ClockFailure", rb_eRuntimeError);
|
|
344
|
+
rb_gc_register_mark_object(clock_failure_exception_class);
|
|
303
345
|
|
|
304
346
|
// Instances of the CpuAndWallTimeWorker class are "TypedData" objects.
|
|
305
347
|
// "TypedData" objects are special objects in the Ruby VM that can wrap C structs.
|
|
@@ -319,11 +361,13 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
319
361
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stats_reset_not_thread_safe", _native_stats_reset_not_thread_safe, 1);
|
|
320
362
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_allocation_count", _native_allocation_count, 0);
|
|
321
363
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_is_running?", _native_is_running, 1);
|
|
364
|
+
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_failure_exception_during_operation", _native_failure_exception_during_operation, 1);
|
|
322
365
|
rb_define_singleton_method(testing_module, "_native_current_sigprof_signal_handler", _native_current_sigprof_signal_handler, 0);
|
|
323
366
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_hold_signals", _native_hold_signals, 0);
|
|
324
367
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_resume_signals", _native_resume_signals, 0);
|
|
325
368
|
rb_define_singleton_method(testing_module, "_native_install_testing_signal_handler", _native_install_testing_signal_handler, 0);
|
|
326
369
|
rb_define_singleton_method(testing_module, "_native_remove_testing_signal_handler", _native_remove_testing_signal_handler, 0);
|
|
370
|
+
rb_define_singleton_method(testing_module, "_native_install_sigprof_handler_on_altstack", _native_install_sigprof_handler_on_altstack, 0);
|
|
327
371
|
rb_define_singleton_method(testing_module, "_native_trigger_sample", _native_trigger_sample, 0);
|
|
328
372
|
rb_define_singleton_method(testing_module, "_native_gc_tracepoint", _native_gc_tracepoint, 1);
|
|
329
373
|
rb_define_singleton_method(testing_module, "_native_simulate_handle_sampling_signal", _native_simulate_handle_sampling_signal, 0);
|
|
@@ -334,7 +378,7 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
334
378
|
rb_define_singleton_method(testing_module, "_native_gvl_profiling_hook_active", _native_gvl_profiling_hook_active, 1);
|
|
335
379
|
}
|
|
336
380
|
|
|
337
|
-
// This structure is used to define a Ruby object that stores a pointer to a
|
|
381
|
+
// This structure is used to define a Ruby object that stores a pointer to a cpu_and_wall_time_worker_state
|
|
338
382
|
// See also https://github.com/ruby/ruby/blob/master/doc/extension.rdoc for how this works
|
|
339
383
|
static const rb_data_type_t cpu_and_wall_time_worker_typed_data = {
|
|
340
384
|
.wrap_struct_name = "Datadog::Profiling::Collectors::CpuAndWallTimeWorker",
|
|
@@ -350,7 +394,7 @@ static const rb_data_type_t cpu_and_wall_time_worker_typed_data = {
|
|
|
350
394
|
static VALUE _native_new(VALUE klass) {
|
|
351
395
|
long now = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
352
396
|
|
|
353
|
-
|
|
397
|
+
cpu_and_wall_time_worker_state *state = ruby_xcalloc(1, sizeof(cpu_and_wall_time_worker_state));
|
|
354
398
|
|
|
355
399
|
// Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
|
|
356
400
|
// being leaked.
|
|
@@ -362,6 +406,8 @@ static VALUE _native_new(VALUE klass) {
|
|
|
362
406
|
state->allocation_counting_enabled = false;
|
|
363
407
|
state->gvl_profiling_enabled = false;
|
|
364
408
|
state->skip_idle_samples_for_testing = false;
|
|
409
|
+
state->sighandler_sampling_enabled = false;
|
|
410
|
+
state->cpu_sampling_interval_ms = 10;
|
|
365
411
|
state->thread_context_collector_instance = Qnil;
|
|
366
412
|
state->idle_sampling_helper_instance = Qnil;
|
|
367
413
|
state->owner_thread = Qnil;
|
|
@@ -370,9 +416,10 @@ static VALUE _native_new(VALUE klass) {
|
|
|
370
416
|
|
|
371
417
|
atomic_init(&state->should_run, false);
|
|
372
418
|
state->failure_exception = Qnil;
|
|
419
|
+
state->failure_exception_during_operation = NULL;
|
|
373
420
|
state->stop_thread = Qnil;
|
|
374
421
|
|
|
375
|
-
state
|
|
422
|
+
during_sample_exit(state);
|
|
376
423
|
|
|
377
424
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
378
425
|
state->gvl_profiling_hook = NULL;
|
|
@@ -404,6 +451,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
404
451
|
VALUE allocation_counting_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("allocation_counting_enabled")));
|
|
405
452
|
VALUE gvl_profiling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("gvl_profiling_enabled")));
|
|
406
453
|
VALUE skip_idle_samples_for_testing = rb_hash_fetch(options, ID2SYM(rb_intern("skip_idle_samples_for_testing")));
|
|
454
|
+
VALUE sighandler_sampling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("sighandler_sampling_enabled")));
|
|
455
|
+
VALUE cpu_sampling_interval_ms = rb_hash_fetch(options, ID2SYM(rb_intern("cpu_sampling_interval_ms")));
|
|
407
456
|
|
|
408
457
|
ENFORCE_BOOLEAN(gc_profiling_enabled);
|
|
409
458
|
ENFORCE_BOOLEAN(no_signals_workaround_enabled);
|
|
@@ -413,9 +462,11 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
413
462
|
ENFORCE_BOOLEAN(allocation_counting_enabled);
|
|
414
463
|
ENFORCE_BOOLEAN(gvl_profiling_enabled);
|
|
415
464
|
ENFORCE_BOOLEAN(skip_idle_samples_for_testing)
|
|
465
|
+
ENFORCE_BOOLEAN(sighandler_sampling_enabled)
|
|
466
|
+
ENFORCE_TYPE(cpu_sampling_interval_ms, T_FIXNUM);
|
|
416
467
|
|
|
417
|
-
|
|
418
|
-
TypedData_Get_Struct(self_instance,
|
|
468
|
+
cpu_and_wall_time_worker_state *state;
|
|
469
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
419
470
|
|
|
420
471
|
state->gc_profiling_enabled = (gc_profiling_enabled == Qtrue);
|
|
421
472
|
state->no_signals_workaround_enabled = (no_signals_workaround_enabled == Qtrue);
|
|
@@ -424,6 +475,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
424
475
|
state->allocation_counting_enabled = (allocation_counting_enabled == Qtrue);
|
|
425
476
|
state->gvl_profiling_enabled = (gvl_profiling_enabled == Qtrue);
|
|
426
477
|
state->skip_idle_samples_for_testing = (skip_idle_samples_for_testing == Qtrue);
|
|
478
|
+
state->sighandler_sampling_enabled = (sighandler_sampling_enabled == Qtrue);
|
|
479
|
+
state->cpu_sampling_interval_ms = NUM2INT(cpu_sampling_interval_ms);
|
|
427
480
|
|
|
428
481
|
double total_overhead_target_percentage = NUM2DBL(dynamic_sampling_rate_overhead_target_percentage);
|
|
429
482
|
if (!state->allocation_profiling_enabled) {
|
|
@@ -445,7 +498,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
445
498
|
|
|
446
499
|
// Since our state contains references to Ruby objects, we need to tell the Ruby GC about them
|
|
447
500
|
static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr) {
|
|
448
|
-
|
|
501
|
+
cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
|
|
449
502
|
|
|
450
503
|
rb_gc_mark(state->thread_context_collector_instance);
|
|
451
504
|
rb_gc_mark(state->idle_sampling_helper_instance);
|
|
@@ -457,8 +510,8 @@ static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr) {
|
|
|
457
510
|
|
|
458
511
|
// Called in a background thread created in CpuAndWallTimeWorker#start
|
|
459
512
|
static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
460
|
-
|
|
461
|
-
TypedData_Get_Struct(instance,
|
|
513
|
+
cpu_and_wall_time_worker_state *state;
|
|
514
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
462
515
|
|
|
463
516
|
// If we already got a delayed exception registered even before starting, raise before starting
|
|
464
517
|
if (state->failure_exception != Qnil) {
|
|
@@ -466,13 +519,10 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
466
519
|
rb_exc_raise(state->failure_exception);
|
|
467
520
|
}
|
|
468
521
|
|
|
469
|
-
|
|
522
|
+
cpu_and_wall_time_worker_state *old_state = active_sampler_instance_state;
|
|
470
523
|
if (old_state != NULL) {
|
|
471
524
|
if (is_thread_alive(old_state->owner_thread)) {
|
|
472
|
-
|
|
473
|
-
rb_eRuntimeError,
|
|
474
|
-
"Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread"
|
|
475
|
-
);
|
|
525
|
+
raise_error(rb_eRuntimeError, "Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread");
|
|
476
526
|
} else {
|
|
477
527
|
// The previously active thread seems to have died without cleaning up after itself.
|
|
478
528
|
// In this case, we can still go ahead and start the profiler BUT we make sure to disable any existing tracepoint
|
|
@@ -489,11 +539,20 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
489
539
|
// situation we stop immediately and never even start the sampling trigger loop.
|
|
490
540
|
if (state->stop_thread == rb_thread_current()) return Qnil;
|
|
491
541
|
|
|
542
|
+
thread_context_collector_profiler_internal_thread_started();
|
|
543
|
+
|
|
492
544
|
// Reset the dynamic sampling rate state, if any (reminder: the monotonic clock reference may change after a fork)
|
|
493
545
|
dynamic_sampling_rate_reset(&state->cpu_dynamic_sampling_rate);
|
|
494
546
|
long now = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
495
547
|
discrete_dynamic_sampler_reset(&state->allocation_sampler, now);
|
|
496
548
|
|
|
549
|
+
// Reset per-thread state, if any. This ensures there's no leftover state from a previous profiler run that would
|
|
550
|
+
// affect or be included in samples taken by this profiler about to run.
|
|
551
|
+
//
|
|
552
|
+
// NOTE: This needs to be called before we enable any tracepoints or anything that could trigger samples (e.g.
|
|
553
|
+
// reset cannot be concurrent with any sampling activity)
|
|
554
|
+
thread_context_collector_reset_all_per_thread_contexts(state->thread_context_collector_instance);
|
|
555
|
+
|
|
497
556
|
// This write to a global is thread-safe BECAUSE we're still holding on to the global VM lock at this point
|
|
498
557
|
active_sampler_instance_state = state;
|
|
499
558
|
active_sampler_instance = instance;
|
|
@@ -503,6 +562,14 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
503
562
|
|
|
504
563
|
block_sigprof_signal_handler_from_running_in_current_thread(); // We want to interrupt the thread with the global VM lock, never this one
|
|
505
564
|
|
|
565
|
+
#ifdef __APPLE__
|
|
566
|
+
// Promote the native thread that will run the sampling loop to a realtime scheduling policy so
|
|
567
|
+
// its periodic wake-ups are not subject to the default ~1-2ms timeshare wake latency. The matching
|
|
568
|
+
// demote happens below in this same function, alongside the SIGPROF unblock dance, so both per-thread
|
|
569
|
+
// policy changes are reverted before Ruby reuses this native thread for an unrelated Ruby thread.
|
|
570
|
+
promote_sampler_thread_to_realtime(MILLIS_AS_NS(state->cpu_sampling_interval_ms));
|
|
571
|
+
#endif
|
|
572
|
+
|
|
506
573
|
// Release GVL, get to the actual work!
|
|
507
574
|
int exception_state;
|
|
508
575
|
rb_protect(release_gvl_and_run_sampling_trigger_loop, instance, &exception_state);
|
|
@@ -524,6 +591,14 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
524
591
|
// had SIGPROF delivery blocked. :hide_the_pain_harold:
|
|
525
592
|
unblock_sigprof_signal_handler_from_running_in_current_thread();
|
|
526
593
|
|
|
594
|
+
#ifdef __APPLE__
|
|
595
|
+
// Pairs with promote_sampler_thread_to_realtime above. Same reasoning as the SIGPROF unblock:
|
|
596
|
+
// Ruby may reuse this native thread for an unrelated Ruby thread that would otherwise inherit
|
|
597
|
+
// our scheduler policy. Doing it here also covers the exception path, since grab_gvl_and_sample
|
|
598
|
+
// can raise and unwind out of the sampling loop.
|
|
599
|
+
demote_sampler_thread_from_realtime();
|
|
600
|
+
#endif
|
|
601
|
+
|
|
527
602
|
// Why replace and not use remove the signal handler? We do this because when a process receives a SIGPROF without
|
|
528
603
|
// having an explicit signal handler set up, the process will instantly terminate with a confusing
|
|
529
604
|
// "Profiling timer expired" message left behind. (This message doesn't come from us -- it's the default message for
|
|
@@ -534,8 +609,9 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
534
609
|
// @ivoanjo: I suspect this will never happen, but the cost of getting it wrong is really high (VM terminates) so this
|
|
535
610
|
// is a just-in-case situation.
|
|
536
611
|
//
|
|
537
|
-
// Note 2:
|
|
538
|
-
replace_sigprof_signal_handler_with_empty_handler
|
|
612
|
+
// Note 2: If exception_state is set, we have a pending exception that we'll re-raise below.
|
|
613
|
+
// In that case, we don't want replace_sigprof_signal_handler_with_empty_handler to raise another exception.
|
|
614
|
+
replace_sigprof_signal_handler_with_empty_handler(handle_sampling_signal, !exception_state);
|
|
539
615
|
|
|
540
616
|
// Ensure that instance is not garbage collected while the native sampling loop is running; this is probably not needed, but just in case
|
|
541
617
|
RB_GC_GUARD(instance);
|
|
@@ -546,68 +622,115 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
546
622
|
}
|
|
547
623
|
|
|
548
624
|
static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread) {
|
|
549
|
-
|
|
550
|
-
TypedData_Get_Struct(self_instance,
|
|
625
|
+
cpu_and_wall_time_worker_state *state;
|
|
626
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
551
627
|
|
|
552
628
|
state->stop_thread = worker_thread;
|
|
553
629
|
|
|
554
|
-
return stop(self_instance,
|
|
630
|
+
return stop(self_instance, Qnil, NULL);
|
|
555
631
|
}
|
|
556
632
|
|
|
557
|
-
|
|
633
|
+
// When providing an `optional_exception`, `optional_exception_during_operation` should be provided as well
|
|
634
|
+
static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception, const char *optional_exception_during_operation) {
|
|
558
635
|
atomic_store(&state->should_run, false);
|
|
559
636
|
state->failure_exception = optional_exception;
|
|
637
|
+
state->failure_exception_during_operation = optional_exception_during_operation;
|
|
560
638
|
|
|
561
639
|
// Disable the tracepoints as soon as possible, so the VM doesn't keep on calling them
|
|
562
640
|
disable_tracepoints(state);
|
|
563
641
|
}
|
|
564
642
|
|
|
565
|
-
static VALUE stop(VALUE self_instance, VALUE optional_exception) {
|
|
566
|
-
|
|
567
|
-
TypedData_Get_Struct(self_instance,
|
|
643
|
+
static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *optional_exception_during_operation) {
|
|
644
|
+
cpu_and_wall_time_worker_state *state;
|
|
645
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
568
646
|
|
|
569
|
-
stop_state(state, optional_exception);
|
|
647
|
+
stop_state(state, optional_exception, optional_exception_during_operation);
|
|
570
648
|
|
|
571
649
|
return Qtrue;
|
|
572
650
|
}
|
|
573
651
|
|
|
652
|
+
// Our SIGPROF handler is not installed with SA_ONSTACK (see `install_sigprof_signal_handler`).
|
|
653
|
+
// If we detect our signal handler is running on the alt stack, it means we interrupted another signal handler
|
|
654
|
+
// that IS running on the alt stack -- in practice, Ruby's GC compaction read-barrier handler.
|
|
655
|
+
#ifdef __APPLE__
|
|
656
|
+
// On macOS, `ucontext->uc_stack.ss_sp` reports the (live) stack pointer near the top of the alt stack rather than the
|
|
657
|
+
// alt-stack base like Linux does, and `ss_size` is the full alt-stack size. Thus the linux version of the check
|
|
658
|
+
// didn't work for macOS.
|
|
659
|
+
// As an alternative, we query `sigaltstack()`; we save/restore errno since `sigaltstack()` may clobber it.
|
|
660
|
+
static bool is_running_on_alternate_signal_stack(DDTRACE_UNUSED void *ucontext) {
|
|
661
|
+
int old_errno = errno;
|
|
662
|
+
stack_t current_alt_stack;
|
|
663
|
+
bool on_alt_stack = sigaltstack(NULL, ¤t_alt_stack) == 0 && (current_alt_stack.ss_flags & SS_ONSTACK) != 0;
|
|
664
|
+
errno = old_errno;
|
|
665
|
+
return on_alt_stack;
|
|
666
|
+
}
|
|
667
|
+
#else
|
|
668
|
+
// NOTE: On Linux we use the alt-stack bounds from the `ucontext` rather than `sigaltstack()`, allowing us to avoid one syscall.
|
|
669
|
+
static bool is_running_on_alternate_signal_stack(void *ucontext) {
|
|
670
|
+
if (ucontext == NULL) return false;
|
|
671
|
+
|
|
672
|
+
stack_t alt_stack = ((ucontext_t *) ucontext)->uc_stack;
|
|
673
|
+
if (alt_stack.ss_size == 0) return false; // No alternate signal stack registered on this thread
|
|
674
|
+
|
|
675
|
+
char stack_local;
|
|
676
|
+
uintptr_t current_sp = (uintptr_t) &stack_local;
|
|
677
|
+
uintptr_t alt_stack_start = (uintptr_t) alt_stack.ss_sp;
|
|
678
|
+
|
|
679
|
+
return current_sp >= alt_stack_start && current_sp < alt_stack_start + alt_stack.ss_size;
|
|
680
|
+
}
|
|
681
|
+
#endif
|
|
682
|
+
|
|
574
683
|
// NOTE: Remember that this will run in the thread and within the scope of user code, including user C code.
|
|
575
684
|
// We need to be careful not to change any state that may be observed OR to restore it if we do. For instance, if anything
|
|
576
685
|
// we do here can set `errno`, then we must be careful to restore the old `errno` after the fact.
|
|
577
|
-
static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info,
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
//
|
|
581
|
-
|
|
686
|
+
static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, void *ucontext) {
|
|
687
|
+
// If we're running on the alternate signal stack, we've interrupted another signal handler that's running
|
|
688
|
+
// there -- in practice, Ruby's GC compaction read-barrier handler.
|
|
689
|
+
// During GC compaction, Ruby protects pages containing Ruby objects, so many of the checks we do below are unsafe
|
|
690
|
+
// since they can touch those pages, and thus we just bail out here in this situation to avoid crashes.
|
|
691
|
+
if (is_running_on_alternate_signal_stack(ucontext)) {
|
|
692
|
+
global_stats.signal_handler_skipped_sample_on_altstack++;
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
582
695
|
|
|
696
|
+
// We must first check that we landed on the correct thread and can proceed.
|
|
697
|
+
// We must never touch the state before we confirm that we have landed on the thread that is holding the GVL on the main
|
|
698
|
+
// ractor as otherwise we may be concurrent with the profiler shutting down and removing its state.
|
|
583
699
|
if (
|
|
584
700
|
!ruby_native_thread_p() || // Not a Ruby thread
|
|
585
701
|
!is_current_thread_holding_the_gvl() || // Not safe to enqueue a sample from this thread
|
|
586
702
|
!ddtrace_rb_ractor_main_p() // We're not on the main Ractor; we currently don't support profiling non-main Ractors
|
|
587
|
-
)
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}
|
|
703
|
+
) return;
|
|
704
|
+
|
|
705
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
591
706
|
|
|
592
|
-
//
|
|
593
|
-
|
|
707
|
+
// This can potentially happen if the CpuAndWallTimeWorker was stopped while the signal delivery was happening; nothing to do
|
|
708
|
+
if (state == NULL) return;
|
|
709
|
+
|
|
710
|
+
// We assume there can be no concurrent nor nested calls to handle_sampling_signal because
|
|
711
|
+
// a) we get triggered using SIGPROF, and the docs state a second SIGPROF will not interrupt an existing one (see sigaction docs on sa_mask)
|
|
594
712
|
// b) we validate we are in the thread that has the global VM lock; if a different thread gets a signal, it will return early
|
|
595
713
|
// because it will not have the global VM lock
|
|
596
714
|
|
|
597
|
-
// Note: rb_postponed_job_register_one ensures that if there's a previous sample_from_postponed_job queued for execution
|
|
598
|
-
// then we will not queue a second one. It does this by doing a linear scan on the existing jobs; in the future we
|
|
599
|
-
// may want to implement that check ourselves.
|
|
600
|
-
|
|
601
715
|
state->stats.signal_handler_enqueued_sample++;
|
|
602
716
|
|
|
603
|
-
|
|
604
|
-
|
|
717
|
+
bool sample_from_signal_handler =
|
|
718
|
+
state->sighandler_sampling_enabled &&
|
|
719
|
+
// Don't sample if we're already in the middle of processing a sample
|
|
720
|
+
!state->during_sample;
|
|
721
|
+
|
|
722
|
+
if (sample_from_signal_handler) {
|
|
723
|
+
// Buffer current stack trace. Note that this will not actually record the sample, for that we still need to wait
|
|
724
|
+
// until the postponed job below gets run.
|
|
725
|
+
bool prepared = thread_context_collector_prepare_sample_inside_signal_handler();
|
|
726
|
+
|
|
727
|
+
if (prepared) state->stats.signal_handler_prepared_sample++;
|
|
728
|
+
}
|
|
729
|
+
|
|
605
730
|
#ifndef NO_POSTPONED_TRIGGER // Ruby 3.3+
|
|
606
731
|
rb_postponed_job_trigger(sample_from_postponed_job_handle);
|
|
607
|
-
state->stats.postponed_job_success++; // Always succeeds
|
|
608
732
|
#else
|
|
609
|
-
|
|
610
|
-
// This is a workaround for https://bugs.ruby-lang.org/issues/19991 (for Ruby < 3.3)
|
|
733
|
+
// Passing in `gc_finalize_deferred_workaround` is a workaround for https://bugs.ruby-lang.org/issues/19991 (for Ruby < 3.3)
|
|
611
734
|
//
|
|
612
735
|
// TL;DR the `rb_postponed_job_register_one` API is not atomic (which is why it got replaced by `rb_postponed_job_trigger`)
|
|
613
736
|
// and in rare cases can cause VM crashes.
|
|
@@ -631,28 +754,15 @@ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED si
|
|
|
631
754
|
//
|
|
632
755
|
// Thus, our workaround is simple: we pass in objspace as our argument, just in case the clobbering happens.
|
|
633
756
|
// In the happy path, we never use this argument so it makes no difference. In the buggy path, we avoid crashing the VM.
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
// Officially, the result of rb_postponed_job_register_one is documented as being opaque, but in practice it does not
|
|
637
|
-
// seem to have changed between Ruby 2.3 and 3.2, and so we track it as a debugging mechanism
|
|
638
|
-
switch (result) {
|
|
639
|
-
case 0:
|
|
640
|
-
state->stats.postponed_job_full++; break;
|
|
641
|
-
case 1:
|
|
642
|
-
state->stats.postponed_job_success++; break;
|
|
643
|
-
case 2:
|
|
644
|
-
state->stats.postponed_job_skipped_already_existed++; break;
|
|
645
|
-
default:
|
|
646
|
-
state->stats.postponed_job_unknown_result++;
|
|
647
|
-
}
|
|
757
|
+
rb_postponed_job_register(0, sample_from_postponed_job, gc_finalize_deferred_workaround /* instead of NULL */);
|
|
648
758
|
#endif
|
|
649
759
|
}
|
|
650
760
|
|
|
651
761
|
// The actual sampling trigger loop always runs **without** the global vm lock.
|
|
652
762
|
static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
653
|
-
|
|
763
|
+
cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
|
|
654
764
|
|
|
655
|
-
uint64_t minimum_time_between_signals = MILLIS_AS_NS(
|
|
765
|
+
uint64_t minimum_time_between_signals = MILLIS_AS_NS(state->cpu_sampling_interval_ms);
|
|
656
766
|
|
|
657
767
|
while (atomic_load(&state->should_run)) {
|
|
658
768
|
state->stats.trigger_sample_attempts++;
|
|
@@ -674,6 +784,10 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
|
674
784
|
// we're doing this, so we may still not signal the correct thread from time to time, but our signal handler
|
|
675
785
|
// includes a check to see if it got called in the right thread
|
|
676
786
|
state->stats.interrupt_thread_attempts++;
|
|
787
|
+
|
|
788
|
+
// Pick up any last-minute attempts to stop before we send the signal
|
|
789
|
+
if (!atomic_load(&state->should_run)) return NULL;
|
|
790
|
+
|
|
677
791
|
pthread_kill(owner.owner, SIGPROF);
|
|
678
792
|
} else {
|
|
679
793
|
if (state->skip_idle_samples_for_testing) {
|
|
@@ -701,7 +815,10 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
|
701
815
|
// `dynamic_sampling_rate_get_sleep` may have changed while the above sleep was ongoing.
|
|
702
816
|
uint64_t extra_sleep =
|
|
703
817
|
dynamic_sampling_rate_get_sleep(&state->cpu_dynamic_sampling_rate, monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE));
|
|
704
|
-
if (state->dynamic_sampling_rate_enabled && extra_sleep > 0)
|
|
818
|
+
if (state->dynamic_sampling_rate_enabled && extra_sleep > 0) {
|
|
819
|
+
state->stats.trigger_sample_extra_sleep++;
|
|
820
|
+
sleep_for(extra_sleep);
|
|
821
|
+
}
|
|
705
822
|
}
|
|
706
823
|
|
|
707
824
|
return NULL; // Unused
|
|
@@ -709,13 +826,15 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
|
709
826
|
|
|
710
827
|
// This is called by the Ruby VM when it wants to shut down the background thread
|
|
711
828
|
static void interrupt_sampling_trigger_loop(void *state_ptr) {
|
|
712
|
-
|
|
829
|
+
cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
|
|
713
830
|
|
|
714
831
|
atomic_store(&state->should_run, false);
|
|
715
832
|
}
|
|
716
833
|
|
|
834
|
+
// Note: If we ever want to get rid of the postponed job execution, remember not to clobber Ruby exceptions, as
|
|
835
|
+
// this function does this helpful job for us now -- https://github.com/ruby/ruby/commit/a98e343d39c4d7bf1e2190b076720f32d9f298b3.
|
|
717
836
|
static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
718
|
-
|
|
837
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
719
838
|
|
|
720
839
|
// This can potentially happen if the CpuAndWallTimeWorker was stopped while the postponed job was waiting to be executed; nothing to do
|
|
721
840
|
if (state == NULL) return;
|
|
@@ -726,17 +845,22 @@ static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
|
726
845
|
return; // We're not on the main Ractor; we currently don't support profiling non-main Ractors
|
|
727
846
|
}
|
|
728
847
|
|
|
729
|
-
state
|
|
848
|
+
during_sample_enter(state);
|
|
730
849
|
|
|
731
850
|
// Rescue against any exceptions that happen during sampling
|
|
732
|
-
safely_call(
|
|
851
|
+
safely_call(
|
|
852
|
+
rescued_sample_from_postponed_job,
|
|
853
|
+
state->self_instance,
|
|
854
|
+
state->self_instance,
|
|
855
|
+
handle_sampling_failure_rescued_sample_from_postponed_job
|
|
856
|
+
);
|
|
733
857
|
|
|
734
|
-
state
|
|
858
|
+
during_sample_exit(state);
|
|
735
859
|
}
|
|
736
860
|
|
|
737
861
|
static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
|
|
738
|
-
|
|
739
|
-
TypedData_Get_Struct(self_instance,
|
|
862
|
+
cpu_and_wall_time_worker_state *state;
|
|
863
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
740
864
|
|
|
741
865
|
long wall_time_ns_before_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
742
866
|
|
|
@@ -747,8 +871,7 @@ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
|
|
|
747
871
|
|
|
748
872
|
state->stats.cpu_sampled++;
|
|
749
873
|
|
|
750
|
-
|
|
751
|
-
thread_context_collector_sample(state->thread_context_collector_instance, wall_time_ns_before_sample, profiler_overhead_stack_thread);
|
|
874
|
+
thread_context_collector_sample(state->thread_context_collector_instance, wall_time_ns_before_sample);
|
|
752
875
|
|
|
753
876
|
long wall_time_ns_after_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
754
877
|
long delta_ns = wall_time_ns_after_sample - wall_time_ns_before_sample;
|
|
@@ -766,11 +889,6 @@ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
|
|
|
766
889
|
return Qnil;
|
|
767
890
|
}
|
|
768
891
|
|
|
769
|
-
static VALUE handle_sampling_failure(VALUE self_instance, VALUE exception) {
|
|
770
|
-
stop(self_instance, exception);
|
|
771
|
-
return Qnil;
|
|
772
|
-
}
|
|
773
|
-
|
|
774
892
|
// This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
|
|
775
893
|
// It SHOULD NOT be used for other purposes.
|
|
776
894
|
static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self) {
|
|
@@ -791,8 +909,8 @@ static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self) {
|
|
|
791
909
|
}
|
|
792
910
|
|
|
793
911
|
static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
|
|
794
|
-
|
|
795
|
-
TypedData_Get_Struct(instance,
|
|
912
|
+
cpu_and_wall_time_worker_state *state;
|
|
913
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
796
914
|
|
|
797
915
|
// Final preparations: Setup signal handler and enable tracepoints. We run these here and not in `_native_sampling_loop`
|
|
798
916
|
// because they may raise exceptions.
|
|
@@ -809,22 +927,19 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
|
|
|
809
927
|
|
|
810
928
|
if (state->gvl_profiling_enabled) {
|
|
811
929
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
812
|
-
#ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS
|
|
813
|
-
gvl_profiling_state_thread_tracking_workaround();
|
|
814
|
-
#endif
|
|
815
|
-
|
|
816
930
|
state->gvl_profiling_hook = rb_internal_thread_add_event_hook(
|
|
817
931
|
on_gvl_event,
|
|
818
932
|
(
|
|
819
933
|
// For now we're only asking for these events, even though there's more
|
|
820
934
|
// (e.g. check docs or gvl-tracing gem)
|
|
935
|
+
RUBY_INTERNAL_THREAD_EVENT_SUSPENDED | /* released gvl */
|
|
821
936
|
RUBY_INTERNAL_THREAD_EVENT_READY /* waiting for gvl */ |
|
|
822
937
|
RUBY_INTERNAL_THREAD_EVENT_RESUMED /* running/runnable */
|
|
823
938
|
),
|
|
824
939
|
NULL
|
|
825
940
|
);
|
|
826
941
|
#else
|
|
827
|
-
|
|
942
|
+
raise_error(rb_eArgError, "GVL profiling is not supported in this Ruby version");
|
|
828
943
|
#endif
|
|
829
944
|
}
|
|
830
945
|
|
|
@@ -836,17 +951,28 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
|
|
|
836
951
|
// If we stopped sampling due to an exception, re-raise it (now in the worker thread)
|
|
837
952
|
if (state->failure_exception != Qnil) rb_exc_raise(state->failure_exception);
|
|
838
953
|
|
|
954
|
+
thread_context_collector_profiler_internal_thread_done(state->thread_context_collector_instance);
|
|
955
|
+
|
|
839
956
|
return Qnil;
|
|
840
957
|
}
|
|
841
958
|
|
|
842
959
|
// This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
|
|
843
960
|
// It SHOULD NOT be used for other purposes.
|
|
844
961
|
static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
845
|
-
|
|
962
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
846
963
|
|
|
847
964
|
return (state != NULL && is_thread_alive(state->owner_thread) && state->self_instance == instance) ? Qtrue : Qfalse;
|
|
848
965
|
}
|
|
849
966
|
|
|
967
|
+
static VALUE _native_failure_exception_during_operation(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
968
|
+
cpu_and_wall_time_worker_state *state;
|
|
969
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
970
|
+
|
|
971
|
+
if (state->failure_exception_during_operation == NULL) return Qnil;
|
|
972
|
+
|
|
973
|
+
return rb_str_new_cstr(state->failure_exception_during_operation);
|
|
974
|
+
}
|
|
975
|
+
|
|
850
976
|
static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext) {
|
|
851
977
|
/* Does nothing on purpose */
|
|
852
978
|
}
|
|
@@ -865,6 +991,28 @@ static VALUE _native_remove_testing_signal_handler(DDTRACE_UNUSED VALUE self) {
|
|
|
865
991
|
return Qtrue;
|
|
866
992
|
}
|
|
867
993
|
|
|
994
|
+
// Reproduces a profiler sample being taken in the middle of GC compaction: during compaction a SIGPROF can be
|
|
995
|
+
// delivered while nested inside Ruby's read-barrier signal handler, which runs on the alternate signal stack. We
|
|
996
|
+
// simulate that here by re-installing the production `handle_sampling_signal` with the `SA_ONSTACK` flag, so it runs
|
|
997
|
+
// on the alternate signal stack just like it would in that scenario.
|
|
998
|
+
//
|
|
999
|
+
// The `SA_ONSTACK` will be undone by any next `replace_sigprof_signal_handler_with_empty_handler` /
|
|
1000
|
+
// `install_sigprof_signal_handler_internal` / `remove_sigprof_signal_handle` so this doesn't need a specific, symmetric
|
|
1001
|
+
// `uninstall` to undo this.
|
|
1002
|
+
static VALUE _native_install_sigprof_handler_on_altstack(DDTRACE_UNUSED VALUE self) {
|
|
1003
|
+
struct sigaction signal_handler_config = {
|
|
1004
|
+
.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK,
|
|
1005
|
+
.sa_sigaction = handle_sampling_signal,
|
|
1006
|
+
};
|
|
1007
|
+
sigemptyset(&signal_handler_config.sa_mask);
|
|
1008
|
+
|
|
1009
|
+
if (sigaction(SIGPROF, &signal_handler_config, NULL) != 0) {
|
|
1010
|
+
rb_sys_fail("Failed to install SA_ONSTACK SIGPROF signal handler for testing");
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
return Qtrue;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
868
1016
|
// This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
|
|
869
1017
|
// It SHOULD NOT be used for other purposes.
|
|
870
1018
|
static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self) {
|
|
@@ -875,8 +1023,8 @@ static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self) {
|
|
|
875
1023
|
// This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
|
|
876
1024
|
// It SHOULD NOT be used for other purposes.
|
|
877
1025
|
static VALUE _native_gc_tracepoint(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
878
|
-
|
|
879
|
-
TypedData_Get_Struct(instance,
|
|
1026
|
+
cpu_and_wall_time_worker_state *state;
|
|
1027
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
880
1028
|
|
|
881
1029
|
return state->gc_tracepoint;
|
|
882
1030
|
}
|
|
@@ -902,7 +1050,7 @@ static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused) {
|
|
|
902
1050
|
int event = rb_tracearg_event_flag(rb_tracearg_from_tracepoint(tracepoint_data));
|
|
903
1051
|
if (event != RUBY_INTERNAL_EVENT_GC_ENTER && event != RUBY_INTERNAL_EVENT_GC_EXIT) return; // Unknown event
|
|
904
1052
|
|
|
905
|
-
|
|
1053
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
906
1054
|
|
|
907
1055
|
// This should not happen in a normal situation because the tracepoint is always enabled after the instance is set
|
|
908
1056
|
// and disabled before it is cleared, but just in case...
|
|
@@ -926,7 +1074,7 @@ static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused) {
|
|
|
926
1074
|
}
|
|
927
1075
|
|
|
928
1076
|
static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
929
|
-
|
|
1077
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
930
1078
|
|
|
931
1079
|
// This can potentially happen if the CpuAndWallTimeWorker was stopped while the postponed job was waiting to be executed; nothing to do
|
|
932
1080
|
if (state == NULL) return;
|
|
@@ -937,16 +1085,26 @@ static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
|
937
1085
|
return; // We're not on the main Ractor; we currently don't support profiling non-main Ractors
|
|
938
1086
|
}
|
|
939
1087
|
|
|
940
|
-
state
|
|
1088
|
+
during_sample_enter(state);
|
|
941
1089
|
|
|
942
|
-
safely_call(
|
|
1090
|
+
safely_call(
|
|
1091
|
+
thread_context_collector_sample_after_gc,
|
|
1092
|
+
state->thread_context_collector_instance,
|
|
1093
|
+
state->self_instance,
|
|
1094
|
+
handle_sampling_failure_thread_context_collector_sample_after_gc
|
|
1095
|
+
);
|
|
943
1096
|
|
|
944
|
-
state
|
|
1097
|
+
during_sample_exit(state);
|
|
945
1098
|
}
|
|
946
1099
|
|
|
947
1100
|
// Equivalent to Ruby begin/rescue call, where we call a C function and jump to the exception handler if an
|
|
948
1101
|
// exception gets raised within
|
|
949
|
-
static VALUE safely_call(
|
|
1102
|
+
static VALUE safely_call(
|
|
1103
|
+
VALUE (*function_to_call_safely)(VALUE),
|
|
1104
|
+
VALUE function_to_call_safely_arg,
|
|
1105
|
+
VALUE instance,
|
|
1106
|
+
VALUE (*handle_sampling_failure)(VALUE, VALUE)
|
|
1107
|
+
) {
|
|
950
1108
|
VALUE exception_handler_function_arg = instance;
|
|
951
1109
|
return rb_rescue2(
|
|
952
1110
|
function_to_call_safely,
|
|
@@ -980,9 +1138,12 @@ static VALUE _native_simulate_sample_from_postponed_job(DDTRACE_UNUSED VALUE sel
|
|
|
980
1138
|
//
|
|
981
1139
|
// In the future, if we add more other components with tracepoints, we will need to coordinate stopping all such
|
|
982
1140
|
// tracepoints before doing the other cleaning steps.
|
|
1141
|
+
//
|
|
1142
|
+
// Note that tests call this method directly in the same process without forking,
|
|
1143
|
+
// and in such a case non-current Threads keep running.
|
|
983
1144
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
984
|
-
|
|
985
|
-
TypedData_Get_Struct(instance,
|
|
1145
|
+
cpu_and_wall_time_worker_state *state;
|
|
1146
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
986
1147
|
|
|
987
1148
|
// Disable all tracepoints, so that there are no more attempts to mutate the profile
|
|
988
1149
|
disable_tracepoints(state);
|
|
@@ -1000,8 +1161,8 @@ static VALUE _native_is_sigprof_blocked_in_current_thread(DDTRACE_UNUSED VALUE s
|
|
|
1000
1161
|
}
|
|
1001
1162
|
|
|
1002
1163
|
static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
1003
|
-
|
|
1004
|
-
TypedData_Get_Struct(instance,
|
|
1164
|
+
cpu_and_wall_time_worker_state *state;
|
|
1165
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1005
1166
|
|
|
1006
1167
|
unsigned long total_cpu_samples_attempted = state->stats.cpu_sampled + state->stats.cpu_skipped;
|
|
1007
1168
|
VALUE effective_cpu_sample_rate =
|
|
@@ -1016,14 +1177,12 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
|
1016
1177
|
VALUE stats_as_hash = rb_hash_new();
|
|
1017
1178
|
VALUE arguments[] = {
|
|
1018
1179
|
ID2SYM(rb_intern("trigger_sample_attempts")), /* => */ UINT2NUM(state->stats.trigger_sample_attempts),
|
|
1180
|
+
ID2SYM(rb_intern("trigger_sample_extra_sleep")), /* => */ UINT2NUM(state->stats.trigger_sample_extra_sleep),
|
|
1019
1181
|
ID2SYM(rb_intern("trigger_simulated_signal_delivery_attempts")), /* => */ UINT2NUM(state->stats.trigger_simulated_signal_delivery_attempts),
|
|
1020
1182
|
ID2SYM(rb_intern("simulated_signal_delivery")), /* => */ UINT2NUM(state->stats.simulated_signal_delivery),
|
|
1021
1183
|
ID2SYM(rb_intern("signal_handler_enqueued_sample")), /* => */ UINT2NUM(state->stats.signal_handler_enqueued_sample),
|
|
1022
|
-
ID2SYM(rb_intern("
|
|
1023
|
-
ID2SYM(rb_intern("
|
|
1024
|
-
ID2SYM(rb_intern("postponed_job_success")), /* => */ UINT2NUM(state->stats.postponed_job_success),
|
|
1025
|
-
ID2SYM(rb_intern("postponed_job_full")), /* => */ UINT2NUM(state->stats.postponed_job_full),
|
|
1026
|
-
ID2SYM(rb_intern("postponed_job_unknown_result")), /* => */ UINT2NUM(state->stats.postponed_job_unknown_result),
|
|
1184
|
+
ID2SYM(rb_intern("signal_handler_prepared_sample")), /* => */ UINT2NUM(state->stats.signal_handler_prepared_sample),
|
|
1185
|
+
ID2SYM(rb_intern("signal_handler_skipped_sample_on_altstack")), /* => */ UINT2NUM(global_stats.signal_handler_skipped_sample_on_altstack),
|
|
1027
1186
|
ID2SYM(rb_intern("interrupt_thread_attempts")), /* => */ UINT2NUM(state->stats.interrupt_thread_attempts),
|
|
1028
1187
|
|
|
1029
1188
|
// CPU Stats
|
|
@@ -1053,28 +1212,32 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
|
1053
1212
|
ID2SYM(rb_intern("gvl_sampling_time_ns_max")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_max, > 0, ULL2NUM),
|
|
1054
1213
|
ID2SYM(rb_intern("gvl_sampling_time_ns_total")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_total, > 0, ULL2NUM),
|
|
1055
1214
|
ID2SYM(rb_intern("gvl_sampling_time_ns_avg")), /* => */ RUBY_AVG_OR_NIL(state->stats.gvl_sampling_time_ns_total, state->stats.after_gvl_running),
|
|
1215
|
+
ID2SYM(rb_intern("gvl_waiting_time_ns_total")), /* => */ state->gvl_profiling_enabled ? ULL2NUM(state->stats.vm_metrics.gvl_waiting_time_ns_total) : Qnil,
|
|
1056
1216
|
};
|
|
1057
1217
|
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(stats_as_hash, arguments[i], arguments[i+1]);
|
|
1218
|
+
|
|
1219
|
+
thread_context_collector_stats(state->thread_context_collector_instance, stats_as_hash);
|
|
1220
|
+
|
|
1058
1221
|
return stats_as_hash;
|
|
1059
1222
|
}
|
|
1060
1223
|
|
|
1061
1224
|
static VALUE _native_stats_reset_not_thread_safe(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
1062
|
-
|
|
1063
|
-
TypedData_Get_Struct(instance,
|
|
1225
|
+
cpu_and_wall_time_worker_state *state;
|
|
1226
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1064
1227
|
reset_stats_not_thread_safe(state);
|
|
1228
|
+
thread_context_collector_stats_reset_not_thread_safe(state->thread_context_collector_instance);
|
|
1065
1229
|
return Qnil;
|
|
1066
1230
|
}
|
|
1067
1231
|
|
|
1068
1232
|
void *simulate_sampling_signal_delivery(DDTRACE_UNUSED void *_unused) {
|
|
1069
|
-
|
|
1233
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1070
1234
|
|
|
1071
1235
|
// This can potentially happen if the CpuAndWallTimeWorker was stopped while the IdleSamplingHelper was trying to execute this action
|
|
1072
1236
|
if (state == NULL) return NULL;
|
|
1073
1237
|
|
|
1074
1238
|
state->stats.simulated_signal_delivery++;
|
|
1075
1239
|
|
|
1076
|
-
//
|
|
1077
|
-
// so that the simulated case is as close to the original one as well (including any metrics increases, etc).
|
|
1240
|
+
// `handle_sampling_signal` does a few things extra on top of `sample_from_postponed_job` so that's why we don't shortcut here
|
|
1078
1241
|
handle_sampling_signal(0, NULL, NULL);
|
|
1079
1242
|
|
|
1080
1243
|
return NULL; // Unused
|
|
@@ -1082,19 +1245,22 @@ void *simulate_sampling_signal_delivery(DDTRACE_UNUSED void *_unused) {
|
|
|
1082
1245
|
|
|
1083
1246
|
static void grab_gvl_and_sample(void) { rb_thread_call_with_gvl(simulate_sampling_signal_delivery, NULL); }
|
|
1084
1247
|
|
|
1085
|
-
static void reset_stats_not_thread_safe(
|
|
1248
|
+
static void reset_stats_not_thread_safe(cpu_and_wall_time_worker_state *state) {
|
|
1086
1249
|
// NOTE: This is not really thread safe so ongoing sampling operations that are concurrent with a reset can have their stats:
|
|
1087
1250
|
// * Lost (writes after stats retrieval but before reset).
|
|
1088
1251
|
// * Included in the previous stats window (writes before stats retrieval and reset).
|
|
1089
1252
|
// * Included in the following stats window (writes after stats retrieval and reset).
|
|
1090
1253
|
// Given the expected infrequency of resetting (~once per 60s profile) and the auxiliary/non-critical nature of these stats
|
|
1091
1254
|
// this momentary loss of accuracy is deemed acceptable to keep overhead to a minimum.
|
|
1255
|
+
|
|
1092
1256
|
state->stats = (struct stats) {
|
|
1093
1257
|
// All these values are initialized to their highest value possible since we always take the min between existing and latest sample
|
|
1094
1258
|
.cpu_sampling_time_ns_min = UINT64_MAX,
|
|
1095
1259
|
.allocation_sampling_time_ns_min = UINT64_MAX,
|
|
1096
1260
|
.gvl_sampling_time_ns_min = UINT64_MAX,
|
|
1261
|
+
// Other fields are reset to 0
|
|
1097
1262
|
};
|
|
1263
|
+
global_stats = (global_stats_t) {0};
|
|
1098
1264
|
}
|
|
1099
1265
|
|
|
1100
1266
|
static void sleep_for(uint64_t time_ns) {
|
|
@@ -1116,7 +1282,7 @@ static void sleep_for(uint64_t time_ns) {
|
|
|
1116
1282
|
}
|
|
1117
1283
|
|
|
1118
1284
|
static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
|
|
1119
|
-
|
|
1285
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state;
|
|
1120
1286
|
|
|
1121
1287
|
bool are_allocations_being_tracked = state != NULL && state->allocation_profiling_enabled && state->allocation_counting_enabled;
|
|
1122
1288
|
|
|
@@ -1126,7 +1292,7 @@ static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
|
|
|
1126
1292
|
#define HANDLE_CLOCK_FAILURE(call) ({ \
|
|
1127
1293
|
long _result = (call); \
|
|
1128
1294
|
if (_result == 0) { \
|
|
1129
|
-
|
|
1295
|
+
delayed_error_clock_failure(state); \
|
|
1130
1296
|
return; \
|
|
1131
1297
|
} \
|
|
1132
1298
|
_result; \
|
|
@@ -1149,7 +1315,7 @@ static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
|
|
|
1149
1315
|
// call `rb_tracearg_from_tracepoint(anything)` anywhere during this function or its callees to get the data, so that's
|
|
1150
1316
|
// why it's not being passed as an argument.
|
|
1151
1317
|
static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *unused2) {
|
|
1152
|
-
|
|
1318
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1153
1319
|
|
|
1154
1320
|
// This should not happen in a normal situation because the tracepoint is always enabled after the instance is set
|
|
1155
1321
|
// and disabled before it is cleared, but just in case...
|
|
@@ -1171,6 +1337,25 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
|
|
|
1171
1337
|
return;
|
|
1172
1338
|
}
|
|
1173
1339
|
|
|
1340
|
+
VALUE current_thread = rb_thread_current();
|
|
1341
|
+
|
|
1342
|
+
// If Ruby is in the middle of raising an exception, we don't want to try to sample. This is because if we accidentally
|
|
1343
|
+
// trigger an exception inside the profiler code, bad things will happen (specifically, Ruby will try to kill off the
|
|
1344
|
+
// thread even though we may try to catch the exception).
|
|
1345
|
+
//
|
|
1346
|
+
// Note that "in the middle of raising an exception" means the exception itself has already been allocated.
|
|
1347
|
+
// What's getting allocated now is probably the backtrace objects (@ivoanjo or at least that's what I've observed)
|
|
1348
|
+
if (is_raised_flag_set(current_thread)) {
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
per_thread_context *thread_context = get_per_thread_context(current_thread);
|
|
1353
|
+
if (!thread_context) {
|
|
1354
|
+
// Context is created eagerly via on_thread_begin_event, so this should not normally be NULL.
|
|
1355
|
+
// We keep the guard since we can't allocate here (inside on_newobj_event).
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1174
1359
|
// Hot path: Dynamic sampling rate is usually enabled and the sampling decision is usually false
|
|
1175
1360
|
if (RB_LIKELY(state->dynamic_sampling_rate_enabled && !discrete_dynamic_sampler_should_sample(&state->allocation_sampler))) {
|
|
1176
1361
|
state->stats.allocation_skipped++;
|
|
@@ -1197,19 +1382,20 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
|
|
|
1197
1382
|
&state->allocation_sampler, HANDLE_CLOCK_FAILURE(monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE))
|
|
1198
1383
|
);
|
|
1199
1384
|
|
|
1200
|
-
|
|
1201
|
-
// invocation is still pending, (e.g. it wouldn't call `on_newobj_event` while it's already running), but I decided
|
|
1202
|
-
// to keep this here for consistency -- every call to the thread context (other than the special gc calls which are
|
|
1203
|
-
// defined as not being able to allocate) sets this.
|
|
1204
|
-
state->during_sample = true;
|
|
1385
|
+
during_sample_enter(state);
|
|
1205
1386
|
|
|
1206
1387
|
// Rescue against any exceptions that happen during sampling
|
|
1207
|
-
safely_call(
|
|
1388
|
+
safely_call(
|
|
1389
|
+
rescued_sample_allocation,
|
|
1390
|
+
(VALUE) thread_context,
|
|
1391
|
+
state->self_instance,
|
|
1392
|
+
handle_sampling_failure_rescued_sample_allocation
|
|
1393
|
+
);
|
|
1208
1394
|
|
|
1209
1395
|
if (state->dynamic_sampling_rate_enabled) {
|
|
1210
1396
|
long now = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
|
|
1211
1397
|
if (now == 0) {
|
|
1212
|
-
|
|
1398
|
+
delayed_error_clock_failure(state);
|
|
1213
1399
|
// NOTE: Not short-circuiting here to make sure cleanup happens
|
|
1214
1400
|
}
|
|
1215
1401
|
uint64_t sampling_time_ns = discrete_dynamic_sampler_after_sample(&state->allocation_sampler, now);
|
|
@@ -1222,10 +1408,10 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
|
|
|
1222
1408
|
|
|
1223
1409
|
state->stats.allocation_sampled++;
|
|
1224
1410
|
|
|
1225
|
-
state
|
|
1411
|
+
during_sample_exit(state);
|
|
1226
1412
|
}
|
|
1227
1413
|
|
|
1228
|
-
static void disable_tracepoints(
|
|
1414
|
+
static void disable_tracepoints(cpu_and_wall_time_worker_state *state) {
|
|
1229
1415
|
if (state->gc_tracepoint != Qnil) {
|
|
1230
1416
|
rb_tracepoint_disable(state->gc_tracepoint);
|
|
1231
1417
|
}
|
|
@@ -1253,8 +1439,9 @@ static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self) {
|
|
|
1253
1439
|
}
|
|
1254
1440
|
}
|
|
1255
1441
|
|
|
1256
|
-
static VALUE rescued_sample_allocation(
|
|
1257
|
-
|
|
1442
|
+
static VALUE rescued_sample_allocation(VALUE arg) {
|
|
1443
|
+
per_thread_context *thread_context = (per_thread_context*) arg;
|
|
1444
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1258
1445
|
|
|
1259
1446
|
// This should not happen in a normal situation because on_newobj_event already checked for this, but just in case...
|
|
1260
1447
|
if (state == NULL) return Qnil;
|
|
@@ -1272,27 +1459,40 @@ static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {
|
|
|
1272
1459
|
// To control bias from sampling, we clamp the maximum weight attributed to a single allocation sample. This avoids
|
|
1273
1460
|
// assigning a very large number to a sample, if for instance the dynamic sampling mechanism chose a really big interval.
|
|
1274
1461
|
unsigned int weight = allocations_since_last_sample > MAX_ALLOC_WEIGHT ? MAX_ALLOC_WEIGHT : (unsigned int) allocations_since_last_sample;
|
|
1275
|
-
thread_context_collector_sample_allocation(state->thread_context_collector_instance, weight, new_object);
|
|
1462
|
+
bool needs_after_allocation = thread_context_collector_sample_allocation(state->thread_context_collector_instance, thread_context, weight, new_object);
|
|
1276
1463
|
// ...but we still represent the skipped samples in the profile, thus the data will account for all allocations.
|
|
1277
1464
|
if (weight < allocations_since_last_sample) {
|
|
1278
1465
|
uint32_t skipped_samples = (uint32_t) uint64_min_of(allocations_since_last_sample - weight, UINT32_MAX);
|
|
1279
1466
|
thread_context_collector_sample_skipped_allocation_samples(state->thread_context_collector_instance, skipped_samples);
|
|
1280
1467
|
}
|
|
1281
1468
|
|
|
1469
|
+
if (needs_after_allocation) {
|
|
1470
|
+
#ifndef NO_POSTPONED_TRIGGER
|
|
1471
|
+
rb_postponed_job_trigger(after_allocation_from_postponed_job_handle);
|
|
1472
|
+
#else
|
|
1473
|
+
// Not needed on legacy rubies
|
|
1474
|
+
#endif
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1282
1477
|
// Return a dummy VALUE because we're called from rb_rescue2 which requires it
|
|
1283
1478
|
return Qnil;
|
|
1284
1479
|
}
|
|
1285
1480
|
|
|
1286
|
-
static void delayed_error(
|
|
1481
|
+
static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error) {
|
|
1287
1482
|
// If we can't raise an immediate exception at the calling site, use the asynchronous flow through the main worker loop.
|
|
1288
|
-
stop_state(state, rb_exc_new_cstr(rb_eRuntimeError, error));
|
|
1483
|
+
stop_state(state, rb_exc_new_cstr(rb_eRuntimeError, error), "delayed_error");
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state) {
|
|
1487
|
+
// If we can't raise an immediate exception at the calling site, use the asynchronous flow through the main worker loop.
|
|
1488
|
+
stop_state(state, rb_exc_new_cstr(clock_failure_exception_class, "failed to get clock time"), "delayed_error_clock_failure");
|
|
1289
1489
|
}
|
|
1290
1490
|
|
|
1291
1491
|
static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg) {
|
|
1292
1492
|
ENFORCE_TYPE(error_msg, T_STRING);
|
|
1293
1493
|
|
|
1294
|
-
|
|
1295
|
-
TypedData_Get_Struct(instance,
|
|
1494
|
+
cpu_and_wall_time_worker_state *state;
|
|
1495
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1296
1496
|
|
|
1297
1497
|
delayed_error(state, rb_string_value_cstr(&error_msg));
|
|
1298
1498
|
|
|
@@ -1316,64 +1516,94 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
|
|
|
1316
1516
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
1317
1517
|
static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused) {
|
|
1318
1518
|
// Be very careful about touching the `state` here or doing anything at all:
|
|
1319
|
-
// This function gets called without the GVL, and potentially from
|
|
1519
|
+
// This function gets called without the GVL, and potentially from non-main Ractors!
|
|
1320
1520
|
//
|
|
1321
|
-
//
|
|
1322
|
-
//
|
|
1323
|
-
|
|
1521
|
+
// Note, even though these events can get called without the GVL, they synchronize-with enabling/disabling of
|
|
1522
|
+
// the hook that calls us using a read-write lock. Thus, disabling the hook cannot be concurrent with calling this function,
|
|
1523
|
+
// and once the disable finishes there can't be "late" calls into this function.
|
|
1324
1524
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1525
|
+
// The thread that this event is about may not be the current thread
|
|
1526
|
+
// (as documented on rb_internal_thread_add_event_hook(), and this is notably the case for READY on Ruby 4.0),
|
|
1527
|
+
// so be careful with native thread locals that are not directly tied to the thread object and the like.
|
|
1528
|
+
|
|
1529
|
+
#ifdef HAVE_RUBY_THREAD_STORAGE_API
|
|
1530
|
+
VALUE target_thread = event_data->thread;
|
|
1531
|
+
#else
|
|
1532
|
+
// On Ruby 3.2 the event does not carry the thread, but all events fire on the thread itself.
|
|
1533
|
+
// However, during early thread startup rb_thread_current() can crash because the execution context (Fiber) isn't
|
|
1534
|
+
// stored in TLS yet; ruby_native_thread_p() guards against this.
|
|
1535
|
+
if (!ruby_native_thread_p()) return;
|
|
1536
|
+
VALUE target_thread = rb_thread_current();
|
|
1537
|
+
#endif
|
|
1538
|
+
|
|
1539
|
+
per_thread_context* thread_context = get_per_thread_context(target_thread);
|
|
1540
|
+
if (!thread_context) return;
|
|
1541
|
+
// If non-NULL the thread is profiled and from the main Ractor
|
|
1542
|
+
|
|
1543
|
+
if (event_id == RUBY_INTERNAL_THREAD_EVENT_SUSPENDED) { /* released gvl */
|
|
1544
|
+
thread_context_collector_on_gvl_released(thread_context);
|
|
1545
|
+
} else if (event_id == RUBY_INTERNAL_THREAD_EVENT_READY) { /* waiting for gvl */
|
|
1546
|
+
thread_context_collector_on_gvl_waiting(thread_context);
|
|
1327
1547
|
} else if (event_id == RUBY_INTERNAL_THREAD_EVENT_RESUMED) { /* running/runnable */
|
|
1328
|
-
// Interesting note: A RUBY_INTERNAL_THREAD_EVENT_RESUMED is guaranteed to be called with the GVL being acquired
|
|
1329
|
-
//
|
|
1548
|
+
// Interesting note: A RUBY_INTERNAL_THREAD_EVENT_RESUMED is guaranteed to be called with the GVL being acquired
|
|
1549
|
+
// and on the event thread.
|
|
1550
|
+
// However, on_gvl_event() is called while holding the scheduler lock, so we do as little work as possible here,
|
|
1551
|
+
// and perform the sample in a postponed_job.
|
|
1552
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1553
|
+
if (state == NULL) return; // This should not happen, but just in case...
|
|
1554
|
+
|
|
1555
|
+
// on_gvl_running prepares a stack sample so we need to gate it with `during_sample_enter` to avoid a
|
|
1556
|
+
// SIGPROF signal coming in during that function and potentially causing a corrupted final state.
|
|
1330
1557
|
//
|
|
1331
|
-
//
|
|
1332
|
-
//
|
|
1333
|
-
//
|
|
1558
|
+
// TODO: Currently, the sample prepared in on_gvl_running can still be clobbered if the signal handler runs
|
|
1559
|
+
// after `during_sample_exit` but before `after_gvl_running_from_postponed_job` gets to run. We'll need to fix
|
|
1560
|
+
// that next.
|
|
1561
|
+
during_sample_enter(state);
|
|
1334
1562
|
|
|
1335
|
-
|
|
1336
|
-
target_thread = gvl_profiling_state_maybe_initialize();
|
|
1337
|
-
#endif
|
|
1563
|
+
on_gvl_running_result result = thread_context_collector_on_gvl_running(state->thread_context_collector_instance, target_thread, thread_context);
|
|
1338
1564
|
|
|
1339
|
-
|
|
1565
|
+
during_sample_exit(state);
|
|
1340
1566
|
|
|
1341
|
-
if (result
|
|
1567
|
+
if (result.waiting_for_gvl_duration_ns > 0) {
|
|
1568
|
+
state->stats.vm_metrics.gvl_waiting_time_ns_total += (uint64_t) result.waiting_for_gvl_duration_ns;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
if (result.action == ON_GVL_RUNNING_SAMPLE) {
|
|
1342
1572
|
#ifndef NO_POSTPONED_TRIGGER
|
|
1343
1573
|
rb_postponed_job_trigger(after_gvl_running_from_postponed_job_handle);
|
|
1344
1574
|
#else
|
|
1345
1575
|
rb_postponed_job_register_one(0, after_gvl_running_from_postponed_job, NULL);
|
|
1346
1576
|
#endif
|
|
1347
|
-
} else if (result == ON_GVL_RUNNING_DONT_SAMPLE) {
|
|
1348
|
-
struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1349
|
-
|
|
1350
|
-
if (state == NULL) return; // This should not happen, but just in case...
|
|
1351
|
-
|
|
1577
|
+
} else if (result.action == ON_GVL_RUNNING_DONT_SAMPLE) {
|
|
1352
1578
|
state->stats.gvl_dont_sample++;
|
|
1353
1579
|
}
|
|
1354
1580
|
} else {
|
|
1355
|
-
|
|
1356
|
-
fprintf(stderr, "[ddtrace] Unexpected value in on_gvl_event (%d)\n", event_id);
|
|
1581
|
+
rb_bug("[ddtrace] Unexpected value in on_gvl_event (%d)\n", event_id);
|
|
1357
1582
|
}
|
|
1358
1583
|
}
|
|
1359
1584
|
|
|
1360
1585
|
static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
1361
|
-
|
|
1586
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1362
1587
|
|
|
1363
1588
|
// This can potentially happen if the CpuAndWallTimeWorker was stopped while the postponed job was waiting to be executed; nothing to do
|
|
1364
1589
|
if (state == NULL) return;
|
|
1365
1590
|
|
|
1366
|
-
state
|
|
1591
|
+
during_sample_enter(state);
|
|
1367
1592
|
|
|
1368
1593
|
// Rescue against any exceptions that happen during sampling
|
|
1369
|
-
safely_call(
|
|
1594
|
+
safely_call(
|
|
1595
|
+
rescued_after_gvl_running_from_postponed_job,
|
|
1596
|
+
state->self_instance,
|
|
1597
|
+
state->self_instance,
|
|
1598
|
+
handle_sampling_failure_rescued_after_gvl_running_from_postponed_job
|
|
1599
|
+
);
|
|
1370
1600
|
|
|
1371
|
-
state
|
|
1601
|
+
during_sample_exit(state);
|
|
1372
1602
|
}
|
|
1373
1603
|
|
|
1374
1604
|
static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance) {
|
|
1375
|
-
|
|
1376
|
-
TypedData_Get_Struct(self_instance,
|
|
1605
|
+
cpu_and_wall_time_worker_state *state;
|
|
1606
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1377
1607
|
|
|
1378
1608
|
long wall_time_ns_before_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
1379
1609
|
thread_context_collector_sample_after_gvl_running(state->thread_context_collector_instance, rb_thread_current(), wall_time_ns_before_sample);
|
|
@@ -1394,13 +1624,95 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
|
|
|
1394
1624
|
}
|
|
1395
1625
|
|
|
1396
1626
|
static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
1397
|
-
|
|
1398
|
-
TypedData_Get_Struct(instance,
|
|
1627
|
+
cpu_and_wall_time_worker_state *state;
|
|
1628
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1399
1629
|
|
|
1400
1630
|
return state->gvl_profiling_hook != NULL ? Qtrue : Qfalse;
|
|
1401
1631
|
}
|
|
1632
|
+
|
|
1633
|
+
static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception) {
|
|
1634
|
+
stop(self_instance, exception, "rescued_after_gvl_running_from_postponed_job");
|
|
1635
|
+
return Qnil;
|
|
1636
|
+
}
|
|
1402
1637
|
#else
|
|
1403
1638
|
static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, DDTRACE_UNUSED VALUE instance) {
|
|
1404
1639
|
return Qfalse;
|
|
1405
1640
|
}
|
|
1406
1641
|
#endif
|
|
1642
|
+
|
|
1643
|
+
static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception) {
|
|
1644
|
+
stop(self_instance, exception, "rescued_sample_from_postponed_job");
|
|
1645
|
+
return Qnil;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception) {
|
|
1649
|
+
stop(self_instance, exception, "thread_context_collector_sample_after_gc");
|
|
1650
|
+
return Qnil;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception) {
|
|
1654
|
+
stop(self_instance, exception, "rescued_sample_allocation");
|
|
1655
|
+
return Qnil;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
static VALUE handle_sampling_failure_rescued_after_allocation(VALUE self_instance, VALUE exception) {
|
|
1659
|
+
stop(self_instance, exception, "rescued_after_allocation");
|
|
1660
|
+
return Qnil;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
static VALUE rescued_after_allocation(VALUE self_instance) {
|
|
1664
|
+
cpu_and_wall_time_worker_state *state;
|
|
1665
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1666
|
+
|
|
1667
|
+
thread_context_collector_after_allocation(state->thread_context_collector_instance);
|
|
1668
|
+
|
|
1669
|
+
// Return a dummy VALUE because we're called from rb_rescue2 which requires it
|
|
1670
|
+
return Qnil;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
// This postponed job callback is used to finalize heap allocation recordings on Ruby 4+.
|
|
1674
|
+
// During on_newobj_event, calling rb_obj_id() is unsafe because it mutates the object.
|
|
1675
|
+
// So we defer getting the object_id until after the event completes.
|
|
1676
|
+
#pragma GCC diagnostic push
|
|
1677
|
+
#pragma GCC diagnostic ignored "-Wunused-function" // This is only used for some Rubies, but we want to build on all to make it easier to dev
|
|
1678
|
+
static void after_allocation_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
1679
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state;
|
|
1680
|
+
|
|
1681
|
+
if (state == NULL || !ddtrace_rb_ractor_main_p()) return;
|
|
1682
|
+
|
|
1683
|
+
// Protect against nested operations
|
|
1684
|
+
if (state->during_sample) return;
|
|
1685
|
+
|
|
1686
|
+
during_sample_enter(state);
|
|
1687
|
+
|
|
1688
|
+
// NOTE: We're not updating the allocation_sampler here.
|
|
1689
|
+
// This means work done in this function isn't accounted for as profiler overhead.
|
|
1690
|
+
// This is acceptable as the amount of work done here is expected to be small.
|
|
1691
|
+
safely_call(
|
|
1692
|
+
rescued_after_allocation,
|
|
1693
|
+
state->self_instance,
|
|
1694
|
+
state->self_instance,
|
|
1695
|
+
handle_sampling_failure_rescued_after_allocation
|
|
1696
|
+
);
|
|
1697
|
+
|
|
1698
|
+
during_sample_exit(state);
|
|
1699
|
+
}
|
|
1700
|
+
#pragma GCC diagnostic pop
|
|
1701
|
+
|
|
1702
|
+
static inline void during_sample_enter(cpu_and_wall_time_worker_state* state) {
|
|
1703
|
+
// Tell the compiler it's not allowed to reorder the `during_sample` flag with anything that happens after.
|
|
1704
|
+
//
|
|
1705
|
+
// In a few cases, we may be checking this flag from a signal handler, so we need to make sure the compiler didn't
|
|
1706
|
+
// get clever and reordered things in such a way that makes us miss the flag update.
|
|
1707
|
+
//
|
|
1708
|
+
// See https://github.com/ruby/ruby/pull/11036 for a similar change made to the Ruby VM with more context.
|
|
1709
|
+
state->during_sample = true;
|
|
1710
|
+
atomic_signal_fence(memory_order_seq_cst);
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
static inline void during_sample_exit(cpu_and_wall_time_worker_state* state) {
|
|
1714
|
+
// See `during_sample_enter` for more context; in this case we set the fence before to make sure anything that
|
|
1715
|
+
// happens before the fence is not reordered with the flag update.
|
|
1716
|
+
atomic_signal_fence(memory_order_seq_cst);
|
|
1717
|
+
state->during_sample = false;
|
|
1718
|
+
}
|