ruby-prof 0.8.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/CHANGES +23 -13
  2. data/{README → README.rdoc} +118 -111
  3. data/Rakefile +14 -26
  4. data/bin/ruby-prof +16 -4
  5. data/examples/empty.png +0 -0
  6. data/examples/graph.dot +106 -0
  7. data/examples/graph.png +0 -0
  8. data/examples/minus.png +0 -0
  9. data/examples/multi.flat.txt +23 -0
  10. data/examples/multi.graph.html +906 -0
  11. data/examples/multi.grind.dat +194 -0
  12. data/examples/multi.stack.html +573 -0
  13. data/examples/plus.png +0 -0
  14. data/examples/stack.html +573 -0
  15. data/ext/ruby_prof/extconf.rb +0 -1
  16. data/ext/ruby_prof/measure_allocations.h +6 -6
  17. data/ext/ruby_prof/measure_cpu_time.h +5 -5
  18. data/ext/ruby_prof/measure_gc_runs.h +1 -1
  19. data/ext/ruby_prof/measure_gc_time.h +1 -1
  20. data/ext/ruby_prof/measure_memory.h +4 -4
  21. data/ext/ruby_prof/measure_process_time.h +1 -1
  22. data/ext/ruby_prof/measure_wall_time.h +1 -1
  23. data/ext/ruby_prof/ruby_prof.c +240 -167
  24. data/ext/ruby_prof/ruby_prof.h +12 -10
  25. data/ext/ruby_prof/version.h +3 -3
  26. data/lib/ruby-prof.rb +7 -1
  27. data/lib/ruby-prof/abstract_printer.rb +5 -5
  28. data/lib/ruby-prof/aggregate_call_info.rb +9 -3
  29. data/lib/ruby-prof/call_info.rb +66 -1
  30. data/lib/ruby-prof/call_stack_printer.rb +751 -0
  31. data/lib/ruby-prof/call_tree_printer.rb +2 -2
  32. data/lib/ruby-prof/dot_printer.rb +151 -0
  33. data/lib/ruby-prof/empty.png +0 -0
  34. data/lib/ruby-prof/flat_printer.rb +16 -16
  35. data/lib/ruby-prof/flat_printer_with_line_numbers.rb +13 -13
  36. data/lib/ruby-prof/graph_html_printer.rb +58 -36
  37. data/lib/ruby-prof/graph_printer.rb +30 -30
  38. data/lib/ruby-prof/method_info.rb +24 -4
  39. data/lib/ruby-prof/minus.png +0 -0
  40. data/lib/ruby-prof/multi_printer.rb +54 -0
  41. data/lib/ruby-prof/plus.png +0 -0
  42. data/lib/ruby-prof/rack.rb +28 -0
  43. data/lib/ruby-prof/result.rb +70 -0
  44. data/lib/ruby-prof/symbol_to_proc.rb +1 -1
  45. data/lib/ruby-prof/task.rb +19 -19
  46. data/lib/ruby-prof/test.rb +3 -3
  47. data/lib/ruby_prof.so +0 -0
  48. data/rails/environment/profile.rb +2 -2
  49. data/rails/example/example_test.rb +2 -2
  50. data/rails/profile_test_helper.rb +1 -1
  51. data/test/aggregate_test.rb +21 -6
  52. data/test/basic_test.rb +3 -3
  53. data/test/duplicate_names_test.rb +2 -2
  54. data/test/enumerable_test.rb +2 -2
  55. data/test/exceptions_test.rb +2 -2
  56. data/test/exclude_threads_test.rb +45 -45
  57. data/test/exec_test.rb +2 -2
  58. data/test/line_number_test.rb +11 -11
  59. data/test/measurement_test.rb +4 -3
  60. data/test/method_elimination_test.rb +74 -0
  61. data/test/module_test.rb +7 -7
  62. data/test/multi_printer_test.rb +81 -0
  63. data/test/no_method_class_test.rb +2 -2
  64. data/test/prime.rb +7 -10
  65. data/test/printers_test.rb +57 -47
  66. data/test/recursive_test.rb +23 -62
  67. data/test/singleton_test.rb +3 -2
  68. data/test/stack_printer_test.rb +74 -0
  69. data/test/stack_test.rb +1 -1
  70. data/test/start_stop_test.rb +2 -2
  71. data/test/test_suite.rb +9 -0
  72. data/test/thread_test.rb +17 -17
  73. data/test/unique_call_path_test.rb +4 -4
  74. metadata +29 -8
