datadog 2.21.0 → 2.38.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 +574 -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 +301 -82
- 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 +63 -32
- data/ext/datadog_profiling_native_extension/collectors_stack.h +7 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +728 -436
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +19 -6
- 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 +41 -37
- 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 +252 -103
- data/ext/datadog_profiling_native_extension/heap_recorder.h +12 -1
- data/ext/datadog_profiling_native_extension/http_transport.c +66 -68
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +3 -22
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +0 -5
- 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 +40 -43
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +6 -0
- data/ext/datadog_profiling_native_extension/profiling.c +65 -19
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +63 -46
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +17 -5
- 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 +110 -85
- data/ext/datadog_profiling_native_extension/stack_recorder.h +3 -1
- data/ext/datadog_profiling_native_extension/time_helpers.h +13 -7
- 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 +18 -24
- 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 +34 -25
- data/ext/libdatadog_api/process_discovery.c +19 -13
- data/ext/libdatadog_extconf_helpers.rb +94 -11
- 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 +38 -9
- data/lib/datadog/appsec/api_security/sampler.rb +7 -4
- 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 +359 -4
- data/lib/datadog/appsec/assets/waf_rules/strict.json +43 -2
- data/lib/datadog/appsec/assets.rb +1 -1
- data/lib/datadog/appsec/autoload.rb +1 -1
- data/lib/datadog/appsec/component.rb +16 -9
- data/lib/datadog/appsec/compressed_json.rb +3 -3
- data/lib/datadog/appsec/configuration.rb +427 -1
- data/lib/datadog/appsec/context.rb +51 -13
- 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 +2 -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 -12
- 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 -12
- 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 +52 -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 +48 -33
- data/lib/datadog/appsec/contrib/rack/response_body.rb +36 -0
- data/lib/datadog/appsec/contrib/rails/gateway/request.rb +40 -1
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +26 -5
- data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/patcher.rb +38 -0
- data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.rb +2 -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 -11
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +43 -13
- data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +4 -4
- data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +1 -1
- data/lib/datadog/appsec/counter_sampler.rb +25 -0
- data/lib/datadog/appsec/default_header_tags.rb +52 -0
- data/lib/datadog/appsec/event.rb +13 -31
- data/lib/datadog/appsec/ext.rb +3 -0
- 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 +43 -5
- data/lib/datadog/appsec/metrics/exporter.rb +16 -3
- data/lib/datadog/appsec/metrics/telemetry.rb +20 -3
- data/lib/datadog/appsec/metrics/telemetry_exporter.rb +20 -1
- data/lib/datadog/appsec/metrics.rb +5 -5
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +8 -6
- data/lib/datadog/appsec/remote.rb +44 -29
- 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 +26 -5
- data/lib/datadog/appsec/security_engine/result.rb +29 -9
- data/lib/datadog/appsec/security_engine/runner.rb +20 -10
- data/lib/datadog/appsec/security_event.rb +5 -7
- data/lib/datadog/appsec/trace_keeper.rb +18 -6
- 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 +6 -3
- data/lib/datadog/core/configuration/agentless_settings_resolver.rb +2 -2
- data/lib/datadog/core/configuration/base.rb +17 -5
- data/lib/datadog/core/configuration/components.rb +185 -17
- data/lib/datadog/core/configuration/components_state.rb +6 -1
- data/lib/datadog/core/configuration/config_helper.rb +11 -2
- data/lib/datadog/core/configuration/deprecations.rb +2 -2
- data/lib/datadog/core/configuration/option.rb +32 -6
- data/lib/datadog/core/configuration/option_definition.rb +42 -14
- data/lib/datadog/core/configuration/options.rb +49 -12
- data/lib/datadog/core/configuration/settings.rb +115 -16
- data/lib/datadog/core/configuration/stable_config.rb +10 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +379 -300
- data/lib/datadog/core/configuration.rb +3 -3
- 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 +6 -0
- data/lib/datadog/core/ddsketch.rb +19 -0
- 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 -0
- 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/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 +59 -0
- data/lib/datadog/core/logger.rb +1 -1
- data/lib/datadog/core/metrics/client.rb +6 -6
- data/lib/datadog/core/metrics/logging.rb +1 -1
- data/lib/datadog/core/pin.rb +8 -8
- data/lib/datadog/core/process_discovery.rb +21 -18
- 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 +1 -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/metrics.rb +11 -2
- data/lib/datadog/core/semaphore.rb +1 -4
- data/lib/datadog/core/tag_builder.rb +0 -4
- data/lib/datadog/core/tag_normalizer.rb +84 -0
- data/lib/datadog/core/telemetry/component.rb +59 -15
- data/lib/datadog/core/telemetry/emitter.rb +6 -6
- data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +2 -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 +177 -171
- 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 +4 -2
- data/lib/datadog/core/telemetry/logging.rb +17 -5
- 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/api/endpoint.rb +9 -4
- data/lib/datadog/core/transport/http/api/instance.rb +4 -21
- data/lib/datadog/core/transport/http/builder.rb +10 -6
- data/lib/datadog/core/transport/http/client.rb +80 -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 +2 -2
- 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 -21
- 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 +6 -2
- data/lib/datadog/core/utils.rb +3 -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 +10 -3
- data/lib/datadog/data_streams/configuration.rb +50 -0
- data/lib/datadog/data_streams/ext.rb +11 -0
- data/lib/datadog/data_streams/extensions.rb +16 -0
- data/lib/datadog/data_streams/pathway_context.rb +169 -0
- data/lib/datadog/data_streams/processor.rb +511 -0
- data/lib/datadog/data_streams/transport/http/stats.rb +58 -0
- data/lib/datadog/data_streams/transport/http.rb +36 -0
- data/lib/datadog/data_streams/transport/stats.rb +39 -0
- data/lib/datadog/data_streams.rb +100 -0
- data/lib/datadog/di/base.rb +15 -7
- data/lib/datadog/di/boot.rb +7 -2
- data/lib/datadog/di/capture_expression.rb +21 -0
- data/lib/datadog/di/capture_expression_evaluator.rb +71 -0
- data/lib/datadog/di/capture_limits.rb +41 -0
- data/lib/datadog/di/code_tracker.rb +217 -15
- data/lib/datadog/di/component.rb +139 -51
- data/lib/datadog/di/configuration.rb +241 -2
- data/lib/datadog/di/context.rb +82 -0
- data/lib/datadog/di/contrib/active_record.rb +34 -5
- data/lib/datadog/di/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 +623 -147
- data/lib/datadog/di/logger.rb +2 -2
- data/lib/datadog/di/probe.rb +122 -14
- data/lib/datadog/di/probe_builder.rb +124 -3
- data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
- data/lib/datadog/di/probe_file_loader.rb +12 -7
- data/lib/datadog/di/probe_manager.rb +195 -100
- data/lib/datadog/di/probe_notification_builder.rb +321 -90
- data/lib/datadog/di/probe_notifier_worker.rb +112 -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 -93
- data/lib/datadog/di/serializer.rb +310 -29
- 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 +2 -34
- data/lib/datadog/di/transport/http.rb +43 -19
- data/lib/datadog/di/transport/input.rb +112 -38
- data/lib/datadog/di/utils.rb +42 -14
- data/lib/datadog/di.rb +204 -4
- 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 +60 -3
- data/lib/datadog/kit/enable_core_dumps.rb +1 -1
- 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 +1 -1
- 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 +65 -1
- 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 +9 -3
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +4 -1
- data/lib/datadog/opentelemetry/sdk.rb +19 -0
- data/lib/datadog/opentelemetry/signal_configuration.rb +53 -0
- data/lib/datadog/opentelemetry.rb +4 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +71 -12
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +52 -4
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +13 -5
- data/lib/datadog/profiling/collectors/info.rb +18 -4
- data/lib/datadog/profiling/collectors/thread_context.rb +10 -5
- data/lib/datadog/profiling/component.rb +82 -40
- data/lib/datadog/profiling/exporter.rb +41 -12
- 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 +0 -2
- data/lib/datadog/profiling/flush.rb +23 -11
- data/lib/datadog/profiling/http_transport.rb +21 -8
- data/lib/datadog/profiling/load_native_extension.rb +2 -2
- data/lib/datadog/profiling/profiler.rb +31 -14
- data/lib/datadog/profiling/scheduler.rb +10 -9
- data/lib/datadog/profiling/stack_recorder.rb +0 -8
- data/lib/datadog/profiling/tag_builder.rb +37 -3
- data/lib/datadog/profiling/tasks/exec.rb +8 -3
- data/lib/datadog/profiling/tasks/help.rb +1 -0
- data/lib/datadog/profiling/tasks/setup.rb +2 -2
- data/lib/datadog/profiling.rb +1 -3
- data/lib/datadog/ruby_version.rb +25 -0
- data/lib/datadog/single_step_instrument.rb +2 -2
- data/lib/datadog/symbol_database/component.rb +734 -0
- data/lib/datadog/symbol_database/configuration.rb +73 -0
- data/lib/datadog/symbol_database/extensions.rb +19 -0
- data/lib/datadog/symbol_database/extractor.rb +1110 -0
- data/lib/datadog/symbol_database/file_hash.rb +48 -0
- data/lib/datadog/symbol_database/logger.rb +43 -0
- data/lib/datadog/symbol_database/remote.rb +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/buffer.rb +3 -3
- data/lib/datadog/tracing/component.rb +17 -17
- data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
- data/lib/datadog/tracing/configuration/ext.rb +22 -0
- data/lib/datadog/tracing/configuration/settings.rb +96 -4
- 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_mailer/event.rb +4 -1
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +10 -7
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +28 -0
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +3 -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 +5 -2
- data/lib/datadog/tracing/contrib/active_job/data_streams.rb +62 -0
- data/lib/datadog/tracing/contrib/active_job/events/discard.rb +5 -2
- data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +5 -2
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +5 -2
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +5 -2
- data/lib/datadog/tracing/contrib/active_job/events/perform.rb +5 -2
- data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +5 -2
- data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
- data/lib/datadog/tracing/contrib/active_job/patcher.rb +17 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +3 -4
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +2 -2
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +2 -5
- data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +2 -5
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +1 -4
- data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +6 -8
- data/lib/datadog/tracing/contrib/component.rb +3 -3
- data/lib/datadog/tracing/contrib/configurable.rb +18 -3
- data/lib/datadog/tracing/contrib/configuration/resolver.rb +14 -4
- data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +1 -5
- data/lib/datadog/tracing/contrib/dalli/integration.rb +4 -1
- data/lib/datadog/tracing/contrib/dalli/quantize.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/patcher.rb +2 -6
- data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +2 -2
- data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +5 -6
- data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
- 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 +1 -0
- data/lib/datadog/tracing/contrib/excon/middleware.rb +3 -7
- data/lib/datadog/tracing/contrib/ext.rb +3 -1
- data/lib/datadog/tracing/contrib/extensions.rb +19 -2
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +16 -9
- data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +3 -7
- data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +10 -7
- data/lib/datadog/tracing/contrib/grape/instrumentation.rb +13 -8
- 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/unified_trace.rb +75 -44
- data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +3 -7
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +3 -7
- 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.rb +1 -0
- data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +1 -0
- data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +1 -0
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +16 -5
- data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/http/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +4 -8
- 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 +1 -0
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +7 -7
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +16 -5
- data/lib/datadog/tracing/contrib/httprb/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +4 -8
- data/lib/datadog/tracing/contrib/kafka/event.rb +1 -0
- 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/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 +11 -0
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +35 -4
- data/lib/datadog/tracing/contrib/karafka.rb +1 -0
- data/lib/datadog/tracing/contrib/mongodb/parsers.rb +5 -5
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +5 -8
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +6 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +3 -6
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +2 -6
- data/lib/datadog/tracing/contrib/opensearch/quantize.rb +2 -2
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +6 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +15 -18
- data/lib/datadog/tracing/contrib/presto/instrumentation.rb +6 -8
- 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 +24 -0
- 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/tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/racecar/event.rb +1 -5
- 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_tagging.rb +23 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +62 -27
- data/lib/datadog/tracing/contrib/rack/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
- data/lib/datadog/tracing/contrib/rack/route_inference.rb +67 -0
- data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +119 -1
- data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/rails/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
- data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/patcher.rb +0 -1
- data/lib/datadog/tracing/contrib/rails/runner.rb +3 -1
- data/lib/datadog/tracing/contrib/rake/instrumentation.rb +6 -4
- data/lib/datadog/tracing/contrib/redis/quantize.rb +1 -1
- data/lib/datadog/tracing/contrib/redis/tags.rb +1 -6
- data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +2 -0
- data/lib/datadog/tracing/contrib/registerable.rb +11 -0
- data/lib/datadog/tracing/contrib/resque/resque_job.rb +1 -0
- data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +5 -6
- data/lib/datadog/tracing/contrib/roda/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/roda/instrumentation.rb +7 -2
- data/lib/datadog/tracing/contrib/sequel/database.rb +1 -0
- data/lib/datadog/tracing/contrib/sequel/dataset.rb +1 -0
- data/lib/datadog/tracing/contrib/sequel/utils.rb +2 -7
- 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 +1 -0
- 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 +3 -2
- 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/framework.rb +1 -1
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +4 -1
- 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/status_range_matcher.rb +13 -1
- data/lib/datadog/tracing/contrib/stripe/request.rb +1 -1
- data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +1 -0
- data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +6 -0
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -6
- data/lib/datadog/tracing/contrib/utils/quantization/{hash.rb → hash_formatter.rb} +4 -2
- 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 +4 -2
- data/lib/datadog/tracing/distributed/baggage.rb +64 -8
- data/lib/datadog/tracing/distributed/datadog.rb +13 -11
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +1 -14
- data/lib/datadog/tracing/distributed/propagation.rb +35 -3
- data/lib/datadog/tracing/distributed/trace_context.rb +85 -62
- data/lib/datadog/tracing/event.rb +1 -1
- data/lib/datadog/tracing/ext.rb +9 -0
- data/lib/datadog/tracing/metadata/ext.rb +23 -1
- data/lib/datadog/tracing/metadata/tagging.rb +2 -2
- data/lib/datadog/tracing/pipeline.rb +1 -1
- data/lib/datadog/tracing/remote.rb +36 -13
- data/lib/datadog/tracing/sampling/ext.rb +2 -0
- 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 +54 -25
- data/lib/datadog/tracing/sampling/span/rule_parser.rb +2 -2
- data/lib/datadog/tracing/span.rb +1 -1
- data/lib/datadog/tracing/span_event.rb +2 -2
- data/lib/datadog/tracing/span_operation.rb +34 -10
- data/lib/datadog/tracing/sync_writer.rb +0 -1
- data/lib/datadog/tracing/trace_digest.rb +7 -0
- data/lib/datadog/tracing/trace_operation.rb +123 -16
- data/lib/datadog/tracing/tracer.rb +82 -23
- data/lib/datadog/tracing/transport/http/client.rb +12 -26
- data/lib/datadog/tracing/transport/http/traces.rb +2 -50
- data/lib/datadog/tracing/transport/http.rb +15 -9
- data/lib/datadog/tracing/transport/io/client.rb +6 -9
- data/lib/datadog/tracing/transport/io/traces.rb +28 -34
- data/lib/datadog/tracing/transport/trace_formatter.rb +22 -0
- data/lib/datadog/tracing/transport/traces.rb +13 -81
- data/lib/datadog/tracing/workers.rb +2 -1
- data/lib/datadog/tracing/writer.rb +1 -1
- data/lib/datadog/version.rb +2 -2
- data/lib/datadog.rb +8 -1
- metadata +185 -36
- 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/configuration/settings.rb +0 -400
- data/lib/datadog/appsec/contrib/rails/ext.rb +0 -13
- 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
|
|
@@ -103,6 +106,7 @@ typedef struct {
|
|
|
103
106
|
bool gvl_profiling_enabled;
|
|
104
107
|
bool skip_idle_samples_for_testing;
|
|
105
108
|
bool sighandler_sampling_enabled;
|
|
109
|
+
uint32_t cpu_sampling_interval_ms;
|
|
106
110
|
VALUE self_instance;
|
|
107
111
|
VALUE thread_context_collector_instance;
|
|
108
112
|
VALUE idle_sampling_helper_instance;
|
|
@@ -117,6 +121,7 @@ typedef struct {
|
|
|
117
121
|
// When something goes wrong during sampling, we record the Ruby exception here, so that it can be "re-raised" on
|
|
118
122
|
// the CpuAndWallTimeWorker thread
|
|
119
123
|
VALUE failure_exception;
|
|
124
|
+
const char *failure_exception_during_operation;
|
|
120
125
|
// Used by `_native_stop` to flag the worker thread to start (see comment on `_native_sampling_loop`)
|
|
121
126
|
VALUE stop_thread;
|
|
122
127
|
|
|
@@ -139,6 +144,8 @@ typedef struct {
|
|
|
139
144
|
// # Generic stats
|
|
140
145
|
// How many times we tried to trigger a sample
|
|
141
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;
|
|
142
149
|
// How many times we tried to simulate signal delivery
|
|
143
150
|
unsigned int trigger_simulated_signal_delivery_attempts;
|
|
144
151
|
// How many times we actually simulated signal delivery
|
|
@@ -147,8 +154,6 @@ typedef struct {
|
|
|
147
154
|
unsigned int signal_handler_enqueued_sample;
|
|
148
155
|
// How many times we prepared a sample (sampled directly) from the signal handler
|
|
149
156
|
unsigned int signal_handler_prepared_sample;
|
|
150
|
-
// How many times the signal handler was called from the wrong thread
|
|
151
|
-
unsigned int signal_handler_wrong_thread;
|
|
152
157
|
// How many times we actually tried to interrupt a thread for sampling
|
|
153
158
|
unsigned int interrupt_thread_attempts;
|
|
154
159
|
|
|
@@ -175,6 +180,11 @@ typedef struct {
|
|
|
175
180
|
unsigned int allocations_during_sample;
|
|
176
181
|
|
|
177
182
|
// # GVL profiling stats
|
|
183
|
+
// Note that this tracks two kind of samples from RESUMED:
|
|
184
|
+
// * samples when Waiting for GVL for more than the threshold
|
|
185
|
+
// * samples for the skip-samples-while-without-GVL optimization,
|
|
186
|
+
// which are needed to attribute the time without GVL to the correct Ruby stack
|
|
187
|
+
|
|
178
188
|
// How many times we triggered the after_gvl_running sampling
|
|
179
189
|
unsigned int after_gvl_running;
|
|
180
190
|
// How many times we skipped the after_gvl_running sampling
|
|
@@ -183,6 +193,11 @@ typedef struct {
|
|
|
183
193
|
uint64_t gvl_sampling_time_ns_min;
|
|
184
194
|
uint64_t gvl_sampling_time_ns_max;
|
|
185
195
|
uint64_t gvl_sampling_time_ns_total;
|
|
196
|
+
|
|
197
|
+
struct vm_metrics {
|
|
198
|
+
// Total time spent waiting for the GVL
|
|
199
|
+
uint64_t gvl_waiting_time_ns_total;
|
|
200
|
+
} vm_metrics;
|
|
186
201
|
} stats;
|
|
187
202
|
} cpu_and_wall_time_worker_state;
|
|
188
203
|
|
|
@@ -191,17 +206,17 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
191
206
|
static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr);
|
|
192
207
|
static VALUE _native_sampling_loop(VALUE self, VALUE instance);
|
|
193
208
|
static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread);
|
|
194
|
-
static VALUE stop(VALUE self_instance, VALUE optional_exception);
|
|
195
|
-
static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception);
|
|
209
|
+
static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *optional_exception_during_operation);
|
|
210
|
+
static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception, const char *optional_operation_name);
|
|
196
211
|
static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
|
|
197
212
|
static void *run_sampling_trigger_loop(void *state_ptr);
|
|
198
213
|
static void interrupt_sampling_trigger_loop(void *state_ptr);
|
|
199
214
|
static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
200
215
|
static VALUE rescued_sample_from_postponed_job(VALUE self_instance);
|
|
201
|
-
static VALUE handle_sampling_failure(VALUE self_instance, VALUE exception);
|
|
202
216
|
static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self);
|
|
203
217
|
static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance);
|
|
204
218
|
static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
219
|
+
static VALUE _native_failure_exception_during_operation(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
205
220
|
static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
|
|
206
221
|
static VALUE _native_install_testing_signal_handler(DDTRACE_UNUSED VALUE self);
|
|
207
222
|
static VALUE _native_remove_testing_signal_handler(DDTRACE_UNUSED VALUE self);
|
|
@@ -209,7 +224,12 @@ static VALUE _native_trigger_sample(DDTRACE_UNUSED VALUE self);
|
|
|
209
224
|
static VALUE _native_gc_tracepoint(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
210
225
|
static void on_gc_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused);
|
|
211
226
|
static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
212
|
-
static VALUE safely_call(
|
|
227
|
+
static VALUE safely_call(
|
|
228
|
+
VALUE (*function_to_call_safely)(VALUE),
|
|
229
|
+
VALUE function_to_call_safely_arg,
|
|
230
|
+
VALUE instance,
|
|
231
|
+
VALUE (*handle_sampling_failure)(VALUE, VALUE)
|
|
232
|
+
);
|
|
213
233
|
static VALUE _native_simulate_handle_sampling_signal(DDTRACE_UNUSED VALUE self);
|
|
214
234
|
static VALUE _native_simulate_sample_from_postponed_job(DDTRACE_UNUSED VALUE self);
|
|
215
235
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
@@ -225,18 +245,26 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
|
|
|
225
245
|
static void disable_tracepoints(cpu_and_wall_time_worker_state *state);
|
|
226
246
|
static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self);
|
|
227
247
|
static VALUE rescued_sample_allocation(VALUE tracepoint_data);
|
|
248
|
+
static VALUE rescued_after_allocation(VALUE self_instance);
|
|
228
249
|
static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error);
|
|
250
|
+
static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state);
|
|
229
251
|
static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
|
|
230
252
|
static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self);
|
|
231
253
|
static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);
|
|
232
254
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
233
|
-
static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused);
|
|
234
|
-
static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
255
|
+
static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused);
|
|
256
|
+
static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
257
|
+
static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance);
|
|
258
|
+
static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception);
|
|
235
259
|
#endif
|
|
236
|
-
static VALUE rescued_after_gvl_running_from_postponed_job(VALUE self_instance);
|
|
237
260
|
static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance);
|
|
261
|
+
static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception);
|
|
262
|
+
static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception);
|
|
263
|
+
static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception);
|
|
264
|
+
static VALUE handle_sampling_failure_rescued_after_allocation(VALUE self_instance, VALUE exception);
|
|
238
265
|
static inline void during_sample_enter(cpu_and_wall_time_worker_state* state);
|
|
239
266
|
static inline void during_sample_exit(cpu_and_wall_time_worker_state* state);
|
|
267
|
+
static void after_allocation_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
240
268
|
|
|
241
269
|
// We're using `on_newobj_event` function with `rb_add_event_hook2`, which requires in its public signature a function
|
|
242
270
|
// with signature `rb_event_hook_func_t` which doesn't match `on_newobj_event`.
|
|
@@ -262,6 +290,7 @@ static inline void during_sample_exit(cpu_and_wall_time_worker_state* state);
|
|
|
262
290
|
// (e.g. signal handler) where it's impossible or just awkward to pass it as an argument.
|
|
263
291
|
static VALUE active_sampler_instance = Qnil;
|
|
264
292
|
static cpu_and_wall_time_worker_state *active_sampler_instance_state = NULL;
|
|
293
|
+
static VALUE clock_failure_exception_class = Qnil;
|
|
265
294
|
|
|
266
295
|
// See handle_sampling_signal for details on what this does
|
|
267
296
|
#ifdef NO_POSTPONED_TRIGGER
|
|
@@ -283,13 +312,15 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
283
312
|
sample_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, sample_from_postponed_job, NULL);
|
|
284
313
|
after_gc_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gc_from_postponed_job, NULL);
|
|
285
314
|
after_gvl_running_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gvl_running_from_postponed_job, NULL);
|
|
315
|
+
after_allocation_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_allocation_from_postponed_job, NULL);
|
|
286
316
|
|
|
287
317
|
if (
|
|
288
318
|
sample_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
289
319
|
after_gc_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
290
|
-
after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
|
|
320
|
+
after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
321
|
+
after_allocation_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
|
|
291
322
|
) {
|
|
292
|
-
|
|
323
|
+
raise_error(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
|
|
293
324
|
}
|
|
294
325
|
#else
|
|
295
326
|
gc_finalize_deferred_workaround = objspace_ptr_for_gc_finalize_deferred_workaround();
|
|
@@ -299,6 +330,8 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
299
330
|
VALUE collectors_cpu_and_wall_time_worker_class = rb_define_class_under(collectors_module, "CpuAndWallTimeWorker", rb_cObject);
|
|
300
331
|
// Hosts methods used for testing the native code using RSpec
|
|
301
332
|
VALUE testing_module = rb_define_module_under(collectors_cpu_and_wall_time_worker_class, "Testing");
|
|
333
|
+
clock_failure_exception_class = rb_define_class_under(collectors_cpu_and_wall_time_worker_class, "ClockFailure", rb_eRuntimeError);
|
|
334
|
+
rb_gc_register_mark_object(clock_failure_exception_class);
|
|
302
335
|
|
|
303
336
|
// Instances of the CpuAndWallTimeWorker class are "TypedData" objects.
|
|
304
337
|
// "TypedData" objects are special objects in the Ruby VM that can wrap C structs.
|
|
@@ -318,6 +351,7 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
318
351
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stats_reset_not_thread_safe", _native_stats_reset_not_thread_safe, 1);
|
|
319
352
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_allocation_count", _native_allocation_count, 0);
|
|
320
353
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_is_running?", _native_is_running, 1);
|
|
354
|
+
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_failure_exception_during_operation", _native_failure_exception_during_operation, 1);
|
|
321
355
|
rb_define_singleton_method(testing_module, "_native_current_sigprof_signal_handler", _native_current_sigprof_signal_handler, 0);
|
|
322
356
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_hold_signals", _native_hold_signals, 0);
|
|
323
357
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_resume_signals", _native_resume_signals, 0);
|
|
@@ -362,6 +396,7 @@ static VALUE _native_new(VALUE klass) {
|
|
|
362
396
|
state->gvl_profiling_enabled = false;
|
|
363
397
|
state->skip_idle_samples_for_testing = false;
|
|
364
398
|
state->sighandler_sampling_enabled = false;
|
|
399
|
+
state->cpu_sampling_interval_ms = 10;
|
|
365
400
|
state->thread_context_collector_instance = Qnil;
|
|
366
401
|
state->idle_sampling_helper_instance = Qnil;
|
|
367
402
|
state->owner_thread = Qnil;
|
|
@@ -370,6 +405,7 @@ static VALUE _native_new(VALUE klass) {
|
|
|
370
405
|
|
|
371
406
|
atomic_init(&state->should_run, false);
|
|
372
407
|
state->failure_exception = Qnil;
|
|
408
|
+
state->failure_exception_during_operation = NULL;
|
|
373
409
|
state->stop_thread = Qnil;
|
|
374
410
|
|
|
375
411
|
during_sample_exit(state);
|
|
@@ -405,6 +441,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
405
441
|
VALUE gvl_profiling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("gvl_profiling_enabled")));
|
|
406
442
|
VALUE skip_idle_samples_for_testing = rb_hash_fetch(options, ID2SYM(rb_intern("skip_idle_samples_for_testing")));
|
|
407
443
|
VALUE sighandler_sampling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("sighandler_sampling_enabled")));
|
|
444
|
+
VALUE cpu_sampling_interval_ms = rb_hash_fetch(options, ID2SYM(rb_intern("cpu_sampling_interval_ms")));
|
|
408
445
|
|
|
409
446
|
ENFORCE_BOOLEAN(gc_profiling_enabled);
|
|
410
447
|
ENFORCE_BOOLEAN(no_signals_workaround_enabled);
|
|
@@ -415,6 +452,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
415
452
|
ENFORCE_BOOLEAN(gvl_profiling_enabled);
|
|
416
453
|
ENFORCE_BOOLEAN(skip_idle_samples_for_testing)
|
|
417
454
|
ENFORCE_BOOLEAN(sighandler_sampling_enabled)
|
|
455
|
+
ENFORCE_TYPE(cpu_sampling_interval_ms, T_FIXNUM);
|
|
418
456
|
|
|
419
457
|
cpu_and_wall_time_worker_state *state;
|
|
420
458
|
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
@@ -427,6 +465,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
427
465
|
state->gvl_profiling_enabled = (gvl_profiling_enabled == Qtrue);
|
|
428
466
|
state->skip_idle_samples_for_testing = (skip_idle_samples_for_testing == Qtrue);
|
|
429
467
|
state->sighandler_sampling_enabled = (sighandler_sampling_enabled == Qtrue);
|
|
468
|
+
state->cpu_sampling_interval_ms = NUM2INT(cpu_sampling_interval_ms);
|
|
430
469
|
|
|
431
470
|
double total_overhead_target_percentage = NUM2DBL(dynamic_sampling_rate_overhead_target_percentage);
|
|
432
471
|
if (!state->allocation_profiling_enabled) {
|
|
@@ -472,10 +511,7 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
472
511
|
cpu_and_wall_time_worker_state *old_state = active_sampler_instance_state;
|
|
473
512
|
if (old_state != NULL) {
|
|
474
513
|
if (is_thread_alive(old_state->owner_thread)) {
|
|
475
|
-
|
|
476
|
-
rb_eRuntimeError,
|
|
477
|
-
"Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread"
|
|
478
|
-
);
|
|
514
|
+
raise_error(rb_eRuntimeError, "Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread");
|
|
479
515
|
} else {
|
|
480
516
|
// The previously active thread seems to have died without cleaning up after itself.
|
|
481
517
|
// In this case, we can still go ahead and start the profiler BUT we make sure to disable any existing tracepoint
|
|
@@ -492,11 +528,20 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
492
528
|
// situation we stop immediately and never even start the sampling trigger loop.
|
|
493
529
|
if (state->stop_thread == rb_thread_current()) return Qnil;
|
|
494
530
|
|
|
531
|
+
thread_context_collector_profiler_internal_thread_started();
|
|
532
|
+
|
|
495
533
|
// Reset the dynamic sampling rate state, if any (reminder: the monotonic clock reference may change after a fork)
|
|
496
534
|
dynamic_sampling_rate_reset(&state->cpu_dynamic_sampling_rate);
|
|
497
535
|
long now = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
498
536
|
discrete_dynamic_sampler_reset(&state->allocation_sampler, now);
|
|
499
537
|
|
|
538
|
+
// Reset per-thread state, if any. This ensures there's no leftover state from a previous profiler run that would
|
|
539
|
+
// affect or be included in samples taken by this profiler about to run.
|
|
540
|
+
//
|
|
541
|
+
// NOTE: This needs to be called before we enable any tracepoints or anything that could trigger samples (e.g.
|
|
542
|
+
// reset cannot be concurrent with any sampling activity)
|
|
543
|
+
thread_context_collector_reset_all_per_thread_contexts(state->thread_context_collector_instance);
|
|
544
|
+
|
|
500
545
|
// This write to a global is thread-safe BECAUSE we're still holding on to the global VM lock at this point
|
|
501
546
|
active_sampler_instance_state = state;
|
|
502
547
|
active_sampler_instance = instance;
|
|
@@ -506,6 +551,14 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
506
551
|
|
|
507
552
|
block_sigprof_signal_handler_from_running_in_current_thread(); // We want to interrupt the thread with the global VM lock, never this one
|
|
508
553
|
|
|
554
|
+
#ifdef __APPLE__
|
|
555
|
+
// Promote the native thread that will run the sampling loop to a realtime scheduling policy so
|
|
556
|
+
// its periodic wake-ups are not subject to the default ~1-2ms timeshare wake latency. The matching
|
|
557
|
+
// demote happens below in this same function, alongside the SIGPROF unblock dance, so both per-thread
|
|
558
|
+
// policy changes are reverted before Ruby reuses this native thread for an unrelated Ruby thread.
|
|
559
|
+
promote_sampler_thread_to_realtime(MILLIS_AS_NS(state->cpu_sampling_interval_ms));
|
|
560
|
+
#endif
|
|
561
|
+
|
|
509
562
|
// Release GVL, get to the actual work!
|
|
510
563
|
int exception_state;
|
|
511
564
|
rb_protect(release_gvl_and_run_sampling_trigger_loop, instance, &exception_state);
|
|
@@ -527,6 +580,14 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
527
580
|
// had SIGPROF delivery blocked. :hide_the_pain_harold:
|
|
528
581
|
unblock_sigprof_signal_handler_from_running_in_current_thread();
|
|
529
582
|
|
|
583
|
+
#ifdef __APPLE__
|
|
584
|
+
// Pairs with promote_sampler_thread_to_realtime above. Same reasoning as the SIGPROF unblock:
|
|
585
|
+
// Ruby may reuse this native thread for an unrelated Ruby thread that would otherwise inherit
|
|
586
|
+
// our scheduler policy. Doing it here also covers the exception path, since grab_gvl_and_sample
|
|
587
|
+
// can raise and unwind out of the sampling loop.
|
|
588
|
+
demote_sampler_thread_from_realtime();
|
|
589
|
+
#endif
|
|
590
|
+
|
|
530
591
|
// Why replace and not use remove the signal handler? We do this because when a process receives a SIGPROF without
|
|
531
592
|
// having an explicit signal handler set up, the process will instantly terminate with a confusing
|
|
532
593
|
// "Profiling timer expired" message left behind. (This message doesn't come from us -- it's the default message for
|
|
@@ -537,8 +598,9 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
537
598
|
// @ivoanjo: I suspect this will never happen, but the cost of getting it wrong is really high (VM terminates) so this
|
|
538
599
|
// is a just-in-case situation.
|
|
539
600
|
//
|
|
540
|
-
// Note 2:
|
|
541
|
-
replace_sigprof_signal_handler_with_empty_handler
|
|
601
|
+
// Note 2: If exception_state is set, we have a pending exception that we'll re-raise below.
|
|
602
|
+
// In that case, we don't want replace_sigprof_signal_handler_with_empty_handler to raise another exception.
|
|
603
|
+
replace_sigprof_signal_handler_with_empty_handler(handle_sampling_signal, !exception_state);
|
|
542
604
|
|
|
543
605
|
// Ensure that instance is not garbage collected while the native sampling loop is running; this is probably not needed, but just in case
|
|
544
606
|
RB_GC_GUARD(instance);
|
|
@@ -554,22 +616,24 @@ static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE
|
|
|
554
616
|
|
|
555
617
|
state->stop_thread = worker_thread;
|
|
556
618
|
|
|
557
|
-
return stop(self_instance,
|
|
619
|
+
return stop(self_instance, Qnil, NULL);
|
|
558
620
|
}
|
|
559
621
|
|
|
560
|
-
|
|
622
|
+
// When providing an `optional_exception`, `optional_exception_during_operation` should be provided as well
|
|
623
|
+
static void stop_state(cpu_and_wall_time_worker_state *state, VALUE optional_exception, const char *optional_exception_during_operation) {
|
|
561
624
|
atomic_store(&state->should_run, false);
|
|
562
625
|
state->failure_exception = optional_exception;
|
|
626
|
+
state->failure_exception_during_operation = optional_exception_during_operation;
|
|
563
627
|
|
|
564
628
|
// Disable the tracepoints as soon as possible, so the VM doesn't keep on calling them
|
|
565
629
|
disable_tracepoints(state);
|
|
566
630
|
}
|
|
567
631
|
|
|
568
|
-
static VALUE stop(VALUE self_instance, VALUE optional_exception) {
|
|
632
|
+
static VALUE stop(VALUE self_instance, VALUE optional_exception, const char *optional_exception_during_operation) {
|
|
569
633
|
cpu_and_wall_time_worker_state *state;
|
|
570
634
|
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
571
635
|
|
|
572
|
-
stop_state(state, optional_exception);
|
|
636
|
+
stop_state(state, optional_exception, optional_exception_during_operation);
|
|
573
637
|
|
|
574
638
|
return Qtrue;
|
|
575
639
|
}
|
|
@@ -578,19 +642,19 @@ static VALUE stop(VALUE self_instance, VALUE optional_exception) {
|
|
|
578
642
|
// We need to be careful not to change any state that may be observed OR to restore it if we do. For instance, if anything
|
|
579
643
|
// we do here can set `errno`, then we must be careful to restore the old `errno` after the fact.
|
|
580
644
|
static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext) {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
//
|
|
584
|
-
if (state == NULL) return;
|
|
585
|
-
|
|
645
|
+
// We must first check that we landed on the correct thread and can proceed.
|
|
646
|
+
// We must never touch the state before we confirm that we have landed on the thread that is holding the GVL on the main
|
|
647
|
+
// ractor as otherwise we may be concurrent with the profiler shutting down and removing its state.
|
|
586
648
|
if (
|
|
587
649
|
!ruby_native_thread_p() || // Not a Ruby thread
|
|
588
650
|
!is_current_thread_holding_the_gvl() || // Not safe to enqueue a sample from this thread
|
|
589
651
|
!ddtrace_rb_ractor_main_p() // We're not on the main Ractor; we currently don't support profiling non-main Ractors
|
|
590
|
-
)
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
652
|
+
) return;
|
|
653
|
+
|
|
654
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
655
|
+
|
|
656
|
+
// This can potentially happen if the CpuAndWallTimeWorker was stopped while the signal delivery was happening; nothing to do
|
|
657
|
+
if (state == NULL) return;
|
|
594
658
|
|
|
595
659
|
// We assume there can be no concurrent nor nested calls to handle_sampling_signal because
|
|
596
660
|
// 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)
|
|
@@ -607,7 +671,7 @@ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED si
|
|
|
607
671
|
if (sample_from_signal_handler) {
|
|
608
672
|
// Buffer current stack trace. Note that this will not actually record the sample, for that we still need to wait
|
|
609
673
|
// until the postponed job below gets run.
|
|
610
|
-
bool prepared = thread_context_collector_prepare_sample_inside_signal_handler(
|
|
674
|
+
bool prepared = thread_context_collector_prepare_sample_inside_signal_handler();
|
|
611
675
|
|
|
612
676
|
if (prepared) state->stats.signal_handler_prepared_sample++;
|
|
613
677
|
}
|
|
@@ -647,7 +711,7 @@ static void handle_sampling_signal(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED si
|
|
|
647
711
|
static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
648
712
|
cpu_and_wall_time_worker_state *state = (cpu_and_wall_time_worker_state *) state_ptr;
|
|
649
713
|
|
|
650
|
-
uint64_t minimum_time_between_signals = MILLIS_AS_NS(
|
|
714
|
+
uint64_t minimum_time_between_signals = MILLIS_AS_NS(state->cpu_sampling_interval_ms);
|
|
651
715
|
|
|
652
716
|
while (atomic_load(&state->should_run)) {
|
|
653
717
|
state->stats.trigger_sample_attempts++;
|
|
@@ -669,6 +733,10 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
|
669
733
|
// we're doing this, so we may still not signal the correct thread from time to time, but our signal handler
|
|
670
734
|
// includes a check to see if it got called in the right thread
|
|
671
735
|
state->stats.interrupt_thread_attempts++;
|
|
736
|
+
|
|
737
|
+
// Pick up any last-minute attempts to stop before we send the signal
|
|
738
|
+
if (!atomic_load(&state->should_run)) return NULL;
|
|
739
|
+
|
|
672
740
|
pthread_kill(owner.owner, SIGPROF);
|
|
673
741
|
} else {
|
|
674
742
|
if (state->skip_idle_samples_for_testing) {
|
|
@@ -696,7 +764,10 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
|
696
764
|
// `dynamic_sampling_rate_get_sleep` may have changed while the above sleep was ongoing.
|
|
697
765
|
uint64_t extra_sleep =
|
|
698
766
|
dynamic_sampling_rate_get_sleep(&state->cpu_dynamic_sampling_rate, monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE));
|
|
699
|
-
if (state->dynamic_sampling_rate_enabled && extra_sleep > 0)
|
|
767
|
+
if (state->dynamic_sampling_rate_enabled && extra_sleep > 0) {
|
|
768
|
+
state->stats.trigger_sample_extra_sleep++;
|
|
769
|
+
sleep_for(extra_sleep);
|
|
770
|
+
}
|
|
700
771
|
}
|
|
701
772
|
|
|
702
773
|
return NULL; // Unused
|
|
@@ -726,7 +797,12 @@ static void sample_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
|
726
797
|
during_sample_enter(state);
|
|
727
798
|
|
|
728
799
|
// Rescue against any exceptions that happen during sampling
|
|
729
|
-
safely_call(
|
|
800
|
+
safely_call(
|
|
801
|
+
rescued_sample_from_postponed_job,
|
|
802
|
+
state->self_instance,
|
|
803
|
+
state->self_instance,
|
|
804
|
+
handle_sampling_failure_rescued_sample_from_postponed_job
|
|
805
|
+
);
|
|
730
806
|
|
|
731
807
|
during_sample_exit(state);
|
|
732
808
|
}
|
|
@@ -744,8 +820,7 @@ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
|
|
|
744
820
|
|
|
745
821
|
state->stats.cpu_sampled++;
|
|
746
822
|
|
|
747
|
-
|
|
748
|
-
thread_context_collector_sample(state->thread_context_collector_instance, wall_time_ns_before_sample, profiler_overhead_stack_thread);
|
|
823
|
+
thread_context_collector_sample(state->thread_context_collector_instance, wall_time_ns_before_sample);
|
|
749
824
|
|
|
750
825
|
long wall_time_ns_after_sample = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
751
826
|
long delta_ns = wall_time_ns_after_sample - wall_time_ns_before_sample;
|
|
@@ -763,11 +838,6 @@ static VALUE rescued_sample_from_postponed_job(VALUE self_instance) {
|
|
|
763
838
|
return Qnil;
|
|
764
839
|
}
|
|
765
840
|
|
|
766
|
-
static VALUE handle_sampling_failure(VALUE self_instance, VALUE exception) {
|
|
767
|
-
stop(self_instance, exception);
|
|
768
|
-
return Qnil;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
841
|
// This method exists only to enable testing Datadog::Profiling::Collectors::CpuAndWallTimeWorker behavior using RSpec.
|
|
772
842
|
// It SHOULD NOT be used for other purposes.
|
|
773
843
|
static VALUE _native_current_sigprof_signal_handler(DDTRACE_UNUSED VALUE self) {
|
|
@@ -806,22 +876,19 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
|
|
|
806
876
|
|
|
807
877
|
if (state->gvl_profiling_enabled) {
|
|
808
878
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
809
|
-
#ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS
|
|
810
|
-
gvl_profiling_state_thread_tracking_workaround();
|
|
811
|
-
#endif
|
|
812
|
-
|
|
813
879
|
state->gvl_profiling_hook = rb_internal_thread_add_event_hook(
|
|
814
880
|
on_gvl_event,
|
|
815
881
|
(
|
|
816
882
|
// For now we're only asking for these events, even though there's more
|
|
817
883
|
// (e.g. check docs or gvl-tracing gem)
|
|
884
|
+
RUBY_INTERNAL_THREAD_EVENT_SUSPENDED | /* released gvl */
|
|
818
885
|
RUBY_INTERNAL_THREAD_EVENT_READY /* waiting for gvl */ |
|
|
819
886
|
RUBY_INTERNAL_THREAD_EVENT_RESUMED /* running/runnable */
|
|
820
887
|
),
|
|
821
888
|
NULL
|
|
822
889
|
);
|
|
823
890
|
#else
|
|
824
|
-
|
|
891
|
+
raise_error(rb_eArgError, "GVL profiling is not supported in this Ruby version");
|
|
825
892
|
#endif
|
|
826
893
|
}
|
|
827
894
|
|
|
@@ -833,6 +900,8 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
|
|
|
833
900
|
// If we stopped sampling due to an exception, re-raise it (now in the worker thread)
|
|
834
901
|
if (state->failure_exception != Qnil) rb_exc_raise(state->failure_exception);
|
|
835
902
|
|
|
903
|
+
thread_context_collector_profiler_internal_thread_done(state->thread_context_collector_instance);
|
|
904
|
+
|
|
836
905
|
return Qnil;
|
|
837
906
|
}
|
|
838
907
|
|
|
@@ -844,6 +913,15 @@ static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
|
844
913
|
return (state != NULL && is_thread_alive(state->owner_thread) && state->self_instance == instance) ? Qtrue : Qfalse;
|
|
845
914
|
}
|
|
846
915
|
|
|
916
|
+
static VALUE _native_failure_exception_during_operation(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
917
|
+
cpu_and_wall_time_worker_state *state;
|
|
918
|
+
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
919
|
+
|
|
920
|
+
if (state->failure_exception_during_operation == NULL) return Qnil;
|
|
921
|
+
|
|
922
|
+
return rb_str_new_cstr(state->failure_exception_during_operation);
|
|
923
|
+
}
|
|
924
|
+
|
|
847
925
|
static void testing_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext) {
|
|
848
926
|
/* Does nothing on purpose */
|
|
849
927
|
}
|
|
@@ -936,14 +1014,24 @@ static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
|
936
1014
|
|
|
937
1015
|
during_sample_enter(state);
|
|
938
1016
|
|
|
939
|
-
safely_call(
|
|
1017
|
+
safely_call(
|
|
1018
|
+
thread_context_collector_sample_after_gc,
|
|
1019
|
+
state->thread_context_collector_instance,
|
|
1020
|
+
state->self_instance,
|
|
1021
|
+
handle_sampling_failure_thread_context_collector_sample_after_gc
|
|
1022
|
+
);
|
|
940
1023
|
|
|
941
1024
|
during_sample_exit(state);
|
|
942
1025
|
}
|
|
943
1026
|
|
|
944
1027
|
// Equivalent to Ruby begin/rescue call, where we call a C function and jump to the exception handler if an
|
|
945
1028
|
// exception gets raised within
|
|
946
|
-
static VALUE safely_call(
|
|
1029
|
+
static VALUE safely_call(
|
|
1030
|
+
VALUE (*function_to_call_safely)(VALUE),
|
|
1031
|
+
VALUE function_to_call_safely_arg,
|
|
1032
|
+
VALUE instance,
|
|
1033
|
+
VALUE (*handle_sampling_failure)(VALUE, VALUE)
|
|
1034
|
+
) {
|
|
947
1035
|
VALUE exception_handler_function_arg = instance;
|
|
948
1036
|
return rb_rescue2(
|
|
949
1037
|
function_to_call_safely,
|
|
@@ -977,6 +1065,9 @@ static VALUE _native_simulate_sample_from_postponed_job(DDTRACE_UNUSED VALUE sel
|
|
|
977
1065
|
//
|
|
978
1066
|
// In the future, if we add more other components with tracepoints, we will need to coordinate stopping all such
|
|
979
1067
|
// tracepoints before doing the other cleaning steps.
|
|
1068
|
+
//
|
|
1069
|
+
// Note that tests call this method directly in the same process without forking,
|
|
1070
|
+
// and in such a case non-current Threads keep running.
|
|
980
1071
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
981
1072
|
cpu_and_wall_time_worker_state *state;
|
|
982
1073
|
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
@@ -1013,11 +1104,11 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
|
1013
1104
|
VALUE stats_as_hash = rb_hash_new();
|
|
1014
1105
|
VALUE arguments[] = {
|
|
1015
1106
|
ID2SYM(rb_intern("trigger_sample_attempts")), /* => */ UINT2NUM(state->stats.trigger_sample_attempts),
|
|
1107
|
+
ID2SYM(rb_intern("trigger_sample_extra_sleep")), /* => */ UINT2NUM(state->stats.trigger_sample_extra_sleep),
|
|
1016
1108
|
ID2SYM(rb_intern("trigger_simulated_signal_delivery_attempts")), /* => */ UINT2NUM(state->stats.trigger_simulated_signal_delivery_attempts),
|
|
1017
1109
|
ID2SYM(rb_intern("simulated_signal_delivery")), /* => */ UINT2NUM(state->stats.simulated_signal_delivery),
|
|
1018
1110
|
ID2SYM(rb_intern("signal_handler_enqueued_sample")), /* => */ UINT2NUM(state->stats.signal_handler_enqueued_sample),
|
|
1019
1111
|
ID2SYM(rb_intern("signal_handler_prepared_sample")), /* => */ UINT2NUM(state->stats.signal_handler_prepared_sample),
|
|
1020
|
-
ID2SYM(rb_intern("signal_handler_wrong_thread")), /* => */ UINT2NUM(state->stats.signal_handler_wrong_thread),
|
|
1021
1112
|
ID2SYM(rb_intern("interrupt_thread_attempts")), /* => */ UINT2NUM(state->stats.interrupt_thread_attempts),
|
|
1022
1113
|
|
|
1023
1114
|
// CPU Stats
|
|
@@ -1047,8 +1138,12 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
|
1047
1138
|
ID2SYM(rb_intern("gvl_sampling_time_ns_max")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_max, > 0, ULL2NUM),
|
|
1048
1139
|
ID2SYM(rb_intern("gvl_sampling_time_ns_total")), /* => */ RUBY_NUM_OR_NIL(state->stats.gvl_sampling_time_ns_total, > 0, ULL2NUM),
|
|
1049
1140
|
ID2SYM(rb_intern("gvl_sampling_time_ns_avg")), /* => */ RUBY_AVG_OR_NIL(state->stats.gvl_sampling_time_ns_total, state->stats.after_gvl_running),
|
|
1141
|
+
ID2SYM(rb_intern("gvl_waiting_time_ns_total")), /* => */ state->gvl_profiling_enabled ? ULL2NUM(state->stats.vm_metrics.gvl_waiting_time_ns_total) : Qnil,
|
|
1050
1142
|
};
|
|
1051
1143
|
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(stats_as_hash, arguments[i], arguments[i+1]);
|
|
1144
|
+
|
|
1145
|
+
thread_context_collector_stats(state->thread_context_collector_instance, stats_as_hash);
|
|
1146
|
+
|
|
1052
1147
|
return stats_as_hash;
|
|
1053
1148
|
}
|
|
1054
1149
|
|
|
@@ -1056,6 +1151,7 @@ static VALUE _native_stats_reset_not_thread_safe(DDTRACE_UNUSED VALUE self, VALU
|
|
|
1056
1151
|
cpu_and_wall_time_worker_state *state;
|
|
1057
1152
|
TypedData_Get_Struct(instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1058
1153
|
reset_stats_not_thread_safe(state);
|
|
1154
|
+
thread_context_collector_stats_reset_not_thread_safe(state->thread_context_collector_instance);
|
|
1059
1155
|
return Qnil;
|
|
1060
1156
|
}
|
|
1061
1157
|
|
|
@@ -1087,6 +1183,7 @@ static void reset_stats_not_thread_safe(cpu_and_wall_time_worker_state *state) {
|
|
|
1087
1183
|
.cpu_sampling_time_ns_min = UINT64_MAX,
|
|
1088
1184
|
.allocation_sampling_time_ns_min = UINT64_MAX,
|
|
1089
1185
|
.gvl_sampling_time_ns_min = UINT64_MAX,
|
|
1186
|
+
// Other fields are reset to 0
|
|
1090
1187
|
};
|
|
1091
1188
|
}
|
|
1092
1189
|
|
|
@@ -1119,7 +1216,7 @@ static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self) {
|
|
|
1119
1216
|
#define HANDLE_CLOCK_FAILURE(call) ({ \
|
|
1120
1217
|
long _result = (call); \
|
|
1121
1218
|
if (_result == 0) { \
|
|
1122
|
-
|
|
1219
|
+
delayed_error_clock_failure(state); \
|
|
1123
1220
|
return; \
|
|
1124
1221
|
} \
|
|
1125
1222
|
_result; \
|
|
@@ -1164,13 +1261,22 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
|
|
|
1164
1261
|
return;
|
|
1165
1262
|
}
|
|
1166
1263
|
|
|
1264
|
+
VALUE current_thread = rb_thread_current();
|
|
1265
|
+
|
|
1167
1266
|
// If Ruby is in the middle of raising an exception, we don't want to try to sample. This is because if we accidentally
|
|
1168
1267
|
// trigger an exception inside the profiler code, bad things will happen (specifically, Ruby will try to kill off the
|
|
1169
1268
|
// thread even though we may try to catch the exception).
|
|
1170
1269
|
//
|
|
1171
1270
|
// Note that "in the middle of raising an exception" means the exception itself has already been allocated.
|
|
1172
1271
|
// What's getting allocated now is probably the backtrace objects (@ivoanjo or at least that's what I've observed)
|
|
1173
|
-
if (is_raised_flag_set(
|
|
1272
|
+
if (is_raised_flag_set(current_thread)) {
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
per_thread_context *thread_context = get_per_thread_context(current_thread);
|
|
1277
|
+
if (!thread_context) {
|
|
1278
|
+
// Context is created eagerly via on_thread_begin_event, so this should not normally be NULL.
|
|
1279
|
+
// We keep the guard since we can't allocate here (inside on_newobj_event).
|
|
1174
1280
|
return;
|
|
1175
1281
|
}
|
|
1176
1282
|
|
|
@@ -1203,12 +1309,17 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
|
|
|
1203
1309
|
during_sample_enter(state);
|
|
1204
1310
|
|
|
1205
1311
|
// Rescue against any exceptions that happen during sampling
|
|
1206
|
-
safely_call(
|
|
1312
|
+
safely_call(
|
|
1313
|
+
rescued_sample_allocation,
|
|
1314
|
+
(VALUE) thread_context,
|
|
1315
|
+
state->self_instance,
|
|
1316
|
+
handle_sampling_failure_rescued_sample_allocation
|
|
1317
|
+
);
|
|
1207
1318
|
|
|
1208
1319
|
if (state->dynamic_sampling_rate_enabled) {
|
|
1209
1320
|
long now = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
|
|
1210
1321
|
if (now == 0) {
|
|
1211
|
-
|
|
1322
|
+
delayed_error_clock_failure(state);
|
|
1212
1323
|
// NOTE: Not short-circuiting here to make sure cleanup happens
|
|
1213
1324
|
}
|
|
1214
1325
|
uint64_t sampling_time_ns = discrete_dynamic_sampler_after_sample(&state->allocation_sampler, now);
|
|
@@ -1252,7 +1363,8 @@ static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self) {
|
|
|
1252
1363
|
}
|
|
1253
1364
|
}
|
|
1254
1365
|
|
|
1255
|
-
static VALUE rescued_sample_allocation(
|
|
1366
|
+
static VALUE rescued_sample_allocation(VALUE arg) {
|
|
1367
|
+
per_thread_context *thread_context = (per_thread_context*) arg;
|
|
1256
1368
|
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1257
1369
|
|
|
1258
1370
|
// This should not happen in a normal situation because on_newobj_event already checked for this, but just in case...
|
|
@@ -1271,20 +1383,33 @@ static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {
|
|
|
1271
1383
|
// To control bias from sampling, we clamp the maximum weight attributed to a single allocation sample. This avoids
|
|
1272
1384
|
// assigning a very large number to a sample, if for instance the dynamic sampling mechanism chose a really big interval.
|
|
1273
1385
|
unsigned int weight = allocations_since_last_sample > MAX_ALLOC_WEIGHT ? MAX_ALLOC_WEIGHT : (unsigned int) allocations_since_last_sample;
|
|
1274
|
-
thread_context_collector_sample_allocation(state->thread_context_collector_instance, weight, new_object);
|
|
1386
|
+
bool needs_after_allocation = thread_context_collector_sample_allocation(state->thread_context_collector_instance, thread_context, weight, new_object);
|
|
1275
1387
|
// ...but we still represent the skipped samples in the profile, thus the data will account for all allocations.
|
|
1276
1388
|
if (weight < allocations_since_last_sample) {
|
|
1277
1389
|
uint32_t skipped_samples = (uint32_t) uint64_min_of(allocations_since_last_sample - weight, UINT32_MAX);
|
|
1278
1390
|
thread_context_collector_sample_skipped_allocation_samples(state->thread_context_collector_instance, skipped_samples);
|
|
1279
1391
|
}
|
|
1280
1392
|
|
|
1393
|
+
if (needs_after_allocation) {
|
|
1394
|
+
#ifndef NO_POSTPONED_TRIGGER
|
|
1395
|
+
rb_postponed_job_trigger(after_allocation_from_postponed_job_handle);
|
|
1396
|
+
#else
|
|
1397
|
+
// Not needed on legacy rubies
|
|
1398
|
+
#endif
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1281
1401
|
// Return a dummy VALUE because we're called from rb_rescue2 which requires it
|
|
1282
1402
|
return Qnil;
|
|
1283
1403
|
}
|
|
1284
1404
|
|
|
1285
1405
|
static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error) {
|
|
1286
1406
|
// If we can't raise an immediate exception at the calling site, use the asynchronous flow through the main worker loop.
|
|
1287
|
-
stop_state(state, rb_exc_new_cstr(rb_eRuntimeError, error));
|
|
1407
|
+
stop_state(state, rb_exc_new_cstr(rb_eRuntimeError, error), "delayed_error");
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state) {
|
|
1411
|
+
// If we can't raise an immediate exception at the calling site, use the asynchronous flow through the main worker loop.
|
|
1412
|
+
stop_state(state, rb_exc_new_cstr(clock_failure_exception_class, "failed to get clock time"), "delayed_error_clock_failure");
|
|
1288
1413
|
}
|
|
1289
1414
|
|
|
1290
1415
|
static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg) {
|
|
@@ -1315,44 +1440,69 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
|
|
|
1315
1440
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
1316
1441
|
static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused) {
|
|
1317
1442
|
// Be very careful about touching the `state` here or doing anything at all:
|
|
1318
|
-
// This function gets called without the GVL, and potentially from
|
|
1443
|
+
// This function gets called without the GVL, and potentially from non-main Ractors!
|
|
1319
1444
|
//
|
|
1320
|
-
//
|
|
1321
|
-
//
|
|
1322
|
-
|
|
1445
|
+
// Note, even though these events can get called without the GVL, they synchronize-with enabling/disabling of
|
|
1446
|
+
// the hook that calls us using a read-write lock. Thus, disabling the hook cannot be concurrent with calling this function,
|
|
1447
|
+
// and once the disable finishes there can't be "late" calls into this function.
|
|
1323
1448
|
|
|
1324
|
-
|
|
1325
|
-
|
|
1449
|
+
// The thread that this event is about may not be the current thread
|
|
1450
|
+
// (as documented on rb_internal_thread_add_event_hook(), and this is notably the case for READY on Ruby 4.0),
|
|
1451
|
+
// so be careful with native thread locals that are not directly tied to the thread object and the like.
|
|
1452
|
+
|
|
1453
|
+
#ifdef HAVE_RUBY_THREAD_STORAGE_API
|
|
1454
|
+
VALUE target_thread = event_data->thread;
|
|
1455
|
+
#else
|
|
1456
|
+
// On Ruby 3.2 the event does not carry the thread, but all events fire on the thread itself.
|
|
1457
|
+
// However, during early thread startup rb_thread_current() can crash because the execution context (Fiber) isn't
|
|
1458
|
+
// stored in TLS yet; ruby_native_thread_p() guards against this.
|
|
1459
|
+
if (!ruby_native_thread_p()) return;
|
|
1460
|
+
VALUE target_thread = rb_thread_current();
|
|
1461
|
+
#endif
|
|
1462
|
+
|
|
1463
|
+
per_thread_context* thread_context = get_per_thread_context(target_thread);
|
|
1464
|
+
if (!thread_context) return;
|
|
1465
|
+
// If non-NULL the thread is profiled and from the main Ractor
|
|
1466
|
+
|
|
1467
|
+
if (event_id == RUBY_INTERNAL_THREAD_EVENT_SUSPENDED) { /* released gvl */
|
|
1468
|
+
thread_context_collector_on_gvl_released(thread_context);
|
|
1469
|
+
} else if (event_id == RUBY_INTERNAL_THREAD_EVENT_READY) { /* waiting for gvl */
|
|
1470
|
+
thread_context_collector_on_gvl_waiting(thread_context);
|
|
1326
1471
|
} else if (event_id == RUBY_INTERNAL_THREAD_EVENT_RESUMED) { /* running/runnable */
|
|
1327
|
-
// Interesting note: A RUBY_INTERNAL_THREAD_EVENT_RESUMED is guaranteed to be called with the GVL being acquired
|
|
1328
|
-
//
|
|
1472
|
+
// Interesting note: A RUBY_INTERNAL_THREAD_EVENT_RESUMED is guaranteed to be called with the GVL being acquired
|
|
1473
|
+
// and on the event thread.
|
|
1474
|
+
// However, on_gvl_event() is called while holding the scheduler lock, so we do as little work as possible here,
|
|
1475
|
+
// and perform the sample in a postponed_job.
|
|
1476
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1477
|
+
if (state == NULL) return; // This should not happen, but just in case...
|
|
1478
|
+
|
|
1479
|
+
// on_gvl_running prepares a stack sample so we need to gate it with `during_sample_enter` to avoid a
|
|
1480
|
+
// SIGPROF signal coming in during that function and potentially causing a corrupted final state.
|
|
1329
1481
|
//
|
|
1330
|
-
//
|
|
1331
|
-
//
|
|
1332
|
-
//
|
|
1482
|
+
// TODO: Currently, the sample prepared in on_gvl_running can still be clobbered if the signal handler runs
|
|
1483
|
+
// after `during_sample_exit` but before `after_gvl_running_from_postponed_job` gets to run. We'll need to fix
|
|
1484
|
+
// that next.
|
|
1485
|
+
during_sample_enter(state);
|
|
1333
1486
|
|
|
1334
|
-
|
|
1335
|
-
target_thread = gvl_profiling_state_maybe_initialize();
|
|
1336
|
-
#endif
|
|
1487
|
+
on_gvl_running_result result = thread_context_collector_on_gvl_running(state->thread_context_collector_instance, target_thread, thread_context);
|
|
1337
1488
|
|
|
1338
|
-
|
|
1489
|
+
during_sample_exit(state);
|
|
1339
1490
|
|
|
1340
|
-
if (result
|
|
1491
|
+
if (result.waiting_for_gvl_duration_ns > 0) {
|
|
1492
|
+
state->stats.vm_metrics.gvl_waiting_time_ns_total += (uint64_t) result.waiting_for_gvl_duration_ns;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
if (result.action == ON_GVL_RUNNING_SAMPLE) {
|
|
1341
1496
|
#ifndef NO_POSTPONED_TRIGGER
|
|
1342
1497
|
rb_postponed_job_trigger(after_gvl_running_from_postponed_job_handle);
|
|
1343
1498
|
#else
|
|
1344
1499
|
rb_postponed_job_register_one(0, after_gvl_running_from_postponed_job, NULL);
|
|
1345
1500
|
#endif
|
|
1346
|
-
} else if (result == ON_GVL_RUNNING_DONT_SAMPLE) {
|
|
1347
|
-
cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
|
1348
|
-
|
|
1349
|
-
if (state == NULL) return; // This should not happen, but just in case...
|
|
1350
|
-
|
|
1501
|
+
} else if (result.action == ON_GVL_RUNNING_DONT_SAMPLE) {
|
|
1351
1502
|
state->stats.gvl_dont_sample++;
|
|
1352
1503
|
}
|
|
1353
1504
|
} else {
|
|
1354
|
-
|
|
1355
|
-
fprintf(stderr, "[ddtrace] Unexpected value in on_gvl_event (%d)\n", event_id);
|
|
1505
|
+
rb_bug("[ddtrace] Unexpected value in on_gvl_event (%d)\n", event_id);
|
|
1356
1506
|
}
|
|
1357
1507
|
}
|
|
1358
1508
|
|
|
@@ -1365,7 +1515,12 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
|
|
|
1365
1515
|
during_sample_enter(state);
|
|
1366
1516
|
|
|
1367
1517
|
// Rescue against any exceptions that happen during sampling
|
|
1368
|
-
safely_call(
|
|
1518
|
+
safely_call(
|
|
1519
|
+
rescued_after_gvl_running_from_postponed_job,
|
|
1520
|
+
state->self_instance,
|
|
1521
|
+
state->self_instance,
|
|
1522
|
+
handle_sampling_failure_rescued_after_gvl_running_from_postponed_job
|
|
1523
|
+
);
|
|
1369
1524
|
|
|
1370
1525
|
during_sample_exit(state);
|
|
1371
1526
|
}
|
|
@@ -1398,12 +1553,76 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
|
|
|
1398
1553
|
|
|
1399
1554
|
return state->gvl_profiling_hook != NULL ? Qtrue : Qfalse;
|
|
1400
1555
|
}
|
|
1556
|
+
|
|
1557
|
+
static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception) {
|
|
1558
|
+
stop(self_instance, exception, "rescued_after_gvl_running_from_postponed_job");
|
|
1559
|
+
return Qnil;
|
|
1560
|
+
}
|
|
1401
1561
|
#else
|
|
1402
1562
|
static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, DDTRACE_UNUSED VALUE instance) {
|
|
1403
1563
|
return Qfalse;
|
|
1404
1564
|
}
|
|
1405
1565
|
#endif
|
|
1406
1566
|
|
|
1567
|
+
static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception) {
|
|
1568
|
+
stop(self_instance, exception, "rescued_sample_from_postponed_job");
|
|
1569
|
+
return Qnil;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception) {
|
|
1573
|
+
stop(self_instance, exception, "thread_context_collector_sample_after_gc");
|
|
1574
|
+
return Qnil;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception) {
|
|
1578
|
+
stop(self_instance, exception, "rescued_sample_allocation");
|
|
1579
|
+
return Qnil;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
static VALUE handle_sampling_failure_rescued_after_allocation(VALUE self_instance, VALUE exception) {
|
|
1583
|
+
stop(self_instance, exception, "rescued_after_allocation");
|
|
1584
|
+
return Qnil;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
static VALUE rescued_after_allocation(VALUE self_instance) {
|
|
1588
|
+
cpu_and_wall_time_worker_state *state;
|
|
1589
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1590
|
+
|
|
1591
|
+
thread_context_collector_after_allocation(state->thread_context_collector_instance);
|
|
1592
|
+
|
|
1593
|
+
// Return a dummy VALUE because we're called from rb_rescue2 which requires it
|
|
1594
|
+
return Qnil;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
// This postponed job callback is used to finalize heap allocation recordings on Ruby 4+.
|
|
1598
|
+
// During on_newobj_event, calling rb_obj_id() is unsafe because it mutates the object.
|
|
1599
|
+
// So we defer getting the object_id until after the event completes.
|
|
1600
|
+
#pragma GCC diagnostic push
|
|
1601
|
+
#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
|
|
1602
|
+
static void after_allocation_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
1603
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state;
|
|
1604
|
+
|
|
1605
|
+
if (state == NULL || !ddtrace_rb_ractor_main_p()) return;
|
|
1606
|
+
|
|
1607
|
+
// Protect against nested operations
|
|
1608
|
+
if (state->during_sample) return;
|
|
1609
|
+
|
|
1610
|
+
during_sample_enter(state);
|
|
1611
|
+
|
|
1612
|
+
// NOTE: We're not updating the allocation_sampler here.
|
|
1613
|
+
// This means work done in this function isn't accounted for as profiler overhead.
|
|
1614
|
+
// This is acceptable as the amount of work done here is expected to be small.
|
|
1615
|
+
safely_call(
|
|
1616
|
+
rescued_after_allocation,
|
|
1617
|
+
state->self_instance,
|
|
1618
|
+
state->self_instance,
|
|
1619
|
+
handle_sampling_failure_rescued_after_allocation
|
|
1620
|
+
);
|
|
1621
|
+
|
|
1622
|
+
during_sample_exit(state);
|
|
1623
|
+
}
|
|
1624
|
+
#pragma GCC diagnostic pop
|
|
1625
|
+
|
|
1407
1626
|
static inline void during_sample_enter(cpu_and_wall_time_worker_state* state) {
|
|
1408
1627
|
// Tell the compiler it's not allowed to reorder the `during_sample` flag with anything that happens after.
|
|
1409
1628
|
//
|