datadog 2.28.0 → 2.41.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 +412 -1
- data/bin/ddprofrb +4 -4
- 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 +1 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +281 -57
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +18 -4
- data/ext/datadog_profiling_native_extension/collectors_stack.c +245 -160
- data/ext/datadog_profiling_native_extension/collectors_stack.h +23 -4
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +725 -432
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +19 -6
- data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +5 -4
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +44 -19
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +17 -12
- data/ext/datadog_profiling_native_extension/extconf.rb +44 -40
- 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 +230 -81
- data/ext/datadog_profiling_native_extension/heap_recorder.h +12 -1
- data/ext/datadog_profiling_native_extension/http_transport.c +61 -69
- data/ext/datadog_profiling_native_extension/macos_sampler_thread.h +55 -0
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +3 -16
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +300 -108
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +17 -9
- data/ext/datadog_profiling_native_extension/profiling.c +44 -5
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +40 -34
- data/ext/datadog_profiling_native_extension/setup_signal_handler.c +26 -8
- data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -3
- data/ext/datadog_profiling_native_extension/stack_recorder.c +94 -69
- 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.h +4 -2
- data/ext/libdatadog_api/crashtracker.c +12 -8
- data/ext/libdatadog_api/crashtracker_report_exception.c +126 -0
- data/ext/libdatadog_api/datadog_ruby_common.c +44 -19
- data/ext/libdatadog_api/datadog_ruby_common.h +17 -12
- data/ext/libdatadog_api/di.c +203 -0
- data/ext/libdatadog_api/extconf.rb +38 -44
- data/ext/libdatadog_api/feature_flags.c +26 -0
- data/ext/libdatadog_api/init.c +9 -2
- data/ext/libdatadog_api/trace_exporter.c +1312 -0
- data/ext/libdatadog_api/trace_exporter.h +5 -0
- data/ext/libdatadog_extconf_helpers.rb +98 -15
- data/lib/datadog/ai_guard/api_client.rb +9 -7
- data/lib/datadog/ai_guard/autoload.rb +10 -0
- data/lib/datadog/ai_guard/component.rb +11 -9
- data/lib/datadog/ai_guard/configuration.rb +106 -2
- data/lib/datadog/ai_guard/contrib/auto_instrument.rb +24 -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 +41 -3
- 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/no_op_result.rb +3 -1
- data/lib/datadog/ai_guard/evaluation/request.rb +22 -17
- data/lib/datadog/ai_guard/evaluation/result.rb +3 -1
- data/lib/datadog/ai_guard/evaluation.rb +51 -7
- data/lib/datadog/ai_guard/ext.rb +15 -0
- data/lib/datadog/ai_guard.rb +34 -8
- data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +22 -43
- data/lib/datadog/appsec/actions_handler.rb +6 -6
- data/lib/datadog/appsec/anonymizer.rb +1 -1
- data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +2 -2
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +6 -6
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +1 -1
- data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +2 -2
- data/lib/datadog/appsec/api_security/route_extractor.rb +26 -14
- data/lib/datadog/appsec/api_security/sampler.rb +5 -5
- data/lib/datadog/appsec/api_security.rb +1 -1
- data/lib/datadog/appsec/assets/blocked.html +2 -1
- data/lib/datadog/appsec/assets.rb +7 -7
- data/lib/datadog/appsec/autoload.rb +4 -4
- data/lib/datadog/appsec/component.rb +29 -22
- data/lib/datadog/appsec/compressed_json.rb +7 -7
- data/lib/datadog/appsec/configuration.rb +436 -1
- data/lib/datadog/appsec/context.rb +49 -12
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +6 -6
- data/lib/datadog/appsec/contrib/active_record/integration.rb +4 -4
- data/lib/datadog/appsec/contrib/active_record/patcher.rb +7 -4
- data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +75 -0
- data/lib/datadog/appsec/contrib/aws_lambda/integration.rb +39 -0
- data/lib/datadog/appsec/contrib/aws_lambda/patcher.rb +30 -0
- data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +146 -0
- data/lib/datadog/appsec/contrib/devise/configuration.rb +1 -1
- data/lib/datadog/appsec/contrib/devise/data_extractor.rb +3 -3
- data/lib/datadog/appsec/contrib/devise/ext.rb +20 -20
- data/lib/datadog/appsec/contrib/devise/integration.rb +5 -5
- data/lib/datadog/appsec/contrib/devise/patcher.rb +8 -8
- data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +14 -13
- data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +8 -8
- data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +24 -9
- data/lib/datadog/appsec/contrib/excon/integration.rb +4 -4
- data/lib/datadog/appsec/contrib/excon/patcher.rb +3 -1
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +118 -14
- data/lib/datadog/appsec/contrib/faraday/integration.rb +5 -5
- data/lib/datadog/appsec/contrib/faraday/patcher.rb +4 -4
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +119 -12
- data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +4 -4
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +76 -26
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +7 -7
- data/lib/datadog/appsec/contrib/graphql/integration.rb +10 -9
- data/lib/datadog/appsec/contrib/graphql/patcher.rb +3 -3
- data/lib/datadog/appsec/contrib/rack/buffered_input.rb +83 -0
- data/lib/datadog/appsec/contrib/rack/ext.rb +29 -29
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +56 -21
- data/lib/datadog/appsec/contrib/rack/gateway/response.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +46 -30
- data/lib/datadog/appsec/contrib/rack/input_peeker.rb +79 -0
- data/lib/datadog/appsec/contrib/rack/integration.rb +7 -7
- data/lib/datadog/appsec/contrib/rack/patcher.rb +3 -3
- data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +11 -8
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +63 -52
- data/lib/datadog/appsec/contrib/rack/response_body.rb +36 -0
- data/lib/datadog/appsec/contrib/rails/gateway/request.rb +43 -4
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +25 -9
- data/lib/datadog/appsec/contrib/rails/integration.rb +6 -6
- data/lib/datadog/appsec/contrib/rails/patcher.rb +17 -17
- data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.rb +3 -1
- data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +3 -3
- data/lib/datadog/appsec/contrib/rails/request.rb +3 -3
- data/lib/datadog/appsec/contrib/rails/request_middleware.rb +2 -1
- data/lib/datadog/appsec/contrib/rest_client/integration.rb +5 -5
- data/lib/datadog/appsec/contrib/rest_client/patcher.rb +3 -1
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +131 -13
- data/lib/datadog/appsec/contrib/sinatra/gateway/request.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/gateway/route_params.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +35 -12
- data/lib/datadog/appsec/contrib/sinatra/integration.rb +5 -5
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +11 -11
- data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +3 -3
- data/lib/datadog/appsec/contrib/sinatra/request_middleware.rb +2 -1
- data/lib/datadog/appsec/counter_sampler.rb +25 -0
- data/lib/datadog/appsec/default_header_tags.rb +52 -0
- data/lib/datadog/appsec/event.rb +15 -31
- data/lib/datadog/appsec/ext.rb +11 -10
- data/lib/datadog/appsec/extensions.rb +2 -2
- data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +2 -3
- data/lib/datadog/appsec/instrumentation/gateway.rb +3 -16
- data/lib/datadog/appsec/metrics/collector.rb +18 -1
- data/lib/datadog/appsec/metrics/exporter.rb +17 -11
- data/lib/datadog/appsec/metrics/telemetry.rb +15 -3
- data/lib/datadog/appsec/metrics/telemetry_exporter.rb +20 -2
- data/lib/datadog/appsec/metrics.rb +4 -4
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +18 -12
- data/lib/datadog/appsec/monitor.rb +1 -1
- data/lib/datadog/appsec/processor/rule_loader.rb +32 -32
- data/lib/datadog/appsec/rate_limiter.rb +1 -1
- data/lib/datadog/appsec/remote.rb +15 -9
- data/lib/datadog/appsec/response.rb +22 -22
- 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 +54 -33
- data/lib/datadog/appsec/security_engine/runner.rb +3 -3
- data/lib/datadog/appsec/security_event.rb +2 -2
- data/lib/datadog/appsec/trace_keeper.rb +18 -6
- data/lib/datadog/appsec/utils/http/body.rb +41 -0
- data/lib/datadog/appsec/utils/http/body_reader.rb +61 -0
- data/lib/datadog/appsec/utils/http/media_range.rb +17 -16
- data/lib/datadog/appsec/utils/http/media_type.rb +28 -36
- data/lib/datadog/appsec/utils/http/url_encoded.rb +107 -0
- data/lib/datadog/appsec.rb +21 -24
- data/lib/datadog/auto_instrument.rb +2 -2
- data/lib/datadog/core/buffer/cruby.rb +1 -1
- data/lib/datadog/core/buffer/thread_safe.rb +1 -1
- data/lib/datadog/core/configuration/agent_settings.rb +1 -1
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +15 -15
- data/lib/datadog/core/configuration/agentless_settings_resolver.rb +2 -2
- data/lib/datadog/core/configuration/base.rb +19 -7
- data/lib/datadog/core/configuration/components.rb +166 -36
- data/lib/datadog/core/configuration/components_state.rb +6 -1
- data/lib/datadog/core/configuration/config_helper.rb +12 -3
- data/lib/datadog/core/configuration/deprecations.rb +6 -6
- data/lib/datadog/core/configuration/ext.rb +11 -11
- data/lib/datadog/core/configuration/option.rb +48 -22
- data/lib/datadog/core/configuration/option_definition.rb +43 -17
- data/lib/datadog/core/configuration/options.rb +43 -9
- data/lib/datadog/core/configuration/settings.rb +155 -106
- data/lib/datadog/core/configuration/stable_config.rb +1 -1
- data/lib/datadog/core/configuration/supported_configurations.rb +44 -1
- data/lib/datadog/core/configuration.rb +11 -11
- 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 +78 -26
- data/lib/datadog/core/crashtracking/tag_builder.rb +5 -5
- data/lib/datadog/core/deprecations.rb +1 -1
- data/lib/datadog/core/diagnostics/environment_logger.rb +45 -8
- data/lib/datadog/core/diagnostics/health.rb +2 -2
- data/lib/datadog/core/encoding.rb +4 -4
- data/lib/datadog/core/environment/agent_info.rb +65 -1
- data/lib/datadog/core/environment/cgroup.rb +4 -4
- data/lib/datadog/core/environment/container.rb +11 -11
- data/lib/datadog/core/environment/execution.rb +3 -3
- data/lib/datadog/core/environment/ext.rb +21 -15
- data/lib/datadog/core/environment/gc.rb +1 -1
- data/lib/datadog/core/environment/git.rb +2 -2
- data/lib/datadog/core/environment/identity.rb +47 -11
- data/lib/datadog/core/environment/platform.rb +8 -8
- data/lib/datadog/core/environment/process.rb +54 -19
- data/lib/datadog/core/environment/socket.rb +15 -2
- data/lib/datadog/core/environment/variable_helpers.rb +1 -1
- data/lib/datadog/core/environment/vm_cache.rb +1 -1
- data/lib/datadog/core/environment/yjit.rb +2 -2
- data/lib/datadog/core/error.rb +5 -5
- data/lib/datadog/core/evp.rb +11 -0
- data/lib/datadog/core/extensions.rb +1 -1
- data/lib/datadog/core/feature_flags.rb +3 -3
- data/lib/datadog/core/git/ext.rb +4 -4
- data/lib/datadog/core/knuth_sampler.rb +3 -1
- data/lib/datadog/core/logger.rb +4 -4
- data/lib/datadog/core/logging/ext.rb +1 -1
- data/lib/datadog/core/metrics/client.rb +26 -26
- data/lib/datadog/core/metrics/ext.rb +5 -5
- data/lib/datadog/core/metrics/helpers.rb +1 -1
- data/lib/datadog/core/metrics/logging.rb +3 -3
- data/lib/datadog/core/metrics/options.rb +5 -5
- data/lib/datadog/core/pin.rb +1 -1
- data/lib/datadog/core/process_discovery.rb +23 -22
- data/lib/datadog/core/rate_limiter.rb +3 -1
- data/lib/datadog/core/remote/client/capabilities.rb +67 -15
- data/lib/datadog/core/remote/client.rb +18 -18
- data/lib/datadog/core/remote/component.rb +70 -34
- data/lib/datadog/core/remote/configuration/content.rb +3 -3
- data/lib/datadog/core/remote/configuration/digest.rb +1 -1
- data/lib/datadog/core/remote/configuration/path.rb +9 -9
- data/lib/datadog/core/remote/configuration/repository.rb +4 -4
- data/lib/datadog/core/remote/configuration/target.rb +9 -9
- data/lib/datadog/core/remote/configuration.rb +4 -4
- data/lib/datadog/core/remote/ext.rb +3 -3
- data/lib/datadog/core/remote/negotiation.rb +1 -1
- data/lib/datadog/core/remote/tie/tracing.rb +7 -7
- data/lib/datadog/core/remote/transport/config.rb +9 -15
- data/lib/datadog/core/remote/transport/http/config.rb +10 -10
- data/lib/datadog/core/remote/transport/http/negotiation.rb +4 -4
- data/lib/datadog/core/remote/transport/http.rb +10 -10
- data/lib/datadog/core/remote/transport/negotiation.rb +3 -3
- data/lib/datadog/core/remote/worker.rb +5 -5
- data/lib/datadog/core/runtime/ext.rb +22 -22
- data/lib/datadog/core/runtime/metrics.rb +11 -12
- data/lib/datadog/core/tag_builder.rb +18 -18
- data/lib/datadog/core/tag_normalizer.rb +7 -7
- data/lib/datadog/core/telemetry/component.rb +16 -26
- data/lib/datadog/core/telemetry/configuration_value.rb +38 -0
- data/lib/datadog/core/telemetry/emitter.rb +5 -5
- data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +10 -9
- data/lib/datadog/core/telemetry/event/app_closing.rb +2 -2
- data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +2 -2
- data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +3 -3
- data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
- data/lib/datadog/core/telemetry/event/app_heartbeat.rb +2 -2
- data/lib/datadog/core/telemetry/event/app_integrations_change.rb +2 -2
- data/lib/datadog/core/telemetry/event/app_started.rb +142 -193
- data/lib/datadog/core/telemetry/event/base.rb +1 -1
- data/lib/datadog/core/telemetry/event/distributions.rb +2 -2
- data/lib/datadog/core/telemetry/event/generate_metrics.rb +3 -3
- data/lib/datadog/core/telemetry/event/log.rb +6 -6
- data/lib/datadog/core/telemetry/event/message_batch.rb +1 -1
- data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +3 -3
- data/lib/datadog/core/telemetry/event.rb +16 -22
- data/lib/datadog/core/telemetry/ext.rb +11 -10
- data/lib/datadog/core/telemetry/logger.rb +1 -1
- data/lib/datadog/core/telemetry/logging.rb +5 -5
- data/lib/datadog/core/telemetry/metric.rb +9 -9
- data/lib/datadog/core/telemetry/metrics_collection.rb +2 -2
- data/lib/datadog/core/telemetry/metrics_manager.rb +1 -1
- data/lib/datadog/core/telemetry/request.rb +6 -8
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +16 -11
- data/lib/datadog/core/telemetry/transport/http.rb +7 -7
- data/lib/datadog/core/telemetry/transport/telemetry.rb +8 -9
- data/lib/datadog/core/telemetry/worker.rb +26 -6
- data/lib/datadog/core/transport/ext.rb +14 -13
- data/lib/datadog/core/transport/http/adapters/net.rb +4 -4
- data/lib/datadog/core/transport/http/adapters/test.rb +1 -1
- data/lib/datadog/core/transport/http/adapters/unix_socket.rb +4 -4
- data/lib/datadog/core/transport/http/api/endpoint.rb +1 -1
- data/lib/datadog/core/transport/http/api/map.rb +1 -1
- data/lib/datadog/core/transport/http/builder.rb +6 -6
- data/lib/datadog/core/transport/http/client.rb +2 -2
- data/lib/datadog/core/transport/http/response.rb +5 -1
- data/lib/datadog/core/transport/http.rb +9 -7
- data/lib/datadog/core/transport/parcel.rb +61 -9
- data/lib/datadog/core/transport/response.rb +1 -1
- data/lib/datadog/core/transport/transport.rb +2 -2
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +172 -20
- data/lib/datadog/core/utils/base64_codec.rb +23 -0
- data/lib/datadog/core/utils/duration.rb +1 -1
- data/lib/datadog/core/utils/{array.rb → enumerable_compat.rb} +2 -2
- 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/core/utils/lru_cache.rb +3 -3
- data/lib/datadog/core/utils/network.rb +11 -11
- data/lib/datadog/core/utils/only_once.rb +1 -1
- data/lib/datadog/core/utils/only_once_successful.rb +1 -1
- data/lib/datadog/core/utils/safe_dup.rb +2 -1
- data/lib/datadog/core/utils/sequence.rb +3 -6
- data/lib/datadog/core/utils/spawn_monkey_patch.rb +68 -0
- data/lib/datadog/core/utils/time.rb +18 -52
- data/lib/datadog/core/utils/url.rb +1 -1
- data/lib/datadog/core/utils.rb +5 -5
- data/lib/datadog/core/workers/async.rb +2 -2
- data/lib/datadog/core/workers/interval_loop.rb +13 -6
- data/lib/datadog/core/workers/polling.rb +2 -2
- data/lib/datadog/core/workers/queue.rb +0 -4
- data/lib/datadog/core/workers/runtime_metrics.rb +13 -5
- data/lib/datadog/core.rb +13 -6
- data/lib/datadog/data_streams/configuration.rb +40 -1
- data/lib/datadog/data_streams/ext.rb +1 -1
- data/lib/datadog/data_streams/extensions.rb +2 -2
- data/lib/datadog/data_streams/pathway_context.rb +8 -8
- data/lib/datadog/data_streams/processor.rb +87 -85
- data/lib/datadog/data_streams/transport/http/stats.rb +9 -3
- data/lib/datadog/data_streams/transport/http.rb +5 -9
- data/lib/datadog/data_streams/transport/stats.rb +10 -17
- data/lib/datadog/data_streams.rb +5 -5
- data/lib/datadog/di/base.rb +16 -8
- data/lib/datadog/di/boot.rb +25 -23
- 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 +218 -16
- data/lib/datadog/di/component.rb +137 -39
- data/lib/datadog/di/configuration.rb +241 -2
- data/lib/datadog/di/context.rb +15 -3
- data/lib/datadog/di/contrib/active_record.rb +6 -2
- data/lib/datadog/di/contrib/railtie.rb +1 -1
- data/lib/datadog/di/contrib.rb +5 -5
- data/lib/datadog/di/el/compiler.rb +114 -38
- data/lib/datadog/di/el/evaluator.rb +82 -2
- data/lib/datadog/di/el/expression.rb +13 -2
- data/lib/datadog/di/el.rb +3 -3
- data/lib/datadog/di/error.rb +10 -0
- data/lib/datadog/di/fatal_exceptions.rb +26 -0
- data/lib/datadog/di/instrumenter.rb +608 -255
- data/lib/datadog/di/logger.rb +1 -1
- data/lib/datadog/di/preload.rb +1 -1
- data/lib/datadog/di/probe.rb +71 -3
- data/lib/datadog/di/probe_builder.rb +98 -15
- data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
- data/lib/datadog/di/probe_file_loader.rb +16 -11
- data/lib/datadog/di/probe_manager.rb +153 -73
- data/lib/datadog/di/probe_notification_builder.rb +218 -38
- data/lib/datadog/di/probe_notifier_worker.rb +111 -35
- data/lib/datadog/di/probe_repository.rb +198 -0
- data/lib/datadog/di/proc_responder.rb +4 -0
- data/lib/datadog/di/redactor.rb +16 -1
- data/lib/datadog/di/remote.rb +193 -33
- data/lib/datadog/di/serializer.rb +201 -39
- data/lib/datadog/di/transport/diagnostics.rb +10 -12
- data/lib/datadog/di/transport/http/diagnostics.rb +5 -3
- data/lib/datadog/di/transport/http/input.rb +3 -3
- data/lib/datadog/di/transport/http.rb +22 -13
- data/lib/datadog/di/transport/input.rb +65 -24
- data/lib/datadog/di/utils.rb +43 -15
- data/lib/datadog/di.rb +147 -3
- data/lib/datadog/error_tracking/collector.rb +3 -2
- data/lib/datadog/error_tracking/component.rb +12 -12
- data/lib/datadog/error_tracking/configuration.rb +55 -2
- data/lib/datadog/error_tracking/ext.rb +7 -7
- data/lib/datadog/error_tracking/extensions.rb +2 -2
- data/lib/datadog/error_tracking/filters.rb +4 -4
- data/lib/datadog/error_tracking.rb +6 -6
- data/lib/datadog/kit/appsec/events/v2.rb +91 -33
- data/lib/datadog/kit/appsec/events.rb +25 -25
- data/lib/datadog/kit/enable_core_dumps.rb +3 -3
- data/lib/datadog/kit/identity.rb +18 -18
- data/lib/datadog/kit/tracing/method_tracer.rb +135 -0
- data/lib/datadog/kit.rb +1 -1
- data/lib/datadog/open_feature/component.rb +62 -13
- data/lib/datadog/open_feature/configuration.rb +24 -1
- data/lib/datadog/open_feature/evaluation_engine.rb +9 -9
- data/lib/datadog/open_feature/exposures/batch_builder.rb +1 -1
- data/lib/datadog/open_feature/exposures/buffer.rb +1 -1
- data/lib/datadog/open_feature/exposures/deduplicator.rb +1 -1
- data/lib/datadog/open_feature/exposures/event.rb +7 -7
- data/lib/datadog/open_feature/exposures/reporter.rb +3 -3
- data/lib/datadog/open_feature/exposures/worker.rb +6 -6
- data/lib/datadog/open_feature/ext.rb +13 -6
- data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +286 -0
- data/lib/datadog/open_feature/flag_evaluation/writer.rb +433 -0
- data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +102 -0
- data/lib/datadog/open_feature/hooks/flag_eval_metrics_hook.rb +51 -0
- data/lib/datadog/open_feature/hooks/span_enrichment_hook/codec.rb +58 -0
- data/lib/datadog/open_feature/hooks/span_enrichment_hook/span_enrichment_state.rb +96 -0
- data/lib/datadog/open_feature/hooks/span_enrichment_hook/span_enrichment_state_store.rb +49 -0
- data/lib/datadog/open_feature/hooks/span_enrichment_hook.rb +184 -0
- data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
- data/lib/datadog/open_feature/native_evaluator.rb +23 -1
- data/lib/datadog/open_feature/noop_evaluator.rb +3 -3
- data/lib/datadog/open_feature/provider.rb +106 -21
- data/lib/datadog/open_feature/remote.rb +3 -3
- data/lib/datadog/open_feature/resolution_details.rb +4 -2
- data/lib/datadog/open_feature/transport.rb +73 -22
- data/lib/datadog/open_feature.rb +2 -2
- data/lib/datadog/opentelemetry/api/baggage.rb +3 -3
- data/lib/datadog/opentelemetry/api/context.rb +4 -4
- data/lib/datadog/opentelemetry/configuration/settings.rb +95 -28
- data/lib/datadog/opentelemetry/ext.rb +9 -0
- data/lib/datadog/opentelemetry/logs.rb +98 -0
- data/lib/datadog/opentelemetry/metrics.rb +17 -44
- data/lib/datadog/opentelemetry/sdk/configurator.rb +46 -6
- 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 +9 -16
- data/lib/datadog/opentelemetry/sdk/propagator.rb +16 -10
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +18 -15
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +32 -32
- data/lib/datadog/opentelemetry/sdk.rb +19 -0
- data/lib/datadog/opentelemetry/signal_configuration.rb +53 -0
- data/lib/datadog/opentelemetry/trace.rb +1 -1
- data/lib/datadog/opentelemetry.rb +8 -7
- data/lib/datadog/profiling/collectors/code_provenance.rb +36 -11
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +50 -3
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +12 -4
- data/lib/datadog/profiling/collectors/info.rb +17 -4
- data/lib/datadog/profiling/collectors/thread_context.rb +16 -18
- data/lib/datadog/profiling/component.rb +62 -33
- data/lib/datadog/profiling/exporter.rb +38 -13
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +6 -5
- data/lib/datadog/profiling/ext.rb +0 -2
- data/lib/datadog/profiling/flush.rb +21 -12
- data/lib/datadog/profiling/http_transport.rb +18 -8
- data/lib/datadog/profiling/load_native_extension.rb +2 -2
- data/lib/datadog/profiling/profiler.rb +28 -13
- data/lib/datadog/profiling/scheduler.rb +2 -2
- data/lib/datadog/profiling/sequence_tracker.rb +1 -1
- data/lib/datadog/profiling/stack_recorder.rb +0 -8
- data/lib/datadog/profiling/tag_builder.rb +19 -19
- data/lib/datadog/profiling/tasks/exec.rb +9 -4
- data/lib/datadog/profiling/tasks/help.rb +1 -0
- data/lib/datadog/profiling/tasks/setup.rb +2 -2
- data/lib/datadog/profiling.rb +32 -29
- 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 +195 -0
- data/lib/datadog/symbol_database/scope.rb +102 -0
- data/lib/datadog/symbol_database/scope_batcher.rb +292 -0
- data/lib/datadog/symbol_database/service_version.rb +66 -0
- data/lib/datadog/symbol_database/symbol.rb +69 -0
- data/lib/datadog/symbol_database/transport/http/endpoint.rb +28 -0
- data/lib/datadog/symbol_database/transport/http.rb +45 -0
- data/lib/datadog/symbol_database/transport.rb +54 -0
- data/lib/datadog/symbol_database/uploader.rb +230 -0
- data/lib/datadog/symbol_database.rb +71 -0
- data/lib/datadog/tracing/analytics.rb +1 -1
- data/lib/datadog/tracing/buffer.rb +8 -8
- data/lib/datadog/tracing/client_ip.rb +4 -4
- data/lib/datadog/tracing/component.rb +39 -7
- data/lib/datadog/tracing/configuration/dynamic/option.rb +4 -0
- data/lib/datadog/tracing/configuration/dynamic.rb +13 -11
- data/lib/datadog/tracing/configuration/ext.rb +52 -38
- data/lib/datadog/tracing/configuration/http.rb +2 -2
- data/lib/datadog/tracing/configuration/settings.rb +51 -17
- data/lib/datadog/tracing/context.rb +1 -1
- data/lib/datadog/tracing/context_provider.rb +2 -2
- data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/action_cable/event.rb +4 -4
- data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +9 -6
- data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +8 -5
- data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +9 -6
- data/lib/datadog/tracing/contrib/action_cable/events.rb +5 -5
- data/lib/datadog/tracing/contrib/action_cable/ext.rb +18 -18
- data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -5
- data/lib/datadog/tracing/contrib/action_cable/integration.rb +7 -7
- data/lib/datadog/tracing/contrib/action_cable/patcher.rb +4 -4
- data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/action_mailer/event.rb +7 -4
- data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +8 -8
- data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +4 -4
- data/lib/datadog/tracing/contrib/action_mailer/events.rb +3 -3
- data/lib/datadog/tracing/contrib/action_mailer/ext.rb +18 -18
- data/lib/datadog/tracing/contrib/action_mailer/integration.rb +7 -7
- data/lib/datadog/tracing/contrib/action_mailer/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +15 -12
- data/lib/datadog/tracing/contrib/action_pack/action_controller/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +31 -3
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +5 -3
- data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +3 -3
- data/lib/datadog/tracing/contrib/action_pack/ext.rb +10 -10
- data/lib/datadog/tracing/contrib/action_pack/integration.rb +7 -7
- data/lib/datadog/tracing/contrib/action_pack/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -1
- data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +3 -3
- data/lib/datadog/tracing/contrib/action_view/event.rb +1 -1
- data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +10 -7
- data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +11 -8
- data/lib/datadog/tracing/contrib/action_view/events.rb +3 -3
- data/lib/datadog/tracing/contrib/action_view/ext.rb +10 -10
- data/lib/datadog/tracing/contrib/action_view/integration.rb +11 -11
- data/lib/datadog/tracing/contrib/action_view/patcher.rb +5 -5
- data/lib/datadog/tracing/contrib/action_view/utils.rb +2 -2
- data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/active_job/data_streams.rb +62 -0
- data/lib/datadog/tracing/contrib/active_job/event.rb +3 -3
- data/lib/datadog/tracing/contrib/active_job/events/discard.rb +10 -7
- data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +10 -7
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +10 -7
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +10 -7
- data/lib/datadog/tracing/contrib/active_job/events/perform.rb +10 -7
- data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +10 -7
- data/lib/datadog/tracing/contrib/active_job/events.rb +6 -6
- data/lib/datadog/tracing/contrib/active_job/ext.rb +23 -23
- data/lib/datadog/tracing/contrib/active_job/integration.rb +7 -7
- data/lib/datadog/tracing/contrib/active_job/patcher.rb +17 -5
- data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/active_model_serializers/event.rb +4 -4
- data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +7 -7
- data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +8 -9
- data/lib/datadog/tracing/contrib/active_model_serializers/events.rb +3 -3
- data/lib/datadog/tracing/contrib/active_model_serializers/ext.rb +10 -10
- data/lib/datadog/tracing/contrib/active_model_serializers/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/active_model_serializers/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/active_record/configuration/makara_resolver.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +9 -9
- data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +4 -4
- data/lib/datadog/tracing/contrib/active_record/event.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +9 -9
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +11 -14
- data/lib/datadog/tracing/contrib/active_record/events.rb +3 -3
- data/lib/datadog/tracing/contrib/active_record/ext.rb +15 -15
- data/lib/datadog/tracing/contrib/active_record/integration.rb +11 -11
- data/lib/datadog/tracing/contrib/active_record/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/active_record/utils.rb +4 -4
- data/lib/datadog/tracing/contrib/active_support/cache/event.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +14 -17
- data/lib/datadog/tracing/contrib/active_support/cache/events.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +5 -8
- data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +5 -5
- data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -4
- data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +3 -3
- data/lib/datadog/tracing/contrib/active_support/ext.rb +16 -16
- data/lib/datadog/tracing/contrib/active_support/integration.rb +8 -8
- data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +2 -2
- data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +3 -3
- data/lib/datadog/tracing/contrib/active_support/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/analytics.rb +1 -1
- data/lib/datadog/tracing/contrib/auto_instrument.rb +4 -4
- data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +3 -3
- data/lib/datadog/tracing/contrib/aws/ext.rb +26 -26
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +7 -11
- data/lib/datadog/tracing/contrib/aws/integration.rb +9 -9
- data/lib/datadog/tracing/contrib/aws/parsed_context.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/patcher.rb +5 -5
- data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +4 -4
- data/lib/datadog/tracing/contrib/aws/service/s3.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/service/sns.rb +3 -3
- data/lib/datadog/tracing/contrib/aws/service/sqs.rb +3 -3
- data/lib/datadog/tracing/contrib/aws/service/states.rb +5 -5
- data/lib/datadog/tracing/contrib/aws/services.rb +14 -14
- data/lib/datadog/tracing/contrib/component.rb +1 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +1 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +1 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/ext.rb +2 -2
- data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +1 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +4 -4
- data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +1 -1
- data/lib/datadog/tracing/contrib/configurable.rb +20 -5
- data/lib/datadog/tracing/contrib/configuration/resolver.rb +14 -4
- data/lib/datadog/tracing/contrib/configuration/resolvers/pattern_resolver.rb +1 -1
- data/lib/datadog/tracing/contrib/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/dalli/ext.rb +13 -13
- data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +6 -10
- data/lib/datadog/tracing/contrib/dalli/integration.rb +10 -7
- data/lib/datadog/tracing/contrib/dalli/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/dalli/quantize.rb +3 -3
- data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/delayed_job/ext.rb +14 -14
- data/lib/datadog/tracing/contrib/delayed_job/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/delayed_job/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +7 -5
- data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +1 -0
- data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +15 -15
- data/lib/datadog/tracing/contrib/elasticsearch/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +12 -16
- data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +4 -4
- data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +7 -3
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +9 -13
- data/lib/datadog/tracing/contrib/ethon/ext.rb +13 -12
- data/lib/datadog/tracing/contrib/ethon/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +4 -11
- data/lib/datadog/tracing/contrib/ethon/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +10 -7
- data/lib/datadog/tracing/contrib/excon/ext.rb +11 -10
- data/lib/datadog/tracing/contrib/excon/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/excon/middleware.rb +10 -14
- data/lib/datadog/tracing/contrib/excon/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/ext.rb +20 -18
- data/lib/datadog/tracing/contrib/extensions.rb +11 -2
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +9 -6
- data/lib/datadog/tracing/contrib/faraday/ext.rb +11 -10
- data/lib/datadog/tracing/contrib/faraday/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +10 -14
- data/lib/datadog/tracing/contrib/faraday/patcher.rb +6 -6
- data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +4 -4
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +30 -27
- data/lib/datadog/tracing/contrib/grape/ext.rb +15 -15
- data/lib/datadog/tracing/contrib/grape/instrumentation.rb +15 -10
- data/lib/datadog/tracing/contrib/grape/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/grape/patcher.rb +10 -5
- data/lib/datadog/tracing/contrib/graphql/configuration/error_extension_env_parser.rb +1 -1
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +3 -3
- data/lib/datadog/tracing/contrib/graphql/ext.rb +9 -9
- data/lib/datadog/tracing/contrib/graphql/integration.rb +9 -9
- data/lib/datadog/tracing/contrib/graphql/patcher.rb +7 -7
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +38 -37
- data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +7 -4
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +11 -15
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +11 -15
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +6 -6
- data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +1 -1
- data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +11 -9
- data/lib/datadog/tracing/contrib/grpc/ext.rb +14 -13
- data/lib/datadog/tracing/contrib/grpc/formatting.rb +7 -7
- data/lib/datadog/tracing/contrib/grpc/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/grpc/intercept_with_datadog.rb +1 -1
- data/lib/datadog/tracing/contrib/grpc/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/grpc.rb +7 -6
- data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +4 -3
- data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/hanami/ext.rb +8 -8
- data/lib/datadog/tracing/contrib/hanami/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/hanami/patcher.rb +5 -5
- data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +4 -3
- data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +6 -4
- data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +1 -1
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +9 -6
- data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +3 -3
- data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +10 -8
- data/lib/datadog/tracing/contrib/http/ext.rb +11 -10
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +10 -14
- data/lib/datadog/tracing/contrib/http/integration.rb +7 -9
- data/lib/datadog/tracing/contrib/http/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/http.rb +7 -6
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +9 -6
- data/lib/datadog/tracing/contrib/httpclient/ext.rb +11 -10
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +12 -12
- data/lib/datadog/tracing/contrib/httpclient/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/httpclient/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +9 -6
- data/lib/datadog/tracing/contrib/httprb/ext.rb +11 -10
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +10 -14
- data/lib/datadog/tracing/contrib/httprb/integration.rb +7 -7
- data/lib/datadog/tracing/contrib/httprb/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/integration.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/kafka/event.rb +4 -3
- data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +7 -3
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +4 -4
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +4 -4
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +5 -5
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/join_group.rb +5 -5
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/leave_group.rb +5 -5
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/sync_group.rb +5 -5
- data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +10 -5
- data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +10 -5
- data/lib/datadog/tracing/contrib/kafka/events.rb +10 -10
- data/lib/datadog/tracing/contrib/kafka/ext.rb +40 -40
- data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +4 -4
- data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +6 -6
- data/lib/datadog/tracing/contrib/kafka/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/kafka/patcher.rb +5 -5
- data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +7 -3
- data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +11 -9
- data/lib/datadog/tracing/contrib/karafka/ext.rb +13 -12
- data/lib/datadog/tracing/contrib/karafka/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/karafka/monitor.rb +14 -14
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +9 -9
- data/lib/datadog/tracing/contrib/karafka.rb +7 -6
- data/lib/datadog/tracing/contrib/lograge/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/lograge/ext.rb +1 -1
- data/lib/datadog/tracing/contrib/lograge/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +9 -9
- data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/mongodb/ext.rb +18 -18
- data/lib/datadog/tracing/contrib/mongodb/instrumentation.rb +3 -3
- data/lib/datadog/tracing/contrib/mongodb/integration.rb +7 -7
- data/lib/datadog/tracing/contrib/mongodb/parsers.rb +9 -9
- data/lib/datadog/tracing/contrib/mongodb/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +18 -21
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +9 -3
- data/lib/datadog/tracing/contrib/mysql2/ext.rb +11 -11
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +9 -12
- data/lib/datadog/tracing/contrib/mysql2/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/mysql2/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/opensearch/ext.rb +23 -23
- data/lib/datadog/tracing/contrib/opensearch/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +15 -19
- data/lib/datadog/tracing/contrib/opensearch/quantize.rb +5 -5
- data/lib/datadog/tracing/contrib/patchable.rb +1 -1
- data/lib/datadog/tracing/contrib/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +9 -3
- data/lib/datadog/tracing/contrib/pg/ext.rb +19 -19
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +21 -24
- data/lib/datadog/tracing/contrib/pg/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/pg/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/presto/ext.rb +21 -21
- data/lib/datadog/tracing/contrib/presto/instrumentation.rb +8 -10
- data/lib/datadog/tracing/contrib/presto/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/presto/patcher.rb +4 -4
- data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +2 -2
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +26 -13
- data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +7 -3
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +29 -5
- data/lib/datadog/tracing/contrib/que/configuration/settings.rb +7 -4
- data/lib/datadog/tracing/contrib/que/ext.rb +19 -18
- data/lib/datadog/tracing/contrib/que/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/que/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/que/tracer.rb +3 -2
- data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +3 -3
- data/lib/datadog/tracing/contrib/racecar/event.rb +5 -9
- data/lib/datadog/tracing/contrib/racecar/events/batch.rb +3 -3
- data/lib/datadog/tracing/contrib/racecar/events/consume.rb +3 -3
- data/lib/datadog/tracing/contrib/racecar/events/message.rb +3 -3
- data/lib/datadog/tracing/contrib/racecar/events.rb +4 -4
- data/lib/datadog/tracing/contrib/racecar/ext.rb +18 -18
- data/lib/datadog/tracing/contrib/racecar/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/racecar/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +14 -4
- data/lib/datadog/tracing/contrib/rack/ext.rb +43 -15
- data/lib/datadog/tracing/contrib/rack/header_collection.rb +3 -3
- data/lib/datadog/tracing/contrib/rack/header_tagging.rb +24 -1
- data/lib/datadog/tracing/contrib/rack/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +28 -25
- data/lib/datadog/tracing/contrib/rack/patcher.rb +5 -5
- data/lib/datadog/tracing/contrib/rack/request_queue.rb +4 -4
- data/lib/datadog/tracing/contrib/rack/route_inference.rb +27 -13
- data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +124 -6
- data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +1 -1
- data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +8 -5
- data/lib/datadog/tracing/contrib/rails/ext.rb +15 -14
- data/lib/datadog/tracing/contrib/rails/framework.rb +11 -11
- data/lib/datadog/tracing/contrib/rails/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
- data/lib/datadog/tracing/contrib/rails/middlewares.rb +1 -1
- data/lib/datadog/tracing/contrib/rails/patcher.rb +8 -9
- data/lib/datadog/tracing/contrib/rails/railtie.rb +4 -4
- data/lib/datadog/tracing/contrib/rails/runner.rb +4 -2
- data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +3 -3
- data/lib/datadog/tracing/contrib/rake/ext.rb +12 -12
- data/lib/datadog/tracing/contrib/rake/instrumentation.rb +9 -7
- data/lib/datadog/tracing/contrib/rake/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/rake/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +3 -3
- data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/redis/ext.rb +16 -16
- data/lib/datadog/tracing/contrib/redis/instrumentation.rb +6 -6
- data/lib/datadog/tracing/contrib/redis/integration.rb +7 -7
- data/lib/datadog/tracing/contrib/redis/patcher.rb +10 -10
- data/lib/datadog/tracing/contrib/redis/quantize.rb +40 -6
- data/lib/datadog/tracing/contrib/redis/tags.rb +6 -11
- data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +16 -8
- data/lib/datadog/tracing/contrib/registerable.rb +11 -0
- data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/resque/ext.rb +7 -7
- data/lib/datadog/tracing/contrib/resque/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/resque/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/resque/resque_job.rb +6 -5
- data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +7 -4
- data/lib/datadog/tracing/contrib/rest_client/ext.rb +10 -9
- data/lib/datadog/tracing/contrib/rest_client/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/rest_client/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +6 -10
- data/lib/datadog/tracing/contrib/roda/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/roda/ext.rb +6 -5
- data/lib/datadog/tracing/contrib/roda/instrumentation.rb +8 -5
- data/lib/datadog/tracing/contrib/roda/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/roda/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/semantic_logger/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/semantic_logger/ext.rb +1 -1
- data/lib/datadog/tracing/contrib/semantic_logger/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/semantic_logger/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/sequel/database.rb +7 -10
- data/lib/datadog/tracing/contrib/sequel/dataset.rb +6 -5
- data/lib/datadog/tracing/contrib/sequel/ext.rb +12 -8
- data/lib/datadog/tracing/contrib/sequel/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/sequel/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/sequel/utils.rb +116 -16
- data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/shoryuken/ext.rb +12 -12
- data/lib/datadog/tracing/contrib/shoryuken/integration.rb +6 -6
- data/lib/datadog/tracing/contrib/shoryuken/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +3 -2
- data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +9 -8
- data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +7 -3
- data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +12 -10
- data/lib/datadog/tracing/contrib/sidekiq/ext.rb +30 -29
- data/lib/datadog/tracing/contrib/sidekiq/integration.rb +7 -7
- data/lib/datadog/tracing/contrib/sidekiq/patcher.rb +8 -8
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/heartbeat.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/job_fetch.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/redis_info.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/scheduled_poller.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/stop.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +15 -14
- data/lib/datadog/tracing/contrib/sidekiq/utils.rb +10 -10
- data/lib/datadog/tracing/contrib/sidekiq.rb +7 -6
- data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +8 -4
- data/lib/datadog/tracing/contrib/sinatra/env.rb +6 -6
- data/lib/datadog/tracing/contrib/sinatra/ext.rb +17 -16
- data/lib/datadog/tracing/contrib/sinatra/framework.rb +2 -2
- data/lib/datadog/tracing/contrib/sinatra/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/sinatra/patcher.rb +5 -5
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +11 -10
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +9 -8
- data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/sneakers/ext.rb +12 -12
- data/lib/datadog/tracing/contrib/sneakers/integration.rb +19 -8
- data/lib/datadog/tracing/contrib/sneakers/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/sneakers/tracer.rb +5 -4
- data/lib/datadog/tracing/contrib/span_attribute_schema.rb +2 -2
- data/lib/datadog/tracing/contrib/status_range_env_parser.rb +2 -2
- data/lib/datadog/tracing/contrib/status_range_matcher.rb +4 -0
- data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/stripe/ext.rb +12 -12
- data/lib/datadog/tracing/contrib/stripe/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/stripe/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/stripe/request.rb +4 -4
- data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/sucker_punch/exception_handler.rb +1 -1
- data/lib/datadog/tracing/contrib/sucker_punch/ext.rb +13 -13
- data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +5 -4
- data/lib/datadog/tracing/contrib/sucker_punch/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/sucker_punch/patcher.rb +4 -4
- data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +9 -3
- data/lib/datadog/tracing/contrib/trilogy/ext.rb +11 -11
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +10 -12
- data/lib/datadog/tracing/contrib/trilogy/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/trilogy/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/utils/database.rb +5 -5
- data/lib/datadog/tracing/contrib/utils/quantization/{hash.rb → hash_formatter.rb} +3 -3
- data/lib/datadog/tracing/contrib/utils/quantization/http.rb +10 -10
- data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +7 -3
- data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +11 -9
- data/lib/datadog/tracing/contrib/waterdrop/ext.rb +4 -3
- data/lib/datadog/tracing/contrib/waterdrop/integration.rb +5 -5
- data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +2 -2
- data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +7 -7
- data/lib/datadog/tracing/contrib/waterdrop/producer.rb +3 -3
- data/lib/datadog/tracing/contrib/waterdrop.rb +7 -6
- data/lib/datadog/tracing/contrib.rb +62 -54
- data/lib/datadog/tracing/correlation.rb +12 -12
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -12
- data/lib/datadog/tracing/diagnostics/ext.rb +19 -19
- data/lib/datadog/tracing/diagnostics/health.rb +1 -1
- data/lib/datadog/tracing/distributed/b3_multi.rb +8 -8
- data/lib/datadog/tracing/distributed/b3_single.rb +4 -4
- data/lib/datadog/tracing/distributed/baggage.rb +82 -28
- data/lib/datadog/tracing/distributed/datadog.rb +32 -30
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +4 -17
- data/lib/datadog/tracing/distributed/fetcher.rb +1 -1
- data/lib/datadog/tracing/distributed/helpers.rb +3 -3
- data/lib/datadog/tracing/distributed/propagation.rb +43 -11
- data/lib/datadog/tracing/distributed/propagation_policy.rb +5 -0
- data/lib/datadog/tracing/distributed/trace_context.rb +108 -85
- data/lib/datadog/tracing/event.rb +2 -2
- data/lib/datadog/tracing/ext.rb +9 -0
- data/lib/datadog/tracing/metadata/analytics.rb +2 -2
- data/lib/datadog/tracing/metadata/errors.rb +4 -4
- data/lib/datadog/tracing/metadata/ext.rb +79 -65
- data/lib/datadog/tracing/metadata/metastruct.rb +1 -1
- data/lib/datadog/tracing/metadata/metastruct_tagging.rb +1 -1
- data/lib/datadog/tracing/metadata/tagging.rb +5 -5
- data/lib/datadog/tracing/metadata.rb +4 -4
- data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
- data/lib/datadog/tracing/pipeline.rb +3 -3
- data/lib/datadog/tracing/remote.rb +48 -10
- data/lib/datadog/tracing/sampling/all_sampler.rb +1 -1
- data/lib/datadog/tracing/sampling/ext.rb +9 -7
- data/lib/datadog/tracing/sampling/matcher.rb +4 -4
- data/lib/datadog/tracing/sampling/priority_sampler.rb +17 -4
- data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +3 -3
- data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +2 -2
- data/lib/datadog/tracing/sampling/rate_sampler.rb +2 -2
- data/lib/datadog/tracing/sampling/rule.rb +5 -5
- data/lib/datadog/tracing/sampling/rule_sampler.rb +66 -37
- data/lib/datadog/tracing/sampling/sampler.rb +2 -2
- data/lib/datadog/tracing/sampling/span/ext.rb +3 -3
- data/lib/datadog/tracing/sampling/span/matcher.rb +2 -2
- data/lib/datadog/tracing/sampling/span/rule.rb +1 -1
- data/lib/datadog/tracing/sampling/span/rule_parser.rb +10 -10
- data/lib/datadog/tracing/span.rb +9 -9
- data/lib/datadog/tracing/span_event.rb +5 -5
- data/lib/datadog/tracing/span_operation.rb +39 -26
- data/lib/datadog/tracing/sync_writer.rb +12 -8
- data/lib/datadog/tracing/trace_digest.rb +7 -0
- data/lib/datadog/tracing/trace_operation.rb +90 -21
- data/lib/datadog/tracing/trace_segment.rb +10 -10
- data/lib/datadog/tracing/tracer.rb +63 -22
- data/lib/datadog/tracing/transport/http/client.rb +4 -4
- data/lib/datadog/tracing/transport/http/statistics.rb +2 -2
- data/lib/datadog/tracing/transport/http/traces.rb +8 -8
- data/lib/datadog/tracing/transport/http.rb +10 -10
- data/lib/datadog/tracing/transport/io/client.rb +8 -11
- data/lib/datadog/tracing/transport/io/response.rb +1 -1
- data/lib/datadog/tracing/transport/io/traces.rb +33 -39
- data/lib/datadog/tracing/transport/io.rb +3 -3
- data/lib/datadog/tracing/transport/native/response.rb +71 -0
- data/lib/datadog/tracing/transport/native.rb +387 -0
- data/lib/datadog/tracing/transport/serializable_trace.rb +19 -19
- data/lib/datadog/tracing/transport/statistics.rb +2 -2
- data/lib/datadog/tracing/transport/trace_formatter.rb +20 -9
- data/lib/datadog/tracing/transport/traces.rb +13 -19
- data/lib/datadog/tracing/utils.rb +2 -2
- data/lib/datadog/tracing/workers.rb +4 -3
- data/lib/datadog/tracing/writer.rb +22 -10
- data/lib/datadog/tracing.rb +3 -3
- data/lib/datadog/version.rb +4 -4
- data/lib/datadog.rb +15 -11
- metadata +91 -23
- data/ext/datadog_profiling_native_extension/clock_id_noop.c +0 -21
- data/lib/datadog/ai_guard/configuration/settings.rb +0 -110
- data/lib/datadog/appsec/configuration/settings.rb +0 -409
- data/lib/datadog/appsec/contrib/rails/ext.rb +0 -13
- data/lib/datadog/core/utils/base64.rb +0 -22
- data/lib/datadog/data_streams/configuration/settings.rb +0 -49
- data/lib/datadog/di/configuration/settings.rb +0 -234
- data/lib/datadog/error_tracking/configuration/settings.rb +0 -63
- data/lib/datadog/tracing/workers/trace_writer.rb +0 -204
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include <ruby.h>
|
|
2
|
+
#include <ruby/debug.h>
|
|
2
3
|
|
|
3
4
|
#include "datadog_ruby_common.h"
|
|
4
5
|
#include "collectors_thread_context.h"
|
|
@@ -42,11 +43,8 @@
|
|
|
42
43
|
// When `thread_context_collector_on_gc_start` gets called, the current cpu and wall-time get recorded to the thread
|
|
43
44
|
// context: `cpu_time_at_gc_start_ns` and `wall_time_at_gc_start_ns`.
|
|
44
45
|
//
|
|
45
|
-
// While `cpu_time_at_gc_start_ns` is set,
|
|
46
|
-
//
|
|
47
|
-
// Wall-time accounting is not affected by this (e.g. we still record 60 seconds every 60 seconds).
|
|
48
|
-
//
|
|
49
|
-
// (Regular samples can still account for the cpu-time between the previous sample and the start of GC.)
|
|
46
|
+
// While `cpu_time_at_gc_start_ns` is set, we don't expect the thread to be sampled: the VM is doing GC
|
|
47
|
+
// on the thread holding the GVL so no other samples can/will be triggered until GC finishes.
|
|
50
48
|
//
|
|
51
49
|
// When `thread_context_collector_on_gc_finish` gets called, the cpu-time and wall-time spent during GC gets recorded
|
|
52
50
|
// into the global gc_tracking structure, and further samples are not affected. (The `cpu_time_at_previous_sample_ns`
|
|
@@ -75,15 +73,14 @@
|
|
|
75
73
|
|
|
76
74
|
#define THREAD_ID_LIMIT_CHARS 44 // Why 44? "#{2**64} (#{2**64})".size + 1 for \0
|
|
77
75
|
#define THREAD_INVOKE_LOCATION_LIMIT_CHARS 512
|
|
78
|
-
#define IS_WALL_TIME true
|
|
79
|
-
#define IS_NOT_WALL_TIME false
|
|
80
76
|
#define MISSING_TRACER_CONTEXT_KEY 0
|
|
81
77
|
#define TIME_BETWEEN_GC_EVENTS_NS MILLIS_AS_NS(10)
|
|
78
|
+
#define GVL_SUSPENDED ((uint64_t)1)
|
|
79
|
+
#define GVL_RUNNING ((uint64_t)0)
|
|
80
|
+
|
|
81
|
+
#define MAX(a, b) ((a) < (b) ? (b) : (a))
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
// (e.g. to avoid trying to gvl profile threads that are not from the main Ractor)
|
|
85
|
-
// and for which there's no data yet
|
|
86
|
-
#define GVL_WAITING_ENABLED_EMPTY RUBY_FIXNUM_MAX
|
|
83
|
+
static ID dd_per_thread_context_id; // Hidden ivar (no @ prefix, inaccessible from Ruby)
|
|
87
84
|
|
|
88
85
|
static ID at_active_span_id; // id of :@active_span in Ruby
|
|
89
86
|
static ID at_active_trace_id; // id of :@active_trace in Ruby
|
|
@@ -107,12 +104,20 @@ static ID server_id; // id of :server in Ruby
|
|
|
107
104
|
static ID otel_context_storage_id; // id of :__opentelemetry_context_storage__ in Ruby
|
|
108
105
|
static ID otel_fiber_context_storage_id; // id of :@opentelemetry_context in Ruby
|
|
109
106
|
|
|
110
|
-
// This is
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
|
|
107
|
+
// This is mutable and gets set last-writer-wins style by
|
|
108
|
+
// `thread_context_collector_reset_all_per_thread_contexts`, which is called whenever
|
|
109
|
+
// profiling is starting or restating.
|
|
110
|
+
//
|
|
111
|
+
// Note: We must be careful to not change this value while the profiler is still running,
|
|
112
|
+
// otherwise new threads can get the new value and cause profiling to stop with an
|
|
113
|
+
// exception due to the mismatched size.
|
|
114
|
+
//
|
|
115
|
+
// The initial value should be kept in sync with the default for DD_PROFILING_MAX_FRAMES
|
|
116
|
+
// in settings.rb. See `initialize_context` for details on why this is needed/used.
|
|
117
|
+
static uint16_t latest_max_frames = 400;
|
|
118
|
+
|
|
119
|
+
// Global tracepoint for RUBY_EVENT_THREAD_BEGIN. Created and enabled once when the first ThreadContext collector is initialized.
|
|
120
|
+
static VALUE thread_begin_tracepoint = Qnil;
|
|
116
121
|
|
|
117
122
|
typedef enum { OTEL_CONTEXT_ENABLED_FALSE, OTEL_CONTEXT_ENABLED_ONLY, OTEL_CONTEXT_ENABLED_BOTH } otel_context_enabled;
|
|
118
123
|
typedef enum { OTEL_CONTEXT_SOURCE_UNKNOWN, OTEL_CONTEXT_SOURCE_FIBER_IVAR, OTEL_CONTEXT_SOURCE_FIBER_LOCAL } otel_context_source;
|
|
@@ -122,28 +127,18 @@ typedef struct {
|
|
|
122
127
|
// Note: Places in this file that usually need to be changed when this struct is changed are tagged with
|
|
123
128
|
// "Update this when modifying state struct"
|
|
124
129
|
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
uint16_t max_frames;
|
|
128
|
-
// Hashmap <Thread Object, per_thread_context>
|
|
129
|
-
// Note: Be very careful when mutating this map, as it gets read e.g. in the middle of GC and signal handlers.
|
|
130
|
-
st_table *hash_map_per_thread_context;
|
|
130
|
+
// Output buffer for stack traces, passed to sample_thread()
|
|
131
|
+
sample_locations locations;
|
|
131
132
|
// Datadog::Profiling::StackRecorder instance
|
|
132
133
|
VALUE recorder_instance;
|
|
133
134
|
// If the tracer is available and enabled, this will be the fiber-local symbol for accessing its running context,
|
|
134
135
|
// to enable code hotspots and endpoint aggregation.
|
|
135
136
|
// When not available, this is set to MISSING_TRACER_CONTEXT_KEY.
|
|
136
137
|
ID tracer_context_key;
|
|
137
|
-
// Track how many regular samples we've taken. Does not include garbage collection samples.
|
|
138
|
-
// Currently **outside** of stats struct because we also use it to decide when to clean the contexts, and thus this
|
|
139
|
-
// is not (just) a stat.
|
|
140
|
-
unsigned int sample_count;
|
|
141
138
|
// Reusable array to get list of threads
|
|
142
139
|
VALUE thread_list_buffer;
|
|
143
140
|
// Used to omit endpoint names (retrieved from tracer) from collected data
|
|
144
141
|
bool endpoint_collection_enabled;
|
|
145
|
-
// Used to omit timestamps / timeline events from collected data
|
|
146
|
-
bool timeline_enabled;
|
|
147
142
|
// Used to control context collection
|
|
148
143
|
otel_context_enabled otel_context_enabled;
|
|
149
144
|
// Used to remember where otel context is being stored after we observe it the first time
|
|
@@ -158,14 +153,26 @@ typedef struct {
|
|
|
158
153
|
VALUE otel_current_span_key;
|
|
159
154
|
// Used to enable native filenames in stack traces
|
|
160
155
|
bool native_filenames_enabled;
|
|
156
|
+
// Used to include class/module names in method names (e.g. "Foo#bar" instead of "bar")
|
|
157
|
+
bool show_classes;
|
|
161
158
|
// Used to cache native filename lookup results (Map[void *function_pointer, char *filename])
|
|
162
159
|
st_table *native_filenames_cache;
|
|
160
|
+
// Used to attribute overhead during sampling to this component
|
|
161
|
+
VALUE overhead_filename;
|
|
162
|
+
// Minimum duration of a "Waiting for GVL" period to trigger a sample
|
|
163
|
+
uint32_t waiting_for_gvl_threshold_ns;
|
|
163
164
|
|
|
164
165
|
struct stats {
|
|
166
|
+
// Track how many regular samples we've taken. Does not include garbage collection samples.
|
|
167
|
+
unsigned int sample_count;
|
|
165
168
|
// Track how many garbage collection samples we've taken.
|
|
166
169
|
unsigned int gc_samples;
|
|
167
170
|
// See thread_context_collector_on_gc_start for details
|
|
168
171
|
unsigned int gc_samples_missed_due_to_missing_context;
|
|
172
|
+
// How many per-thread samples were skipped because the thread has been continuously suspended
|
|
173
|
+
// (no GVL) since its previous sample, so its Ruby stack cannot have changed.
|
|
174
|
+
unsigned int inactive_thread_samples_skipped;
|
|
175
|
+
unsigned int profiler_thread_samples_skipped;
|
|
169
176
|
} stats;
|
|
170
177
|
|
|
171
178
|
struct {
|
|
@@ -177,16 +184,90 @@ typedef struct {
|
|
|
177
184
|
} gc_tracking;
|
|
178
185
|
} thread_context_collector_state;
|
|
179
186
|
|
|
180
|
-
// Tracks per-thread state
|
|
181
|
-
|
|
187
|
+
// Tracks per-thread state.
|
|
188
|
+
// This state is global and lives forever on the Ruby Thread (until the Thread is GC'd).
|
|
189
|
+
// The state is created early on for all threads on the main Ractor
|
|
190
|
+
// (enabling a TracePoint only enables it for the current Ractor).
|
|
191
|
+
// The state is either created when the Thread starts running (via the RUBY_EVENT_THREAD_BEGIN TracePoint),
|
|
192
|
+
// or via `thread_context_collector_reset_all_per_thread_contexts` when the profiler starts.
|
|
193
|
+
//
|
|
194
|
+
// Unfortunately that RUBY_EVENT_THREAD_BEGIN TracePoint still fires after some other events:
|
|
195
|
+
// * RUBY_INTERNAL_THREAD_EVENT_RESUMED for the Thread acquiring the GVL for the first time
|
|
196
|
+
// * an early SIGPROF calling handle_sampling_signal()
|
|
197
|
+
// * Ruby might check for interrupts and run postponed jobs (e.g. thread_context_collector_sample)
|
|
198
|
+
// * another RUBY_EVENT_THREAD_BEGIN TracePoint which might run before ours
|
|
199
|
+
// * etc
|
|
200
|
+
// For those cases we have to ignore those events and we cannot assume the state is always set,
|
|
201
|
+
// however this only matters for a very short period when a thread starts.
|
|
202
|
+
struct per_thread_context {
|
|
182
203
|
sampling_buffer sampling_buffer;
|
|
183
204
|
char thread_id[THREAD_ID_LIMIT_CHARS];
|
|
184
205
|
ddog_CharSlice thread_id_char_slice;
|
|
185
206
|
char thread_invoke_location[THREAD_INVOKE_LOCATION_LIMIT_CHARS];
|
|
186
207
|
ddog_CharSlice thread_invoke_location_char_slice;
|
|
187
208
|
thread_cpu_time_id thread_cpu_time_id;
|
|
188
|
-
long cpu_time_at_previous_sample_ns;
|
|
189
|
-
long wall_time_at_previous_sample_ns;
|
|
209
|
+
long cpu_time_at_previous_sample_ns;
|
|
210
|
+
long wall_time_at_previous_sample_ns;
|
|
211
|
+
|
|
212
|
+
// There are 3 possible states for the GVL (per thread), and 3 transitions for which we receive GVL events:
|
|
213
|
+
// Thread holds the GVL
|
|
214
|
+
// on_gvl_released() the thread releases the GVL (RUBY_INTERNAL_THREAD_EVENT_SUSPENDED)
|
|
215
|
+
// Thread runs without the GVL
|
|
216
|
+
// on_gvl_waiting() the thread wants the GVL (RUBY_INTERNAL_THREAD_EVENT_READY)
|
|
217
|
+
// Thread is "Waiting for GVL"
|
|
218
|
+
// on_gvl_running() the thread now got the GVL (RUBY_INTERNAL_THREAD_EVENT_RESUMED)
|
|
219
|
+
// ... and the cycle restarts
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
// --- GVL waiting tracking state machine ---
|
|
223
|
+
//
|
|
224
|
+
// gvl_waiting_at tracks the GVL wait state for each profiled thread:
|
|
225
|
+
//
|
|
226
|
+
// ┌───────────────────────────────────┐
|
|
227
|
+
// │ on_gvl_waiting │
|
|
228
|
+
// │ ▼
|
|
229
|
+
// Not Waiting (0) ◀────────────────── Waiting (> 0)
|
|
230
|
+
// ▲ on_gvl_running │
|
|
231
|
+
// │ (below threshold) │ on_gvl_running (above threshold)
|
|
232
|
+
// │ ▼
|
|
233
|
+
// └─────────────────────────── Sample Pending (< 0)
|
|
234
|
+
// sample / sample_after_gvl_running
|
|
235
|
+
//
|
|
236
|
+
// Not Waiting (0): thread is running or not waiting for the GVL
|
|
237
|
+
// Waiting (> 0): monotonic wall time (ns) when the thread started waiting
|
|
238
|
+
// Sample Pending (< 0): negated timestamp; the wait ended and a sample is pending
|
|
239
|
+
//
|
|
240
|
+
// The field is accessed under the GVL for most functions EXCEPT on_gvl_waiting() which writes to it without the GVL.
|
|
241
|
+
// So we need to pack the above state in a single long to ensure atomicity.
|
|
242
|
+
long gvl_waiting_at;
|
|
243
|
+
|
|
244
|
+
// Per-thread "state + version" word, updated on every GVL state transition. The encoding is:
|
|
245
|
+
// - low bit: current state (1 = currently suspended, 0 = currently running)
|
|
246
|
+
// - bits 1+: monotonic event counter (incremented on every RESUMED)
|
|
247
|
+
// The hooks set the state bit explicitly rather than relying on parity, so the encoding stays
|
|
248
|
+
// correct even when events are not paired properly (as in tests).
|
|
249
|
+
//
|
|
250
|
+
// Note that SUSPENDED can happen multiple times in a row on Ruby 3.2,
|
|
251
|
+
// see https://github.com/DataDog/dd-trace-rb/pull/5777#discussion_r3388560254,
|
|
252
|
+
// the encoding is designed to naturally not change the field in such a case.
|
|
253
|
+
uint64_t gvl_state_change_count;
|
|
254
|
+
// Snapshot of the thread's gvl_state_change_count at the moment we last sampled it.
|
|
255
|
+
// Equality with this snapshot means no GVL transition since the last sample.
|
|
256
|
+
uint64_t gvl_state_change_count_at_previous_sample;
|
|
257
|
+
// True when the previous per-tick sample was skipped by the SUSPENDED-skip optimization, so the
|
|
258
|
+
// flush-before-serialize pass knows it needs to report this thread.
|
|
259
|
+
// As a result, we will accumulate all wall & CPU time as a single batch per reporting period,
|
|
260
|
+
// but this is deemed worth it for this optimization. In any case we don't know exactly
|
|
261
|
+
// at what time a thread was doing CPU work (unless it's on CPU 100% of the time).
|
|
262
|
+
bool was_skipped_at_last_sample;
|
|
263
|
+
// Set as true for CpuAndWallTimeWorker and IdleSamplingHelper threads.
|
|
264
|
+
// When true, per-tick samples are skipped entirely; the thread is sampled only once per
|
|
265
|
+
// reporting period during the on_serialize flush.
|
|
266
|
+
//
|
|
267
|
+
// These threads are always in native code so their stacks aren't interesting;
|
|
268
|
+
// the Profiling::Scheduler thread on the other hand does a lot of different
|
|
269
|
+
// things using a mix of Ruby and native code, so that one isn't considered internal.
|
|
270
|
+
bool is_profiler_internal_thread;
|
|
190
271
|
|
|
191
272
|
struct {
|
|
192
273
|
// Both of these fields are set by on_gc_start and kept until on_gc_finish is called.
|
|
@@ -194,7 +275,7 @@ typedef struct {
|
|
|
194
275
|
long cpu_time_at_start_ns;
|
|
195
276
|
long wall_time_at_start_ns;
|
|
196
277
|
} gc_tracking;
|
|
197
|
-
}
|
|
278
|
+
};
|
|
198
279
|
|
|
199
280
|
// Used to correlate profiles with traces
|
|
200
281
|
typedef struct {
|
|
@@ -212,29 +293,26 @@ typedef struct {
|
|
|
212
293
|
|
|
213
294
|
static void thread_context_collector_typed_data_mark(void *state_ptr);
|
|
214
295
|
static void thread_context_collector_typed_data_free(void *state_ptr);
|
|
215
|
-
static
|
|
216
|
-
static
|
|
296
|
+
static void per_thread_context_typed_data_mark(void *ctx_ptr);
|
|
297
|
+
static void per_thread_context_typed_data_free(void *ctx_ptr);
|
|
217
298
|
static VALUE _native_new(VALUE klass);
|
|
218
299
|
static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
|
|
219
|
-
static VALUE _native_sample(VALUE self, VALUE collector_instance, VALUE
|
|
300
|
+
static VALUE _native_sample(VALUE self, VALUE collector_instance, VALUE allow_exception);
|
|
220
301
|
static VALUE _native_on_gc_start(VALUE self, VALUE collector_instance);
|
|
221
302
|
static VALUE _native_on_gc_finish(VALUE self, VALUE collector_instance);
|
|
222
303
|
static VALUE _native_sample_after_gc(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE allow_exception);
|
|
223
304
|
static void update_metrics_and_sample(
|
|
224
305
|
thread_context_collector_state *state,
|
|
225
306
|
VALUE thread_being_sampled,
|
|
226
|
-
VALUE stack_from_thread,
|
|
227
307
|
per_thread_context *thread_context,
|
|
228
|
-
sampling_buffer* sampling_buffer,
|
|
229
308
|
long current_cpu_time_ns,
|
|
230
|
-
long current_monotonic_wall_time_ns
|
|
309
|
+
long current_monotonic_wall_time_ns,
|
|
310
|
+
bool force_sample
|
|
231
311
|
);
|
|
232
312
|
static void trigger_sample_for_thread(
|
|
233
313
|
thread_context_collector_state *state,
|
|
234
|
-
VALUE
|
|
235
|
-
VALUE stack_from_thread,
|
|
314
|
+
VALUE thread_being_sampled,
|
|
236
315
|
per_thread_context *thread_context,
|
|
237
|
-
sampling_buffer* sampling_buffer,
|
|
238
316
|
sample_values values,
|
|
239
317
|
long current_monotonic_wall_time_ns,
|
|
240
318
|
ddog_CharSlice *ruby_vm_type,
|
|
@@ -243,19 +321,16 @@ static void trigger_sample_for_thread(
|
|
|
243
321
|
bool is_safe_to_allocate_objects
|
|
244
322
|
);
|
|
245
323
|
static VALUE _native_thread_list(VALUE self);
|
|
246
|
-
static
|
|
247
|
-
static per_thread_context *
|
|
248
|
-
static void initialize_context(VALUE thread, per_thread_context *thread_context
|
|
249
|
-
static void free_context(per_thread_context* thread_context);
|
|
324
|
+
static void check_frozen_thread(VALUE thread);
|
|
325
|
+
static per_thread_context *get_or_create_context_for(VALUE thread);
|
|
326
|
+
static void initialize_context(VALUE thread, per_thread_context *thread_context);
|
|
250
327
|
static VALUE _native_inspect(VALUE self, VALUE collector_instance);
|
|
251
|
-
static VALUE
|
|
252
|
-
static
|
|
253
|
-
static VALUE stats_as_ruby_hash(thread_context_collector_state *state);
|
|
328
|
+
static VALUE per_thread_context_to_ruby_hash(per_thread_context *thread_context);
|
|
329
|
+
static VALUE stats_to_ruby_hash(thread_context_collector_state *state, VALUE hash);
|
|
254
330
|
static VALUE gc_tracking_as_ruby_hash(thread_context_collector_state *state);
|
|
255
|
-
static void remove_context_for_dead_threads(thread_context_collector_state *state);
|
|
256
|
-
static int remove_if_dead_thread(st_data_t key_thread, st_data_t value_context, st_data_t _argument);
|
|
257
331
|
static VALUE _native_per_thread_context(VALUE self, VALUE collector_instance);
|
|
258
|
-
static long
|
|
332
|
+
static long update_cpu_time_since_previous_sample(per_thread_context *thread_context, long current_cpu_time_ns);
|
|
333
|
+
static long update_wall_time_since_previous_sample(per_thread_context *thread_context, long current_wall_time_ns);
|
|
259
334
|
static long cpu_time_now_ns(per_thread_context *thread_context);
|
|
260
335
|
static long thread_id_for(VALUE thread);
|
|
261
336
|
static VALUE _native_stats(VALUE self, VALUE collector_instance);
|
|
@@ -285,16 +360,17 @@ static VALUE _native_sample_skipped_allocation_samples(DDTRACE_UNUSED VALUE self
|
|
|
285
360
|
static bool handle_gvl_waiting(
|
|
286
361
|
thread_context_collector_state *state,
|
|
287
362
|
VALUE thread_being_sampled,
|
|
288
|
-
VALUE stack_from_thread,
|
|
289
363
|
per_thread_context *thread_context,
|
|
290
|
-
sampling_buffer* sampling_buffer,
|
|
291
364
|
long current_cpu_time_ns
|
|
292
365
|
);
|
|
293
366
|
static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
294
|
-
static VALUE
|
|
295
|
-
|
|
296
|
-
static VALUE
|
|
297
|
-
static VALUE
|
|
367
|
+
static VALUE _native_on_gvl_released(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
368
|
+
#ifndef NO_GVL_INSTRUMENTATION
|
|
369
|
+
static VALUE _native_gvl_waiting_at_for(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
370
|
+
static VALUE _native_on_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread);
|
|
371
|
+
static VALUE _native_sample_after_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread, VALUE allow_exception);
|
|
372
|
+
#endif
|
|
373
|
+
static VALUE _native_apply_delta_to_cpu_time_at_previous_sample_ns(DDTRACE_UNUSED VALUE self, VALUE thread, VALUE delta_ns);
|
|
298
374
|
static void otel_without_ddtrace_trace_identifiers_for(
|
|
299
375
|
thread_context_collector_state *state,
|
|
300
376
|
VALUE thread,
|
|
@@ -305,11 +381,17 @@ static otel_span otel_span_from(VALUE otel_context, VALUE otel_current_span_key)
|
|
|
305
381
|
static uint64_t otel_span_id_to_uint(VALUE otel_span_id);
|
|
306
382
|
static VALUE safely_lookup_hash_without_going_into_ruby_code(VALUE hash, VALUE key);
|
|
307
383
|
static VALUE _native_system_epoch_time_now_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
|
308
|
-
static VALUE _native_prepare_sample_inside_signal_handler(DDTRACE_UNUSED VALUE self
|
|
384
|
+
static VALUE _native_prepare_sample_inside_signal_handler(DDTRACE_UNUSED VALUE self);
|
|
385
|
+
static VALUE _native_mark_thread_as_profiler_internal(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
386
|
+
static VALUE _native_remove_per_thread_context_for(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
387
|
+
static VALUE _native_global_reset_per_thread_context(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
|
388
|
+
static bool skip_sample(thread_context_collector_state *state, per_thread_context *thread_context, bool is_gvl_waiting_state, bool force_sample);
|
|
389
|
+
static void on_thread_begin_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused);
|
|
309
390
|
|
|
310
391
|
void collectors_thread_context_init(VALUE profiling_module) {
|
|
311
392
|
VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
|
|
312
393
|
VALUE collectors_thread_context_class = rb_define_class_under(collectors_module, "ThreadContext", rb_cObject);
|
|
394
|
+
|
|
313
395
|
// Hosts methods used for testing the native code using RSpec
|
|
314
396
|
VALUE testing_module = rb_define_module_under(collectors_thread_context_class, "Testing");
|
|
315
397
|
|
|
@@ -326,7 +408,7 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
|
326
408
|
rb_define_singleton_method(collectors_thread_context_class, "_native_initialize", _native_initialize, -1);
|
|
327
409
|
rb_define_singleton_method(collectors_thread_context_class, "_native_inspect", _native_inspect, 1);
|
|
328
410
|
rb_define_singleton_method(collectors_thread_context_class, "_native_reset_after_fork", _native_reset_after_fork, 1);
|
|
329
|
-
rb_define_singleton_method(testing_module, "_native_sample", _native_sample,
|
|
411
|
+
rb_define_singleton_method(testing_module, "_native_sample", _native_sample, 2);
|
|
330
412
|
rb_define_singleton_method(testing_module, "_native_sample_allocation", _native_sample_allocation, 3);
|
|
331
413
|
rb_define_singleton_method(testing_module, "_native_on_gc_start", _native_on_gc_start, 1);
|
|
332
414
|
rb_define_singleton_method(testing_module, "_native_on_gc_finish", _native_on_gc_finish, 1);
|
|
@@ -338,14 +420,18 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
|
338
420
|
rb_define_singleton_method(testing_module, "_native_new_empty_thread", _native_new_empty_thread, 0);
|
|
339
421
|
rb_define_singleton_method(testing_module, "_native_sample_skipped_allocation_samples", _native_sample_skipped_allocation_samples, 2);
|
|
340
422
|
rb_define_singleton_method(testing_module, "_native_system_epoch_time_now_ns", _native_system_epoch_time_now_ns, 1);
|
|
341
|
-
rb_define_singleton_method(testing_module, "_native_prepare_sample_inside_signal_handler", _native_prepare_sample_inside_signal_handler,
|
|
423
|
+
rb_define_singleton_method(testing_module, "_native_prepare_sample_inside_signal_handler", _native_prepare_sample_inside_signal_handler, 0);
|
|
424
|
+
rb_define_singleton_method(testing_module, "_native_remove_per_thread_context_for", _native_remove_per_thread_context_for, 1);
|
|
425
|
+
rb_define_singleton_method(testing_module, "_native_global_reset_per_thread_context", _native_global_reset_per_thread_context, 1);
|
|
426
|
+
rb_define_singleton_method(testing_module, "_native_mark_thread_as_profiler_internal", _native_mark_thread_as_profiler_internal, 1);
|
|
427
|
+
rb_define_singleton_method(testing_module, "_native_on_gvl_waiting", _native_on_gvl_waiting, 1);
|
|
428
|
+
rb_define_singleton_method(testing_module, "_native_on_gvl_released", _native_on_gvl_released, 1);
|
|
342
429
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
343
|
-
rb_define_singleton_method(testing_module, "_native_on_gvl_waiting", _native_on_gvl_waiting, 1);
|
|
344
430
|
rb_define_singleton_method(testing_module, "_native_gvl_waiting_at_for", _native_gvl_waiting_at_for, 1);
|
|
345
|
-
rb_define_singleton_method(testing_module, "_native_on_gvl_running", _native_on_gvl_running,
|
|
431
|
+
rb_define_singleton_method(testing_module, "_native_on_gvl_running", _native_on_gvl_running, 2);
|
|
346
432
|
rb_define_singleton_method(testing_module, "_native_sample_after_gvl_running", _native_sample_after_gvl_running, 3);
|
|
347
|
-
rb_define_singleton_method(testing_module, "_native_apply_delta_to_cpu_time_at_previous_sample_ns", _native_apply_delta_to_cpu_time_at_previous_sample_ns, 3);
|
|
348
433
|
#endif
|
|
434
|
+
rb_define_singleton_method(testing_module, "_native_apply_delta_to_cpu_time_at_previous_sample_ns", _native_apply_delta_to_cpu_time_at_previous_sample_ns, 2);
|
|
349
435
|
|
|
350
436
|
at_active_span_id = rb_intern_const("@active_span");
|
|
351
437
|
at_active_trace_id = rb_intern_const("@active_trace");
|
|
@@ -366,10 +452,12 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
|
366
452
|
otel_context_storage_id = rb_intern_const("__opentelemetry_context_storage__");
|
|
367
453
|
otel_fiber_context_storage_id = rb_intern_const("@opentelemetry_context");
|
|
368
454
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
455
|
+
dd_per_thread_context_id = rb_intern_const("dd_per_thread_context");
|
|
456
|
+
|
|
457
|
+
// This will raise if Ruby already ran out of thread-local keys
|
|
458
|
+
per_thread_context_tls_init();
|
|
459
|
+
|
|
460
|
+
rb_global_variable(&thread_begin_tracepoint);
|
|
373
461
|
|
|
374
462
|
gc_profiling_init();
|
|
375
463
|
}
|
|
@@ -394,10 +482,10 @@ static void thread_context_collector_typed_data_mark(void *state_ptr) {
|
|
|
394
482
|
|
|
395
483
|
// Update this when modifying state struct
|
|
396
484
|
rb_gc_mark(state->recorder_instance);
|
|
397
|
-
st_foreach(state->hash_map_per_thread_context, hash_map_per_thread_context_mark, 0 /* unused */);
|
|
398
485
|
rb_gc_mark(state->thread_list_buffer);
|
|
399
486
|
rb_gc_mark(state->main_thread);
|
|
400
487
|
rb_gc_mark(state->otel_current_span_key);
|
|
488
|
+
rb_gc_mark(state->overhead_filename);
|
|
401
489
|
}
|
|
402
490
|
|
|
403
491
|
static void thread_context_collector_typed_data_free(void *state_ptr) {
|
|
@@ -407,36 +495,54 @@ static void thread_context_collector_typed_data_free(void *state_ptr) {
|
|
|
407
495
|
|
|
408
496
|
// Important: Remember that we're only guaranteed to see here what's been set in _native_new, aka
|
|
409
497
|
// pointers that have been set NULL there may still be NULL here.
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
// Free each entry in the map
|
|
413
|
-
st_foreach(state->hash_map_per_thread_context, hash_map_per_thread_context_free_values, 0 /* unused */);
|
|
414
|
-
// ...and then the map
|
|
415
|
-
st_free_table(state->hash_map_per_thread_context);
|
|
498
|
+
sample_locations_free(&state->locations);
|
|
416
499
|
|
|
417
500
|
st_free_table(state->native_filenames_cache);
|
|
418
501
|
|
|
419
502
|
ruby_xfree(state);
|
|
420
503
|
}
|
|
421
504
|
|
|
422
|
-
//
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
505
|
+
// per_thread_context is wrapped in a TypedData Ruby object stored as an ivar on each Ruby Thread.
|
|
506
|
+
// This gives us automatic GC marking (for sampling_buffer iseq VALUEs) and lifecycle management.
|
|
507
|
+
static const rb_data_type_t per_thread_context_typed_data = {
|
|
508
|
+
.wrap_struct_name = "Datadog::Profiling::PerThreadContext",
|
|
509
|
+
.function = {
|
|
510
|
+
.dmark = per_thread_context_typed_data_mark,
|
|
511
|
+
.dfree = per_thread_context_typed_data_free,
|
|
512
|
+
.dsize = NULL,
|
|
513
|
+
},
|
|
514
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
515
|
+
};
|
|
426
516
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
517
|
+
static void per_thread_context_typed_data_mark(void *ctx_ptr) {
|
|
518
|
+
per_thread_context *ctx = (per_thread_context *) ctx_ptr;
|
|
519
|
+
if (sampling_buffer_needs_marking(&ctx->sampling_buffer)) {
|
|
520
|
+
sampling_buffer_mark(&ctx->sampling_buffer);
|
|
430
521
|
}
|
|
522
|
+
}
|
|
431
523
|
|
|
432
|
-
|
|
524
|
+
static void per_thread_context_typed_data_free(void *ctx_ptr) {
|
|
525
|
+
per_thread_context *ctx = (per_thread_context *) ctx_ptr;
|
|
526
|
+
sampling_buffer_free(&ctx->sampling_buffer);
|
|
527
|
+
free(ctx);
|
|
433
528
|
}
|
|
434
529
|
|
|
435
|
-
//
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
530
|
+
// Only for testing: removes the per-thread context without recreating it, so the thread has no context.
|
|
531
|
+
// This simulates situations where we observe a thread running before our RUBY_EVENT_THREAD_BEGIN tracepoint fires,
|
|
532
|
+
// so that we can test our `get_per_thread_context(...) => null` code paths.
|
|
533
|
+
static VALUE _native_remove_per_thread_context_for(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
|
534
|
+
check_frozen_thread(thread);
|
|
535
|
+
per_thread_context *ctx = get_per_thread_context(thread);
|
|
536
|
+
if (ctx != NULL) {
|
|
537
|
+
set_per_thread_context(thread, NULL);
|
|
538
|
+
rb_ivar_set(thread, dd_per_thread_context_id, Qnil);
|
|
539
|
+
}
|
|
540
|
+
return Qnil;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
static VALUE _native_global_reset_per_thread_context(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
|
|
544
|
+
thread_context_collector_reset_all_per_thread_contexts(collector_instance);
|
|
545
|
+
return Qnil;
|
|
440
546
|
}
|
|
441
547
|
|
|
442
548
|
static VALUE _native_new(VALUE klass) {
|
|
@@ -446,18 +552,13 @@ static VALUE _native_new(VALUE klass) {
|
|
|
446
552
|
// being leaked.
|
|
447
553
|
|
|
448
554
|
// Update this when modifying state struct
|
|
449
|
-
state->locations = NULL;
|
|
450
|
-
state->max_frames = 0;
|
|
451
|
-
state->hash_map_per_thread_context =
|
|
452
|
-
// "numtable" is an awful name, but TL;DR it's what should be used when keys are `VALUE`s.
|
|
453
|
-
st_init_numtable();
|
|
454
555
|
state->recorder_instance = Qnil;
|
|
455
556
|
state->tracer_context_key = MISSING_TRACER_CONTEXT_KEY;
|
|
456
557
|
VALUE thread_list_buffer = rb_ary_new();
|
|
457
558
|
state->thread_list_buffer = thread_list_buffer;
|
|
458
559
|
state->endpoint_collection_enabled = true;
|
|
459
|
-
state->timeline_enabled = true;
|
|
460
560
|
state->native_filenames_enabled = false;
|
|
561
|
+
state->show_classes = false;
|
|
461
562
|
state->native_filenames_cache = st_init_numtable();
|
|
462
563
|
state->otel_context_enabled = OTEL_CONTEXT_ENABLED_FALSE;
|
|
463
564
|
state->otel_context_source = OTEL_CONTEXT_SOURCE_UNKNOWN;
|
|
@@ -490,28 +591,32 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
490
591
|
VALUE max_frames = rb_hash_fetch(options, ID2SYM(rb_intern("max_frames")));
|
|
491
592
|
VALUE tracer_context_key = rb_hash_fetch(options, ID2SYM(rb_intern("tracer_context_key")));
|
|
492
593
|
VALUE endpoint_collection_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("endpoint_collection_enabled")));
|
|
493
|
-
VALUE timeline_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("timeline_enabled")));
|
|
494
594
|
VALUE waiting_for_gvl_threshold_ns = rb_hash_fetch(options, ID2SYM(rb_intern("waiting_for_gvl_threshold_ns")));
|
|
495
595
|
VALUE otel_context_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("otel_context_enabled")));
|
|
496
596
|
VALUE native_filenames_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("native_filenames_enabled")));
|
|
597
|
+
VALUE show_classes = rb_hash_fetch(options, ID2SYM(rb_intern("show_classes")));
|
|
598
|
+
VALUE overhead_filename = rb_hash_fetch(options, ID2SYM(rb_intern("overhead_filename")));
|
|
497
599
|
|
|
498
600
|
ENFORCE_TYPE(max_frames, T_FIXNUM);
|
|
499
601
|
ENFORCE_BOOLEAN(endpoint_collection_enabled);
|
|
500
|
-
ENFORCE_BOOLEAN(timeline_enabled);
|
|
501
602
|
ENFORCE_TYPE(waiting_for_gvl_threshold_ns, T_FIXNUM);
|
|
502
603
|
ENFORCE_BOOLEAN(native_filenames_enabled);
|
|
604
|
+
ENFORCE_BOOLEAN(show_classes);
|
|
605
|
+
ENFORCE_TYPE(overhead_filename, T_STRING);
|
|
606
|
+
|
|
607
|
+
uint16_t max_frame_int = sampling_buffer_check_max_frames(NUM2INT(max_frames));
|
|
503
608
|
|
|
504
609
|
thread_context_collector_state *state;
|
|
505
610
|
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
506
611
|
|
|
507
612
|
// Update this when modifying state struct
|
|
508
|
-
state->
|
|
509
|
-
state->locations = ruby_xcalloc(state->max_frames, sizeof(ddog_prof_Location));
|
|
510
|
-
// hash_map_per_thread_context is already initialized, nothing to do here
|
|
613
|
+
sample_locations_initialize(&state->locations, max_frame_int, show_classes == Qtrue);
|
|
511
614
|
state->recorder_instance = enforce_recorder_instance(recorder_instance);
|
|
615
|
+
recorder_install_on_serialize(recorder_instance, self_instance);
|
|
512
616
|
state->endpoint_collection_enabled = (endpoint_collection_enabled == Qtrue);
|
|
513
|
-
state->timeline_enabled = (timeline_enabled == Qtrue);
|
|
514
617
|
state->native_filenames_enabled = (native_filenames_enabled == Qtrue);
|
|
618
|
+
state->show_classes = (show_classes == Qtrue);
|
|
619
|
+
state->overhead_filename = overhead_filename;
|
|
515
620
|
if (otel_context_enabled == Qfalse || otel_context_enabled == Qnil) {
|
|
516
621
|
state->otel_context_enabled = OTEL_CONTEXT_ENABLED_FALSE;
|
|
517
622
|
} else if (otel_context_enabled == ID2SYM(rb_intern("only"))) {
|
|
@@ -522,7 +627,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
522
627
|
raise_error(rb_eArgError, "Unexpected value for otel_context_enabled: %+" PRIsVALUE, otel_context_enabled);
|
|
523
628
|
}
|
|
524
629
|
|
|
525
|
-
|
|
630
|
+
state->waiting_for_gvl_threshold_ns = NUM2UINT(waiting_for_gvl_threshold_ns);
|
|
526
631
|
|
|
527
632
|
if (RTEST(tracer_context_key)) {
|
|
528
633
|
ENFORCE_TYPE(tracer_context_key, T_SYMBOL);
|
|
@@ -532,19 +637,22 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
532
637
|
state->tracer_context_key = rb_to_id(tracer_context_key);
|
|
533
638
|
}
|
|
534
639
|
|
|
640
|
+
if (thread_begin_tracepoint == Qnil) {
|
|
641
|
+
thread_begin_tracepoint = rb_tracepoint_new(Qnil, RUBY_EVENT_THREAD_BEGIN, on_thread_begin_event, NULL);
|
|
642
|
+
rb_tracepoint_enable(thread_begin_tracepoint);
|
|
643
|
+
}
|
|
644
|
+
|
|
535
645
|
return Qtrue;
|
|
536
646
|
}
|
|
537
647
|
|
|
538
648
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
|
539
649
|
// It SHOULD NOT be used for other purposes.
|
|
540
|
-
static VALUE _native_sample(DDTRACE_UNUSED VALUE _self, VALUE collector_instance, VALUE
|
|
650
|
+
static VALUE _native_sample(DDTRACE_UNUSED VALUE _self, VALUE collector_instance, VALUE allow_exception) {
|
|
541
651
|
ENFORCE_BOOLEAN(allow_exception);
|
|
542
652
|
|
|
543
|
-
if (!is_thread_alive(profiler_overhead_stack_thread)) raise_error(rb_eArgError, "Unexpected: profiler_overhead_stack_thread is not alive");
|
|
544
|
-
|
|
545
653
|
if (allow_exception == Qfalse) debug_enter_unsafe_context();
|
|
546
654
|
|
|
547
|
-
thread_context_collector_sample(collector_instance, monotonic_wall_time_now_ns(RAISE_ON_FAILURE)
|
|
655
|
+
thread_context_collector_sample(collector_instance, monotonic_wall_time_now_ns(RAISE_ON_FAILURE));
|
|
548
656
|
|
|
549
657
|
if (allow_exception == Qfalse) debug_leave_unsafe_context();
|
|
550
658
|
|
|
@@ -587,6 +695,44 @@ static VALUE _native_sample_after_gc(DDTRACE_UNUSED VALUE self, VALUE collector_
|
|
|
587
695
|
return Qtrue;
|
|
588
696
|
}
|
|
589
697
|
|
|
698
|
+
// Record profiler sampling overhead as a placeholder stack
|
|
699
|
+
static void record_sampling_overhead(thread_context_collector_state *state, per_thread_context *current_thread_context) {
|
|
700
|
+
long wall_time_after_sampling = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
701
|
+
long cpu_time_after_sampling = cpu_time_now_ns(current_thread_context);
|
|
702
|
+
|
|
703
|
+
long overhead_cpu_time_ns = update_cpu_time_since_previous_sample(current_thread_context, cpu_time_after_sampling);
|
|
704
|
+
long overhead_wall_time_ns = update_wall_time_since_previous_sample(current_thread_context, wall_time_after_sampling);
|
|
705
|
+
|
|
706
|
+
ddog_prof_Label overhead_labels[] = {
|
|
707
|
+
{.key = DDOG_CHARSLICE_C("thread id"), .str = DDOG_CHARSLICE_C("0"), .num = 0},
|
|
708
|
+
{.key = DDOG_CHARSLICE_C("thread name"), .str = DDOG_CHARSLICE_C("Datadog::Profiling::Sampling"), .num = 0},
|
|
709
|
+
{.key = DDOG_CHARSLICE_C("state"), .str = DDOG_CHARSLICE_C("had cpu"), .num = 0},
|
|
710
|
+
{.key = DDOG_CHARSLICE_C("profiler overhead"), .num = 1},
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
int64_t end_timestamp_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, wall_time_after_sampling);
|
|
714
|
+
|
|
715
|
+
ddog_prof_Location overhead_location = {
|
|
716
|
+
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C(""), .build_id_id = {}},
|
|
717
|
+
.function = {
|
|
718
|
+
.name = DDOG_CHARSLICE_C("sampling"),
|
|
719
|
+
.filename = char_slice_from_ruby_string(state->overhead_filename),
|
|
720
|
+
},
|
|
721
|
+
.line = 0,
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
record_sample(
|
|
725
|
+
state->recorder_instance,
|
|
726
|
+
(ddog_prof_Slice_Location) {.ptr = &overhead_location, .len = 1},
|
|
727
|
+
(sample_values) {.cpu_time_ns = overhead_cpu_time_ns, .cpu_or_wall_samples = 1, .wall_time_ns = overhead_wall_time_ns},
|
|
728
|
+
(sample_labels) {
|
|
729
|
+
.labels = (ddog_prof_Slice_Label) {.ptr = overhead_labels, .len = sizeof(overhead_labels) / sizeof(overhead_labels[0])},
|
|
730
|
+
.state_label = NULL,
|
|
731
|
+
.end_timestamp_ns = end_timestamp_ns,
|
|
732
|
+
}
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
|
|
590
736
|
// This function gets called from the Collectors::CpuAndWallTimeWorker to trigger the actual sampling.
|
|
591
737
|
//
|
|
592
738
|
// Assumption 1: This function is called in a thread that is holding the Global VM Lock. Caller is responsible for enforcing this.
|
|
@@ -595,14 +741,12 @@ static VALUE _native_sample_after_gc(DDTRACE_UNUSED VALUE self, VALUE collector_
|
|
|
595
741
|
// Assumption 4: This function IS NOT called in a reentrant way.
|
|
596
742
|
// Assumption 5: This function is called from the main Ractor (if Ruby has support for Ractors).
|
|
597
743
|
//
|
|
598
|
-
|
|
599
|
-
// (belonging to ddtrace), so that the overhead is visible in the profile rather than blamed on user code.
|
|
600
|
-
void thread_context_collector_sample(VALUE self_instance, long current_monotonic_wall_time_ns, VALUE profiler_overhead_stack_thread) {
|
|
744
|
+
void thread_context_collector_sample(VALUE self_instance, long current_monotonic_wall_time_ns) {
|
|
601
745
|
thread_context_collector_state *state;
|
|
602
746
|
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
603
747
|
|
|
604
748
|
VALUE current_thread = rb_thread_current();
|
|
605
|
-
per_thread_context *current_thread_context = get_or_create_context_for(current_thread
|
|
749
|
+
per_thread_context *current_thread_context = get_or_create_context_for(current_thread);
|
|
606
750
|
long cpu_time_at_sample_start_for_current_thread = cpu_time_now_ns(current_thread_context);
|
|
607
751
|
|
|
608
752
|
VALUE threads = thread_list(state);
|
|
@@ -610,76 +754,57 @@ void thread_context_collector_sample(VALUE self_instance, long current_monotonic
|
|
|
610
754
|
const long thread_count = RARRAY_LEN(threads);
|
|
611
755
|
for (long i = 0; i < thread_count; i++) {
|
|
612
756
|
VALUE thread = RARRAY_AREF(threads, i);
|
|
613
|
-
per_thread_context *thread_context = get_or_create_context_for(thread
|
|
757
|
+
per_thread_context *thread_context = get_or_create_context_for(thread);
|
|
614
758
|
|
|
615
|
-
// We account for cpu-time for the current thread in a different way
|
|
616
|
-
// blaming the time the profiler took on whatever
|
|
617
|
-
|
|
759
|
+
// We account for cpu-time for the current thread in a different way: we use the cpu-time at sampling start,
|
|
760
|
+
// to avoid blaming the time the profiler took on whatever is currently running on the thread,
|
|
761
|
+
// and instead we report that time the profiler took as sampling overhead below.
|
|
762
|
+
long current_cpu_time_ns = (thread == current_thread) ? cpu_time_at_sample_start_for_current_thread : cpu_time_now_ns(thread_context);
|
|
618
763
|
|
|
619
764
|
update_metrics_and_sample(
|
|
620
765
|
state,
|
|
621
|
-
|
|
622
|
-
/* stack_from_thread: */ thread,
|
|
766
|
+
thread,
|
|
623
767
|
thread_context,
|
|
624
|
-
&thread_context->sampling_buffer,
|
|
625
768
|
current_cpu_time_ns,
|
|
626
|
-
current_monotonic_wall_time_ns
|
|
627
|
-
|
|
769
|
+
current_monotonic_wall_time_ns,
|
|
770
|
+
false);
|
|
628
771
|
}
|
|
629
772
|
|
|
630
|
-
state->sample_count++;
|
|
773
|
+
state->stats.sample_count++;
|
|
631
774
|
|
|
632
|
-
//
|
|
633
|
-
//
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
// Here we use the overhead thread's sampling buffer so as to not invalidate the cache in the buffer of the thread being sampled
|
|
642
|
-
&get_or_create_context_for(profiler_overhead_stack_thread, state)->sampling_buffer,
|
|
643
|
-
cpu_time_now_ns(current_thread_context),
|
|
644
|
-
monotonic_wall_time_now_ns(RAISE_ON_FAILURE)
|
|
645
|
-
);
|
|
775
|
+
// If the current thread is a profiler-internal thread, we don't use record_sampling_overhead()
|
|
776
|
+
// and accept the sampling overhead is attributed to the profiler-internal thread
|
|
777
|
+
// (both are under the same datadog group in the flamegraph).
|
|
778
|
+
// The reason we need to do this is profiler-internal threads are skipped during per-tick sampling
|
|
779
|
+
// so their timestamps are not updated. record_sampling_overhead() would see the stale previous-sample timestamps and
|
|
780
|
+
// attribute the entire sleep interval incorrectly as overhead instead of just the sampling work.
|
|
781
|
+
if (!current_thread_context->is_profiler_internal_thread) {
|
|
782
|
+
record_sampling_overhead(state, current_thread_context);
|
|
783
|
+
}
|
|
646
784
|
}
|
|
647
785
|
|
|
648
786
|
static void update_metrics_and_sample(
|
|
649
787
|
thread_context_collector_state *state,
|
|
650
788
|
VALUE thread_being_sampled,
|
|
651
|
-
VALUE stack_from_thread, // This can be different when attributing profiler overhead using a different stack
|
|
652
789
|
per_thread_context *thread_context,
|
|
653
|
-
sampling_buffer* sampling_buffer,
|
|
654
790
|
long current_cpu_time_ns,
|
|
655
|
-
long current_monotonic_wall_time_ns
|
|
791
|
+
long current_monotonic_wall_time_ns,
|
|
792
|
+
bool force_sample
|
|
656
793
|
) {
|
|
657
794
|
bool is_gvl_waiting_state =
|
|
658
|
-
handle_gvl_waiting(state, thread_being_sampled,
|
|
795
|
+
handle_gvl_waiting(state, thread_being_sampled, thread_context, current_cpu_time_ns);
|
|
796
|
+
|
|
797
|
+
if (skip_sample(state, thread_context, is_gvl_waiting_state, force_sample)) return;
|
|
659
798
|
|
|
660
799
|
// Don't assign/update cpu during "Waiting for GVL"
|
|
661
|
-
long cpu_time_elapsed_ns = is_gvl_waiting_state ? 0 :
|
|
662
|
-
&thread_context->cpu_time_at_previous_sample_ns,
|
|
663
|
-
current_cpu_time_ns,
|
|
664
|
-
thread_context->gc_tracking.cpu_time_at_start_ns,
|
|
665
|
-
IS_NOT_WALL_TIME
|
|
666
|
-
);
|
|
800
|
+
long cpu_time_elapsed_ns = is_gvl_waiting_state ? 0 : update_cpu_time_since_previous_sample(thread_context, current_cpu_time_ns);
|
|
667
801
|
|
|
668
|
-
long wall_time_elapsed_ns =
|
|
669
|
-
&thread_context->wall_time_at_previous_sample_ns,
|
|
670
|
-
current_monotonic_wall_time_ns,
|
|
671
|
-
// We explicitly pass in `INVALID_TIME` as an argument for `gc_start_time_ns` here because we don't want wall-time
|
|
672
|
-
// accounting to change during GC.
|
|
673
|
-
// E.g. if 60 seconds pass in the real world, 60 seconds of wall-time are recorded, regardless of the thread doing
|
|
674
|
-
// GC or not.
|
|
675
|
-
INVALID_TIME,
|
|
676
|
-
IS_WALL_TIME
|
|
677
|
-
);
|
|
802
|
+
long wall_time_elapsed_ns = update_wall_time_since_previous_sample(thread_context, current_monotonic_wall_time_ns);
|
|
678
803
|
|
|
679
804
|
// A thread enters "Waiting for GVL", well, as the name implies, without the GVL.
|
|
680
805
|
//
|
|
681
806
|
// As a consequence, it's possible that a thread enters "Waiting for GVL" in parallel with the current thread working
|
|
682
|
-
// on sampling, and thus for the
|
|
807
|
+
// on sampling, and thus for the `current_monotonic_wall_time_ns` (which is recorded at the start of sampling)
|
|
683
808
|
// to be < the time at which we started Waiting for GVL.
|
|
684
809
|
//
|
|
685
810
|
// All together, this means that when `handle_gvl_waiting` creates an extra sample (see comments on that function for
|
|
@@ -694,9 +819,7 @@ static void update_metrics_and_sample(
|
|
|
694
819
|
trigger_sample_for_thread(
|
|
695
820
|
state,
|
|
696
821
|
thread_being_sampled,
|
|
697
|
-
stack_from_thread,
|
|
698
822
|
thread_context,
|
|
699
|
-
sampling_buffer,
|
|
700
823
|
(sample_values) {.cpu_time_ns = cpu_time_elapsed_ns, .cpu_or_wall_samples = 1, .wall_time_ns = wall_time_elapsed_ns},
|
|
701
824
|
current_monotonic_wall_time_ns,
|
|
702
825
|
NULL,
|
|
@@ -706,6 +829,37 @@ static void update_metrics_and_sample(
|
|
|
706
829
|
);
|
|
707
830
|
}
|
|
708
831
|
|
|
832
|
+
static bool skip_sample(thread_context_collector_state *state, per_thread_context *thread_context, bool is_gvl_waiting_state, bool force_sample) {
|
|
833
|
+
if (!force_sample && thread_context->is_profiler_internal_thread) {
|
|
834
|
+
state->stats.profiler_thread_samples_skipped++;
|
|
835
|
+
return true;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// Racy read but harmless, can only cause an extra sample
|
|
839
|
+
uint64_t gvl_state_change_count = thread_context->gvl_state_change_count;
|
|
840
|
+
|
|
841
|
+
// Skip this per-tick sample entirely when the thread does not have the GVL and did not acquire
|
|
842
|
+
// it since the previous sample: its Ruby-level stack has not changed. The skipped wall-time will
|
|
843
|
+
// be picked up by either by an extra sample when the thread acquires the GVL, or by
|
|
844
|
+
// the on-serialize flush in the stack recorder (using was_skipped_at_last_sample).
|
|
845
|
+
// The check is gated by `!is_gvl_waiting_state` so the existing "Waiting for GVL" machinery
|
|
846
|
+
// in handle_gvl_waiting (situation 1 extra sample, situation 2 regular sample) keeps running.
|
|
847
|
+
// TODO: we could probably also skip while "Waiting for GVL"
|
|
848
|
+
if (!is_gvl_waiting_state &&
|
|
849
|
+
!force_sample &&
|
|
850
|
+
(gvl_state_change_count & GVL_SUSPENDED) &&
|
|
851
|
+
gvl_state_change_count == thread_context->gvl_state_change_count_at_previous_sample) {
|
|
852
|
+
state->stats.inactive_thread_samples_skipped++;
|
|
853
|
+
thread_context->was_skipped_at_last_sample = true;
|
|
854
|
+
return true; // Do NOT update wall_time_at_previous_sample_ns or cpu_time_at_previous_sample_ns
|
|
855
|
+
} else {
|
|
856
|
+
// We are going to sample, update the state accordingly:
|
|
857
|
+
thread_context->gvl_state_change_count_at_previous_sample = gvl_state_change_count;
|
|
858
|
+
thread_context->was_skipped_at_last_sample = false;
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
709
863
|
// This function gets called when Ruby is about to start running the Garbage Collector on the current thread.
|
|
710
864
|
// It updates the per_thread_context of the current thread to include the current cpu/wall times, to be used to later
|
|
711
865
|
// create an event including the cpu/wall time spent in garbage collector work.
|
|
@@ -719,15 +873,13 @@ static void update_metrics_and_sample(
|
|
|
719
873
|
void thread_context_collector_on_gc_start(VALUE self_instance) {
|
|
720
874
|
thread_context_collector_state *state;
|
|
721
875
|
if (!rb_typeddata_is_kind_of(self_instance, &thread_context_collector_typed_data)) return;
|
|
722
|
-
// This should never fail
|
|
876
|
+
// This should never fail when the above check passes
|
|
723
877
|
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
724
878
|
|
|
725
|
-
per_thread_context *thread_context =
|
|
879
|
+
per_thread_context *thread_context = get_per_thread_context(rb_thread_current());
|
|
726
880
|
|
|
727
|
-
//
|
|
728
|
-
//
|
|
729
|
-
// GC" is a rare enough case that we can just ignore it.
|
|
730
|
-
// We can always improve this later if we find that this happens often (and we have the counter to help us figure that out)!
|
|
881
|
+
// Context is created eagerly via on_thread_begin_event, so this should not normally be NULL.
|
|
882
|
+
// We can't get_or_create_context_for() here since we can't allocate (GC context).
|
|
731
883
|
if (thread_context == NULL) {
|
|
732
884
|
state->stats.gc_samples_missed_due_to_missing_context++;
|
|
733
885
|
return;
|
|
@@ -752,13 +904,12 @@ __attribute__((warn_unused_result))
|
|
|
752
904
|
bool thread_context_collector_on_gc_finish(VALUE self_instance) {
|
|
753
905
|
thread_context_collector_state *state;
|
|
754
906
|
if (!rb_typeddata_is_kind_of(self_instance, &thread_context_collector_typed_data)) return false;
|
|
755
|
-
// This should never fail
|
|
907
|
+
// This should never fail when the above check passes
|
|
756
908
|
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
757
909
|
|
|
758
|
-
per_thread_context *thread_context =
|
|
910
|
+
per_thread_context *thread_context = get_per_thread_context(rb_thread_current());
|
|
759
911
|
|
|
760
|
-
//
|
|
761
|
-
// how often this happens -- see on_gc_start.
|
|
912
|
+
// Context is created eagerly, so this should not normally be NULL (see on_gc_start).
|
|
762
913
|
if (thread_context == NULL) return false;
|
|
763
914
|
|
|
764
915
|
long cpu_time_at_start_ns = thread_context->gc_tracking.cpu_time_at_start_ns;
|
|
@@ -799,9 +950,9 @@ bool thread_context_collector_on_gc_finish(VALUE self_instance) {
|
|
|
799
950
|
state->gc_tracking.accumulated_wall_time_ns += gc_wall_time_elapsed_ns;
|
|
800
951
|
state->gc_tracking.wall_time_at_previous_gc_ns = wall_time_at_finish_ns;
|
|
801
952
|
|
|
802
|
-
// Update cpu-time accounting so it doesn't include the cpu-time spent in GC during the next sample
|
|
803
|
-
// We don't
|
|
804
|
-
//
|
|
953
|
+
// Update cpu-time accounting so it doesn't include the cpu-time spent in GC during the next sample.
|
|
954
|
+
// We don't do the same for wall-time, because GC is just like any other reason a thread didn't make
|
|
955
|
+
// progress -- time always goes forward regardless of the thread making progress on what it wanted.
|
|
805
956
|
if (thread_context->cpu_time_at_previous_sample_ns != INVALID_TIME) {
|
|
806
957
|
thread_context->cpu_time_at_previous_sample_ns += gc_cpu_time_elapsed_ns;
|
|
807
958
|
}
|
|
@@ -842,11 +993,7 @@ VALUE thread_context_collector_sample_after_gc(VALUE self_instance) {
|
|
|
842
993
|
ddog_prof_Slice_Label slice_labels = {.ptr = labels, .len = label_pos};
|
|
843
994
|
|
|
844
995
|
// The end_timestamp_ns is treated specially by libdatadog and that's why it's not added as a ddog_prof_Label
|
|
845
|
-
int64_t end_timestamp_ns =
|
|
846
|
-
|
|
847
|
-
if (state->timeline_enabled) {
|
|
848
|
-
end_timestamp_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, state->gc_tracking.wall_time_at_previous_gc_ns);
|
|
849
|
-
}
|
|
996
|
+
int64_t end_timestamp_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, state->gc_tracking.wall_time_at_previous_gc_ns);
|
|
850
997
|
|
|
851
998
|
record_placeholder_stack(
|
|
852
999
|
state->recorder_instance,
|
|
@@ -879,10 +1026,8 @@ VALUE thread_context_collector_sample_after_gc(VALUE self_instance) {
|
|
|
879
1026
|
|
|
880
1027
|
static void trigger_sample_for_thread(
|
|
881
1028
|
thread_context_collector_state *state,
|
|
882
|
-
VALUE
|
|
883
|
-
VALUE stack_from_thread, // This can be different when attributing profiler overhead using a different stack
|
|
1029
|
+
VALUE thread_being_sampled,
|
|
884
1030
|
per_thread_context *thread_context,
|
|
885
|
-
sampling_buffer* sampling_buffer,
|
|
886
1031
|
sample_values values,
|
|
887
1032
|
long current_monotonic_wall_time_ns,
|
|
888
1033
|
// These two labels are only used for allocation profiling; @ivoanjo: may want to refactor this at some point?
|
|
@@ -896,7 +1041,6 @@ static void trigger_sample_for_thread(
|
|
|
896
1041
|
int max_label_count =
|
|
897
1042
|
1 + // thread id
|
|
898
1043
|
1 + // thread name
|
|
899
|
-
1 + // profiler overhead
|
|
900
1044
|
2 + // ruby vm type and allocation class
|
|
901
1045
|
1 + // state (only set for cpu/wall-time samples)
|
|
902
1046
|
2; // local root span id and span id
|
|
@@ -908,13 +1052,13 @@ static void trigger_sample_for_thread(
|
|
|
908
1052
|
.str = thread_context->thread_id_char_slice
|
|
909
1053
|
};
|
|
910
1054
|
|
|
911
|
-
VALUE thread_name = thread_name_for(
|
|
1055
|
+
VALUE thread_name = thread_name_for(thread_being_sampled);
|
|
912
1056
|
if (thread_name != Qnil) {
|
|
913
1057
|
labels[label_pos++] = (ddog_prof_Label) {
|
|
914
1058
|
.key = DDOG_CHARSLICE_C("thread name"),
|
|
915
1059
|
.str = char_slice_from_ruby_string(thread_name)
|
|
916
1060
|
};
|
|
917
|
-
} else if (
|
|
1061
|
+
} else if (thread_being_sampled == state->main_thread) { // Threads are often not named, but we can have a nice fallback for this special thread
|
|
918
1062
|
ddog_CharSlice main_thread_name = DDOG_CHARSLICE_C("main");
|
|
919
1063
|
labels[label_pos++] = (ddog_prof_Label) {
|
|
920
1064
|
.key = DDOG_CHARSLICE_C("thread name"),
|
|
@@ -930,11 +1074,11 @@ static void trigger_sample_for_thread(
|
|
|
930
1074
|
}
|
|
931
1075
|
|
|
932
1076
|
trace_identifiers trace_identifiers_result = {.valid = false, .trace_endpoint = Qnil};
|
|
933
|
-
trace_identifiers_for(state,
|
|
1077
|
+
trace_identifiers_for(state, thread_being_sampled, &trace_identifiers_result, is_safe_to_allocate_objects);
|
|
934
1078
|
|
|
935
1079
|
if (!trace_identifiers_result.valid && state->otel_context_enabled != OTEL_CONTEXT_ENABLED_FALSE) {
|
|
936
1080
|
// If we couldn't get something with ddtrace, let's see if we can get some trace identifiers from opentelemetry directly
|
|
937
|
-
otel_without_ddtrace_trace_identifiers_for(state,
|
|
1081
|
+
otel_without_ddtrace_trace_identifiers_for(state, thread_being_sampled, &trace_identifiers_result, is_safe_to_allocate_objects);
|
|
938
1082
|
}
|
|
939
1083
|
|
|
940
1084
|
if (trace_identifiers_result.valid) {
|
|
@@ -959,13 +1103,6 @@ static void trigger_sample_for_thread(
|
|
|
959
1103
|
}
|
|
960
1104
|
}
|
|
961
1105
|
|
|
962
|
-
if (thread != stack_from_thread) {
|
|
963
|
-
labels[label_pos++] = (ddog_prof_Label) {
|
|
964
|
-
.key = DDOG_CHARSLICE_C("profiler overhead"),
|
|
965
|
-
.num = 1
|
|
966
|
-
};
|
|
967
|
-
}
|
|
968
|
-
|
|
969
1106
|
if (ruby_vm_type != NULL) {
|
|
970
1107
|
labels[label_pos++] = (ddog_prof_Label) {
|
|
971
1108
|
.key = DDOG_CHARSLICE_C("ruby vm type"),
|
|
@@ -1006,13 +1143,14 @@ static void trigger_sample_for_thread(
|
|
|
1006
1143
|
|
|
1007
1144
|
// The end_timestamp_ns is treated specially by libdatadog and that's why it's not added as a ddog_prof_Label
|
|
1008
1145
|
int64_t end_timestamp_ns = 0;
|
|
1009
|
-
if (
|
|
1146
|
+
if (current_monotonic_wall_time_ns != INVALID_TIME) {
|
|
1010
1147
|
end_timestamp_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, current_monotonic_wall_time_ns);
|
|
1011
1148
|
}
|
|
1012
1149
|
|
|
1013
1150
|
sample_thread(
|
|
1014
|
-
|
|
1015
|
-
sampling_buffer,
|
|
1151
|
+
thread_being_sampled,
|
|
1152
|
+
&thread_context->sampling_buffer,
|
|
1153
|
+
state->locations,
|
|
1016
1154
|
state->recorder_instance,
|
|
1017
1155
|
values,
|
|
1018
1156
|
(sample_labels) {
|
|
@@ -1022,7 +1160,8 @@ static void trigger_sample_for_thread(
|
|
|
1022
1160
|
.is_gvl_waiting_state = is_gvl_waiting_state,
|
|
1023
1161
|
},
|
|
1024
1162
|
state->native_filenames_enabled,
|
|
1025
|
-
state->native_filenames_cache
|
|
1163
|
+
state->native_filenames_cache,
|
|
1164
|
+
state->show_classes
|
|
1026
1165
|
);
|
|
1027
1166
|
}
|
|
1028
1167
|
|
|
@@ -1040,32 +1179,38 @@ static VALUE _native_thread_list(DDTRACE_UNUSED VALUE _self) {
|
|
|
1040
1179
|
return result;
|
|
1041
1180
|
}
|
|
1042
1181
|
|
|
1043
|
-
static
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
if (st_lookup(state->hash_map_per_thread_context, (st_data_t) thread, &value_context)) {
|
|
1048
|
-
thread_context = (per_thread_context*) value_context;
|
|
1049
|
-
} else {
|
|
1050
|
-
thread_context = calloc(1, sizeof(per_thread_context)); // See "note on calloc vs ruby_xcalloc use" in heap_recorder.c
|
|
1051
|
-
initialize_context(thread, thread_context, state);
|
|
1052
|
-
st_insert(state->hash_map_per_thread_context, (st_data_t) thread, (st_data_t) thread_context);
|
|
1182
|
+
static void check_frozen_thread(VALUE thread) {
|
|
1183
|
+
if (RB_OBJ_FROZEN(thread)) {
|
|
1184
|
+
raise_error(rb_eFrozenError, "Cannot setup profiler state for Thread %"PRIsVALUE" because it is frozen. Please avoid freezing Thread instances and/or report the issue to dd-trace-rb", thread);
|
|
1053
1185
|
}
|
|
1054
|
-
|
|
1055
|
-
return thread_context;
|
|
1056
1186
|
}
|
|
1057
1187
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1188
|
+
// See the docs on struct per_thread_context.
|
|
1189
|
+
// This allocates a Ruby object and therefore needs the GVL and is not safe to call from RUBY_INTERNAL_EVENT_* hooks.
|
|
1190
|
+
static per_thread_context *get_or_create_context_for(VALUE thread) {
|
|
1191
|
+
per_thread_context *thread_context = get_per_thread_context(thread);
|
|
1192
|
+
if (thread_context != NULL) return thread_context;
|
|
1061
1193
|
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1194
|
+
check_frozen_thread(thread);
|
|
1195
|
+
|
|
1196
|
+
thread_context = calloc(1, sizeof(per_thread_context)); // See "note on calloc vs ruby_xcalloc use" in heap_recorder.c
|
|
1197
|
+
initialize_context(thread, thread_context);
|
|
1198
|
+
|
|
1199
|
+
VALUE wrapper = TypedData_Wrap_Struct(rb_cObject, &per_thread_context_typed_data, thread_context);
|
|
1200
|
+
rb_ivar_set(thread, dd_per_thread_context_id, wrapper);
|
|
1065
1201
|
|
|
1202
|
+
set_per_thread_context(thread, thread_context);
|
|
1066
1203
|
return thread_context;
|
|
1067
1204
|
}
|
|
1068
1205
|
|
|
1206
|
+
static void on_thread_begin_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused) {
|
|
1207
|
+
if (!ddtrace_rb_ractor_main_p()) return;
|
|
1208
|
+
|
|
1209
|
+
VALUE thread = rb_tracearg_self(rb_tracearg_from_tracepoint(tracepoint_data));
|
|
1210
|
+
ENFORCE_THREAD(thread);
|
|
1211
|
+
get_or_create_context_for(thread);
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1069
1214
|
#define LOGGING_GEM_PATH "/lib/logging/diagnostic_context.rb"
|
|
1070
1215
|
|
|
1071
1216
|
// The `logging` gem monkey patches thread creation, which makes the `invoke_location_for` useless, since every thread
|
|
@@ -1087,8 +1232,11 @@ static bool is_logging_gem_monkey_patch(VALUE invoke_file_location) {
|
|
|
1087
1232
|
return strncmp(invoke_file + invoke_file_len - logging_gem_path_len, LOGGING_GEM_PATH, logging_gem_path_len) == 0;
|
|
1088
1233
|
}
|
|
1089
1234
|
|
|
1090
|
-
static void initialize_context(VALUE thread, per_thread_context *thread_context
|
|
1091
|
-
|
|
1235
|
+
static void initialize_context(VALUE thread, per_thread_context *thread_context) {
|
|
1236
|
+
// We always create per_thread_context's with latest_max_frames; that value is kept in sync with the
|
|
1237
|
+
// active profiler's max_frames by the global reset that runs when profiling starts
|
|
1238
|
+
// so we expect to always see here the latest correct value to be used.
|
|
1239
|
+
sampling_buffer_initialize(&thread_context->sampling_buffer, latest_max_frames);
|
|
1092
1240
|
|
|
1093
1241
|
snprintf(thread_context->thread_id, THREAD_ID_LIMIT_CHARS, "%"PRIu64" (%lu)", native_thread_id_for(thread), (unsigned long) thread_id_for(thread));
|
|
1094
1242
|
thread_context->thread_id_char_slice = (ddog_CharSlice) {.ptr = thread_context->thread_id, .len = strlen(thread_context->thread_id)};
|
|
@@ -1107,7 +1255,7 @@ static void initialize_context(VALUE thread, per_thread_context *thread_context,
|
|
|
1107
1255
|
} else {
|
|
1108
1256
|
snprintf(thread_context->thread_invoke_location, THREAD_INVOKE_LOCATION_LIMIT_CHARS, "%s", "(Unnamed thread)");
|
|
1109
1257
|
}
|
|
1110
|
-
} else if (thread !=
|
|
1258
|
+
} else if (thread != rb_thread_main()) {
|
|
1111
1259
|
// If the first function of a thread is native code, there won't be an invoke location, so we use this fallback.
|
|
1112
1260
|
// NOTE: In the future, I wonder if we could take the pointer to the native function, and try to see if there's a native
|
|
1113
1261
|
// symbol attached to it.
|
|
@@ -1121,32 +1269,54 @@ static void initialize_context(VALUE thread, per_thread_context *thread_context,
|
|
|
1121
1269
|
|
|
1122
1270
|
thread_context->thread_cpu_time_id = thread_cpu_time_id_for(thread);
|
|
1123
1271
|
|
|
1124
|
-
|
|
1125
|
-
thread_context->cpu_time_at_previous_sample_ns =
|
|
1126
|
-
thread_context->wall_time_at_previous_sample_ns = INVALID_TIME;
|
|
1272
|
+
thread_context->wall_time_at_previous_sample_ns = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
1273
|
+
thread_context->cpu_time_at_previous_sample_ns = cpu_time_now_ns(thread_context);
|
|
1127
1274
|
|
|
1128
1275
|
// These will only be used during a GC operation
|
|
1129
1276
|
thread_context->gc_tracking.cpu_time_at_start_ns = INVALID_TIME;
|
|
1130
1277
|
thread_context->gc_tracking.wall_time_at_start_ns = INVALID_TIME;
|
|
1131
1278
|
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
// kind of synchronization (e.g. by threads without the GVL).
|
|
1135
|
-
//
|
|
1136
|
-
// We set this marker here for two purposes:
|
|
1137
|
-
// * To make sure there's no stale data from a previous execution of the profiler.
|
|
1138
|
-
// * To mark threads that are actually being profiled
|
|
1139
|
-
//
|
|
1140
|
-
// (Setting this is potentially a race, but what we want is to avoid _stale_ data, so
|
|
1141
|
-
// if this gets set concurrently with context initialization, then such a value will belong
|
|
1142
|
-
// to the current profiler instance, so that's OK)
|
|
1143
|
-
gvl_profiling_state_thread_object_set(thread, GVL_WAITING_ENABLED_EMPTY);
|
|
1144
|
-
#endif
|
|
1279
|
+
thread_context->gvl_waiting_at = 0;
|
|
1280
|
+
thread_context->gvl_state_change_count = 0;
|
|
1145
1281
|
}
|
|
1146
1282
|
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1283
|
+
// This MUST be called before profiling starts, so that a new profiler session starts from a fresh state and never
|
|
1284
|
+
// observes or includes any leftover stale state from a previous session.
|
|
1285
|
+
// Such a call MUST happen while the CpuAndWallTimeWorker is stopped (e.g. no tracepoints active, no signals
|
|
1286
|
+
// triggering samples, no gvl hooks, etc).
|
|
1287
|
+
//
|
|
1288
|
+
// It updates the global `latest_max_frames` from the given (latest) ThreadContext and (re)creates every per-thread
|
|
1289
|
+
// context's sampling buffer sized accordingly, so the buffers always match the collector that's about to start
|
|
1290
|
+
// sampling -- even if a previous session used a different max_frames.
|
|
1291
|
+
void thread_context_collector_reset_all_per_thread_contexts(VALUE self_instance) {
|
|
1292
|
+
thread_context_collector_state *state;
|
|
1293
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1294
|
+
|
|
1295
|
+
// Update global max frames to be used when allocating sampling buffers
|
|
1296
|
+
if (state->locations.len == 0) {
|
|
1297
|
+
raise_error(rb_eRuntimeError, "BUG: Unexpected locations.len == 0. Is this ThreadContext not initialized?");
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
latest_max_frames = state->locations.len;
|
|
1301
|
+
|
|
1302
|
+
VALUE threads = thread_list(state);
|
|
1303
|
+
const long thread_count = RARRAY_LEN(threads);
|
|
1304
|
+
for (long i = 0; i < thread_count; i++) {
|
|
1305
|
+
VALUE thread = rb_ary_entry(threads, i);
|
|
1306
|
+
per_thread_context *thread_context = get_per_thread_context(thread);
|
|
1307
|
+
if (thread_context != NULL) {
|
|
1308
|
+
bool is_profiler_internal_thread = thread_context->is_profiler_internal_thread;
|
|
1309
|
+
|
|
1310
|
+
sampling_buffer_free(&thread_context->sampling_buffer);
|
|
1311
|
+
memset(thread_context, 0, sizeof(per_thread_context));
|
|
1312
|
+
initialize_context(thread, thread_context);
|
|
1313
|
+
|
|
1314
|
+
thread_context->is_profiler_internal_thread = is_profiler_internal_thread;
|
|
1315
|
+
} else {
|
|
1316
|
+
// If thread didn't have a context, let's trigger its creation
|
|
1317
|
+
get_or_create_context_for(thread);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1150
1320
|
}
|
|
1151
1321
|
|
|
1152
1322
|
static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
|
@@ -1156,16 +1326,14 @@ static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instanc
|
|
|
1156
1326
|
VALUE result = rb_str_new2(" (native state)");
|
|
1157
1327
|
|
|
1158
1328
|
// Update this when modifying state struct
|
|
1159
|
-
rb_str_concat(result, rb_sprintf(" max_frames=%d", state->
|
|
1160
|
-
rb_str_concat(result, rb_sprintf(" hash_map_per_thread_context=%"PRIsVALUE, per_thread_context_st_table_as_ruby_hash(state)));
|
|
1329
|
+
rb_str_concat(result, rb_sprintf(" max_frames=%d", state->locations.len));
|
|
1161
1330
|
rb_str_concat(result, rb_sprintf(" recorder_instance=%"PRIsVALUE, state->recorder_instance));
|
|
1162
1331
|
VALUE tracer_context_key = state->tracer_context_key == MISSING_TRACER_CONTEXT_KEY ? Qnil : ID2SYM(state->tracer_context_key);
|
|
1163
1332
|
rb_str_concat(result, rb_sprintf(" tracer_context_key=%+"PRIsVALUE, tracer_context_key));
|
|
1164
|
-
rb_str_concat(result, rb_sprintf("
|
|
1165
|
-
rb_str_concat(result, rb_sprintf(" stats=%"PRIsVALUE, stats_as_ruby_hash(state)));
|
|
1333
|
+
rb_str_concat(result, rb_sprintf(" stats=%"PRIsVALUE, stats_to_ruby_hash(state, rb_hash_new())));
|
|
1166
1334
|
rb_str_concat(result, rb_sprintf(" endpoint_collection_enabled=%"PRIsVALUE, state->endpoint_collection_enabled ? Qtrue : Qfalse));
|
|
1167
|
-
rb_str_concat(result, rb_sprintf(" timeline_enabled=%"PRIsVALUE, state->timeline_enabled ? Qtrue : Qfalse));
|
|
1168
1335
|
rb_str_concat(result, rb_sprintf(" native_filenames_enabled=%"PRIsVALUE, state->native_filenames_enabled ? Qtrue : Qfalse));
|
|
1336
|
+
rb_str_concat(result, rb_sprintf(" show_classes=%"PRIsVALUE, state->show_classes ? Qtrue : Qfalse));
|
|
1169
1337
|
// Note: `st_table_size()` is available from Ruby 3.2+ but not before
|
|
1170
1338
|
rb_str_concat(result, rb_sprintf(" native_filenames_cache_size=%zu", state->native_filenames_cache->num_entries));
|
|
1171
1339
|
rb_str_concat(result, rb_sprintf(" otel_context_enabled=%d", state->otel_context_enabled));
|
|
@@ -1177,53 +1345,51 @@ static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instanc
|
|
|
1177
1345
|
rb_str_concat(result, rb_sprintf(" main_thread=%"PRIsVALUE, state->main_thread));
|
|
1178
1346
|
rb_str_concat(result, rb_sprintf(" gc_tracking=%"PRIsVALUE, gc_tracking_as_ruby_hash(state)));
|
|
1179
1347
|
rb_str_concat(result, rb_sprintf(" otel_current_span_key=%"PRIsVALUE, state->otel_current_span_key));
|
|
1180
|
-
rb_str_concat(result, rb_sprintf("
|
|
1348
|
+
rb_str_concat(result, rb_sprintf(" waiting_for_gvl_threshold_ns=%u", state->waiting_for_gvl_threshold_ns));
|
|
1181
1349
|
|
|
1182
1350
|
return result;
|
|
1183
1351
|
}
|
|
1184
1352
|
|
|
1185
|
-
static VALUE
|
|
1186
|
-
VALUE result = rb_hash_new();
|
|
1187
|
-
st_foreach(state->hash_map_per_thread_context, per_thread_context_as_ruby_hash, result);
|
|
1188
|
-
return result;
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
static int per_thread_context_as_ruby_hash(st_data_t key_thread, st_data_t value_context, st_data_t result_hash) {
|
|
1192
|
-
VALUE thread = (VALUE) key_thread;
|
|
1193
|
-
per_thread_context *thread_context = (per_thread_context*) value_context;
|
|
1194
|
-
VALUE result = (VALUE) result_hash;
|
|
1353
|
+
static VALUE per_thread_context_to_ruby_hash(per_thread_context *thread_context) {
|
|
1195
1354
|
VALUE context_as_hash = rb_hash_new();
|
|
1196
|
-
rb_hash_aset(result, thread, context_as_hash);
|
|
1197
1355
|
|
|
1198
1356
|
VALUE arguments[] = {
|
|
1199
1357
|
ID2SYM(rb_intern("thread_id")), /* => */ rb_str_new2(thread_context->thread_id),
|
|
1200
1358
|
ID2SYM(rb_intern("thread_invoke_location")), /* => */ rb_str_new2(thread_context->thread_invoke_location),
|
|
1201
1359
|
ID2SYM(rb_intern("thread_cpu_time_id_valid?")), /* => */ thread_context->thread_cpu_time_id.valid ? Qtrue : Qfalse,
|
|
1202
|
-
|
|
1360
|
+
#ifdef __APPLE__
|
|
1361
|
+
ID2SYM(rb_intern("thread_cpu_time_id")), /* => */ ULL2NUM(thread_context->thread_cpu_time_id.clock_id),
|
|
1362
|
+
#else
|
|
1363
|
+
ID2SYM(rb_intern("thread_cpu_time_id")), /* => */ CLOCKID2NUM(thread_context->thread_cpu_time_id.clock_id),
|
|
1364
|
+
#endif
|
|
1203
1365
|
ID2SYM(rb_intern("cpu_time_at_previous_sample_ns")), /* => */ LONG2NUM(thread_context->cpu_time_at_previous_sample_ns),
|
|
1204
1366
|
ID2SYM(rb_intern("wall_time_at_previous_sample_ns")), /* => */ LONG2NUM(thread_context->wall_time_at_previous_sample_ns),
|
|
1205
1367
|
|
|
1206
1368
|
ID2SYM(rb_intern("gc_tracking.cpu_time_at_start_ns")), /* => */ LONG2NUM(thread_context->gc_tracking.cpu_time_at_start_ns),
|
|
1207
1369
|
ID2SYM(rb_intern("gc_tracking.wall_time_at_start_ns")), /* => */ LONG2NUM(thread_context->gc_tracking.wall_time_at_start_ns),
|
|
1208
1370
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1371
|
+
ID2SYM(rb_intern("gvl_waiting_at")), /* => */ LONG2NUM(thread_context->gvl_waiting_at),
|
|
1372
|
+
ID2SYM(rb_intern("gvl_state_change_count")), /* => */ ULL2NUM(thread_context->gvl_state_change_count),
|
|
1373
|
+
ID2SYM(rb_intern("gvl_state_change_count_at_previous_sample")), /* => */ ULL2NUM(thread_context->gvl_state_change_count_at_previous_sample),
|
|
1374
|
+
ID2SYM(rb_intern("was_skipped_at_last_sample")), /* => */ thread_context->was_skipped_at_last_sample ? Qtrue : Qfalse,
|
|
1375
|
+
ID2SYM(rb_intern("is_profiler_internal_thread")), /* => */ thread_context->is_profiler_internal_thread ? Qtrue : Qfalse,
|
|
1212
1376
|
};
|
|
1213
1377
|
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(context_as_hash, arguments[i], arguments[i+1]);
|
|
1214
1378
|
|
|
1215
|
-
return
|
|
1379
|
+
return context_as_hash;
|
|
1216
1380
|
}
|
|
1217
1381
|
|
|
1218
|
-
static VALUE
|
|
1382
|
+
static VALUE stats_to_ruby_hash(thread_context_collector_state *state, VALUE hash) {
|
|
1219
1383
|
// Update this when modifying state struct (stats inner struct)
|
|
1220
|
-
VALUE stats_as_hash = rb_hash_new();
|
|
1221
1384
|
VALUE arguments[] = {
|
|
1385
|
+
ID2SYM(rb_intern("sample_count")), /* => */ UINT2NUM(state->stats.sample_count),
|
|
1222
1386
|
ID2SYM(rb_intern("gc_samples")), /* => */ UINT2NUM(state->stats.gc_samples),
|
|
1223
1387
|
ID2SYM(rb_intern("gc_samples_missed_due_to_missing_context")), /* => */ UINT2NUM(state->stats.gc_samples_missed_due_to_missing_context),
|
|
1388
|
+
ID2SYM(rb_intern("inactive_thread_samples_skipped")), /* => */ UINT2NUM(state->stats.inactive_thread_samples_skipped),
|
|
1389
|
+
ID2SYM(rb_intern("profiler_thread_samples_skipped")), /* => */ UINT2NUM(state->stats.profiler_thread_samples_skipped),
|
|
1224
1390
|
};
|
|
1225
|
-
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(
|
|
1226
|
-
return
|
|
1391
|
+
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(hash, arguments[i], arguments[i+1]);
|
|
1392
|
+
return hash;
|
|
1227
1393
|
}
|
|
1228
1394
|
|
|
1229
1395
|
static VALUE gc_tracking_as_ruby_hash(thread_context_collector_state *state) {
|
|
@@ -1239,70 +1405,80 @@ static VALUE gc_tracking_as_ruby_hash(thread_context_collector_state *state) {
|
|
|
1239
1405
|
return result;
|
|
1240
1406
|
}
|
|
1241
1407
|
|
|
1242
|
-
static void remove_context_for_dead_threads(thread_context_collector_state *state) {
|
|
1243
|
-
st_foreach(state->hash_map_per_thread_context, remove_if_dead_thread, 0 /* unused */);
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
static int remove_if_dead_thread(st_data_t key_thread, st_data_t value_context, DDTRACE_UNUSED st_data_t _argument) {
|
|
1247
|
-
VALUE thread = (VALUE) key_thread;
|
|
1248
|
-
per_thread_context* thread_context = (per_thread_context*) value_context;
|
|
1249
|
-
|
|
1250
|
-
if (is_thread_alive(thread)) return ST_CONTINUE;
|
|
1251
|
-
|
|
1252
|
-
free_context(thread_context);
|
|
1253
|
-
return ST_DELETE;
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
1408
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
|
1257
1409
|
// It SHOULD NOT be used for other purposes.
|
|
1258
1410
|
//
|
|
1259
|
-
// Returns the whole contents of the per_thread_context structs being tracked.
|
|
1411
|
+
// Returns the whole contents of the per_thread_context structs being tracked, by iterating all live threads.
|
|
1260
1412
|
static VALUE _native_per_thread_context(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
|
1261
1413
|
thread_context_collector_state *state;
|
|
1262
1414
|
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1263
1415
|
|
|
1264
|
-
|
|
1416
|
+
VALUE result = rb_hash_new();
|
|
1417
|
+
VALUE threads = thread_list(state);
|
|
1418
|
+
const long thread_count = RARRAY_LEN(threads);
|
|
1419
|
+
for (long i = 0; i < thread_count; i++) {
|
|
1420
|
+
VALUE thread = RARRAY_AREF(threads, i);
|
|
1421
|
+
per_thread_context *thread_context = get_per_thread_context(thread);
|
|
1422
|
+
if (thread_context != NULL) {
|
|
1423
|
+
rb_hash_aset(result, thread, per_thread_context_to_ruby_hash(thread_context));
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
return result;
|
|
1265
1427
|
}
|
|
1266
1428
|
|
|
1267
|
-
static long update_time_since_previous_sample(long *time_at_previous_sample_ns, long current_time_ns,
|
|
1429
|
+
static long update_time_since_previous_sample(long *time_at_previous_sample_ns, long current_time_ns, per_thread_context *thread_context) {
|
|
1268
1430
|
// If we didn't have a time for the previous sample, we use the current one
|
|
1269
1431
|
if (*time_at_previous_sample_ns == INVALID_TIME) *time_at_previous_sample_ns = current_time_ns;
|
|
1270
1432
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1433
|
+
// We don't expect to be sampling a thread (and thus updating these counters) while Ruby is doing GC (between
|
|
1434
|
+
// `thread_context_collector_on_gc_start` and `thread_context_collector_on_gc_finish`)
|
|
1435
|
+
if (thread_context->gc_tracking.cpu_time_at_start_ns != INVALID_TIME) {
|
|
1436
|
+
raise_error(
|
|
1437
|
+
rb_eRuntimeError,
|
|
1438
|
+
"BUG: Unexpected sample during GC (thread_id=%s, gc_tracking.cpu_time_at_start_ns=%ld, "
|
|
1439
|
+
"gc_tracking.wall_time_at_start_ns=%ld, monotonic_wall_time_now_ns=%ld)",
|
|
1440
|
+
thread_context->thread_id,
|
|
1441
|
+
thread_context->gc_tracking.cpu_time_at_start_ns,
|
|
1442
|
+
thread_context->gc_tracking.wall_time_at_start_ns,
|
|
1443
|
+
monotonic_wall_time_now_ns(RAISE_ON_FAILURE)
|
|
1444
|
+
);
|
|
1445
|
+
}
|
|
1273
1446
|
|
|
1274
|
-
|
|
1275
|
-
|
|
1447
|
+
long elapsed_time_ns = current_time_ns - *time_at_previous_sample_ns; // Capture all time since previous sample
|
|
1448
|
+
*time_at_previous_sample_ns = current_time_ns;
|
|
1276
1449
|
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
} else {
|
|
1280
|
-
elapsed_time_ns = gc_start_time_ns - *time_at_previous_sample_ns; // Capture time between previous sample and start of GC only
|
|
1281
|
-
}
|
|
1450
|
+
return elapsed_time_ns;
|
|
1451
|
+
}
|
|
1282
1452
|
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1453
|
+
static long update_cpu_time_since_previous_sample(per_thread_context *thread_context, long current_cpu_time_ns) {
|
|
1454
|
+
long elapsed_time_ns = update_time_since_previous_sample(
|
|
1455
|
+
&thread_context->cpu_time_at_previous_sample_ns,
|
|
1456
|
+
current_cpu_time_ns,
|
|
1457
|
+
thread_context
|
|
1458
|
+
);
|
|
1289
1459
|
|
|
1460
|
+
// We don't expect cpu-time to go backwards, so let's flag this as a bug
|
|
1290
1461
|
if (elapsed_time_ns < 0) {
|
|
1291
|
-
|
|
1292
|
-
// Wall-time can actually go backwards (e.g. when the system clock gets set) so we can't assume time going backwards
|
|
1293
|
-
// was a bug.
|
|
1294
|
-
// @ivoanjo: I've also observed time going backwards spuriously on macOS, see discussion on
|
|
1295
|
-
// https://github.com/DataDog/dd-trace-rb/pull/2336.
|
|
1296
|
-
elapsed_time_ns = 0;
|
|
1297
|
-
} else {
|
|
1298
|
-
// We don't expect non-wall time to go backwards, so let's flag this as a bug
|
|
1299
|
-
raise_error(rb_eRuntimeError, "BUG: Unexpected negative elapsed_time_ns between samples");
|
|
1300
|
-
}
|
|
1462
|
+
raise_error(rb_eRuntimeError, "BUG: Unexpected CPU time going backwards between samples");
|
|
1301
1463
|
}
|
|
1302
1464
|
|
|
1303
1465
|
return elapsed_time_ns;
|
|
1304
1466
|
}
|
|
1305
1467
|
|
|
1468
|
+
static long update_wall_time_since_previous_sample(per_thread_context *thread_context, long current_wall_time_ns) {
|
|
1469
|
+
long elapsed_time_ns = update_time_since_previous_sample(
|
|
1470
|
+
&thread_context->wall_time_at_previous_sample_ns,
|
|
1471
|
+
current_wall_time_ns,
|
|
1472
|
+
thread_context
|
|
1473
|
+
);
|
|
1474
|
+
|
|
1475
|
+
// Wall-time can actually go backwards (e.g. when the system clock gets set) so we can't assume time going backwards
|
|
1476
|
+
// was a bug.
|
|
1477
|
+
// @ivoanjo: I've also observed time going backwards spuriously on macOS, see discussion on
|
|
1478
|
+
// https://github.com/DataDog/dd-trace-rb/pull/2336.
|
|
1479
|
+
return long_max_of(elapsed_time_ns, 0);
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1306
1482
|
// Safety: This function is assumed never to raise exceptions by callers
|
|
1307
1483
|
static long cpu_time_now_ns(per_thread_context *thread_context) {
|
|
1308
1484
|
thread_cpu_time cpu_time = thread_cpu_time_for(thread_context->thread_cpu_time_id);
|
|
@@ -1339,13 +1515,23 @@ VALUE enforce_thread_context_collector_instance(VALUE object) {
|
|
|
1339
1515
|
return object;
|
|
1340
1516
|
}
|
|
1341
1517
|
|
|
1518
|
+
// Finalize any pending heap allocation recordings.
|
|
1519
|
+
// On Ruby 4+, heap allocations are recorded in two phases: during on_newobj_event we capture
|
|
1520
|
+
// the object reference, then later we safely call rb_obj_id() to get the object ID.
|
|
1521
|
+
void thread_context_collector_after_allocation(VALUE self_instance) {
|
|
1522
|
+
thread_context_collector_state *state;
|
|
1523
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1524
|
+
|
|
1525
|
+
recorder_after_sample(state->recorder_instance);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1342
1528
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
|
1343
1529
|
// It SHOULD NOT be used for other purposes.
|
|
1344
1530
|
static VALUE _native_stats(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
|
1345
1531
|
thread_context_collector_state *state;
|
|
1346
1532
|
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1347
1533
|
|
|
1348
|
-
return
|
|
1534
|
+
return stats_to_ruby_hash(state, rb_hash_new());
|
|
1349
1535
|
}
|
|
1350
1536
|
|
|
1351
1537
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
|
@@ -1440,17 +1626,18 @@ static bool should_collect_resource(VALUE root_span) {
|
|
|
1440
1626
|
//
|
|
1441
1627
|
// Assumption: This method gets called BEFORE restarting profiling -- e.g. there are no components attempting to
|
|
1442
1628
|
// trigger samples at the same time.
|
|
1629
|
+
//
|
|
1630
|
+
// Note that tests call this method directly in the same process without forking,
|
|
1631
|
+
// and in such a case non-current Threads keep running.
|
|
1443
1632
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
|
|
1444
1633
|
thread_context_collector_state *state;
|
|
1445
1634
|
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1446
1635
|
|
|
1447
|
-
// Release all context memory before clearing the existing context
|
|
1448
|
-
st_foreach(state->hash_map_per_thread_context, hash_map_per_thread_context_free_values, 0 /* unused */);
|
|
1449
|
-
|
|
1450
|
-
st_clear(state->hash_map_per_thread_context);
|
|
1451
|
-
|
|
1452
1636
|
state->stats = (struct stats) {}; // Resets all stats back to zero
|
|
1453
1637
|
|
|
1638
|
+
// No need to clean-up the per-thread context because the CpuAndWallTimeWorker always cleans
|
|
1639
|
+
// it up unconditionally on every start/restart and that includes after a fork.
|
|
1640
|
+
|
|
1454
1641
|
rb_funcall(state->recorder_instance, rb_intern("reset_after_fork"), 0);
|
|
1455
1642
|
|
|
1456
1643
|
return Qtrue;
|
|
@@ -1470,25 +1657,25 @@ static VALUE thread_list(thread_context_collector_state *state) {
|
|
|
1470
1657
|
// expected to be called from a signal handler and to be async-signal-safe.
|
|
1471
1658
|
//
|
|
1472
1659
|
// Also, no allocation (Ruby or malloc) can happen.
|
|
1473
|
-
bool thread_context_collector_prepare_sample_inside_signal_handler(
|
|
1474
|
-
thread_context_collector_state *state;
|
|
1475
|
-
if (!rb_typeddata_is_kind_of(self_instance, &thread_context_collector_typed_data)) return false;
|
|
1476
|
-
// This should never fail if the above check passes
|
|
1477
|
-
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1478
|
-
|
|
1660
|
+
bool thread_context_collector_prepare_sample_inside_signal_handler(void) {
|
|
1479
1661
|
VALUE current_thread = rb_thread_current();
|
|
1480
|
-
per_thread_context *thread_context =
|
|
1662
|
+
per_thread_context *thread_context = get_per_thread_context(current_thread);
|
|
1481
1663
|
if (thread_context == NULL) return false;
|
|
1482
1664
|
|
|
1483
1665
|
return prepare_sample_thread(current_thread, &thread_context->sampling_buffer);
|
|
1484
1666
|
}
|
|
1485
1667
|
|
|
1486
|
-
|
|
1668
|
+
// This method gets called from inside the RUBY_INTERNAL_EVENT_NEWOBJ tracepoint so it should never allocate in the
|
|
1669
|
+
// Ruby heap.
|
|
1670
|
+
//
|
|
1671
|
+
// Returns true if the after_allocation needs to be called (to do work that can't be done from inside the
|
|
1672
|
+
// tracepoint, such as allocate new objects), and false if it doesn't
|
|
1673
|
+
//
|
|
1674
|
+
// The callers must ensure thread_context is non-NULL.
|
|
1675
|
+
bool thread_context_collector_sample_allocation(VALUE self_instance, per_thread_context *thread_context, unsigned int sample_weight, VALUE new_object) {
|
|
1487
1676
|
thread_context_collector_state *state;
|
|
1488
1677
|
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1489
1678
|
|
|
1490
|
-
VALUE current_thread = rb_thread_current();
|
|
1491
|
-
|
|
1492
1679
|
enum ruby_value_type type = rb_type(new_object);
|
|
1493
1680
|
|
|
1494
1681
|
// Tag samples with the VM internal types
|
|
@@ -1553,38 +1740,48 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
|
1553
1740
|
class_name = ruby_vm_type; // For other weird internal things we just use the VM type
|
|
1554
1741
|
}
|
|
1555
1742
|
|
|
1556
|
-
track_object(state->recorder_instance, new_object, sample_weight, class_name);
|
|
1743
|
+
bool needs_after_allocation = track_object(state->recorder_instance, new_object, sample_weight, class_name);
|
|
1557
1744
|
|
|
1558
|
-
|
|
1745
|
+
VALUE current_thread = rb_thread_current();
|
|
1559
1746
|
|
|
1560
1747
|
trigger_sample_for_thread(
|
|
1561
1748
|
state,
|
|
1562
|
-
|
|
1563
|
-
/* stack_from_thread: */ current_thread,
|
|
1749
|
+
current_thread,
|
|
1564
1750
|
thread_context,
|
|
1565
|
-
&thread_context->sampling_buffer,
|
|
1566
1751
|
(sample_values) {.alloc_samples = sample_weight, .alloc_samples_unscaled = 1, .heap_sample = true},
|
|
1567
1752
|
INVALID_TIME, // For now we're not collecting timestamps for allocation events, as per profiling team internal discussions
|
|
1568
1753
|
&ruby_vm_type,
|
|
1569
|
-
|
|
1754
|
+
&class_name,
|
|
1570
1755
|
/* is_gvl_waiting_state: */ false,
|
|
1571
1756
|
/* is_safe_to_allocate_objects: */ false // Not safe to allocate further inside the NEWOBJ tracepoint
|
|
1572
1757
|
);
|
|
1758
|
+
|
|
1759
|
+
return needs_after_allocation;
|
|
1573
1760
|
}
|
|
1574
1761
|
|
|
1575
1762
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
|
1576
1763
|
// It SHOULD NOT be used for other purposes.
|
|
1577
1764
|
static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object) {
|
|
1765
|
+
per_thread_context *thread_context = get_per_thread_context(rb_thread_current());
|
|
1766
|
+
if (thread_context == NULL) {
|
|
1767
|
+
rb_raise(rb_eRuntimeError, "Missing per_thread_context for current thread in _native_sample_allocation");
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1578
1770
|
debug_enter_unsafe_context();
|
|
1579
1771
|
|
|
1580
|
-
thread_context_collector_sample_allocation(collector_instance, NUM2UINT(sample_weight), new_object);
|
|
1772
|
+
bool needs_after_allocation = thread_context_collector_sample_allocation(collector_instance, thread_context, NUM2UINT(sample_weight), new_object);
|
|
1581
1773
|
|
|
1582
1774
|
debug_leave_unsafe_context();
|
|
1583
1775
|
|
|
1584
|
-
|
|
1776
|
+
// We could instead choose to automatically trigger the after allocation here; yet, it seems kinda nice to keep it manual for
|
|
1777
|
+
// the tests so we can pull on each lever separately and observe "the sausage being made" in steps
|
|
1778
|
+
return needs_after_allocation ? Qtrue : Qfalse;
|
|
1585
1779
|
}
|
|
1586
1780
|
|
|
1587
|
-
static VALUE new_empty_thread_inner(DDTRACE_UNUSED void *arg) {
|
|
1781
|
+
static VALUE new_empty_thread_inner(DDTRACE_UNUSED void *arg) {
|
|
1782
|
+
rb_thread_sleep(INT_MAX);
|
|
1783
|
+
return Qnil;
|
|
1784
|
+
}
|
|
1588
1785
|
|
|
1589
1786
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
|
1590
1787
|
// It SHOULD NOT be used for other purposes.
|
|
@@ -1799,7 +1996,7 @@ static void otel_without_ddtrace_trace_identifiers_for(
|
|
|
1799
1996
|
VALUE otel_current_span_key = get_otel_current_span_key(state, is_safe_to_allocate_objects);
|
|
1800
1997
|
if (otel_current_span_key == Qnil) return;
|
|
1801
1998
|
|
|
1802
|
-
|
|
1999
|
+
long active_context_index = RARRAY_LEN(context_storage) - 1;
|
|
1803
2000
|
if (active_context_index < 0) return;
|
|
1804
2001
|
|
|
1805
2002
|
otel_span active_span = otel_span_from(rb_ary_entry(context_storage, active_context_index), otel_current_span_key);
|
|
@@ -1808,7 +2005,7 @@ static void otel_without_ddtrace_trace_identifiers_for(
|
|
|
1808
2005
|
otel_span local_root_span = active_span;
|
|
1809
2006
|
|
|
1810
2007
|
// Now find the oldest span starting from the active span that still has the same trace id as the active span
|
|
1811
|
-
for (
|
|
2008
|
+
for (long i = active_context_index - 1; i >= 0; i--) {
|
|
1812
2009
|
otel_span checking_span = otel_span_from(rb_ary_entry(context_storage, i), otel_current_span_key);
|
|
1813
2010
|
if (checking_span.span == Qnil) return;
|
|
1814
2011
|
|
|
@@ -1877,57 +2074,179 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
|
1877
2074
|
((uint64_t)span_bytes[7]);
|
|
1878
2075
|
}
|
|
1879
2076
|
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
intptr_t thread_being_profiled = gvl_profiling_state_get(thread);
|
|
1892
|
-
if (!thread_being_profiled) return;
|
|
2077
|
+
void thread_context_collector_stats(VALUE self_instance, VALUE stats_hash) {
|
|
2078
|
+
thread_context_collector_state *state;
|
|
2079
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
2080
|
+
stats_to_ruby_hash(state, stats_hash);
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
void thread_context_collector_stats_reset_not_thread_safe(VALUE self_instance) {
|
|
2084
|
+
thread_context_collector_state *state;
|
|
2085
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
2086
|
+
state->stats = (struct stats) {};
|
|
2087
|
+
}
|
|
1893
2088
|
|
|
1894
|
-
|
|
1895
|
-
|
|
2089
|
+
static void mark_thread_as_profiler_internal(per_thread_context *ctx) {
|
|
2090
|
+
ctx->is_profiler_internal_thread = true;
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
void thread_context_collector_profiler_internal_thread_started(void) {
|
|
2094
|
+
per_thread_context *ctx = get_or_create_context_for(rb_thread_current());
|
|
2095
|
+
mark_thread_as_profiler_internal(ctx);
|
|
2096
|
+
}
|
|
1896
2097
|
|
|
1897
|
-
|
|
2098
|
+
static VALUE _native_mark_thread_as_profiler_internal(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
|
2099
|
+
per_thread_context *ctx = get_or_create_context_for(thread);
|
|
2100
|
+
mark_thread_as_profiler_internal(ctx);
|
|
2101
|
+
return Qnil;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
// Called via rb_ensure when a profiler-internal thread (worker or idle helper) is about to exit.
|
|
2105
|
+
// Records a final sample so the thread's accumulated cpu/wall time since the last on_serialize
|
|
2106
|
+
// flush is not lost. on_serialize (below) also flushes profiler-internal threads during periodic
|
|
2107
|
+
// serialization, but it can't help at shutdown: by the time the final serialize runs, these
|
|
2108
|
+
// threads are already dead and absent from thread_list.
|
|
2109
|
+
void thread_context_collector_profiler_internal_thread_done(VALUE self_instance) {
|
|
2110
|
+
thread_context_collector_state *state;
|
|
2111
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
2112
|
+
|
|
2113
|
+
VALUE current_thread = rb_thread_current();
|
|
2114
|
+
per_thread_context *thread_context = get_or_create_context_for(current_thread);
|
|
2115
|
+
if (!thread_context->is_profiler_internal_thread) {
|
|
2116
|
+
rb_raise(rb_eRuntimeError, "current thread %"PRIsVALUE" is not profiler-internal thread", current_thread);
|
|
1898
2117
|
}
|
|
1899
2118
|
|
|
1900
|
-
|
|
2119
|
+
long current_cpu_time_ns = cpu_time_now_ns(thread_context);
|
|
2120
|
+
long current_monotonic_wall_time_ns = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
2121
|
+
|
|
2122
|
+
update_metrics_and_sample(
|
|
2123
|
+
state,
|
|
2124
|
+
current_thread,
|
|
2125
|
+
thread_context,
|
|
2126
|
+
current_cpu_time_ns,
|
|
2127
|
+
current_monotonic_wall_time_ns,
|
|
2128
|
+
true);
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
// Flushes threads whose last per-tick sample was skipped (either by the SUSPENDED-skip
|
|
2132
|
+
// optimization, or by is_profiler_internal_thread) so their accumulated time is recorded.
|
|
2133
|
+
// Called by the stack recorder at the start of _native_serialize (regular periodic flush).
|
|
2134
|
+
void thread_context_collector_on_serialize(VALUE self_instance) {
|
|
2135
|
+
thread_context_collector_state *state;
|
|
2136
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
2137
|
+
|
|
2138
|
+
long current_monotonic_wall_time_ns = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
2139
|
+
VALUE threads = thread_list(state);
|
|
2140
|
+
const long thread_count = RARRAY_LEN(threads);
|
|
2141
|
+
|
|
2142
|
+
for (long i = 0; i < thread_count; i++) {
|
|
2143
|
+
VALUE thread = RARRAY_AREF(threads, i);
|
|
2144
|
+
per_thread_context *thread_context = get_per_thread_context(thread);
|
|
2145
|
+
|
|
2146
|
+
if (thread_context != NULL && (thread_context->was_skipped_at_last_sample || thread_context->is_profiler_internal_thread)) {
|
|
2147
|
+
long current_cpu_time_ns = cpu_time_now_ns(thread_context);
|
|
2148
|
+
// We need to force_sample=true otherwise this sample would be skipped too
|
|
2149
|
+
update_metrics_and_sample(
|
|
2150
|
+
state,
|
|
2151
|
+
thread,
|
|
2152
|
+
thread_context,
|
|
2153
|
+
current_cpu_time_ns,
|
|
2154
|
+
current_monotonic_wall_time_ns,
|
|
2155
|
+
true);
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
void thread_context_collector_on_gvl_released(per_thread_context *thread_context) {
|
|
2161
|
+
thread_context->gvl_state_change_count |= GVL_SUSPENDED;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
void thread_context_collector_on_gvl_waiting(per_thread_context *thread_context) {
|
|
2165
|
+
long current_monotonic_wall_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
|
|
2166
|
+
if (current_monotonic_wall_time_ns <= 0) return;
|
|
2167
|
+
|
|
2168
|
+
thread_context->gvl_waiting_at = current_monotonic_wall_time_ns;
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
|
2172
|
+
ENFORCE_THREAD(thread);
|
|
2173
|
+
|
|
2174
|
+
debug_enter_unsafe_context();
|
|
2175
|
+
|
|
2176
|
+
per_thread_context *thread_context = get_per_thread_context(thread);
|
|
2177
|
+
if (thread_context) thread_context_collector_on_gvl_waiting(thread_context);
|
|
2178
|
+
|
|
2179
|
+
debug_leave_unsafe_context();
|
|
2180
|
+
|
|
2181
|
+
return Qnil;
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
static VALUE _native_on_gvl_released(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
|
2185
|
+
ENFORCE_THREAD(thread);
|
|
2186
|
+
|
|
2187
|
+
debug_enter_unsafe_context();
|
|
2188
|
+
|
|
2189
|
+
per_thread_context *thread_context = get_per_thread_context(thread);
|
|
2190
|
+
if (thread_context) thread_context_collector_on_gvl_released(thread_context);
|
|
2191
|
+
|
|
2192
|
+
debug_leave_unsafe_context();
|
|
2193
|
+
|
|
2194
|
+
return Qnil;
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
#ifndef NO_GVL_INSTRUMENTATION
|
|
2198
|
+
// This function runs on the passed thread and has the GVL because it gets called just after the Ruby thread acquired the GVL
|
|
1901
2199
|
__attribute__((warn_unused_result))
|
|
1902
|
-
on_gvl_running_result
|
|
1903
|
-
|
|
2200
|
+
on_gvl_running_result thread_context_collector_on_gvl_running(VALUE self_instance, VALUE thread, per_thread_context *thread_context) {
|
|
2201
|
+
thread_context_collector_state *state;
|
|
2202
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1904
2203
|
|
|
1905
|
-
//
|
|
1906
|
-
|
|
2204
|
+
// Bump the event counter and clears the state bit to "running"
|
|
2205
|
+
uint64_t counter_portion = thread_context->gvl_state_change_count >> 1;
|
|
2206
|
+
thread_context->gvl_state_change_count = ((counter_portion + 1) << 1) | GVL_RUNNING;
|
|
1907
2207
|
|
|
2208
|
+
long gvl_waiting_at = thread_context->gvl_waiting_at;
|
|
2209
|
+
// Thread was not waiting on gvl
|
|
2210
|
+
if (gvl_waiting_at == 0) {
|
|
2211
|
+
return (on_gvl_running_result) {.action = ON_GVL_RUNNING_UNKNOWN, .waiting_for_gvl_duration_ns = 0};
|
|
2212
|
+
}
|
|
1908
2213
|
// @ivoanjo: I'm not sure if this can happen -- It means we should've sampled already but haven't gotten the chance yet?
|
|
1909
|
-
if (gvl_waiting_at < 0)
|
|
2214
|
+
if (gvl_waiting_at < 0) {
|
|
2215
|
+
return (on_gvl_running_result) {.action = ON_GVL_RUNNING_SAMPLE, .waiting_for_gvl_duration_ns = 0};
|
|
2216
|
+
}
|
|
1910
2217
|
|
|
1911
2218
|
long waiting_for_gvl_duration_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE) - gvl_waiting_at;
|
|
1912
2219
|
|
|
1913
|
-
bool should_sample = waiting_for_gvl_duration_ns >= waiting_for_gvl_threshold_ns;
|
|
2220
|
+
bool should_sample = waiting_for_gvl_duration_ns >= state->waiting_for_gvl_threshold_ns;
|
|
1914
2221
|
|
|
1915
2222
|
if (should_sample) {
|
|
1916
2223
|
// We flip the gvl_waiting_at to negative to mark that the thread is now running and no longer waiting
|
|
1917
|
-
|
|
2224
|
+
long gvl_waiting_at_is_now_running = -gvl_waiting_at;
|
|
1918
2225
|
|
|
1919
|
-
|
|
2226
|
+
thread_context->gvl_waiting_at = gvl_waiting_at_is_now_running;
|
|
1920
2227
|
} else {
|
|
1921
|
-
|
|
1922
|
-
|
|
2228
|
+
thread_context->gvl_waiting_at = 0;
|
|
2229
|
+
|
|
2230
|
+
// Even though the GVL wait itself was below threshold, if the thread had skipped samples
|
|
2231
|
+
// (was suspended for a long time without the GVL), we still need to force a sample now.
|
|
2232
|
+
// Otherwise, the accumulated idle wall-time would be reported against whatever stack the
|
|
2233
|
+
// thread runs next, misrepresenting the time spent idle.
|
|
2234
|
+
if (thread_context->was_skipped_at_last_sample) {
|
|
2235
|
+
should_sample = true;
|
|
2236
|
+
}
|
|
1923
2237
|
}
|
|
1924
2238
|
|
|
1925
|
-
|
|
1926
|
-
|
|
2239
|
+
if (should_sample) {
|
|
2240
|
+
// We prepare the sample here because the postponed job might be called some time later,
|
|
2241
|
+
// possibly after some Ruby calls which change the Ruby stack,
|
|
2242
|
+
// and we want to attribute the time acquiring or without the GVL to the correct Ruby stack.
|
|
2243
|
+
prepare_sample_thread(thread, &thread_context->sampling_buffer);
|
|
2244
|
+
}
|
|
1927
2245
|
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
2246
|
+
return (on_gvl_running_result) {
|
|
2247
|
+
.action = should_sample ? ON_GVL_RUNNING_SAMPLE : ON_GVL_RUNNING_DONT_SAMPLE,
|
|
2248
|
+
.waiting_for_gvl_duration_ns = waiting_for_gvl_duration_ns,
|
|
2249
|
+
};
|
|
1931
2250
|
}
|
|
1932
2251
|
|
|
1933
2252
|
// Why does this method need to exist?
|
|
@@ -1947,7 +2266,7 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
|
1947
2266
|
//
|
|
1948
2267
|
// Stack:
|
|
1949
2268
|
// If the thread starts working without the end of the Waiting for GVL sample, then by the time the thread is sampled
|
|
1950
|
-
// via the regular cpu/wall-time samples mechanism, the stack can be
|
|
2269
|
+
// via the regular cpu/wall-time samples mechanism, the stack can be inaccurate (e.g. does not correctly pinpoint
|
|
1951
2270
|
// where the waiting happened).
|
|
1952
2271
|
//
|
|
1953
2272
|
// Arguably, the last sample after Waiting for GVL ended (when gvl_waiting_at < 0) should always come from this method
|
|
@@ -1956,17 +2275,19 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
|
1956
2275
|
//
|
|
1957
2276
|
// ---
|
|
1958
2277
|
//
|
|
2278
|
+
// Always called with the GVL, either from a postponed_job or from tests.
|
|
2279
|
+
//
|
|
1959
2280
|
// NOTE: In normal use, current_thread is expected to be == rb_thread_current(); the `current_thread` parameter only
|
|
1960
2281
|
// exists to enable testing.
|
|
1961
2282
|
VALUE thread_context_collector_sample_after_gvl_running(VALUE self_instance, VALUE current_thread, long current_monotonic_wall_time_ns) {
|
|
1962
2283
|
thread_context_collector_state *state;
|
|
1963
2284
|
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1964
2285
|
|
|
1965
|
-
|
|
2286
|
+
per_thread_context *thread_context = get_or_create_context_for(current_thread);
|
|
1966
2287
|
|
|
1967
|
-
|
|
2288
|
+
long gvl_waiting_at = thread_context->gvl_waiting_at;
|
|
1968
2289
|
|
|
1969
|
-
if (gvl_waiting_at >= 0) {
|
|
2290
|
+
if (gvl_waiting_at >= 0 && !thread_context->was_skipped_at_last_sample) {
|
|
1970
2291
|
// @ivoanjo: I'm not sure if this can ever happen. This means that we're not on the same thread
|
|
1971
2292
|
// that ran `thread_context_collector_on_gvl_running` and made the decision to sample OR a regular sample was
|
|
1972
2293
|
// triggered ahead of us.
|
|
@@ -1974,9 +2295,7 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
|
1974
2295
|
return Qfalse;
|
|
1975
2296
|
}
|
|
1976
2297
|
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
// We don't actually account for cpu-time during Waiting for GVL. BUT, we may chose to push an
|
|
2298
|
+
// We don't actually account for cpu-time during Waiting for GVL. BUT, we may choose to push an
|
|
1980
2299
|
// extra sample to represent the period prior to Waiting for GVL. To support that, we retrieve the current
|
|
1981
2300
|
// cpu-time of the thread and let `update_metrics_and_sample` decide what to do with it.
|
|
1982
2301
|
long cpu_time_for_thread = cpu_time_now_ns(thread_context);
|
|
@@ -1985,13 +2304,11 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
|
1985
2304
|
|
|
1986
2305
|
update_metrics_and_sample(
|
|
1987
2306
|
state,
|
|
1988
|
-
|
|
1989
|
-
/* stack_from_thread: */ current_thread,
|
|
2307
|
+
current_thread,
|
|
1990
2308
|
thread_context,
|
|
1991
|
-
&thread_context->sampling_buffer,
|
|
1992
2309
|
cpu_time_for_thread,
|
|
1993
|
-
current_monotonic_wall_time_ns
|
|
1994
|
-
|
|
2310
|
+
current_monotonic_wall_time_ns,
|
|
2311
|
+
false);
|
|
1995
2312
|
|
|
1996
2313
|
return Qtrue;
|
|
1997
2314
|
}
|
|
@@ -2002,14 +2319,12 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
|
2002
2319
|
static bool handle_gvl_waiting(
|
|
2003
2320
|
thread_context_collector_state *state,
|
|
2004
2321
|
VALUE thread_being_sampled,
|
|
2005
|
-
VALUE stack_from_thread,
|
|
2006
2322
|
per_thread_context *thread_context,
|
|
2007
|
-
sampling_buffer* sampling_buffer,
|
|
2008
2323
|
long current_cpu_time_ns
|
|
2009
2324
|
) {
|
|
2010
|
-
|
|
2325
|
+
long gvl_waiting_at = thread_context->gvl_waiting_at;
|
|
2011
2326
|
|
|
2012
|
-
bool is_gvl_waiting_state = gvl_waiting_at != 0
|
|
2327
|
+
bool is_gvl_waiting_state = gvl_waiting_at != 0;
|
|
2013
2328
|
|
|
2014
2329
|
if (!is_gvl_waiting_state) return false;
|
|
2015
2330
|
|
|
@@ -2022,17 +2337,17 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
|
2022
2337
|
// ...──────────────┬───────────────────...
|
|
2023
2338
|
// Other state │ Waiting for GVL
|
|
2024
2339
|
// ...──────────────┴───────────────────...
|
|
2025
|
-
// ▲
|
|
2340
|
+
// ▲ ▲
|
|
2026
2341
|
// └─ Previous sample └─ Regular sample (caller)
|
|
2027
2342
|
//
|
|
2028
2343
|
// In this case, we'll want to push two samples: a) one for the current time (handled by the caller), b) an extra sample
|
|
2029
|
-
// to represent the remaining cpu/wall time before the "Waiting for GVL" started:
|
|
2344
|
+
// to represent the remaining cpu/wall time before the "Waiting for GVL" started (for timeline purposes):
|
|
2030
2345
|
//
|
|
2031
2346
|
// time ─────►
|
|
2032
2347
|
// ...──────────────┬───────────────────...
|
|
2033
2348
|
// Other state │ Waiting for GVL
|
|
2034
2349
|
// ...──────────────┴───────────────────...
|
|
2035
|
-
// ▲
|
|
2350
|
+
// ▲ ▲ ▲
|
|
2036
2351
|
// └─ Prev... └─ Extra sample └─ Regular sample (caller)
|
|
2037
2352
|
//
|
|
2038
2353
|
// 2. The current sample is the n-th one after we entered the "Waiting for GVL" state
|
|
@@ -2042,7 +2357,7 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
|
2042
2357
|
// ...──────────────┬───────────────────────────────────────────────...
|
|
2043
2358
|
// Other state │ Waiting for GVL
|
|
2044
2359
|
// ...──────────────┴───────────────────────────────────────────────...
|
|
2045
|
-
// ▲
|
|
2360
|
+
// ▲ ▲ ▲
|
|
2046
2361
|
// └─ Previous sample └─ Previous sample └─ Regular sample (caller)
|
|
2047
2362
|
//
|
|
2048
2363
|
// In this case, we just report back to the caller that the thread is in the "Waiting for GVL" state.
|
|
@@ -2057,33 +2372,22 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
|
2057
2372
|
|
|
2058
2373
|
if (gvl_waiting_at < 0) {
|
|
2059
2374
|
// Negative means the waiting for GVL just ended, so we clear the state, so next samples no longer represent waiting
|
|
2060
|
-
|
|
2375
|
+
thread_context->gvl_waiting_at = 0;
|
|
2061
2376
|
}
|
|
2062
2377
|
|
|
2063
2378
|
long gvl_waiting_started_wall_time_ns = labs(gvl_waiting_at);
|
|
2064
2379
|
|
|
2065
2380
|
if (thread_context->wall_time_at_previous_sample_ns < gvl_waiting_started_wall_time_ns) { // situation 1 above
|
|
2066
|
-
long cpu_time_elapsed_ns =
|
|
2067
|
-
&thread_context->cpu_time_at_previous_sample_ns,
|
|
2068
|
-
current_cpu_time_ns,
|
|
2069
|
-
thread_context->gc_tracking.cpu_time_at_start_ns,
|
|
2070
|
-
IS_NOT_WALL_TIME
|
|
2071
|
-
);
|
|
2381
|
+
long cpu_time_elapsed_ns = update_cpu_time_since_previous_sample(thread_context, current_cpu_time_ns);
|
|
2072
2382
|
|
|
2073
|
-
long duration_until_start_of_gvl_waiting_ns =
|
|
2074
|
-
|
|
2075
|
-
gvl_waiting_started_wall_time_ns,
|
|
2076
|
-
INVALID_TIME,
|
|
2077
|
-
IS_WALL_TIME
|
|
2078
|
-
);
|
|
2383
|
+
long duration_until_start_of_gvl_waiting_ns =
|
|
2384
|
+
update_wall_time_since_previous_sample(thread_context, gvl_waiting_started_wall_time_ns);
|
|
2079
2385
|
|
|
2080
2386
|
// Push extra sample
|
|
2081
2387
|
trigger_sample_for_thread(
|
|
2082
2388
|
state,
|
|
2083
2389
|
thread_being_sampled,
|
|
2084
|
-
stack_from_thread,
|
|
2085
2390
|
thread_context,
|
|
2086
|
-
sampling_buffer,
|
|
2087
2391
|
(sample_values) {.cpu_time_ns = cpu_time_elapsed_ns, .cpu_or_wall_samples = 1, .wall_time_ns = duration_until_start_of_gvl_waiting_ns},
|
|
2088
2392
|
gvl_waiting_started_wall_time_ns,
|
|
2089
2393
|
NULL,
|
|
@@ -2096,36 +2400,31 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
|
2096
2400
|
return true;
|
|
2097
2401
|
}
|
|
2098
2402
|
|
|
2099
|
-
static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
|
2100
|
-
ENFORCE_THREAD(thread);
|
|
2101
|
-
|
|
2102
|
-
debug_enter_unsafe_context();
|
|
2103
|
-
|
|
2104
|
-
thread_context_collector_on_gvl_waiting(thread_from_thread_object(thread));
|
|
2105
|
-
|
|
2106
|
-
debug_leave_unsafe_context();
|
|
2107
|
-
|
|
2108
|
-
return Qnil;
|
|
2109
|
-
}
|
|
2110
|
-
|
|
2111
2403
|
static VALUE _native_gvl_waiting_at_for(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
|
2112
2404
|
ENFORCE_THREAD(thread);
|
|
2113
2405
|
|
|
2114
2406
|
debug_enter_unsafe_context();
|
|
2115
2407
|
|
|
2116
|
-
|
|
2408
|
+
per_thread_context *thread_context = get_per_thread_context(thread);
|
|
2409
|
+
VALUE result = thread_context ? LONG2NUM(thread_context->gvl_waiting_at) : Qnil;
|
|
2117
2410
|
|
|
2118
2411
|
debug_leave_unsafe_context();
|
|
2119
2412
|
|
|
2120
|
-
return
|
|
2413
|
+
return result;
|
|
2121
2414
|
}
|
|
2122
2415
|
|
|
2123
|
-
static VALUE _native_on_gvl_running(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
|
2416
|
+
static VALUE _native_on_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread) {
|
|
2124
2417
|
ENFORCE_THREAD(thread);
|
|
2125
2418
|
|
|
2126
2419
|
debug_enter_unsafe_context();
|
|
2127
2420
|
|
|
2128
|
-
|
|
2421
|
+
per_thread_context *thread_context = get_per_thread_context(thread);
|
|
2422
|
+
VALUE result;
|
|
2423
|
+
if (thread_context) {
|
|
2424
|
+
result = thread_context_collector_on_gvl_running(collector_instance, thread, thread_context).action == ON_GVL_RUNNING_SAMPLE ? Qtrue : Qfalse;
|
|
2425
|
+
} else {
|
|
2426
|
+
result = Qfalse;
|
|
2427
|
+
}
|
|
2129
2428
|
|
|
2130
2429
|
debug_leave_unsafe_context();
|
|
2131
2430
|
|
|
@@ -2136,8 +2435,6 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
|
2136
2435
|
ENFORCE_THREAD(thread);
|
|
2137
2436
|
ENFORCE_BOOLEAN(allow_exception);
|
|
2138
2437
|
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
2438
|
if (allow_exception == Qfalse) debug_enter_unsafe_context();
|
|
2142
2439
|
|
|
2143
2440
|
VALUE result = thread_context_collector_sample_after_gvl_running(
|
|
@@ -2151,31 +2448,27 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
|
2151
2448
|
return result;
|
|
2152
2449
|
}
|
|
2153
2450
|
|
|
2154
|
-
static VALUE _native_apply_delta_to_cpu_time_at_previous_sample_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread, VALUE delta_ns) {
|
|
2155
|
-
ENFORCE_THREAD(thread);
|
|
2156
|
-
|
|
2157
|
-
thread_context_collector_state *state;
|
|
2158
|
-
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
2159
|
-
|
|
2160
|
-
per_thread_context *thread_context = get_context_for(thread, state);
|
|
2161
|
-
if (thread_context == NULL) raise_error(rb_eArgError, "Unexpected: This method cannot be used unless the per-thread context for the thread already exists");
|
|
2162
|
-
|
|
2163
|
-
thread_context->cpu_time_at_previous_sample_ns += NUM2LONG(delta_ns);
|
|
2164
|
-
|
|
2165
|
-
return Qtrue;
|
|
2166
|
-
}
|
|
2167
|
-
|
|
2168
2451
|
#else
|
|
2169
2452
|
static bool handle_gvl_waiting(
|
|
2170
2453
|
DDTRACE_UNUSED thread_context_collector_state *state,
|
|
2171
2454
|
DDTRACE_UNUSED VALUE thread_being_sampled,
|
|
2172
|
-
DDTRACE_UNUSED VALUE stack_from_thread,
|
|
2173
2455
|
DDTRACE_UNUSED per_thread_context *thread_context,
|
|
2174
|
-
DDTRACE_UNUSED sampling_buffer* sampling_buffer,
|
|
2175
2456
|
DDTRACE_UNUSED long current_cpu_time_ns
|
|
2176
2457
|
) { return false; }
|
|
2458
|
+
|
|
2177
2459
|
#endif // NO_GVL_INSTRUMENTATION
|
|
2178
2460
|
|
|
2461
|
+
static VALUE _native_apply_delta_to_cpu_time_at_previous_sample_ns(DDTRACE_UNUSED VALUE self, VALUE thread, VALUE delta_ns) {
|
|
2462
|
+
ENFORCE_THREAD(thread);
|
|
2463
|
+
|
|
2464
|
+
per_thread_context *thread_context = get_per_thread_context(thread);
|
|
2465
|
+
if (thread_context == NULL) raise_error(rb_eArgError, "Unexpected: This method cannot be used unless the per-thread context for the thread already exists");
|
|
2466
|
+
|
|
2467
|
+
thread_context->cpu_time_at_previous_sample_ns += NUM2LONG(delta_ns);
|
|
2468
|
+
|
|
2469
|
+
return Qtrue;
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2179
2472
|
#define MAX_SAFE_LOOKUP_SIZE 16
|
|
2180
2473
|
|
|
2181
2474
|
typedef struct { VALUE lookup_key; VALUE result; } safe_lookup_hash_state;
|
|
@@ -2220,6 +2513,6 @@ static VALUE _native_system_epoch_time_now_ns(DDTRACE_UNUSED VALUE self, VALUE c
|
|
|
2220
2513
|
return LONG2NUM(system_epoch_time_ns);
|
|
2221
2514
|
}
|
|
2222
2515
|
|
|
2223
|
-
static VALUE _native_prepare_sample_inside_signal_handler(DDTRACE_UNUSED VALUE self
|
|
2224
|
-
return thread_context_collector_prepare_sample_inside_signal_handler(
|
|
2516
|
+
static VALUE _native_prepare_sample_inside_signal_handler(DDTRACE_UNUSED VALUE self) {
|
|
2517
|
+
return thread_context_collector_prepare_sample_inside_signal_handler() ? Qtrue : Qfalse;
|
|
2225
2518
|
}
|