datadog 2.17.0 → 2.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +686 -1
- data/README.md +0 -1
- data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
- data/ext/datadog_profiling_native_extension/clock_id.h +9 -1
- data/ext/datadog_profiling_native_extension/clock_id_from_mach.c +66 -0
- data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +3 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +344 -126
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +4 -4
- data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +3 -2
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +18 -4
- data/ext/datadog_profiling_native_extension/collectors_stack.c +314 -91
- data/ext/datadog_profiling_native_extension/collectors_stack.h +26 -3
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +680 -352
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +18 -5
- data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +65 -1
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +40 -1
- data/ext/datadog_profiling_native_extension/encoded_profile.c +2 -1
- data/ext/datadog_profiling_native_extension/extconf.rb +48 -34
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +4 -43
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +15 -47
- data/ext/datadog_profiling_native_extension/heap_recorder.c +455 -430
- data/ext/datadog_profiling_native_extension/heap_recorder.h +16 -7
- data/ext/datadog_profiling_native_extension/http_transport.c +66 -68
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +18 -15
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +8 -10
- data/ext/datadog_profiling_native_extension/macos_sampler_thread.h +55 -0
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +0 -13
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +73 -69
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +10 -4
- data/ext/datadog_profiling_native_extension/profiling.c +65 -19
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +64 -59
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +20 -16
- data/ext/datadog_profiling_native_extension/setup_signal_handler.c +32 -10
- data/ext/datadog_profiling_native_extension/setup_signal_handler.h +2 -1
- data/ext/datadog_profiling_native_extension/stack_recorder.c +253 -131
- data/ext/datadog_profiling_native_extension/stack_recorder.h +3 -1
- data/ext/datadog_profiling_native_extension/time_helpers.h +1 -0
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +2 -1
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +9 -4
- data/ext/libdatadog_api/crashtracker.c +16 -15
- data/ext/libdatadog_api/crashtracker_report_exception.c +126 -0
- data/ext/libdatadog_api/datadog_ruby_common.c +65 -1
- data/ext/libdatadog_api/datadog_ruby_common.h +40 -1
- data/ext/libdatadog_api/ddsketch.c +102 -0
- data/ext/libdatadog_api/di.c +203 -0
- data/ext/libdatadog_api/extconf.rb +21 -25
- data/ext/libdatadog_api/feature_flags.c +554 -0
- data/ext/libdatadog_api/feature_flags.h +5 -0
- data/ext/libdatadog_api/helpers.h +27 -0
- data/ext/libdatadog_api/init.c +13 -1
- data/ext/libdatadog_api/library_config.c +88 -37
- data/ext/libdatadog_api/library_config.h +6 -0
- data/ext/libdatadog_api/process_discovery.c +25 -24
- data/ext/libdatadog_extconf_helpers.rb +107 -14
- data/lib/datadog/ai_guard/api_client.rb +84 -0
- data/lib/datadog/ai_guard/autoload.rb +10 -0
- data/lib/datadog/ai_guard/component.rb +44 -0
- data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
- data/lib/datadog/ai_guard/configuration.rb +115 -0
- data/lib/datadog/ai_guard/contrib/auto_instrument.rb +24 -0
- data/lib/datadog/ai_guard/contrib/integration.rb +37 -0
- data/lib/datadog/ai_guard/contrib/rack/integration.rb +42 -0
- data/lib/datadog/ai_guard/contrib/rack/patcher.rb +26 -0
- data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +97 -0
- data/lib/datadog/ai_guard/contrib/rails/integration.rb +41 -0
- data/lib/datadog/ai_guard/contrib/rails/patcher.rb +97 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +80 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +41 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +30 -0
- data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
- data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
- data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
- data/lib/datadog/ai_guard/evaluation/no_op_result.rb +36 -0
- data/lib/datadog/ai_guard/evaluation/request.rb +86 -0
- data/lib/datadog/ai_guard/evaluation/result.rb +45 -0
- data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
- data/lib/datadog/ai_guard/evaluation.rb +108 -0
- data/lib/datadog/ai_guard/ext.rb +29 -0
- data/lib/datadog/ai_guard.rb +181 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +66 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +36 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
- data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
- data/lib/datadog/appsec/api_security/route_extractor.rb +104 -0
- data/lib/datadog/appsec/api_security/sampler.rb +62 -0
- data/lib/datadog/appsec/api_security.rb +14 -0
- data/lib/datadog/appsec/assets/blocked.html +10 -1
- data/lib/datadog/appsec/assets/blocked.json +1 -1
- data/lib/datadog/appsec/assets/blocked.text +3 -1
- data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +601 -74
- data/lib/datadog/appsec/assets/waf_rules/strict.json +48 -75
- data/lib/datadog/appsec/assets.rb +1 -1
- data/lib/datadog/appsec/autoload.rb +2 -2
- data/lib/datadog/appsec/component.rb +37 -64
- data/lib/datadog/appsec/compressed_json.rb +3 -3
- data/lib/datadog/appsec/configuration.rb +427 -1
- data/lib/datadog/appsec/context.rb +77 -16
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
- data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/active_record/patcher.rb +4 -1
- data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +75 -0
- data/lib/datadog/appsec/contrib/aws_lambda/integration.rb +39 -0
- data/lib/datadog/appsec/contrib/aws_lambda/patcher.rb +30 -0
- data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +144 -0
- data/lib/datadog/appsec/contrib/devise/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +4 -2
- data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +2 -1
- data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +1 -1
- data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/excon/patcher.rb +3 -1
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +152 -13
- data/lib/datadog/appsec/contrib/faraday/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/faraday/patcher.rb +1 -1
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +138 -13
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +74 -24
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +4 -2
- data/lib/datadog/appsec/contrib/graphql/integration.rb +2 -1
- data/lib/datadog/appsec/contrib/rack/buffered_input.rb +83 -0
- data/lib/datadog/appsec/contrib/rack/ext.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +47 -13
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +40 -19
- data/lib/datadog/appsec/contrib/rack/input_peeker.rb +79 -0
- data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +80 -62
- data/lib/datadog/appsec/contrib/rack/response_body.rb +36 -0
- data/lib/datadog/appsec/contrib/rails/gateway/request.rb +35 -1
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +47 -5
- data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/patcher.rb +52 -26
- data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.rb +29 -0
- data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +33 -0
- data/lib/datadog/appsec/contrib/rest_client/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rest_client/patcher.rb +3 -1
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +167 -12
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +63 -10
- data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +11 -21
- data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +31 -0
- data/lib/datadog/appsec/counter_sampler.rb +25 -0
- data/lib/datadog/appsec/default_header_tags.rb +52 -0
- data/lib/datadog/appsec/event.rb +14 -47
- data/lib/datadog/appsec/ext.rb +3 -0
- data/lib/datadog/appsec/instrumentation/gateway/argument.rb +17 -1
- data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +2 -3
- data/lib/datadog/appsec/instrumentation/gateway.rb +2 -15
- data/lib/datadog/appsec/metrics/collector.rb +47 -5
- data/lib/datadog/appsec/metrics/exporter.rb +16 -3
- data/lib/datadog/appsec/metrics/telemetry.rb +21 -4
- data/lib/datadog/appsec/metrics/telemetry_exporter.rb +47 -0
- data/lib/datadog/appsec/metrics.rb +5 -4
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +8 -6
- data/lib/datadog/appsec/processor/rule_loader.rb +5 -6
- data/lib/datadog/appsec/remote.rb +51 -76
- data/lib/datadog/appsec/response.rb +18 -4
- data/lib/datadog/appsec/route_normalizer/rails_route_pattern.rb +176 -0
- data/lib/datadog/appsec/route_normalizer/route_pattern.rb +378 -0
- data/lib/datadog/appsec/route_normalizer/route_text.rb +57 -0
- data/lib/datadog/appsec/route_normalizer.rb +80 -0
- data/lib/datadog/appsec/security_engine/engine.rb +197 -0
- data/lib/datadog/appsec/security_engine/result.rb +45 -9
- data/lib/datadog/appsec/security_engine/runner.rb +44 -21
- data/lib/datadog/appsec/security_event.rb +5 -7
- data/lib/datadog/appsec/thread_safe_ref.rb +61 -0
- data/lib/datadog/appsec/trace_keeper.rb +36 -0
- data/lib/datadog/appsec/utils/hash_coercion.rb +23 -0
- data/lib/datadog/appsec/utils/http/body.rb +38 -0
- data/lib/datadog/appsec/utils/http/body_reader.rb +61 -0
- data/lib/datadog/appsec/utils/http/media_range.rb +2 -1
- data/lib/datadog/appsec/utils/http/media_type.rb +32 -26
- data/lib/datadog/appsec/utils/http/url_encoded.rb +52 -0
- data/lib/datadog/appsec.rb +10 -17
- data/lib/datadog/auto_instrument_base.rb +2 -1
- data/lib/datadog/core/configuration/agent_settings.rb +52 -0
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +5 -47
- data/lib/datadog/core/configuration/base.rb +17 -5
- data/lib/datadog/core/configuration/components.rb +192 -20
- data/lib/datadog/core/configuration/components_state.rb +6 -1
- data/lib/datadog/core/configuration/config_helper.rb +109 -0
- data/lib/datadog/core/configuration/deprecations.rb +36 -0
- data/lib/datadog/core/configuration/ext.rb +0 -1
- data/lib/datadog/core/configuration/option.rb +95 -65
- data/lib/datadog/core/configuration/option_definition.rb +42 -23
- data/lib/datadog/core/configuration/options.rb +57 -18
- data/lib/datadog/core/configuration/settings.rb +160 -25
- data/lib/datadog/core/configuration/stable_config.rb +11 -2
- data/lib/datadog/core/configuration/supported_configurations.rb +413 -0
- data/lib/datadog/core/configuration.rb +4 -4
- data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
- data/lib/datadog/core/contrib/rails/utils.rb +7 -3
- data/lib/datadog/core/crashtracking/component.rb +73 -21
- data/lib/datadog/core/crashtracking/tag_builder.rb +9 -21
- data/lib/datadog/core/ddsketch.rb +19 -0
- data/lib/datadog/core/deprecations.rb +2 -2
- data/lib/datadog/core/diagnostics/environment_logger.rb +3 -1
- data/lib/datadog/core/environment/agent_info.rb +65 -1
- data/lib/datadog/core/environment/cgroup.rb +52 -25
- data/lib/datadog/core/environment/container.rb +140 -46
- data/lib/datadog/core/environment/ext.rb +13 -2
- data/lib/datadog/core/environment/git.rb +2 -2
- data/lib/datadog/core/environment/identity.rb +40 -4
- data/lib/datadog/core/environment/process.rb +122 -0
- data/lib/datadog/core/environment/socket.rb +13 -0
- data/lib/datadog/core/environment/variable_helpers.rb +3 -3
- data/lib/datadog/core/environment/yjit.rb +2 -1
- data/lib/datadog/core/error.rb +6 -6
- data/lib/datadog/core/evp.rb +11 -0
- data/lib/datadog/core/feature_flags.rb +61 -0
- data/lib/datadog/core/knuth_sampler.rb +57 -0
- data/lib/datadog/core/logger.rb +1 -1
- data/lib/datadog/core/metrics/client.rb +7 -7
- data/lib/datadog/core/metrics/logging.rb +1 -1
- data/lib/datadog/core/pin.rb +8 -8
- data/lib/datadog/core/process_discovery/tracer_memfd.rb +13 -0
- data/lib/datadog/core/process_discovery.rb +51 -21
- data/lib/datadog/core/rate_limiter.rb +11 -1
- data/lib/datadog/core/remote/client/capabilities.rb +49 -6
- data/lib/datadog/core/remote/client.rb +15 -7
- data/lib/datadog/core/remote/component.rb +64 -36
- data/lib/datadog/core/remote/configuration/content.rb +15 -2
- data/lib/datadog/core/remote/configuration/digest.rb +14 -7
- data/lib/datadog/core/remote/configuration/repository.rb +13 -1
- data/lib/datadog/core/remote/configuration/target.rb +13 -6
- data/lib/datadog/core/remote/transport/config.rb +9 -36
- data/lib/datadog/core/remote/transport/http/config.rb +15 -55
- data/lib/datadog/core/remote/transport/http/negotiation.rb +14 -44
- data/lib/datadog/core/remote/transport/http.rb +15 -24
- data/lib/datadog/core/remote/transport/negotiation.rb +8 -33
- data/lib/datadog/core/remote/worker.rb +25 -37
- data/lib/datadog/core/runtime/ext.rb +0 -1
- data/lib/datadog/core/runtime/metrics.rb +11 -2
- data/lib/datadog/core/semaphore.rb +1 -4
- data/lib/datadog/core/tag_builder.rb +52 -0
- data/lib/datadog/core/tag_normalizer.rb +84 -0
- data/lib/datadog/core/telemetry/component.rb +65 -17
- data/lib/datadog/core/telemetry/emitter.rb +6 -6
- data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +3 -3
- data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
- data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
- data/lib/datadog/core/telemetry/event/app_started.rb +203 -89
- data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
- data/lib/datadog/core/telemetry/event.rb +2 -7
- data/lib/datadog/core/telemetry/ext.rb +1 -0
- data/lib/datadog/core/telemetry/logger.rb +5 -2
- data/lib/datadog/core/telemetry/logging.rb +22 -4
- data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
- data/lib/datadog/core/telemetry/request.rb +17 -5
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +8 -34
- data/lib/datadog/core/telemetry/transport/http.rb +21 -16
- data/lib/datadog/core/telemetry/transport/telemetry.rb +8 -17
- data/lib/datadog/core/telemetry/worker.rb +108 -32
- data/lib/datadog/core/transport/ext.rb +3 -0
- data/lib/datadog/core/transport/http/adapters/net.rb +17 -2
- data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
- data/lib/datadog/core/transport/http/api/instance.rb +4 -21
- data/lib/datadog/core/transport/http/builder.rb +11 -7
- data/lib/datadog/core/transport/http/client.rb +80 -0
- data/lib/datadog/core/transport/http/env.rb +8 -0
- data/lib/datadog/core/transport/http/response.rb +4 -0
- data/lib/datadog/core/transport/http.rb +24 -19
- data/lib/datadog/core/transport/parcel.rb +61 -9
- data/lib/datadog/core/transport/response.rb +16 -2
- data/lib/datadog/core/transport/transport.rb +90 -0
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +1 -1
- data/lib/datadog/core/utils/{base64.rb → base64_codec.rb} +4 -3
- data/lib/datadog/core/utils/enumerable_compat.rb +29 -0
- data/lib/datadog/core/utils/fnv.rb +26 -0
- data/lib/datadog/core/utils/forking.rb +3 -1
- data/lib/datadog/core/utils/hash.rb +0 -23
- data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -14
- data/lib/datadog/core/utils/network.rb +22 -1
- data/lib/datadog/core/utils/only_once.rb +1 -1
- data/lib/datadog/core/utils/only_once_successful.rb +8 -2
- data/lib/datadog/core/utils/safe_dup.rb +2 -2
- data/lib/datadog/core/utils/sequence.rb +2 -0
- data/lib/datadog/core/utils/spawn_monkey_patch.rb +68 -0
- data/lib/datadog/core/utils/time.rb +1 -1
- data/lib/datadog/core/utils.rb +10 -1
- data/lib/datadog/core/workers/async.rb +11 -2
- data/lib/datadog/core/workers/interval_loop.rb +51 -3
- data/lib/datadog/core/workers/polling.rb +2 -0
- data/lib/datadog/core/workers/queue.rb +96 -1
- data/lib/datadog/core/workers/runtime_metrics.rb +9 -1
- data/lib/datadog/core.rb +11 -2
- data/lib/datadog/data_streams/configuration.rb +50 -0
- data/lib/datadog/data_streams/ext.rb +11 -0
- data/lib/datadog/data_streams/extensions.rb +16 -0
- data/lib/datadog/data_streams/pathway_context.rb +169 -0
- data/lib/datadog/data_streams/processor.rb +511 -0
- data/lib/datadog/data_streams/transport/http/stats.rb +58 -0
- data/lib/datadog/data_streams/transport/http.rb +36 -0
- data/lib/datadog/data_streams/transport/stats.rb +39 -0
- data/lib/datadog/data_streams.rb +100 -0
- data/lib/datadog/di/base.rb +15 -7
- data/lib/datadog/di/boot.rb +11 -1
- data/lib/datadog/di/code_tracker.rb +217 -15
- data/lib/datadog/di/component.rb +143 -48
- data/lib/datadog/di/configuration.rb +235 -2
- data/lib/datadog/di/context.rb +76 -0
- data/lib/datadog/di/contrib/active_record.rb +34 -5
- data/lib/datadog/di/el/compiler.rb +168 -0
- data/lib/datadog/di/el/evaluator.rb +159 -0
- data/lib/datadog/di/el/expression.rb +42 -0
- data/lib/datadog/di/el.rb +5 -0
- data/lib/datadog/di/error.rb +44 -0
- data/lib/datadog/di/fatal_exceptions.rb +26 -0
- data/lib/datadog/di/instrumenter.rb +620 -132
- data/lib/datadog/di/logger.rb +2 -2
- data/lib/datadog/di/probe.rb +78 -15
- data/lib/datadog/di/probe_builder.rb +42 -3
- data/lib/datadog/di/probe_file_loader/railtie.rb +15 -0
- data/lib/datadog/di/probe_file_loader.rb +87 -0
- data/lib/datadog/di/probe_manager.rb +195 -100
- data/lib/datadog/di/probe_notification_builder.rb +285 -100
- data/lib/datadog/di/probe_notifier_worker.rb +120 -36
- data/lib/datadog/di/probe_repository.rb +198 -0
- data/lib/datadog/di/proc_responder.rb +36 -0
- data/lib/datadog/di/redactor.rb +24 -2
- data/lib/datadog/di/remote.rb +258 -95
- data/lib/datadog/di/serializer.rb +297 -20
- data/lib/datadog/di/transport/diagnostics.rb +12 -42
- data/lib/datadog/di/transport/http/diagnostics.rb +4 -34
- data/lib/datadog/di/transport/http/input.rb +12 -34
- data/lib/datadog/di/transport/http.rb +43 -19
- data/lib/datadog/di/transport/input.rb +121 -39
- data/lib/datadog/di/utils.rb +42 -14
- data/lib/datadog/di.rb +203 -10
- data/lib/datadog/error_tracking/collector.rb +2 -1
- data/lib/datadog/error_tracking/component.rb +2 -2
- data/lib/datadog/error_tracking/configuration.rb +55 -2
- data/lib/datadog/error_tracking/filters.rb +2 -2
- data/lib/datadog/kit/appsec/events/v2.rb +253 -0
- data/lib/datadog/kit/appsec/events.rb +11 -10
- data/lib/datadog/kit/enable_core_dumps.rb +1 -1
- data/lib/datadog/kit/identity.rb +17 -11
- data/lib/datadog/kit/tracing/method_tracer.rb +135 -0
- data/lib/datadog/open_feature/component.rb +100 -0
- data/lib/datadog/open_feature/configuration.rb +37 -0
- data/lib/datadog/open_feature/evaluation_engine.rb +70 -0
- data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
- data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
- data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
- data/lib/datadog/open_feature/exposures/event.rb +60 -0
- data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
- data/lib/datadog/open_feature/exposures/worker.rb +116 -0
- data/lib/datadog/open_feature/ext.rb +16 -0
- data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +286 -0
- data/lib/datadog/open_feature/flag_evaluation/writer.rb +433 -0
- data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +101 -0
- data/lib/datadog/open_feature/hooks/flag_eval_metrics_hook.rb +49 -0
- data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
- data/lib/datadog/open_feature/native_evaluator.rb +60 -0
- data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
- data/lib/datadog/open_feature/provider.rb +191 -0
- data/lib/datadog/open_feature/remote.rb +67 -0
- data/lib/datadog/open_feature/resolution_details.rb +35 -0
- data/lib/datadog/open_feature/transport.rb +121 -0
- data/lib/datadog/open_feature.rb +19 -0
- data/lib/datadog/opentelemetry/api/baggage.rb +3 -3
- data/lib/datadog/opentelemetry/api/context.rb +10 -9
- data/lib/datadog/opentelemetry/configuration/settings.rb +226 -0
- data/lib/datadog/opentelemetry/ext.rb +9 -0
- data/lib/datadog/opentelemetry/logs.rb +98 -0
- data/lib/datadog/opentelemetry/metrics.rb +90 -0
- data/lib/datadog/opentelemetry/sdk/configurator.rb +66 -2
- data/lib/datadog/opentelemetry/sdk/id_generator.rb +16 -10
- data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +30 -0
- data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +28 -0
- data/lib/datadog/opentelemetry/sdk/propagator.rb +13 -7
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +12 -9
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +14 -10
- data/lib/datadog/opentelemetry/sdk.rb +19 -0
- data/lib/datadog/opentelemetry/signal_configuration.rb +53 -0
- data/lib/datadog/opentelemetry/trace.rb +4 -4
- data/lib/datadog/opentelemetry.rb +4 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +83 -15
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +52 -3
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +13 -4
- data/lib/datadog/profiling/collectors/info.rb +59 -4
- data/lib/datadog/profiling/collectors/thread_context.rb +17 -5
- data/lib/datadog/profiling/component.rb +73 -49
- data/lib/datadog/profiling/exporter.rb +48 -13
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +24 -5
- data/lib/datadog/profiling/ext/exec_monkey_patch.rb +32 -0
- data/lib/datadog/profiling/ext.rb +2 -15
- data/lib/datadog/profiling/flush.rb +23 -11
- data/lib/datadog/profiling/http_transport.rb +24 -11
- data/lib/datadog/profiling/load_native_extension.rb +2 -2
- data/lib/datadog/profiling/profiler.rb +31 -12
- data/lib/datadog/profiling/scheduler.rb +12 -10
- data/lib/datadog/profiling/sequence_tracker.rb +44 -0
- data/lib/datadog/profiling/stack_recorder.rb +5 -13
- data/lib/datadog/profiling/tag_builder.rb +42 -38
- data/lib/datadog/profiling/tasks/exec.rb +10 -5
- data/lib/datadog/profiling/tasks/help.rb +1 -0
- data/lib/datadog/profiling/tasks/setup.rb +4 -2
- data/lib/datadog/profiling.rb +8 -11
- data/lib/datadog/ruby_version.rb +25 -0
- data/lib/datadog/single_step_instrument.rb +10 -1
- data/lib/datadog/symbol_database/component.rb +734 -0
- data/lib/datadog/symbol_database/configuration.rb +73 -0
- data/lib/datadog/symbol_database/extensions.rb +19 -0
- data/lib/datadog/symbol_database/extractor.rb +1110 -0
- data/lib/datadog/symbol_database/file_hash.rb +48 -0
- data/lib/datadog/symbol_database/logger.rb +43 -0
- data/lib/datadog/symbol_database/remote.rb +180 -0
- data/lib/datadog/symbol_database/scope.rb +102 -0
- data/lib/datadog/symbol_database/scope_batcher.rb +292 -0
- data/lib/datadog/symbol_database/service_version.rb +66 -0
- data/lib/datadog/symbol_database/symbol.rb +69 -0
- data/lib/datadog/symbol_database/transport/http/endpoint.rb +28 -0
- data/lib/datadog/symbol_database/transport/http.rb +45 -0
- data/lib/datadog/symbol_database/transport.rb +54 -0
- data/lib/datadog/symbol_database/uploader.rb +230 -0
- data/lib/datadog/symbol_database.rb +71 -0
- data/lib/datadog/tracing/analytics.rb +1 -1
- data/lib/datadog/tracing/buffer.rb +10 -10
- data/lib/datadog/tracing/component.rb +17 -17
- data/lib/datadog/tracing/configuration/dynamic.rb +6 -8
- data/lib/datadog/tracing/configuration/ext.rb +25 -5
- data/lib/datadog/tracing/configuration/settings.rb +125 -11
- data/lib/datadog/tracing/context.rb +2 -2
- data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +4 -1
- data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +4 -1
- data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +4 -1
- data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +4 -1
- data/lib/datadog/tracing/contrib/action_cable/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -1
- data/lib/datadog/tracing/contrib/action_mailer/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +29 -11
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +47 -12
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +3 -1
- data/lib/datadog/tracing/contrib/action_pack/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/action_pack/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
- data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +4 -1
- data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +6 -3
- data/lib/datadog/tracing/contrib/active_job/event.rb +8 -8
- data/lib/datadog/tracing/contrib/active_job/events/discard.rb +6 -3
- data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +6 -3
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +6 -3
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +6 -3
- data/lib/datadog/tracing/contrib/active_job/events/perform.rb +6 -3
- data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +6 -3
- data/lib/datadog/tracing/contrib/active_job/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
- data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +2 -2
- data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +4 -4
- data/lib/datadog/tracing/contrib/active_model_serializers/integration.rb +1 -2
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +3 -3
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +2 -2
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +7 -10
- data/lib/datadog/tracing/contrib/active_record/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/utils.rb +16 -16
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +15 -12
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +1 -4
- data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +13 -0
- data/lib/datadog/tracing/contrib/active_support/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +2 -1
- data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +8 -10
- data/lib/datadog/tracing/contrib/aws/ext.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +8 -10
- data/lib/datadog/tracing/contrib/aws/parsed_context.rb +3 -1
- data/lib/datadog/tracing/contrib/aws/patcher.rb +5 -1
- data/lib/datadog/tracing/contrib/aws/service/base.rb +2 -1
- data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/service/s3.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/service/sns.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/service/sqs.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/service/states.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/services.rb +7 -7
- data/lib/datadog/tracing/contrib/component.rb +3 -3
- data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +1 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +1 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +1 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +1 -1
- data/lib/datadog/tracing/contrib/configurable.rb +22 -7
- data/lib/datadog/tracing/contrib/configuration/resolver.rb +14 -4
- data/lib/datadog/tracing/contrib/configuration/resolvers/pattern_resolver.rb +4 -4
- data/lib/datadog/tracing/contrib/dalli/ext.rb +3 -2
- data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +1 -5
- data/lib/datadog/tracing/contrib/dalli/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/dalli/quantize.rb +1 -1
- data/lib/datadog/tracing/contrib/delayed_job/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +2 -0
- data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +1 -0
- data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +3 -2
- data/lib/datadog/tracing/contrib/elasticsearch/integration.rb +4 -4
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +47 -53
- data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +7 -7
- data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +7 -8
- data/lib/datadog/tracing/contrib/ethon/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/ethon/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +1 -8
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +16 -5
- data/lib/datadog/tracing/contrib/excon/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/excon/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/excon/middleware.rb +5 -9
- data/lib/datadog/tracing/contrib/ext.rb +6 -4
- data/lib/datadog/tracing/contrib/extensions.rb +28 -11
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +16 -9
- data/lib/datadog/tracing/contrib/faraday/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/faraday/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +7 -9
- data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +18 -15
- data/lib/datadog/tracing/contrib/grape/instrumentation.rb +13 -8
- data/lib/datadog/tracing/contrib/grape/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/grape/patcher.rb +6 -1
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
- data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/graphql/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/graphql/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +105 -63
- data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +11 -15
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +6 -10
- data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +1 -1
- data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/grpc/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/grpc.rb +1 -0
- data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/hanami/ext.rb +2 -2
- data/lib/datadog/tracing/contrib/hanami/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +2 -1
- data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +10 -11
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +16 -5
- data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +4 -4
- data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/http/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +9 -13
- data/lib/datadog/tracing/contrib/http/integration.rb +0 -2
- data/lib/datadog/tracing/contrib/http.rb +1 -0
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +16 -5
- data/lib/datadog/tracing/contrib/httpclient/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +10 -10
- data/lib/datadog/tracing/contrib/httpclient/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +16 -5
- data/lib/datadog/tracing/contrib/httprb/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +8 -12
- data/lib/datadog/tracing/contrib/httprb/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/event.rb +2 -1
- data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/join_group.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/leave_group.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/sync_group.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
- data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
- data/lib/datadog/tracing/contrib/kafka/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
- data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/karafka/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
- data/lib/datadog/tracing/contrib/karafka/monitor.rb +24 -13
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +39 -8
- data/lib/datadog/tracing/contrib/karafka.rb +1 -0
- data/lib/datadog/tracing/contrib/lograge/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/lograge/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +4 -2
- data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -1
- data/lib/datadog/tracing/contrib/mongodb/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/mongodb/parsers.rb +6 -6
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +11 -14
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +6 -0
- data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -1
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +19 -12
- data/lib/datadog/tracing/contrib/mysql2/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/opensearch/ext.rb +3 -2
- data/lib/datadog/tracing/contrib/opensearch/integration.rb +1 -2
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +64 -70
- data/lib/datadog/tracing/contrib/opensearch/quantize.rb +7 -7
- data/lib/datadog/tracing/contrib/patcher.rb +7 -9
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +6 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +3 -6
- data/lib/datadog/tracing/contrib/pg/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/presto/ext.rb +1 -1
- data/lib/datadog/tracing/contrib/presto/instrumentation.rb +9 -11
- data/lib/datadog/tracing/contrib/presto/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +1 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +13 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +6 -2
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +25 -1
- data/lib/datadog/tracing/contrib/que/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/que/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/que/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/que/tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/racecar/event.rb +2 -6
- data/lib/datadog/tracing/contrib/racecar/events/batch.rb +2 -2
- data/lib/datadog/tracing/contrib/racecar/events/consume.rb +1 -1
- data/lib/datadog/tracing/contrib/racecar/events/message.rb +2 -2
- data/lib/datadog/tracing/contrib/racecar/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +11 -1
- data/lib/datadog/tracing/contrib/rack/ext.rb +28 -0
- data/lib/datadog/tracing/contrib/rack/header_collection.rb +1 -1
- data/lib/datadog/tracing/contrib/rack/header_tagging.rb +55 -32
- data/lib/datadog/tracing/contrib/rack/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +83 -44
- data/lib/datadog/tracing/contrib/rack/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/rack/request_queue.rb +4 -3
- data/lib/datadog/tracing/contrib/rack/route_inference.rb +67 -0
- data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +126 -2
- data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +6 -3
- data/lib/datadog/tracing/contrib/rails/ext.rb +3 -1
- data/lib/datadog/tracing/contrib/rails/integration.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/log_injection.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/middlewares.rb +3 -3
- data/lib/datadog/tracing/contrib/rails/patcher.rb +4 -2
- data/lib/datadog/tracing/contrib/rails/runner.rb +64 -40
- data/lib/datadog/tracing/contrib/rake/instrumentation.rb +10 -8
- data/lib/datadog/tracing/contrib/rake/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +2 -2
- data/lib/datadog/tracing/contrib/redis/ext.rb +3 -2
- data/lib/datadog/tracing/contrib/redis/integration.rb +2 -2
- data/lib/datadog/tracing/contrib/redis/patcher.rb +4 -4
- data/lib/datadog/tracing/contrib/redis/quantize.rb +2 -2
- data/lib/datadog/tracing/contrib/redis/tags.rb +2 -7
- data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +6 -4
- data/lib/datadog/tracing/contrib/registerable.rb +11 -0
- data/lib/datadog/tracing/contrib/registry.rb +1 -1
- data/lib/datadog/tracing/contrib/resque/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/resque/resque_job.rb +2 -1
- data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/rest_client/ext.rb +4 -2
- data/lib/datadog/tracing/contrib/rest_client/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +8 -9
- data/lib/datadog/tracing/contrib/roda/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/roda/instrumentation.rb +8 -3
- data/lib/datadog/tracing/contrib/roda/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/semantic_logger/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/semantic_logger/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/sequel/database.rb +6 -5
- data/lib/datadog/tracing/contrib/sequel/dataset.rb +2 -1
- data/lib/datadog/tracing/contrib/sequel/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/sequel/utils.rb +1 -6
- data/lib/datadog/tracing/contrib/shoryuken/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/heartbeat.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/job_fetch.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/redis_info.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/scheduled_poller.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/stop.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +8 -4
- data/lib/datadog/tracing/contrib/sidekiq/utils.rb +1 -1
- data/lib/datadog/tracing/contrib/sidekiq.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/sinatra/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +40 -39
- data/lib/datadog/tracing/contrib/sneakers/integration.rb +15 -4
- data/lib/datadog/tracing/contrib/sneakers/tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/span_attribute_schema.rb +1 -1
- data/lib/datadog/tracing/contrib/status_range_matcher.rb +13 -1
- data/lib/datadog/tracing/contrib/stripe/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/stripe/request.rb +2 -2
- data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +1 -0
- data/lib/datadog/tracing/contrib/sucker_punch/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +6 -0
- data/lib/datadog/tracing/contrib/trilogy/ext.rb +1 -1
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -6
- data/lib/datadog/tracing/contrib/trilogy/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/utils/quantization/{hash.rb → hash_formatter.rb} +15 -13
- data/lib/datadog/tracing/contrib/utils/quantization/http.rb +6 -6
- data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +31 -0
- data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +50 -0
- data/lib/datadog/tracing/contrib/waterdrop/ext.rb +18 -0
- data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
- data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
- data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +49 -0
- data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
- data/lib/datadog/tracing/contrib/waterdrop.rb +42 -0
- data/lib/datadog/tracing/contrib.rb +9 -0
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +12 -4
- data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
- data/lib/datadog/tracing/distributed/baggage.rb +133 -12
- data/lib/datadog/tracing/distributed/datadog.rb +17 -16
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +11 -26
- data/lib/datadog/tracing/distributed/helpers.rb +1 -1
- data/lib/datadog/tracing/distributed/none.rb +4 -2
- data/lib/datadog/tracing/distributed/propagation.rb +39 -4
- data/lib/datadog/tracing/distributed/propagation_policy.rb +1 -1
- data/lib/datadog/tracing/distributed/trace_context.rb +107 -78
- data/lib/datadog/tracing/event.rb +5 -7
- data/lib/datadog/tracing/ext.rb +9 -0
- data/lib/datadog/tracing/flush.rb +1 -1
- data/lib/datadog/tracing/metadata/analytics.rb +1 -1
- data/lib/datadog/tracing/metadata/ext.rb +23 -1
- data/lib/datadog/tracing/metadata/tagging.rb +6 -6
- data/lib/datadog/tracing/pipeline/span_filter.rb +3 -1
- data/lib/datadog/tracing/pipeline/span_processor.rb +3 -1
- data/lib/datadog/tracing/pipeline.rb +2 -2
- data/lib/datadog/tracing/remote.rb +36 -13
- data/lib/datadog/tracing/sampling/ext.rb +2 -2
- data/lib/datadog/tracing/sampling/priority_sampler.rb +16 -1
- data/lib/datadog/tracing/sampling/rate_sampler.rb +8 -19
- data/lib/datadog/tracing/sampling/rule.rb +1 -1
- data/lib/datadog/tracing/sampling/rule_sampler.rb +77 -48
- data/lib/datadog/tracing/sampling/span/rule_parser.rb +3 -3
- data/lib/datadog/tracing/sampling/span/sampler.rb +0 -7
- data/lib/datadog/tracing/span.rb +2 -2
- data/lib/datadog/tracing/span_event.rb +12 -12
- data/lib/datadog/tracing/span_link.rb +12 -12
- data/lib/datadog/tracing/span_operation.rb +63 -19
- data/lib/datadog/tracing/sync_writer.rb +1 -2
- data/lib/datadog/tracing/trace_digest.rb +28 -23
- data/lib/datadog/tracing/trace_operation.rb +209 -98
- data/lib/datadog/tracing/trace_segment.rb +2 -2
- data/lib/datadog/tracing/tracer.rb +113 -52
- data/lib/datadog/tracing/transport/http/client.rb +12 -26
- data/lib/datadog/tracing/transport/http/traces.rb +4 -52
- data/lib/datadog/tracing/transport/http.rb +15 -9
- data/lib/datadog/tracing/transport/io/client.rb +11 -14
- data/lib/datadog/tracing/transport/io/traces.rb +28 -34
- data/lib/datadog/tracing/transport/statistics.rb +1 -1
- data/lib/datadog/tracing/transport/trace_formatter.rb +22 -0
- data/lib/datadog/tracing/transport/traces.rb +14 -82
- data/lib/datadog/tracing/workers.rb +4 -3
- data/lib/datadog/tracing/writer.rb +1 -1
- data/lib/datadog/tracing.rb +2 -2
- data/lib/datadog/version.rb +2 -2
- data/lib/datadog.rb +14 -0
- metadata +201 -40
- data/ext/datadog_profiling_native_extension/clock_id_noop.c +0 -21
- data/ext/libdatadog_api/macos_development.md +0 -26
- data/lib/datadog/appsec/assets/waf_rules/processors.json +0 -321
- data/lib/datadog/appsec/assets/waf_rules/scanners.json +0 -1023
- data/lib/datadog/appsec/configuration/settings.rb +0 -342
- data/lib/datadog/appsec/contrib/rails/ext.rb +0 -13
- data/lib/datadog/appsec/processor/rule_merger.rb +0 -171
- data/lib/datadog/appsec/processor.rb +0 -107
- data/lib/datadog/core/remote/transport/http/api.rb +0 -53
- data/lib/datadog/core/remote/transport/http/client.rb +0 -49
- data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
- data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
- data/lib/datadog/core/transport/http/api/spec.rb +0 -36
- data/lib/datadog/di/configuration/settings.rb +0 -212
- data/lib/datadog/di/transport/http/api.rb +0 -42
- data/lib/datadog/di/transport/http/client.rb +0 -47
- data/lib/datadog/error_tracking/configuration/settings.rb +0 -63
- data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
- data/lib/datadog/tracing/transport/http/api.rb +0 -44
- data/lib/datadog/tracing/workers/trace_writer.rb +0 -199
|
@@ -5,6 +5,18 @@
|
|
|
5
5
|
#include "libdatadog_helpers.h"
|
|
6
6
|
#include "time_helpers.h"
|
|
7
7
|
|
|
8
|
+
// note on calloc vs ruby_xcalloc use:
|
|
9
|
+
// * Whenever we're allocating memory after being called by the Ruby VM in a "regular" situation (e.g. initializer)
|
|
10
|
+
// we should use `ruby_xcalloc` to give the VM visibility into what we're doing + give it a chance to manage GC
|
|
11
|
+
// * BUT, when we're being called during a sample, being in the middle of an object allocation is a very special
|
|
12
|
+
// situation for the VM to be in, and we've found the hard way (e.g. https://bugs.ruby-lang.org/issues/20629 and
|
|
13
|
+
// https://github.com/DataDog/dd-trace-rb/pull/4240 ) that it can be easy to do things the VM didn't expect.
|
|
14
|
+
// * Thus, out of caution and to avoid future potential issues such as the ones above, whenever we allocate memory
|
|
15
|
+
// during **sampling** we use `calloc` instead of `ruby_xcalloc`. Note that we've never seen issues from using
|
|
16
|
+
// `ruby_xcalloc` at any time, so this is a **precaution** not a "we've seen it break". But it seems a harmless
|
|
17
|
+
// one to use.
|
|
18
|
+
// This applies to both heap_recorder.c and collectors_thread_context.c
|
|
19
|
+
|
|
8
20
|
// Minimum age (in GC generations) of heap objects we want to include in heap
|
|
9
21
|
// recorder iterations. Object with age 0 represent objects that have yet to undergo
|
|
10
22
|
// a GC and, thus, may just be noise/trash at instant of iteration and are usually not
|
|
@@ -24,80 +36,37 @@
|
|
|
24
36
|
|
|
25
37
|
// A compact representation of a stacktrace frame for a heap allocation.
|
|
26
38
|
typedef struct {
|
|
27
|
-
|
|
28
|
-
|
|
39
|
+
ddog_prof_ManagedStringId name;
|
|
40
|
+
ddog_prof_ManagedStringId filename;
|
|
29
41
|
int32_t line;
|
|
30
42
|
} heap_frame;
|
|
31
43
|
|
|
44
|
+
// We use memcmp/st_hash below to compare/hash an entire array of heap_frames, so want to make sure no padding is added
|
|
45
|
+
// We could define the structure to be packed, but that seems even weirder across compilers, and this seems more portable?
|
|
46
|
+
_Static_assert(
|
|
47
|
+
sizeof(heap_frame) == sizeof(ddog_prof_ManagedStringId) * 2 + sizeof(int32_t),
|
|
48
|
+
"Size of heap_frame does not match the sum of its members. Padding detected."
|
|
49
|
+
);
|
|
50
|
+
|
|
32
51
|
// A compact representation of a stacktrace for a heap allocation.
|
|
33
|
-
//
|
|
34
|
-
// We could use a ddog_prof_Slice_Location instead but it has a lot of
|
|
35
|
-
// unused fields. Because we have to keep these stacks around for at
|
|
36
|
-
// least the lifetime of the objects allocated therein, we would be
|
|
37
|
-
// incurring a non-negligible memory overhead for little purpose.
|
|
52
|
+
// Used to dedup heap allocation stacktraces across multiple objects sharing the same allocation location.
|
|
38
53
|
typedef struct {
|
|
54
|
+
// How many objects are currently tracked in object_records recorder for this heap record.
|
|
55
|
+
uint32_t num_tracked_objects;
|
|
56
|
+
|
|
39
57
|
uint16_t frames_len;
|
|
40
58
|
heap_frame frames[];
|
|
41
|
-
}
|
|
42
|
-
static
|
|
43
|
-
static void
|
|
44
|
-
static st_index_t heap_stack_hash(heap_stack*, st_index_t);
|
|
59
|
+
} heap_record;
|
|
60
|
+
static heap_record* heap_record_new(heap_recorder*, ddog_prof_Slice_Location);
|
|
61
|
+
static void heap_record_free(heap_recorder*, heap_record*, bool should_unintern);
|
|
45
62
|
|
|
46
63
|
#if MAX_FRAMES_LIMIT > UINT16_MAX
|
|
47
64
|
#error Frames len type not compatible with MAX_FRAMES_LIMIT
|
|
48
65
|
#endif
|
|
49
66
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
};
|
|
54
|
-
// This struct allows us to use two different types of stacks when
|
|
55
|
-
// interacting with a heap_record hash.
|
|
56
|
-
//
|
|
57
|
-
// The idea is that we'll always want to use heap_stack-keys when
|
|
58
|
-
// adding new entries to the hash since that's the compact stack
|
|
59
|
-
// representation we rely on internally.
|
|
60
|
-
//
|
|
61
|
-
// However, when querying for an existing heap record, we'd save a
|
|
62
|
-
// lot of allocations if we could query with the
|
|
63
|
-
// ddog_prof_Slice_Location we receive in our external API.
|
|
64
|
-
//
|
|
65
|
-
// To allow this interchange, we need a union and need to ensure
|
|
66
|
-
// that whatever shape of the union, the heap_record_key_cmp_st
|
|
67
|
-
// and heap_record_hash_st functions return the same results for
|
|
68
|
-
// equivalent stacktraces.
|
|
69
|
-
typedef struct {
|
|
70
|
-
enum heap_record_key_type type;
|
|
71
|
-
union {
|
|
72
|
-
// key never owns this if set
|
|
73
|
-
heap_stack *heap_stack;
|
|
74
|
-
// key never owns this if set
|
|
75
|
-
ddog_prof_Slice_Location *location_slice;
|
|
76
|
-
};
|
|
77
|
-
} heap_record_key;
|
|
78
|
-
static heap_record_key* heap_record_key_new(heap_stack*);
|
|
79
|
-
static void heap_record_key_free(heap_record_key*);
|
|
80
|
-
static int heap_record_key_cmp_st(st_data_t, st_data_t);
|
|
81
|
-
static st_index_t heap_record_key_hash_st(st_data_t);
|
|
82
|
-
static const struct st_hash_type st_hash_type_heap_record_key = {
|
|
83
|
-
heap_record_key_cmp_st,
|
|
84
|
-
heap_record_key_hash_st,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
// Need to implement these functions to support the location-slice based keys
|
|
88
|
-
static st_index_t ddog_location_hash(ddog_prof_Location, st_index_t seed);
|
|
89
|
-
static st_index_t ddog_location_slice_hash(ddog_prof_Slice_Location, st_index_t seed);
|
|
90
|
-
|
|
91
|
-
// A heap record is used for deduping heap allocation stacktraces across multiple
|
|
92
|
-
// objects sharing the same allocation location.
|
|
93
|
-
typedef struct {
|
|
94
|
-
// How many objects are currently tracked by the heap recorder for this heap record.
|
|
95
|
-
uint32_t num_tracked_objects;
|
|
96
|
-
// stack is owned by the associated record and gets cleaned up alongside it
|
|
97
|
-
heap_stack *stack;
|
|
98
|
-
} heap_record;
|
|
99
|
-
static heap_record* heap_record_new(heap_stack*);
|
|
100
|
-
static void heap_record_free(heap_record*);
|
|
67
|
+
static int heap_record_cmp_st(st_data_t, st_data_t);
|
|
68
|
+
static st_index_t heap_record_hash_st(st_data_t);
|
|
69
|
+
static const struct st_hash_type st_hash_type_heap_record = { .compare = heap_record_cmp_st, .hash = heap_record_hash_st };
|
|
101
70
|
|
|
102
71
|
// An object record is used for storing data about currently tracked live objects
|
|
103
72
|
typedef struct {
|
|
@@ -106,22 +75,35 @@ typedef struct {
|
|
|
106
75
|
live_object_data object_data;
|
|
107
76
|
} object_record;
|
|
108
77
|
static object_record* object_record_new(long, heap_record*, live_object_data);
|
|
109
|
-
static void object_record_free(object_record
|
|
110
|
-
static VALUE object_record_inspect(object_record*);
|
|
78
|
+
static void object_record_free(heap_recorder*, object_record*, bool should_unintern);
|
|
79
|
+
static VALUE object_record_inspect(heap_recorder*, object_record*);
|
|
111
80
|
static object_record SKIPPED_RECORD = {0};
|
|
112
81
|
|
|
82
|
+
// A pending recording is used to defer the object_id call on Ruby 4+
|
|
83
|
+
// where calling rb_obj_id during on_newobj_event is unsafe.
|
|
84
|
+
typedef struct {
|
|
85
|
+
VALUE object_ref;
|
|
86
|
+
heap_record *heap_record;
|
|
87
|
+
live_object_data object_data;
|
|
88
|
+
} pending_recording;
|
|
89
|
+
|
|
90
|
+
#define MAX_PENDING_RECORDINGS 256
|
|
91
|
+
|
|
113
92
|
struct heap_recorder {
|
|
114
93
|
// Config
|
|
115
94
|
// Whether the recorder should try to determine approximate sizes for tracked objects.
|
|
116
95
|
bool size_enabled;
|
|
117
96
|
uint sample_rate;
|
|
118
97
|
|
|
119
|
-
// Map[key:
|
|
120
|
-
// NOTE: We always use heap_record_key.type == HEAP_STACK for storage but support lookups
|
|
121
|
-
// via heap_record_key.type == LOCATION_SLICE to allow for allocation-free fast-paths.
|
|
98
|
+
// Map[key: heap_record*, record: nothing] (This is a set, basically)
|
|
122
99
|
// NOTE: This table is currently only protected by the GVL since we never interact with it
|
|
123
100
|
// outside the GVL.
|
|
124
|
-
// NOTE: This table has ownership of
|
|
101
|
+
// NOTE: This table has ownership of its heap_records.
|
|
102
|
+
//
|
|
103
|
+
// This is a cpu/memory trade-off: Maintaining the "heap_records" map means we spend extra CPU when sampling as we need
|
|
104
|
+
// to do de-duplication, but we reduce the memory footprint of the heap profiler.
|
|
105
|
+
// In the future, it may be worth revisiting if we can move this inside libdatadog: if libdatadog was able to track
|
|
106
|
+
// entire stacks for us, then we wouldn't need to do it on the Ruby side.
|
|
125
107
|
st_table *heap_records;
|
|
126
108
|
|
|
127
109
|
// Map[obj_id: long, record: object_record*]
|
|
@@ -132,6 +114,8 @@ struct heap_recorder {
|
|
|
132
114
|
//
|
|
133
115
|
// TODO: @ivoanjo We've evolved to actually never need to look up on object_records (we only insert and iterate),
|
|
134
116
|
// so right now this seems to be just a really really fancy self-resizing list/set.
|
|
117
|
+
// If we replace this with a list, we could record the latest id and compare it when inserting to make sure our
|
|
118
|
+
// assumption of ids never reused + always increasing always holds. (This as an alternative to checking for duplicates)
|
|
135
119
|
st_table *object_records;
|
|
136
120
|
|
|
137
121
|
// Map[obj_id: long, record: object_record*]
|
|
@@ -156,12 +140,28 @@ struct heap_recorder {
|
|
|
156
140
|
// Data for a heap recording that was started but not yet ended
|
|
157
141
|
object_record *active_recording;
|
|
158
142
|
|
|
159
|
-
//
|
|
143
|
+
// Pending recordings that need to be finalized after on_newobj_event completes.
|
|
144
|
+
// On Ruby 4+, we can't call rb_obj_id during the newobj event, so we store the
|
|
145
|
+
// VALUE reference here and finalize it via a postponed job.
|
|
146
|
+
pending_recording pending_recordings[MAX_PENDING_RECORDINGS];
|
|
147
|
+
// Temporary storage for the recording in progress, used between start and end
|
|
148
|
+
VALUE active_deferred_object;
|
|
149
|
+
live_object_data active_deferred_object_data;
|
|
150
|
+
uint16_t pending_recordings_count;
|
|
151
|
+
|
|
152
|
+
// Reusable arrays, implementing a flyweight pattern for things like iteration
|
|
153
|
+
#define REUSABLE_LOCATIONS_SIZE MAX_FRAMES_LIMIT
|
|
160
154
|
ddog_prof_Location *reusable_locations;
|
|
161
155
|
|
|
156
|
+
#define REUSABLE_FRAME_DETAILS_SIZE (2 * MAX_FRAMES_LIMIT) // because it'll be used for both function names AND file names)
|
|
157
|
+
ddog_prof_ManagedStringId *reusable_ids;
|
|
158
|
+
ddog_CharSlice *reusable_char_slices;
|
|
159
|
+
|
|
162
160
|
// Sampling state
|
|
163
161
|
uint num_recordings_skipped;
|
|
164
162
|
|
|
163
|
+
ddog_prof_ManagedStringStorage string_storage;
|
|
164
|
+
|
|
165
165
|
struct stats_last_update {
|
|
166
166
|
size_t objects_alive;
|
|
167
167
|
size_t objects_dead;
|
|
@@ -182,27 +182,35 @@ struct heap_recorder {
|
|
|
182
182
|
double ewma_objects_alive;
|
|
183
183
|
double ewma_objects_dead;
|
|
184
184
|
double ewma_objects_skipped;
|
|
185
|
+
|
|
186
|
+
unsigned long deferred_recordings_skipped_buffer_full;
|
|
187
|
+
unsigned long deferred_recordings_finalized;
|
|
185
188
|
} stats_lifetime;
|
|
186
189
|
};
|
|
187
190
|
|
|
188
|
-
struct
|
|
189
|
-
|
|
191
|
+
typedef struct {
|
|
192
|
+
heap_recorder *heap_recorder;
|
|
190
193
|
ddog_prof_Slice_Location locations;
|
|
191
|
-
};
|
|
194
|
+
} end_heap_allocation_args;
|
|
192
195
|
|
|
193
196
|
static heap_record* get_or_create_heap_record(heap_recorder*, ddog_prof_Slice_Location);
|
|
194
197
|
static void cleanup_heap_record_if_unused(heap_recorder*, heap_record*);
|
|
195
198
|
static void on_committed_object_record_cleanup(heap_recorder *heap_recorder, object_record *record);
|
|
196
|
-
static int
|
|
197
|
-
static int
|
|
199
|
+
static int st_heap_record_entry_free_no_unintern(st_data_t, st_data_t, st_data_t);
|
|
200
|
+
static int st_object_record_entry_free_no_unintern(st_data_t, st_data_t, st_data_t);
|
|
198
201
|
static int st_object_record_update(st_data_t, st_data_t, st_data_t);
|
|
199
202
|
static int st_object_records_iterate(st_data_t, st_data_t, st_data_t);
|
|
200
203
|
static int st_object_records_debug(st_data_t key, st_data_t value, st_data_t extra);
|
|
201
204
|
static int update_object_record_entry(st_data_t*, st_data_t*, st_data_t, int);
|
|
205
|
+
static void inc_tracked_objects_or_fail(heap_record *heap_record);
|
|
202
206
|
static void commit_recording(heap_recorder *, heap_record *, object_record *active_recording);
|
|
203
207
|
static VALUE end_heap_allocation_recording(VALUE end_heap_allocation_args);
|
|
204
208
|
static void heap_recorder_update(heap_recorder *heap_recorder, bool full_update);
|
|
205
209
|
static inline double ewma_stat(double previous, double current);
|
|
210
|
+
static void unintern_or_raise(heap_recorder *, ddog_prof_ManagedStringId);
|
|
211
|
+
static void unintern_all_or_raise(heap_recorder *recorder, ddog_prof_Slice_ManagedStringId ids);
|
|
212
|
+
static VALUE get_ruby_string_or_raise(heap_recorder*, ddog_prof_ManagedStringId);
|
|
213
|
+
static long obj_id_or_fail(VALUE obj);
|
|
206
214
|
|
|
207
215
|
// ==========================
|
|
208
216
|
// Heap Recorder External API
|
|
@@ -213,16 +221,20 @@ static inline double ewma_stat(double previous, double current);
|
|
|
213
221
|
// happens under the GVL.
|
|
214
222
|
//
|
|
215
223
|
// ==========================
|
|
216
|
-
heap_recorder* heap_recorder_new(
|
|
224
|
+
heap_recorder* heap_recorder_new(ddog_prof_ManagedStringStorage string_storage) {
|
|
217
225
|
heap_recorder *recorder = ruby_xcalloc(1, sizeof(heap_recorder));
|
|
218
226
|
|
|
219
|
-
recorder->heap_records = st_init_table(&
|
|
227
|
+
recorder->heap_records = st_init_table(&st_hash_type_heap_record);
|
|
220
228
|
recorder->object_records = st_init_numtable();
|
|
221
229
|
recorder->object_records_snapshot = NULL;
|
|
222
|
-
recorder->reusable_locations = ruby_xcalloc(
|
|
230
|
+
recorder->reusable_locations = ruby_xcalloc(REUSABLE_LOCATIONS_SIZE, sizeof(ddog_prof_Location));
|
|
231
|
+
recorder->reusable_ids = ruby_xcalloc(REUSABLE_FRAME_DETAILS_SIZE, sizeof(ddog_prof_ManagedStringId));
|
|
232
|
+
recorder->reusable_char_slices = ruby_xcalloc(REUSABLE_FRAME_DETAILS_SIZE, sizeof(ddog_CharSlice));
|
|
223
233
|
recorder->active_recording = NULL;
|
|
224
234
|
recorder->size_enabled = true;
|
|
225
235
|
recorder->sample_rate = 1; // By default do no sampling on top of what allocation profiling already does
|
|
236
|
+
recorder->string_storage = string_storage;
|
|
237
|
+
recorder->active_deferred_object = Qnil;
|
|
226
238
|
|
|
227
239
|
return recorder;
|
|
228
240
|
}
|
|
@@ -238,20 +250,29 @@ void heap_recorder_free(heap_recorder *heap_recorder) {
|
|
|
238
250
|
heap_recorder_finish_iteration(heap_recorder);
|
|
239
251
|
}
|
|
240
252
|
|
|
253
|
+
// NOTE: We don't unintern the strings referenced by the records we're about to free, thus we use
|
|
254
|
+
// `..._no_unintern` and `should_unintern: false`. This is intentional: `heap_recorder_free` is only ever called as part of
|
|
255
|
+
// tearing down the entire stack recorder, and the caller drops the whole managed string storage right after
|
|
256
|
+
// (see `stack_recorder_typed_data_free`), so there's no need to spend effort updating the managed string table.
|
|
257
|
+
// Crucially, this also keeps us from crashing: this code runs from the stack recorder's GC free callback, and
|
|
258
|
+
// because uninterning can fail, we can't raise exceptions in the middle of a dfree.
|
|
259
|
+
|
|
241
260
|
// Clean-up all object records
|
|
242
|
-
st_foreach(heap_recorder->object_records,
|
|
261
|
+
st_foreach(heap_recorder->object_records, st_object_record_entry_free_no_unintern, (st_data_t) heap_recorder);
|
|
243
262
|
st_free_table(heap_recorder->object_records);
|
|
244
263
|
|
|
245
264
|
// Clean-up all heap records (this includes those only referred to by queued_samples)
|
|
246
|
-
st_foreach(heap_recorder->heap_records,
|
|
265
|
+
st_foreach(heap_recorder->heap_records, st_heap_record_entry_free_no_unintern, (st_data_t) heap_recorder);
|
|
247
266
|
st_free_table(heap_recorder->heap_records);
|
|
248
267
|
|
|
249
268
|
if (heap_recorder->active_recording != NULL && heap_recorder->active_recording != &SKIPPED_RECORD) {
|
|
250
269
|
// If there's a partial object record, clean it up as well
|
|
251
|
-
object_record_free(heap_recorder->active_recording);
|
|
270
|
+
object_record_free(heap_recorder, heap_recorder->active_recording, false);
|
|
252
271
|
}
|
|
253
272
|
|
|
254
273
|
ruby_xfree(heap_recorder->reusable_locations);
|
|
274
|
+
ruby_xfree(heap_recorder->reusable_ids);
|
|
275
|
+
ruby_xfree(heap_recorder->reusable_char_slices);
|
|
255
276
|
|
|
256
277
|
ruby_xfree(heap_recorder);
|
|
257
278
|
}
|
|
@@ -270,7 +291,7 @@ void heap_recorder_set_sample_rate(heap_recorder *heap_recorder, int sample_rate
|
|
|
270
291
|
}
|
|
271
292
|
|
|
272
293
|
if (sample_rate <= 0) {
|
|
273
|
-
|
|
294
|
+
raise_error(rb_eArgError, "Heap sample rate must be a positive integer value but was %d", sample_rate);
|
|
274
295
|
}
|
|
275
296
|
|
|
276
297
|
heap_recorder->sample_rate = sample_rate;
|
|
@@ -292,7 +313,7 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder) {
|
|
|
292
313
|
// simply be noticed on next heap_recorder_prepare_iteration.
|
|
293
314
|
//
|
|
294
315
|
// There is one small caveat though: fork only preserves one thread and in a Ruby app, that
|
|
295
|
-
// will be the thread holding
|
|
316
|
+
// will be the thread holding the GVL. Since we support iteration on the heap recorder
|
|
296
317
|
// outside of the GVL, any state specific to that interaction may be inconsistent after fork
|
|
297
318
|
// (e.g. an acquired lock for thread safety). Iteration operates on object_records_snapshot
|
|
298
319
|
// though and that one will be updated on next heap_recorder_prepare_iteration so we really
|
|
@@ -305,88 +326,144 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder) {
|
|
|
305
326
|
heap_recorder->stats_lifetime = (struct stats_lifetime) {0};
|
|
306
327
|
}
|
|
307
328
|
|
|
308
|
-
|
|
329
|
+
bool start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice alloc_class) {
|
|
309
330
|
if (heap_recorder == NULL) {
|
|
310
|
-
return;
|
|
331
|
+
return false;
|
|
311
332
|
}
|
|
312
333
|
|
|
313
334
|
if (heap_recorder->active_recording != NULL) {
|
|
314
|
-
|
|
335
|
+
raise_error(rb_eRuntimeError, "Detected consecutive heap allocation recording starts without end.");
|
|
315
336
|
}
|
|
316
337
|
|
|
317
338
|
if (++heap_recorder->num_recordings_skipped < heap_recorder->sample_rate ||
|
|
318
339
|
#ifdef NO_IMEMO_OBJECT_ID
|
|
319
|
-
// On Ruby
|
|
340
|
+
// On Ruby 4, we can't ask the object_id from IMEMOs (https://github.com/ruby/ruby/pull/13347)
|
|
320
341
|
RB_BUILTIN_TYPE(new_obj) == RUBY_T_IMEMO
|
|
321
342
|
#else
|
|
322
343
|
false
|
|
323
344
|
#endif
|
|
345
|
+
// If we got really unlucky and an allocation showed up during an update (because it triggered an allocation
|
|
346
|
+
// directly OR because the GVL got released in the middle of an update), let's skip this sample as well.
|
|
347
|
+
// See notes on `heap_recorder_update` for details.
|
|
348
|
+
|| heap_recorder->updating
|
|
324
349
|
) {
|
|
325
350
|
heap_recorder->active_recording = &SKIPPED_RECORD;
|
|
326
|
-
return;
|
|
351
|
+
return false;
|
|
327
352
|
}
|
|
328
353
|
|
|
329
|
-
|
|
354
|
+
bool needs_after_allocation = false;
|
|
330
355
|
|
|
331
|
-
|
|
332
|
-
if
|
|
333
|
-
|
|
356
|
+
#ifdef USE_DEFERRED_HEAP_ALLOCATION_RECORDING
|
|
357
|
+
// Skip if we've hit the pending recordings limit or if there's already a deferred object being recorded
|
|
358
|
+
if (heap_recorder->pending_recordings_count >= MAX_PENDING_RECORDINGS) {
|
|
359
|
+
heap_recorder->stats_lifetime.deferred_recordings_skipped_buffer_full++;
|
|
360
|
+
heap_recorder->active_recording = &SKIPPED_RECORD;
|
|
361
|
+
return true; // If the buffer is full, we keep asking for a callback (see `needs_after_allocation` below)
|
|
362
|
+
} else {
|
|
363
|
+
// The intuition here is: We start by asking for an `after_allocation` callback when the buffer is about to go
|
|
364
|
+
// from empty -> non-empty, because this is going to be mapped onto a postponed job, so after it gets queued once
|
|
365
|
+
// it doesn't seem worth it to keep spamming requests.
|
|
366
|
+
//
|
|
367
|
+
// Yet, if for some reason the postponed job doesn't flush the pending list (or if e.g. it ran with `during_sample == true` and thus
|
|
368
|
+
// was skipped) we need to have some mechanism to recover -- and so if the buffer starts accumulating too much we
|
|
369
|
+
// start always requesting the callback to happen so that we eventually flush the buffer.
|
|
370
|
+
needs_after_allocation =
|
|
371
|
+
heap_recorder->pending_recordings_count == 0 || heap_recorder->pending_recordings_count >= (MAX_PENDING_RECORDINGS / 2);
|
|
334
372
|
}
|
|
373
|
+
#endif
|
|
335
374
|
|
|
336
|
-
heap_recorder->
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
375
|
+
heap_recorder->num_recordings_skipped = 0;
|
|
376
|
+
|
|
377
|
+
live_object_data object_data = (live_object_data) {
|
|
378
|
+
.weight = weight * heap_recorder->sample_rate,
|
|
379
|
+
.class = intern_or_raise(heap_recorder->string_storage, alloc_class),
|
|
380
|
+
.alloc_gen = rb_gc_count(),
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
#ifdef USE_DEFERRED_HEAP_ALLOCATION_RECORDING
|
|
384
|
+
// On Ruby 4+, we can't call rb_obj_id during on_newobj_event as it mutates the object.
|
|
385
|
+
// Instead, we store the VALUE reference and will get the object_id later via a postponed job.
|
|
386
|
+
// active_deferred_object != Qnil indicates we're in deferred mode.
|
|
387
|
+
heap_recorder->active_deferred_object = new_obj;
|
|
388
|
+
heap_recorder->active_deferred_object_data = object_data;
|
|
389
|
+
#else
|
|
390
|
+
heap_recorder->active_recording = object_record_new(obj_id_or_fail(new_obj), NULL, object_data);
|
|
391
|
+
#endif
|
|
392
|
+
|
|
393
|
+
return needs_after_allocation;
|
|
345
394
|
}
|
|
346
395
|
|
|
347
396
|
// end_heap_allocation_recording_with_rb_protect gets called while the stack_recorder is holding one of the profile
|
|
348
397
|
// locks. To enable us to correctly unlock the profile on exception, we wrap the call to end_heap_allocation_recording
|
|
349
398
|
// with an rb_protect.
|
|
350
399
|
__attribute__((warn_unused_result))
|
|
351
|
-
int end_heap_allocation_recording_with_rb_protect(
|
|
400
|
+
int end_heap_allocation_recording_with_rb_protect(heap_recorder *heap_recorder, ddog_prof_Slice_Location locations) {
|
|
352
401
|
if (heap_recorder == NULL) {
|
|
353
402
|
return 0;
|
|
354
403
|
}
|
|
404
|
+
if (heap_recorder->active_recording == &SKIPPED_RECORD) {
|
|
405
|
+
// Short circuit, in this case there's nothing to be done
|
|
406
|
+
heap_recorder->active_recording = NULL;
|
|
407
|
+
return 0;
|
|
408
|
+
}
|
|
355
409
|
|
|
356
410
|
int exception_state;
|
|
357
|
-
|
|
411
|
+
end_heap_allocation_args args = {
|
|
358
412
|
.heap_recorder = heap_recorder,
|
|
359
413
|
.locations = locations,
|
|
360
414
|
};
|
|
361
|
-
rb_protect(end_heap_allocation_recording, (VALUE) &
|
|
415
|
+
rb_protect(end_heap_allocation_recording, (VALUE) &args, &exception_state);
|
|
362
416
|
return exception_state;
|
|
363
417
|
}
|
|
364
418
|
|
|
365
|
-
static VALUE end_heap_allocation_recording(VALUE
|
|
366
|
-
|
|
419
|
+
static VALUE end_heap_allocation_recording(VALUE protect_args) {
|
|
420
|
+
end_heap_allocation_args *args = (end_heap_allocation_args *) protect_args;
|
|
367
421
|
|
|
368
|
-
|
|
422
|
+
heap_recorder *heap_recorder = args->heap_recorder;
|
|
369
423
|
ddog_prof_Slice_Location locations = args->locations;
|
|
370
424
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
// and not end up with a still active recording. the local active_recording still holds the
|
|
379
|
-
// data required for committing though.
|
|
380
|
-
heap_recorder->active_recording = NULL;
|
|
425
|
+
#ifdef USE_DEFERRED_HEAP_ALLOCATION_RECORDING
|
|
426
|
+
if (heap_recorder->active_deferred_object == Qnil) {
|
|
427
|
+
// Recording ended without having been started?
|
|
428
|
+
raise_error(rb_eRuntimeError, "Ended a heap recording that was not started");
|
|
429
|
+
}
|
|
430
|
+
#else
|
|
431
|
+
object_record *active_recording = heap_recorder->active_recording;
|
|
381
432
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
433
|
+
if (active_recording == NULL) {
|
|
434
|
+
// Recording ended without having been started?
|
|
435
|
+
raise_error(rb_eRuntimeError, "Ended a heap recording that was not started");
|
|
436
|
+
}
|
|
437
|
+
// From now on, mark the global active recording as invalid so we can short-circuit at any point
|
|
438
|
+
// and not end up with a still active recording. the local active_recording still holds the
|
|
439
|
+
// data required for committing though.
|
|
440
|
+
heap_recorder->active_recording = NULL;
|
|
441
|
+
|
|
442
|
+
if (active_recording == &SKIPPED_RECORD) {
|
|
443
|
+
raise_error(
|
|
444
|
+
rb_eRuntimeError,
|
|
445
|
+
"BUG: end_heap_allocation_recording should never observe SKIPPED_RECORDING because " \
|
|
446
|
+
"end_heap_allocation_recording_with_rb_protect is supposed to test for it directly"
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
#endif
|
|
385
450
|
|
|
386
451
|
heap_record *heap_record = get_or_create_heap_record(heap_recorder, locations);
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
452
|
+
inc_tracked_objects_or_fail(heap_record);
|
|
453
|
+
|
|
454
|
+
#ifdef USE_DEFERRED_HEAP_ALLOCATION_RECORDING
|
|
455
|
+
// Commit is delayed, so we need to record all we'll need for it
|
|
456
|
+
pending_recording *pending = &heap_recorder->pending_recordings[heap_recorder->pending_recordings_count++];
|
|
457
|
+
pending->object_ref = heap_recorder->active_deferred_object;
|
|
458
|
+
pending->heap_record = heap_record;
|
|
459
|
+
pending->object_data = heap_recorder->active_deferred_object_data;
|
|
460
|
+
|
|
461
|
+
heap_recorder->active_deferred_object = Qnil;
|
|
462
|
+
heap_recorder->active_deferred_object_data = (live_object_data) {0};
|
|
463
|
+
#else
|
|
464
|
+
// And then commit the new allocation
|
|
465
|
+
commit_recording(heap_recorder, heap_record, active_recording);
|
|
466
|
+
#endif
|
|
390
467
|
|
|
391
468
|
return Qnil;
|
|
392
469
|
}
|
|
@@ -399,17 +476,72 @@ void heap_recorder_update_young_objects(heap_recorder *heap_recorder) {
|
|
|
399
476
|
heap_recorder_update(heap_recorder, /* full_update: */ false);
|
|
400
477
|
}
|
|
401
478
|
|
|
402
|
-
|
|
403
|
-
if (heap_recorder
|
|
404
|
-
|
|
479
|
+
void heap_recorder_finalize_pending_recordings(heap_recorder *heap_recorder) {
|
|
480
|
+
if (heap_recorder == NULL) {
|
|
481
|
+
return; // Nothing to do
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
uint count = heap_recorder->pending_recordings_count;
|
|
485
|
+
if (count == 0) {
|
|
486
|
+
return; // Nothing to do
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
heap_recorder->stats_lifetime.deferred_recordings_finalized += count;
|
|
490
|
+
|
|
491
|
+
for (uint i = 0; i < count; i++) {
|
|
492
|
+
pending_recording *pending = &heap_recorder->pending_recordings[i];
|
|
493
|
+
|
|
494
|
+
// This is the step we couldn't do during the original sample call -- we're now expected to be called in a context
|
|
495
|
+
// where it's finally safe to call this
|
|
496
|
+
long obj_id = obj_id_or_fail(pending->object_ref);
|
|
497
|
+
|
|
498
|
+
// Create the object record now that we have the object_id
|
|
499
|
+
object_record *record = object_record_new(obj_id, pending->heap_record, pending->object_data);
|
|
500
|
+
|
|
501
|
+
commit_recording(heap_recorder, pending->heap_record, record);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
heap_recorder->pending_recordings_count = 0;
|
|
505
|
+
}
|
|
405
506
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
507
|
+
// Mark pending recordings to prevent GC from collecting the objects
|
|
508
|
+
// while they're waiting to be finalized
|
|
509
|
+
void heap_recorder_mark_pending_recordings(heap_recorder *heap_recorder) {
|
|
510
|
+
if (heap_recorder == NULL) {
|
|
410
511
|
return;
|
|
411
512
|
}
|
|
412
513
|
|
|
514
|
+
for (uint i = 0; i < heap_recorder->pending_recordings_count; i++) {
|
|
515
|
+
rb_gc_mark(heap_recorder->pending_recordings[i].object_ref);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
rb_gc_mark(heap_recorder->active_deferred_object);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// NOTE: This function needs and assumes it gets called with the GVL being held.
|
|
522
|
+
// But importantly **some of the operations inside `st_object_record_update` may cause a thread switch**,
|
|
523
|
+
// so we can't assume a single update happens in a single "atomic" step -- other threads may get some running time
|
|
524
|
+
// in the meanwhile.
|
|
525
|
+
static void heap_recorder_update(heap_recorder *heap_recorder, bool full_update) {
|
|
526
|
+
if (heap_recorder->updating) {
|
|
527
|
+
if (full_update) {
|
|
528
|
+
// There's another thread that's already doing an update :(
|
|
529
|
+
//
|
|
530
|
+
// Because there's a lock on the `StackRecorder` (see @no_concurrent_serialize_mutex) then it's not possible that
|
|
531
|
+
// the other update is a full update.
|
|
532
|
+
// Thus we expect is happening is that the GVL got released by the other thread in the middle of a non-full update
|
|
533
|
+
// and the scheduler thread decided now was a great time to serialize the profile.
|
|
534
|
+
//
|
|
535
|
+
// So, let's yield the time on the current thread until Ruby goes back to the other thread doing the update and
|
|
536
|
+
// it finishes cleanly.
|
|
537
|
+
while (heap_recorder->updating) { rb_thread_schedule(); }
|
|
538
|
+
} else {
|
|
539
|
+
// Non-full updates are optional, so let's walk away
|
|
540
|
+
heap_recorder->stats_lifetime.updates_skipped_concurrent++;
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
413
545
|
if (heap_recorder->object_records_snapshot != NULL) {
|
|
414
546
|
// While serialization is happening, it runs without the GVL and uses the object_records_snapshot.
|
|
415
547
|
// Although we iterate on a snapshot of object_records, these records point to other data that has not been
|
|
@@ -474,14 +606,14 @@ void heap_recorder_prepare_iteration(heap_recorder *heap_recorder) {
|
|
|
474
606
|
|
|
475
607
|
if (heap_recorder->object_records_snapshot != NULL) {
|
|
476
608
|
// we could trivially handle this but we raise to highlight and catch unexpected usages.
|
|
477
|
-
|
|
609
|
+
raise_error(rb_eRuntimeError, "New heap recorder iteration prepared without the previous one having been finished.");
|
|
478
610
|
}
|
|
479
611
|
|
|
480
612
|
heap_recorder_update(heap_recorder, /* full_update: */ true);
|
|
481
613
|
|
|
482
614
|
heap_recorder->object_records_snapshot = st_copy(heap_recorder->object_records);
|
|
483
615
|
if (heap_recorder->object_records_snapshot == NULL) {
|
|
484
|
-
|
|
616
|
+
raise_error(rb_eRuntimeError, "Failed to create heap snapshot.");
|
|
485
617
|
}
|
|
486
618
|
}
|
|
487
619
|
|
|
@@ -492,7 +624,7 @@ void heap_recorder_finish_iteration(heap_recorder *heap_recorder) {
|
|
|
492
624
|
|
|
493
625
|
if (heap_recorder->object_records_snapshot == NULL) {
|
|
494
626
|
// we could trivially handle this but we raise to highlight and catch unexpected usages.
|
|
495
|
-
|
|
627
|
+
raise_error(rb_eRuntimeError, "Heap recorder iteration finished without having been prepared.");
|
|
496
628
|
}
|
|
497
629
|
|
|
498
630
|
st_free_table(heap_recorder->object_records_snapshot);
|
|
@@ -534,20 +666,21 @@ bool heap_recorder_for_each_live_object(
|
|
|
534
666
|
|
|
535
667
|
VALUE heap_recorder_state_snapshot(heap_recorder *heap_recorder) {
|
|
536
668
|
VALUE arguments[] = {
|
|
537
|
-
ID2SYM(rb_intern("num_object_records")), /* => */
|
|
538
|
-
ID2SYM(rb_intern("num_heap_records")), /* => */
|
|
669
|
+
ID2SYM(rb_intern("num_object_records")), /* => */ ULONG2NUM(heap_recorder->object_records->num_entries),
|
|
670
|
+
ID2SYM(rb_intern("num_heap_records")), /* => */ ULONG2NUM(heap_recorder->heap_records->num_entries),
|
|
671
|
+
ID2SYM(rb_intern("pending_recordings_count")), /* => */ ULONG2NUM(heap_recorder->pending_recordings_count),
|
|
539
672
|
|
|
540
673
|
// Stats as of last update
|
|
541
|
-
ID2SYM(rb_intern("last_update_objects_alive")), /* => */
|
|
542
|
-
ID2SYM(rb_intern("last_update_objects_dead")), /* => */
|
|
543
|
-
ID2SYM(rb_intern("last_update_objects_skipped")), /* => */
|
|
544
|
-
ID2SYM(rb_intern("last_update_objects_frozen")), /* => */
|
|
674
|
+
ID2SYM(rb_intern("last_update_objects_alive")), /* => */ ULONG2NUM(heap_recorder->stats_last_update.objects_alive),
|
|
675
|
+
ID2SYM(rb_intern("last_update_objects_dead")), /* => */ ULONG2NUM(heap_recorder->stats_last_update.objects_dead),
|
|
676
|
+
ID2SYM(rb_intern("last_update_objects_skipped")), /* => */ ULONG2NUM(heap_recorder->stats_last_update.objects_skipped),
|
|
677
|
+
ID2SYM(rb_intern("last_update_objects_frozen")), /* => */ ULONG2NUM(heap_recorder->stats_last_update.objects_frozen),
|
|
545
678
|
|
|
546
679
|
// Lifetime stats
|
|
547
|
-
ID2SYM(rb_intern("lifetime_updates_successful")), /* => */
|
|
548
|
-
ID2SYM(rb_intern("lifetime_updates_skipped_concurrent")), /* => */
|
|
549
|
-
ID2SYM(rb_intern("lifetime_updates_skipped_gcgen")), /* => */
|
|
550
|
-
ID2SYM(rb_intern("lifetime_updates_skipped_time")), /* => */
|
|
680
|
+
ID2SYM(rb_intern("lifetime_updates_successful")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.updates_successful),
|
|
681
|
+
ID2SYM(rb_intern("lifetime_updates_skipped_concurrent")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.updates_skipped_concurrent),
|
|
682
|
+
ID2SYM(rb_intern("lifetime_updates_skipped_gcgen")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.updates_skipped_gcgen),
|
|
683
|
+
ID2SYM(rb_intern("lifetime_updates_skipped_time")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.updates_skipped_time),
|
|
551
684
|
ID2SYM(rb_intern("lifetime_ewma_young_objects_alive")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_young_objects_alive),
|
|
552
685
|
ID2SYM(rb_intern("lifetime_ewma_young_objects_dead")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_young_objects_dead),
|
|
553
686
|
// Note: Here "young" refers to the young update; objects skipped includes non-young objects
|
|
@@ -555,61 +688,54 @@ VALUE heap_recorder_state_snapshot(heap_recorder *heap_recorder) {
|
|
|
555
688
|
ID2SYM(rb_intern("lifetime_ewma_objects_alive")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_objects_alive),
|
|
556
689
|
ID2SYM(rb_intern("lifetime_ewma_objects_dead")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_objects_dead),
|
|
557
690
|
ID2SYM(rb_intern("lifetime_ewma_objects_skipped")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_objects_skipped),
|
|
691
|
+
|
|
692
|
+
ID2SYM(rb_intern("lifetime_deferred_recordings_skipped_buffer_full")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.deferred_recordings_skipped_buffer_full),
|
|
693
|
+
ID2SYM(rb_intern("lifetime_deferred_recordings_finalized")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.deferred_recordings_finalized),
|
|
558
694
|
};
|
|
559
695
|
VALUE hash = rb_hash_new();
|
|
560
696
|
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(hash, arguments[i], arguments[i+1]);
|
|
697
|
+
|
|
561
698
|
return hash;
|
|
562
699
|
}
|
|
563
700
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
.heap_stack = stack,
|
|
569
|
-
};
|
|
570
|
-
heap_record_key location_based_key = (heap_record_key) {
|
|
571
|
-
.type = LOCATION_SLICE,
|
|
572
|
-
.location_slice = &locations,
|
|
573
|
-
};
|
|
574
|
-
|
|
575
|
-
st_index_t stack_hash = heap_record_key_hash_st((st_data_t) &stack_based_key);
|
|
576
|
-
st_index_t location_hash = heap_record_key_hash_st((st_data_t) &location_based_key);
|
|
577
|
-
|
|
578
|
-
heap_stack_free(stack);
|
|
579
|
-
|
|
580
|
-
if (stack_hash != location_hash) {
|
|
581
|
-
rb_raise(rb_eRuntimeError, "Heap record key hashes built from the same locations differ. stack_based_hash=%"PRI_VALUE_PREFIX"u location_based_hash=%"PRI_VALUE_PREFIX"u", stack_hash, location_hash);
|
|
582
|
-
}
|
|
583
|
-
}
|
|
701
|
+
typedef struct {
|
|
702
|
+
heap_recorder *recorder;
|
|
703
|
+
VALUE debug_ary;
|
|
704
|
+
} debug_context;
|
|
584
705
|
|
|
585
706
|
VALUE heap_recorder_testonly_debug(heap_recorder *heap_recorder) {
|
|
586
707
|
if (heap_recorder == NULL) {
|
|
587
|
-
|
|
708
|
+
raise_error(rb_eArgError, "heap_recorder is NULL");
|
|
588
709
|
}
|
|
589
710
|
|
|
590
|
-
VALUE
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
rb_str_catf(debug_str, "state snapshot: %"PRIsVALUE"\n------\n", heap_recorder_state_snapshot(heap_recorder));
|
|
711
|
+
VALUE debug_ary = rb_ary_new();
|
|
712
|
+
debug_context context = (debug_context) {.recorder = heap_recorder, .debug_ary = debug_ary};
|
|
713
|
+
st_foreach(heap_recorder->object_records, st_object_records_debug, (st_data_t) &context);
|
|
594
714
|
|
|
595
|
-
return
|
|
715
|
+
return rb_ary_new_from_args(2,
|
|
716
|
+
rb_ary_new_from_args(2, ID2SYM(rb_intern("records")), debug_ary),
|
|
717
|
+
rb_ary_new_from_args(2, ID2SYM(rb_intern("state")), heap_recorder_state_snapshot(heap_recorder))
|
|
718
|
+
);
|
|
596
719
|
}
|
|
597
720
|
|
|
598
721
|
// ==========================
|
|
599
722
|
// Heap Recorder Internal API
|
|
600
723
|
// ==========================
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
heap_record_free((heap_record *)
|
|
724
|
+
// NOTE: Only expected to be used from heap_recorder_free, which will separately destroy the string table
|
|
725
|
+
static int st_heap_record_entry_free_no_unintern(st_data_t key, DDTRACE_UNUSED st_data_t value, st_data_t extra_arg) {
|
|
726
|
+
heap_recorder *recorder = (heap_recorder *) extra_arg;
|
|
727
|
+
heap_record_free(recorder, (heap_record *) key, false);
|
|
605
728
|
return ST_DELETE;
|
|
606
729
|
}
|
|
607
730
|
|
|
608
|
-
|
|
609
|
-
|
|
731
|
+
// NOTE: Only expected to be used from heap_recorder_free, which will separately destroy the string table
|
|
732
|
+
static int st_object_record_entry_free_no_unintern(DDTRACE_UNUSED st_data_t key, st_data_t value, st_data_t extra_arg) {
|
|
733
|
+
heap_recorder *recorder = (heap_recorder *) extra_arg;
|
|
734
|
+
object_record_free(recorder, (object_record *) value, false);
|
|
610
735
|
return ST_DELETE;
|
|
611
736
|
}
|
|
612
737
|
|
|
738
|
+
// NOTE: Some operations inside this function can cause the GVL to be released! Plan accordingly.
|
|
613
739
|
static int st_object_record_update(st_data_t key, st_data_t value, st_data_t extra_arg) {
|
|
614
740
|
long obj_id = (long) key;
|
|
615
741
|
object_record *record = (object_record*) value;
|
|
@@ -635,7 +761,7 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
|
|
|
635
761
|
return ST_CONTINUE;
|
|
636
762
|
}
|
|
637
763
|
|
|
638
|
-
if (!ruby_ref_from_id(LONG2NUM(obj_id), &ref)) {
|
|
764
|
+
if (!ruby_ref_from_id(LONG2NUM(obj_id), &ref)) { // Note: This function call can cause the GVL to be released
|
|
639
765
|
// Id no longer associated with a valid ref. Need to delete this object record!
|
|
640
766
|
on_committed_object_record_cleanup(recorder, record);
|
|
641
767
|
recorder->stats_last_update.objects_dead++;
|
|
@@ -651,7 +777,8 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
|
|
|
651
777
|
) {
|
|
652
778
|
// if we were asked to update sizes and this object was not already seen as being frozen,
|
|
653
779
|
// update size again.
|
|
654
|
-
record->object_data.size = ruby_obj_memsize_of(ref);
|
|
780
|
+
record->object_data.size = ruby_obj_memsize_of(ref); // Note: This function call can cause the GVL to be released... maybe?
|
|
781
|
+
// (With T_DATA for instance, since it can be a custom method supplied by extensions)
|
|
655
782
|
// Check if it's now frozen so we skip a size update next time
|
|
656
783
|
record->object_data.is_frozen = RB_OBJ_FROZEN(ref);
|
|
657
784
|
}
|
|
@@ -671,7 +798,7 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
|
|
|
671
798
|
// WARN: This can get called outside the GVL. NO HEAP ALLOCATIONS OR EXCEPTIONS ARE ALLOWED.
|
|
672
799
|
static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t value, st_data_t extra) {
|
|
673
800
|
object_record *record = (object_record*) value;
|
|
674
|
-
const
|
|
801
|
+
const heap_record *stack = record->heap_record;
|
|
675
802
|
iteration_context *context = (iteration_context*) extra;
|
|
676
803
|
|
|
677
804
|
const heap_recorder *recorder = context->heap_recorder;
|
|
@@ -687,8 +814,10 @@ static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t val
|
|
|
687
814
|
locations[i] = (ddog_prof_Location) {
|
|
688
815
|
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C(""), .build_id_id = {}},
|
|
689
816
|
.function = {
|
|
690
|
-
.name =
|
|
691
|
-
.
|
|
817
|
+
.name = DDOG_CHARSLICE_C(""),
|
|
818
|
+
.name_id = frame->name,
|
|
819
|
+
.filename = DDOG_CHARSLICE_C(""),
|
|
820
|
+
.filename_id = frame->filename,
|
|
692
821
|
},
|
|
693
822
|
.line = frame->line,
|
|
694
823
|
};
|
|
@@ -707,11 +836,11 @@ static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t val
|
|
|
707
836
|
}
|
|
708
837
|
|
|
709
838
|
static int st_object_records_debug(DDTRACE_UNUSED st_data_t key, st_data_t value, st_data_t extra) {
|
|
710
|
-
|
|
839
|
+
debug_context *context = (debug_context*) extra;
|
|
711
840
|
|
|
712
841
|
object_record *record = (object_record*) value;
|
|
713
842
|
|
|
714
|
-
|
|
843
|
+
rb_ary_push(context->debug_ary, object_record_inspect(context->recorder, record));
|
|
715
844
|
|
|
716
845
|
return ST_CONTINUE;
|
|
717
846
|
}
|
|
@@ -725,75 +854,53 @@ static int update_object_record_entry(DDTRACE_UNUSED st_data_t *key, st_data_t *
|
|
|
725
854
|
return ST_CONTINUE;
|
|
726
855
|
}
|
|
727
856
|
|
|
857
|
+
static void inc_tracked_objects_or_fail(heap_record *heap_record) {
|
|
858
|
+
if (heap_record->num_tracked_objects == UINT32_MAX) {
|
|
859
|
+
raise_error(rb_eRuntimeError, "Reached maximum number of tracked objects for heap record");
|
|
860
|
+
}
|
|
861
|
+
heap_record->num_tracked_objects++;
|
|
862
|
+
}
|
|
863
|
+
|
|
728
864
|
static void commit_recording(heap_recorder *heap_recorder, heap_record *heap_record, object_record *active_recording) {
|
|
729
865
|
// Link the object record with the corresponding heap record. This was the last remaining thing we
|
|
730
866
|
// needed to fully build the object_record.
|
|
731
867
|
active_recording->heap_record = heap_record;
|
|
732
|
-
if (heap_record->num_tracked_objects == UINT32_MAX) {
|
|
733
|
-
rb_raise(rb_eRuntimeError, "Reached maximum number of tracked objects for heap record");
|
|
734
|
-
}
|
|
735
|
-
heap_record->num_tracked_objects++;
|
|
736
868
|
|
|
737
869
|
int existing_error = st_update(heap_recorder->object_records, active_recording->obj_id, update_object_record_entry, (st_data_t) active_recording);
|
|
738
870
|
if (existing_error) {
|
|
739
871
|
object_record *existing_record = NULL;
|
|
740
872
|
st_lookup(heap_recorder->object_records, active_recording->obj_id, (st_data_t *) &existing_record);
|
|
741
|
-
if (existing_record == NULL)
|
|
873
|
+
if (existing_record == NULL) raise_error(rb_eRuntimeError, "Unexpected NULL when reading existing record");
|
|
742
874
|
|
|
743
|
-
VALUE existing_inspect = object_record_inspect(existing_record);
|
|
744
|
-
VALUE new_inspect = object_record_inspect(active_recording);
|
|
745
|
-
|
|
875
|
+
VALUE existing_inspect = object_record_inspect(heap_recorder, existing_record);
|
|
876
|
+
VALUE new_inspect = object_record_inspect(heap_recorder, active_recording);
|
|
877
|
+
raise_error(rb_eRuntimeError, "Object ids are supposed to be unique. We got 2 allocation recordings with "
|
|
746
878
|
"the same id. previous={%"PRIsVALUE"} new={%"PRIsVALUE"}", existing_inspect, new_inspect);
|
|
747
879
|
}
|
|
748
880
|
}
|
|
749
881
|
|
|
750
|
-
// Struct holding data required for an update operation on heap_records
|
|
751
|
-
typedef struct {
|
|
752
|
-
// [in] The locations we did this update with
|
|
753
|
-
ddog_prof_Slice_Location locations;
|
|
754
|
-
// [out] Pointer that will be updated to the updated heap record to prevent having to do
|
|
755
|
-
// another lookup to access the updated heap record.
|
|
756
|
-
heap_record **record;
|
|
757
|
-
} heap_record_update_data;
|
|
758
|
-
|
|
759
|
-
// This function assumes ownership of stack_data is passed on to it so it'll either transfer ownership or clean-up.
|
|
760
882
|
static int update_heap_record_entry_with_new_allocation(st_data_t *key, st_data_t *value, st_data_t data, int existing) {
|
|
761
|
-
|
|
883
|
+
heap_record **new_or_existing_record = (heap_record **) data;
|
|
884
|
+
(*new_or_existing_record) = (heap_record *) (*key);
|
|
762
885
|
|
|
763
886
|
if (!existing) {
|
|
764
|
-
|
|
765
|
-
// we need to initialize a heap_record_key with a new stack and use that for the key storage. We can't use the
|
|
766
|
-
// locations-based key we used for the update call because we don't own its lifecycle. So we create a new
|
|
767
|
-
// heap stack and will pass ownership of it to the heap_record.
|
|
768
|
-
heap_stack *stack = heap_stack_new(update_data->locations);
|
|
769
|
-
(*key) = (st_data_t) heap_record_key_new(stack);
|
|
770
|
-
(*value) = (st_data_t) heap_record_new(stack);
|
|
887
|
+
(*value) = (st_data_t) true; // We're only using this hash as a set
|
|
771
888
|
}
|
|
772
889
|
|
|
773
|
-
heap_record *record = (heap_record*) (*value);
|
|
774
|
-
(*update_data->record) = record;
|
|
775
|
-
|
|
776
890
|
return ST_CONTINUE;
|
|
777
891
|
}
|
|
778
892
|
|
|
779
893
|
static heap_record* get_or_create_heap_record(heap_recorder *heap_recorder, ddog_prof_Slice_Location locations) {
|
|
780
|
-
//
|
|
781
|
-
|
|
782
|
-
// NOTE: If we end up creating a new record, we'll create a heap-allocated key we own and use that for storage
|
|
783
|
-
// instead of this one.
|
|
784
|
-
heap_record_key lookup_key = (heap_record_key) {
|
|
785
|
-
.type = LOCATION_SLICE,
|
|
786
|
-
.location_slice = &locations,
|
|
787
|
-
};
|
|
894
|
+
// See note on "heap_records" definition for why we keep this map.
|
|
895
|
+
heap_record *stack = heap_record_new(heap_recorder, locations);
|
|
788
896
|
|
|
789
|
-
heap_record *
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
}
|
|
794
|
-
st_update(heap_recorder->heap_records, (st_data_t) &lookup_key, update_heap_record_entry_with_new_allocation, (st_data_t) &update_data);
|
|
897
|
+
heap_record *new_or_existing_record = NULL; // Will be set inside update_heap_record_entry_with_new_allocation
|
|
898
|
+
bool existing = st_update(heap_recorder->heap_records, (st_data_t) stack, update_heap_record_entry_with_new_allocation, (st_data_t) &new_or_existing_record);
|
|
899
|
+
if (existing) {
|
|
900
|
+
heap_record_free(heap_recorder, stack, true);
|
|
901
|
+
}
|
|
795
902
|
|
|
796
|
-
return
|
|
903
|
+
return new_or_existing_record;
|
|
797
904
|
}
|
|
798
905
|
|
|
799
906
|
static void cleanup_heap_record_if_unused(heap_recorder *heap_recorder, heap_record *heap_record) {
|
|
@@ -802,18 +909,10 @@ static void cleanup_heap_record_if_unused(heap_recorder *heap_recorder, heap_rec
|
|
|
802
909
|
return;
|
|
803
910
|
}
|
|
804
911
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
.heap_stack = heap_record->stack,
|
|
808
|
-
};
|
|
809
|
-
// We need to access the deleted key to free it since we gave ownership of the keys to the hash.
|
|
810
|
-
// st_delete will change this pointer to point to the removed key if one is found.
|
|
811
|
-
heap_record_key *deleted_key = &heap_key;
|
|
812
|
-
if (!st_delete(heap_recorder->heap_records, (st_data_t*) &deleted_key, NULL)) {
|
|
813
|
-
rb_raise(rb_eRuntimeError, "Attempted to cleanup an untracked heap_record");
|
|
912
|
+
if (!st_delete(heap_recorder->heap_records, (st_data_t*) &heap_record, NULL)) {
|
|
913
|
+
raise_error(rb_eRuntimeError, "Attempted to cleanup an untracked heap_record");
|
|
814
914
|
};
|
|
815
|
-
|
|
816
|
-
heap_record_free(heap_record);
|
|
915
|
+
heap_record_free(heap_recorder, heap_record, true);
|
|
817
916
|
}
|
|
818
917
|
|
|
819
918
|
static void on_committed_object_record_cleanup(heap_recorder *heap_recorder, object_record *record) {
|
|
@@ -821,67 +920,56 @@ static void on_committed_object_record_cleanup(heap_recorder *heap_recorder, obj
|
|
|
821
920
|
// (See PROF-10656 Datadog-internal for details). Just in case, I've sprinkled a bunch of NULL tests in this function for now.
|
|
822
921
|
// Once we figure out the issue we can get rid of them again.
|
|
823
922
|
|
|
824
|
-
if (heap_recorder == NULL)
|
|
825
|
-
if (heap_recorder->heap_records == NULL)
|
|
826
|
-
if (record == NULL)
|
|
923
|
+
if (heap_recorder == NULL) raise_error(rb_eRuntimeError, "heap_recorder was NULL in on_committed_object_record_cleanup");
|
|
924
|
+
if (heap_recorder->heap_records == NULL) raise_error(rb_eRuntimeError, "heap_recorder->heap_records was NULL in on_committed_object_record_cleanup");
|
|
925
|
+
if (record == NULL) raise_error(rb_eRuntimeError, "record was NULL in on_committed_object_record_cleanup");
|
|
827
926
|
|
|
828
927
|
// Starting with the associated heap record. There will now be one less tracked object pointing to it
|
|
829
928
|
heap_record *heap_record = record->heap_record;
|
|
830
929
|
|
|
831
|
-
if (heap_record == NULL)
|
|
832
|
-
if (heap_record->stack == NULL) rb_raise(rb_eRuntimeError, "heap_record->stack was NULL in on_committed_object_record_cleanup");
|
|
930
|
+
if (heap_record == NULL) raise_error(rb_eRuntimeError, "heap_record was NULL in on_committed_object_record_cleanup");
|
|
833
931
|
|
|
834
932
|
heap_record->num_tracked_objects--;
|
|
835
933
|
|
|
836
934
|
// One less object using this heap record, it may have become unused...
|
|
837
935
|
cleanup_heap_record_if_unused(heap_recorder, heap_record);
|
|
838
936
|
|
|
839
|
-
object_record_free(record);
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
// ===============
|
|
843
|
-
// Heap Record API
|
|
844
|
-
// ===============
|
|
845
|
-
heap_record* heap_record_new(heap_stack *stack) {
|
|
846
|
-
heap_record *record = ruby_xcalloc(1, sizeof(heap_record));
|
|
847
|
-
record->num_tracked_objects = 0;
|
|
848
|
-
record->stack = stack;
|
|
849
|
-
return record;
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
void heap_record_free(heap_record *record) {
|
|
853
|
-
heap_stack_free(record->stack);
|
|
854
|
-
ruby_xfree(record);
|
|
937
|
+
object_record_free(heap_recorder, record, true);
|
|
855
938
|
}
|
|
856
939
|
|
|
857
940
|
// =================
|
|
858
941
|
// Object Record API
|
|
859
942
|
// =================
|
|
860
943
|
object_record* object_record_new(long obj_id, heap_record *heap_record, live_object_data object_data) {
|
|
861
|
-
object_record *record =
|
|
944
|
+
object_record *record = calloc(1, sizeof(object_record)); // See "note on calloc vs ruby_xcalloc use" above
|
|
862
945
|
record->obj_id = obj_id;
|
|
863
946
|
record->heap_record = heap_record;
|
|
864
947
|
record->object_data = object_data;
|
|
865
948
|
return record;
|
|
866
949
|
}
|
|
867
950
|
|
|
868
|
-
void object_record_free(object_record *record) {
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
951
|
+
void object_record_free(heap_recorder *recorder, object_record *record, bool should_unintern) {
|
|
952
|
+
// When tearing down the whole recorder state, we skip uninterning as it's not needed (the managed
|
|
953
|
+
// string table is going to be destroyed anyway) and if there's any failures we can't raise
|
|
954
|
+
// in the middle of a dfree callback.
|
|
955
|
+
if (should_unintern) unintern_or_raise(recorder, record->object_data.class);
|
|
956
|
+
|
|
957
|
+
free(record); // See "note on calloc vs ruby_xcalloc use" above
|
|
873
958
|
}
|
|
874
959
|
|
|
875
|
-
VALUE object_record_inspect(object_record *record) {
|
|
876
|
-
heap_frame top_frame = record->heap_record->
|
|
960
|
+
VALUE object_record_inspect(heap_recorder *recorder, object_record *record) {
|
|
961
|
+
heap_frame top_frame = record->heap_record->frames[0];
|
|
962
|
+
VALUE filename = get_ruby_string_or_raise(recorder, top_frame.filename);
|
|
877
963
|
live_object_data object_data = record->object_data;
|
|
878
|
-
|
|
879
|
-
|
|
964
|
+
|
|
965
|
+
VALUE inspect = rb_sprintf("obj_id=%ld weight=%d size=%zu location=%"PRIsVALUE":%d alloc_gen=%zu gen_age=%zu frozen=%d ",
|
|
966
|
+
record->obj_id, object_data.weight, object_data.size, filename,
|
|
880
967
|
(int) top_frame.line, object_data.alloc_gen, object_data.gen_age, object_data.is_frozen);
|
|
881
968
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
969
|
+
if (record->object_data.class.value > 0) {
|
|
970
|
+
VALUE class = get_ruby_string_or_raise(recorder, record->object_data.class);
|
|
971
|
+
|
|
972
|
+
rb_str_catf(inspect, "class=%"PRIsVALUE" ", class);
|
|
885
973
|
}
|
|
886
974
|
VALUE ref;
|
|
887
975
|
|
|
@@ -901,202 +989,119 @@ VALUE object_record_inspect(object_record *record) {
|
|
|
901
989
|
}
|
|
902
990
|
|
|
903
991
|
// ==============
|
|
904
|
-
// Heap
|
|
905
|
-
// ==============
|
|
906
|
-
// WARN: Must be kept in-sync with ::char_slice_hash
|
|
907
|
-
st_index_t string_hash(char *str, st_index_t seed) {
|
|
908
|
-
return st_hash(str, strlen(str), seed);
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
// WARN: Must be kept in-sync with ::string_hash
|
|
912
|
-
st_index_t char_slice_hash(ddog_CharSlice char_slice, st_index_t seed) {
|
|
913
|
-
return st_hash(char_slice.ptr, char_slice.len, seed);
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
// WARN: Must be kept in-sync with ::ddog_location_hash
|
|
917
|
-
st_index_t heap_frame_hash(heap_frame *frame, st_index_t seed) {
|
|
918
|
-
st_index_t hash = string_hash(frame->name, seed);
|
|
919
|
-
hash = string_hash(frame->filename, hash);
|
|
920
|
-
hash = st_hash(&frame->line, sizeof(frame->line), hash);
|
|
921
|
-
return hash;
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
// WARN: Must be kept in-sync with ::heap_frame_hash
|
|
925
|
-
st_index_t ddog_location_hash(ddog_prof_Location location, st_index_t seed) {
|
|
926
|
-
st_index_t hash = char_slice_hash(location.function.name, seed);
|
|
927
|
-
hash = char_slice_hash(location.function.filename, hash);
|
|
928
|
-
// Convert ddog_prof line type to the same type we use for our heap_frames to
|
|
929
|
-
// ensure we have compatible hashes
|
|
930
|
-
int32_t line_as_int32 = (int32_t) location.line;
|
|
931
|
-
hash = st_hash(&line_as_int32, sizeof(line_as_int32), hash);
|
|
932
|
-
return hash;
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
// ==============
|
|
936
|
-
// Heap Stack API
|
|
992
|
+
// Heap Record API
|
|
937
993
|
// ==============
|
|
938
|
-
|
|
994
|
+
heap_record* heap_record_new(heap_recorder *recorder, ddog_prof_Slice_Location locations) {
|
|
939
995
|
uint16_t frames_len = locations.len;
|
|
940
996
|
if (frames_len > MAX_FRAMES_LIMIT) {
|
|
941
997
|
// This is not expected as MAX_FRAMES_LIMIT is shared with the stacktrace construction mechanism
|
|
942
|
-
|
|
998
|
+
raise_error(rb_eRuntimeError, "Found stack with more than %d frames (%d)", MAX_FRAMES_LIMIT, frames_len);
|
|
943
999
|
}
|
|
944
|
-
|
|
1000
|
+
heap_record *stack = calloc(1, sizeof(heap_record) + frames_len * sizeof(heap_frame)); // See "note on calloc vs ruby_xcalloc use" above
|
|
1001
|
+
stack->num_tracked_objects = 0;
|
|
945
1002
|
stack->frames_len = frames_len;
|
|
1003
|
+
|
|
1004
|
+
// Intern all these strings...
|
|
1005
|
+
ddog_CharSlice *strings = recorder->reusable_char_slices;
|
|
1006
|
+
// Put all the char slices in the same array; we'll pull them out in the same order from the ids array
|
|
946
1007
|
for (uint16_t i = 0; i < stack->frames_len; i++) {
|
|
947
1008
|
const ddog_prof_Location *location = &locations.ptr[i];
|
|
1009
|
+
strings[i] = location->function.filename;
|
|
1010
|
+
strings[i + stack->frames_len] = location->function.name;
|
|
1011
|
+
}
|
|
1012
|
+
intern_all_or_raise(recorder->string_storage, (ddog_prof_Slice_CharSlice) { .ptr = strings, .len = stack->frames_len * 2 }, recorder->reusable_ids, stack->frames_len * 2);
|
|
1013
|
+
|
|
1014
|
+
// ...and record them for later use
|
|
1015
|
+
for (uint16_t i = 0; i < stack->frames_len; i++) {
|
|
948
1016
|
stack->frames[i] = (heap_frame) {
|
|
949
|
-
.
|
|
950
|
-
.
|
|
1017
|
+
.filename = recorder->reusable_ids[i],
|
|
1018
|
+
.name = recorder->reusable_ids[i + stack->frames_len],
|
|
951
1019
|
// ddog_prof_Location is a int64_t. We don't expect to have to profile files with more than
|
|
952
1020
|
// 2M lines so this cast should be fairly safe?
|
|
953
|
-
.line = (int32_t)
|
|
1021
|
+
.line = (int32_t) locations.ptr[i].line,
|
|
954
1022
|
};
|
|
955
1023
|
}
|
|
956
|
-
return stack;
|
|
957
|
-
}
|
|
958
1024
|
|
|
959
|
-
|
|
960
|
-
for (uint64_t i = 0; i < stack->frames_len; i++) {
|
|
961
|
-
heap_frame *frame = &stack->frames[i];
|
|
962
|
-
ruby_xfree(frame->name);
|
|
963
|
-
ruby_xfree(frame->filename);
|
|
964
|
-
}
|
|
965
|
-
ruby_xfree(stack);
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
// WARN: Must be kept in-sync with ::ddog_location_slice_hash
|
|
969
|
-
st_index_t heap_stack_hash(heap_stack *stack, st_index_t seed) {
|
|
970
|
-
st_index_t hash = seed;
|
|
971
|
-
for (uint64_t i = 0; i < stack->frames_len; i++) {
|
|
972
|
-
hash = heap_frame_hash(&stack->frames[i], hash);
|
|
973
|
-
}
|
|
974
|
-
return hash;
|
|
1025
|
+
return stack;
|
|
975
1026
|
}
|
|
976
1027
|
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1028
|
+
void heap_record_free(heap_recorder *recorder, heap_record *stack, bool should_unintern) {
|
|
1029
|
+
// When tearing down the whole recorder state, we skip uninterning as it's not needed (the managed
|
|
1030
|
+
// string table is going to be destroyed anyway) and if there's any failures we can't raise
|
|
1031
|
+
// in the middle of a dfree callback.
|
|
1032
|
+
if (should_unintern) {
|
|
1033
|
+
ddog_prof_ManagedStringId *ids = recorder->reusable_ids;
|
|
1034
|
+
|
|
1035
|
+
// Put all the ids in the same array; doesn't really matter the order
|
|
1036
|
+
for (u_int16_t i = 0; i < stack->frames_len; i++) {
|
|
1037
|
+
ids[i] = stack->frames[i].filename;
|
|
1038
|
+
ids[i + stack->frames_len] = stack->frames[i].name;
|
|
1039
|
+
}
|
|
1040
|
+
unintern_all_or_raise(recorder, (ddog_prof_Slice_ManagedStringId) { .ptr = ids, .len = stack->frames_len * 2 });
|
|
982
1041
|
}
|
|
983
|
-
return hash;
|
|
984
|
-
}
|
|
985
1042
|
|
|
986
|
-
//
|
|
987
|
-
// Heap Record Key API
|
|
988
|
-
// ===================
|
|
989
|
-
heap_record_key* heap_record_key_new(heap_stack *stack) {
|
|
990
|
-
heap_record_key *key = ruby_xmalloc(sizeof(heap_record_key));
|
|
991
|
-
key->type = HEAP_STACK;
|
|
992
|
-
key->heap_stack = stack;
|
|
993
|
-
return key;
|
|
1043
|
+
free(stack); // See "note on calloc vs ruby_xcalloc use" above
|
|
994
1044
|
}
|
|
995
1045
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1046
|
+
// The entire stack is represented by ids (name, filename) and lines (integers) so we can treat is as just
|
|
1047
|
+
// a big string of bytes and compare it all in one go.
|
|
1048
|
+
int heap_record_cmp_st(st_data_t key1, st_data_t key2) {
|
|
1049
|
+
heap_record *stack1 = (heap_record*) key1;
|
|
1050
|
+
heap_record *stack2 = (heap_record*) key2;
|
|
999
1051
|
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
return key->heap_stack->frames_len;
|
|
1052
|
+
if (stack1->frames_len != stack2->frames_len) {
|
|
1053
|
+
return ((int) stack1->frames_len) - ((int) stack2->frames_len);
|
|
1003
1054
|
} else {
|
|
1004
|
-
return
|
|
1055
|
+
return memcmp(stack1->frames, stack2->frames, stack1->frames_len * sizeof(heap_frame));
|
|
1005
1056
|
}
|
|
1006
1057
|
}
|
|
1007
1058
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
return key->heap_stack->frames[entry_i].line;
|
|
1011
|
-
} else {
|
|
1012
|
-
return key->location_slice->ptr[entry_i].line;
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1059
|
+
// Initial seed for hash function, same as Ruby uses
|
|
1060
|
+
#define FNV1_32A_INIT 0x811c9dc5
|
|
1015
1061
|
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
} else {
|
|
1022
|
-
ddog_CharSlice name = key->location_slice->ptr[entry_i].function.name;
|
|
1023
|
-
(*name_ptr) = name.ptr;
|
|
1024
|
-
return name.len;
|
|
1025
|
-
}
|
|
1062
|
+
// The entire stack is represented by ids (name, filename) and lines (integers) so we can treat is as just
|
|
1063
|
+
// a big string of bytes and hash it all in one go.
|
|
1064
|
+
st_index_t heap_record_hash_st(st_data_t key) {
|
|
1065
|
+
heap_record *stack = (heap_record*) key;
|
|
1066
|
+
return st_hash(stack->frames, stack->frames_len * sizeof(heap_frame), FNV1_32A_INIT);
|
|
1026
1067
|
}
|
|
1027
1068
|
|
|
1028
|
-
static
|
|
1029
|
-
if (
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
ddog_CharSlice filename = key->location_slice->ptr[entry_i].function.filename;
|
|
1035
|
-
(*filename_ptr) = filename.ptr;
|
|
1036
|
-
return filename.len;
|
|
1069
|
+
static void unintern_or_raise(heap_recorder *recorder, ddog_prof_ManagedStringId id) {
|
|
1070
|
+
if (id.value == 0) return; // Empty string, nothing to do
|
|
1071
|
+
|
|
1072
|
+
ddog_prof_MaybeError result = ddog_prof_ManagedStringStorage_unintern(recorder->string_storage, id);
|
|
1073
|
+
if (result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
|
|
1074
|
+
raise_error(rb_eRuntimeError, "Failed to unintern id: %"PRIsVALUE, get_error_details_and_drop(&result.some));
|
|
1037
1075
|
}
|
|
1038
1076
|
}
|
|
1039
1077
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
// Fast path, check if lengths differ
|
|
1045
|
-
size_t key_record1_len = heap_record_key_len(key_record1);
|
|
1046
|
-
size_t key_record2_len = heap_record_key_len(key_record2);
|
|
1047
|
-
|
|
1048
|
-
if (key_record1_len != key_record2_len) {
|
|
1049
|
-
return ((int) key_record1_len) - ((int) key_record2_len);
|
|
1078
|
+
static void unintern_all_or_raise(heap_recorder *recorder, ddog_prof_Slice_ManagedStringId ids) {
|
|
1079
|
+
ddog_prof_MaybeError result = ddog_prof_ManagedStringStorage_unintern_all(recorder->string_storage, ids);
|
|
1080
|
+
if (result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
|
|
1081
|
+
raise_error(rb_eRuntimeError, "Failed to unintern_all: %"PRIsVALUE, get_error_details_and_drop(&result.some));
|
|
1050
1082
|
}
|
|
1083
|
+
}
|
|
1051
1084
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
size_t line2 = heap_record_key_entry_line(key_record2, i);
|
|
1057
|
-
if (line1 != line2) {
|
|
1058
|
-
return ((int) line1) - ((int)line2);
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
// Then come names, they are usually smaller than filenames
|
|
1062
|
-
const char *name1, *name2;
|
|
1063
|
-
size_t name1_len = heap_record_key_entry_name(key_record1, i, &name1);
|
|
1064
|
-
size_t name2_len = heap_record_key_entry_name(key_record2, i, &name2);
|
|
1065
|
-
if (name1_len != name2_len) {
|
|
1066
|
-
return ((int) name1_len) - ((int) name2_len);
|
|
1067
|
-
}
|
|
1068
|
-
int name_cmp_result = strncmp(name1, name2, name1_len);
|
|
1069
|
-
if (name_cmp_result != 0) {
|
|
1070
|
-
return name_cmp_result;
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
// Then come filenames
|
|
1074
|
-
const char *filename1, *filename2;
|
|
1075
|
-
int64_t filename1_len = heap_record_key_entry_filename(key_record1, i, &filename1);
|
|
1076
|
-
int64_t filename2_len = heap_record_key_entry_filename(key_record2, i, &filename2);
|
|
1077
|
-
if (filename1_len != filename2_len) {
|
|
1078
|
-
return ((int) filename1_len) - ((int) filename2_len);
|
|
1079
|
-
}
|
|
1080
|
-
int filename_cmp_result = strncmp(filename1, filename2, filename1_len);
|
|
1081
|
-
if (filename_cmp_result != 0) {
|
|
1082
|
-
return filename_cmp_result;
|
|
1083
|
-
}
|
|
1085
|
+
static VALUE get_ruby_string_or_raise(heap_recorder *recorder, ddog_prof_ManagedStringId id) {
|
|
1086
|
+
ddog_StringWrapperResult get_string_result = ddog_prof_ManagedStringStorage_get_string(recorder->string_storage, id);
|
|
1087
|
+
if (get_string_result.tag == DDOG_STRING_WRAPPER_RESULT_ERR) {
|
|
1088
|
+
raise_error(rb_eRuntimeError, "Failed to get string: %"PRIsVALUE, get_error_details_and_drop(&get_string_result.err));
|
|
1084
1089
|
}
|
|
1090
|
+
VALUE ruby_string = ruby_string_from_vec_u8(get_string_result.ok.message);
|
|
1091
|
+
ddog_StringWrapper_drop((ddog_StringWrapper *) &get_string_result.ok);
|
|
1085
1092
|
|
|
1086
|
-
|
|
1087
|
-
return 0;
|
|
1093
|
+
return ruby_string;
|
|
1088
1094
|
}
|
|
1089
1095
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
return heap_stack_hash(record_key->heap_stack, FNV1_32A_INIT);
|
|
1097
|
-
} else {
|
|
1098
|
-
return ddog_location_slice_hash(*record_key->location_slice, FNV1_32A_INIT);
|
|
1096
|
+
static long obj_id_or_fail(VALUE obj) {
|
|
1097
|
+
VALUE ruby_obj_id = rb_obj_id(obj);
|
|
1098
|
+
if (!FIXNUM_P(ruby_obj_id)) {
|
|
1099
|
+
// Bignum object ids indicate the fixnum range is exhausted - all future IDs will also be bignums.
|
|
1100
|
+
// Heap profiling cannot continue.
|
|
1101
|
+
raise_error(rb_eRuntimeError, "Heap profiling: bignum object id detected. Heap profiling cannot continue.");
|
|
1099
1102
|
}
|
|
1103
|
+
|
|
1104
|
+
return FIX2LONG(ruby_obj_id);
|
|
1100
1105
|
}
|
|
1101
1106
|
|
|
1102
1107
|
static inline double ewma_stat(double previous, double current) {
|
|
@@ -1106,7 +1111,7 @@ static inline double ewma_stat(double previous, double current) {
|
|
|
1106
1111
|
|
|
1107
1112
|
VALUE heap_recorder_testonly_is_object_recorded(heap_recorder *heap_recorder, VALUE obj_id) {
|
|
1108
1113
|
if (heap_recorder == NULL) {
|
|
1109
|
-
|
|
1114
|
+
raise_error(rb_eArgError, "heap_recorder is NULL");
|
|
1110
1115
|
}
|
|
1111
1116
|
|
|
1112
1117
|
// Check if object records contains an object with this object_id
|
|
@@ -1115,8 +1120,28 @@ VALUE heap_recorder_testonly_is_object_recorded(heap_recorder *heap_recorder, VA
|
|
|
1115
1120
|
|
|
1116
1121
|
void heap_recorder_testonly_reset_last_update(heap_recorder *heap_recorder) {
|
|
1117
1122
|
if (heap_recorder == NULL) {
|
|
1118
|
-
|
|
1123
|
+
raise_error(rb_eArgError, "heap_recorder is NULL");
|
|
1119
1124
|
}
|
|
1120
1125
|
|
|
1121
1126
|
heap_recorder->last_update_ns = 0;
|
|
1122
1127
|
}
|
|
1128
|
+
|
|
1129
|
+
void heap_recorder_testonly_benchmark_intern(heap_recorder *heap_recorder, ddog_CharSlice string, int times, bool use_all) {
|
|
1130
|
+
if (heap_recorder == NULL) raise_error(rb_eArgError, "heap profiling must be enabled");
|
|
1131
|
+
if (times > REUSABLE_FRAME_DETAILS_SIZE) raise_error(rb_eArgError, "times cannot be > than REUSABLE_FRAME_DETAILS_SIZE");
|
|
1132
|
+
|
|
1133
|
+
if (use_all) {
|
|
1134
|
+
ddog_CharSlice *strings = heap_recorder->reusable_char_slices;
|
|
1135
|
+
|
|
1136
|
+
for (int i = 0; i < times; i++) strings[i] = string;
|
|
1137
|
+
|
|
1138
|
+
intern_all_or_raise(
|
|
1139
|
+
heap_recorder->string_storage,
|
|
1140
|
+
(ddog_prof_Slice_CharSlice) { .ptr = strings, .len = times },
|
|
1141
|
+
heap_recorder->reusable_ids,
|
|
1142
|
+
times
|
|
1143
|
+
);
|
|
1144
|
+
} else {
|
|
1145
|
+
for (int i = 0; i < times; i++) intern_or_raise(heap_recorder->string_storage, string);
|
|
1146
|
+
}
|
|
1147
|
+
}
|