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 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
Binary file
@@ -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
+
@@ -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
+