@@ -8,7 +8,7 @@ module RubyProf
8
8
  def print(output = STDOUT, options = {})
9
9
  @output = output
10
10
  setup_options(options)
11
-
11
+
12
12
  # add a header - this information is somewhat arbitrary
13
13
  @output << "events: "
14
14
  case RubyProf.measure_mode
@@ -56,7 +56,7 @@ module RubyProf
56
56
  end
57
57
 
58
58
  def print_methods(thread_id, methods)
59
- methods.reverse_each do |method|
59
+ methods.reverse_each do |method|
60
60
  # Print out the file and method name
61
61
  @output << "fl=#{file(method)}\n"
62
62
  @output << "fn=#{method_name(method)}\n"
@@ -0,0 +1,151 @@
1
+ require 'set'
2
+ require 'ruby-prof/abstract_printer'
3
+
4
+ module RubyProf
5
+ # Generates a graphviz graph in dot format.
6
+ # To use the dot printer:
7
+ #
8
+ # result = RubyProf.profile do
9
+ # [code to profile]
10
+ # end
11
+ #
12
+ # printer = RubyProf::DotPrinter.new(result)
13
+ # printer.print(STDOUT)
14
+ #
15
+ # You can use either dot viewer such as GraphViz, or the dot command line tool
16
+ # to reformat the output into a wide variety of outputs:
17
+ #
18
+ # dot -Tpng graph.dot > graph.png
19
+ #
20
+ class DotPrinter < RubyProf::AbstractPrinter
21
+ CLASS_COLOR = '"#666666"'
22
+ EDGE_COLOR = '"#666666"'
23
+
24
+ # Creates the DotPrinter using a RubyProf::Result.
25
+ def initialize(result)
26
+ super(result)
27
+ @seen_methods = Set.new
28
+ end
29
+
30
+ # Print a graph report to the provided output.
31
+ #
32
+ # output - Any IO object, including STDOUT or a file. The default value is
33
+ # STDOUT.
34
+ #
35
+ # options - Hash of print options. See #setup_options
36
+ # for more information.
37
+ #
38
+ # When profiling results that cover a large number of method calls it
39
+ # helps to use the :min_percent option, for example:
40
+ #
41
+ # DotPrinter.new(result).print(STDOUT, :min_percent=>5)
42
+ #
43
+ def print(output = STDOUT, options = {})
44
+ @output = output
45
+ setup_options(options)
46
+
47
+ total_time = thread_times.values.inject{|a,b| a+b}
48
+
49
+ puts 'digraph "Profile" {'
50
+ puts "label=\"#{mode_name} >=#{min_percent}%\\nTotal: #{total_time}\";"
51
+ puts "labelloc=t;"
52
+ puts "labeljust=l;"
53
+ print_threads
54
+ puts '}'
55
+ end
56
+
57
+ private
58
+
59
+ # Something of a hack, figure out which constant went with the
60
+ # RubyProf.measure_mode so that we can display it. Otherwise it's easy to
61
+ # forget what measurement was made.
62
+ def mode_name
63
+ mode = RubyProf.constants.find{|c| RubyProf.const_get(c) == RubyProf.measure_mode}
64
+ end
65
+
66
+ # Computes the total time per thread:
67
+ def thread_times
68
+ @thread_times ||= begin
69
+ times = {}
70
+ @result.threads.each do |thread_id, methods|
71
+ toplevel = methods.sort.last
72
+
73
+ total_time = toplevel.total_time
74
+ # This looks like a hack for very small times... from GraphPrinter
75
+ total_time = 0.01 if total_time == 0
76
+ times[thread_id] = total_time
77
+ end
78
+
79
+ times
80
+ end
81
+ end
82
+
83
+ def print_threads
84
+ # sort assumes that spawned threads have higher object_ids
85
+ @result.threads.sort.each do |thread_id, methods|
86
+ puts "subgraph \"Thread #{thread_id}\" {"
87
+
88
+ print_methods(thread_id, methods)
89
+ puts "}"
90
+
91
+ print_classes(thread_id, methods)
92
+ end
93
+ end
94
+
95
+ # Determines an ID to use to represent the subject in the Dot file.
96
+ def dot_id(subject)
97
+ subject.object_id
98
+ end
99
+
100
+ def print_methods(thread_id, methods)
101
+ total_time = thread_times[thread_id]
102
+ # Print each method in total time order
103
+ methods.reverse_each do |method|
104
+ total_percentage = (method.total_time/total_time) * 100
105
+ self_percentage = (method.self_time/total_time) * 100
106
+
107
+ next if total_percentage < min_percent
108
+ name = method_name(method).split("#").last
109
+ puts "#{dot_id(method)} [label=\"#{name}\\n(#{total_percentage.round}%)\"];"
110
+ @seen_methods << method
111
+ print_edges(total_time, method)
112
+ end
113
+ end
114
+
115
+ def print_classes(thread_id, methods)
116
+ methods.group_by{|m| m.klass_name}.each do |cls, methods|
117
+ # Filter down to just seen methods
118
+ big_methods, small_methods = methods.partition{|m| @seen_methods.include? m}
119
+
120
+ if !big_methods.empty?
121
+ puts "subgraph cluster_#{cls.object_id} {"
122
+ puts "label = \"#{cls}\";"
123
+ puts "fontcolor = #{CLASS_COLOR};"
124
+ puts "fontsize = 16;"
125
+ puts "color = #{CLASS_COLOR};"
126
+ big_methods.each do |m|
127
+ puts "#{m.object_id};"
128
+ end
129
+ puts "}"
130
+ end
131
+ end
132
+ end
133
+
134
+ def print_edges(total_time, method)
135
+ method.aggregate_children.sort_by(&:total_time).reverse.each do |child|
136
+
137
+ target_percentage = (child.target.total_time / total_time) * 100.0
138
+ next if target_percentage < min_percent
139
+
140
+ # Get children method
141
+ puts "#{dot_id(method)} -> #{dot_id(child.target)} [label=\"#{child.called}/#{child.target.called}\" fontsize=10 fontcolor=#{EDGE_COLOR}];"
142
+ end
143
+ end
144
+
145
+ # Silly little helper for printing to the @output
146
+ def puts(str)
147
+ @output.puts(str)
148
+ end
149
+
150
+ end
151
+ end
Binary file
@@ -1,7 +1,7 @@
1
1
  require 'ruby-prof/abstract_printer'
