datadog 2.17.0 → 2.37.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 +686 -1
- data/README.md +0 -1
- data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
- data/ext/datadog_profiling_native_extension/clock_id.h +9 -1
- 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 +344 -126
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +4 -4
- data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +3 -2
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +18 -4
- data/ext/datadog_profiling_native_extension/collectors_stack.c +314 -91
- data/ext/datadog_profiling_native_extension/collectors_stack.h +26 -3
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +680 -352
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +18 -5
- data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +65 -1
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +40 -1
- data/ext/datadog_profiling_native_extension/encoded_profile.c +2 -1
- data/ext/datadog_profiling_native_extension/extconf.rb +48 -34
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +4 -43
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +15 -47
- data/ext/datadog_profiling_native_extension/heap_recorder.c +455 -430
- data/ext/datadog_profiling_native_extension/heap_recorder.h +16 -7
- data/ext/datadog_profiling_native_extension/http_transport.c +66 -68
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +18 -15
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +8 -10
- data/ext/datadog_profiling_native_extension/macos_sampler_thread.h +55 -0
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +0 -13
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +73 -69
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +10 -4
- data/ext/datadog_profiling_native_extension/profiling.c +65 -19
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +64 -59
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +20 -16
- data/ext/datadog_profiling_native_extension/setup_signal_handler.c +32 -10
- data/ext/datadog_profiling_native_extension/setup_signal_handler.h +2 -1
- data/ext/datadog_profiling_native_extension/stack_recorder.c +253 -131
- data/ext/datadog_profiling_native_extension/stack_recorder.h +3 -1
- data/ext/datadog_profiling_native_extension/time_helpers.h +1 -0
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +2 -1
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +9 -4
- data/ext/libdatadog_api/crashtracker.c +16 -15
- data/ext/libdatadog_api/crashtracker_report_exception.c +126 -0
- data/ext/libdatadog_api/datadog_ruby_common.c +65 -1
- data/ext/libdatadog_api/datadog_ruby_common.h +40 -1
- data/ext/libdatadog_api/ddsketch.c +102 -0
- data/ext/libdatadog_api/di.c +203 -0
- data/ext/libdatadog_api/extconf.rb +21 -25
- data/ext/libdatadog_api/feature_flags.c +554 -0
- data/ext/libdatadog_api/feature_flags.h +5 -0
- data/ext/libdatadog_api/helpers.h +27 -0
- data/ext/libdatadog_api/init.c +13 -1
- data/ext/libdatadog_api/library_config.c +88 -37
- data/ext/libdatadog_api/library_config.h +6 -0
- data/ext/libdatadog_api/process_discovery.c +25 -24
- data/ext/libdatadog_extconf_helpers.rb +107 -14
- data/lib/datadog/ai_guard/api_client.rb +84 -0
- data/lib/datadog/ai_guard/autoload.rb +10 -0
- data/lib/datadog/ai_guard/component.rb +44 -0
- data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
- data/lib/datadog/ai_guard/configuration.rb +115 -0
- data/lib/datadog/ai_guard/contrib/auto_instrument.rb +24 -0
- data/lib/datadog/ai_guard/contrib/integration.rb +37 -0
- data/lib/datadog/ai_guard/contrib/rack/integration.rb +42 -0
- data/lib/datadog/ai_guard/contrib/rack/patcher.rb +26 -0
- data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +97 -0
- data/lib/datadog/ai_guard/contrib/rails/integration.rb +41 -0
- data/lib/datadog/ai_guard/contrib/rails/patcher.rb +97 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +80 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +41 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +30 -0
- data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
- data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
- data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
- data/lib/datadog/ai_guard/evaluation/no_op_result.rb +36 -0
- data/lib/datadog/ai_guard/evaluation/request.rb +86 -0
- data/lib/datadog/ai_guard/evaluation/result.rb +45 -0
- data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
- data/lib/datadog/ai_guard/evaluation.rb +108 -0
- data/lib/datadog/ai_guard/ext.rb +29 -0
- data/lib/datadog/ai_guard.rb +181 -0
- data/lib/datadog/appsec/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 +14 -0
- data/lib/datadog/appsec/assets/blocked.html +10 -1
- data/lib/datadog/appsec/assets/blocked.json +1 -1
- data/lib/datadog/appsec/assets/blocked.text +3 -1
- data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +601 -74
- data/lib/datadog/appsec/assets/waf_rules/strict.json +48 -75
- data/lib/datadog/appsec/assets.rb +1 -1
- data/lib/datadog/appsec/autoload.rb +2 -2
- data/lib/datadog/appsec/component.rb +37 -64
- data/lib/datadog/appsec/compressed_json.rb +3 -3
- data/lib/datadog/appsec/configuration.rb +427 -1
- data/lib/datadog/appsec/context.rb +77 -16
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
- data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/active_record/patcher.rb +4 -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 +144 -0
- data/lib/datadog/appsec/contrib/devise/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +4 -2
- data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +2 -1
- data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +1 -1
- data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/excon/patcher.rb +3 -1
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +152 -13
- data/lib/datadog/appsec/contrib/faraday/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/faraday/patcher.rb +1 -1
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +138 -13
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +74 -24
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +4 -2
- data/lib/datadog/appsec/contrib/graphql/integration.rb +2 -1
- data/lib/datadog/appsec/contrib/rack/buffered_input.rb +83 -0
- data/lib/datadog/appsec/contrib/rack/ext.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +47 -13
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +40 -19
- data/lib/datadog/appsec/contrib/rack/input_peeker.rb +79 -0
- data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +80 -62
- data/lib/datadog/appsec/contrib/rack/response_body.rb +36 -0
- data/lib/datadog/appsec/contrib/rails/gateway/request.rb +35 -1
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +47 -5
- data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/patcher.rb +52 -26
- 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/rest_client/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rest_client/patcher.rb +3 -1
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +167 -12
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +63 -10
- data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +11 -21
- data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +31 -0
- data/lib/datadog/appsec/counter_sampler.rb +25 -0
- data/lib/datadog/appsec/default_header_tags.rb +52 -0
- data/lib/datadog/appsec/event.rb +14 -47
- data/lib/datadog/appsec/ext.rb +3 -0
- data/lib/datadog/appsec/instrumentation/gateway/argument.rb +17 -1
- data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +2 -3
- data/lib/datadog/appsec/instrumentation/gateway.rb +2 -15
- data/lib/datadog/appsec/metrics/collector.rb +47 -5
- data/lib/datadog/appsec/metrics/exporter.rb +16 -3
- data/lib/datadog/appsec/metrics/telemetry.rb +21 -4
- data/lib/datadog/appsec/metrics/telemetry_exporter.rb +47 -0
- data/lib/datadog/appsec/metrics.rb +5 -4
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +8 -6
- data/lib/datadog/appsec/processor/rule_loader.rb +5 -6
- data/lib/datadog/appsec/remote.rb +51 -76
- data/lib/datadog/appsec/response.rb +18 -4
- 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 +45 -9
- data/lib/datadog/appsec/security_engine/runner.rb +44 -21
- data/lib/datadog/appsec/security_event.rb +5 -7
- 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 +38 -0
- data/lib/datadog/appsec/utils/http/body_reader.rb +61 -0
- data/lib/datadog/appsec/utils/http/media_range.rb +2 -1
- data/lib/datadog/appsec/utils/http/media_type.rb +32 -26
- data/lib/datadog/appsec/utils/http/url_encoded.rb +52 -0
- data/lib/datadog/appsec.rb +10 -17
- data/lib/datadog/auto_instrument_base.rb +2 -1
- data/lib/datadog/core/configuration/agent_settings.rb +52 -0
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +5 -47
- data/lib/datadog/core/configuration/base.rb +17 -5
- data/lib/datadog/core/configuration/components.rb +192 -20
- data/lib/datadog/core/configuration/components_state.rb +6 -1
- 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 +0 -1
- data/lib/datadog/core/configuration/option.rb +95 -65
- data/lib/datadog/core/configuration/option_definition.rb +42 -23
- data/lib/datadog/core/configuration/options.rb +57 -18
- data/lib/datadog/core/configuration/settings.rb +160 -25
- data/lib/datadog/core/configuration/stable_config.rb +11 -2
- data/lib/datadog/core/configuration/supported_configurations.rb +413 -0
- data/lib/datadog/core/configuration.rb +4 -4
- data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
- data/lib/datadog/core/contrib/rails/utils.rb +7 -3
- data/lib/datadog/core/crashtracking/component.rb +73 -21
- data/lib/datadog/core/crashtracking/tag_builder.rb +9 -21
- data/lib/datadog/core/ddsketch.rb +19 -0
- data/lib/datadog/core/deprecations.rb +2 -2
- data/lib/datadog/core/diagnostics/environment_logger.rb +3 -1
- data/lib/datadog/core/environment/agent_info.rb +65 -1
- data/lib/datadog/core/environment/cgroup.rb +52 -25
- data/lib/datadog/core/environment/container.rb +140 -46
- data/lib/datadog/core/environment/ext.rb +13 -2
- data/lib/datadog/core/environment/git.rb +2 -2
- data/lib/datadog/core/environment/identity.rb +40 -4
- data/lib/datadog/core/environment/process.rb +122 -0
- data/lib/datadog/core/environment/socket.rb +13 -0
- data/lib/datadog/core/environment/variable_helpers.rb +3 -3
- data/lib/datadog/core/environment/yjit.rb +2 -1
- data/lib/datadog/core/error.rb +6 -6
- data/lib/datadog/core/evp.rb +11 -0
- data/lib/datadog/core/feature_flags.rb +61 -0
- data/lib/datadog/core/knuth_sampler.rb +57 -0
- data/lib/datadog/core/logger.rb +1 -1
- data/lib/datadog/core/metrics/client.rb +7 -7
- data/lib/datadog/core/metrics/logging.rb +1 -1
- data/lib/datadog/core/pin.rb +8 -8
- data/lib/datadog/core/process_discovery/tracer_memfd.rb +13 -0
- data/lib/datadog/core/process_discovery.rb +51 -21
- data/lib/datadog/core/rate_limiter.rb +11 -1
- data/lib/datadog/core/remote/client/capabilities.rb +49 -6
- data/lib/datadog/core/remote/client.rb +15 -7
- data/lib/datadog/core/remote/component.rb +64 -36
- data/lib/datadog/core/remote/configuration/content.rb +15 -2
- data/lib/datadog/core/remote/configuration/digest.rb +14 -7
- data/lib/datadog/core/remote/configuration/repository.rb +13 -1
- data/lib/datadog/core/remote/configuration/target.rb +13 -6
- data/lib/datadog/core/remote/transport/config.rb +9 -36
- data/lib/datadog/core/remote/transport/http/config.rb +15 -55
- data/lib/datadog/core/remote/transport/http/negotiation.rb +14 -44
- data/lib/datadog/core/remote/transport/http.rb +15 -24
- data/lib/datadog/core/remote/transport/negotiation.rb +8 -33
- data/lib/datadog/core/remote/worker.rb +25 -37
- data/lib/datadog/core/runtime/ext.rb +0 -1
- data/lib/datadog/core/runtime/metrics.rb +11 -2
- data/lib/datadog/core/semaphore.rb +1 -4
- data/lib/datadog/core/tag_builder.rb +52 -0
- data/lib/datadog/core/tag_normalizer.rb +84 -0
- data/lib/datadog/core/telemetry/component.rb +65 -17
- data/lib/datadog/core/telemetry/emitter.rb +6 -6
- data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +3 -3
- 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_started.rb +203 -89
- data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
- data/lib/datadog/core/telemetry/event.rb +2 -7
- data/lib/datadog/core/telemetry/ext.rb +1 -0
- data/lib/datadog/core/telemetry/logger.rb +5 -2
- data/lib/datadog/core/telemetry/logging.rb +22 -4
- data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
- data/lib/datadog/core/telemetry/request.rb +17 -5
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +8 -34
- data/lib/datadog/core/telemetry/transport/http.rb +21 -16
- data/lib/datadog/core/telemetry/transport/telemetry.rb +8 -17
- data/lib/datadog/core/telemetry/worker.rb +108 -32
- data/lib/datadog/core/transport/ext.rb +3 -0
- data/lib/datadog/core/transport/http/adapters/net.rb +17 -2
- data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
- data/lib/datadog/core/transport/http/api/instance.rb +4 -21
- data/lib/datadog/core/transport/http/builder.rb +11 -7
- 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 +4 -0
- data/lib/datadog/core/transport/http.rb +24 -19
- data/lib/datadog/core/transport/parcel.rb +61 -9
- data/lib/datadog/core/transport/response.rb +16 -2
- data/lib/datadog/core/transport/transport.rb +90 -0
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +1 -1
- data/lib/datadog/core/utils/{base64.rb → base64_codec.rb} +4 -3
- 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 +3 -1
- data/lib/datadog/core/utils/hash.rb +0 -23
- data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -14
- data/lib/datadog/core/utils/network.rb +22 -1
- data/lib/datadog/core/utils/only_once.rb +1 -1
- data/lib/datadog/core/utils/only_once_successful.rb +8 -2
- data/lib/datadog/core/utils/safe_dup.rb +2 -2
- data/lib/datadog/core/utils/sequence.rb +2 -0
- data/lib/datadog/core/utils/spawn_monkey_patch.rb +68 -0
- data/lib/datadog/core/utils/time.rb +1 -1
- data/lib/datadog/core/utils.rb +10 -1
- data/lib/datadog/core/workers/async.rb +11 -2
- data/lib/datadog/core/workers/interval_loop.rb +51 -3
- data/lib/datadog/core/workers/polling.rb +2 -0
- data/lib/datadog/core/workers/queue.rb +96 -1
- data/lib/datadog/core/workers/runtime_metrics.rb +9 -1
- data/lib/datadog/core.rb +11 -2
- data/lib/datadog/data_streams/configuration.rb +50 -0
- data/lib/datadog/data_streams/ext.rb +11 -0
- data/lib/datadog/data_streams/extensions.rb +16 -0
- data/lib/datadog/data_streams/pathway_context.rb +169 -0
- data/lib/datadog/data_streams/processor.rb +511 -0
- data/lib/datadog/data_streams/transport/http/stats.rb +58 -0
- data/lib/datadog/data_streams/transport/http.rb +36 -0
- data/lib/datadog/data_streams/transport/stats.rb +39 -0
- data/lib/datadog/data_streams.rb +100 -0
- data/lib/datadog/di/base.rb +15 -7
- data/lib/datadog/di/boot.rb +11 -1
- data/lib/datadog/di/code_tracker.rb +217 -15
- data/lib/datadog/di/component.rb +143 -48
- data/lib/datadog/di/configuration.rb +235 -2
- data/lib/datadog/di/context.rb +76 -0
- data/lib/datadog/di/contrib/active_record.rb +34 -5
- data/lib/datadog/di/el/compiler.rb +168 -0
- data/lib/datadog/di/el/evaluator.rb +159 -0
- data/lib/datadog/di/el/expression.rb +42 -0
- data/lib/datadog/di/el.rb +5 -0
- data/lib/datadog/di/error.rb +44 -0
- data/lib/datadog/di/fatal_exceptions.rb +26 -0
- data/lib/datadog/di/instrumenter.rb +620 -132
- data/lib/datadog/di/logger.rb +2 -2
- data/lib/datadog/di/probe.rb +78 -15
- data/lib/datadog/di/probe_builder.rb +42 -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 +195 -100
- data/lib/datadog/di/probe_notification_builder.rb +285 -100
- data/lib/datadog/di/probe_notifier_worker.rb +120 -36
- 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 -2
- data/lib/datadog/di/remote.rb +258 -95
- data/lib/datadog/di/serializer.rb +297 -20
- data/lib/datadog/di/transport/diagnostics.rb +12 -42
- data/lib/datadog/di/transport/http/diagnostics.rb +4 -34
- data/lib/datadog/di/transport/http/input.rb +12 -34
- data/lib/datadog/di/transport/http.rb +43 -19
- data/lib/datadog/di/transport/input.rb +121 -39
- data/lib/datadog/di/utils.rb +42 -14
- data/lib/datadog/di.rb +203 -10
- data/lib/datadog/error_tracking/collector.rb +2 -1
- data/lib/datadog/error_tracking/component.rb +2 -2
- data/lib/datadog/error_tracking/configuration.rb +55 -2
- data/lib/datadog/error_tracking/filters.rb +2 -2
- data/lib/datadog/kit/appsec/events/v2.rb +253 -0
- data/lib/datadog/kit/appsec/events.rb +11 -10
- data/lib/datadog/kit/enable_core_dumps.rb +1 -1
- data/lib/datadog/kit/identity.rb +17 -11
- data/lib/datadog/kit/tracing/method_tracer.rb +135 -0
- data/lib/datadog/open_feature/component.rb +100 -0
- data/lib/datadog/open_feature/configuration.rb +37 -0
- data/lib/datadog/open_feature/evaluation_engine.rb +70 -0
- data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
- data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
- data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
- data/lib/datadog/open_feature/exposures/event.rb +60 -0
- data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
- data/lib/datadog/open_feature/exposures/worker.rb +116 -0
- data/lib/datadog/open_feature/ext.rb +16 -0
- data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +286 -0
- data/lib/datadog/open_feature/flag_evaluation/writer.rb +433 -0
- data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +101 -0
- data/lib/datadog/open_feature/hooks/flag_eval_metrics_hook.rb +49 -0
- data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
- data/lib/datadog/open_feature/native_evaluator.rb +60 -0
- data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
- data/lib/datadog/open_feature/provider.rb +191 -0
- data/lib/datadog/open_feature/remote.rb +67 -0
- data/lib/datadog/open_feature/resolution_details.rb +35 -0
- data/lib/datadog/open_feature/transport.rb +121 -0
- data/lib/datadog/open_feature.rb +19 -0
- data/lib/datadog/opentelemetry/api/baggage.rb +3 -3
- data/lib/datadog/opentelemetry/api/context.rb +10 -9
- 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 +66 -2
- 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 +13 -7
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +12 -9
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +14 -10
- data/lib/datadog/opentelemetry/sdk.rb +19 -0
- data/lib/datadog/opentelemetry/signal_configuration.rb +53 -0
- data/lib/datadog/opentelemetry/trace.rb +4 -4
- data/lib/datadog/opentelemetry.rb +4 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +83 -15
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +52 -3
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +13 -4
- data/lib/datadog/profiling/collectors/info.rb +59 -4
- data/lib/datadog/profiling/collectors/thread_context.rb +17 -5
- data/lib/datadog/profiling/component.rb +73 -49
- data/lib/datadog/profiling/exporter.rb +48 -13
- 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 -15
- data/lib/datadog/profiling/flush.rb +23 -11
- data/lib/datadog/profiling/http_transport.rb +24 -11
- data/lib/datadog/profiling/load_native_extension.rb +2 -2
- data/lib/datadog/profiling/profiler.rb +31 -12
- data/lib/datadog/profiling/scheduler.rb +12 -10
- data/lib/datadog/profiling/sequence_tracker.rb +44 -0
- data/lib/datadog/profiling/stack_recorder.rb +5 -13
- data/lib/datadog/profiling/tag_builder.rb +42 -38
- data/lib/datadog/profiling/tasks/exec.rb +10 -5
- data/lib/datadog/profiling/tasks/help.rb +1 -0
- data/lib/datadog/profiling/tasks/setup.rb +4 -2
- data/lib/datadog/profiling.rb +8 -11
- data/lib/datadog/ruby_version.rb +25 -0
- data/lib/datadog/single_step_instrument.rb +10 -1
- data/lib/datadog/symbol_database/component.rb +734 -0
- data/lib/datadog/symbol_database/configuration.rb +73 -0
- data/lib/datadog/symbol_database/extensions.rb +19 -0
- data/lib/datadog/symbol_database/extractor.rb +1110 -0
- data/lib/datadog/symbol_database/file_hash.rb +48 -0
- data/lib/datadog/symbol_database/logger.rb +43 -0
- data/lib/datadog/symbol_database/remote.rb +180 -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 +1 -1
- data/lib/datadog/tracing/buffer.rb +10 -10
- data/lib/datadog/tracing/component.rb +17 -17
- data/lib/datadog/tracing/configuration/dynamic.rb +6 -8
- data/lib/datadog/tracing/configuration/ext.rb +25 -5
- data/lib/datadog/tracing/configuration/settings.rb +125 -11
- data/lib/datadog/tracing/context.rb +2 -2
- data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +4 -1
- data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +4 -1
- data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +4 -1
- data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +4 -1
- data/lib/datadog/tracing/contrib/action_cable/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -1
- data/lib/datadog/tracing/contrib/action_mailer/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +29 -11
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +47 -12
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +3 -1
- data/lib/datadog/tracing/contrib/action_pack/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/action_pack/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
- data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +4 -1
- data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +6 -3
- data/lib/datadog/tracing/contrib/active_job/event.rb +8 -8
- data/lib/datadog/tracing/contrib/active_job/events/discard.rb +6 -3
- data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +6 -3
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +6 -3
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +6 -3
- data/lib/datadog/tracing/contrib/active_job/events/perform.rb +6 -3
- data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +6 -3
- data/lib/datadog/tracing/contrib/active_job/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
- data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +2 -2
- data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +4 -4
- data/lib/datadog/tracing/contrib/active_model_serializers/integration.rb +1 -2
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +3 -3
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +2 -2
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +7 -10
- data/lib/datadog/tracing/contrib/active_record/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/utils.rb +16 -16
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +15 -12
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +1 -4
- data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +13 -0
- data/lib/datadog/tracing/contrib/active_support/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +2 -1
- data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +8 -10
- data/lib/datadog/tracing/contrib/aws/ext.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +8 -10
- data/lib/datadog/tracing/contrib/aws/parsed_context.rb +3 -1
- data/lib/datadog/tracing/contrib/aws/patcher.rb +5 -1
- data/lib/datadog/tracing/contrib/aws/service/base.rb +2 -1
- data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/service/s3.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/service/sns.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/service/sqs.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/service/states.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/services.rb +7 -7
- data/lib/datadog/tracing/contrib/component.rb +3 -3
- data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +1 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +1 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +1 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +1 -1
- data/lib/datadog/tracing/contrib/configurable.rb +22 -7
- data/lib/datadog/tracing/contrib/configuration/resolver.rb +14 -4
- data/lib/datadog/tracing/contrib/configuration/resolvers/pattern_resolver.rb +4 -4
- data/lib/datadog/tracing/contrib/dalli/ext.rb +3 -2
- data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +1 -5
- data/lib/datadog/tracing/contrib/dalli/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/dalli/quantize.rb +1 -1
- data/lib/datadog/tracing/contrib/delayed_job/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +2 -0
- data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +1 -0
- data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +3 -2
- data/lib/datadog/tracing/contrib/elasticsearch/integration.rb +4 -4
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +47 -53
- data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +7 -7
- data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +7 -8
- data/lib/datadog/tracing/contrib/ethon/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/ethon/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +1 -8
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +16 -5
- data/lib/datadog/tracing/contrib/excon/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/excon/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/excon/middleware.rb +5 -9
- data/lib/datadog/tracing/contrib/ext.rb +6 -4
- data/lib/datadog/tracing/contrib/extensions.rb +28 -11
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +16 -9
- data/lib/datadog/tracing/contrib/faraday/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/faraday/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +7 -9
- data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +18 -15
- data/lib/datadog/tracing/contrib/grape/instrumentation.rb +13 -8
- data/lib/datadog/tracing/contrib/grape/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/grape/patcher.rb +6 -1
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
- data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/graphql/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/graphql/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +105 -63
- data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +11 -15
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +6 -10
- data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +1 -1
- data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/grpc/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/grpc.rb +1 -0
- data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/hanami/ext.rb +2 -2
- data/lib/datadog/tracing/contrib/hanami/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +2 -1
- data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +10 -11
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +16 -5
- data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +4 -4
- data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/http/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +9 -13
- data/lib/datadog/tracing/contrib/http/integration.rb +0 -2
- data/lib/datadog/tracing/contrib/http.rb +1 -0
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +16 -5
- data/lib/datadog/tracing/contrib/httpclient/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +10 -10
- data/lib/datadog/tracing/contrib/httpclient/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +16 -5
- data/lib/datadog/tracing/contrib/httprb/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +8 -12
- data/lib/datadog/tracing/contrib/httprb/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/event.rb +2 -1
- data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/join_group.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/leave_group.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/sync_group.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +1 -1
- 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 +1 -1
- data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
- data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/karafka/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
- data/lib/datadog/tracing/contrib/karafka/monitor.rb +24 -13
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +39 -8
- data/lib/datadog/tracing/contrib/karafka.rb +1 -0
- data/lib/datadog/tracing/contrib/lograge/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/lograge/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +4 -2
- data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -1
- data/lib/datadog/tracing/contrib/mongodb/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/mongodb/parsers.rb +6 -6
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +11 -14
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +6 -0
- data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -1
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +19 -12
- data/lib/datadog/tracing/contrib/mysql2/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/opensearch/ext.rb +3 -2
- data/lib/datadog/tracing/contrib/opensearch/integration.rb +1 -2
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +64 -70
- data/lib/datadog/tracing/contrib/opensearch/quantize.rb +7 -7
- data/lib/datadog/tracing/contrib/patcher.rb +7 -9
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +6 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +3 -6
- data/lib/datadog/tracing/contrib/pg/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/presto/ext.rb +1 -1
- data/lib/datadog/tracing/contrib/presto/instrumentation.rb +9 -11
- data/lib/datadog/tracing/contrib/presto/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +1 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +13 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +6 -2
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +25 -1
- data/lib/datadog/tracing/contrib/que/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/que/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/que/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/que/tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/racecar/event.rb +2 -6
- data/lib/datadog/tracing/contrib/racecar/events/batch.rb +2 -2
- data/lib/datadog/tracing/contrib/racecar/events/consume.rb +1 -1
- data/lib/datadog/tracing/contrib/racecar/events/message.rb +2 -2
- data/lib/datadog/tracing/contrib/racecar/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +11 -1
- data/lib/datadog/tracing/contrib/rack/ext.rb +28 -0
- data/lib/datadog/tracing/contrib/rack/header_collection.rb +1 -1
- data/lib/datadog/tracing/contrib/rack/header_tagging.rb +55 -32
- data/lib/datadog/tracing/contrib/rack/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +83 -44
- data/lib/datadog/tracing/contrib/rack/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/rack/request_queue.rb +4 -3
- data/lib/datadog/tracing/contrib/rack/route_inference.rb +67 -0
- data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +126 -2
- data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +6 -3
- data/lib/datadog/tracing/contrib/rails/ext.rb +3 -1
- data/lib/datadog/tracing/contrib/rails/integration.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/log_injection.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/middlewares.rb +3 -3
- data/lib/datadog/tracing/contrib/rails/patcher.rb +4 -2
- data/lib/datadog/tracing/contrib/rails/runner.rb +64 -40
- data/lib/datadog/tracing/contrib/rake/instrumentation.rb +10 -8
- data/lib/datadog/tracing/contrib/rake/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +2 -2
- data/lib/datadog/tracing/contrib/redis/ext.rb +3 -2
- data/lib/datadog/tracing/contrib/redis/integration.rb +2 -2
- data/lib/datadog/tracing/contrib/redis/patcher.rb +4 -4
- data/lib/datadog/tracing/contrib/redis/quantize.rb +2 -2
- data/lib/datadog/tracing/contrib/redis/tags.rb +2 -7
- data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +6 -4
- data/lib/datadog/tracing/contrib/registerable.rb +11 -0
- data/lib/datadog/tracing/contrib/registry.rb +1 -1
- data/lib/datadog/tracing/contrib/resque/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/resque/resque_job.rb +2 -1
- data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/rest_client/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/rest_client/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +8 -9
- data/lib/datadog/tracing/contrib/roda/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/roda/instrumentation.rb +8 -3
- data/lib/datadog/tracing/contrib/roda/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/semantic_logger/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/semantic_logger/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/sequel/database.rb +6 -5
- data/lib/datadog/tracing/contrib/sequel/dataset.rb +2 -1
- data/lib/datadog/tracing/contrib/sequel/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/sequel/utils.rb +1 -6
- data/lib/datadog/tracing/contrib/shoryuken/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq/integration.rb +1 -1
- 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 +8 -4
- data/lib/datadog/tracing/contrib/sidekiq/utils.rb +1 -1
- data/lib/datadog/tracing/contrib/sidekiq.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/sinatra/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +40 -39
- data/lib/datadog/tracing/contrib/sneakers/integration.rb +15 -4
- data/lib/datadog/tracing/contrib/sneakers/tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/span_attribute_schema.rb +1 -1
- data/lib/datadog/tracing/contrib/status_range_matcher.rb +13 -1
- data/lib/datadog/tracing/contrib/stripe/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/stripe/request.rb +2 -2
- data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +1 -0
- data/lib/datadog/tracing/contrib/sucker_punch/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +6 -0
- data/lib/datadog/tracing/contrib/trilogy/ext.rb +1 -1
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -6
- data/lib/datadog/tracing/contrib/trilogy/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/utils/quantization/{hash.rb → hash_formatter.rb} +15 -13
- data/lib/datadog/tracing/contrib/utils/quantization/http.rb +6 -6
- 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 +9 -0
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +12 -4
- data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
- data/lib/datadog/tracing/distributed/baggage.rb +133 -12
- data/lib/datadog/tracing/distributed/datadog.rb +17 -16
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +11 -26
- data/lib/datadog/tracing/distributed/helpers.rb +1 -1
- data/lib/datadog/tracing/distributed/none.rb +4 -2
- data/lib/datadog/tracing/distributed/propagation.rb +39 -4
- data/lib/datadog/tracing/distributed/propagation_policy.rb +1 -1
- data/lib/datadog/tracing/distributed/trace_context.rb +107 -78
- data/lib/datadog/tracing/event.rb +5 -7
- data/lib/datadog/tracing/ext.rb +9 -0
- data/lib/datadog/tracing/flush.rb +1 -1
- data/lib/datadog/tracing/metadata/analytics.rb +1 -1
- data/lib/datadog/tracing/metadata/ext.rb +23 -1
- data/lib/datadog/tracing/metadata/tagging.rb +6 -6
- data/lib/datadog/tracing/pipeline/span_filter.rb +3 -1
- data/lib/datadog/tracing/pipeline/span_processor.rb +3 -1
- data/lib/datadog/tracing/pipeline.rb +2 -2
- data/lib/datadog/tracing/remote.rb +36 -13
- data/lib/datadog/tracing/sampling/ext.rb +2 -2
- data/lib/datadog/tracing/sampling/priority_sampler.rb +16 -1
- data/lib/datadog/tracing/sampling/rate_sampler.rb +8 -19
- data/lib/datadog/tracing/sampling/rule.rb +1 -1
- data/lib/datadog/tracing/sampling/rule_sampler.rb +77 -48
- data/lib/datadog/tracing/sampling/span/rule_parser.rb +3 -3
- data/lib/datadog/tracing/sampling/span/sampler.rb +0 -7
- data/lib/datadog/tracing/span.rb +2 -2
- data/lib/datadog/tracing/span_event.rb +12 -12
- data/lib/datadog/tracing/span_link.rb +12 -12
- data/lib/datadog/tracing/span_operation.rb +63 -19
- data/lib/datadog/tracing/sync_writer.rb +1 -2
- data/lib/datadog/tracing/trace_digest.rb +28 -23
- data/lib/datadog/tracing/trace_operation.rb +209 -98
- data/lib/datadog/tracing/trace_segment.rb +2 -2
- data/lib/datadog/tracing/tracer.rb +113 -52
- data/lib/datadog/tracing/transport/http/client.rb +12 -26
- data/lib/datadog/tracing/transport/http/traces.rb +4 -52
- data/lib/datadog/tracing/transport/http.rb +15 -9
- data/lib/datadog/tracing/transport/io/client.rb +11 -14
- data/lib/datadog/tracing/transport/io/traces.rb +28 -34
- data/lib/datadog/tracing/transport/statistics.rb +1 -1
- data/lib/datadog/tracing/transport/trace_formatter.rb +22 -0
- data/lib/datadog/tracing/transport/traces.rb +14 -82
- data/lib/datadog/tracing/workers.rb +4 -3
- data/lib/datadog/tracing/writer.rb +1 -1
- data/lib/datadog/tracing.rb +2 -2
- data/lib/datadog/version.rb +2 -2
- data/lib/datadog.rb +14 -0
- metadata +201 -40
- 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 -321
- data/lib/datadog/appsec/assets/waf_rules/scanners.json +0 -1023
- data/lib/datadog/appsec/configuration/settings.rb +0 -342
- data/lib/datadog/appsec/contrib/rails/ext.rb +0 -13
- data/lib/datadog/appsec/processor/rule_merger.rb +0 -171
- data/lib/datadog/appsec/processor.rb +0 -107
- data/lib/datadog/core/remote/transport/http/api.rb +0 -53
- data/lib/datadog/core/remote/transport/http/client.rb +0 -49
- data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
- data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
- data/lib/datadog/core/transport/http/api/spec.rb +0 -36
- data/lib/datadog/di/configuration/settings.rb +0 -212
- data/lib/datadog/di/transport/http/api.rb +0 -42
- data/lib/datadog/di/transport/http/client.rb +0 -47
- data/lib/datadog/error_tracking/configuration/settings.rb +0 -63
- data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
- data/lib/datadog/tracing/transport/http/api.rb +0 -44
- data/lib/datadog/tracing/workers/trace_writer.rb +0 -199
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
#include "setup_signal_handler.h"
|
|
18
18
|
#include "time_helpers.h"
|
|
19
19
|
|
|
20
|
+
#ifdef __APPLE__
|
|
21
|
+
#include "macos_sampler_thread.h"
|
|
22
|
+
#endif
|
|
23
|
+
|
|
20
24
|
// Used to trigger the execution of Collectors::ThreadContext, which implements all of the sampling logic
|
|
21
25
|
// itself; this class only implements the "when to do it" part.
|
|
22
26
|
//
|
|
@@ -76,8 +80,6 @@
|
|
|
76
80
|
//
|
|
77
81
|
// ---
|
|
78
82
|
|
|
79
|
-
#define ERR_CLOCK_FAIL "failed to get clock time"
|
|
80
|
-
|
|
81
83
|
// Maximum allowed value for an allocation weight. Attempts to use higher values will result in clamping.
|
|
82
84
|
// See https://docs.google.com/document/d/1lWLB714wlLBBq6T4xZyAc4a5wtWhSmr4-hgiPKeErlA/edit#heading=h.ugp0zxcj5iqh
|
|
83
85
|
// (Datadog-only link) for research backing the choice of this value.
|
|
@@ -89,6 +91,7 @@ unsigned int MAX_ALLOC_WEIGHT = 10000;
|
|
|
89
91
|
static rb_postponed_job_handle_t sample_from_postponed_job_handle;
|
|
90
92
|
static rb_postponed_job_handle_t after_gc_from_postponed_job_handle;
|
|
91
93
|
static rb_postponed_job_handle_t after_gvl_running_from_postponed_job_handle;
|
|
94
|
+
static rb_postponed_job_handle_t after_allocation_from_postponed_job_handle;
|
|
92
95
|
#endif
|
|
93
96
|
|
|
94
97
|
// Contains state for a single CpuAndWallTimeWorker instance
|
|
@@ -102,6 +105,8 @@ typedef struct {
|
|
|
102
105
|
bool allocation_counting_enabled;
|
|
103
106
|
bool gvl_profiling_enabled;
|
|
104
107
|
bool skip_idle_samples_for_testing;
|
|
108
|
+
bool sighandler_sampling_enabled;
|
|
109
|
+
uint32_t cpu_sampling_interval_ms;
|
|
105
110
|
VALUE self_instance;
|
|
106
111
|
VALUE thread_context_collector_instance;
|
|
107
112
|
VALUE idle_sampling_helper_instance;
|
|
@@ -116,13 +121,18 @@ typedef struct {
|
|
|
116
121
|
// When something goes wrong during sampling, we record the Ruby exception here, so that it can be "re-raised" on
|
|
117
122
|
// the CpuAndWallTimeWorker thread
|
|
118
123
|
VALUE failure_exception;
|
|
124
|
+
const char *failure_exception_during_operation;
|
|
119
125
|
// Used by `_native_stop` to flag the worker thread to start (see comment on `_native_sampling_loop`)
|
|
120
126
|
VALUE stop_thread;
|
|
121
127
|
|
|
122
128
|
// Others
|
|
123
129
|
|
|
124
130
|
// Used to detect/avoid nested sampling, e.g. when on_newobj_event gets triggered by a memory allocation
|
|
125
|
-
// that happens during another sample
|
|
131
|
+
// that happens during another sample, or when the signal handler gets triggered while we're already in the middle of
|
|
132
|
+
// sampling.
|
|
133
|
+
//
|
|
134
|
+
// @ivoanjo: Right now we always sample inside `safely_call`; if that ever changes, this flag may need to become
|
|
135
|
+
// volatile/atomic/have some barriers to ensure it's visible during e.g. signal handlers.
|
|
126
136
|
bool during_sample;
|
|
127
137
|
|
|
128
138
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
@@ -134,27 +144,21 @@ typedef struct {
|
|
|
134
144
|
// # Generic stats
|
|
135
145
|
// How many times we tried to trigger a sample
|
|
136
146
|
unsigned int trigger_sample_attempts;
|
|
147
|
+
// How many times extra sleep was triggered by dynamic sampling rate
|
|
148
|
+
unsigned int trigger_sample_extra_sleep;
|
|
137
149
|
// How many times we tried to simulate signal delivery
|
|
138
150
|
unsigned int trigger_simulated_signal_delivery_attempts;
|
|
139
151
|
// How many times we actually simulated signal delivery
|
|
140
152
|
unsigned int simulated_signal_delivery;
|
|
141
|
-
// How many times we actually called rb_postponed_job_register_one from
|
|
153
|
+
// How many times we actually called rb_postponed_job_register_one from the signal handler
|
|
142
154
|
unsigned int signal_handler_enqueued_sample;
|
|
155
|
+
// How many times we prepared a sample (sampled directly) from the signal handler
|
|
156
|
+
unsigned int signal_handler_prepared_sample;
|
|
143
157
|
// How many times the signal handler was called from the wrong thread
|
|
144
158
|
unsigned int signal_handler_wrong_thread;
|
|
145
159
|
// How many times we actually tried to interrupt a thread for sampling
|
|
146
160
|
unsigned int interrupt_thread_attempts;
|
|
147
161
|
|
|
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
162
|
// # CPU/Walltime sampling stats
|
|
159
163
|
// How many times we actually CPU/wall sampled
|
|
160
164
|
unsigned int cpu_sampled;
|
|
@@ -178,6 +182,11 @@ typedef struct {
|
|
|
178
182
|
unsigned int allocations_during_sample;
|
|
179
183
|
|
|
180
184
|
// # GVL profiling stats
|
|
185
|
+
// Note that this tracks two kind of samples from RESUMED:
|
|
186
|
+
// * samples when Waiting for GVL for more than the threshold
|
|
187
|
+
// * samples for the skip-samples-while-without-GVL optimization,
|
|
188
|
+
// which are needed to attribute the time without GVL to the correct Ruby stack
|
|
189
|
+
|
|
181
190
|
// How many times we triggered the after_gvl_running sampling
|
|
182
191
|
unsigned int after_gvl_running;
|
|
183
192
|
// How many times we skipped the after_gvl_running sampling
|
|
@@ -186,6 +195,11 @@ typedef struct {
|
|
|
186
195
|
uint64_t gvl_sampling_time_ns_min;
|
|
187
196
|
uint64_t gvl_sampling_time_ns_max;
|
|
188
197
|
uint64_t gvl_sampling_time_ns_total;
|
|
198
|
+
|
|
199
|
+
struct vm_metrics {
|
|
200
|
+
// Total time spent waiting for the GVL
|
|
201
|
+
uint64_t gvl_waiting_time_ns_total;
|
|
202
|
+
} vm_metrics;
|
|
189
203
|
} stats;
|
|
190
204
|
} cpu_and_wall_time_worker_state;
|
|
191
205
|
|
|
@@ -194,17 +208,17 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
194
208
|
static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr);
|
|
195
209
|
static VALUE _native_sampling_loop(VALUE self, VALUE instance);
|
|
196
210
|
static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread);
|
|
197
|
-
static VALUE stop(VALUE self_instance, VALUE optional_exception);
|
|
198
|
-
static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception);
|
|
211
|
+
static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *optional_exception_during_operation);
|
|
212
|
+
static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception, const char *optional_operation_name);
|
|
199
213
|
static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
|
|
200
214
|
static void *run_sampling_trigger_loop(void *state_ptr);
|
|
201
215
|
static void interrupt_sampling_trigger_loop(void *state_ptr);
|
|
202
216
|
static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
203
217
|
static VALUE rescued_sample_from_postponed_job(VALUE self_instance);
|
|
204
|
-
static VALUE handle_sampling_failure(VALUE self_instance, VALUE exception);
|
|
205
218
|
static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self);
|
|
206
219
|
static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance);
|
|
207
220
|
static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
221
|
+
static VALUE _native_failure_exception_during_operation(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
208
222
|
static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
|
|
209
223
|
static VALUE _native_install_testing_signal_handler(DDTRACE_UNUSED VALUE self);
|
|
210
224
|
static VALUE _native_remove_testing_signal_handler(DDTRACE_UNUSED VALUE self);
|
|
@@ -212,7 +226,12 @@ static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self);
|
|
|
212
226
|
static VALUE _native_gc_tracepoint(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
213
227
|
static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused);
|
|
214
228
|
static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
215
|
-
static VALUE safely_call(
|
|
229
|
+
static VALUE safely_call(
|
|
230
|
+
VALUE (*function_to_call_safely)(VALUE),
|
|
231
|
+
VALUE function_to_call_safely_arg,
|
|
232
|
+
VALUE instance,
|
|
233
|
+
VALUE (*handle_sampling_failure)(VALUE, VALUE)
|
|
234
|
+
);
|
|
216
235
|
static VALUE _native_simulate_handle_sampling_signal(DDTRACE_UNUSED VALUE self);
|
|
217
236
|
static VALUE _native_simulate_sample_from_postponed_job(DDTRACE_UNUSED VALUE self);
|
|
218
237
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
@@ -228,16 +247,26 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
|
|
|
228
247
|
static void disable_tracepoints(cpu_and_wall_time_worker_state *state);
|
|
229
248
|
static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self);
|
|
230
249
|
static VALUE rescued_sample_allocation(VALUE tracepoint_data);
|
|
250
|
+
static VALUE rescued_after_allocation(VALUE self_instance);
|
|
231
251
|
static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error);
|
|
252
|
+
static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state);
|
|
232
253
|
static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
|
|
233
254
|
static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self);
|
|
234
255
|
static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);
|
|
235
256
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
236
|
-
static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused);
|
|
237
|
-
static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
257
|
+
static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused);
|
|
258
|
+
static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
259
|
+
static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance);
|
|
260
|
+
static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception);
|
|
238
261
|
#endif
|
|
239
|
-
static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance);
|
|
240
262
|
static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
263
|
+
static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception);
|
|
264
|
+
static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception);
|
|
265
|
+
static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception);
|
|
266
|
+
static VALUE handle_sampling_failure_rescued_after_allocation(VALUE self_instance, VALUE exception);
|
|
267
|
+
static inline void during_sample_enter(cpu_and_wall_time_worker_state* state);
|
|
268
|
+
static inline void during_sample_exit(cpu_and_wall_time_worker_state* state);
|
|
269
|
+
static void after_allocation_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
241
270
|
|
|
242
271
|
// We're using `on_newobj_event` function with `rb_add_event_hook2`, which requires in its public signature a function
|
|
243
272
|
// with signature `rb_event_hook_func_t` which doesn't match `on_newobj_event`.
|
|
@@ -263,6 +292,7 @@ static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE
|
|
|
263
292
|
// (e.g. signal handler) where it's impossible or just awkward to pass it as an argument.
|
|
264
293
|
static VALUE active_sampler_instance = Qnil;
|
|
265
294
|
static cpu_and_wall_time_worker_state *active_sampler_instance_state = NULL;
|
|
295
|
+
static VALUE clock_failure_exception_class = Qnil;
|
|
266
296
|
|
|
267
297
|
// See handle_sampling_signal for details on what this does
|
|
268
298
|
#ifdef NO_POSTPONED_TRIGGER
|
|
@@ -284,13 +314,15 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
284
314
|
sample_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, sample_from_postponed_job, NULL);
|
|
285
315
|
after_gc_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gc_from_postponed_job, NULL);
|
|
286
316
|
after_gvl_running_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gvl_running_from_postponed_job, NULL);
|
|
317
|
+
after_allocation_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_allocation_from_postponed_job, NULL);
|
|
287
318
|
|
|
288
319
|
if (
|
|
289
320
|
sample_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
290
321
|
after_gc_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
291
|
-
after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
|
|
322
|
+
after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
323
|
+
after_allocation_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
|
|
292
324
|
) {
|
|
293
|
-
|
|
325
|
+
raise_error(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
|
|
294
326
|
}
|
|
295
327
|
#else
|
|
296
328
|
gc_finalize_deferred_workaround = objspace_ptr_for_gc_finalize_deferred_workaround();
|
|
@@ -300,6 +332,8 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
300
332
|
VALUE collectors_cpu_and_wall_time_worker_class = rb_define_class_under(collectors_module, "CpuAndWallTimeWorker", rb_cObject);
|
|
301
333
|
// Hosts methods used for testing the native code using RSpec
|
|
302
334
|
VALUE testing_module = rb_define_module_under(collectors_cpu_and_wall_time_worker_class, "Testing");
|
|
335
|
+
clock_failure_exception_class = rb_define_class_under(collectors_cpu_and_wall_time_worker_class, "ClockFailure", rb_eRuntimeError);
|
|
336
|
+
rb_gc_register_mark_object(clock_failure_exception_class);
|
|
303
337
|
|
|
304
338
|
// Instances of the CpuAndWallTimeWorker class are "TypedData" objects.
|
|
305
339
|
// "TypedData" objects are special objects in the Ruby VM that can wrap C structs.
|
|
@@ -319,6 +353,7 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
319
353
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stats_reset_not_thread_safe", _native_stats_reset_not_thread_safe, 1);
|
|
320
354
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_allocation_count", _native_allocation_count, 0);
|
|
321
355
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_is_running?", _native_is_running, 1);
|
|
356
|
+
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_failure_exception_during_operation", _native_failure_exception_during_operation, 1);
|
|
322
357
|
rb_define_singleton_method(testing_module, "_native_current_sigprof_signal_handler", _native_current_sigprof_signal_handler, 0);
|
|
323
358
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_hold_signals", _native_hold_signals, 0);
|
|
324
359
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_resume_signals", _native_resume_signals, 0);
|
|
@@ -362,6 +397,8 @@ static VALUE _native_new(VALUE klass) {
|
|
|
362
397
|
state->allocation_counting_enabled = false;
|
|
363
398
|
state->gvl_profiling_enabled = false;
|
|
364
399
|
state->skip_idle_samples_for_testing = false;
|
|
400
|
+
state->sighandler_sampling_enabled = false;
|
|
401
|
+
state->cpu_sampling_interval_ms = 10;
|
|
365
402
|
state->thread_context_collector_instance = Qnil;
|
|
366
403
|
state->idle_sampling_helper_instance = Qnil;
|
|
367
404
|
state->owner_thread = Qnil;
|
|
@@ -370,9 +407,10 @@ static VALUE _native_new(VALUE klass) {
|
|
|
370
407
|
|
|
371
408
|
atomic_init(&state->should_run, false);
|
|
372
409
|
state->failure_exception = Qnil;
|
|
410
|
+
state->failure_exception_during_operation = NULL;
|
|
373
411
|
state->stop_thread = Qnil;
|
|
374
412
|
|
|
375
|
-
state
|
|
413
|
+
during_sample_exit(state);
|
|
376
414
|
|
|
377
415
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
378
416
|
state->gvl_profiling_hook = NULL;
|
|
@@ -404,6 +442,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
404
442
|
VALUE allocation_counting_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("allocation_counting_enabled")));
|
|
405
443
|
VALUE gvl_profiling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("gvl_profiling_enabled")));
|
|
406
444
|
VALUE skip_idle_samples_for_testing = rb_hash_fetch(options, ID2SYM(rb_intern("skip_idle_samples_for_testing")));
|
|
445
|
+
VALUE sighandler_sampling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("sighandler_sampling_enabled")));
|
|
446
|
+
VALUE cpu_sampling_interval_ms = rb_hash_fetch(options, ID2SYM(rb_intern("cpu_sampling_interval_ms")));
|
|
407
447
|
|
|
408
448
|
ENFORCE_BOOLEAN(gc_profiling_enabled);
|
|
409
449
|
ENFORCE_BOOLEAN(no_signals_workaround_enabled);
|
|
@@ -413,6 +453,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
413
453
|
ENFORCE_BOOLEAN(allocation_counting_enabled);
|
|
414
454
|
ENFORCE_BOOLEAN(gvl_profiling_enabled);
|
|
415
455
|
ENFORCE_BOOLEAN(skip_idle_samples_for_testing)
|
|
456
|
+
ENFORCE_BOOLEAN(sighandler_sampling_enabled)
|
|
457
|
+
ENFORCE_TYPE(cpu_sampling_interval_ms, T_FIXNUM);
|
|
416
458
|
|
|
417
459
|
cpu_and_wall_time_worker_state *state;
|
|
418
460
|
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
@@ -424,6 +466,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
424
466
|
state->allocation_counting_enabled = (allocation_counting_enabled == Qtrue);
|
|
425
467
|
state->gvl_profiling_enabled = (gvl_profiling_enabled == Qtrue);
|
|
426
468
|
state->skip_idle_samples_for_testing = (skip_idle_samples_for_testing == Qtrue);
|
|
469
|
+
state->sighandler_sampling_enabled = (sighandler_sampling_enabled == Qtrue);
|
|
470
|
+
state->cpu_sampling_interval_ms = NUM2INT(cpu_sampling_interval_ms);
|
|
427
471
|
|
|
428
472
|
double total_overhead_target_percentage = NUM2DBL(dynamic_sampling_rate_overhead_target_percentage);
|
|
429
473
|
if (!state->allocation_profiling_enabled) {
|
|
@@ -469,10 +513,7 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
469
513
|
cpu_and_wall_time_worker_state *old_state = active_sampler_instance_state;
|
|
470
514
|
if (old_state != NULL) {
|
|
471
515
|
if (is_thread_alive(old_state->owner_thread)) {
|
|
472
|
-
|
|
473
|
-
rb_eRuntimeError,
|
|
474
|
-
"Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread"
|
|
475
|
-
);
|
|
516
|
+
raise_error(rb_eRuntimeError, "Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread");
|
|
476
517
|
} else {
|
|
477
518
|
// The previously active thread seems to have died without cleaning up after itself.
|
|
478
519
|
// In this case, we can still go ahead and start the profiler BUT we make sure to disable any existing tracepoint
|
|
@@ -489,6 +530,8 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
489
530
|
// situation we stop immediately and never even start the sampling trigger loop.
|
|
490
531
|
if (state->stop_thread == rb_thread_current()) return Qnil;
|
|
491
532
|
|
|
533
|
+
thread_context_collector_profiler_internal_thread_started();
|
|
534
|
+
|
|
492
535
|
// Reset the dynamic sampling rate state, if any (reminder: the monotonic clock reference may change after a fork)
|
|
493
536
|
dynamic_sampling_rate_reset(&state->cpu_dynamic_sampling_rate);
|
|
494
537
|
long now = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
@@ -503,6 +546,14 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
503
546
|
|
|
504
547
|
block_sigprof_signal_handler_from_running_in_current_thread(); // We want to interrupt the thread with the global VM lock, never this one
|
|
505
548
|
|
|
549
|
+
#ifdef __APPLE__
|
|
550
|
+
// Promote the native thread that will run the sampling loop to a realtime scheduling policy so
|
|
551
|
+
// its periodic wake-ups are not subject to the default ~1-2ms timeshare wake latency. The matching
|
|
552
|
+
// demote happens below in this same function, alongside the SIGPROF unblock dance, so both per-thread
|
|
553
|
+
// policy changes are reverted before Ruby reuses this native thread for an unrelated Ruby thread.
|
|
554
|
+
promote_sampler_thread_to_realtime(MILLIS_AS_NS(state->cpu_sampling_interval_ms));
|
|
555
|
+
#endif
|
|
556
|
+
|
|
506
557
|
// Release GVL, get to the actual work!
|
|
507
558
|
int exception_state;
|
|
508
559
|
rb_protect(release_gvl_and_run_sampling_trigger_loop, instance, &exception_state);
|
|
@@ -524,6 +575,14 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
524
575
|
// had SIGPROF delivery blocked. :hide_the_pain_harold:
|
|
525
576
|
unblock_sigprof_signal_handler_from_running_in_current_thread();
|
|
526
577
|
|
|
578
|
+
#ifdef __APPLE__
|
|
579
|
+
// Pairs with promote_sampler_thread_to_realtime above. Same reasoning as the SIGPROF unblock:
|
|
580
|
+
// Ruby may reuse this native thread for an unrelated Ruby thread that would otherwise inherit
|
|
581
|
+
// our scheduler policy. Doing it here also covers the exception path, since grab_gvl_and_sample
|
|
582
|
+
// can raise and unwind out of the sampling loop.
|
|
583
|
+
demote_sampler_thread_from_realtime();
|
|
584
|
+
#endif
|
|
585
|
+
|
|
527
586
|
// Why replace and not use remove the signal handler? We do this because when a process receives a SIGPROF without
|
|
528
587
|
// having an explicit signal handler set up, the process will instantly terminate with a confusing
|
|
529
588
|
// "Profiling timer expired" message left behind. (This message doesn't come from us -- it's the default message for
|
|
@@ -534,8 +593,9 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
534
593
|
// @ivoanjo: I suspect this will never happen, but the cost of getting it wrong is really high (VM terminates) so this
|
|
535
594
|
// is a just-in-case situation.
|
|
536
595
|
//
|
|
537
|
-
// Note 2:
|
|
538
|
-
replace_sigprof_signal_handler_with_empty_handler
|
|
596
|
+
// Note 2: If exception_state is set, we have a pending exception that we'll re-raise below.
|
|
597
|
+
// In that case, we don't want replace_sigprof_signal_handler_with_empty_handler to raise another exception.
|
|
598
|
+
replace_sigprof_signal_handler_with_empty_handler(handle_sampling_signal, !exception_state);
|
|
539
599
|
|
|
540
600
|
// Ensure that instance is not garbage collected while the native sampling loop is running; this is probably not needed, but just in case
|
|
541
601
|
RB_GC_GUARD(instance);
|
|
@@ -551,22 +611,24 @@ static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE
|
|
|
551
611
|
|
|
552
612
|
state->stop_thread = worker_thread;
|
|
553
613
|
|
|
554
|
-
return stop(self_instance,
|
|
614
|
+
return stop(self_instance, Qnil, NULL);
|
|
555
615
|
}
|
|
556
616
|
|
|
557
|
-
|
|
617
|
+
// When providing an `optional_exception`, `optional_exception_during_operation` should be provided as well
|
|
618
|
+
static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception, const char *optional_exception_during_operation) {
|
|
558
619
|
atomic_store(&state->should_run, false);
|
|
559
620
|
state->failure_exception = optional_exception;
|
|
621
|
+
state->failure_exception_during_operation = optional_exception_during_operation;
|
|
560
622
|
|
|
561
623
|
// Disable the tracepoints as soon as possible, so the VM doesn't keep on calling them
|
|
562
624
|
disable_tracepoints(state);
|
|
563
625
|
}
|
|
564
626
|
|
|
565
|
-
static VALUE stop(VALUE self_instance, VALUE optional_exception) {
|
|
627
|
+
static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *optional_exception_during_operation) {
|
|
566
628
|
cpu_and_wall_time_worker_state *state;
|
|
567
629
|
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
568
630
|
|
|
569
|
-
stop_state(state, optional_exception);
|
|
631
|
+
stop_state(state, optional_exception, optional_exception_during_operation);
|
|
570
632
|
|
|
571
633
|
return Qtrue;
|
|
572
634
|
}
|
|
@@ -589,25 +651,30 @@ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED si
|
|
|
589
651
|
return;
|
|
590
652
|
}
|
|
591
653
|
|
|
592
|
-
// We
|
|
593
|
-
// a) we get triggered using SIGPROF, and the docs state a second SIGPROF will not interrupt an existing one
|
|
654
|
+
// We assume there can be no concurrent nor nested calls to handle_sampling_signal because
|
|
655
|
+
// a) we get triggered using SIGPROF, and the docs state a second SIGPROF will not interrupt an existing one (see sigaction docs on sa_mask)
|
|
594
656
|
// b) we validate we are in the thread that has the global VM lock; if a different thread gets a signal, it will return early
|
|
595
657
|
// because it will not have the global VM lock
|
|
596
658
|
|
|
597
|
-
// Note: rb_postponed_job_register_one ensures that if there's a previous sample_from_postponed_job queued for execution
|
|
598
|
-
// then we will not queue a second one. It does this by doing a linear scan on the existing jobs; in the future we
|
|
599
|
-
// may want to implement that check ourselves.
|
|
600
|
-
|
|
601
659
|
state->stats.signal_handler_enqueued_sample++;
|
|
602
660
|
|
|
603
|
-
|
|
604
|
-
|
|
661
|
+
bool sample_from_signal_handler =
|
|
662
|
+
state->sighandler_sampling_enabled &&
|
|
663
|
+
// Don't sample if we're already in the middle of processing a sample
|
|
664
|
+
!state->during_sample;
|
|
665
|
+
|
|
666
|
+
if (sample_from_signal_handler) {
|
|
667
|
+
// Buffer current stack trace. Note that this will not actually record the sample, for that we still need to wait
|
|
668
|
+
// until the postponed job below gets run.
|
|
669
|
+
bool prepared = thread_context_collector_prepare_sample_inside_signal_handler();
|
|
670
|
+
|
|
671
|
+
if (prepared) state->stats.signal_handler_prepared_sample++;
|
|
672
|
+
}
|
|
673
|
+
|
|
605
674
|
#ifndef NO_POSTPONED_TRIGGER // Ruby 3.3+
|
|
606
675
|
rb_postponed_job_trigger(sample_from_postponed_job_handle);
|
|
607
|
-
state->stats.postponed_job_success++; // Always succeeds
|
|
608
676
|
#else
|
|
609
|
-
|
|
610
|
-
// This is a workaround for https://bugs.ruby-lang.org/issues/19991 (for Ruby < 3.3)
|
|
677
|
+
// Passing in `gc_finalize_deferred_workaround` is a workaround for https://bugs.ruby-lang.org/issues/19991 (for Ruby < 3.3)
|
|
611
678
|
//
|
|
612
679
|
// TL;DR the `rb_postponed_job_register_one` API is not atomic (which is why it got replaced by `rb_postponed_job_trigger`)
|
|
613
680
|
// and in rare cases can cause VM crashes.
|
|
@@ -631,20 +698,7 @@ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED si
|
|
|
631
698
|
//
|
|
632
699
|
// Thus, our workaround is simple: we pass in objspace as our argument, just in case the clobbering happens.
|
|
633
700
|
// In the happy path, we never use this argument so it makes no difference. In the buggy path, we avoid crashing the VM.
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
// Officially, the result of rb_postponed_job_register_one is documented as being opaque, but in practice it does not
|
|
637
|
-
// seem to have changed between Ruby 2.3 and 3.2, and so we track it as a debugging mechanism
|
|
638
|
-
switch (result) {
|
|
639
|
-
case 0:
|
|
640
|
-
state->stats.postponed_job_full++; break;
|
|
641
|
-
case 1:
|
|
642
|
-
state->stats.postponed_job_success++; break;
|
|
643
|
-
case 2:
|
|
644
|
-
state->stats.postponed_job_skipped_already_existed++; break;
|
|
645
|
-
default:
|
|
646
|
-
state->stats.postponed_job_unknown_result++;
|
|
647
|
-
}
|
|
701
|
+
rb_postponed_job_register(0, sample_from_postponed_job, gc_finalize_deferred_workaround /* instead of NULL */);
|
|
648
702
|
#endif
|
|
649
703
|
}
|
|
650
704
|
|
|
@@ -652,7 +706,7 @@ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED si
|
|
|
652
706
|
static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
653
707
|
cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
|
|
654
708
|
|
|
655
|
-
uint64_t minimum_time_between_signals = MILLIS_AS_NS(
|
|
709
|
+
uint64_t minimum_time_between_signals = MILLIS_AS_NS(state->cpu_sampling_interval_ms);
|
|
656
710
|
|
|
657
711
|
while (atomic_load(&state->should_run)) {
|
|
658
712
|
state->stats.trigger_sample_attempts++;
|
|
@@ -674,6 +728,10 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
|
674
728
|
// we're doing this, so we may still not signal the correct thread from time to time, but our signal handler
|
|
675
729
|
// includes a check to see if it got called in the right thread
|
|
676
730
|
state->stats.interrupt_thread_attempts++;
|
|
731
|
+
|
|
732
|
+
// Pick up any last-minute attempts to stop before we send the signal
|
|
733
|
+
if (!atomic_load(&state->should_run)) return NULL;
|
|
734
|
+
|
|
677
735
|
pthread_kill(owner.owner, SIGPROF);
|
|
678
736
|
} else {
|
|
679
737
|
if (state->skip_idle_samples_for_testing) {
|
|
@@ -701,7 +759,10 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
|
701
759
|
// `dynamic_sampling_rate_get_sleep` may have changed while the above sleep was ongoing.
|
|
702
760
|
uint64_t extra_sleep =
|
|
703
761
|
dynamic_sampling_rate_get_sleep(&state->cpu_dynamic_sampling_rate, monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE));
|
|
704
|
-
if (state->dynamic_sampling_rate_enabled && extra_sleep > 0)
|
|
762
|
+
if (state->dynamic_sampling_rate_enabled && extra_sleep > 0) {
|
|
763
|
+
state->stats.trigger_sample_extra_sleep++;
|
|
764
|
+
sleep_for(extra_sleep);
|
|
765
|
+
}
|
|
705
766
|
}
|
|
706
767
|
|
|
707
768
|
return NULL; // Unused
|
|
@@ -714,6 +775,8 @@ static void interrupt_sampling_trigger_loop(void *state_ptr) {
|
|
|
714
775
|
atomic_store(&state->should_run, false);
|
|
715
776
|
}
|
|
716
777
|
|
|
778
|
+
// Note: If we ever want to get rid of the postponed job execution, remember not to clobber Ruby exceptions, as
|
|
779
|
+
// this function does this helpful job for us now -- https://github.com/ruby/ruby/commit/a98e343d39c4d7bf1e2190b076720f32d9f298b3.
|
|
717
780
|
static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
718
781
|
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
719
782
|
|
|
@@ -726,12 +789,17 @@ static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
|
726
789
|
return; // We're not on the main Ractor; we currently don't support profiling non-main Ractors
|
|
727
790
|
}
|
|
728
791
|
|
|
729
|
-
state
|
|
792
|
+
during_sample_enter(state);
|
|
730
793
|
|
|
731
794
|
// Rescue against any exceptions that happen during sampling
|
|
732
|
-
safely_call(
|
|
795
|
+
safely_call(
|
|
796
|
+
rescued_sample_from_postponed_job,
|
|
797
|
+
state->self_instance,
|
|
798
|
+
state->self_instance,
|
|
799
|
+
handle_sampling_failure_rescued_sample_from_postponed_job
|
|
800
|
+
);
|
|
733
801
|
|
|
734
|
-
state
|
|
802
|
+
during_sample_exit(state);
|
|
735
803
|
}
|
|
736
804
|
|
|
737
805
|
static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
|
|
@@ -747,8 +815,7 @@ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
|
|
|
747
815
|
|
|
748
816
|
state->stats.cpu_sampled++;
|
|
749
817
|
|
|
750
|
-
|
|
751
|
-
thread_context_collector_sample(state->thread_context_collector_instance, wall_time_ns_before_sample, profiler_overhead_stack_thread);
|
|
818
|
+
thread_context_collector_sample(state->thread_context_collector_instance, wall_time_ns_before_sample);
|
|
752
819
|
|
|
753
820
|
long wall_time_ns_after_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
754
821
|
long delta_ns = wall_time_ns_after_sample - wall_time_ns_before_sample;
|
|
@@ -766,11 +833,6 @@ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
|
|
|
766
833
|
return Qnil;
|
|
767
834
|
}
|
|
768
835
|
|
|
769
|
-
static VALUE handle_sampling_failure(VALUE self_instance, VALUE exception) {
|
|
770
|
-
stop(self_instance, exception);
|
|
771
|
-
return Qnil;
|
|
772
|
-
}
|
|
773
|
-
|
|
774
836
|
// This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
|
|
775
837
|
// It SHOULD NOT be used for other purposes.
|
|
776
838
|
static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self) {
|
|
@@ -809,22 +871,19 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
|
|
|
809
871
|
|
|
810
872
|
if (state->gvl_profiling_enabled) {
|
|
811
873
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
812
|
-
#ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS
|
|
813
|
-
gvl_profiling_state_thread_tracking_workaround();
|
|
814
|
-
#endif
|
|
815
|
-
|
|
816
874
|
state->gvl_profiling_hook = rb_internal_thread_add_event_hook(
|
|
817
875
|
on_gvl_event,
|
|
818
876
|
(
|
|
819
877
|
// For now we're only asking for these events, even though there's more
|
|
820
878
|
// (e.g. check docs or gvl-tracing gem)
|
|
879
|
+
RUBY_INTERNAL_THREAD_EVENT_SUSPENDED | /* released gvl */
|
|
821
880
|
RUBY_INTERNAL_THREAD_EVENT_READY /* waiting for gvl */ |
|
|
822
881
|
RUBY_INTERNAL_THREAD_EVENT_RESUMED /* running/runnable */
|
|
823
882
|
),
|
|
824
883
|
NULL
|
|
825
884
|
);
|
|
826
885
|
#else
|
|
827
|
-
|
|
886
|
+
raise_error(rb_eArgError, "GVL profiling is not supported in this Ruby version");
|
|
828
887
|
#endif
|
|
829
888
|
}
|
|
830
889
|
|
|
@@ -836,6 +895,8 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
|
|
|
836
895
|
// If we stopped sampling due to an exception, re-raise it (now in the worker thread)
|
|
837
896
|
if (state->failure_exception != Qnil) rb_exc_raise(state->failure_exception);
|
|
838
897
|
|
|
898
|
+
thread_context_collector_profiler_internal_thread_done(state->thread_context_collector_instance);
|
|
899
|
+
|
|
839
900
|
return Qnil;
|
|
840
901
|
}
|
|
841
902
|
|
|
@@ -847,6 +908,15 @@ static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
|
847
908
|
return (state != NULL && is_thread_alive(state->owner_thread) && state->self_instance == instance) ? Qtrue : Qfalse;
|
|
848
909
|
}
|
|
849
910
|
|
|
911
|
+
static VALUE _native_failure_exception_during_operation(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
912
|
+
cpu_and_wall_time_worker_state *state;
|
|
913
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
914
|
+
|
|
915
|
+
if (state->failure_exception_during_operation == NULL) return Qnil;
|
|
916
|
+
|
|
917
|
+
return rb_str_new_cstr(state->failure_exception_during_operation);
|
|
918
|
+
}
|
|
919
|
+
|
|
850
920
|
static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext) {
|
|
851
921
|
/* Does nothing on purpose */
|
|
852
922
|
}
|
|
@@ -937,16 +1007,26 @@ static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
|
937
1007
|
return; // We're not on the main Ractor; we currently don't support profiling non-main Ractors
|
|
938
1008
|
}
|
|
939
1009
|
|
|
940
|
-
state
|
|
1010
|
+
during_sample_enter(state);
|
|
941
1011
|
|
|
942
|
-
safely_call(
|
|
1012
|
+
safely_call(
|
|
1013
|
+
thread_context_collector_sample_after_gc,
|
|
1014
|
+
state->thread_context_collector_instance,
|
|
1015
|
+
state->self_instance,
|
|
1016
|
+
handle_sampling_failure_thread_context_collector_sample_after_gc
|
|
1017
|
+
);
|
|
943
1018
|
|
|
944
|
-
state
|
|
1019
|
+
during_sample_exit(state);
|
|
945
1020
|
}
|
|
946
1021
|
|
|
947
1022
|
// Equivalent to Ruby begin/rescue call, where we call a C function and jump to the exception handler if an
|
|
948
1023
|
// exception gets raised within
|
|
949
|
-
static VALUE safely_call(
|
|
1024
|
+
static VALUE safely_call(
|
|
1025
|
+
VALUE (*function_to_call_safely)(VALUE),
|
|
1026
|
+
VALUE function_to_call_safely_arg,
|
|
1027
|
+
VALUE instance,
|
|
1028
|
+
VALUE (*handle_sampling_failure)(VALUE, VALUE)
|
|
1029
|
+
) {
|
|
950
1030
|
VALUE exception_handler_function_arg = instance;
|
|
951
1031
|
return rb_rescue2(
|
|
952
1032
|
function_to_call_safely,
|
|
@@ -980,6 +1060,9 @@ static VALUE _native_simulate_sample_from_postponed_job(DDTRACE_UNUSED VALUE sel
|
|
|
980
1060
|
//
|
|
981
1061
|
// In the future, if we add more other components with tracepoints, we will need to coordinate stopping all such
|
|
982
1062
|
// tracepoints before doing the other cleaning steps.
|
|
1063
|
+
//
|
|
1064
|
+
// Note that tests call this method directly in the same process without forking,
|
|
1065
|
+
// and in such a case non-current Threads keep running.
|
|
983
1066
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
984
1067
|
cpu_and_wall_time_worker_state *state;
|
|
985
1068
|
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
@@ -1016,14 +1099,12 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
|
1016
1099
|
VALUE stats_as_hash = rb_hash_new();
|
|
1017
1100
|
VALUE arguments[] = {
|
|
1018
1101
|
ID2SYM(rb_intern("trigger_sample_attempts")), /* => */ UINT2NUM(state->stats.trigger_sample_attempts),
|
|
1102
|
+
ID2SYM(rb_intern("trigger_sample_extra_sleep")), /* => */ UINT2NUM(state->stats.trigger_sample_extra_sleep),
|
|
1019
1103
|
ID2SYM(rb_intern("trigger_simulated_signal_delivery_attempts")), /* => */ UINT2NUM(state->stats.trigger_simulated_signal_delivery_attempts),
|
|
1020
1104
|
ID2SYM(rb_intern("simulated_signal_delivery")), /* => */ UINT2NUM(state->stats.simulated_signal_delivery),
|
|
1021
1105
|
ID2SYM(rb_intern("signal_handler_enqueued_sample")), /* => */ UINT2NUM(state->stats.signal_handler_enqueued_sample),
|
|
1106
|
+
ID2SYM(rb_intern("signal_handler_prepared_sample")), /* => */ UINT2NUM(state->stats.signal_handler_prepared_sample),
|
|
1022
1107
|
ID2SYM(rb_intern("signal_handler_wrong_thread")), /* => */ UINT2NUM(state->stats.signal_handler_wrong_thread),
|
|
1023
|
-
ID2SYM(rb_intern("postponed_job_skipped_already_existed")), /* => */ UINT2NUM(state->stats.postponed_job_skipped_already_existed),
|
|
1024
|
-
ID2SYM(rb_intern("postponed_job_success")), /* => */ UINT2NUM(state->stats.postponed_job_success),
|
|
1025
|
-
ID2SYM(rb_intern("postponed_job_full")), /* => */ UINT2NUM(state->stats.postponed_job_full),
|
|
1026
|
-
ID2SYM(rb_intern("postponed_job_unknown_result")), /* => */ UINT2NUM(state->stats.postponed_job_unknown_result),
|
|
1027
1108
|
ID2SYM(rb_intern("interrupt_thread_attempts")), /* => */ UINT2NUM(state->stats.interrupt_thread_attempts),
|
|
1028
1109
|
|
|
1029
1110
|
// CPU Stats
|
|
@@ -1053,8 +1134,12 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
|
1053
1134
|
ID2SYM(rb_intern("gvl_sampling_time_ns_max")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_max, > 0, ULL2NUM),
|
|
1054
1135
|
ID2SYM(rb_intern("gvl_sampling_time_ns_total")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_total, > 0, ULL2NUM),
|
|
1055
1136
|
ID2SYM(rb_intern("gvl_sampling_time_ns_avg")), /* => */ RUBY_AVG_OR_NIL(state->stats.gvl_sampling_time_ns_total, state->stats.after_gvl_running),
|
|
1137
|
+
ID2SYM(rb_intern("gvl_waiting_time_ns_total")), /* => */ state->gvl_profiling_enabled ? ULL2NUM(state->stats.vm_metrics.gvl_waiting_time_ns_total) : Qnil,
|
|
1056
1138
|
};
|
|
1057
1139
|
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(stats_as_hash, arguments[i], arguments[i+1]);
|
|
1140
|
+
|
|
1141
|
+
thread_context_collector_stats(state->thread_context_collector_instance, stats_as_hash);
|
|
1142
|
+
|
|
1058
1143
|
return stats_as_hash;
|
|
1059
1144
|
}
|
|
1060
1145
|
|
|
@@ -1062,6 +1147,7 @@ static VALUE _native_stats_reset_not_thread_safe(DDTRACE_UNUSED VALUE self, VALU
|
|
|
1062
1147
|
cpu_and_wall_time_worker_state *state;
|
|
1063
1148
|
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1064
1149
|
reset_stats_not_thread_safe(state);
|
|
1150
|
+
thread_context_collector_stats_reset_not_thread_safe(state->thread_context_collector_instance);
|
|
1065
1151
|
return Qnil;
|
|
1066
1152
|
}
|
|
1067
1153
|
|
|
@@ -1073,8 +1159,7 @@ void *simulate_sampling_signal_delivery(DDTRACE_UNUSED void *_unused) {
|
|
|
1073
1159
|
|
|
1074
1160
|
state->stats.simulated_signal_delivery++;
|
|
1075
1161
|
|
|
1076
|
-
//
|
|
1077
|
-
// so that the simulated case is as close to the original one as well (including any metrics increases, etc).
|
|
1162
|
+
// `handle_sampling_signal` does a few things extra on top of `sample_from_postponed_job` so that's why we don't shortcut here
|
|
1078
1163
|
handle_sampling_signal(0, NULL, NULL);
|
|
1079
1164
|
|
|
1080
1165
|
return NULL; // Unused
|
|
@@ -1094,6 +1179,7 @@ static void reset_stats_not_thread_safe(cpu_and_wall_time_worker_state *state) {
|
|
|
1094
1179
|
.cpu_sampling_time_ns_min = UINT64_MAX,
|
|
1095
1180
|
.allocation_sampling_time_ns_min = UINT64_MAX,
|
|
1096
1181
|
.gvl_sampling_time_ns_min = UINT64_MAX,
|
|
1182
|
+
// Other fields are reset to 0
|
|
1097
1183
|
};
|
|
1098
1184
|
}
|
|
1099
1185
|
|
|
@@ -1126,7 +1212,7 @@ static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
|
|
|
1126
1212
|
#define HANDLE_CLOCK_FAILURE(call) ({ \
|
|
1127
1213
|
long _result = (call); \
|
|
1128
1214
|
if (_result == 0) { \
|
|
1129
|
-
|
|
1215
|
+
delayed_error_clock_failure(state); \
|
|
1130
1216
|
return; \
|
|
1131
1217
|
} \
|
|
1132
1218
|
_result; \
|
|
@@ -1171,13 +1257,22 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
|
|
|
1171
1257
|
return;
|
|
1172
1258
|
}
|
|
1173
1259
|
|
|
1260
|
+
VALUE current_thread = rb_thread_current();
|
|
1261
|
+
|
|
1174
1262
|
// If Ruby is in the middle of raising an exception, we don't want to try to sample. This is because if we accidentally
|
|
1175
1263
|
// trigger an exception inside the profiler code, bad things will happen (specifically, Ruby will try to kill off the
|
|
1176
1264
|
// thread even though we may try to catch the exception).
|
|
1177
1265
|
//
|
|
1178
1266
|
// Note that "in the middle of raising an exception" means the exception itself has already been allocated.
|
|
1179
1267
|
// What's getting allocated now is probably the backtrace objects (@ivoanjo or at least that's what I've observed)
|
|
1180
|
-
if (is_raised_flag_set(
|
|
1268
|
+
if (is_raised_flag_set(current_thread)) {
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
per_thread_context *thread_context = get_per_thread_context(current_thread);
|
|
1273
|
+
if (!thread_context) {
|
|
1274
|
+
// Context is created eagerly via on_thread_begin_event, so this should not normally be NULL.
|
|
1275
|
+
// We keep the guard since we can't allocate here (inside on_newobj_event).
|
|
1181
1276
|
return;
|
|
1182
1277
|
}
|
|
1183
1278
|
|
|
@@ -1207,19 +1302,20 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
|
|
|
1207
1302
|
&state->allocation_sampler, HANDLE_CLOCK_FAILURE(monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE))
|
|
1208
1303
|
);
|
|
1209
1304
|
|
|
1210
|
-
|
|
1211
|
-
// invocation is still pending, (e.g. it wouldn't call `on_newobj_event` while it's already running), but I decided
|
|
1212
|
-
// to keep this here for consistency -- every call to the thread context (other than the special gc calls which are
|
|
1213
|
-
// defined as not being able to allocate) sets this.
|
|
1214
|
-
state->during_sample = true;
|
|
1305
|
+
during_sample_enter(state);
|
|
1215
1306
|
|
|
1216
1307
|
// Rescue against any exceptions that happen during sampling
|
|
1217
|
-
safely_call(
|
|
1308
|
+
safely_call(
|
|
1309
|
+
rescued_sample_allocation,
|
|
1310
|
+
(VALUE) thread_context,
|
|
1311
|
+
state->self_instance,
|
|
1312
|
+
handle_sampling_failure_rescued_sample_allocation
|
|
1313
|
+
);
|
|
1218
1314
|
|
|
1219
1315
|
if (state->dynamic_sampling_rate_enabled) {
|
|
1220
1316
|
long now = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
|
|
1221
1317
|
if (now == 0) {
|
|
1222
|
-
|
|
1318
|
+
delayed_error_clock_failure(state);
|
|
1223
1319
|
// NOTE: Not short-circuiting here to make sure cleanup happens
|
|
1224
1320
|
}
|
|
1225
1321
|
uint64_t sampling_time_ns = discrete_dynamic_sampler_after_sample(&state->allocation_sampler, now);
|
|
@@ -1232,7 +1328,7 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
|
|
|
1232
1328
|
|
|
1233
1329
|
state->stats.allocation_sampled++;
|
|
1234
1330
|
|
|
1235
|
-
state
|
|
1331
|
+
during_sample_exit(state);
|
|
1236
1332
|
}
|
|
1237
1333
|
|
|
1238
1334
|
static void disable_tracepoints(cpu_and_wall_time_worker_state *state) {
|
|
@@ -1263,7 +1359,8 @@ static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self) {
|
|
|
1263
1359
|
}
|
|
1264
1360
|
}
|
|
1265
1361
|
|
|
1266
|
-
static VALUE rescued_sample_allocation(
|
|
1362
|
+
static VALUE rescued_sample_allocation(VALUE arg) {
|
|
1363
|
+
per_thread_context *thread_context = (per_thread_context*) arg;
|
|
1267
1364
|
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1268
1365
|
|
|
1269
1366
|
// This should not happen in a normal situation because on_newobj_event already checked for this, but just in case...
|
|
@@ -1282,20 +1379,33 @@ static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {
|
|
|
1282
1379
|
// To control bias from sampling, we clamp the maximum weight attributed to a single allocation sample. This avoids
|
|
1283
1380
|
// assigning a very large number to a sample, if for instance the dynamic sampling mechanism chose a really big interval.
|
|
1284
1381
|
unsigned int weight = allocations_since_last_sample > MAX_ALLOC_WEIGHT ? MAX_ALLOC_WEIGHT : (unsigned int) allocations_since_last_sample;
|
|
1285
|
-
thread_context_collector_sample_allocation(state->thread_context_collector_instance, weight, new_object);
|
|
1382
|
+
bool needs_after_allocation = thread_context_collector_sample_allocation(state->thread_context_collector_instance, thread_context, weight, new_object);
|
|
1286
1383
|
// ...but we still represent the skipped samples in the profile, thus the data will account for all allocations.
|
|
1287
1384
|
if (weight < allocations_since_last_sample) {
|
|
1288
1385
|
uint32_t skipped_samples = (uint32_t) uint64_min_of(allocations_since_last_sample - weight, UINT32_MAX);
|
|
1289
1386
|
thread_context_collector_sample_skipped_allocation_samples(state->thread_context_collector_instance, skipped_samples);
|
|
1290
1387
|
}
|
|
1291
1388
|
|
|
1389
|
+
if (needs_after_allocation) {
|
|
1390
|
+
#ifndef NO_POSTPONED_TRIGGER
|
|
1391
|
+
rb_postponed_job_trigger(after_allocation_from_postponed_job_handle);
|
|
1392
|
+
#else
|
|
1393
|
+
// Not needed on legacy rubies
|
|
1394
|
+
#endif
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1292
1397
|
// Return a dummy VALUE because we're called from rb_rescue2 which requires it
|
|
1293
1398
|
return Qnil;
|
|
1294
1399
|
}
|
|
1295
1400
|
|
|
1296
1401
|
static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error) {
|
|
1297
1402
|
// If we can't raise an immediate exception at the calling site, use the asynchronous flow through the main worker loop.
|
|
1298
|
-
stop_state(state, rb_exc_new_cstr(rb_eRuntimeError, error));
|
|
1403
|
+
stop_state(state, rb_exc_new_cstr(rb_eRuntimeError, error), "delayed_error");
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state) {
|
|
1407
|
+
// If we can't raise an immediate exception at the calling site, use the asynchronous flow through the main worker loop.
|
|
1408
|
+
stop_state(state, rb_exc_new_cstr(clock_failure_exception_class, "failed to get clock time"), "delayed_error_clock_failure");
|
|
1299
1409
|
}
|
|
1300
1410
|
|
|
1301
1411
|
static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg) {
|
|
@@ -1326,44 +1436,65 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
|
|
|
1326
1436
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
1327
1437
|
static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused) {
|
|
1328
1438
|
// Be very careful about touching the `state` here or doing anything at all:
|
|
1329
|
-
// This function gets called without the GVL, and potentially from
|
|
1330
|
-
|
|
1331
|
-
//
|
|
1332
|
-
//
|
|
1333
|
-
|
|
1439
|
+
// This function gets called without the GVL, and potentially from non-main Ractors!
|
|
1440
|
+
|
|
1441
|
+
// The thread that this event is about may not be the current thread
|
|
1442
|
+
// (as documented on rb_internal_thread_add_event_hook(), and this is notably the case for READY on Ruby 4.0),
|
|
1443
|
+
// so be careful with native thread locals that are not directly tied to the thread object and the like.
|
|
1444
|
+
|
|
1445
|
+
// On Ruby 3.2 the event does not carry the thread, but all events always fire on the event thread on Ruby 3.2.
|
|
1446
|
+
// However, during early thread startup rb_thread_current() can crash because the execution context (Fiber) isn't
|
|
1447
|
+
// stored in TLS yet; ruby_native_thread_p() guards against this.
|
|
1448
|
+
#ifdef HAVE_RUBY_THREAD_STORAGE_API
|
|
1449
|
+
VALUE target_thread = event_data->thread;
|
|
1450
|
+
#else
|
|
1451
|
+
if (!ruby_native_thread_p()) return;
|
|
1452
|
+
VALUE target_thread = rb_thread_current();
|
|
1453
|
+
#endif
|
|
1334
1454
|
|
|
1335
|
-
|
|
1336
|
-
|
|
1455
|
+
per_thread_context* thread_context = get_per_thread_context(target_thread);
|
|
1456
|
+
if (!thread_context) return;
|
|
1457
|
+
// If non-NULL the thread is profiled and from the main Ractor
|
|
1458
|
+
|
|
1459
|
+
if (event_id == RUBY_INTERNAL_THREAD_EVENT_SUSPENDED) { /* released gvl */
|
|
1460
|
+
thread_context_collector_on_gvl_released(thread_context);
|
|
1461
|
+
} else if (event_id == RUBY_INTERNAL_THREAD_EVENT_READY) { /* waiting for gvl */
|
|
1462
|
+
thread_context_collector_on_gvl_waiting(thread_context);
|
|
1337
1463
|
} else if (event_id == RUBY_INTERNAL_THREAD_EVENT_RESUMED) { /* running/runnable */
|
|
1338
|
-
// Interesting note: A RUBY_INTERNAL_THREAD_EVENT_RESUMED is guaranteed to be called with the GVL being acquired
|
|
1339
|
-
//
|
|
1464
|
+
// Interesting note: A RUBY_INTERNAL_THREAD_EVENT_RESUMED is guaranteed to be called with the GVL being acquired
|
|
1465
|
+
// and on the event thread.
|
|
1466
|
+
// However, on_gvl_event() is called while holding the scheduler lock, so we do as little work as possible here,
|
|
1467
|
+
// and perform the sample in a postponed_job.
|
|
1468
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1469
|
+
if (state == NULL) return; // This should not happen, but just in case...
|
|
1470
|
+
|
|
1471
|
+
// on_gvl_running prepares a stack sample so we need to gate it with `during_sample_enter` to avoid a
|
|
1472
|
+
// SIGPROF signal coming in during that function and potentially causing a corrupted final state.
|
|
1340
1473
|
//
|
|
1341
|
-
//
|
|
1342
|
-
//
|
|
1343
|
-
//
|
|
1474
|
+
// TODO: Currently, the sample prepared in on_gvl_running can still be clobbered if the signal handler runs
|
|
1475
|
+
// after `during_sample_exit` but before `after_gvl_running_from_postponed_job` gets to run. We'll need to fix
|
|
1476
|
+
// that next.
|
|
1477
|
+
during_sample_enter(state);
|
|
1344
1478
|
|
|
1345
|
-
|
|
1346
|
-
target_thread = gvl_profiling_state_maybe_initialize();
|
|
1347
|
-
#endif
|
|
1479
|
+
on_gvl_running_result result = thread_context_collector_on_gvl_running(state->thread_context_collector_instance, target_thread, thread_context);
|
|
1348
1480
|
|
|
1349
|
-
|
|
1481
|
+
during_sample_exit(state);
|
|
1482
|
+
|
|
1483
|
+
if (result.waiting_for_gvl_duration_ns > 0) {
|
|
1484
|
+
state->stats.vm_metrics.gvl_waiting_time_ns_total += (uint64_t) result.waiting_for_gvl_duration_ns;
|
|
1485
|
+
}
|
|
1350
1486
|
|
|
1351
|
-
if (result == ON_GVL_RUNNING_SAMPLE) {
|
|
1487
|
+
if (result.action == ON_GVL_RUNNING_SAMPLE) {
|
|
1352
1488
|
#ifndef NO_POSTPONED_TRIGGER
|
|
1353
1489
|
rb_postponed_job_trigger(after_gvl_running_from_postponed_job_handle);
|
|
1354
1490
|
#else
|
|
1355
1491
|
rb_postponed_job_register_one(0, after_gvl_running_from_postponed_job, NULL);
|
|
1356
1492
|
#endif
|
|
1357
|
-
} else if (result == ON_GVL_RUNNING_DONT_SAMPLE) {
|
|
1358
|
-
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1359
|
-
|
|
1360
|
-
if (state == NULL) return; // This should not happen, but just in case...
|
|
1361
|
-
|
|
1493
|
+
} else if (result.action == ON_GVL_RUNNING_DONT_SAMPLE) {
|
|
1362
1494
|
state->stats.gvl_dont_sample++;
|
|
1363
1495
|
}
|
|
1364
1496
|
} else {
|
|
1365
|
-
|
|
1366
|
-
fprintf(stderr, "[ddtrace] Unexpected value in on_gvl_event (%d)\n", event_id);
|
|
1497
|
+
rb_bug("[ddtrace] Unexpected value in on_gvl_event (%d)\n", event_id);
|
|
1367
1498
|
}
|
|
1368
1499
|
}
|
|
1369
1500
|
|
|
@@ -1373,12 +1504,17 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
|
|
|
1373
1504
|
// This can potentially happen if the CpuAndWallTimeWorker was stopped while the postponed job was waiting to be executed; nothing to do
|
|
1374
1505
|
if (state == NULL) return;
|
|
1375
1506
|
|
|
1376
|
-
state
|
|
1507
|
+
during_sample_enter(state);
|
|
1377
1508
|
|
|
1378
1509
|
// Rescue against any exceptions that happen during sampling
|
|
1379
|
-
safely_call(
|
|
1510
|
+
safely_call(
|
|
1511
|
+
rescued_after_gvl_running_from_postponed_job,
|
|
1512
|
+
state->self_instance,
|
|
1513
|
+
state->self_instance,
|
|
1514
|
+
handle_sampling_failure_rescued_after_gvl_running_from_postponed_job
|
|
1515
|
+
);
|
|
1380
1516
|
|
|
1381
|
-
state
|
|
1517
|
+
during_sample_exit(state);
|
|
1382
1518
|
}
|
|
1383
1519
|
|
|
1384
1520
|
static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance) {
|
|
@@ -1409,8 +1545,90 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
|
|
|
1409
1545
|
|
|
1410
1546
|
return state->gvl_profiling_hook != NULL ? Qtrue : Qfalse;
|
|
1411
1547
|
}
|
|
1548
|
+
|
|
1549
|
+
static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception) {
|
|
1550
|
+
stop(self_instance, exception, "rescued_after_gvl_running_from_postponed_job");
|
|
1551
|
+
return Qnil;
|
|
1552
|
+
}
|
|
1412
1553
|
#else
|
|
1413
1554
|
static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, DDTRACE_UNUSED VALUE instance) {
|
|
1414
1555
|
return Qfalse;
|
|
1415
1556
|
}
|
|
1416
1557
|
#endif
|
|
1558
|
+
|
|
1559
|
+
static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception) {
|
|
1560
|
+
stop(self_instance, exception, "rescued_sample_from_postponed_job");
|
|
1561
|
+
return Qnil;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception) {
|
|
1565
|
+
stop(self_instance, exception, "thread_context_collector_sample_after_gc");
|
|
1566
|
+
return Qnil;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception) {
|
|
1570
|
+
stop(self_instance, exception, "rescued_sample_allocation");
|
|
1571
|
+
return Qnil;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
static VALUE handle_sampling_failure_rescued_after_allocation(VALUE self_instance, VALUE exception) {
|
|
1575
|
+
stop(self_instance, exception, "rescued_after_allocation");
|
|
1576
|
+
return Qnil;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
static VALUE rescued_after_allocation(VALUE self_instance) {
|
|
1580
|
+
cpu_and_wall_time_worker_state *state;
|
|
1581
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1582
|
+
|
|
1583
|
+
thread_context_collector_after_allocation(state->thread_context_collector_instance);
|
|
1584
|
+
|
|
1585
|
+
// Return a dummy VALUE because we're called from rb_rescue2 which requires it
|
|
1586
|
+
return Qnil;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// This postponed job callback is used to finalize heap allocation recordings on Ruby 4+.
|
|
1590
|
+
// During on_newobj_event, calling rb_obj_id() is unsafe because it mutates the object.
|
|
1591
|
+
// So we defer getting the object_id until after the event completes.
|
|
1592
|
+
#pragma GCC diagnostic push
|
|
1593
|
+
#pragma GCC diagnostic ignored "-Wunused-function" // This is only used for some Rubies, but we want to build on all to make it easier to dev
|
|
1594
|
+
static void after_allocation_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
1595
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state;
|
|
1596
|
+
|
|
1597
|
+
if (state == NULL || !ddtrace_rb_ractor_main_p()) return;
|
|
1598
|
+
|
|
1599
|
+
// Protect against nested operations
|
|
1600
|
+
if (state->during_sample) return;
|
|
1601
|
+
|
|
1602
|
+
during_sample_enter(state);
|
|
1603
|
+
|
|
1604
|
+
// NOTE: We're not updating the allocation_sampler here.
|
|
1605
|
+
// This means work done in this function isn't accounted for as profiler overhead.
|
|
1606
|
+
// This is acceptable as the amount of work done here is expected to be small.
|
|
1607
|
+
safely_call(
|
|
1608
|
+
rescued_after_allocation,
|
|
1609
|
+
state->self_instance,
|
|
1610
|
+
state->self_instance,
|
|
1611
|
+
handle_sampling_failure_rescued_after_allocation
|
|
1612
|
+
);
|
|
1613
|
+
|
|
1614
|
+
during_sample_exit(state);
|
|
1615
|
+
}
|
|
1616
|
+
#pragma GCC diagnostic pop
|
|
1617
|
+
|
|
1618
|
+
static inline void during_sample_enter(cpu_and_wall_time_worker_state* state) {
|
|
1619
|
+
// Tell the compiler it's not allowed to reorder the `during_sample` flag with anything that happens after.
|
|
1620
|
+
//
|
|
1621
|
+
// In a few cases, we may be checking this flag from a signal handler, so we need to make sure the compiler didn't
|
|
1622
|
+
// get clever and reordered things in such a way that makes us miss the flag update.
|
|
1623
|
+
//
|
|
1624
|
+
// See https://github.com/ruby/ruby/pull/11036 for a similar change made to the Ruby VM with more context.
|
|
1625
|
+
state->during_sample = true;
|
|
1626
|
+
atomic_signal_fence(memory_order_seq_cst);
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
static inline void during_sample_exit(cpu_and_wall_time_worker_state* state) {
|
|
1630
|
+
// See `during_sample_enter` for more context; in this case we set the fence before to make sure anything that
|
|
1631
|
+
// happens before the fence is not reordered with the flag update.
|
|
1632
|
+
atomic_signal_fence(memory_order_seq_cst);
|
|
1633
|
+
state->during_sample = false;
|
|
1634
|
+
}
|