class Vernier::Output::FileListing
def format_file(output, filename, all_samples, total:)
def format_file(output, filename, all_samples, total:) samples = all_samples[filename] # file_name, lines, file_wall, file_cpu, file_idle, file_sort output << sprintf(" TOTAL | SELF | LINE SOURCE\n") File.readlines(filename).each_with_index do |line, i| lineno = i + 1 calls = samples[lineno] if calls && calls.total > 0 output << sprintf("%5.1f%% | %5.1f%% | % 4i %s", 100 * calls.total / total.to_f, 100 * calls.self / total.to_f, lineno, line) else output << sprintf(" | | % 4i %s", lineno, line) end end end
def format_file_html(output, filename, relevant_files)
def format_file_html(output, filename, relevant_files) samples = relevant_files[filename] # file_name, lines, file_wall, file_cpu, file_idle, file_sort output << sprintf(" TOTAL | SELF | LINE SOURCE\n") File.readlines(filename).each_with_index do |line, i| lineno = i + 1 calls = samples[lineno] if calls && calls.total > 0 output << sprintf("%5.1f%% | %5.1f%% | % 4i %s", 100 * calls.total / total.to_f, 100 * calls.self / total.to_f, lineno, CGI::escapeHTML(line)) else output << sprintf(" | | % 4i %s", lineno, CGI::escapeHTML(line)) end end end
def html_output(output, relevant_files)
def html_output(output, relevant_files) output << "<pre>" output << " SELF FILE\n" relevant_files.sort_by {|k, v| -v.values.map(&:self).sum }.each do |filename, file_contents| tmpl = "<details style=\"display:inline-block;vertical-align:top;\"><summary>%s</summary>" output << sprintf("% 5.1f%% #{tmpl}\n", file_contents.values.map(&:self).sum * 100 / total.to_f, filename) format_file_html(output, filename, relevant_files) output << "</details>\n" end output << "</pre>" end
def initialize(profile)
def initialize(profile) @profile = profile end
def output(template: nil)
def output(template: nil) output = +"" relevant_files = samples_by_file.select do |k, v| next if k.start_with?("gem:") next if k.start_with?("rubylib:") next if k.start_with?("<") v.values.map(&:total).sum > total * 0.01 end if template == "html" html_output(output, relevant_files) else relevant_files.keys.sort.each do |filename| output << "="*80 << "\n" output << filename << "\n" output << "-"*80 << "\n" format_file(output, filename, samples_by_file, total: total) end output << "="*80 << "\n" end end
def samples_by_file
def samples_by_file thread = @profile.main_thread if Hash === thread # live profile stack_table = @profile._stack_table filename_filter = FilenameFilter.new else stack_table = thread.stack_table filename_filter = ->(x) { x } end weights = thread[:weights] samples = thread[:samples] self_samples_by_frame = Hash.new do |h, k| h[k] = SamplesByLocation.new end samples.zip(weights).each do |stack_idx, weight| # self time top_frame_index = stack_table.stack_frame_idx(stack_idx) self_samples_by_frame[top_frame_index].self += weight # total time while stack_idx frame_idx = stack_table.stack_frame_idx(stack_idx) self_samples_by_frame[frame_idx].total += weight stack_idx = stack_table.stack_parent_idx(stack_idx) end end samples_by_file = Hash.new do |h, k| h[k] = Hash.new do |h2, k2| h2[k2] = SamplesByLocation.new end end self_samples_by_frame.each do |frame, samples| line = stack_table.frame_line_no(frame) func_index = stack_table.frame_func_idx(frame) filename = stack_table.func_filename(func_index) samples_by_file[filename][line] += samples end samples_by_file.transform_keys! do |filename| filename_filter.call(filename) end end
def total
def total thread = @profile.main_thread thread[:weights].sum end