2
2
 
3
3
  module RubyProf
4
- # Generates flat[link:files/examples/flat_txt.html] profile reports as text.
4
+ # Generates flat[link:files/examples/flat_txt.html] profile reports as text.
5
5
  # To use the flat printer:
6
6
  #
7
7
  # result = RubyProf.profile do
@@ -13,28 +13,28 @@ module RubyProf
13
13
  #
14
14
  class FlatPrinter < AbstractPrinter
15
15
  # Print a flat profile report to the provided output.
16
- #
17
- # output - Any IO oject, including STDOUT or a file.
16
+ #
17
+ # output - Any IO oject, including STDOUT or a file.
18
18
  # The default value is STDOUT.
19
- #
20
- # options - Hash of print options. See #setup_options
19
+ #
20
+ # options - Hash of print options. See #setup_options
21
21
  # for more information.
22
22
  #
23
23
  def print(output = STDOUT, options = {})
24
24
  @output = output
25
25
  setup_options(options)
26
26
  print_threads
27
- end
28
-
29
- private
30
-
27
+ end
28
+
29
+ private
30
+
31
31
  def print_threads
32
32
  @result.threads.each do |thread_id, methods|
33
33
  print_methods(thread_id, methods)
34
34
  @output << "\n" * 2
35
35
  end
36
36
  end
37
-
37
+
38
38
  def print_methods(thread_id, methods)
39
39
  # Get total time
40
40
  toplevel = methods.max
@@ -42,27 +42,27 @@ module RubyProf
42
42
  if total_time == 0
43
43
  total_time = 0.01
44
44
  end
45
-
45
+
46
46
  # Now sort methods by largest self time,
47
47
  # not total time like in other printouts
48
48
  methods = methods.sort do |m1, m2|
49
49
  m1.self_time <=> m2.self_time
50
50
  end.reverse
51
-
51
+
52
52
  @output << "Thread ID: %d\n" % thread_id
53
53
  @output << "Total: %0.6f\n" % total_time
54
54
  @output << "\n"
55
55
  @output << " %self total self wait child calls name\n"
56
56
 
57
- sum = 0
57
+ sum = 0
58
58
  methods.each do |method|
