ruby-prof 2.0.2 → 2.0.4

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.
@@ -1,168 +1,169 @@
1
- # encoding: utf-8
2
-
3
- require 'erb'
4
- require 'fileutils'
5
- require 'base64'
6
- require 'set'
7
- require 'stringio'
8
-
9
- module RubyProf
10
- # Prints a HTML visualization of the call tree.
11
- #
12
- # To use the printer:
13
- #
14
- # result = RubyProf.profile do
15
- # [code to profile]
16
- # end
17
- #
18
- # printer = RubyProf::CallStackPrinter.new(result)
19
- # printer.print(STDOUT)
20
-
21
- class CallStackPrinter < AbstractPrinter
22
- include ERB::Util
23
-
24
- # Specify print options.
25
- #
26
- # output - Any IO object, including STDOUT or a file.
27
- #
28
- # Keyword arguments:
29
- # title: - a String to override the default "ruby-prof call stack"
30
- # title of the report.
31
- #
32
- # threshold: - a float from 0 to 100 that sets the threshold of
33
- # results displayed.
34
- # Default value is 1.0
35
- #
36
- # expansion: - a float from 0 to 100 that sets the threshold of
37
- # results that are expanded, if the percent_total
38
- # exceeds it.
39
- # Default value is 10.0
40
- #
41
- # application: - a String to override the name of the application,
42
- # as it appears on the report.
43
- #
44
- # Also accepts min_percent:, max_percent:, filter_by:, and sort_method:
45
- # from AbstractPrinter.
46
- def print(output = STDOUT, title: "ruby-prof call stack", threshold: 1.0,
47
- expansion: 10.0, application: $PROGRAM_NAME,
48
- min_percent: 0, max_percent: 100, filter_by: :self_time, sort_method: nil, **)
49
- @min_percent = min_percent
50
- @max_percent = max_percent
51
- @filter_by = filter_by
52
- @sort_method = sort_method
53
- @title = title
54
- @threshold = threshold
55
- @expansion = expansion
56
- @application = application
57
- output << ERB.new(self.template).result(binding)
58
- end
59
-
60
- def print_stack(output, visited, call_tree, parent_time)
61
- total_time = call_tree.total_time
62
- percent_parent = (total_time/parent_time)*100
63
- percent_total = (total_time/@overall_time)*100
64
- return unless percent_total > min_percent
65
- color = self.color(percent_total)
66
- visible = percent_total >= threshold
67
- expanded = percent_total >= expansion
68
- display = visible ? "block" : "none"
69
-
70
- output << "<li class=\"color#{color}\" style=\"display:#{display}\">" << "\n"
71
-
72
- if visited.include?(call_tree)
73
- output << "<a href=\"#\" class=\"toggle empty\" ></a>" << "\n"
74
- output << "<span>%s %s</span>" % [link(call_tree.target, true), graph_link(call_tree)] << "\n"
75
- else
76
- visited << call_tree
77
-
78
- if call_tree.children.empty?
79
- output << "<a href=\"#\" class=\"toggle empty\" ></a>" << "\n"
80
- else
81
- visible_children = call_tree.children.any?{|ci| (ci.total_time/@overall_time)*100 >= threshold}
82
- image = visible_children ? (expanded ? "minus" : "plus") : "empty"
83
- output << "<a href=\"#\" class=\"toggle #{image}\" ></a>" << "\n"
84
- end
85
- output << "<span>%4.2f%% (%4.2f%%) %s %s</span>" % [percent_total, percent_parent,
86
- link(call_tree.target, false), graph_link(call_tree)] << "\n"
87
-
88
- unless call_tree.children.empty?
89
- output << (expanded ? '<ul>' : '<ul style="display:none">') << "\n"
90
- call_tree.children.sort_by{|c| -c.total_time}.each do |child_call_tree|
91
- print_stack(output, visited, child_call_tree, total_time)
92
- end
93
- output << '</ul>' << "\n"
94
- end
95
-
96
- visited.delete(call_tree)
97
- end
98
- output << '</li>' << "\n"
99
- end
100
-
101
- def name(call_tree)
102
- method = call_tree.target
103
- method.full_name
104
- end
105
-
106
- def link(method, recursive)
107
- method_name = "#{recursive ? '*' : ''}#{method.full_name}"
108
- if method.source_file.nil?
109
- h method_name
110
- else
111
- file = File.expand_path(method.source_file)
112
- "<a href=\"file://#{file}##{method.line}\">#{h method_name}</a>"
113
- end
114
- end
115
-
116
- def graph_link(call_tree)
117
- total_calls = call_tree.target.called
118
- totals = total_calls.to_s
119
- "[#{call_tree.called} calls, #{totals} total]"
120
- end
121
-
122
- def method_href(method)
123
- h(method.full_name.gsub(/[><#\.\?=:]/,"_"))
124
- end
125
-
126
- def total_time(call_trees)
127
- sum(call_trees.map{|ci| ci.total_time})
128
- end
129
-
130
- def sum(a)
131
- a.inject(0.0){|s,t| s+=t}
132
- end
133
-
134
- def dump(ci)
135
- $stderr.printf "%s/%d t:%f s:%f w:%f \n", ci, ci.object_id, ci.total_time, ci.self_time, ci.wait_time
136
- end
137
-
138
- def color(p)
139
- case i = p.to_i
140
- when 0..5
141
- "01"
142
- when 5..10
143
- "05"
144
- when 100
145
- "9"
146
- else
147
- "#{i/10}"
148
- end
149
- end
150
-
151
- attr_reader :application, :title, :threshold, :expansion
152
-
153
- def arguments
154
- ARGV.join(' ')
155
- end
156
-
157
- def base64_image
158
- @data ||= begin
159
- file = open_asset('call_stack_printer.png')
160
- Base64.encode64(file).gsub(/\n/, '')
161
- end
162
- end
163
-
164
- def template
165
- open_asset('call_stack_printer.html.erb')
166
- end
167
- end
168
- end
1
+ # encoding: utf-8
2
+
3
+ require 'erb'
4
+ require 'fileutils'
5
+ require 'base64'
6
+ require 'set'
7
+ require 'stringio'
8
+
9
+ module RubyProf
10
+ # Prints a HTML visualization of the call tree.
11
+ #
12
+ # To use the printer:
13
+ #
14
+ # result = RubyProf.profile do
15
+ # [code to profile]
16
+ # end
17
+ #
18
+ # printer = RubyProf::CallStackPrinter.new(result)
19
+ # printer.print(STDOUT)
20
+
21
+ class CallStackPrinter < AbstractPrinter
22
+ include ERB::Util
23
+
24
+ # Specify print options.
25
+ #
26
+ # output - Any IO object, including STDOUT or a file.
27
+ #
28
+ # Keyword arguments:
29
+ # title: - a String to override the default "ruby-prof call stack"
30
+ # title of the report.
31
+ #
32
+ # threshold: - a float from 0 to 100 that sets the threshold of
33
+ # results displayed.
34
+ # Default value is 1.0
35
+ #
36
+ # expansion: - a float from 0 to 100 that sets the threshold of
37
+ # results that are expanded, if the percent_total
38
+ # exceeds it.
39
+ # Default value is 10.0
40
+ #
41
+ # application: - a String to override the name of the application,
42
+ # as it appears on the report.
43
+ #
44
+ # Also accepts min_percent:, max_percent:, filter_by:, and sort_method:
45
+ # from AbstractPrinter.
46
+ def print(output = STDOUT, title: "ruby-prof call stack", threshold: 1.0,
47
+ expansion: 10.0, application: $PROGRAM_NAME,
48
+ min_percent: 0, max_percent: 100, filter_by: :self_time, sort_method: nil, max_depth: nil, **)
49
+ @min_percent = min_percent
50
+ @max_percent = max_percent
51
+ @filter_by = filter_by
52
+ @sort_method = sort_method
53
+ @max_depth = max_depth
54
+ @title = title
55
+ @threshold = threshold
56
+ @expansion = expansion
57
+ @application = application
58
+ output << ERB.new(self.template).result(binding)
59
+ end
60
+
61
+ def print_stack(output, visited, call_tree, parent_time, depth = 0)
62
+ total_time = call_tree.total_time
63
+ percent_parent = (total_time/parent_time)*100
64
+ percent_total = (total_time/@overall_time)*100
65
+ return unless percent_total > min_percent
66
+ color = self.color(percent_total)
67
+ visible = percent_total >= threshold
68
+ expanded = percent_total >= expansion
69
+ display = visible ? "block" : "none"
70
+
71
+ output << "<li class=\"color#{color}\" style=\"display:#{display}\">" << "\n"
72
+
73
+ if visited.include?(call_tree)
74
+ output << "<a href=\"#\" class=\"toggle empty\" ></a>" << "\n"
75
+ output << "<span>%s %s</span>" % [link(call_tree.target, true), graph_link(call_tree)] << "\n"
76
+ else
77
+ visited << call_tree
78
+
79
+ if call_tree.children.empty? || (@max_depth && depth >= @max_depth)
80
+ output << "<a href=\"#\" class=\"toggle empty\" ></a>" << "\n"
81
+ else
82
+ visible_children = call_tree.children.any?{|ci| (ci.total_time/@overall_time)*100 >= threshold}
83
+ image = visible_children ? (expanded ? "minus" : "plus") : "empty"
84
+ output << "<a href=\"#\" class=\"toggle #{image}\" ></a>" << "\n"
85
+ end
86
+ output << "<span>%4.2f%% (%4.2f%%) %s %s</span>" % [percent_total, percent_parent,
87
+ link(call_tree.target, false), graph_link(call_tree)] << "\n"
88
+
89
+ unless call_tree.children.empty? || (@max_depth && depth >= @max_depth)
90
+ output << (expanded ? '<ul>' : '<ul style="display:none">') << "\n"
91
+ call_tree.children.sort_by{|c| -c.total_time}.each do |child_call_tree|
92
+ print_stack(output, visited, child_call_tree, total_time, depth + 1)
93
+ end
94
+ output << '</ul>' << "\n"
95
+ end
96
+
97
+ visited.delete(call_tree)
98
+ end
99
+ output << '</li>' << "\n"
100
+ end
101
+
102
+ def name(call_tree)
103
+ method = call_tree.target
104
+ method.full_name
105
+ end
106
+
107
+ def link(method, recursive)
108
+ method_name = "#{recursive ? '*' : ''}#{method.full_name}"
109
+ if method.source_file.nil?
110
+ h method_name
111
+ else
112
+ file = File.expand_path(method.source_file)
113
+ "<a href=\"file://#{file}##{method.line}\">#{h method_name}</a>"
114
+ end
115
+ end
116
+
117
+ def graph_link(call_tree)
118
+ total_calls = call_tree.target.called
119
+ totals = total_calls.to_s
120
+ "[#{call_tree.called} calls, #{totals} total]"
121
+ end
122
+
123
+ def method_href(method)
124
+ h(method.full_name.gsub(/[><#\.\?=:]/,"_"))
125
+ end
126
+
127
+ def total_time(call_trees)
128
+ sum(call_trees.map{|ci| ci.total_time})
129
+ end
130
+
131
+ def sum(a)
132
+ a.inject(0.0){|s,t| s+=t}
133
+ end
134
+
135
+ def dump(ci)
136
+ $stderr.printf "%s/%d t:%f s:%f w:%f \n", ci, ci.object_id, ci.total_time, ci.self_time, ci.wait_time
137
+ end
138
+
139
+ def color(p)
140
+ case i = p.to_i
141
+ when 0..5
142
+ "01"
143
+ when 5..10
144
+ "05"
145
+ when 100
146
+ "9"
147
+ else
148
+ "#{i/10}"
149
+ end
150
+ end
151
+
152
+ attr_reader :application, :title, :threshold, :expansion
153
+
154
+ def arguments
155
+ ARGV.join(' ')
156
+ end
157
+
158
+ def base64_image
159
+ @data ||= begin
160
+ file = open_asset('call_stack_printer.png')
161
+ Base64.encode64(file).gsub(/\n/, '')
162
+ end
163
+ end
164
+
165
+ def template
166
+ open_asset('call_stack_printer.html.erb')
167
+ end
168
+ end
169
+ end
@@ -1,79 +1,78 @@
1
- # encoding: utf-8
2
-
3
- require 'erb'
4
- require 'json'
5
-
6
- module RubyProf
7
- # Prints a HTML flame graph visualization of the call tree.
8
- #
9
- # To use the printer:
10
- #
11
- # result = RubyProf.profile do
12
- # [code to profile]
13
- # end
14
- #
15
- # printer = RubyProf::FlameGraphPrinter.new(result)
16
- # printer.print(STDOUT)
17
-
18
- class FlameGraphPrinter < AbstractPrinter
19
- include ERB::Util
20
-
21
- # Specify print options.
22
- #
23
- # output - Any IO object, including STDOUT or a file.
24
- #
25
- # Keyword arguments:
26
- # title: - a String to override the default "ruby-prof flame graph"
27
- # title of the report.
28
- #
29
- # Also accepts min_percent:, max_percent:, filter_by:, and sort_method:
30
- # from AbstractPrinter.
31
- def print(output = STDOUT, title: "ruby-prof flame graph",
32
- min_percent: 0, max_percent: 100, filter_by: :self_time, sort_method: nil, **)
33
- @min_percent = min_percent
34
- @max_percent = max_percent
35
- @filter_by = filter_by
36
- @sort_method = sort_method
37
- @title = title
38
- output << ERB.new(self.template).result(binding)
39
- end
40
-
41
- attr_reader :title
42
-
43
- def build_flame_data(call_tree, visited = Set.new)
44
- node = {
45
- name: call_tree.target.full_name,
46
- value: call_tree.total_time,
47
- self_value: call_tree.self_time,
48
- called: call_tree.called,
49
- children: []
50
- }
51
-
52
- unless visited.include?(call_tree.target)
53
- visited.add(call_tree.target)
54
- call_tree.children.sort_by { |c| -c.total_time }.each do |child|
55
- node[:children] << build_flame_data(child, visited)
56
- end
57
- visited.delete(call_tree.target)
58
- end
59
-
60
- node
61
- end
62
-
63
- def flame_data_json
64
- threads = @result.threads.map do |thread|
65
- {
66
- id: thread.id,
67
- fiber_id: thread.fiber_id,
68
- total_time: thread.total_time,
69
- data: build_flame_data(thread.call_tree)
70
- }
71
- end
72
- JSON.generate(threads)
73
- end
74
-
75
- def template
76
- open_asset('flame_graph_printer.html.erb')
77
- end
78
- end
79
- end
1
+ # encoding: utf-8
2
+
3
+ require 'erb'
4
+ require 'json'
5
+
6
+ module RubyProf
7
+ # Prints a HTML flame graph visualization of the call tree.
8
+ #
9
+ # To use the printer:
10
+ #
11
+ # result = RubyProf.profile do
12
+ # [code to profile]
13
+ # end
14
+ #
15
+ # printer = RubyProf::FlameGraphPrinter.new(result)
16
+ # printer.print(STDOUT)
17
+
18
+ class FlameGraphPrinter < AbstractPrinter
19
+ include ERB::Util
20
+
21
+ # Specify print options.
22
+ #
23
+ # output - Any IO object, including STDOUT or a file.
24
+ #
25
+ # Keyword arguments:
26
+ # title: - a String to override the default "ruby-prof flame graph"
27
+ # title of the report.
28
+ #
29
+ # Also accepts min_percent:, max_percent:, filter_by:, and sort_method:
30
+ # from AbstractPrinter.
31
+ def print(output = STDOUT, title: "ruby-prof flame graph",
32
+ min_percent: 0, max_percent: 100, filter_by: :self_time, sort_method: nil, max_depth: nil, **)
33
+ @min_percent = min_percent
34
+ @max_percent = max_percent
35
+ @filter_by = filter_by
36
+ @sort_method = sort_method
37
+ @max_depth = max_depth
38
+ @title = title
39
+ output << ERB.new(self.template).result(binding)
40
+ end
41
+
42
+ attr_reader :title
43
+
44
+ def build_flame_data(call_tree, depth = 0)
45
+ node = {
46
+ name: call_tree.target.full_name,
47
+ value: call_tree.total_time,
48
+ self_value: call_tree.self_time,
49
+ called: call_tree.called,
50
+ children: []
51
+ }
52
+
53
+ if @max_depth.nil? || depth < @max_depth
54
+ call_tree.children.each do |child|
55
+ node[:children] << build_flame_data(child, depth + 1)
56
+ end
57
+ end
58
+
59
+ node
60
+ end
61
+
62
+ def flame_data_json
63
+ threads = @result.threads.map do |thread|
64
+ {
65
+ id: thread.id,
66
+ fiber_id: thread.fiber_id,
67
+ total_time: thread.total_time,
68
+ data: build_flame_data(thread.call_tree)
69
+ }
70
+ end
71
+ JSON.generate(threads)
72
+ end
73
+
74
+ def template
75
+ open_asset('flame_graph_printer.html.erb')
76
+ end
77
+ end
78
+ end
@@ -1,3 +1,3 @@
1
1
  module RubyProf
2
- VERSION = "2.0.2"
2
+ VERSION = "2.0.4"
3
3
  end
data/ruby-prof.gemspec CHANGED
@@ -1,66 +1,66 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
- $:.push File.expand_path("../lib", __FILE__)
4
- require "ruby-prof/version"
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "ruby-prof"
8
-
9
- spec.homepage = "https://github.com/ruby-prof/ruby-prof/"
10
- spec.summary = "Fast Ruby profiler"
11
- spec.description = <<-EOF
12
- ruby-prof is a fast code profiler for Ruby. It is a C extension and
13
- therefore is many times faster than the standard Ruby profiler. It
14
- supports both flat and graph profiles. For each method, graph profiles
15
- show how long the method ran, which methods called it and which
16
- methods it called. RubyProf generate both text and html and can output
17
- it to standard out or to a file.
18
- EOF
19
- spec.license = 'BSD-2-Clause'
20
- spec.version = RubyProf::VERSION
21
-
22
- spec.metadata = {
23
- "bug_tracker_uri" => "https://github.com/ruby-prof/ruby-prof/issues",
24
- "changelog_uri" => "https://github.com/ruby-prof/ruby-prof/blob/master/CHANGELOG.md",
25
- "documentation_uri" => "https://ruby-prof.github.io/",
26
- "source_code_uri" => "https://github.com/ruby-prof/ruby-prof/tree/v#{spec.version}",
27
- }
28
-
29
- spec.author = "Shugo Maeda, Charlie Savage, Roger Pack, Stefan Kaes"
30
- spec.email = "shugo@ruby-lang.org, cfis@savagexi.com, rogerdpack@gmail.com, skaes@railsexpress.de"
31
- spec.platform = Gem::Platform::RUBY
32
- spec.require_path = "lib"
33
- spec.bindir = "bin"
34
- spec.executables = ["ruby-prof", "ruby-prof-check-trace"]
35
- spec.extensions = ["ext/ruby_prof/extconf.rb"]
36
- spec.files = Dir['CHANGELOG.md',
37
- 'LICENSE',
38
- 'Rakefile',
39
- 'README.md',
40
- 'ruby-prof.gemspec',
41
- 'bin/ruby-prof',
42
- 'bin/ruby-prof-check-trace',
43
- 'docs/**/*',
44
- 'ext/ruby_prof/extconf.rb',
45
- 'ext/ruby_prof/*.c',
46
- 'ext/ruby_prof/*.h',
47
- 'ext/ruby_prof/vc/*.sln',
48
- 'ext/ruby_prof/vc/*.vcxproj',
49
- 'lib/ruby-prof.rb',
50
- 'lib/unprof.rb',
51
- 'lib/ruby-prof/*.rb',
52
- 'lib/ruby-prof/assets/*',
53
- 'lib/ruby-prof/profile/*.rb',
54
- 'lib/ruby-prof/printers/*.rb',
55
- 'test/*.rb']
56
-
57
- spec.test_files = Dir["test/test_*.rb"]
58
- spec.required_ruby_version = '>= 3.2.0'
59
- spec.date = Time.now.strftime('%Y-%m-%d')
60
- spec.homepage = 'https://github.com/ruby-prof/ruby-prof'
61
- spec.add_dependency('base64')
62
- spec.add_dependency('ostruct')
63
- spec.add_development_dependency('minitest')
64
- spec.add_development_dependency('rake-compiler')
65
- spec.add_development_dependency('rdoc')
66
- end
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ $:.push File.expand_path("../lib", __FILE__)
4
+ require "ruby-prof/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ruby-prof"
8
+
9
+ spec.homepage = "https://github.com/ruby-prof/ruby-prof/"
10
+ spec.summary = "Fast Ruby profiler"
11
+ spec.description = <<-EOF
12
+ ruby-prof is a fast code profiler for Ruby. It is a C extension and
13
+ therefore is many times faster than the standard Ruby profiler. It
14
+ supports both flat and graph profiles. For each method, graph profiles
15
+ show how long the method ran, which methods called it and which
16
+ methods it called. RubyProf generate both text and html and can output
17
+ it to standard out or to a file.
18
+ EOF
19
+ spec.license = 'BSD-2-Clause'
20
+ spec.version = RubyProf::VERSION
21
+
22
+ spec.metadata = {
23
+ "bug_tracker_uri" => "https://github.com/ruby-prof/ruby-prof/issues",
24
+ "changelog_uri" => "https://github.com/ruby-prof/ruby-prof/blob/master/CHANGELOG.md",
25
+ "documentation_uri" => "https://ruby-prof.github.io/",
26
+ "source_code_uri" => "https://github.com/ruby-prof/ruby-prof/tree/#{spec.version}",
27
+ }
28
+
29
+ spec.author = "Shugo Maeda, Charlie Savage, Roger Pack, Stefan Kaes"
30
+ spec.email = "shugo@ruby-lang.org, cfis@savagexi.com, rogerdpack@gmail.com, skaes@railsexpress.de"
31
+ spec.platform = Gem::Platform::RUBY
32
+ spec.require_path = "lib"
33
+ spec.bindir = "bin"
34
+ spec.executables = ["ruby-prof", "ruby-prof-check-trace"]
35
+ spec.extensions = ["ext/ruby_prof/extconf.rb"]
36
+ spec.files = Dir['CHANGELOG.md',
37
+ 'LICENSE',
38
+ 'Rakefile',
39
+ 'README.md',
40
+ 'ruby-prof.gemspec',
41
+ 'bin/ruby-prof',
42
+ 'bin/ruby-prof-check-trace',
43
+ 'docs/**/*',
44
+ 'ext/ruby_prof/extconf.rb',
45
+ 'ext/ruby_prof/*.c',
46
+ 'ext/ruby_prof/*.h',
47
+ 'ext/ruby_prof/vc/*.sln',
48
+ 'ext/ruby_prof/vc/*.vcxproj',
49
+ 'lib/ruby-prof.rb',
50
+ 'lib/unprof.rb',
51
+ 'lib/ruby-prof/*.rb',
52
+ 'lib/ruby-prof/assets/*',
53
+ 'lib/ruby-prof/profile/*.rb',
54
+ 'lib/ruby-prof/printers/*.rb',
55
+ 'test/*.rb']
56
+
57
+ spec.test_files = Dir["test/test_*.rb"]
58
+ spec.required_ruby_version = '>= 3.2.0'
59
+ spec.date = Time.now.strftime('%Y-%m-%d')
60
+ spec.homepage = 'https://github.com/ruby-prof/ruby-prof'
61
+ spec.add_dependency('base64')
62
+ spec.add_dependency('ostruct')
63
+ spec.add_development_dependency('minitest')
64
+ spec.add_development_dependency('rake-compiler')
65
+ spec.add_development_dependency('rdoc')
66
+ end
@@ -96,4 +96,15 @@ class PrinterFlatTest < TestCase
96
96
 
97
97
  assert self_percents.min >= 0.1
98
98
  end
99
+
100
+ def test_flat_result_nil_sort_method
101
+ printer = RubyProf::FlatPrinter.new(self.run_profile)
102
+
103
+ output = StringIO.new
104
+ printer.print(output, sort_method: nil)
105
+ total_times = flat_output_nth_column_values(output.string, 2)
106
+
107
+ # nil sort_method should fall back to default (total_time)
108
+ assert_sorted total_times
109
+ end
99
110
  end