From e31dc15d5a2c8eaa3720ffe1cac29eb9f6083a7c Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Fri, 27 Mar 2026 16:33:30 +0100 Subject: [PATCH] perf: Use FilenameCache in profilers --- .../sentry/interfaces/stacktrace_builder.rb | 3 +++ sentry-ruby/lib/sentry/profiler.rb | 1 + sentry-ruby/lib/sentry/profiler/helpers.rb | 22 +------------------ .../lib/sentry/utils/filename_cache.rb | 2 ++ sentry-ruby/lib/sentry/vernier/output.rb | 3 ++- sentry-ruby/lib/sentry/vernier/profiler.rb | 4 +++- sentry-ruby/spec/sentry/profiler_spec.rb | 16 ++++++++++++++ 7 files changed, 28 insertions(+), 23 deletions(-) diff --git a/sentry-ruby/lib/sentry/interfaces/stacktrace_builder.rb b/sentry-ruby/lib/sentry/interfaces/stacktrace_builder.rb index 14bea700b..c7595cf39 100644 --- a/sentry-ruby/lib/sentry/interfaces/stacktrace_builder.rb +++ b/sentry-ruby/lib/sentry/interfaces/stacktrace_builder.rb @@ -22,6 +22,9 @@ class StacktraceBuilder # @return [Boolean] attr_reader :strip_backtrace_load_path + # @return [FilenameCache] + attr_reader :filename_cache + # @param project_root [String] # @param app_dirs_pattern [Regexp, nil] # @param linecache [LineCache] diff --git a/sentry-ruby/lib/sentry/profiler.rb b/sentry-ruby/lib/sentry/profiler.rb index ba5b5d525..9e329cdd1 100644 --- a/sentry-ruby/lib/sentry/profiler.rb +++ b/sentry-ruby/lib/sentry/profiler.rb @@ -26,6 +26,7 @@ def initialize(configuration) @project_root = configuration.project_root @app_dirs_pattern = configuration.app_dirs_pattern @in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}") + @filename_cache = configuration.stacktrace_builder.filename_cache end def start diff --git a/sentry-ruby/lib/sentry/profiler/helpers.rb b/sentry-ruby/lib/sentry/profiler/helpers.rb index 3c446fba0..eaa3167e9 100644 --- a/sentry-ruby/lib/sentry/profiler/helpers.rb +++ b/sentry-ruby/lib/sentry/profiler/helpers.rb @@ -9,28 +9,8 @@ def in_app?(abs_path) abs_path.match?(@in_app_pattern) end - # copied from stacktrace.rb since I don't want to touch existing code - # TODO-neel-profiler try to fetch this from stackprof once we patch - # the native extension def compute_filename(abs_path, in_app) - return nil if abs_path.nil? - - under_project_root = @project_root && abs_path.start_with?(@project_root) - - prefix = - if under_project_root && in_app - @project_root - else - longest_load_path = $LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size) - - if under_project_root - longest_load_path || @project_root - else - longest_load_path - end - end - - prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path + @filename_cache.compute_filename(abs_path, in_app, true) end def split_module(name) diff --git a/sentry-ruby/lib/sentry/utils/filename_cache.rb b/sentry-ruby/lib/sentry/utils/filename_cache.rb index 0472eaf74..976021d85 100644 --- a/sentry-ruby/lib/sentry/utils/filename_cache.rb +++ b/sentry-ruby/lib/sentry/utils/filename_cache.rb @@ -2,6 +2,8 @@ module Sentry class FilenameCache + attr_reader :cache + def initialize(project_root) @project_root = project_root @load_paths = $LOAD_PATH.map(&:to_s).sort_by(&:size).reverse.freeze diff --git a/sentry-ruby/lib/sentry/vernier/output.rb b/sentry-ruby/lib/sentry/vernier/output.rb index 7002f82a1..10233543f 100644 --- a/sentry-ruby/lib/sentry/vernier/output.rb +++ b/sentry-ruby/lib/sentry/vernier/output.rb @@ -10,11 +10,12 @@ class Output attr_reader :profile - def initialize(profile, project_root:, in_app_pattern:, app_dirs_pattern:) + def initialize(profile, project_root:, in_app_pattern:, app_dirs_pattern:, filename_cache:) @profile = profile @project_root = project_root @in_app_pattern = in_app_pattern @app_dirs_pattern = app_dirs_pattern + @filename_cache = filename_cache end def to_h diff --git a/sentry-ruby/lib/sentry/vernier/profiler.rb b/sentry-ruby/lib/sentry/vernier/profiler.rb index 028f85979..5047119f7 100644 --- a/sentry-ruby/lib/sentry/vernier/profiler.rb +++ b/sentry-ruby/lib/sentry/vernier/profiler.rb @@ -24,6 +24,7 @@ def initialize(configuration) @project_root = configuration.project_root @app_dirs_pattern = configuration.app_dirs_pattern @in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}") + @filename_cache = configuration.stacktrace_builder.filename_cache end def set_initial_sample_decision(transaction_sampled) @@ -125,7 +126,8 @@ def output result, project_root: @project_root, app_dirs_pattern: @app_dirs_pattern, - in_app_pattern: @in_app_pattern + in_app_pattern: @in_app_pattern, + filename_cache: @filename_cache ) end end diff --git a/sentry-ruby/spec/sentry/profiler_spec.rb b/sentry-ruby/spec/sentry/profiler_spec.rb index 2de41ca13..eea87917e 100644 --- a/sentry-ruby/spec/sentry/profiler_spec.rb +++ b/sentry-ruby/spec/sentry/profiler_spec.rb @@ -320,6 +320,22 @@ last_elapsed = elapsed end end + + it 'reuses filename_cache entries with stacktrace_builder' do + filename_cache = Sentry.configuration.stacktrace_builder.filename_cache + spec_path = "#{Dir.pwd}/spec/sentry/profiler_spec.rb" + + # building a stacktrace populates the cache + Sentry.configuration.stacktrace_builder.build( + backtrace: ["#{spec_path}:7:in `foo'"] + ) + + expect(filename_cache.cache).to have_key(spec_path) + + # profiler reuses the cached entry instead of recomputing + expect(filename_cache).not_to receive(:longest_load_path).with(spec_path) + subject.to_h + end end end end