59
59
  self_percent = (method.self_time / total_time) * 100
60
60
  next if self_percent < min_percent
61
-
61
+
62
62
  sum += method.self_time
63
63
  #self_time_called = method.called > 0 ? method.self_time/method.called : 0
64
64
  #total_time_called = method.called > 0? method.total_time/method.called : 0
65
-
65
+
66
66
  @output << "%6.2f %8.2f %8.2f %8.2f %8.2f %8d %s\n" % [
67
67
  method.self_time / total_time * 100, # %self
68
68
  method.total_time, # total
@@ -75,4 +75,4 @@ module RubyProf
75
75
  end
76
76
  end
77
77
  end
78
- end
78
+ end
@@ -1,7 +1,7 @@
1
1
  require 'ruby-prof/abstract_printer'
2
2
  require 'pathname'
3
3
  module RubyProf
4
- # Generates flat[link:files/examples/flat_txt.html] profile reports as text.
4
+ # Generates flat[link:files/examples/flat_txt.html] profile reports as text.
5
5
  # To use the flat printer with line numbers:
6
6
  #
7
7
  # result = RubyProf.profile do
@@ -20,26 +20,26 @@ module RubyProf
20
20
  if total_time == 0
21
21
  total_time = 0.01
22
22
  end
23
-
23
+
24
24
  # Now sort methods by largest self time,
25
25
  # not total time like in other printouts
26
26
  methods = methods.sort do |m1, m2|
27
27
  m1.self_time <=> m2.self_time
28
28
  end.reverse
29
-
29
+
30
30
  @output << "Thread ID: %d\n" % thread_id
31
31
  @output << "Total: %0.6f\n" % total_time
32
32
  @output << "\n"
33
33
  @output << " %self total self wait child calls name\n"
34
- sum = 0
34
+ sum = 0
35
35
  methods.each do |method|
36
36
  self_percent = (method.self_time / total_time) * 100
37
37
  next if self_percent < min_percent
38
-
38
+
39
39
  sum += method.self_time
40
40
  #self_time_called = method.called > 0 ? method.self_time/method.called : 0
41
41
  #total_time_called = method.called > 0? method.total_time/method.called : 0
42
-
42
+
43
43
  @output << "%6.2f %8.2f %8.2f %8.2f %8.2f %8d %s " % [
44
44
  method.self_time / total_time * 100, # %self
45
45
  method.total_time, # total
@@ -47,26 +47,26 @@ module RubyProf
47
47
  method.wait_time, # wait
48
48
  method.children_time, # children
49
49
  method.called, # calls
50
- method_name(method), # name
50
+ method_name(method), # name
51
51
  ]
52
52
  if method.source_file != 'ruby_runtime'
53
53
  @output << " %s:%s" % [File.expand_path(method.source_file), method.line]
54
54
  end
55
55
  @output << "\n\tcalled from: "
56
-
56
+
57
57
  # make sure they're unique
58
- method.call_infos.map{|ci|
58
+ method.call_infos.map{|ci|
59
59
  if ci.parent && ci.parent.target.source_file != 'ruby_runtime'
60
60
  [method_name(ci.parent.target), File.expand_path(ci.parent.target.source_file), ci.parent.target.line]
61
61
  else
62
62
  nil
63
- end
63
+ end
64
64
  }.compact.uniq.each{|args|
65
- @output << " %s (%s:%s) " % args
65
+ @output << " %s (%s:%s) " % args
66
66
  }
67
- @output << "\n\n"
67
+ @output << "\n\n"
68
68
  end
69
69
  end
70
70
  end
71
- end
71
+ end
72
72
 
@@ -2,8 +2,8 @@ require 'ruby-prof/abstract_printer'
2
2
  require 'erb'
3
3
 
4
4
  module RubyProf
5
- # Generates graph[link:files/examples/graph_html.html] profile reports as html.
6
- # To use the grap html printer:
5
+ # Generates graph[link:files/examples/graph_html.html] profile reports as html.
6
+ # To use the graph html printer:
7
7
  #
8
8
  # result = RubyProf.profile do
9
9
  # [code to profile]
@@ -14,20 +14,20 @@ module RubyProf
14
14
  #
15
15
  # The constructor takes two arguments. The first is
16
16
  # a RubyProf::Result object generated from a profiling
17
- # run. The second is the minimum %total (the methods
17
+ # run. The second is the minimum %total (the methods
18
18
  # total time divided by the overall total time) that
19
- # a method must take for it to be printed out in
19
+ # a method must take for it to be printed out in
20
20
  # the report. Use this parameter to eliminate methods
21
21
  # that are not important to the overall profiling results.
22
-
22
+
23
23
  class GraphHtmlPrinter < AbstractPrinter
24
24
  include ERB::Util
25
-
25
+
26
26
  PERCENTAGE_WIDTH = 8
27
27
  TIME_WIDTH = 10
28
28
  CALL_WIDTH = 20
29
-
30
- # Create a GraphPrinter. Result is a RubyProf::Result
29
+
30
+ # Create a GraphPrinter. Result is a RubyProf::Result
31
31
  # object generated from a profiling run.
32
32
  def initialize(result)
33
33
  super(result)
@@ -36,17 +36,17 @@ module RubyProf
36
36
  end
37
37
 
38
38
  # Print a graph html report to the provided output.
39
- #
40
- # output - Any IO oject, including STDOUT or a file.
39
+ #
40
+ # output - Any IO oject, including STDOUT or a file.
41
41
  # The default value is STDOUT.
42
- #
43
- # options - Hash of print options. See #setup_options
42
+ #
43
+ # options - Hash of print options. See #setup_options
44
44
  # for more information.
45
45
  #
46
46
  def print(output = STDOUT, options = {})
47
47
  @output = output
48
48
  setup_options(options)
49
-
49
+
50
50
  filename = options[:filename]
51
51
  template = filename ? File.read(filename).untaint : (options[:template] || self.template)
52
52
  _erbout = @output
@@ -55,38 +55,46 @@ module RubyProf
55
55
  @output << erb.result(binding)
56
56
  end
57
57
 
58
+ def total_time(call_infos)
59
+ sum(call_infos.map{|ci| ci.total_time})
60
+ end
61
+
62
+ def sum(a)
63
+ a.inject(0.0){|s,t| s+=t}
64
+ end
65
+
58
66
  # These methods should be private but then ERB doesn't
59
- # work. Turn off RDOC though
67
+ # work. Turn off RDOC though
60
68
  #--
61
69
  def calculate_thread_times
62
70
  # Cache thread times since this is an expensive
63
- # operation with the required sorting
71
+ # operation with the required sorting
72
+ @overall_threads_time = 0.0
73
+ @thread_times = Hash.new
64
74
  @result.threads.each do |thread_id, methods|
65
- top = methods.max
66
-
67
- thread_time = 0.01
68
- thread_time = top.total_time if top.total_time > 0
69
-
70
- @thread_times[thread_id] = thread_time
75
+ roots = methods.select{|m| m.root?}
76
+ thread_total_time = sum(roots.map{|r| self.total_time(r.call_infos)})
77
+ @overall_threads_time += thread_total_time
78
+ @thread_times[thread_id] = thread_total_time
71
79
  end
72
80
  end
73
-
81
+
74
82
  def thread_time(thread_id)
75
83
  @thread_times[thread_id]
76
84
  end
77
-
85
+
78
86
  def total_percent(thread_id, method)
79
87
  overall_time = self.thread_time(thread_id)
80
88
  (method.total_time/overall_time) * 100
81
89
  end
82
-
90
+
83
91
  def self_percent(method)
84
92
  overall_time = self.thread_time(method.thread_id)
85
93
  (method.self_time/overall_time) * 100
86
94
  end
87
95
 
88
96
  # Creates a link to a method. Note that we do not create
89
- # links to methods which are under the min_perecent
97
+ # links to methods which are under the min_perecent
90
98
  # specified by the user, since they will not be
91
99
  # printed out.
92
100
  def create_link(thread_id, method)
@@ -95,14 +103,27 @@ module RubyProf
95
103
  h method.full_name
96
104
  else
97
105
  href = '#' + method_href(thread_id, method)
98
- "<a href=\"#{href}\">#{h method.full_name}</a>"
106
+ "<a href=\"#{href}\">#{h method.full_name}</a>"
99
107
  end
100
108
  end
101
-
109
+
102
110
  def method_href(thread_id, method)
103
111
  h(method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + thread_id.to_s)
104
112
  end
105
-
113
+
114
+ def file_link(path, linenum)
115
+ srcfile = File.expand_path(path)
116
+ if srcfile =~ /\/ruby_runtime$/
117
+ ""
118
+ else
119
+ if RUBY_PLATFORM =~ /darwin/
120
+ "<a href=\"txmt://open?url=file://#{h srcfile}&line=#{linenum}\" title=\"#{h srcfile}:#{linenum}\">#{linenum}</a>"
121
+ else
122
+ "<a href=\"file://#{h srcfile}?line=#{linenum}\" title=\"#{h srcfile}:#{linenum}\">#{linenum}</a>"
123
+ end
124
+ end
125
+ end
126
+
106
127
  def template
107
128
  '
108
129
  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
@@ -149,7 +170,7 @@ module RubyProf
149
170
  td {
150
171
  border-left: 1px solid #CCC;
151
172
  text-align: center;
152
- }
173
+ }
153
174
 
154
175
  .method_name {
155
176
  text-align: left;
@@ -164,7 +185,7 @@ module RubyProf
164
185
  <th>Thread ID</th>
165
186
  <th>Total Time</th>
166
187
  </tr>
167
- <% for thread_id, methods in @result.threads %>
188
+ <% for thread_id in @result.threads.keys.sort %>
168
189
  <tr>
169
190
  <td><a href="#<%= thread_id %>"><%= thread_id %></a></td>
170
191
  <td><%= thread_time(thread_id) %></td>
@@ -173,7 +194,8 @@ module RubyProf
173
194
  </table>
174
195
 
175
196
  <!-- Methods Tables -->
176
- <% for thread_id, methods in @result.threads
197
+ <% for thread_id in @result.threads.keys.sort
198
+ methods = @result.threads[thread_id]
177
199
  total_time = thread_time(thread_id) %>
178
200
  <h2><a name="<%= thread_id %>">Thread <%= thread_id %></a></h2>
179
201
 
@@ -196,7 +218,7 @@ module RubyProf
196
218
  next if total_percentage < min_percent
197
219
  next if min_time && method.total_time < min_time
198
220
  self_percentage = (method.self_time/total_time) * 100 %>
199
-
221
+
200
222
  <!-- Parents -->
201
223
  <% for caller in method.aggregate_parents.sort_by(&:total_time)
202
224
  next unless caller.parent
@@ -211,7 +233,7 @@ module RubyProf
211
233
  <% called = "#{caller.called}/#{method.called}" %>
212
234
  <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
213
235
  <td class="method_name"><%= create_link(thread_id, caller.parent.target) %></td>
214
- <td><a href="file://<%=h srcfile=File.expand_path(caller.parent.target.source_file) %>#line=<%= linenum=caller.line %>" title="<%=h srcfile %>:<%= linenum %>"><%= caller.line %></a></td>
236
+ <td><%= file_link(caller.parent.target.source_file, caller.line) %></td>
215
237
  </tr>
216
238
  <% end %>
217
239
 
@@ -224,7 +246,7 @@ module RubyProf
224
246
  <td><%= sprintf("%#{TIME_WIDTH}.2f", method.children_time) %></td>
225
247
  <td><%= sprintf("%#{CALL_WIDTH}i", method.called) %></td>
226
248
  <td class="method_name"><a name="<%= method_href(thread_id, method) %>"><%= h method.full_name %></a></td>
227
- <td><a href="file://<%=h srcfile=File.expand_path(method.source_file) %>#line=<%= linenum=method.line %>" title="<%=h srcfile %>:<%= linenum %>"><%= method.line %></a></td>
249
+ <td><%= file_link(method.source_file, method.line) %></td>
228
250
  </tr>
229
251
 
230
252
  <!-- Children -->
@@ -240,7 +262,7 @@ module RubyProf
240
262
  <% called = "#{callee.called}/#{callee.target.called}" %>
241
263
  <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
242
264
  <td class="method_name"><%= create_link(thread_id, callee.target) %></td>
243
- <td><a href="file://<%=h srcfile=File.expand_path(method.source_file) %>#line=<%= linenum=callee.line %>" title="<%=h srcfile %>:<%= linenum %>"><%= callee.line %></a></td>
265
+ <td><%= file_link(method.source_file, callee.line) %></td>
244
266
  </tr>
245
267
  <% end %>
246
268
  <!-- Create divider row -->
@@ -252,5 +274,5 @@ module RubyProf
252
274
  </html>'
253
275
  end
254
276
  end
255
- end
277
+ end
256
278