dec3e4ce64
Sherlock will be a new GitLab specific tool for measuring the performance of Rails requests (and SideKiq jobs at some point). Some of the things that are currently tracked: * SQL queries along with their timings, backtraces and query plans (using "EXPLAIN ANALYZE" for PostgreSQL and regular "EXPLAIN" for MySQL) * Timings of application files (including views) on a per line basis * Some meta data such as the request method, path, total duration, etc More tracking (e.g. Rugged or gitlab-shell timings) might be added in the future. Sherlock will replace any existing tools we have used so far (e.g. active_record_query_trace and rack-mini-profiler), hence the corresponding Gems have been removed from the Gemfile. Sherlock can be enabled by starting Rails as following: ENABLE_SHERLOCK=1 bundle exec rails s Recorded transactions can be found at `/sherlock/transactions`.
60 lines
1.5 KiB
Ruby
60 lines
1.5 KiB
Ruby
module Gitlab
|
|
module Sherlock
|
|
class LineProfiler
|
|
# The minimum amount of time that has to be spent in a file for it to be
|
|
# included in a list of samples.
|
|
MINIMUM_DURATION = 10.0
|
|
|
|
def profile(&block)
|
|
if RUBY_ENGINE == 'ruby'
|
|
profile_mri(&block)
|
|
else
|
|
raise NotImplementedError,
|
|
'Line profiling is not supported on this platform'
|
|
end
|
|
end
|
|
|
|
def profile_mri
|
|
retval = nil
|
|
samples = lineprof(/^#{Rails.root.to_s}/) { retval = yield }
|
|
|
|
file_samples = aggregate_rblineprof(samples)
|
|
|
|
[retval, file_samples]
|
|
end
|
|
|
|
# Returns an Array of file samples based on the output of rblineprof.
|
|
def aggregate_rblineprof(lineprof_stats)
|
|
samples = []
|
|
|
|
lineprof_stats.each do |(file, stats)|
|
|
source_lines = File.read(file).each_line.to_a
|
|
line_samples = []
|
|
|
|
total_duration = microsec_to_millisec(stats[0][0])
|
|
total_events = stats[0][2]
|
|
|
|
next if total_duration <= MINIMUM_DURATION
|
|
|
|
stats[1..-1].each_with_index do |data, index|
|
|
next unless source_lines[index]
|
|
|
|
duration = microsec_to_millisec(data[0])
|
|
events = data[2]
|
|
|
|
line_samples << LineSample.new(duration, events)
|
|
end
|
|
|
|
samples << FileSample.
|
|
new(file, line_samples, total_duration, total_events)
|
|
end
|
|
|
|
samples
|
|
end
|
|
|
|
def microsec_to_millisec(microsec)
|
|
microsec / 1000.0
|
|
end
|
|
end
|
|
end
|
|
end
|