Skip to content

Commit c4e1980

Browse files
authored
Merge pull request #125 from ywenc/init-html-view
Option to display file output in html view
2 parents 5d64993 + eb6bf26 commit c4e1980

File tree

2 files changed

+81
-28
lines changed

2 files changed

+81
-28
lines changed

lib/vernier/output/file_listing.rb

+41-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require_relative "filename_filter"
4+
require "cgi/util"
45

56
module Vernier
67
module Output
@@ -41,8 +42,6 @@ def samples_by_file
4142
h[k] = SamplesByLocation.new
4243
end
4344

44-
total = weights.sum
45-
4645
samples.zip(weights).each do |stack_idx, weight|
4746
# self time
4847
top_frame_index = stack_table.stack_frame_idx(stack_idx)
@@ -75,7 +74,7 @@ def samples_by_file
7574
end
7675
end
7776

78-
def output
77+
def output(template: nil)
7978
output = +""
8079

8180
relevant_files = samples_by_file.select do |k, v|
@@ -84,13 +83,18 @@ def output
8483
next if k.start_with?("<")
8584
v.values.map(&:total).sum > total * 0.01
8685
end
87-
relevant_files.keys.sort.each do |filename|
86+
87+
if template == "html"
88+
html_output(output, relevant_files)
89+
else
90+
relevant_files.keys.sort.each do |filename|
91+
output << "="*80 << "\n"
92+
output << filename << "\n"
93+
output << "-"*80 << "\n"
94+
format_file(output, filename, samples_by_file, total: total)
95+
end
8896
output << "="*80 << "\n"
89-
output << filename << "\n"
90-
output << "-"*80 << "\n"
91-
format_file(output, filename, samples_by_file, total: total)
9297
end
93-
output << "="*80 << "\n"
9498
end
9599

96100
def total
@@ -114,6 +118,35 @@ def format_file(output, filename, all_samples, total:)
114118
end
115119
end
116120
end
121+
122+
def html_output(output, relevant_files)
123+
output << "<pre>"
124+
output << " SELF FILE\n"
125+
relevant_files.sort_by {|k, v| -v.values.map(&:self).sum }.each do |filename, file_contents|
126+
tmpl = "<details style=\"display:inline-block;vertical-align:top;\"><summary>%s</summary>"
127+
output << sprintf("% 5.1f%% #{tmpl}\n", file_contents.values.map(&:self).sum * 100 / total.to_f, filename)
128+
format_file_html(output, filename, relevant_files)
129+
output << "</details>\n"
130+
end
131+
output << "</pre>"
132+
end
133+
134+
def format_file_html(output, filename, relevant_files)
135+
samples = relevant_files[filename]
136+
137+
# file_name, lines, file_wall, file_cpu, file_idle, file_sort
138+
output << sprintf(" TOTAL | SELF | LINE SOURCE\n")
139+
File.readlines(filename).each_with_index do |line, i|
140+
lineno = i + 1
141+
calls = samples[lineno]
142+
143+
if calls && calls.total > 0
144+
output << sprintf("%5.1f%% | %5.1f%% | % 4i %s", 100 * calls.total / total.to_f, 100 * calls.self / total.to_f, lineno, CGI::escapeHTML(line))
145+
else
146+
output << sprintf(" | | % 4i %s", lineno, CGI::escapeHTML(line))
147+
end
148+
end
149+
end
117150
end
118151
end
119152
end

test/output/test_file_listing.rb

+40-20
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,58 @@
33
require "test_helper"
44

55
class TestOutputFileListing < Minitest::Test
6-
def test_complex_profile
7-
result = Vernier.trace do
8-
# Proper Ruby sleep
9-
sleep 0.01
6+
describe "with a live profile" do
7+
before do
8+
@result = Vernier.trace do
9+
# Proper Ruby sleep
10+
sleep 0.01
1011

11-
# Sleep inside rb_thread_call_without_gvl
12-
GVLTest.sleep_without_gvl(0.01)
12+
# Sleep inside rb_thread_call_without_gvl
13+
GVLTest.sleep_without_gvl(0.01)
1314

14-
# Sleep with GVL held
15-
GVLTest.sleep_holding_gvl(0.01)
15+
# Sleep with GVL held
16+
GVLTest.sleep_holding_gvl(0.01)
1617

17-
# Ruby busy sleep
18-
target = Process.clock_gettime(Process::CLOCK_MONOTONIC) + 0.01
19-
while Process.clock_gettime(Process::CLOCK_MONOTONIC) < target
18+
# Ruby busy sleep
19+
target = Process.clock_gettime(Process::CLOCK_MONOTONIC) + 0.01
20+
while Process.clock_gettime(Process::CLOCK_MONOTONIC) < target
21+
end
2022
end
2123
end
2224

23-
output = Vernier::Output::FileListing.new(result).output
24-
assert_match(/\d+\.\d% \| *\d+\.\d% \| *\d+ +sleep 0\.01/, output)
25-
assert_match(/\d+\.\d% \| *\d+\.\d% \| *\d+ +GVLTest\.sleep_without_gvl/, output)
26-
assert_match(/\d+\.\d% \| *\d+\.\d% \| *\d+ +GVLTest\.sleep_holding_gvl/, output)
27-
assert_match(/\d+\.\d% \| *\d+\.\d% \| *\d+ +while Process\.clock_gettime/, output)
25+
def test_complex_profile
26+
output = Vernier::Output::FileListing.new(@result).output
27+
assert_match(/\d+\.\d% \| *\d+\.\d% \| *\d+ +sleep 0\.01/, output)
28+
assert_match(/\d+\.\d% \| *\d+\.\d% \| *\d+ +GVLTest\.sleep_without_gvl/, output)
29+
assert_match(/\d+\.\d% \| *\d+\.\d% \| *\d+ +GVLTest\.sleep_holding_gvl/, output)
30+
assert_match(/\d+\.\d% \| *\d+\.\d% \| *\d+ +while Process\.clock_gettime/, output)
31+
end
32+
33+
def test_html_output
34+
output = Vernier::Output::FileListing.new(@result).output(template: "html")
35+
assert_match(/<details style=\"display:inline-block;vertical-align:top;\"><summary>.+#{Regexp.escape(File.basename(__FILE__))}<\/summary>/, output)
36+
end
2837
end
2938

30-
def test_parsed_profile
31-
profile = Vernier::ParsedProfile.read_file(fixture_path("gvl_sleep.vernier.json"))
32-
output = Vernier::Output::FileListing.new(profile).output
33-
assert_includes output, <<TEXT
39+
describe "with a parsed profile" do
40+
before do
41+
@profile = Vernier::ParsedProfile.read_file(fixture_path("gvl_sleep.vernier.json"))
42+
end
43+
44+
def test_parsed_profile
45+
output = Vernier::Output::FileListing.new(@profile).output
46+
assert_includes output, <<TEXT
3447
24.8% | 0.0% | 44 run(:cfunc_sleep_gvl)
3548
24.7% | 0.0% | 45 run(:cfunc_sleep_idle)
3649
24.6% | 0.0% | 46 run(:ruby_sleep_gvl)
3750
24.7% | 0.0% | 47 run(:sleep_idle)
3851
TEXT
52+
end
53+
54+
def test_html_output
55+
output = Vernier::Output::FileListing.new(@profile).output(template: "html")
56+
assert_includes output,
57+
" 24.5% <details style=\"display:inline-block;vertical-align:top;\"><summary>examples/gvl_sleep.rb</summary>\n"
58+
end
3959
end
4060
end

0 commit comments

Comments
 (0)