tw-studios-jruby-prof 0.1.1
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.
- data/README +46 -0
- data/lib/jruby-prof.jar +0 -0
- data/lib/jruby-prof.rb +66 -0
- data/lib/jruby-prof/abstract_printer.rb +21 -0
- data/lib/jruby-prof/flat_text_printer.rb +35 -0
- data/lib/jruby-prof/graph_html_printer.rb +123 -0
- data/lib/jruby-prof/graph_text_printer.rb +45 -0
- data/lib/jruby-prof/invocation_set.rb +34 -0
- data/lib/jruby-prof/method.rb +107 -0
- data/lib/jruby-prof/profile_invocation.rb +43 -0
- data/lib/jruby-prof/simple_tree_printer.rb +27 -0
- data/lib/jruby-prof/tree_html_printer.rb +144 -0
- data/src/org/jruby/prof/Invocation.java +14 -0
- data/src/org/jruby/prof/JRubyProf.java +94 -0
- data/src/org/jruby/prof/ProfEventHook.java +49 -0
- data/templates/graph_row.html.erb +18 -0
- data/test/basic_test.rb +291 -0
- metadata +73 -0
data/README
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
jruby-prof
|
3
|
+
==========
|
4
|
+
|
5
|
+
Daniel Lucraft
|
6
|
+
http://danlucraft.com/blog/2010/03/jruby-prof/
|
7
|
+
|
8
|
+
This is a clone of ruby-prof for JRuby. It supports all the usual output
|
9
|
+
modes, plus a call tree output mode.
|
10
|
+
|
11
|
+
The only measure mode it currently supports is wall time.
|
12
|
+
|
13
|
+
*** NB. This is really new code. It probably has lots of bugs and things. - March 2010 ***
|
14
|
+
|
15
|
+
INSTALL
|
16
|
+
-------
|
17
|
+
|
18
|
+
$ jruby -S gem install jruby-prof
|
19
|
+
|
20
|
+
USAGE
|
21
|
+
-----
|
22
|
+
|
23
|
+
In your program:
|
24
|
+
|
25
|
+
require 'rubygems'
|
26
|
+
require 'jruby-prof'
|
27
|
+
|
28
|
+
result = JRubyProf.profile do
|
29
|
+
# my stuff
|
30
|
+
end
|
31
|
+
|
32
|
+
JRubyProf.print_flat_text(result, "flat.txt")
|
33
|
+
JRubyProf.print_graph_text(result, "graph.txt")
|
34
|
+
JRubyProf.print_graph_html(result, "graph.html")
|
35
|
+
JRubyProf.print_call_tree(result, "call_tree.txt")
|
36
|
+
JRubyProf.print_tree_html(result, "call_tree.html")
|
37
|
+
|
38
|
+
In order to tell JRuby to send events to JRubyProf for processing, you must
|
39
|
+
run your script with the debug flag on:
|
40
|
+
|
41
|
+
$ jruby --debug my_script.rb
|
42
|
+
|
43
|
+
LICENSE
|
44
|
+
-------
|
45
|
+
|
46
|
+
MIT
|
data/lib/jruby-prof.jar
ADDED
Binary file
|
data/lib/jruby-prof.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
require 'java'
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
require File.dirname(__FILE__) + "/jruby-prof.jar"
|
6
|
+
|
7
|
+
java_import org.jruby.prof.JRubyProf
|
8
|
+
|
9
|
+
require 'jruby-prof/abstract_printer'
|
10
|
+
require 'jruby-prof/simple_tree_printer'
|
11
|
+
require 'jruby-prof/flat_text_printer'
|
12
|
+
require 'jruby-prof/graph_text_printer'
|
13
|
+
require 'jruby-prof/graph_html_printer'
|
14
|
+
require 'jruby-prof/invocation_set'
|
15
|
+
require 'jruby-prof/method'
|
16
|
+
require 'jruby-prof/tree_html_printer'
|
17
|
+
|
18
|
+
require 'jruby-prof/profile_invocation'
|
19
|
+
require 'jruby'
|
20
|
+
class JRubyProf
|
21
|
+
|
22
|
+
def self.start
|
23
|
+
raise RuntimeError, "JRubyProf already running" if running?
|
24
|
+
start_tracing(JRuby.runtime)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.stop
|
28
|
+
stop_tracing(JRuby.runtime)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.running?
|
32
|
+
self.is_running
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.profile
|
36
|
+
raise ArgumentError, "profile requires a block" unless block_given?
|
37
|
+
start
|
38
|
+
yield
|
39
|
+
stop
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.print_call_tree(result, filename)
|
43
|
+
printer = SimpleTreePrinter.new(ThreadSet.new(result.values.to_a, JRubyProf.lastTracingDuration))
|
44
|
+
printer.print_to_file(filename)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.print_tree_html(result, filename)
|
48
|
+
printer = TreeHtmlPrinter.new(ThreadSet.new(result.values.to_a, JRubyProf.lastTracingDuration))
|
49
|
+
printer.print_to_file(filename)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.print_flat_text(result, filename)
|
53
|
+
printer = FlatTextPrinter.new(ThreadSet.new(result.values.to_a, JRubyProf.lastTracingDuration))
|
54
|
+
printer.print_to_file(filename)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.print_graph_text(result, filename)
|
58
|
+
printer = GraphTextPrinter.new(ThreadSet.new(result.values.to_a, JRubyProf.lastTracingDuration))
|
59
|
+
printer.print_to_file(filename)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.print_graph_html(result, filename)
|
63
|
+
printer = GraphHtmlPrinter.new(ThreadSet.new(result.values.to_a, JRubyProf.lastTracingDuration))
|
64
|
+
printer.print_to_file(filename)
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
class JRubyProf
|
3
|
+
class AbstractPrinter
|
4
|
+
attr_reader :thread_set
|
5
|
+
|
6
|
+
def initialize(thread_set, options={})
|
7
|
+
@thread_set = thread_set
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def print_to_file(filename)
|
12
|
+
puts "Dumping trace to #{File.expand_path(filename)}"
|
13
|
+
File.open(filename, "w") do |f|
|
14
|
+
print_on(f)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
class JRubyProf
|
3
|
+
class FlatTextPrinter < AbstractPrinter
|
4
|
+
TABLE_HEADER = " %self cumulative total self children calls self/call total/call name"
|
5
|
+
|
6
|
+
def print_on(output)
|
7
|
+
thread_set.invocations.each_with_index do |invocation, i|
|
8
|
+
output.puts
|
9
|
+
output.puts "Thread #{i + 1} / #{thread_set.length}"
|
10
|
+
output.puts
|
11
|
+
methods = invocation.get_methods.values.sort_by {|m| m.duration }.reverse
|
12
|
+
output.puts TABLE_HEADER
|
13
|
+
total_duration = 0
|
14
|
+
rows = methods.map do |method|
|
15
|
+
total = method.duration
|
16
|
+
children = method.childrens_duration
|
17
|
+
self_ = total - children
|
18
|
+
total_duration += self_ unless self_ < 0
|
19
|
+
next if method.name == "#"
|
20
|
+
[total, children, self_, method.count, method.name]
|
21
|
+
end
|
22
|
+
cumulative = 0
|
23
|
+
rows = rows.compact.sort_by {|r| r[2]}.reverse
|
24
|
+
rows.each do |row|
|
25
|
+
total, children, self_, count, name = *row
|
26
|
+
cumulative += self_
|
27
|
+
self_pc = (self_.to_f/total_duration)*100
|
28
|
+
self_per_call = self_.to_f/count
|
29
|
+
total_per_call = total.to_f/count
|
30
|
+
output.puts "#{("%2.2f" % self_pc).rjust(6)} #{cumulative.to_s.rjust(11)} #{total.to_s.rjust(8)} #{self_.to_s.rjust(7)} #{children.to_s.rjust(8)} #{count.to_s.rjust(7)} #{("%2.2f" % self_per_call).to_s.rjust(9)} #{("%2.2f" % total_per_call).to_s.rjust(10)} #{name}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
|
2
|
+
class JRubyProf
|
3
|
+
class GraphHtmlPrinter < GraphTextPrinter
|
4
|
+
def print_on(output)
|
5
|
+
output.puts HEADER
|
6
|
+
total_duration = thread_set.duration
|
7
|
+
thread_set.invocations.each_with_index do |thread, i|
|
8
|
+
methods = thread.get_methods.values.sort_by {|m| m.duration }.reverse
|
9
|
+
output.puts "<h3>Thread #{i + 1}/#{thread_set.length}</h3>"
|
10
|
+
output.puts TABLE_HEADER
|
11
|
+
rows = methods.map do |method|
|
12
|
+
method.parent_contexts.each do |context|
|
13
|
+
print_method(output, context, total_duration, false)
|
14
|
+
end
|
15
|
+
print_method(output, method, total_duration, true)
|
16
|
+
method.child_contexts.each do |context|
|
17
|
+
print_method(output, context, total_duration, false)
|
18
|
+
end
|
19
|
+
output.puts <<-HTML
|
20
|
+
<tr class="break">
|
21
|
+
<td colspan="7"></td>
|
22
|
+
</tr>
|
23
|
+
HTML
|
24
|
+
end
|
25
|
+
output.puts TABLE_FOOTER
|
26
|
+
end
|
27
|
+
output.puts FOOTER
|
28
|
+
end
|
29
|
+
|
30
|
+
def print_method(output, method, total_duration, major_row)
|
31
|
+
return if method.name =~ /JRubyProf\.stop/
|
32
|
+
total = method.duration
|
33
|
+
total_pc = (total.to_f/total_duration)*100
|
34
|
+
children = method.childrens_duration
|
35
|
+
self_ = total - children
|
36
|
+
self_pc = (self_.to_f/total_duration)*100
|
37
|
+
calls = method.count
|
38
|
+
name = method.name
|
39
|
+
inv_id = nil
|
40
|
+
template = File.read(File.join(File.dirname(__FILE__), "..", "..", "templates", "graph_row.html.erb"))
|
41
|
+
erb = ERB.new(template)
|
42
|
+
output.puts(erb.result(binding))
|
43
|
+
end
|
44
|
+
|
45
|
+
def safe_name(name)
|
46
|
+
name.gsub("#", "_inst_").gsub(".", "_stat_")
|
47
|
+
end
|
48
|
+
|
49
|
+
HEADER = <<HTML
|
50
|
+
<html>
|
51
|
+
<body>
|
52
|
+
<head>
|
53
|
+
<style media="all" type="text/css">
|
54
|
+
table {
|
55
|
+
border-collapse: collapse;
|
56
|
+
border: 1px solid #CCC;
|
57
|
+
font-family: Verdana, Arial, Helvetica, sans-serif;
|
58
|
+
font-size: 9pt;
|
59
|
+
line-height: normal;
|
60
|
+
}
|
61
|
+
|
62
|
+
th {
|
63
|
+
text-align: center;
|
64
|
+
border-top: 1px solid #FB7A31;
|
65
|
+
border-bottom: 1px solid #FB7A31;
|
66
|
+
background: #FFC;
|
67
|
+
padding: 0.3em;
|
68
|
+
border-left: 1px solid silver;
|
69
|
+
}
|
70
|
+
|
71
|
+
tr.break td {
|
72
|
+
border: 0;
|
73
|
+
border-top: 1px solid #FB7A31;
|
74
|
+
padding: 0;
|
75
|
+
margin: 0;
|
76
|
+
}
|
77
|
+
|
78
|
+
tr.method td {
|
79
|
+
font-weight: bold;
|
80
|
+
}
|
81
|
+
|
82
|
+
td {
|
83
|
+
padding: 0.3em;
|
84
|
+
}
|
85
|
+
|
86
|
+
td:first-child {
|
87
|
+
width: 190px;
|
88
|
+
}
|
89
|
+
|
90
|
+
td {
|
91
|
+
border-left: 1px solid #CCC;
|
92
|
+
text-align: center;
|
93
|
+
}
|
94
|
+
</style>
|
95
|
+
</head>
|
96
|
+
HTML
|
97
|
+
|
98
|
+
TABLE_HEADER = <<-HTML
|
99
|
+
<table>
|
100
|
+
<tr>
|
101
|
+
<th>%total</th>
|
102
|
+
<th>%self</th>
|
103
|
+
<th>total</th>
|
104
|
+
<th>self</th>
|
105
|
+
<th>children</th>
|
106
|
+
<th>calls</th>
|
107
|
+
<th>Name</th>
|
108
|
+
</tr>
|
109
|
+
HTML
|
110
|
+
|
111
|
+
TABLE_FOOTER = <<HTML
|
112
|
+
</table>
|
113
|
+
<br />
|
114
|
+
<br />
|
115
|
+
HTML
|
116
|
+
|
117
|
+
FOOTER = <<-HTML
|
118
|
+
</body>
|
119
|
+
</html>
|
120
|
+
HTML
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
class JRubyProf
|
3
|
+
class GraphTextPrinter < AbstractPrinter
|
4
|
+
TABLE_HEADER = " %total %self total self children calls Name"
|
5
|
+
|
6
|
+
def print_on(output)
|
7
|
+
thread_set.invocations.each_with_index do |invocation, i|
|
8
|
+
output.puts
|
9
|
+
output.puts "Thread #{i + 1} / #{thread_set.length}"
|
10
|
+
output.puts
|
11
|
+
methods = invocation.get_methods.values.sort_by {|m| m.duration }.reverse
|
12
|
+
output.puts TABLE_HEADER
|
13
|
+
output.puts "-"*100
|
14
|
+
total_duration = thread_set.duration
|
15
|
+
rows = methods.map do |method|
|
16
|
+
method.parent_contexts.each do |context|
|
17
|
+
print_method(output, context, total_duration, false)
|
18
|
+
end
|
19
|
+
print_method(output, method, total_duration, true)
|
20
|
+
method.child_contexts.each do |context|
|
21
|
+
print_method(output, context, total_duration, false)
|
22
|
+
end
|
23
|
+
output.puts "-"*100
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def print_method(output, method, total_duration, print_percents)
|
29
|
+
return if method.name =~ /JRubyProf\.stop/
|
30
|
+
total = method.duration
|
31
|
+
total_pc = (total.to_f/total_duration)*100
|
32
|
+
children = method.childrens_duration
|
33
|
+
self_ = total - children
|
34
|
+
self_pc = (self_.to_f/total_duration)*100
|
35
|
+
calls = method.count
|
36
|
+
name = method.name
|
37
|
+
if print_percents
|
38
|
+
output.print " #{("%2.2f" % total_pc).rjust(6)}% #{("%2.2f" % self_pc).rjust(6)}%"
|
39
|
+
else
|
40
|
+
output.print " "
|
41
|
+
end
|
42
|
+
output.puts "#{total.to_s.rjust(11)} #{self_.to_s.rjust(9)} #{children.to_s.rjust(11)} #{calls.to_s.rjust(8)} #{name}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
class JRubyProf
|
3
|
+
class ThreadSet
|
4
|
+
attr_reader :invocations, :duration
|
5
|
+
|
6
|
+
def initialize(invocations, duration)
|
7
|
+
@invocations = invocations.map do |inv|
|
8
|
+
c = inv
|
9
|
+
c = c.parent while c.parent
|
10
|
+
c
|
11
|
+
end
|
12
|
+
@duration = duration
|
13
|
+
end
|
14
|
+
|
15
|
+
def length
|
16
|
+
invocations.length
|
17
|
+
end
|
18
|
+
|
19
|
+
def top_level_duration
|
20
|
+
invocations.inject(0.0) do |m, inv|
|
21
|
+
m + inv.childrens_duration
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.add_methods(h, inv, duration=nil, count=nil)
|
26
|
+
return if inv.name =~ /CachingCallSite\.stop_tracing/ or inv.name =~ /JRubyProf\.stop/
|
27
|
+
h[inv.name] ||= Method.new(inv.class_name, inv.method_name, inv.static?)
|
28
|
+
h[inv.name].add_invocation(inv)
|
29
|
+
inv.children.each do |child_inv|
|
30
|
+
add_methods(h, child_inv)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
|
2
|
+
class JRubyProf
|
3
|
+
class Method
|
4
|
+
attr_reader :class_name, :method_name, :invocations
|
5
|
+
|
6
|
+
def initialize(class_name, method_name, static)
|
7
|
+
@class_name, @method_name, @static = class_name, method_name, static
|
8
|
+
@invocations = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def static?
|
12
|
+
@static
|
13
|
+
end
|
14
|
+
|
15
|
+
def toplevel?
|
16
|
+
method_name == nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_invocation(inv)
|
20
|
+
invocations << inv
|
21
|
+
end
|
22
|
+
|
23
|
+
def duration
|
24
|
+
if toplevel?
|
25
|
+
childrens_duration
|
26
|
+
else
|
27
|
+
invocations.inject(0) {|m, inv| m + inv.duration}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def count
|
32
|
+
if toplevel?
|
33
|
+
1
|
34
|
+
else
|
35
|
+
invocations.inject(0) {|m, inv| m + inv.count}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def childrens_duration
|
40
|
+
invocations.inject(0) {|m, inv| m + inv.children.inject(0) {|m1, inv1| m1 + inv1.duration }}
|
41
|
+
end
|
42
|
+
|
43
|
+
def name
|
44
|
+
"#{class_name}#{static? ? "." : "#"}#{method_name || "toplevel"}"
|
45
|
+
end
|
46
|
+
|
47
|
+
class CallContext
|
48
|
+
attr_accessor :name, :duration, :childrens_duration, :count
|
49
|
+
|
50
|
+
def toplevel?
|
51
|
+
@name == "#"
|
52
|
+
end
|
53
|
+
|
54
|
+
def name
|
55
|
+
toplevel? ? "#toplevel" : @name
|
56
|
+
end
|
57
|
+
|
58
|
+
def initialize(name)
|
59
|
+
@name = name
|
60
|
+
@duration, @childrens_duration, @count = 0, 0, 0
|
61
|
+
end
|
62
|
+
|
63
|
+
def duration
|
64
|
+
toplevel? ? @childrens_duration : @duration
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def child_contexts
|
69
|
+
@child_contexts ||= begin
|
70
|
+
h = {}
|
71
|
+
invocations.each do |inv|
|
72
|
+
inv.children.each do |inv2|
|
73
|
+
h[inv2.name] ||= CallContext.new(inv2.name)
|
74
|
+
cc = h[inv2.name]
|
75
|
+
cc.duration += inv2.duration
|
76
|
+
cc.childrens_duration += inv2.childrens_duration
|
77
|
+
cc.count += inv2.count
|
78
|
+
end
|
79
|
+
end
|
80
|
+
h.values
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def parent_contexts
|
85
|
+
@parent_contexts ||= begin
|
86
|
+
h = {}
|
87
|
+
invocations.each do |inv|
|
88
|
+
inv2 = inv.parent
|
89
|
+
next unless inv2
|
90
|
+
h[inv2.name] ||= CallContext.new(inv2.name)
|
91
|
+
cc = h[inv2.name]
|
92
|
+
cc.duration += inv.duration
|
93
|
+
cc.childrens_duration += inv.childrens_duration
|
94
|
+
cc.count += inv.count
|
95
|
+
end
|
96
|
+
h.values
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def inspect
|
101
|
+
"#<JRubyProf::Method #{class_name} #{method_name}>"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
class JRubyProf
|
3
|
+
java_import org.jruby.prof.Invocation
|
4
|
+
class Invocation
|
5
|
+
alias :method_name :methodName
|
6
|
+
alias :class_name :className
|
7
|
+
|
8
|
+
def name
|
9
|
+
"#{class_name}#{static? ? "." : "#"}#{method_name}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def childrens_duration
|
13
|
+
children.inject(0) {|m, inv| m + inv.duration}
|
14
|
+
end
|
15
|
+
|
16
|
+
def static?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_method
|
21
|
+
method = Method.new(class_name, method_name, static?)
|
22
|
+
method.add_invocation(self)
|
23
|
+
method
|
24
|
+
end
|
25
|
+
|
26
|
+
def id
|
27
|
+
@id ||= Invocation.new_id
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.new_id
|
31
|
+
@id ||= 0
|
32
|
+
@id += 1
|
33
|
+
@id
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_methods
|
37
|
+
h = {}
|
38
|
+
ThreadSet.add_methods(h, self)
|
39
|
+
h
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
class JRubyProf
|
3
|
+
class SimpleTreePrinter < AbstractPrinter
|
4
|
+
def print_on(output)
|
5
|
+
thread_set.invocations.each_with_index do |invocation, i|
|
6
|
+
output.puts
|
7
|
+
output.puts "*** Thread #{i + 1} / #{thread_set.length}"
|
8
|
+
output.puts
|
9
|
+
dump_from_root(output, invocation)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def dump(f, inv, indent=0)
|
16
|
+
f.print(" "*indent)
|
17
|
+
f.puts "#{inv.name} - #{inv.duration}"
|
18
|
+
inv.children.each {|child_inv| dump(f, child_inv, indent + 2)}
|
19
|
+
end
|
20
|
+
|
21
|
+
def dump_from_root(f, inv)
|
22
|
+
current = inv
|
23
|
+
current = current.parent while current.parent
|
24
|
+
dump(f, current)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
|
2
|
+
class JRubyProf
|
3
|
+
class TreeHtmlPrinter < AbstractPrinter
|
4
|
+
def print_on(output)
|
5
|
+
total_duration = thread_set.duration
|
6
|
+
output.puts HEADER
|
7
|
+
thread_set.invocations.each_with_index do |invocation, i|
|
8
|
+
all_invocations = []
|
9
|
+
get_invocations(all_invocations, invocation)
|
10
|
+
output.puts "<h3>Thread #{i + 1}/#{thread_set.length}</h3>"
|
11
|
+
output.puts TABLE_HEADER
|
12
|
+
all_invocations = all_invocations.sort_by {|i| m = i.to_method.duration }.reverse
|
13
|
+
all_invocations.each do |inv|
|
14
|
+
next if inv.name =~ /CachingCallSite\.stop_tracing/
|
15
|
+
next if inv.name =~ /JRubyProf\.stop/
|
16
|
+
next if inv.duration < 5
|
17
|
+
#next if inv.name == "#"
|
18
|
+
c = inv
|
19
|
+
parents = []
|
20
|
+
while c.parent
|
21
|
+
c = c.parent
|
22
|
+
parents << c
|
23
|
+
end
|
24
|
+
parents.reverse.each do |parent_inv|
|
25
|
+
print_invocation(output, parent_inv, total_duration, false)
|
26
|
+
end
|
27
|
+
print_invocation(output, inv, total_duration, true)
|
28
|
+
inv.children.each do |child_inv|
|
29
|
+
next if child_inv.name =~ /CachingCallSite\.stop_tracing/
|
30
|
+
print_invocation(output, child_inv, total_duration, false)
|
31
|
+
end
|
32
|
+
output.puts <<-HTML
|
33
|
+
<tr class="break">
|
34
|
+
<td colspan="7"></td>
|
35
|
+
</tr>
|
36
|
+
HTML
|
37
|
+
end
|
38
|
+
output.puts TABLE_FOOTER
|
39
|
+
end
|
40
|
+
output.puts FOOTER
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_invocations(arr, invocation)
|
44
|
+
arr << invocation
|
45
|
+
invocation.children.each do |inv|
|
46
|
+
get_invocations(arr, inv)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def print_invocation(output, invocation, total_duration, major_row)
|
51
|
+
next if invocation.name =~ /JRubyProf\.stop/
|
52
|
+
method = invocation.to_method
|
53
|
+
total = method.duration
|
54
|
+
total_pc = (total.to_f/total_duration)*100
|
55
|
+
children = method.childrens_duration
|
56
|
+
self_ = total - children
|
57
|
+
self_pc = (self_.to_f/total_duration)*100
|
58
|
+
calls = method.count
|
59
|
+
name = method.name
|
60
|
+
inv_id = invocation.id
|
61
|
+
template = File.read(File.join(File.dirname(__FILE__), "..", "..", "templates", "graph_row.html.erb"))
|
62
|
+
erb = ERB.new(template)
|
63
|
+
output.puts(erb.result(binding))
|
64
|
+
end
|
65
|
+
|
66
|
+
def safe_name(name)
|
67
|
+
name.gsub("#", "_inst_").gsub(".", "_stat_")
|
68
|
+
end
|
69
|
+
|
70
|
+
HEADER = <<HTML
|
71
|
+
<html>
|
72
|
+
<body>
|
73
|
+
<head>
|
74
|
+
<style media="all" type="text/css">
|
75
|
+
table {
|
76
|
+
border-collapse: collapse;
|
77
|
+
border: 1px solid #CCC;
|
78
|
+
font-family: Verdana, Arial, Helvetica, sans-serif;
|
79
|
+
font-size: 9pt;
|
80
|
+
line-height: normal;
|
81
|
+
}
|
82
|
+
|
83
|
+
th {
|
84
|
+
text-align: center;
|
85
|
+
border-top: 1px solid #339;
|
86
|
+
border-bottom: 1px solid #339;
|
87
|
+
background: #CDF;
|
88
|
+
padding: 0.3em;
|
89
|
+
border-left: 1px solid silver;
|
90
|
+
}
|
91
|
+
|
92
|
+
tr.break td {
|
93
|
+
border: 0;
|
94
|
+
border-top: 1px solid #339;
|
95
|
+
padding: 0;
|
96
|
+
margin: 0;
|
97
|
+
}
|
98
|
+
|
99
|
+
tr.method td {
|
100
|
+
font-weight: bold;
|
101
|
+
}
|
102
|
+
|
103
|
+
td {
|
104
|
+
padding: 0.3em;
|
105
|
+
}
|
106
|
+
|
107
|
+
td:first-child {
|
108
|
+
width: 190px;
|
109
|
+
}
|
110
|
+
|
111
|
+
td {
|
112
|
+
border-left: 1px solid #CCC;
|
113
|
+
text-align: center;
|
114
|
+
}
|
115
|
+
</style>
|
116
|
+
</head>
|
117
|
+
HTML
|
118
|
+
|
119
|
+
TABLE_HEADER = <<-HTML
|
120
|
+
<table>
|
121
|
+
<tr>
|
122
|
+
<th>%total</th>
|
123
|
+
<th>%self</th>
|
124
|
+
<th>total</th>
|
125
|
+
<th>self</th>
|
126
|
+
<th>children</th>
|
127
|
+
<th>calls</th>
|
128
|
+
<th>Name</th>
|
129
|
+
</tr>
|
130
|
+
HTML
|
131
|
+
|
132
|
+
TABLE_FOOTER = <<HTML
|
133
|
+
</table>
|
134
|
+
<br />
|
135
|
+
<br />
|
136
|
+
HTML
|
137
|
+
|
138
|
+
FOOTER = <<-HTML
|
139
|
+
</body>
|
140
|
+
</html>
|
141
|
+
HTML
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
package org.jruby.prof;
|
3
|
+
|
4
|
+
import java.util.*;
|
5
|
+
public class Invocation {
|
6
|
+
public String className;
|
7
|
+
public String methodName;
|
8
|
+
public Invocation parent;
|
9
|
+
public boolean returned = false;
|
10
|
+
public long duration = 0;
|
11
|
+
public long count = 0;
|
12
|
+
public long startTimeCurrent;
|
13
|
+
public ArrayList<Invocation> children = new ArrayList<Invocation>();
|
14
|
+
}
|
@@ -0,0 +1,94 @@
|
|
1
|
+
|
2
|
+
package org.jruby.prof;
|
3
|
+
|
4
|
+
import java.util.*;
|
5
|
+
import org.jruby.Ruby;
|
6
|
+
import org.jruby.runtime.ThreadContext;
|
7
|
+
|
8
|
+
public class JRubyProf {
|
9
|
+
|
10
|
+
private static Map<ThreadContext, Invocation> currentInvocations;
|
11
|
+
|
12
|
+
public static ProfEventHook hook = null;
|
13
|
+
public static long startedTracingTime;
|
14
|
+
public static long lastTracingDuration;
|
15
|
+
|
16
|
+
public static void startTracing(Ruby runtime) {
|
17
|
+
hook = new ProfEventHook();
|
18
|
+
runtime.addEventHook(hook);
|
19
|
+
currentInvocations = Collections.synchronizedMap(new HashMap<ThreadContext, Invocation>());
|
20
|
+
shouldProfile = true;
|
21
|
+
startedTracingTime = System.currentTimeMillis();
|
22
|
+
}
|
23
|
+
|
24
|
+
public static Map<ThreadContext, Invocation> stopTracing(Ruby runtime) {
|
25
|
+
shouldProfile = false;
|
26
|
+
runtime.removeEventHook(hook);
|
27
|
+
hook = null;
|
28
|
+
lastTracingDuration = System.currentTimeMillis() - startedTracingTime;
|
29
|
+
for (ThreadContext context : currentInvocations.keySet()) {
|
30
|
+
Invocation inv = currentInvocations.get(context);
|
31
|
+
while (inv.parent != null)
|
32
|
+
inv = inv.parent;
|
33
|
+
currentInvocations.put(context, inv);
|
34
|
+
}
|
35
|
+
return currentInvocations;
|
36
|
+
}
|
37
|
+
|
38
|
+
public static boolean isRunning() {
|
39
|
+
return shouldProfile;
|
40
|
+
}
|
41
|
+
|
42
|
+
private static boolean shouldProfile = false;
|
43
|
+
private boolean initProfileMethod = false;
|
44
|
+
|
45
|
+
public static synchronized void before(ThreadContext context, String className, String methodName) {
|
46
|
+
if (!shouldProfile) return;
|
47
|
+
Invocation inv = null;
|
48
|
+
if (currentInvocations.containsKey(context)) {
|
49
|
+
Invocation parent = currentInvocations.get(context);
|
50
|
+
for (Invocation subinv : parent.children) {
|
51
|
+
if (subinv.className.equals(className) && subinv.methodName.equals(methodName)) {
|
52
|
+
inv = subinv;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
if (inv == null) {
|
56
|
+
inv = new Invocation();
|
57
|
+
inv.parent = parent;
|
58
|
+
inv.className = className;
|
59
|
+
inv.methodName = methodName;
|
60
|
+
parent.children.add(inv);
|
61
|
+
}
|
62
|
+
currentInvocations.put(context, inv);
|
63
|
+
//System.out.printf("pushing %s %s to %s %s in %s\n", inv.className, inv.methodName, parent.className, parent.methodName, context.toString());
|
64
|
+
//System.out.printf("current for %s is %s %s\n", context.toString(), inv.className, inv.methodName);
|
65
|
+
}
|
66
|
+
else {
|
67
|
+
inv = new Invocation();
|
68
|
+
currentInvocations.put(context, inv);
|
69
|
+
//System.out.printf("current for %s is %s %s\n", context.toString(), inv.className, inv.methodName);
|
70
|
+
before(context, className, methodName);
|
71
|
+
}
|
72
|
+
inv.startTimeCurrent = System.currentTimeMillis();
|
73
|
+
}
|
74
|
+
|
75
|
+
public static synchronized void after(ThreadContext context, String className, String methodName) {
|
76
|
+
if (!shouldProfile) return;
|
77
|
+
Invocation current = currentInvocations.get(context);
|
78
|
+
long time;
|
79
|
+
if (current == null) return;
|
80
|
+
time = System.currentTimeMillis() - current.startTimeCurrent;
|
81
|
+
if (current.startTimeCurrent == 0)
|
82
|
+
System.out.printf("warning, startTimeCurrent is 0 in after\n");
|
83
|
+
current.startTimeCurrent = 0;
|
84
|
+
current.duration += time;
|
85
|
+
current.returned = true;
|
86
|
+
current.count += 1;
|
87
|
+
if (current.parent != null) {
|
88
|
+
currentInvocations.put(context, current.parent);
|
89
|
+
//System.out.printf("popping: %s %s took %dms, now at %s %s\n", current.className, current.methodName, time, current.parent.className, current.parent.methodName);
|
90
|
+
}
|
91
|
+
//System.out.printf("current for %s is %s %s\n", context.toString(), current.parent.className, current.parent.methodName);
|
92
|
+
}
|
93
|
+
|
94
|
+
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
package org.jruby.prof;
|
3
|
+
|
4
|
+
import org.jruby.prof.JRubyProf;
|
5
|
+
|
6
|
+
import org.jruby.RubyObject;
|
7
|
+
import org.jruby.RubyModule;
|
8
|
+
import org.jruby.MetaClass;
|
9
|
+
import org.jruby.runtime.EventHook;
|
10
|
+
import org.jruby.runtime.RubyEvent;
|
11
|
+
import org.jruby.runtime.ThreadContext;
|
12
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
13
|
+
|
14
|
+
public class ProfEventHook extends EventHook {
|
15
|
+
public void eventHandler(ThreadContext context, String eventName, String file, int line, String methodName, IRubyObject type) {
|
16
|
+
RubyModule module = (RubyModule) type;
|
17
|
+
String className;
|
18
|
+
if (module == null) {
|
19
|
+
className = "null";
|
20
|
+
}
|
21
|
+
else {
|
22
|
+
if (module instanceof MetaClass) {
|
23
|
+
IRubyObject obj = ((MetaClass) module).getAttached();
|
24
|
+
if (obj instanceof RubyModule) {
|
25
|
+
module = (RubyModule) obj;
|
26
|
+
className = "<Class::" + module.getName() + ">";
|
27
|
+
}
|
28
|
+
else if (obj instanceof RubyObject) {
|
29
|
+
className = "<Instance::" + ((RubyObject) obj).getType().getName() + ">";
|
30
|
+
}
|
31
|
+
else {
|
32
|
+
className = "unknown";
|
33
|
+
}
|
34
|
+
}
|
35
|
+
else {
|
36
|
+
className = module.getName();
|
37
|
+
}
|
38
|
+
}
|
39
|
+
//System.out.printf("eventHandler(_, %s, %s, %d, %s, %s)\n", eventName, file, line, methodName, className);
|
40
|
+
if (className.equals("<Class::Java::OrgJrubyProf::JRubyProf>")) return;
|
41
|
+
if (eventName.equals("call") || eventName.equals("c-call")) {
|
42
|
+
JRubyProf.before(context, className, methodName);
|
43
|
+
}
|
44
|
+
else if (eventName.equals("return") || eventName.equals("c-return")) {
|
45
|
+
JRubyProf.after(context, className, methodName);
|
46
|
+
}
|
47
|
+
}
|
48
|
+
public boolean isInterestedInEvent(RubyEvent event) { return true; }
|
49
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
<tr class="<%= major_row ? "method" : "" %>">
|
4
|
+
<td><%= ("%2.2f%" % total_pc) if major_row %></td>
|
5
|
+
<td><%= ("%2.2f%" % self_pc) if major_row %></td>
|
6
|
+
<td><%= total/1000.0 %></td>
|
7
|
+
<td><%= self_/1000.0 %></td>
|
8
|
+
<td><%= children/1000.0 %></td>
|
9
|
+
<td><%= calls %></td>
|
10
|
+
<td>
|
11
|
+
<% if major_row %>
|
12
|
+
<a name="<%= safe_name(name) + (inv_id ? "_#{inv_id}" : "") %>"><%= name %></a>
|
13
|
+
<% else %>
|
14
|
+
<a href="#<%= safe_name(name) + (inv_id ? "_#{inv_id}" : "") %>"><%= name %></a>
|
15
|
+
<% end %>
|
16
|
+
</td>
|
17
|
+
</tr>
|
18
|
+
|
data/test/basic_test.rb
ADDED
@@ -0,0 +1,291 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
4
|
+
require 'jruby-prof'
|
5
|
+
|
6
|
+
class C1
|
7
|
+
def C1.hello
|
8
|
+
sleep(0.1)
|
9
|
+
end
|
10
|
+
|
11
|
+
def hello
|
12
|
+
sleep(0.2)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module M1
|
17
|
+
def hello
|
18
|
+
sleep(0.3)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class C2
|
23
|
+
include M1
|
24
|
+
extend M1
|
25
|
+
end
|
26
|
+
|
27
|
+
class C3
|
28
|
+
def hello
|
29
|
+
sleep(0.4)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module M4
|
34
|
+
def hello
|
35
|
+
sleep(0.5)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module M5
|
40
|
+
include M4
|
41
|
+
def goodbye
|
42
|
+
hello
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class C6
|
47
|
+
include M5
|
48
|
+
def test
|
49
|
+
goodbye
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class BasicTest < Test::Unit::TestCase
|
54
|
+
def setup
|
55
|
+
# Need to use wall time for this test due to the sleep calls
|
56
|
+
#RubyProf::measure_mode = RubyProf::WALL_TIME
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_running
|
60
|
+
assert(!JRubyProf.running?)
|
61
|
+
JRubyProf.start
|
62
|
+
assert(JRubyProf.running?)
|
63
|
+
JRubyProf.stop
|
64
|
+
assert(!JRubyProf.running?)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_double_profile
|
68
|
+
JRubyProf.start
|
69
|
+
assert_raise(RuntimeError) do
|
70
|
+
JRubyProf.start
|
71
|
+
end
|
72
|
+
|
73
|
+
assert_raise(RuntimeError) do
|
74
|
+
JRubyProf.profile do
|
75
|
+
puts 1
|
76
|
+
end
|
77
|
+
end
|
78
|
+
JRubyProf.stop
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_no_block
|
82
|
+
assert_raise(ArgumentError) do
|
83
|
+
JRubyProf.profile
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_class_methods
|
88
|
+
result = JRubyProf.profile do
|
89
|
+
C1.hello
|
90
|
+
end
|
91
|
+
|
92
|
+
# Length should be 3:
|
93
|
+
# BasicTest#test_class_methods
|
94
|
+
# <Class::C1>#hello
|
95
|
+
# Kernel#sleep
|
96
|
+
|
97
|
+
methods = result.values.to_a.first.get_methods.values.sort_by {|m| m.name}
|
98
|
+
p methods
|
99
|
+
assert_equal(3, methods.length)
|
100
|
+
|
101
|
+
# Check the names
|
102
|
+
assert_equal('BasicTest#test_class_methods', methods[0].name)
|
103
|
+
assert_equal('<Class::C1>#hello', methods[1].name)
|
104
|
+
assert_equal('Kernel#sleep', methods[2].name)
|
105
|
+
|
106
|
+
# Check times
|
107
|
+
assert_in_delta(0.1, methods[0].total_time, 0.01)
|
108
|
+
assert_in_delta(0, methods[0].wait_time, 0.01)
|
109
|
+
assert_in_delta(0, methods[0].self_time, 0.01)
|
110
|
+
|
111
|
+
assert_in_delta(0.1, methods[1].total_time, 0.01)
|
112
|
+
assert_in_delta(0, methods[1].wait_time, 0.01)
|
113
|
+
assert_in_delta(0, methods[1].self_time, 0.01)
|
114
|
+
|
115
|
+
assert_in_delta(0.1, methods[2].total_time, 0.01)
|
116
|
+
assert_in_delta(0, methods[2].wait_time, 0.01)
|
117
|
+
assert_in_delta(0.1, methods[2].self_time, 0.01)
|
118
|
+
end
|
119
|
+
#
|
120
|
+
#if RUBY_VERSION < '1.9'
|
121
|
+
# PARENT = Object
|
122
|
+
#else
|
123
|
+
# PARENT = BasicObject
|
124
|
+
#end
|
125
|
+
#
|
126
|
+
#def test_instance_methods
|
127
|
+
# result = RubyProf.profile do
|
128
|
+
# C1.new.hello
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# # Methods called
|
132
|
+
# # BasicTest#test_instance_methods
|
133
|
+
# # Class.new
|
134
|
+
# # Class:Object#allocate
|
135
|
+
# # for Object#initialize
|
136
|
+
# # C1#hello
|
137
|
+
# # Kernel#sleep
|
138
|
+
#
|
139
|
+
# methods = result.threads.values.first.sort.reverse
|
140
|
+
# assert_equal(6, methods.length)
|
141
|
+
# names = methods.map(&:full_name)
|
142
|
+
# assert_equal('BasicTest#test_instance_methods', names[0])
|
143
|
+
# assert_equal('C1#hello', names[1])
|
144
|
+
# assert_equal('Kernel#sleep', names[2])
|
145
|
+
# assert_equal('Class#new', names[3])
|
146
|
+
# # order can differ
|
147
|
+
# assert(names.include?("<Class::#{PARENT}>#allocate"))
|
148
|
+
# assert(names.include?("#{PARENT}#initialize"))
|
149
|
+
#
|
150
|
+
# # Check times
|
151
|
+
# assert_in_delta(0.2, methods[0].total_time, 0.02)
|
152
|
+
# assert_in_delta(0, methods[0].wait_time, 0.02)
|
153
|
+
# assert_in_delta(0, methods[0].self_time, 0.02)
|
154
|
+
#
|
155
|
+
# assert_in_delta(0.2, methods[1].total_time, 0.02)
|
156
|
+
# assert_in_delta(0, methods[1].wait_time, 0.02)
|
157
|
+
# assert_in_delta(0, methods[1].self_time, 0.02)
|
158
|
+
#
|
159
|
+
# assert_in_delta(0.2, methods[2].total_time, 0.02)
|
160
|
+
# assert_in_delta(0, methods[2].wait_time, 0.02)
|
161
|
+
# assert_in_delta(0.2, methods[2].self_time, 0.02)
|
162
|
+
#
|
163
|
+
# assert_in_delta(0, methods[3].total_time, 0.01)
|
164
|
+
# assert_in_delta(0, methods[3].wait_time, 0.01)
|
165
|
+
# assert_in_delta(0, methods[3].self_time, 0.01)
|
166
|
+
#
|
167
|
+
# assert_in_delta(0, methods[4].total_time, 0.01)
|
168
|
+
# assert_in_delta(0, methods[4].wait_time, 0.01)
|
169
|
+
# assert_in_delta(0, methods[4].self_time, 0.01)
|
170
|
+
#
|
171
|
+
# assert_in_delta(0, methods[5].total_time, 0.01)
|
172
|
+
# assert_in_delta(0, methods[5].wait_time, 0.01)
|
173
|
+
# assert_in_delta(0, methods[5].self_time, 0.01)
|
174
|
+
#end
|
175
|
+
#
|
176
|
+
#def test_module_methods
|
177
|
+
# result = RubyProf.profile do
|
178
|
+
# C2.hello
|
179
|
+
# end
|
180
|
+
#
|
181
|
+
# # Methods:
|
182
|
+
# # BasicTest#test_module_methods
|
183
|
+
# # M1#hello
|
184
|
+
# # Kernel#sleep
|
185
|
+
#
|
186
|
+
# methods = result.threads.values.first.sort.reverse
|
187
|
+
# assert_equal(3, methods.length)
|
188
|
+
#
|
189
|
+
# assert_equal('BasicTest#test_module_methods', methods[0].full_name)
|
190
|
+
# assert_equal('M1#hello', methods[1].full_name)
|
191
|
+
# assert_equal('Kernel#sleep', methods[2].full_name)
|
192
|
+
#
|
193
|
+
# # Check times
|
194
|
+
# assert_in_delta(0.3, methods[0].total_time, 0.1)
|
195
|
+
# assert_in_delta(0, methods[0].wait_time, 0.02)
|
196
|
+
# assert_in_delta(0, methods[0].self_time, 0.02)
|
197
|
+
#
|
198
|
+
# assert_in_delta(0.3, methods[1].total_time, 0.1)
|
199
|
+
# assert_in_delta(0, methods[1].wait_time, 0.02)
|
200
|
+
# assert_in_delta(0, methods[1].self_time, 0.02)
|
201
|
+
#
|
202
|
+
# assert_in_delta(0.3, methods[2].total_time, 0.1)
|
203
|
+
# assert_in_delta(0, methods[2].wait_time, 0.02)
|
204
|
+
# assert_in_delta(0.3, methods[2].self_time, 0.1)
|
205
|
+
#end
|
206
|
+
#
|
207
|
+
#def test_module_instance_methods
|
208
|
+
# result = RubyProf.profile do
|
209
|
+
# C2.new.hello
|
210
|
+
# end
|
211
|
+
#
|
212
|
+
# # Methods:
|
213
|
+
# # BasicTest#test_module_instance_methods
|
214
|
+
# # Class#new
|
215
|
+
# # <Class::Object>#allocate
|
216
|
+
# # Object#initialize
|
217
|
+
# # M1#hello
|
218
|
+
# # Kernel#sleep
|
219
|
+
#
|
220
|
+
# methods = result.threads.values.first.sort.reverse
|
221
|
+
# assert_equal(6, methods.length)
|
222
|
+
# names = methods.map(&:full_name)
|
223
|
+
# assert_equal('BasicTest#test_module_instance_methods', names[0])
|
224
|
+
# assert_equal('M1#hello', names[1])
|
225
|
+
# assert_equal('Kernel#sleep', names[2])
|
226
|
+
# assert_equal('Class#new', names[3])
|
227
|
+
# assert(names.include?("<Class::#{PARENT}>#allocate"))
|
228
|
+
# assert(names.include?("#{PARENT}#initialize"))
|
229
|
+
#
|
230
|
+
# # Check times
|
231
|
+
# assert_in_delta(0.3, methods[0].total_time, 0.1)
|
232
|
+
# assert_in_delta(0, methods[0].wait_time, 0.1)
|
233
|
+
# assert_in_delta(0, methods[0].self_time, 0.1)
|
234
|
+
#
|
235
|
+
# assert_in_delta(0.3, methods[1].total_time, 0.02)
|
236
|
+
# assert_in_delta(0, methods[1].wait_time, 0.01)
|
237
|
+
# assert_in_delta(0, methods[1].self_time, 0.01)
|
238
|
+
#
|
239
|
+
# assert_in_delta(0.3, methods[2].total_time, 0.02)
|
240
|
+
# assert_in_delta(0, methods[2].wait_time, 0.01)
|
241
|
+
# assert_in_delta(0.3, methods[2].self_time, 0.02)
|
242
|
+
#
|
243
|
+
# assert_in_delta(0, methods[3].total_time, 0.01)
|
244
|
+
# assert_in_delta(0, methods[3].wait_time, 0.01)
|
245
|
+
# assert_in_delta(0, methods[3].self_time, 0.01)
|
246
|
+
#
|
247
|
+
# assert_in_delta(0, methods[4].total_time, 0.01)
|
248
|
+
# assert_in_delta(0, methods[4].wait_time, 0.01)
|
249
|
+
# assert_in_delta(0, methods[4].self_time, 0.01)
|
250
|
+
#
|
251
|
+
# assert_in_delta(0, methods[5].total_time, 0.01)
|
252
|
+
# assert_in_delta(0, methods[5].wait_time, 0.01)
|
253
|
+
# assert_in_delta(0, methods[5].self_time, 0.01)
|
254
|
+
#end
|
255
|
+
#
|
256
|
+
#def test_singleton
|
257
|
+
# c3 = C3.new
|
258
|
+
#
|
259
|
+
# class << c3
|
260
|
+
# def hello
|
261
|
+
# end
|
262
|
+
# end
|
263
|
+
#
|
264
|
+
# result = RubyProf.profile do
|
265
|
+
# c3.hello
|
266
|
+
# end
|
267
|
+
#
|
268
|
+
# methods = result.threads.values.first.sort.reverse
|
269
|
+
# assert_equal(2, methods.length)
|
270
|
+
#
|
271
|
+
# assert_equal('BasicTest#test_singleton', methods[0].full_name)
|
272
|
+
# assert_equal('<Object::C3>#hello', methods[1].full_name)
|
273
|
+
#
|
274
|
+
# assert_in_delta(0, methods[0].total_time, 0.01)
|
275
|
+
# assert_in_delta(0, methods[0].wait_time, 0.01)
|
276
|
+
# assert_in_delta(0, methods[0].self_time, 0.01)
|
277
|
+
#
|
278
|
+
# assert_in_delta(0, methods[1].total_time, 0.01)
|
279
|
+
# assert_in_delta(0, methods[1].wait_time, 0.01)
|
280
|
+
# assert_in_delta(0, methods[1].self_time, 0.01)
|
281
|
+
#end
|
282
|
+
#
|
283
|
+
#def test_traceback
|
284
|
+
# RubyProf.start
|
285
|
+
# assert_raise(NoMethodError) do
|
286
|
+
# RubyProf.xxx
|
287
|
+
# end
|
288
|
+
#
|
289
|
+
# RubyProf.stop
|
290
|
+
#end
|
291
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tw-studios-jruby-prof
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel Lucraft
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-10-05 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: dan@fluentradical.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README
|
26
|
+
- lib/jruby-prof
|
27
|
+
- lib/jruby-prof/abstract_printer.rb
|
28
|
+
- lib/jruby-prof/flat_text_printer.rb
|
29
|
+
- lib/jruby-prof/graph_html_printer.rb
|
30
|
+
- lib/jruby-prof/graph_text_printer.rb
|
31
|
+
- lib/jruby-prof/invocation_set.rb
|
32
|
+
- lib/jruby-prof/method.rb
|
33
|
+
- lib/jruby-prof/profile_invocation.rb
|
34
|
+
- lib/jruby-prof/simple_tree_printer.rb
|
35
|
+
- lib/jruby-prof/tree_html_printer.rb
|
36
|
+
- lib/jruby-prof.jar
|
37
|
+
- lib/jruby-prof.rb
|
38
|
+
- templates/graph_row.html.erb
|
39
|
+
- src/org
|
40
|
+
- src/org/jruby
|
41
|
+
- src/org/jruby/prof
|
42
|
+
- src/org/jruby/prof/Invocation.java
|
43
|
+
- src/org/jruby/prof/JRubyProf.java
|
44
|
+
- src/org/jruby/prof/ProfEventHook.java
|
45
|
+
- test/basic_test.rb
|
46
|
+
has_rdoc: false
|
47
|
+
homepage: http://danlucraft.com/blog
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.0.1
|
69
|
+
signing_key:
|
70
|
+
specification_version: 2
|
71
|
+
summary: A Ruby level profiler for JRuby
|
72
|
+
test_files: []
|
73
|
+
|