datadog 2.4.0 → 2.44.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 +1261 -1
- data/README.md +3 -1
- data/bin/ddprofrb +4 -4
- data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
- data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +12 -24
- 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 +653 -212
- 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 +33 -20
- data/ext/datadog_profiling_native_extension/collectors_stack.c +409 -113
- data/ext/datadog_profiling_native_extension/collectors_stack.h +41 -5
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +1208 -623
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +33 -8
- data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +240 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +85 -16
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +47 -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 +99 -120
- 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 +762 -608
- data/ext/datadog_profiling_native_extension/heap_recorder.h +67 -28
- 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 +3 -40
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +796 -417
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +48 -12
- data/ext/datadog_profiling_native_extension/profiling.c +93 -27
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +77 -137
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +21 -20
- 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 +458 -302
- data/ext/datadog_profiling_native_extension/stack_recorder.h +14 -3
- data/ext/datadog_profiling_native_extension/time_helpers.h +18 -11
- 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 +33 -30
- 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 +85 -16
- data/ext/libdatadog_api/datadog_ruby_common.h +47 -1
- data/ext/libdatadog_api/ddsketch.c +102 -0
- data/ext/libdatadog_api/di.c +218 -0
- data/ext/libdatadog_api/extconf.rb +44 -45
- data/ext/libdatadog_api/feature_flags.c +588 -0
- data/ext/libdatadog_api/feature_flags.h +5 -0
- data/ext/libdatadog_api/helpers.h +27 -0
- data/ext/libdatadog_api/init.c +35 -0
- data/ext/libdatadog_api/library_config.c +173 -0
- data/ext/libdatadog_api/library_config.h +25 -0
- data/ext/libdatadog_api/otel_thread_context.c +268 -0
- data/ext/libdatadog_api/otel_thread_context.h +5 -0
- data/ext/libdatadog_api/process_discovery.c +118 -0
- data/ext/libdatadog_api/process_discovery.h +5 -0
- data/ext/libdatadog_api/trace_exporter.c +1932 -0
- data/ext/libdatadog_api/trace_exporter.h +5 -0
- data/ext/libdatadog_extconf_helpers.rb +116 -18
- data/lib/datadog/ai_guard/autoload.rb +10 -0
- data/lib/datadog/ai_guard/component.rb +53 -0
- data/lib/datadog/ai_guard/configuration/ext.rb +19 -0
- data/lib/datadog/ai_guard/configuration.rb +128 -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 +73 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +41 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/message_adapter.rb +76 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +31 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/redaction_change_set.rb +135 -0
- data/lib/datadog/ai_guard/evaluation/client.rb +50 -0
- data/lib/datadog/ai_guard/evaluation/content_part.rb +48 -0
- data/lib/datadog/ai_guard/evaluation/message.rb +43 -0
- data/lib/datadog/ai_guard/evaluation/message_builder.rb +31 -0
- data/lib/datadog/ai_guard/evaluation/no_op_result.rb +36 -0
- data/lib/datadog/ai_guard/evaluation/outcome.rb +24 -0
- data/lib/datadog/ai_guard/evaluation/request.rb +30 -0
- data/lib/datadog/ai_guard/evaluation/response.rb +33 -0
- data/lib/datadog/ai_guard/evaluation/result.rb +38 -0
- data/lib/datadog/ai_guard/evaluation/tool_call.rb +31 -0
- data/lib/datadog/ai_guard/evaluation.rb +130 -0
- data/lib/datadog/ai_guard/ext.rb +32 -0
- data/lib/datadog/ai_guard/http_client.rb +86 -0
- data/lib/datadog/ai_guard/metrics/telemetry.rb +43 -0
- data/lib/datadog/ai_guard/redaction/replacements.rb +97 -0
- data/lib/datadog/ai_guard/redaction/result.rb +29 -0
- data/lib/datadog/ai_guard/redaction.rb +80 -0
- data/lib/datadog/ai_guard.rb +182 -0
- data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +68 -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 +1 -99
- 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 -75
- data/lib/datadog/appsec/compressed_json.rb +40 -0
- data/lib/datadog/appsec/configuration.rb +436 -1
- data/lib/datadog/appsec/context.rb +135 -0
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +94 -0
- data/lib/datadog/appsec/contrib/active_record/integration.rb +41 -0
- data/lib/datadog/appsec/contrib/active_record/patcher.rb +104 -0
- 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 +34 -26
- 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 +21 -36
- 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 -106
- 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 +3 -6
- data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +17 -15
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +118 -114
- 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 -35
- 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/rails/request_middleware.rb +2 -1
- 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 -71
- 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/contrib/sinatra/request_middleware.rb +2 -1
- 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 -128
- data/lib/datadog/appsec/ext.rb +17 -1
- 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 -36
- data/lib/datadog/appsec/monitor.rb +1 -1
- data/lib/datadog/appsec/processor/rule_loader.rb +54 -60
- data/lib/datadog/appsec/rate_limiter.rb +1 -1
- data/lib/datadog/appsec/remote.rb +59 -70
- data/lib/datadog/appsec/response.rb +42 -94
- 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.rb +35 -28
- 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 +46 -59
- 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 +283 -54
- 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 +51 -30
- data/lib/datadog/core/configuration/options.rb +58 -19
- data/lib/datadog/core/configuration/settings.rb +341 -140
- data/lib/datadog/core/configuration/stable_config.rb +32 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +426 -0
- data/lib/datadog/core/configuration.rb +51 -21
- 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 +89 -39
- 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 +46 -9
- 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/gc.rb +1 -1
- 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/vm_cache.rb +1 -1
- data/lib/datadog/core/environment/yjit.rb +8 -2
- 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/helpers.rb +1 -1
- data/lib/datadog/core/metrics/logging.rb +8 -8
- data/lib/datadog/core/metrics/options.rb +5 -5
- 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 +82 -11
- data/lib/datadog/core/remote/client.rb +130 -107
- data/lib/datadog/core/remote/component.rb +97 -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 +19 -6
- 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 -106
- data/lib/datadog/core/remote/transport/negotiation.rb +9 -21
- data/lib/datadog/core/remote/worker.rb +40 -41
- data/lib/datadog/core/runtime/ext.rb +22 -22
- data/lib/datadog/core/runtime/metrics.rb +34 -14
- data/lib/datadog/core/semaphore.rb +32 -0
- 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 +143 -60
- data/lib/datadog/core/telemetry/configuration_value.rb +38 -0
- data/lib/datadog/core/telemetry/emitter.rb +26 -14
- data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +67 -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 +274 -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 -9
- 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 +35 -17
- data/lib/datadog/core/telemetry/metric.rb +37 -15
- 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 +23 -11
- 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 -10
- 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 +181 -29
- 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 +3 -4
- data/lib/datadog/core/utils/spawn_monkey_patch.rb +68 -0
- data/lib/datadog/core/utils/time.rb +19 -33
- 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 +41 -15
- 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 +167 -0
- data/lib/datadog/data_streams/processor.rb +542 -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 +256 -18
- data/lib/datadog/di/component.rb +220 -0
- data/lib/datadog/di/configuration.rb +241 -2
- data/lib/datadog/di/context.rb +82 -0
- data/lib/datadog/di/contrib/active_record.rb +41 -0
- data/lib/datadog/di/contrib/railtie.rb +15 -0
- data/lib/datadog/di/contrib.rb +28 -0
- data/lib/datadog/di/el/compiler.rb +244 -0
- data/lib/datadog/di/el/evaluator.rb +239 -0
- data/lib/datadog/di/el/expression.rb +53 -0
- data/lib/datadog/di/el.rb +5 -0
- data/lib/datadog/di/error.rb +66 -0
- data/lib/datadog/di/fatal_exceptions.rb +26 -0
- data/lib/datadog/di/instrumenter.rb +978 -0
- data/lib/datadog/di/logger.rb +30 -0
- data/lib/datadog/di/preload.rb +18 -0
- data/lib/datadog/di/probe.rb +188 -14
- data/lib/datadog/di/probe_builder.rb +132 -3
- 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 +352 -0
- data/lib/datadog/di/probe_notification_builder.rb +452 -0
- data/lib/datadog/di/probe_notifier_worker.rb +381 -0
- 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 +310 -0
- data/lib/datadog/di/serializer.rb +487 -96
- 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 +170 -0
- data/lib/datadog/di.rb +224 -2
- 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/agentless/configuration_source.rb +246 -0
- data/lib/datadog/open_feature/agentless/parser.rb +43 -0
- data/lib/datadog/open_feature/agentless/response.rb +19 -0
- data/lib/datadog/open_feature/agentless/transport.rb +103 -0
- data/lib/datadog/open_feature/component.rb +165 -0
- data/lib/datadog/open_feature/configuration/agentless_endpoint.rb +90 -0
- data/lib/datadog/open_feature/configuration/source/resolution.rb +22 -0
- data/lib/datadog/open_feature/configuration/source.rb +43 -0
- data/lib/datadog/open_feature/configuration.rb +168 -0
- data/lib/datadog/open_feature/evaluation_engine.rb +96 -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 +67 -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 +40 -0
- data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +442 -0
- data/lib/datadog/open_feature/flag_evaluation/writer.rb +544 -0
- data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +105 -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 +74 -0
- data/lib/datadog/open_feature/noop_evaluator.rb +31 -0
- data/lib/datadog/open_feature/provider.rb +235 -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 +126 -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 +21 -15
- 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 +57 -0
- data/lib/datadog/opentelemetry/trace.rb +8 -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 +60 -3
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -4
- data/lib/datadog/profiling/collectors/info.rb +62 -8
- data/lib/datadog/profiling/collectors/thread_context.rb +25 -18
- data/lib/datadog/profiling/component.rb +123 -104
- 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 +38 -18
- data/lib/datadog/profiling/tag_builder.rb +41 -41
- data/lib/datadog/profiling/tasks/exec.rb +11 -6
- data/lib/datadog/profiling/tasks/help.rb +1 -0
- data/lib/datadog/profiling/tasks/setup.rb +4 -2
- data/lib/datadog/profiling.rb +42 -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 +1092 -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 +63 -23
- data/lib/datadog/tracing/configuration/dynamic/option.rb +4 -0
- data/lib/datadog/tracing/configuration/dynamic.rb +16 -16
- data/lib/datadog/tracing/configuration/ext.rb +62 -33
- data/lib/datadog/tracing/configuration/http.rb +2 -2
- data/lib/datadog/tracing/configuration/settings.rb +182 -34
- data/lib/datadog/tracing/context.rb +27 -5
- data/lib/datadog/tracing/context_provider.rb +12 -8
- 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 +5 -5
- 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 +3 -3
- 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 +38 -20
- 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 +3 -3
- 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 +3 -3
- 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 +9 -9
- 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 +17 -19
- data/lib/datadog/tracing/contrib/active_record/events.rb +3 -3
- 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 +29 -21
- 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 +16 -6
- 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 +27 -27
- 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 +6 -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 +63 -64
- data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +9 -9
- data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +7 -3
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +19 -17
- 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 +18 -17
- data/lib/datadog/tracing/contrib/excon/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/ext.rb +23 -20
- 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 +20 -17
- 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 +69 -36
- 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 +26 -24
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +14 -18
- 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 +13 -8
- 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 +14 -14
- data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +1 -7
- 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 -24
- 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 -27
- 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 +21 -29
- 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 +8 -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 +11 -6
- data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +11 -6
- data/lib/datadog/tracing/contrib/kafka/events.rb +10 -10
- 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 +10 -10
- data/lib/datadog/tracing/contrib/patchable.rb +1 -1
- data/lib/datadog/tracing/contrib/patcher.rb +15 -14
- 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 +3 -2
- 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 +4 -4
- 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 +14 -4
- 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 +6 -6
- 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 +131 -7
- 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 +5 -5
- 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 +41 -7
- data/lib/datadog/tracing/contrib/redis/tags.rb +7 -12
- data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +20 -12
- 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 +19 -15
- 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 +8 -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/jdbc_connection_string.rb +140 -0
- data/lib/datadog/tracing/contrib/sequel/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/sequel/utils.rb +103 -16
- 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 +14 -9
- 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 +8 -4
- 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 +5 -4
- 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} +17 -15
- 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 +21 -14
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +23 -15
- 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 +250 -0
- data/lib/datadog/tracing/distributed/datadog.rb +41 -38
- 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 +73 -14
- data/lib/datadog/tracing/distributed/propagation_policy.rb +47 -0
- data/lib/datadog/tracing/distributed/trace_context.rb +31 -259
- data/lib/datadog/tracing/distributed/trace_state/datadog.rb +233 -0
- data/lib/datadog/tracing/distributed/trace_state/ext.rb +44 -0
- data/lib/datadog/tracing/distributed/trace_state/open_telemetry.rb +156 -0
- data/lib/datadog/tracing/distributed/trace_state.rb +121 -0
- 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 +89 -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 +9 -9
- data/lib/datadog/tracing/metadata.rb +5 -3
- data/lib/datadog/tracing/otel_thread_context.rb +99 -0
- 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 +228 -30
- 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 +91 -58
- 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 +29 -12
- 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 +120 -40
- data/lib/datadog/tracing/sync_writer.rb +22 -14
- data/lib/datadog/tracing/trace_digest.rb +58 -28
- data/lib/datadog/tracing/trace_operation.rb +263 -120
- data/lib/datadog/tracing/trace_segment.rb +18 -16
- data/lib/datadog/tracing/tracer.rb +208 -73
- 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 -73
- 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 +33 -39
- data/lib/datadog/tracing/transport/io.rb +3 -3
- data/lib/datadog/tracing/transport/native/response.rb +71 -0
- data/lib/datadog/tracing/transport/native.rb +379 -0
- data/lib/datadog/tracing/transport/serializable_trace.rb +47 -23
- data/lib/datadog/tracing/transport/span_events_negotiation.rb +41 -0
- data/lib/datadog/tracing/transport/statistics.rb +3 -3
- data/lib/datadog/tracing/transport/trace_formatter.rb +49 -10
- data/lib/datadog/tracing/transport/traces.rb +45 -99
- data/lib/datadog/tracing/utils.rb +3 -3
- data/lib/datadog/tracing/workers.rb +12 -10
- data/lib/datadog/tracing/writer.rb +54 -39
- data/lib/datadog/tracing.rb +19 -6
- data/lib/datadog/version.rb +4 -4
- data/lib/datadog.rb +21 -5
- metadata +372 -70
- 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 -207
- 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 -59
- data/lib/datadog/appsec/contrib/patcher.rb +0 -12
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +0 -81
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +0 -60
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +0 -66
- data/lib/datadog/appsec/contrib/rails/ext.rb +0 -13
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +0 -66
- data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -14
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +0 -61
- data/lib/datadog/appsec/monitor/reactive/set_user.rb +0 -58
- data/lib/datadog/appsec/processor/actions.rb +0 -49
- data/lib/datadog/appsec/processor/rule_merger.rb +0 -170
- data/lib/datadog/appsec/processor.rb +0 -170
- 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 -61
- 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 -163
- data/lib/datadog/tracing/contrib/rails/utils.rb +0 -26
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +0 -84
- 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,14 +17,11 @@
|
|
|
17
17
|
#include "setup_signal_handler.h"
|
|
18
18
|
#include "time_helpers.h"
|
|
19
19
|
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// See https://docs.google.com/document/d/1lWLB714wlLBBq6T4xZyAc4a5wtWhSmr4-hgiPKeErlA/edit#heading=h.ugp0zxcj5iqh
|
|
24
|
-
// (Datadog-only link) for research backing the choice of this value.
|
|
25
|
-
unsigned int MAX_ALLOC_WEIGHT = 10000;
|
|
20
|
+
#ifdef __APPLE__
|
|
21
|
+
#include "macos_sampler_thread.h"
|
|
22
|
+
#endif
|
|
26
23
|
|
|
27
|
-
// Used to trigger the execution of Collectors::
|
|
24
|
+
// Used to trigger the execution of Collectors::ThreadContext, which implements all of the sampling logic
|
|
28
25
|
// itself; this class only implements the "when to do it" part.
|
|
29
26
|
//
|
|
30
27
|
// This file implements the native bits of the Datadog::Profiling::Collectors::CpuAndWallTimeWorker class
|
|
@@ -40,7 +37,7 @@ unsigned int MAX_ALLOC_WEIGHT = 10000;
|
|
|
40
37
|
// Currently, sampling Ruby threads requires calling Ruby VM APIs that are only safe to call while holding on to the
|
|
41
38
|
// global VM lock (and are not async-signal safe -- cannot be called from a signal handler).
|
|
42
39
|
//
|
|
43
|
-
// @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
|
|
44
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
|
|
45
42
|
// we're considering it as a given.
|
|
46
43
|
//
|
|
@@ -71,28 +68,44 @@ unsigned int MAX_ALLOC_WEIGHT = 10000;
|
|
|
71
68
|
// 4. The Ruby VM calls our `sample_from_postponed_job` from a thread holding the global VM lock. A sample is recorded by
|
|
72
69
|
// calling `thread_context_collector_sample`.
|
|
73
70
|
//
|
|
74
|
-
//
|
|
71
|
+
//
|
|
72
|
+
// ### Hooks and TracePoints
|
|
73
|
+
//
|
|
74
|
+
// This class uses various hooks:
|
|
75
|
+
// * A RUBY_INTERNAL_EVENT_GC_ENTER & RUBY_INTERNAL_EVENT_GC_EXIT TracePoint
|
|
76
|
+
// * A RUBY_INTERNAL_EVENT_NEWOBJ "event hook"/internal tracepoint
|
|
77
|
+
// * A GVL thread event hook
|
|
78
|
+
//
|
|
79
|
+
// We refer to those collectively as "hooks".
|
|
80
|
+
//
|
|
81
|
+
// ### Hooks and Forking
|
|
75
82
|
//
|
|
76
83
|
// When the Ruby VM forks, the CPU/Wall-time profiling stops naturally because it's triggered by a background thread
|
|
77
84
|
// that doesn't get automatically restarted by the VM on the child process. (The profiler does trigger its restart at
|
|
78
85
|
// some point -- see `Profiling::Tasks::Setup` for details).
|
|
79
86
|
//
|
|
80
|
-
// But this doesn't apply to any
|
|
81
|
-
// always remember consider this case of -- the worker thread may not be alive but the
|
|
87
|
+
// But this doesn't apply to any hooks this class may use, which will continue to be active. Thus, we need to
|
|
88
|
+
// always remember consider this case of -- the worker thread may not be alive but the hooks can continue to
|
|
82
89
|
// trigger samples.
|
|
83
90
|
//
|
|
84
91
|
// ---
|
|
85
92
|
|
|
93
|
+
// Maximum allowed value for an allocation weight. Attempts to use higher values will result in clamping.
|
|
94
|
+
// See https://docs.google.com/document/d/1lWLB714wlLBBq6T4xZyAc4a5wtWhSmr4-hgiPKeErlA/edit#heading=h.ugp0zxcj5iqh
|
|
95
|
+
// (Datadog-only link) for research backing the choice of this value.
|
|
96
|
+
unsigned int MAX_ALLOC_WEIGHT = 10000;
|
|
97
|
+
|
|
86
98
|
#ifndef NO_POSTPONED_TRIGGER
|
|
87
99
|
// Used to call the rb_postponed_job_trigger from Ruby 3.3+. These get initialized in
|
|
88
100
|
// `collectors_cpu_and_wall_time_worker_init` below and always get reused after that.
|
|
89
101
|
static rb_postponed_job_handle_t sample_from_postponed_job_handle;
|
|
90
102
|
static rb_postponed_job_handle_t after_gc_from_postponed_job_handle;
|
|
91
103
|
static rb_postponed_job_handle_t after_gvl_running_from_postponed_job_handle;
|
|
104
|
+
static rb_postponed_job_handle_t commit_heap_recordings_from_postponed_job_may_lose_gvl_handle;
|
|
92
105
|
#endif
|
|
93
106
|
|
|
94
107
|
// Contains state for a single CpuAndWallTimeWorker instance
|
|
95
|
-
struct
|
|
108
|
+
typedef struct {
|
|
96
109
|
// These are immutable after initialization
|
|
97
110
|
|
|
98
111
|
bool gc_profiling_enabled;
|
|
@@ -102,6 +115,10 @@ struct cpu_and_wall_time_worker_state {
|
|
|
102
115
|
bool allocation_counting_enabled;
|
|
103
116
|
bool gvl_profiling_enabled;
|
|
104
117
|
bool skip_idle_samples_for_testing;
|
|
118
|
+
bool sighandler_sampling_enabled;
|
|
119
|
+
uint32_t cpu_sampling_interval_ms;
|
|
120
|
+
// Minimum duration of a "Waiting for GVL" period to trigger a sample
|
|
121
|
+
uint32_t waiting_for_gvl_threshold_ns;
|
|
105
122
|
VALUE self_instance;
|
|
106
123
|
VALUE thread_context_collector_instance;
|
|
107
124
|
VALUE idle_sampling_helper_instance;
|
|
@@ -116,13 +133,29 @@ struct cpu_and_wall_time_worker_state {
|
|
|
116
133
|
// When something goes wrong during sampling, we record the Ruby exception here, so that it can be "re-raised" on
|
|
117
134
|
// the CpuAndWallTimeWorker thread
|
|
118
135
|
VALUE failure_exception;
|
|
136
|
+
const char *failure_exception_during_operation;
|
|
119
137
|
// Used by `_native_stop` to flag the worker thread to start (see comment on `_native_sampling_loop`)
|
|
120
138
|
VALUE stop_thread;
|
|
121
139
|
|
|
122
140
|
// Others
|
|
123
141
|
|
|
124
|
-
// Used to detect/avoid nested sampling,
|
|
125
|
-
// that happens during another sample
|
|
142
|
+
// Used to detect/avoid nested sampling, and intended to behave as a lock to ensure the profiler doesn't recurse on itself,
|
|
143
|
+
// e.g. when on_newobj_event gets triggered by a memory allocation that happens during another sample, or when the
|
|
144
|
+
// signal handler gets triggered while we're already in the middle of sampling.
|
|
145
|
+
//
|
|
146
|
+
// It's not an actual lock because we rely on the GVL for correct synchronization
|
|
147
|
+
// (and thus this flag is only valid when we know we have the GVL).
|
|
148
|
+
//
|
|
149
|
+
// Similar to a lock, it should not be held across long-running operations,
|
|
150
|
+
// **in particular it MUST NEVER be held during operations where we might lose the GVL**
|
|
151
|
+
// because effectively that would stop the profiler from working until control
|
|
152
|
+
// goes back to that special thread, which on a contended Ruby app, can take hundreds of ms (or more).
|
|
153
|
+
//
|
|
154
|
+
// Because what we want is "profiler doesn't recurse on itself" we want this to behave as a non-reentrant lock
|
|
155
|
+
// (FIXME: We should have checks for this)
|
|
156
|
+
//
|
|
157
|
+
// @ivoanjo: Right now we always sample inside `safely_call`; if that ever changes, this flag may need to become
|
|
158
|
+
// volatile/atomic/have some barriers to ensure it's visible during e.g. signal handlers.
|
|
126
159
|
bool during_sample;
|
|
127
160
|
|
|
128
161
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
@@ -134,27 +167,19 @@ struct cpu_and_wall_time_worker_state {
|
|
|
134
167
|
// # Generic stats
|
|
135
168
|
// How many times we tried to trigger a sample
|
|
136
169
|
unsigned int trigger_sample_attempts;
|
|
170
|
+
// How many times extra sleep was triggered by dynamic sampling rate
|
|
171
|
+
unsigned int trigger_sample_extra_sleep;
|
|
137
172
|
// How many times we tried to simulate signal delivery
|
|
138
173
|
unsigned int trigger_simulated_signal_delivery_attempts;
|
|
139
174
|
// How many times we actually simulated signal delivery
|
|
140
175
|
unsigned int simulated_signal_delivery;
|
|
141
|
-
// How many times we actually called rb_postponed_job_register_one from
|
|
176
|
+
// How many times we actually called rb_postponed_job_register_one from the signal handler
|
|
142
177
|
unsigned int signal_handler_enqueued_sample;
|
|
143
|
-
// How many times
|
|
144
|
-
unsigned int
|
|
178
|
+
// How many times we prepared a sample (sampled directly) from the signal handler
|
|
179
|
+
unsigned int signal_handler_prepared_sample;
|
|
145
180
|
// How many times we actually tried to interrupt a thread for sampling
|
|
146
181
|
unsigned int interrupt_thread_attempts;
|
|
147
182
|
|
|
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
183
|
// # CPU/Walltime sampling stats
|
|
159
184
|
// How many times we actually CPU/wall sampled
|
|
160
185
|
unsigned int cpu_sampled;
|
|
@@ -178,35 +203,58 @@ struct cpu_and_wall_time_worker_state {
|
|
|
178
203
|
unsigned int allocations_during_sample;
|
|
179
204
|
|
|
180
205
|
// # GVL profiling stats
|
|
206
|
+
// Note that this tracks two kind of samples from RESUMED:
|
|
207
|
+
// * samples when Waiting for GVL for more than the threshold
|
|
208
|
+
// * samples for the skip-samples-while-without-GVL optimization,
|
|
209
|
+
// which are needed to attribute the time without GVL to the correct Ruby stack
|
|
210
|
+
|
|
181
211
|
// How many times we triggered the after_gvl_running sampling
|
|
182
212
|
unsigned int after_gvl_running;
|
|
213
|
+
// How many times we skipped the after_gvl_running sampling
|
|
214
|
+
unsigned int gvl_dont_sample;
|
|
215
|
+
// Min/max/total wall-time spent on gvl sampling
|
|
216
|
+
uint64_t gvl_sampling_time_ns_min;
|
|
217
|
+
uint64_t gvl_sampling_time_ns_max;
|
|
218
|
+
uint64_t gvl_sampling_time_ns_total;
|
|
219
|
+
|
|
220
|
+
struct vm_metrics {
|
|
221
|
+
// Total time spent waiting for the GVL
|
|
222
|
+
uint64_t gvl_waiting_time_ns_total;
|
|
223
|
+
} vm_metrics;
|
|
183
224
|
} stats;
|
|
184
|
-
};
|
|
225
|
+
} cpu_and_wall_time_worker_state;
|
|
185
226
|
|
|
186
227
|
static VALUE _native_new(VALUE klass);
|
|
187
228
|
static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
|
|
188
229
|
static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr);
|
|
189
230
|
static VALUE _native_sampling_loop(VALUE self, VALUE instance);
|
|
231
|
+
static VALUE _native_profiler_internal_thread_done(VALUE self_instance);
|
|
190
232
|
static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread);
|
|
191
|
-
static VALUE stop(VALUE self_instance, VALUE optional_exception);
|
|
192
|
-
static void stop_state(
|
|
233
|
+
static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *optional_exception_during_operation);
|
|
234
|
+
static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception, const char *optional_operation_name);
|
|
193
235
|
static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
|
|
194
236
|
static void *run_sampling_trigger_loop(void *state_ptr);
|
|
195
237
|
static void interrupt_sampling_trigger_loop(void *state_ptr);
|
|
196
238
|
static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
197
239
|
static VALUE rescued_sample_from_postponed_job(VALUE self_instance);
|
|
198
|
-
static VALUE handle_sampling_failure(VALUE self_instance, VALUE exception);
|
|
199
240
|
static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self);
|
|
200
241
|
static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance);
|
|
201
242
|
static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
243
|
+
static VALUE _native_failure_exception_during_operation(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
202
244
|
static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
|
|
203
245
|
static VALUE _native_install_testing_signal_handler(DDTRACE_UNUSED VALUE self);
|
|
204
246
|
static VALUE _native_remove_testing_signal_handler(DDTRACE_UNUSED VALUE self);
|
|
247
|
+
static VALUE _native_install_sigprof_handler_on_altstack(DDTRACE_UNUSED VALUE self);
|
|
205
248
|
static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self);
|
|
206
249
|
static VALUE _native_gc_tracepoint(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
207
250
|
static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused);
|
|
208
251
|
static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
209
|
-
static VALUE safely_call(
|
|
252
|
+
static VALUE safely_call(
|
|
253
|
+
VALUE (*function_to_call_safely)(VALUE),
|
|
254
|
+
VALUE function_to_call_safely_arg,
|
|
255
|
+
VALUE instance,
|
|
256
|
+
VALUE (*handle_sampling_failure)(VALUE, VALUE)
|
|
257
|
+
);
|
|
210
258
|
static VALUE _native_simulate_handle_sampling_signal(DDTRACE_UNUSED VALUE self);
|
|
211
259
|
static VALUE _native_simulate_sample_from_postponed_job(DDTRACE_UNUSED VALUE self);
|
|
212
260
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
@@ -215,22 +263,35 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
|
215
263
|
static VALUE _native_stats_reset_not_thread_safe(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
216
264
|
void *simulate_sampling_signal_delivery(DDTRACE_UNUSED void *_unused);
|
|
217
265
|
static void grab_gvl_and_sample(void);
|
|
218
|
-
static void reset_stats_not_thread_safe(
|
|
266
|
+
static void reset_stats_not_thread_safe(cpu_and_wall_time_worker_state *state);
|
|
219
267
|
static void sleep_for(uint64_t time_ns);
|
|
220
268
|
static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self);
|
|
221
269
|
static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *unused2);
|
|
222
|
-
static void
|
|
270
|
+
static void disable_hooks(cpu_and_wall_time_worker_state *state);
|
|
223
271
|
static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self);
|
|
224
272
|
static VALUE rescued_sample_allocation(VALUE tracepoint_data);
|
|
225
|
-
static
|
|
273
|
+
static VALUE rescued_commit_heap_recordings_may_lose_gvl(VALUE self_instance);
|
|
274
|
+
static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error);
|
|
275
|
+
static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state);
|
|
226
276
|
static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
|
|
227
277
|
static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self);
|
|
228
278
|
static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);
|
|
229
279
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
230
|
-
static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused);
|
|
231
|
-
static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
280
|
+
static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused);
|
|
281
|
+
static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
282
|
+
static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance);
|
|
283
|
+
static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception);
|
|
232
284
|
#endif
|
|
233
285
|
static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
286
|
+
static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception);
|
|
287
|
+
static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception);
|
|
288
|
+
static VALUE handle_sampling_failure_thread_context_collector_heap_update(VALUE self_instance, VALUE exception);
|
|
289
|
+
static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception);
|
|
290
|
+
static VALUE handle_sampling_failure_rescued_commit_heap_recordings(VALUE self_instance, VALUE exception);
|
|
291
|
+
static inline void during_sample_enter(cpu_and_wall_time_worker_state* state);
|
|
292
|
+
static inline void during_sample_exit(cpu_and_wall_time_worker_state* state);
|
|
293
|
+
static VALUE during_sample_exit_rescue(VALUE state_ptr);
|
|
294
|
+
static void commit_heap_recordings_from_postponed_job_may_lose_gvl(DDTRACE_UNUSED void *_unused);
|
|
234
295
|
|
|
235
296
|
// We're using `on_newobj_event` function with `rb_add_event_hook2`, which requires in its public signature a function
|
|
236
297
|
// with signature `rb_event_hook_func_t` which doesn't match `on_newobj_event`.
|
|
@@ -255,7 +316,17 @@ static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE
|
|
|
255
316
|
// This global state is needed because a bunch of functions on this file need to access it from situations
|
|
256
317
|
// (e.g. signal handler) where it's impossible or just awkward to pass it as an argument.
|
|
257
318
|
static VALUE active_sampler_instance = Qnil;
|
|
258
|
-
static
|
|
319
|
+
static cpu_and_wall_time_worker_state *active_sampler_instance_state = NULL;
|
|
320
|
+
static VALUE clock_failure_exception_class = Qnil;
|
|
321
|
+
|
|
322
|
+
// Stats that live outside of any particular `cpu_and_wall_time_worker_state`, so that they can always be safely
|
|
323
|
+
// touched, even when we're not sure it's safe to touch the state (e.g. signal handler, no GVL, etc).
|
|
324
|
+
typedef struct {
|
|
325
|
+
// How many times we skipped sampling because we were running on the alternate signal stack (e.g. nested inside
|
|
326
|
+
// another signal handler, presumably GC compaction).
|
|
327
|
+
unsigned int signal_handler_skipped_sample_on_altstack;
|
|
328
|
+
} global_stats_t;
|
|
329
|
+
static global_stats_t global_stats;
|
|
259
330
|
|
|
260
331
|
// See handle_sampling_signal for details on what this does
|
|
261
332
|
#ifdef NO_POSTPONED_TRIGGER
|
|
@@ -277,13 +348,16 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
277
348
|
sample_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, sample_from_postponed_job, NULL);
|
|
278
349
|
after_gc_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gc_from_postponed_job, NULL);
|
|
279
350
|
after_gvl_running_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gvl_running_from_postponed_job, NULL);
|
|
351
|
+
commit_heap_recordings_from_postponed_job_may_lose_gvl_handle =
|
|
352
|
+
rb_postponed_job_preregister(unused_flags, commit_heap_recordings_from_postponed_job_may_lose_gvl, NULL);
|
|
280
353
|
|
|
281
354
|
if (
|
|
282
355
|
sample_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
283
356
|
after_gc_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
284
|
-
after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
|
|
357
|
+
after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
358
|
+
commit_heap_recordings_from_postponed_job_may_lose_gvl_handle == POSTPONED_JOB_HANDLE_INVALID
|
|
285
359
|
) {
|
|
286
|
-
|
|
360
|
+
raise_error(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
|
|
287
361
|
}
|
|
288
362
|
#else
|
|
289
363
|
gc_finalize_deferred_workaround = objspace_ptr_for_gc_finalize_deferred_workaround();
|
|
@@ -293,6 +367,8 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
293
367
|
VALUE collectors_cpu_and_wall_time_worker_class = rb_define_class_under(collectors_module, "CpuAndWallTimeWorker", rb_cObject);
|
|
294
368
|
// Hosts methods used for testing the native code using RSpec
|
|
295
369
|
VALUE testing_module = rb_define_module_under(collectors_cpu_and_wall_time_worker_class, "Testing");
|
|
370
|
+
clock_failure_exception_class = rb_define_class_under(collectors_cpu_and_wall_time_worker_class, "ClockFailure", rb_eRuntimeError);
|
|
371
|
+
rb_gc_register_mark_object(clock_failure_exception_class);
|
|
296
372
|
|
|
297
373
|
// Instances of the CpuAndWallTimeWorker class are "TypedData" objects.
|
|
298
374
|
// "TypedData" objects are special objects in the Ruby VM that can wrap C structs.
|
|
@@ -306,19 +382,20 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
306
382
|
|
|
307
383
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_initialize", _native_initialize, -1);
|
|
308
384
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_sampling_loop", _native_sampling_loop, 1);
|
|
385
|
+
rb_define_method(collectors_cpu_and_wall_time_worker_class, "_native_profiler_internal_thread_done", _native_profiler_internal_thread_done, 0);
|
|
309
386
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stop", _native_stop, 2);
|
|
310
387
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_reset_after_fork", _native_reset_after_fork, 1);
|
|
311
388
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stats", _native_stats, 1);
|
|
312
389
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stats_reset_not_thread_safe", _native_stats_reset_not_thread_safe, 1);
|
|
313
390
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_allocation_count", _native_allocation_count, 0);
|
|
314
391
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_is_running?", _native_is_running, 1);
|
|
392
|
+
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_failure_exception_during_operation", _native_failure_exception_during_operation, 1);
|
|
315
393
|
rb_define_singleton_method(testing_module, "_native_current_sigprof_signal_handler", _native_current_sigprof_signal_handler, 0);
|
|
316
394
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_hold_signals", _native_hold_signals, 0);
|
|
317
395
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_resume_signals", _native_resume_signals, 0);
|
|
318
|
-
// TODO: Remove `_native_is_running` from `testing_module` (should be in class) once `prof-correctness` has been updated to not need it
|
|
319
|
-
rb_define_singleton_method(testing_module, "_native_is_running?", _native_is_running, 1);
|
|
320
396
|
rb_define_singleton_method(testing_module, "_native_install_testing_signal_handler", _native_install_testing_signal_handler, 0);
|
|
321
397
|
rb_define_singleton_method(testing_module, "_native_remove_testing_signal_handler", _native_remove_testing_signal_handler, 0);
|
|
398
|
+
rb_define_singleton_method(testing_module, "_native_install_sigprof_handler_on_altstack", _native_install_sigprof_handler_on_altstack, 0);
|
|
322
399
|
rb_define_singleton_method(testing_module, "_native_trigger_sample", _native_trigger_sample, 0);
|
|
323
400
|
rb_define_singleton_method(testing_module, "_native_gc_tracepoint", _native_gc_tracepoint, 1);
|
|
324
401
|
rb_define_singleton_method(testing_module, "_native_simulate_handle_sampling_signal", _native_simulate_handle_sampling_signal, 0);
|
|
@@ -329,7 +406,7 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
329
406
|
rb_define_singleton_method(testing_module, "_native_gvl_profiling_hook_active", _native_gvl_profiling_hook_active, 1);
|
|
330
407
|
}
|
|
331
408
|
|
|
332
|
-
// This structure is used to define a Ruby object that stores a pointer to a
|
|
409
|
+
// This structure is used to define a Ruby object that stores a pointer to a cpu_and_wall_time_worker_state
|
|
333
410
|
// See also https://github.com/ruby/ruby/blob/master/doc/extension.rdoc for how this works
|
|
334
411
|
static const rb_data_type_t cpu_and_wall_time_worker_typed_data = {
|
|
335
412
|
.wrap_struct_name = "Datadog::Profiling::Collectors::CpuAndWallTimeWorker",
|
|
@@ -345,7 +422,7 @@ static const rb_data_type_t cpu_and_wall_time_worker_typed_data = {
|
|
|
345
422
|
static VALUE _native_new(VALUE klass) {
|
|
346
423
|
long now = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
347
424
|
|
|
348
|
-
|
|
425
|
+
cpu_and_wall_time_worker_state *state = ruby_xcalloc(1, sizeof(cpu_and_wall_time_worker_state));
|
|
349
426
|
|
|
350
427
|
// Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
|
|
351
428
|
// being leaked.
|
|
@@ -357,6 +434,9 @@ static VALUE _native_new(VALUE klass) {
|
|
|
357
434
|
state->allocation_counting_enabled = false;
|
|
358
435
|
state->gvl_profiling_enabled = false;
|
|
359
436
|
state->skip_idle_samples_for_testing = false;
|
|
437
|
+
state->sighandler_sampling_enabled = false;
|
|
438
|
+
state->cpu_sampling_interval_ms = 10;
|
|
439
|
+
state->waiting_for_gvl_threshold_ns = 10 * 1000 * 1000;
|
|
360
440
|
state->thread_context_collector_instance = Qnil;
|
|
361
441
|
state->idle_sampling_helper_instance = Qnil;
|
|
362
442
|
state->owner_thread = Qnil;
|
|
@@ -365,9 +445,10 @@ static VALUE _native_new(VALUE klass) {
|
|
|
365
445
|
|
|
366
446
|
atomic_init(&state->should_run, false);
|
|
367
447
|
state->failure_exception = Qnil;
|
|
448
|
+
state->failure_exception_during_operation = NULL;
|
|
368
449
|
state->stop_thread = Qnil;
|
|
369
450
|
|
|
370
|
-
state
|
|
451
|
+
during_sample_exit(state);
|
|
371
452
|
|
|
372
453
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
373
454
|
state->gvl_profiling_hook = NULL;
|
|
@@ -399,6 +480,9 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
399
480
|
VALUE allocation_counting_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("allocation_counting_enabled")));
|
|
400
481
|
VALUE gvl_profiling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("gvl_profiling_enabled")));
|
|
401
482
|
VALUE skip_idle_samples_for_testing = rb_hash_fetch(options, ID2SYM(rb_intern("skip_idle_samples_for_testing")));
|
|
483
|
+
VALUE sighandler_sampling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("sighandler_sampling_enabled")));
|
|
484
|
+
VALUE cpu_sampling_interval_ms = rb_hash_fetch(options, ID2SYM(rb_intern("cpu_sampling_interval_ms")));
|
|
485
|
+
VALUE waiting_for_gvl_threshold_ns = rb_hash_fetch(options, ID2SYM(rb_intern("waiting_for_gvl_threshold_ns")));
|
|
402
486
|
|
|
403
487
|
ENFORCE_BOOLEAN(gc_profiling_enabled);
|
|
404
488
|
ENFORCE_BOOLEAN(no_signals_workaround_enabled);
|
|
@@ -408,9 +492,12 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
408
492
|
ENFORCE_BOOLEAN(allocation_counting_enabled);
|
|
409
493
|
ENFORCE_BOOLEAN(gvl_profiling_enabled);
|
|
410
494
|
ENFORCE_BOOLEAN(skip_idle_samples_for_testing)
|
|
495
|
+
ENFORCE_BOOLEAN(sighandler_sampling_enabled)
|
|
496
|
+
ENFORCE_TYPE(cpu_sampling_interval_ms, T_FIXNUM);
|
|
497
|
+
ENFORCE_TYPE(waiting_for_gvl_threshold_ns, T_FIXNUM);
|
|
411
498
|
|
|
412
|
-
|
|
413
|
-
TypedData_Get_Struct(self_instance,
|
|
499
|
+
cpu_and_wall_time_worker_state *state;
|
|
500
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
414
501
|
|
|
415
502
|
state->gc_profiling_enabled = (gc_profiling_enabled == Qtrue);
|
|
416
503
|
state->no_signals_workaround_enabled = (no_signals_workaround_enabled == Qtrue);
|
|
@@ -419,6 +506,9 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
419
506
|
state->allocation_counting_enabled = (allocation_counting_enabled == Qtrue);
|
|
420
507
|
state->gvl_profiling_enabled = (gvl_profiling_enabled == Qtrue);
|
|
421
508
|
state->skip_idle_samples_for_testing = (skip_idle_samples_for_testing == Qtrue);
|
|
509
|
+
state->sighandler_sampling_enabled = (sighandler_sampling_enabled == Qtrue);
|
|
510
|
+
state->cpu_sampling_interval_ms = NUM2INT(cpu_sampling_interval_ms);
|
|
511
|
+
state->waiting_for_gvl_threshold_ns = NUM2UINT(waiting_for_gvl_threshold_ns);
|
|
422
512
|
|
|
423
513
|
double total_overhead_target_percentage = NUM2DBL(dynamic_sampling_rate_overhead_target_percentage);
|
|
424
514
|
if (!state->allocation_profiling_enabled) {
|
|
@@ -440,7 +530,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
440
530
|
|
|
441
531
|
// Since our state contains references to Ruby objects, we need to tell the Ruby GC about them
|
|
442
532
|
static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr) {
|
|
443
|
-
|
|
533
|
+
cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
|
|
444
534
|
|
|
445
535
|
rb_gc_mark(state->thread_context_collector_instance);
|
|
446
536
|
rb_gc_mark(state->idle_sampling_helper_instance);
|
|
@@ -452,31 +542,28 @@ static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr) {
|
|
|
452
542
|
|
|
453
543
|
// Called in a background thread created in CpuAndWallTimeWorker#start
|
|
454
544
|
static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
455
|
-
|
|
456
|
-
TypedData_Get_Struct(instance,
|
|
545
|
+
cpu_and_wall_time_worker_state *state;
|
|
546
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
457
547
|
|
|
458
548
|
// If we already got a delayed exception registered even before starting, raise before starting
|
|
459
549
|
if (state->failure_exception != Qnil) {
|
|
460
|
-
|
|
550
|
+
disable_hooks(state);
|
|
461
551
|
rb_exc_raise(state->failure_exception);
|
|
462
552
|
}
|
|
463
553
|
|
|
464
|
-
|
|
554
|
+
cpu_and_wall_time_worker_state *old_state = active_sampler_instance_state;
|
|
465
555
|
if (old_state != NULL) {
|
|
466
556
|
if (is_thread_alive(old_state->owner_thread)) {
|
|
467
|
-
|
|
468
|
-
rb_eRuntimeError,
|
|
469
|
-
"Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread"
|
|
470
|
-
);
|
|
557
|
+
raise_error(rb_eRuntimeError, "Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread");
|
|
471
558
|
} else {
|
|
472
559
|
// The previously active thread seems to have died without cleaning up after itself.
|
|
473
|
-
// In this case, we can still go ahead and start the profiler BUT we make sure to disable any existing
|
|
560
|
+
// In this case, we can still go ahead and start the profiler BUT we make sure to disable any existing hooks
|
|
474
561
|
// first as:
|
|
475
|
-
// a) If this is a new instance of the CpuAndWallTimeWorker, we don't want the
|
|
562
|
+
// a) If this is a new instance of the CpuAndWallTimeWorker, we don't want the hooks from the old instance
|
|
476
563
|
// being kept around
|
|
477
564
|
// b) If this is the same instance of the CpuAndWallTimeWorker if we call enable on a tracepoint that is already
|
|
478
565
|
// enabled, it will start firing more than once, see https://bugs.ruby-lang.org/issues/19114 for details.
|
|
479
|
-
|
|
566
|
+
disable_hooks(old_state);
|
|
480
567
|
}
|
|
481
568
|
}
|
|
482
569
|
|
|
@@ -484,11 +571,20 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
484
571
|
// situation we stop immediately and never even start the sampling trigger loop.
|
|
485
572
|
if (state->stop_thread == rb_thread_current()) return Qnil;
|
|
486
573
|
|
|
574
|
+
thread_context_collector_profiler_internal_thread_started();
|
|
575
|
+
|
|
487
576
|
// Reset the dynamic sampling rate state, if any (reminder: the monotonic clock reference may change after a fork)
|
|
488
577
|
dynamic_sampling_rate_reset(&state->cpu_dynamic_sampling_rate);
|
|
489
578
|
long now = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
490
579
|
discrete_dynamic_sampler_reset(&state->allocation_sampler, now);
|
|
491
580
|
|
|
581
|
+
// Reset per-thread state, if any. This ensures there's no leftover state from a previous profiler run that would
|
|
582
|
+
// affect or be included in samples taken by this profiler about to run.
|
|
583
|
+
//
|
|
584
|
+
// NOTE: This needs to be called before we enable any hooks or anything that could trigger samples (e.g.
|
|
585
|
+
// reset cannot be concurrent with any sampling activity)
|
|
586
|
+
thread_context_collector_reset_all_per_thread_contexts(state->thread_context_collector_instance);
|
|
587
|
+
|
|
492
588
|
// This write to a global is thread-safe BECAUSE we're still holding on to the global VM lock at this point
|
|
493
589
|
active_sampler_instance_state = state;
|
|
494
590
|
active_sampler_instance = instance;
|
|
@@ -498,13 +594,21 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
498
594
|
|
|
499
595
|
block_sigprof_signal_handler_from_running_in_current_thread(); // We want to interrupt the thread with the global VM lock, never this one
|
|
500
596
|
|
|
597
|
+
#ifdef __APPLE__
|
|
598
|
+
// Promote the native thread that will run the sampling loop to a realtime scheduling policy so
|
|
599
|
+
// its periodic wake-ups are not subject to the default ~1-2ms timeshare wake latency. The matching
|
|
600
|
+
// demote happens below in this same function, alongside the SIGPROF unblock dance, so both per-thread
|
|
601
|
+
// policy changes are reverted before Ruby reuses this native thread for an unrelated Ruby thread.
|
|
602
|
+
promote_sampler_thread_to_realtime(MILLIS_AS_NS(state->cpu_sampling_interval_ms));
|
|
603
|
+
#endif
|
|
604
|
+
|
|
501
605
|
// Release GVL, get to the actual work!
|
|
502
606
|
int exception_state;
|
|
503
607
|
rb_protect(release_gvl_and_run_sampling_trigger_loop, instance, &exception_state);
|
|
504
608
|
|
|
505
609
|
// The sample trigger loop finished (either cleanly or with an error); let's clean up
|
|
506
610
|
|
|
507
|
-
|
|
611
|
+
disable_hooks(state);
|
|
508
612
|
|
|
509
613
|
active_sampler_instance_state = NULL;
|
|
510
614
|
active_sampler_instance = Qnil;
|
|
@@ -519,6 +623,14 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
519
623
|
// had SIGPROF delivery blocked. :hide_the_pain_harold:
|
|
520
624
|
unblock_sigprof_signal_handler_from_running_in_current_thread();
|
|
521
625
|
|
|
626
|
+
#ifdef __APPLE__
|
|
627
|
+
// Pairs with promote_sampler_thread_to_realtime above. Same reasoning as the SIGPROF unblock:
|
|
628
|
+
// Ruby may reuse this native thread for an unrelated Ruby thread that would otherwise inherit
|
|
629
|
+
// our scheduler policy. Doing it here also covers the exception path, since grab_gvl_and_sample
|
|
630
|
+
// can raise and unwind out of the sampling loop.
|
|
631
|
+
demote_sampler_thread_from_realtime();
|
|
632
|
+
#endif
|
|
633
|
+
|
|
522
634
|
// Why replace and not use remove the signal handler? We do this because when a process receives a SIGPROF without
|
|
523
635
|
// having an explicit signal handler set up, the process will instantly terminate with a confusing
|
|
524
636
|
// "Profiling timer expired" message left behind. (This message doesn't come from us -- it's the default message for
|
|
@@ -529,8 +641,9 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
529
641
|
// @ivoanjo: I suspect this will never happen, but the cost of getting it wrong is really high (VM terminates) so this
|
|
530
642
|
// is a just-in-case situation.
|
|
531
643
|
//
|
|
532
|
-
// Note 2:
|
|
533
|
-
replace_sigprof_signal_handler_with_empty_handler
|
|
644
|
+
// Note 2: If exception_state is set, we have a pending exception that we'll re-raise below.
|
|
645
|
+
// In that case, we don't want replace_sigprof_signal_handler_with_empty_handler to raise another exception.
|
|
646
|
+
replace_sigprof_signal_handler_with_empty_handler(handle_sampling_signal, !exception_state);
|
|
534
647
|
|
|
535
648
|
// Ensure that instance is not garbage collected while the native sampling loop is running; this is probably not needed, but just in case
|
|
536
649
|
RB_GC_GUARD(instance);
|
|
@@ -540,69 +653,138 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
540
653
|
return Qnil;
|
|
541
654
|
}
|
|
542
655
|
|
|
656
|
+
static VALUE _native_profiler_internal_thread_done(VALUE self_instance) {
|
|
657
|
+
cpu_and_wall_time_worker_state *state;
|
|
658
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
659
|
+
|
|
660
|
+
during_sample_enter(state);
|
|
661
|
+
return rb_ensure(
|
|
662
|
+
thread_context_collector_profiler_internal_thread_done, state->thread_context_collector_instance,
|
|
663
|
+
during_sample_exit_rescue, (VALUE) state
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
|
|
543
667
|
static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread) {
|
|
544
|
-
|
|
545
|
-
TypedData_Get_Struct(self_instance,
|
|
668
|
+
cpu_and_wall_time_worker_state *state;
|
|
669
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
546
670
|
|
|
547
671
|
state->stop_thread = worker_thread;
|
|
548
672
|
|
|
549
|
-
return stop(self_instance,
|
|
673
|
+
return stop(self_instance, Qnil, NULL);
|
|
550
674
|
}
|
|
551
675
|
|
|
552
|
-
|
|
676
|
+
// When providing an `optional_exception`, `optional_exception_during_operation` should be provided as well
|
|
677
|
+
static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception, const char *optional_exception_during_operation) {
|
|
553
678
|
atomic_store(&state->should_run, false);
|
|
554
679
|
state->failure_exception = optional_exception;
|
|
680
|
+
state->failure_exception_during_operation = optional_exception_during_operation;
|
|
555
681
|
|
|
556
|
-
// Disable the
|
|
557
|
-
|
|
682
|
+
// Disable the hooks as soon as possible, so the VM doesn't keep on calling them
|
|
683
|
+
disable_hooks(state);
|
|
558
684
|
}
|
|
559
685
|
|
|
560
|
-
static VALUE stop(VALUE self_instance, VALUE optional_exception) {
|
|
561
|
-
|
|
562
|
-
TypedData_Get_Struct(self_instance,
|
|
686
|
+
static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *optional_exception_during_operation) {
|
|
687
|
+
cpu_and_wall_time_worker_state *state;
|
|
688
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
563
689
|
|
|
564
|
-
stop_state(state, optional_exception);
|
|
690
|
+
stop_state(state, optional_exception, optional_exception_during_operation);
|
|
565
691
|
|
|
566
692
|
return Qtrue;
|
|
567
693
|
}
|
|
568
694
|
|
|
695
|
+
// Our SIGPROF handler is not installed with SA_ONSTACK (see `install_sigprof_signal_handler`).
|
|
696
|
+
// If we detect our signal handler is running on the alt stack, it means we interrupted another signal handler
|
|
697
|
+
// that IS running on the alt stack -- in practice, Ruby's GC compaction read-barrier handler.
|
|
698
|
+
#ifdef __APPLE__
|
|
699
|
+
// On macOS, `ucontext->uc_stack.ss_sp` reports the (live) stack pointer near the top of the alt stack rather than the
|
|
700
|
+
// alt-stack base like Linux does, and `ss_size` is the full alt-stack size. Thus the linux version of the check
|
|
701
|
+
// didn't work for macOS.
|
|
702
|
+
// As an alternative, we query `sigaltstack()`; we save/restore errno since `sigaltstack()` may clobber it.
|
|
703
|
+
static bool is_running_on_alternate_signal_stack(DDTRACE_UNUSED void *ucontext) {
|
|
704
|
+
int old_errno = errno;
|
|
705
|
+
stack_t current_alt_stack;
|
|
706
|
+
bool on_alt_stack = sigaltstack(NULL, ¤t_alt_stack) == 0 && (current_alt_stack.ss_flags & SS_ONSTACK) != 0;
|
|
707
|
+
errno = old_errno;
|
|
708
|
+
return on_alt_stack;
|
|
709
|
+
}
|
|
710
|
+
#else
|
|
711
|
+
// NOTE: On Linux we use the alt-stack bounds from the `ucontext` rather than `sigaltstack()`, allowing us to avoid one syscall.
|
|
712
|
+
static bool is_running_on_alternate_signal_stack(void *ucontext) {
|
|
713
|
+
if (ucontext == NULL) return false;
|
|
714
|
+
|
|
715
|
+
stack_t alt_stack = ((ucontext_t *) ucontext)->uc_stack;
|
|
716
|
+
if (alt_stack.ss_size == 0) return false; // No alternate signal stack registered on this thread
|
|
717
|
+
|
|
718
|
+
char stack_local;
|
|
719
|
+
uintptr_t current_sp = (uintptr_t) &stack_local;
|
|
720
|
+
uintptr_t alt_stack_start = (uintptr_t) alt_stack.ss_sp;
|
|
721
|
+
|
|
722
|
+
return current_sp >= alt_stack_start && current_sp < alt_stack_start + alt_stack.ss_size;
|
|
723
|
+
}
|
|
724
|
+
#endif
|
|
725
|
+
|
|
569
726
|
// NOTE: Remember that this will run in the thread and within the scope of user code, including user C code.
|
|
570
727
|
// 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
|
|
571
728
|
// we do here can set `errno`, then we must be careful to restore the old `errno` after the fact.
|
|
572
|
-
static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info,
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
//
|
|
576
|
-
|
|
729
|
+
static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, void *ucontext) {
|
|
730
|
+
// If we're running on the alternate signal stack, we've interrupted another signal handler that's running
|
|
731
|
+
// there -- in practice, Ruby's GC compaction read-barrier handler.
|
|
732
|
+
// During GC compaction, Ruby protects pages containing Ruby objects, so many of the checks we do below are unsafe
|
|
733
|
+
// since they can touch those pages, and thus we just bail out here in this situation to avoid crashes.
|
|
734
|
+
if (is_running_on_alternate_signal_stack(ucontext)) {
|
|
735
|
+
global_stats.signal_handler_skipped_sample_on_altstack++;
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
577
738
|
|
|
739
|
+
// We must first check that we landed on the correct thread and can proceed.
|
|
740
|
+
// We must never touch the state before we confirm that we have landed on the thread that is holding the GVL on the main
|
|
741
|
+
// ractor as otherwise we may be concurrent with the profiler shutting down and removing its state.
|
|
578
742
|
if (
|
|
579
743
|
!ruby_native_thread_p() || // Not a Ruby thread
|
|
580
744
|
!is_current_thread_holding_the_gvl() || // Not safe to enqueue a sample from this thread
|
|
581
745
|
!ddtrace_rb_ractor_main_p() // We're not on the main Ractor; we currently don't support profiling non-main Ractors
|
|
582
|
-
)
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
746
|
+
) return;
|
|
747
|
+
|
|
748
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
749
|
+
|
|
750
|
+
// This can potentially happen if the CpuAndWallTimeWorker was stopped while the signal delivery was happening; nothing to do
|
|
751
|
+
if (state == NULL) return;
|
|
586
752
|
|
|
587
|
-
// We
|
|
588
|
-
// a) we get triggered using SIGPROF, and the docs state a second SIGPROF will not interrupt an existing one
|
|
753
|
+
// We assume there can be no concurrent nor nested calls to handle_sampling_signal because
|
|
754
|
+
// 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)
|
|
589
755
|
// 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
|
|
590
756
|
// because it will not have the global VM lock
|
|
591
|
-
|
|
592
|
-
//
|
|
593
|
-
// then we will not queue a second one. It does this by doing a linear scan on the existing jobs; in the future we
|
|
594
|
-
// may want to implement that check ourselves.
|
|
757
|
+
// c) `simulate_sampling_signal_delivery` calls us directly instead of via a real SIGPROF, and blocks SIGPROF delivery on its
|
|
758
|
+
// thread for the duration of that call, so it can't be nested into by a real SIGPROF either
|
|
595
759
|
|
|
596
760
|
state->stats.signal_handler_enqueued_sample++;
|
|
597
761
|
|
|
598
|
-
|
|
599
|
-
|
|
762
|
+
bool sample_from_signal_handler =
|
|
763
|
+
state->sighandler_sampling_enabled &&
|
|
764
|
+
// Don't sample if we're already in the middle of processing a sample
|
|
765
|
+
!state->during_sample;
|
|
766
|
+
|
|
767
|
+
if (sample_from_signal_handler) {
|
|
768
|
+
if (rb_during_gc()) {
|
|
769
|
+
// During GC Ruby might be marking our buffer or compacting (moving) where iseqs live so we skip preparing the
|
|
770
|
+
// sample immediately, and instead ask on_gc_finish to do the job when GC is at its end.
|
|
771
|
+
//
|
|
772
|
+
// When GC profiling is disabled, we just fall back to sampling the stack after GC finishes, e.g. it's the
|
|
773
|
+
// equivalent of disabling `sample_from_signal_handler` for samples that happen during GC.
|
|
774
|
+
thread_context_collector_request_prepare_on_gc_finish();
|
|
775
|
+
} else {
|
|
776
|
+
// Buffer current stack trace. Note that this will not actually record the sample, for that we still need to wait
|
|
777
|
+
// until the postponed job below gets run.
|
|
778
|
+
bool prepared = thread_context_collector_prepare_sample_inside_signal_handler();
|
|
779
|
+
|
|
780
|
+
if (prepared) state->stats.signal_handler_prepared_sample++;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
600
784
|
#ifndef NO_POSTPONED_TRIGGER // Ruby 3.3+
|
|
601
785
|
rb_postponed_job_trigger(sample_from_postponed_job_handle);
|
|
602
|
-
state->stats.postponed_job_success++; // Always succeeds
|
|
603
786
|
#else
|
|
604
|
-
|
|
605
|
-
// This is a workaround for https://bugs.ruby-lang.org/issues/19991 (for Ruby < 3.3)
|
|
787
|
+
// Passing in `gc_finalize_deferred_workaround` is a workaround for https://bugs.ruby-lang.org/issues/19991 (for Ruby < 3.3)
|
|
606
788
|
//
|
|
607
789
|
// TL;DR the `rb_postponed_job_register_one` API is not atomic (which is why it got replaced by `rb_postponed_job_trigger`)
|
|
608
790
|
// and in rare cases can cause VM crashes.
|
|
@@ -626,28 +808,15 @@ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED si
|
|
|
626
808
|
//
|
|
627
809
|
// Thus, our workaround is simple: we pass in objspace as our argument, just in case the clobbering happens.
|
|
628
810
|
// In the happy path, we never use this argument so it makes no difference. In the buggy path, we avoid crashing the VM.
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
// Officially, the result of rb_postponed_job_register_one is documented as being opaque, but in practice it does not
|
|
632
|
-
// seem to have changed between Ruby 2.3 and 3.2, and so we track it as a debugging mechanism
|
|
633
|
-
switch (result) {
|
|
634
|
-
case 0:
|
|
635
|
-
state->stats.postponed_job_full++; break;
|
|
636
|
-
case 1:
|
|
637
|
-
state->stats.postponed_job_success++; break;
|
|
638
|
-
case 2:
|
|
639
|
-
state->stats.postponed_job_skipped_already_existed++; break;
|
|
640
|
-
default:
|
|
641
|
-
state->stats.postponed_job_unknown_result++;
|
|
642
|
-
}
|
|
811
|
+
rb_postponed_job_register(0, sample_from_postponed_job, gc_finalize_deferred_workaround /* instead of NULL */);
|
|
643
812
|
#endif
|
|
644
813
|
}
|
|
645
814
|
|
|
646
815
|
// The actual sampling trigger loop always runs **without** the global vm lock.
|
|
647
816
|
static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
648
|
-
|
|
817
|
+
cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
|
|
649
818
|
|
|
650
|
-
uint64_t minimum_time_between_signals = MILLIS_AS_NS(
|
|
819
|
+
uint64_t minimum_time_between_signals = MILLIS_AS_NS(state->cpu_sampling_interval_ms);
|
|
651
820
|
|
|
652
821
|
while (atomic_load(&state->should_run)) {
|
|
653
822
|
state->stats.trigger_sample_attempts++;
|
|
@@ -669,6 +838,10 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
|
669
838
|
// we're doing this, so we may still not signal the correct thread from time to time, but our signal handler
|
|
670
839
|
// includes a check to see if it got called in the right thread
|
|
671
840
|
state->stats.interrupt_thread_attempts++;
|
|
841
|
+
|
|
842
|
+
// Pick up any last-minute attempts to stop before we send the signal
|
|
843
|
+
if (!atomic_load(&state->should_run)) return NULL;
|
|
844
|
+
|
|
672
845
|
pthread_kill(owner.owner, SIGPROF);
|
|
673
846
|
} else {
|
|
674
847
|
if (state->skip_idle_samples_for_testing) {
|
|
@@ -696,7 +869,10 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
|
696
869
|
// `dynamic_sampling_rate_get_sleep` may have changed while the above sleep was ongoing.
|
|
697
870
|
uint64_t extra_sleep =
|
|
698
871
|
dynamic_sampling_rate_get_sleep(&state->cpu_dynamic_sampling_rate, monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE));
|
|
699
|
-
if (state->dynamic_sampling_rate_enabled && extra_sleep > 0)
|
|
872
|
+
if (state->dynamic_sampling_rate_enabled && extra_sleep > 0) {
|
|
873
|
+
state->stats.trigger_sample_extra_sleep++;
|
|
874
|
+
sleep_for(extra_sleep);
|
|
875
|
+
}
|
|
700
876
|
}
|
|
701
877
|
|
|
702
878
|
return NULL; // Unused
|
|
@@ -704,13 +880,15 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
|
704
880
|
|
|
705
881
|
// This is called by the Ruby VM when it wants to shut down the background thread
|
|
706
882
|
static void interrupt_sampling_trigger_loop(void *state_ptr) {
|
|
707
|
-
|
|
883
|
+
cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
|
|
708
884
|
|
|
709
885
|
atomic_store(&state->should_run, false);
|
|
710
886
|
}
|
|
711
887
|
|
|
888
|
+
// Note: If we ever want to get rid of the postponed job execution, remember not to clobber Ruby exceptions, as
|
|
889
|
+
// this function does this helpful job for us now -- https://github.com/ruby/ruby/commit/a98e343d39c4d7bf1e2190b076720f32d9f298b3.
|
|
712
890
|
static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
713
|
-
|
|
891
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
714
892
|
|
|
715
893
|
// This can potentially happen if the CpuAndWallTimeWorker was stopped while the postponed job was waiting to be executed; nothing to do
|
|
716
894
|
if (state == NULL) return;
|
|
@@ -721,29 +899,40 @@ static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
|
721
899
|
return; // We're not on the main Ractor; we currently don't support profiling non-main Ractors
|
|
722
900
|
}
|
|
723
901
|
|
|
724
|
-
state
|
|
902
|
+
during_sample_enter(state);
|
|
725
903
|
|
|
726
904
|
// Rescue against any exceptions that happen during sampling
|
|
727
|
-
safely_call(
|
|
905
|
+
VALUE needs_otel_span_key = safely_call(
|
|
906
|
+
rescued_sample_from_postponed_job,
|
|
907
|
+
state->self_instance,
|
|
908
|
+
state->self_instance,
|
|
909
|
+
handle_sampling_failure_rescued_sample_from_postponed_job
|
|
910
|
+
);
|
|
728
911
|
|
|
729
|
-
state
|
|
912
|
+
during_sample_exit(state);
|
|
913
|
+
|
|
914
|
+
// Extracting the otel span key can lose the GVL, so we move it outside `during_sample`
|
|
915
|
+
// (It can't raise: it rescues its own exceptions)
|
|
916
|
+
if (needs_otel_span_key == Qtrue) {
|
|
917
|
+
thread_context_collector_resolve_otel_span_key_may_lose_gvl(state->thread_context_collector_instance);
|
|
918
|
+
}
|
|
730
919
|
}
|
|
731
920
|
|
|
732
921
|
static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
|
|
733
|
-
|
|
734
|
-
TypedData_Get_Struct(self_instance,
|
|
922
|
+
cpu_and_wall_time_worker_state *state;
|
|
923
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
735
924
|
|
|
736
925
|
long wall_time_ns_before_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
737
926
|
|
|
738
927
|
if (state->dynamic_sampling_rate_enabled && !dynamic_sampling_rate_should_sample(&state->cpu_dynamic_sampling_rate, wall_time_ns_before_sample)) {
|
|
739
928
|
state->stats.cpu_skipped++;
|
|
740
|
-
return
|
|
929
|
+
return Qfalse;
|
|
741
930
|
}
|
|
742
931
|
|
|
743
932
|
state->stats.cpu_sampled++;
|
|
744
933
|
|
|
745
|
-
|
|
746
|
-
|
|
934
|
+
bool needs_otel_span_key =
|
|
935
|
+
thread_context_collector_sample(state->thread_context_collector_instance, wall_time_ns_before_sample);
|
|
747
936
|
|
|
748
937
|
long wall_time_ns_after_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
749
938
|
long delta_ns = wall_time_ns_after_sample - wall_time_ns_before_sample;
|
|
@@ -757,13 +946,7 @@ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
|
|
|
757
946
|
|
|
758
947
|
dynamic_sampling_rate_after_sample(&state->cpu_dynamic_sampling_rate, wall_time_ns_after_sample, sampling_time_ns);
|
|
759
948
|
|
|
760
|
-
|
|
761
|
-
return Qnil;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
static VALUE handle_sampling_failure(VALUE self_instance, VALUE exception) {
|
|
765
|
-
stop(self_instance, exception);
|
|
766
|
-
return Qnil;
|
|
949
|
+
return needs_otel_span_key ? Qtrue : Qfalse;
|
|
767
950
|
}
|
|
768
951
|
|
|
769
952
|
// This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
|
|
@@ -786,10 +969,10 @@ static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self) {
|
|
|
786
969
|
}
|
|
787
970
|
|
|
788
971
|
static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
|
|
789
|
-
|
|
790
|
-
TypedData_Get_Struct(instance,
|
|
972
|
+
cpu_and_wall_time_worker_state *state;
|
|
973
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
791
974
|
|
|
792
|
-
// Final preparations: Setup signal handler and enable
|
|
975
|
+
// Final preparations: Setup signal handler and enable hooks. We run these here and not in `_native_sampling_loop`
|
|
793
976
|
// because they may raise exceptions.
|
|
794
977
|
install_sigprof_signal_handler(handle_sampling_signal, "handle_sampling_signal");
|
|
795
978
|
if (state->gc_profiling_enabled) rb_tracepoint_enable(state->gc_tracepoint);
|
|
@@ -804,22 +987,19 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
|
|
|
804
987
|
|
|
805
988
|
if (state->gvl_profiling_enabled) {
|
|
806
989
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
807
|
-
#ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS
|
|
808
|
-
gvl_profiling_state_thread_tracking_workaround();
|
|
809
|
-
#endif
|
|
810
|
-
|
|
811
990
|
state->gvl_profiling_hook = rb_internal_thread_add_event_hook(
|
|
812
991
|
on_gvl_event,
|
|
813
992
|
(
|
|
814
993
|
// For now we're only asking for these events, even though there's more
|
|
815
994
|
// (e.g. check docs or gvl-tracing gem)
|
|
995
|
+
RUBY_INTERNAL_THREAD_EVENT_SUSPENDED | /* released gvl */
|
|
816
996
|
RUBY_INTERNAL_THREAD_EVENT_READY /* waiting for gvl */ |
|
|
817
997
|
RUBY_INTERNAL_THREAD_EVENT_RESUMED /* running/runnable */
|
|
818
998
|
),
|
|
819
999
|
NULL
|
|
820
1000
|
);
|
|
821
1001
|
#else
|
|
822
|
-
|
|
1002
|
+
raise_error(rb_eArgError, "GVL profiling is not supported in this Ruby version");
|
|
823
1003
|
#endif
|
|
824
1004
|
}
|
|
825
1005
|
|
|
@@ -831,17 +1011,28 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
|
|
|
831
1011
|
// If we stopped sampling due to an exception, re-raise it (now in the worker thread)
|
|
832
1012
|
if (state->failure_exception != Qnil) rb_exc_raise(state->failure_exception);
|
|
833
1013
|
|
|
1014
|
+
thread_context_collector_profiler_internal_thread_done(state->thread_context_collector_instance);
|
|
1015
|
+
|
|
834
1016
|
return Qnil;
|
|
835
1017
|
}
|
|
836
1018
|
|
|
837
1019
|
// This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
|
|
838
1020
|
// It SHOULD NOT be used for other purposes.
|
|
839
1021
|
static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
840
|
-
|
|
1022
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
841
1023
|
|
|
842
1024
|
return (state != NULL && is_thread_alive(state->owner_thread) && state->self_instance == instance) ? Qtrue : Qfalse;
|
|
843
1025
|
}
|
|
844
1026
|
|
|
1027
|
+
static VALUE _native_failure_exception_during_operation(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
1028
|
+
cpu_and_wall_time_worker_state *state;
|
|
1029
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1030
|
+
|
|
1031
|
+
if (state->failure_exception_during_operation == NULL) return Qnil;
|
|
1032
|
+
|
|
1033
|
+
return rb_str_new_cstr(state->failure_exception_during_operation);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
845
1036
|
static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext) {
|
|
846
1037
|
/* Does nothing on purpose */
|
|
847
1038
|
}
|
|
@@ -860,6 +1051,28 @@ static VALUE _native_remove_testing_signal_handler(DDTRACE_UNUSED VALUE self) {
|
|
|
860
1051
|
return Qtrue;
|
|
861
1052
|
}
|
|
862
1053
|
|
|
1054
|
+
// Reproduces a profiler sample being taken in the middle of GC compaction: during compaction a SIGPROF can be
|
|
1055
|
+
// delivered while nested inside Ruby's read-barrier signal handler, which runs on the alternate signal stack. We
|
|
1056
|
+
// simulate that here by re-installing the production `handle_sampling_signal` with the `SA_ONSTACK` flag, so it runs
|
|
1057
|
+
// on the alternate signal stack just like it would in that scenario.
|
|
1058
|
+
//
|
|
1059
|
+
// The `SA_ONSTACK` will be undone by any next `replace_sigprof_signal_handler_with_empty_handler` /
|
|
1060
|
+
// `install_sigprof_signal_handler_internal` / `remove_sigprof_signal_handle` so this doesn't need a specific, symmetric
|
|
1061
|
+
// `uninstall` to undo this.
|
|
1062
|
+
static VALUE _native_install_sigprof_handler_on_altstack(DDTRACE_UNUSED VALUE self) {
|
|
1063
|
+
struct sigaction signal_handler_config = {
|
|
1064
|
+
.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK,
|
|
1065
|
+
.sa_sigaction = handle_sampling_signal,
|
|
1066
|
+
};
|
|
1067
|
+
sigemptyset(&signal_handler_config.sa_mask);
|
|
1068
|
+
|
|
1069
|
+
if (sigaction(SIGPROF, &signal_handler_config, NULL) != 0) {
|
|
1070
|
+
rb_sys_fail("Failed to install SA_ONSTACK SIGPROF signal handler for testing");
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
return Qtrue;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
863
1076
|
// This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
|
|
864
1077
|
// It SHOULD NOT be used for other purposes.
|
|
865
1078
|
static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self) {
|
|
@@ -870,8 +1083,8 @@ static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self) {
|
|
|
870
1083
|
// This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
|
|
871
1084
|
// It SHOULD NOT be used for other purposes.
|
|
872
1085
|
static VALUE _native_gc_tracepoint(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
873
|
-
|
|
874
|
-
TypedData_Get_Struct(instance,
|
|
1086
|
+
cpu_and_wall_time_worker_state *state;
|
|
1087
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
875
1088
|
|
|
876
1089
|
return state->gc_tracepoint;
|
|
877
1090
|
}
|
|
@@ -897,7 +1110,7 @@ static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused) {
|
|
|
897
1110
|
int event = rb_tracearg_event_flag(rb_tracearg_from_tracepoint(tracepoint_data));
|
|
898
1111
|
if (event != RUBY_INTERNAL_EVENT_GC_ENTER && event != RUBY_INTERNAL_EVENT_GC_EXIT) return; // Unknown event
|
|
899
1112
|
|
|
900
|
-
|
|
1113
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
901
1114
|
|
|
902
1115
|
// This should not happen in a normal situation because the tracepoint is always enabled after the instance is set
|
|
903
1116
|
// and disabled before it is cleared, but just in case...
|
|
@@ -906,7 +1119,10 @@ static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused) {
|
|
|
906
1119
|
if (event == RUBY_INTERNAL_EVENT_GC_ENTER) {
|
|
907
1120
|
thread_context_collector_on_gc_start(state->thread_context_collector_instance);
|
|
908
1121
|
} else if (event == RUBY_INTERNAL_EVENT_GC_EXIT) {
|
|
909
|
-
bool
|
|
1122
|
+
bool allow_prepare_sample = !state->during_sample;
|
|
1123
|
+
if (allow_prepare_sample) during_sample_enter(state);
|
|
1124
|
+
bool should_flush = thread_context_collector_on_gc_finish(state->thread_context_collector_instance, allow_prepare_sample);
|
|
1125
|
+
if (allow_prepare_sample) during_sample_exit(state);
|
|
910
1126
|
|
|
911
1127
|
// We use rb_postponed_job_register_one to ask Ruby to run thread_context_collector_sample_after_gc when the
|
|
912
1128
|
// thread collector flags it's time to flush.
|
|
@@ -921,7 +1137,7 @@ static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused) {
|
|
|
921
1137
|
}
|
|
922
1138
|
|
|
923
1139
|
static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
924
|
-
|
|
1140
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
925
1141
|
|
|
926
1142
|
// This can potentially happen if the CpuAndWallTimeWorker was stopped while the postponed job was waiting to be executed; nothing to do
|
|
927
1143
|
if (state == NULL) return;
|
|
@@ -932,16 +1148,35 @@ static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
|
932
1148
|
return; // We're not on the main Ractor; we currently don't support profiling non-main Ractors
|
|
933
1149
|
}
|
|
934
1150
|
|
|
935
|
-
state
|
|
1151
|
+
during_sample_enter(state);
|
|
936
1152
|
|
|
937
|
-
safely_call(
|
|
1153
|
+
safely_call(
|
|
1154
|
+
thread_context_collector_sample_after_gc,
|
|
1155
|
+
state->thread_context_collector_instance,
|
|
1156
|
+
state->self_instance,
|
|
1157
|
+
handle_sampling_failure_thread_context_collector_sample_after_gc
|
|
1158
|
+
);
|
|
938
1159
|
|
|
939
|
-
state
|
|
1160
|
+
during_sample_exit(state);
|
|
1161
|
+
|
|
1162
|
+
// This part runs separately from above because it may lose the GVL and we don't want `during_sample` to be set in
|
|
1163
|
+
// such a situation
|
|
1164
|
+
safely_call(
|
|
1165
|
+
thread_context_collector_heap_update_may_lose_gvl,
|
|
1166
|
+
state->thread_context_collector_instance,
|
|
1167
|
+
state->self_instance,
|
|
1168
|
+
handle_sampling_failure_thread_context_collector_heap_update
|
|
1169
|
+
);
|
|
940
1170
|
}
|
|
941
1171
|
|
|
942
1172
|
// Equivalent to Ruby begin/rescue call, where we call a C function and jump to the exception handler if an
|
|
943
1173
|
// exception gets raised within
|
|
944
|
-
static VALUE safely_call(
|
|
1174
|
+
static VALUE safely_call(
|
|
1175
|
+
VALUE (*function_to_call_safely)(VALUE),
|
|
1176
|
+
VALUE function_to_call_safely_arg,
|
|
1177
|
+
VALUE instance,
|
|
1178
|
+
VALUE (*handle_sampling_failure)(VALUE, VALUE)
|
|
1179
|
+
) {
|
|
945
1180
|
VALUE exception_handler_function_arg = instance;
|
|
946
1181
|
return rb_rescue2(
|
|
947
1182
|
function_to_call_safely,
|
|
@@ -969,18 +1204,21 @@ static VALUE _native_simulate_sample_from_postponed_job(DDTRACE_UNUSED VALUE sel
|
|
|
969
1204
|
|
|
970
1205
|
// After the Ruby VM forks, this method gets called in the child process to clean up any leftover state from the parent.
|
|
971
1206
|
//
|
|
972
|
-
// Assumption: This method gets called BEFORE restarting profiling. Note that profiling-related
|
|
1207
|
+
// Assumption: This method gets called BEFORE restarting profiling. Note that profiling-related hooks may still
|
|
973
1208
|
// be active, so we make sure to disable them before calling into anything else, so that there are no components
|
|
974
1209
|
// attempting to trigger samples at the same time as the reset is done.
|
|
975
1210
|
//
|
|
976
|
-
// In the future, if we add more other components with
|
|
977
|
-
//
|
|
1211
|
+
// In the future, if we add more other components with hooks, we will need to coordinate stopping all such
|
|
1212
|
+
// hooks before doing the other cleaning steps.
|
|
1213
|
+
//
|
|
1214
|
+
// Note that tests call this method directly in the same process without forking,
|
|
1215
|
+
// and in such a case non-current Threads keep running.
|
|
978
1216
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
979
|
-
|
|
980
|
-
TypedData_Get_Struct(instance,
|
|
1217
|
+
cpu_and_wall_time_worker_state *state;
|
|
1218
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
981
1219
|
|
|
982
|
-
// Disable all
|
|
983
|
-
|
|
1220
|
+
// Disable all hooks, so that there are no more attempts to mutate the profile
|
|
1221
|
+
disable_hooks(state);
|
|
984
1222
|
|
|
985
1223
|
reset_stats_not_thread_safe(state);
|
|
986
1224
|
|
|
@@ -995,8 +1233,8 @@ static VALUE _native_is_sigprof_blocked_in_current_thread(DDTRACE_UNUSED VALUE s
|
|
|
995
1233
|
}
|
|
996
1234
|
|
|
997
1235
|
static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
998
|
-
|
|
999
|
-
TypedData_Get_Struct(instance,
|
|
1236
|
+
cpu_and_wall_time_worker_state *state;
|
|
1237
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1000
1238
|
|
|
1001
1239
|
unsigned long total_cpu_samples_attempted = state->stats.cpu_sampled + state->stats.cpu_skipped;
|
|
1002
1240
|
VALUE effective_cpu_sample_rate =
|
|
@@ -1011,14 +1249,12 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
|
1011
1249
|
VALUE stats_as_hash = rb_hash_new();
|
|
1012
1250
|
VALUE arguments[] = {
|
|
1013
1251
|
ID2SYM(rb_intern("trigger_sample_attempts")), /* => */ UINT2NUM(state->stats.trigger_sample_attempts),
|
|
1252
|
+
ID2SYM(rb_intern("trigger_sample_extra_sleep")), /* => */ UINT2NUM(state->stats.trigger_sample_extra_sleep),
|
|
1014
1253
|
ID2SYM(rb_intern("trigger_simulated_signal_delivery_attempts")), /* => */ UINT2NUM(state->stats.trigger_simulated_signal_delivery_attempts),
|
|
1015
1254
|
ID2SYM(rb_intern("simulated_signal_delivery")), /* => */ UINT2NUM(state->stats.simulated_signal_delivery),
|
|
1016
1255
|
ID2SYM(rb_intern("signal_handler_enqueued_sample")), /* => */ UINT2NUM(state->stats.signal_handler_enqueued_sample),
|
|
1017
|
-
ID2SYM(rb_intern("
|
|
1018
|
-
ID2SYM(rb_intern("
|
|
1019
|
-
ID2SYM(rb_intern("postponed_job_success")), /* => */ UINT2NUM(state->stats.postponed_job_success),
|
|
1020
|
-
ID2SYM(rb_intern("postponed_job_full")), /* => */ UINT2NUM(state->stats.postponed_job_full),
|
|
1021
|
-
ID2SYM(rb_intern("postponed_job_unknown_result")), /* => */ UINT2NUM(state->stats.postponed_job_unknown_result),
|
|
1256
|
+
ID2SYM(rb_intern("signal_handler_prepared_sample")), /* => */ UINT2NUM(state->stats.signal_handler_prepared_sample),
|
|
1257
|
+
ID2SYM(rb_intern("signal_handler_skipped_sample_on_altstack")), /* => */ UINT2NUM(global_stats.signal_handler_skipped_sample_on_altstack),
|
|
1022
1258
|
ID2SYM(rb_intern("interrupt_thread_attempts")), /* => */ UINT2NUM(state->stats.interrupt_thread_attempts),
|
|
1023
1259
|
|
|
1024
1260
|
// CPU Stats
|
|
@@ -1042,47 +1278,69 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
|
1042
1278
|
ID2SYM(rb_intern("allocations_during_sample")), /* => */ state->allocation_profiling_enabled ? UINT2NUM(state->stats.allocations_during_sample) : Qnil,
|
|
1043
1279
|
|
|
1044
1280
|
// GVL profiling stats
|
|
1045
|
-
ID2SYM(rb_intern("after_gvl_running")),
|
|
1281
|
+
ID2SYM(rb_intern("after_gvl_running")), /* => */ UINT2NUM(state->stats.after_gvl_running),
|
|
1282
|
+
ID2SYM(rb_intern("gvl_dont_sample")), /* => */ UINT2NUM(state->stats.gvl_dont_sample),
|
|
1283
|
+
ID2SYM(rb_intern("gvl_sampling_time_ns_min")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_min, != UINT64_MAX, ULL2NUM),
|
|
1284
|
+
ID2SYM(rb_intern("gvl_sampling_time_ns_max")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_max, > 0, ULL2NUM),
|
|
1285
|
+
ID2SYM(rb_intern("gvl_sampling_time_ns_total")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_total, > 0, ULL2NUM),
|
|
1286
|
+
ID2SYM(rb_intern("gvl_sampling_time_ns_avg")), /* => */ RUBY_AVG_OR_NIL(state->stats.gvl_sampling_time_ns_total, state->stats.after_gvl_running),
|
|
1287
|
+
ID2SYM(rb_intern("gvl_waiting_time_ns_total")), /* => */ state->gvl_profiling_enabled ? ULL2NUM(state->stats.vm_metrics.gvl_waiting_time_ns_total) : Qnil,
|
|
1046
1288
|
};
|
|
1047
1289
|
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(stats_as_hash, arguments[i], arguments[i+1]);
|
|
1290
|
+
|
|
1291
|
+
thread_context_collector_stats(state->thread_context_collector_instance, stats_as_hash);
|
|
1292
|
+
|
|
1048
1293
|
return stats_as_hash;
|
|
1049
1294
|
}
|
|
1050
1295
|
|
|
1051
1296
|
static VALUE _native_stats_reset_not_thread_safe(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
1052
|
-
|
|
1053
|
-
TypedData_Get_Struct(instance,
|
|
1297
|
+
cpu_and_wall_time_worker_state *state;
|
|
1298
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1054
1299
|
reset_stats_not_thread_safe(state);
|
|
1300
|
+
thread_context_collector_stats_reset_not_thread_safe(state->thread_context_collector_instance);
|
|
1055
1301
|
return Qnil;
|
|
1056
1302
|
}
|
|
1057
1303
|
|
|
1058
1304
|
void *simulate_sampling_signal_delivery(DDTRACE_UNUSED void *_unused) {
|
|
1059
|
-
|
|
1305
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1060
1306
|
|
|
1061
1307
|
// This can potentially happen if the CpuAndWallTimeWorker was stopped while the IdleSamplingHelper was trying to execute this action
|
|
1062
1308
|
if (state == NULL) return NULL;
|
|
1063
1309
|
|
|
1310
|
+
// Since this is not a real signal firing, we need to block SIGPROF delivery on this thread to avoid an actual SIGPROF
|
|
1311
|
+
// signal coming in nested and interrupting us on this thread. Thus we respect the invariant of "no nesting" for `handle_sampling_signal`.
|
|
1312
|
+
//
|
|
1313
|
+
// Not needed when `no_signals_workaround_enabled` is set: no SIGPROFs are ever sent in that mode, so there's nothing to mask.
|
|
1314
|
+
if (!state->no_signals_workaround_enabled) block_sigprof_signal_handler_from_running_in_current_thread();
|
|
1315
|
+
|
|
1064
1316
|
state->stats.simulated_signal_delivery++;
|
|
1065
1317
|
|
|
1066
|
-
//
|
|
1067
|
-
// so that the simulated case is as close to the original one as well (including any metrics increases, etc).
|
|
1318
|
+
// `handle_sampling_signal` does a few things extra on top of `sample_from_postponed_job` so that's why we don't shortcut here
|
|
1068
1319
|
handle_sampling_signal(0, NULL, NULL);
|
|
1069
1320
|
|
|
1321
|
+
if (!state->no_signals_workaround_enabled) unblock_sigprof_signal_handler_from_running_in_current_thread();
|
|
1322
|
+
|
|
1070
1323
|
return NULL; // Unused
|
|
1071
1324
|
}
|
|
1072
1325
|
|
|
1073
1326
|
static void grab_gvl_and_sample(void) { rb_thread_call_with_gvl(simulate_sampling_signal_delivery, NULL); }
|
|
1074
1327
|
|
|
1075
|
-
static void reset_stats_not_thread_safe(
|
|
1328
|
+
static void reset_stats_not_thread_safe(cpu_and_wall_time_worker_state *state) {
|
|
1076
1329
|
// NOTE: This is not really thread safe so ongoing sampling operations that are concurrent with a reset can have their stats:
|
|
1077
1330
|
// * Lost (writes after stats retrieval but before reset).
|
|
1078
1331
|
// * Included in the previous stats window (writes before stats retrieval and reset).
|
|
1079
1332
|
// * Included in the following stats window (writes after stats retrieval and reset).
|
|
1080
1333
|
// Given the expected infrequency of resetting (~once per 60s profile) and the auxiliary/non-critical nature of these stats
|
|
1081
1334
|
// this momentary loss of accuracy is deemed acceptable to keep overhead to a minimum.
|
|
1335
|
+
|
|
1082
1336
|
state->stats = (struct stats) {
|
|
1083
|
-
|
|
1084
|
-
.
|
|
1337
|
+
// All these values are initialized to their highest value possible since we always take the min between existing and latest sample
|
|
1338
|
+
.cpu_sampling_time_ns_min = UINT64_MAX,
|
|
1339
|
+
.allocation_sampling_time_ns_min = UINT64_MAX,
|
|
1340
|
+
.gvl_sampling_time_ns_min = UINT64_MAX,
|
|
1341
|
+
// Other fields are reset to 0
|
|
1085
1342
|
};
|
|
1343
|
+
global_stats = (global_stats_t) {0};
|
|
1086
1344
|
}
|
|
1087
1345
|
|
|
1088
1346
|
static void sleep_for(uint64_t time_ns) {
|
|
@@ -1104,7 +1362,7 @@ static void sleep_for(uint64_t time_ns) {
|
|
|
1104
1362
|
}
|
|
1105
1363
|
|
|
1106
1364
|
static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
|
|
1107
|
-
|
|
1365
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state;
|
|
1108
1366
|
|
|
1109
1367
|
bool are_allocations_being_tracked = state != NULL && state->allocation_profiling_enabled && state->allocation_counting_enabled;
|
|
1110
1368
|
|
|
@@ -1114,7 +1372,7 @@ static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
|
|
|
1114
1372
|
#define HANDLE_CLOCK_FAILURE(call) ({ \
|
|
1115
1373
|
long _result = (call); \
|
|
1116
1374
|
if (_result == 0) { \
|
|
1117
|
-
|
|
1375
|
+
delayed_error_clock_failure(state); \
|
|
1118
1376
|
return; \
|
|
1119
1377
|
} \
|
|
1120
1378
|
_result; \
|
|
@@ -1123,6 +1381,9 @@ static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
|
|
|
1123
1381
|
// Implements memory-related profiling events. This function is called by Ruby via the `rb_add_event_hook2`
|
|
1124
1382
|
// when the RUBY_INTERNAL_EVENT_NEWOBJ event is triggered.
|
|
1125
1383
|
//
|
|
1384
|
+
// This function is called from the RUBY_INTERNAL_EVENT_NEWOBJ tracepoint so it should neither allocate in the
|
|
1385
|
+
// Ruby heap nor release the GVL (https://github.com/DataDog/dd-trace-rb/pull/4240).
|
|
1386
|
+
//
|
|
1126
1387
|
// When allocation sampling is enabled, this function gets called for almost all* objects allocated by the Ruby VM.
|
|
1127
1388
|
// (*In some weird cases the VM may skip this tracepoint.)
|
|
1128
1389
|
//
|
|
@@ -1137,7 +1398,7 @@ static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
|
|
|
1137
1398
|
// call `rb_tracearg_from_tracepoint(anything)` anywhere during this function or its callees to get the data, so that's
|
|
1138
1399
|
// why it's not being passed as an argument.
|
|
1139
1400
|
static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *unused2) {
|
|
1140
|
-
|
|
1401
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1141
1402
|
|
|
1142
1403
|
// This should not happen in a normal situation because the tracepoint is always enabled after the instance is set
|
|
1143
1404
|
// and disabled before it is cleared, but just in case...
|
|
@@ -1159,6 +1420,25 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
|
|
|
1159
1420
|
return;
|
|
1160
1421
|
}
|
|
1161
1422
|
|
|
1423
|
+
VALUE current_thread = rb_thread_current();
|
|
1424
|
+
|
|
1425
|
+
// If Ruby is in the middle of raising an exception, we don't want to try to sample. This is because if we accidentally
|
|
1426
|
+
// trigger an exception inside the profiler code, bad things will happen (specifically, Ruby will try to kill off the
|
|
1427
|
+
// thread even though we may try to catch the exception).
|
|
1428
|
+
//
|
|
1429
|
+
// Note that "in the middle of raising an exception" means the exception itself has already been allocated.
|
|
1430
|
+
// What's getting allocated now is probably the backtrace objects (@ivoanjo or at least that's what I've observed)
|
|
1431
|
+
if (is_raised_flag_set(current_thread)) {
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
per_thread_context *thread_context = get_per_thread_context(current_thread);
|
|
1436
|
+
if (!thread_context) {
|
|
1437
|
+
// Context is created eagerly via on_thread_begin_event, so this should not normally be NULL.
|
|
1438
|
+
// We keep the guard since we can't allocate here (inside on_newobj_event).
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1162
1442
|
// Hot path: Dynamic sampling rate is usually enabled and the sampling decision is usually false
|
|
1163
1443
|
if (RB_LIKELY(state->dynamic_sampling_rate_enabled && !discrete_dynamic_sampler_should_sample(&state->allocation_sampler))) {
|
|
1164
1444
|
state->stats.allocation_skipped++;
|
|
@@ -1185,19 +1465,20 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
|
|
|
1185
1465
|
&state->allocation_sampler, HANDLE_CLOCK_FAILURE(monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE))
|
|
1186
1466
|
);
|
|
1187
1467
|
|
|
1188
|
-
|
|
1189
|
-
// invocation is still pending, (e.g. it wouldn't call `on_newobj_event` while it's already running), but I decided
|
|
1190
|
-
// to keep this here for consistency -- every call to the thread context (other than the special gc calls which are
|
|
1191
|
-
// defined as not being able to allocate) sets this.
|
|
1192
|
-
state->during_sample = true;
|
|
1468
|
+
during_sample_enter(state);
|
|
1193
1469
|
|
|
1194
1470
|
// Rescue against any exceptions that happen during sampling
|
|
1195
|
-
safely_call(
|
|
1471
|
+
safely_call(
|
|
1472
|
+
rescued_sample_allocation,
|
|
1473
|
+
(VALUE) thread_context,
|
|
1474
|
+
state->self_instance,
|
|
1475
|
+
handle_sampling_failure_rescued_sample_allocation
|
|
1476
|
+
);
|
|
1196
1477
|
|
|
1197
1478
|
if (state->dynamic_sampling_rate_enabled) {
|
|
1198
1479
|
long now = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
|
|
1199
1480
|
if (now == 0) {
|
|
1200
|
-
|
|
1481
|
+
delayed_error_clock_failure(state);
|
|
1201
1482
|
// NOTE: Not short-circuiting here to make sure cleanup happens
|
|
1202
1483
|
}
|
|
1203
1484
|
uint64_t sampling_time_ns = discrete_dynamic_sampler_after_sample(&state->allocation_sampler, now);
|
|
@@ -1210,10 +1491,10 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
|
|
|
1210
1491
|
|
|
1211
1492
|
state->stats.allocation_sampled++;
|
|
1212
1493
|
|
|
1213
|
-
state
|
|
1494
|
+
during_sample_exit(state);
|
|
1214
1495
|
}
|
|
1215
1496
|
|
|
1216
|
-
static void
|
|
1497
|
+
static void disable_hooks(cpu_and_wall_time_worker_state *state) {
|
|
1217
1498
|
if (state->gc_tracepoint != Qnil) {
|
|
1218
1499
|
rb_tracepoint_disable(state->gc_tracepoint);
|
|
1219
1500
|
}
|
|
@@ -1241,8 +1522,9 @@ static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self) {
|
|
|
1241
1522
|
}
|
|
1242
1523
|
}
|
|
1243
1524
|
|
|
1244
|
-
static VALUE rescued_sample_allocation(
|
|
1245
|
-
|
|
1525
|
+
static VALUE rescued_sample_allocation(VALUE arg) {
|
|
1526
|
+
per_thread_context *thread_context = (per_thread_context*) arg;
|
|
1527
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1246
1528
|
|
|
1247
1529
|
// This should not happen in a normal situation because on_newobj_event already checked for this, but just in case...
|
|
1248
1530
|
if (state == NULL) return Qnil;
|
|
@@ -1260,27 +1542,40 @@ static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {
|
|
|
1260
1542
|
// To control bias from sampling, we clamp the maximum weight attributed to a single allocation sample. This avoids
|
|
1261
1543
|
// assigning a very large number to a sample, if for instance the dynamic sampling mechanism chose a really big interval.
|
|
1262
1544
|
unsigned int weight = allocations_since_last_sample > MAX_ALLOC_WEIGHT ? MAX_ALLOC_WEIGHT : (unsigned int) allocations_since_last_sample;
|
|
1263
|
-
thread_context_collector_sample_allocation(state->thread_context_collector_instance, weight, new_object);
|
|
1545
|
+
bool needs_commit = thread_context_collector_sample_allocation(state->thread_context_collector_instance, thread_context, weight, new_object);
|
|
1264
1546
|
// ...but we still represent the skipped samples in the profile, thus the data will account for all allocations.
|
|
1265
1547
|
if (weight < allocations_since_last_sample) {
|
|
1266
1548
|
uint32_t skipped_samples = (uint32_t) uint64_min_of(allocations_since_last_sample - weight, UINT32_MAX);
|
|
1267
1549
|
thread_context_collector_sample_skipped_allocation_samples(state->thread_context_collector_instance, skipped_samples);
|
|
1268
1550
|
}
|
|
1269
1551
|
|
|
1552
|
+
if (needs_commit) {
|
|
1553
|
+
#ifndef NO_POSTPONED_TRIGGER
|
|
1554
|
+
rb_postponed_job_trigger(commit_heap_recordings_from_postponed_job_may_lose_gvl_handle);
|
|
1555
|
+
#else
|
|
1556
|
+
rb_postponed_job_register_one(0, commit_heap_recordings_from_postponed_job_may_lose_gvl, NULL);
|
|
1557
|
+
#endif
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1270
1560
|
// Return a dummy VALUE because we're called from rb_rescue2 which requires it
|
|
1271
1561
|
return Qnil;
|
|
1272
1562
|
}
|
|
1273
1563
|
|
|
1274
|
-
static void delayed_error(
|
|
1564
|
+
static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error) {
|
|
1565
|
+
// If we can't raise an immediate exception at the calling site, use the asynchronous flow through the main worker loop.
|
|
1566
|
+
stop_state(state, rb_exc_new_cstr(rb_eRuntimeError, error), "delayed_error");
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state) {
|
|
1275
1570
|
// If we can't raise an immediate exception at the calling site, use the asynchronous flow through the main worker loop.
|
|
1276
|
-
stop_state(state, rb_exc_new_cstr(
|
|
1571
|
+
stop_state(state, rb_exc_new_cstr(clock_failure_exception_class, "failed to get clock time"), "delayed_error_clock_failure");
|
|
1277
1572
|
}
|
|
1278
1573
|
|
|
1279
1574
|
static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg) {
|
|
1280
1575
|
ENFORCE_TYPE(error_msg, T_STRING);
|
|
1281
1576
|
|
|
1282
|
-
|
|
1283
|
-
TypedData_Get_Struct(instance,
|
|
1577
|
+
cpu_and_wall_time_worker_state *state;
|
|
1578
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1284
1579
|
|
|
1285
1580
|
delayed_error(state, rb_string_value_cstr(&error_msg));
|
|
1286
1581
|
|
|
@@ -1304,64 +1599,210 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
|
|
|
1304
1599
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
1305
1600
|
static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused) {
|
|
1306
1601
|
// Be very careful about touching the `state` here or doing anything at all:
|
|
1307
|
-
// This function gets called without the GVL, and potentially from
|
|
1602
|
+
// This function gets called without the GVL, and potentially from non-main Ractors!
|
|
1308
1603
|
//
|
|
1309
|
-
//
|
|
1310
|
-
//
|
|
1311
|
-
|
|
1604
|
+
// Note, even though these events can get called without the GVL, they synchronize-with enabling/disabling of
|
|
1605
|
+
// the hook that calls us using a read-write lock. Thus, disabling the hook cannot be concurrent with calling this function,
|
|
1606
|
+
// and once the disable finishes there can't be "late" calls into this function.
|
|
1607
|
+
|
|
1608
|
+
// The thread that this event is about may not be the current thread
|
|
1609
|
+
// (as documented on rb_internal_thread_add_event_hook(), and this is notably the case for READY on Ruby 4.0),
|
|
1610
|
+
// so be careful with native thread locals that are not directly tied to the thread object and the like.
|
|
1611
|
+
|
|
1612
|
+
#ifdef HAVE_RUBY_THREAD_STORAGE_API
|
|
1613
|
+
VALUE target_thread = event_data->thread;
|
|
1614
|
+
#else
|
|
1615
|
+
// On Ruby 3.2 the event does not carry the thread, but all events fire on the thread itself.
|
|
1616
|
+
// However, during early thread startup rb_thread_current() can crash because the execution context (Fiber) isn't
|
|
1617
|
+
// stored in TLS yet; ruby_native_thread_p() guards against this.
|
|
1618
|
+
if (!ruby_native_thread_p()) return;
|
|
1619
|
+
VALUE target_thread = rb_thread_current();
|
|
1620
|
+
#endif
|
|
1621
|
+
|
|
1622
|
+
per_thread_context* thread_context = get_per_thread_context(target_thread);
|
|
1623
|
+
if (!thread_context) return;
|
|
1624
|
+
// If non-NULL the thread is profiled and from the main Ractor
|
|
1312
1625
|
|
|
1313
|
-
if (event_id ==
|
|
1314
|
-
|
|
1626
|
+
if (event_id == RUBY_INTERNAL_THREAD_EVENT_SUSPENDED) { /* released gvl */
|
|
1627
|
+
thread_context_collector_on_gvl_released(thread_context);
|
|
1628
|
+
} else if (event_id == RUBY_INTERNAL_THREAD_EVENT_READY) { /* waiting for gvl */
|
|
1629
|
+
thread_context_collector_on_gvl_waiting(thread_context);
|
|
1315
1630
|
} else if (event_id == RUBY_INTERNAL_THREAD_EVENT_RESUMED) { /* running/runnable */
|
|
1316
|
-
//
|
|
1317
|
-
//
|
|
1318
|
-
//
|
|
1319
|
-
// it
|
|
1320
|
-
//
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1631
|
+
// We must only use async-signal-safe functions here and not call arbitrary Ruby APIs and not allocate!
|
|
1632
|
+
// One might assume RUBY_INTERNAL_THREAD_EVENT_RESUMED means having the GVL and running that thread.
|
|
1633
|
+
// However, the reality is more complicated (https://bugs.ruby-lang.org/issues/22098),
|
|
1634
|
+
// it only "sort of" has the GVL but not fully, and it's called while holding the scheduler lock,
|
|
1635
|
+
// so we do as little work as possible here, and perform the sample in a postponed_job.
|
|
1636
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1637
|
+
if (state == NULL) return; // This should not happen, but just in case...
|
|
1638
|
+
|
|
1639
|
+
// on_gvl_running prepares a stack sample so we need to gate it with `during_sample_enter` to avoid a
|
|
1640
|
+
// SIGPROF signal coming in during that function and potentially causing a corrupted final state.
|
|
1641
|
+
//
|
|
1642
|
+
// TODO: Currently, the sample prepared in on_gvl_running can still be clobbered if the signal handler runs
|
|
1643
|
+
// after `during_sample_exit` but before `after_gvl_running_from_postponed_job` gets to run. We'll need to fix
|
|
1644
|
+
// that next.
|
|
1645
|
+
during_sample_enter(state);
|
|
1646
|
+
|
|
1647
|
+
on_gvl_running_result result =
|
|
1648
|
+
thread_context_collector_on_gvl_running(target_thread, thread_context, state->waiting_for_gvl_threshold_ns);
|
|
1325
1649
|
|
|
1326
|
-
|
|
1650
|
+
during_sample_exit(state);
|
|
1327
1651
|
|
|
1328
|
-
if (
|
|
1329
|
-
|
|
1652
|
+
if (result.waiting_for_gvl_duration_ns > 0) {
|
|
1653
|
+
state->stats.vm_metrics.gvl_waiting_time_ns_total += (uint64_t) result.waiting_for_gvl_duration_ns;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
if (result.action == ON_GVL_RUNNING_SAMPLE) {
|
|
1330
1657
|
#ifndef NO_POSTPONED_TRIGGER
|
|
1331
1658
|
rb_postponed_job_trigger(after_gvl_running_from_postponed_job_handle);
|
|
1332
1659
|
#else
|
|
1333
1660
|
rb_postponed_job_register_one(0, after_gvl_running_from_postponed_job, NULL);
|
|
1334
1661
|
#endif
|
|
1662
|
+
} else if (result.action == ON_GVL_RUNNING_DONT_SAMPLE) {
|
|
1663
|
+
state->stats.gvl_dont_sample++;
|
|
1335
1664
|
}
|
|
1336
1665
|
} else {
|
|
1337
|
-
|
|
1338
|
-
fprintf(stderr, "[ddtrace] Unexpected value in on_gvl_event (%d)\n", event_id);
|
|
1666
|
+
rb_bug("[ddtrace] Unexpected value in on_gvl_event (%d)\n", event_id);
|
|
1339
1667
|
}
|
|
1340
1668
|
}
|
|
1341
1669
|
|
|
1342
1670
|
static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
1343
|
-
|
|
1671
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1344
1672
|
|
|
1345
1673
|
// This can potentially happen if the CpuAndWallTimeWorker was stopped while the postponed job was waiting to be executed; nothing to do
|
|
1346
1674
|
if (state == NULL) return;
|
|
1347
1675
|
|
|
1348
|
-
state
|
|
1676
|
+
during_sample_enter(state);
|
|
1677
|
+
|
|
1678
|
+
// Rescue against any exceptions that happen during sampling
|
|
1679
|
+
safely_call(
|
|
1680
|
+
rescued_after_gvl_running_from_postponed_job,
|
|
1681
|
+
state->self_instance,
|
|
1682
|
+
state->self_instance,
|
|
1683
|
+
handle_sampling_failure_rescued_after_gvl_running_from_postponed_job
|
|
1684
|
+
);
|
|
1685
|
+
|
|
1686
|
+
during_sample_exit(state);
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance) {
|
|
1690
|
+
cpu_and_wall_time_worker_state *state;
|
|
1691
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1692
|
+
|
|
1693
|
+
long wall_time_ns_before_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
1694
|
+
thread_context_collector_sample_after_gvl_running(state->thread_context_collector_instance, rb_thread_current(), wall_time_ns_before_sample);
|
|
1695
|
+
long wall_time_ns_after_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
1349
1696
|
|
|
1350
|
-
|
|
1697
|
+
long delta_ns = wall_time_ns_after_sample - wall_time_ns_before_sample;
|
|
1698
|
+
|
|
1699
|
+
// Guard against wall-time going backwards, see https://github.com/DataDog/dd-trace-rb/pull/2336 for discussion.
|
|
1700
|
+
uint64_t sampling_time_ns = delta_ns < 0 ? 0 : delta_ns;
|
|
1701
|
+
|
|
1702
|
+
state->stats.gvl_sampling_time_ns_min = uint64_min_of(sampling_time_ns, state->stats.gvl_sampling_time_ns_min);
|
|
1703
|
+
state->stats.gvl_sampling_time_ns_max = uint64_max_of(sampling_time_ns, state->stats.gvl_sampling_time_ns_max);
|
|
1704
|
+
state->stats.gvl_sampling_time_ns_total += sampling_time_ns;
|
|
1351
1705
|
|
|
1352
1706
|
state->stats.after_gvl_running++;
|
|
1353
1707
|
|
|
1354
|
-
|
|
1708
|
+
return Qnil;
|
|
1355
1709
|
}
|
|
1356
1710
|
|
|
1357
1711
|
static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
1358
|
-
|
|
1359
|
-
TypedData_Get_Struct(instance,
|
|
1712
|
+
cpu_and_wall_time_worker_state *state;
|
|
1713
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1360
1714
|
|
|
1361
1715
|
return state->gvl_profiling_hook != NULL ? Qtrue : Qfalse;
|
|
1362
1716
|
}
|
|
1717
|
+
|
|
1718
|
+
static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception) {
|
|
1719
|
+
stop(self_instance, exception, "rescued_after_gvl_running_from_postponed_job");
|
|
1720
|
+
return Qnil;
|
|
1721
|
+
}
|
|
1363
1722
|
#else
|
|
1364
1723
|
static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, DDTRACE_UNUSED VALUE instance) {
|
|
1365
1724
|
return Qfalse;
|
|
1366
1725
|
}
|
|
1367
1726
|
#endif
|
|
1727
|
+
|
|
1728
|
+
static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception) {
|
|
1729
|
+
stop(self_instance, exception, "rescued_sample_from_postponed_job");
|
|
1730
|
+
return Qnil;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception) {
|
|
1734
|
+
stop(self_instance, exception, "thread_context_collector_sample_after_gc");
|
|
1735
|
+
return Qnil;
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
static VALUE handle_sampling_failure_thread_context_collector_heap_update(VALUE self_instance, VALUE exception) {
|
|
1739
|
+
stop(self_instance, exception, "thread_context_collector_heap_update_may_lose_gvl");
|
|
1740
|
+
return Qnil;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception) {
|
|
1744
|
+
stop(self_instance, exception, "rescued_sample_allocation");
|
|
1745
|
+
return Qnil;
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
static VALUE handle_sampling_failure_rescued_commit_heap_recordings(VALUE self_instance, VALUE exception) {
|
|
1749
|
+
stop(self_instance, exception, "rescued_commit_heap_recordings_may_lose_gvl");
|
|
1750
|
+
return Qnil;
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
static VALUE rescued_commit_heap_recordings_may_lose_gvl(VALUE self_instance) {
|
|
1754
|
+
cpu_and_wall_time_worker_state *state;
|
|
1755
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1756
|
+
|
|
1757
|
+
thread_context_collector_commit_heap_recordings_may_lose_gvl(state->thread_context_collector_instance);
|
|
1758
|
+
|
|
1759
|
+
// Return a dummy VALUE because we're called from rb_rescue2 which requires it
|
|
1760
|
+
return Qnil;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
// This postponed job callback is used to commit heap allocation recordings.
|
|
1764
|
+
// During on_newobj_event, we can't take the weak reference the heap recorder needs to track the object, so we defer
|
|
1765
|
+
// that until after the event completes.
|
|
1766
|
+
static void commit_heap_recordings_from_postponed_job_may_lose_gvl(DDTRACE_UNUSED void *_unused) {
|
|
1767
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state;
|
|
1768
|
+
|
|
1769
|
+
if (state == NULL || !ddtrace_rb_ractor_main_p()) return;
|
|
1770
|
+
|
|
1771
|
+
if (state->during_sample) {
|
|
1772
|
+
delayed_error(state, "commit_heap_recordings_from_postponed_job_may_lose_gvl called during_sample");
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
// NOTE: We're not updating the allocation_sampler here.
|
|
1777
|
+
// This means work done in this function isn't accounted for as profiler overhead.
|
|
1778
|
+
// This is acceptable as the amount of work done here is expected to be small.
|
|
1779
|
+
safely_call(
|
|
1780
|
+
rescued_commit_heap_recordings_may_lose_gvl,
|
|
1781
|
+
state->self_instance,
|
|
1782
|
+
state->self_instance,
|
|
1783
|
+
handle_sampling_failure_rescued_commit_heap_recordings
|
|
1784
|
+
);
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
static inline void during_sample_enter(cpu_and_wall_time_worker_state* state) {
|
|
1788
|
+
// Tell the compiler it's not allowed to reorder the `during_sample` flag with anything that happens after.
|
|
1789
|
+
//
|
|
1790
|
+
// In a few cases, we may be checking this flag from a signal handler, so we need to make sure the compiler didn't
|
|
1791
|
+
// get clever and reordered things in such a way that makes us miss the flag update.
|
|
1792
|
+
//
|
|
1793
|
+
// See https://github.com/ruby/ruby/pull/11036 for a similar change made to the Ruby VM with more context.
|
|
1794
|
+
state->during_sample = true;
|
|
1795
|
+
atomic_signal_fence(memory_order_seq_cst);
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
static inline void during_sample_exit(cpu_and_wall_time_worker_state* state) {
|
|
1799
|
+
// See `during_sample_enter` for more context; in this case we set the fence before to make sure anything that
|
|
1800
|
+
// happens before the fence is not reordered with the flag update.
|
|
1801
|
+
atomic_signal_fence(memory_order_seq_cst);
|
|
1802
|
+
state->during_sample = false;
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
static VALUE during_sample_exit_rescue(VALUE state_ptr) {
|
|
1806
|
+
during_sample_exit((cpu_and_wall_time_worker_state *) state_ptr);
|
|
1807
|
+
return Qnil;
|
|
1808
|
+
}
|