ruby-dtrace 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +3 -0
- data/Manifest.txt +2 -1
- data/lib/dtrace.rb +7 -1
- data/plugin/dtrace/README +78 -1
- data/plugin/dtrace/bin/dtracer.rb +6 -1
- data/plugin/dtrace/lib/dtrace_report.rb +19 -4
- data/plugin/dtrace/lib/dtracer.rb +23 -55
- data/plugin/dtrace/lib/dtracer_client.rb +9 -1
- data/plugin/dtrace/scripts/default.d +11 -0
- data/plugin/dtrace/scripts/rails_mysql.d +29 -0
- data/plugin/dtrace/views/dtrace/_report.rhtml +15 -45
- metadata +4 -3
- data/plugin/dtrace/lib/dtrace_tracer.rb +0 -34
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
@@ -32,10 +32,11 @@ plugin/dtrace/bin/dtracer.rb
|
|
32
32
|
plugin/dtrace/init.rb
|
33
33
|
plugin/dtrace/lib/dtrace_helper.rb
|
34
34
|
plugin/dtrace/lib/dtrace_report.rb
|
35
|
-
plugin/dtrace/lib/dtrace_tracer.rb
|
36
35
|
plugin/dtrace/lib/dtracer.rb
|
37
36
|
plugin/dtrace/lib/dtracer_client.rb
|
38
37
|
plugin/dtrace/public/stylesheets/dtrace.css
|
38
|
+
plugin/dtrace/scripts/default.d
|
39
|
+
plugin/dtrace/scripts/rails_mysql.d
|
39
40
|
plugin/dtrace/tasks/dtrace.rake
|
40
41
|
plugin/dtrace/test/dtrace_test.rb
|
41
42
|
plugin/dtrace/views/dtrace/_report.rhtml
|
data/lib/dtrace.rb
CHANGED
data/plugin/dtrace/README
CHANGED
@@ -1,4 +1,81 @@
|
|
1
1
|
Dtrace
|
2
2
|
======
|
3
3
|
|
4
|
-
|
4
|
+
A simple plugin to run a DTrace script while your Rails app runs.
|
5
|
+
|
6
|
+
Installation:
|
7
|
+
|
8
|
+
script/plugin install http://ruby-dtrace.rubyforge.org/svn/trunk/plugin/dtrace
|
9
|
+
|
10
|
+
Or copy the plugin/dtrace directory from the installed ruby-dtrace gem
|
11
|
+
to your app's vendor/plugins directory.
|
12
|
+
|
13
|
+
Choose a mode of operation:
|
14
|
+
|
15
|
+
The process running DTrace needs additional privileges: on Solaris,
|
16
|
+
you can grant the dtrace_* privileges, and on both Leopard and Solaris
|
17
|
+
you can run the process as root.
|
18
|
+
|
19
|
+
This plugin can run in two modes: you can run the whole app with
|
20
|
+
DTrace privileges, or you can run a helper process with the
|
21
|
+
privileges, and the app will use DRb to communicate with it.
|
22
|
+
|
23
|
+
If you choose to run the app as root, you need to set the :tracer
|
24
|
+
option to the dtrace macro to :self, and if you choose to run the
|
25
|
+
helper as root, you need to set the :tracer option to :helper.
|
26
|
+
|
27
|
+
Configuring the plugin:
|
28
|
+
|
29
|
+
Add the following line to either the specific controller you want
|
30
|
+
traced, or to the ApplicationController, in which case it will be
|
31
|
+
applied to every controller in your app:
|
32
|
+
|
33
|
+
dtrace :on, :tracer => :self
|
34
|
+
|
35
|
+
or
|
36
|
+
|
37
|
+
dtrace :on, :tracer => :helper
|
38
|
+
|
39
|
+
You can also set the name of the script to be run, from the
|
40
|
+
vendor/plugins/dtrace/scripts/ directory with the :script option:
|
41
|
+
|
42
|
+
dtrace :on, :tracer => :self, :script => 'rails_mysql.d'
|
43
|
+
|
44
|
+
There are two scripts in the distribution:
|
45
|
+
* default.d
|
46
|
+
- a very simple script to log system calls, which doesn't require
|
47
|
+
a DTrace-enabled ruby.
|
48
|
+
|
49
|
+
* rails_mysql.d
|
50
|
+
- a simplified version of the script shown here:
|
51
|
+
http://blogs.sun.com/bmc/entry/dtrace_on_rails, which relies on
|
52
|
+
DTrace-enabled ruby, and the use of MySQL with the
|
53
|
+
libmysqlclient-based adapter, not the pure-ruby adapter.
|
54
|
+
|
55
|
+
Troubleshooting:
|
56
|
+
|
57
|
+
If you don't get a DTrace report, check the log for messages like:
|
58
|
+
|
59
|
+
DTrace start setup: unable to open dtrace (not root?)
|
60
|
+
|
61
|
+
(indicating a lack of privileges to start DTrace: check your setting
|
62
|
+
of the :tracer option, and how you are starting the application and/or
|
63
|
+
helper).
|
64
|
+
|
65
|
+
DTrace start compile: probe description pid14484::mysql_real_query:entry does not match any probes
|
66
|
+
|
67
|
+
(indicating the function mysql_real_query isn't found: either you're
|
68
|
+
not using MySQL, or you are using the pure ruby client).
|
69
|
+
|
70
|
+
DTrace start compile: probe description ruby15253:::function-entry does not match any probes
|
71
|
+
|
72
|
+
(indicating you are not using a DTrace-enabled ruby binary)
|
73
|
+
|
74
|
+
A DRb::DRbConnError reported by Rails, like this:
|
75
|
+
|
76
|
+
druby://localhost:2999 - #<Errno::ECONNREFUSED: Connection refused - connect(2)>
|
77
|
+
|
78
|
+
indicates you've selected the :helper option, but the helper isn't
|
79
|
+
running. Check that your app can connect to localhost:2999 for the DRb
|
80
|
+
service the helper provides.
|
81
|
+
|
@@ -10,34 +10,49 @@ module DtraceReport
|
|
10
10
|
module DtraceMacro
|
11
11
|
def dtrace(enable=:on, options={})
|
12
12
|
if enable == :on
|
13
|
+
|
14
|
+
# Set tracer type, in-process or helper-process
|
13
15
|
if options[:tracer] == :self
|
14
|
-
|
16
|
+
tracer = Dtracer.new
|
15
17
|
elsif options[:tracer] == :helper
|
16
|
-
|
18
|
+
tracer = DtracerClient.new
|
17
19
|
else
|
18
20
|
raise "tracer option is self or helper"
|
19
21
|
end
|
22
|
+
|
23
|
+
tracer.logger = logger
|
24
|
+
|
25
|
+
# Set script, or default
|
26
|
+
if options[:script]
|
27
|
+
tracer.script = options[:script]
|
28
|
+
else
|
29
|
+
tracer.script = 'default.d'
|
30
|
+
end
|
31
|
+
|
32
|
+
DtraceReport.tracer = tracer
|
20
33
|
end
|
21
34
|
end
|
22
35
|
end
|
23
36
|
|
24
37
|
attr_reader :dtrace_report
|
38
|
+
attr_reader :dtrace_script
|
25
39
|
|
26
40
|
protected
|
27
41
|
def self.tracer=(tracer)
|
28
42
|
@@tracer = tracer
|
29
43
|
end
|
30
|
-
|
44
|
+
|
31
45
|
def enable_dtrace
|
32
46
|
@@tracer.start_dtrace($$)
|
33
47
|
end
|
34
48
|
|
35
49
|
def append_dtrace_report
|
50
|
+
@dtrace_script = @@tracer.script
|
36
51
|
@dtrace_report = @@tracer.end_dtrace
|
37
52
|
# yuck!
|
38
53
|
old_template_root = @template.base_path
|
39
54
|
begin
|
40
|
-
@template.view_paths =
|
55
|
+
@template.view_paths = File.expand_path(File.dirname(__FILE__) + '/../views')
|
41
56
|
response.body.gsub!(/<\/body/, @template.render(:partial => 'dtrace/report') + '</body')
|
42
57
|
ensure
|
43
58
|
@template.view_paths = old_template_root
|
@@ -1,84 +1,52 @@
|
|
1
1
|
require 'dtrace'
|
2
2
|
|
3
3
|
class Dtracer
|
4
|
-
|
5
|
-
|
6
|
-
progtext = <<EOD
|
7
|
-
self string uri;
|
8
|
-
|
9
|
-
pid$1::mysql_real_query:entry
|
10
|
-
{
|
11
|
-
@queries[copyinstr(arg1)] = count();
|
12
|
-
}
|
13
|
-
|
14
|
-
ruby$1:::function-entry
|
15
|
-
{
|
16
|
-
@rbclasses[this->class = copyinstr(arg0)] = count();
|
17
|
-
this->sep = strjoin(this->class, "#");
|
18
|
-
@rbmethods[strjoin(this->sep, copyinstr(arg1))] = count();
|
19
|
-
}
|
20
|
-
|
21
|
-
syscall:::entry
|
22
|
-
{
|
23
|
-
@syscalls[probefunc] = count();
|
24
|
-
}
|
25
|
-
|
26
|
-
END
|
27
|
-
{
|
28
|
-
printf("report:syscalls");
|
29
|
-
printa(@syscalls);
|
30
|
-
printf("report:rbclasses");
|
31
|
-
printa(@rbclasses);
|
32
|
-
printf("report:rbmethods");
|
33
|
-
printa(@rbmethods);
|
34
|
-
printf("report:queries");
|
35
|
-
printa(@queries);
|
36
|
-
}
|
4
|
+
attr_writer :logger, :dprogram
|
5
|
+
attr_reader :script
|
37
6
|
|
38
|
-
|
7
|
+
def script=(script)
|
8
|
+
@script = script
|
9
|
+
scriptdir = File.expand_path(File.dirname(__FILE__) + "/../scripts")
|
10
|
+
@dprogram = IO.read("#{scriptdir}/#{script}")
|
11
|
+
end
|
39
12
|
|
13
|
+
def start_dtrace(pid)
|
40
14
|
begin
|
41
15
|
@d = Dtrace.new
|
42
16
|
@d.setopt("aggsize", "4m")
|
43
17
|
@d.setopt("bufsize", "4m")
|
44
18
|
rescue DtraceException => e
|
45
|
-
|
19
|
+
@logger.warn("DTrace start setup: #{e.message}")
|
46
20
|
return
|
47
21
|
end
|
48
22
|
|
49
23
|
begin
|
50
|
-
prog = @d.compile(
|
24
|
+
prog = @d.compile(@dprogram, pid.to_s)
|
51
25
|
prog.execute
|
52
26
|
@d.go
|
53
27
|
rescue DtraceException => e
|
54
|
-
|
28
|
+
@logger.warn("DTrace start compile: #{e.message}")
|
55
29
|
end
|
56
|
-
|
57
30
|
end
|
58
31
|
|
59
32
|
def end_dtrace
|
60
|
-
|
33
|
+
# Check presence of handle and correct status.
|
34
|
+
return [] unless @d && @d.status == Dtrace::STATUS_OKAY
|
61
35
|
|
62
|
-
|
36
|
+
dtrace_data = nil
|
63
37
|
begin
|
64
|
-
dtrace_report = Hash.new
|
65
38
|
c = DtraceConsumer.new(@d)
|
66
|
-
c.consume_once do |
|
67
|
-
|
68
|
-
dtrace_report[current_report][e.tuple.first] = e.value
|
69
|
-
elsif e.respond_to? :value
|
70
|
-
if e.value =~ /report:(.*)/
|
71
|
-
current_report = Regexp.last_match(1)
|
72
|
-
unless dtrace_report[current_report]
|
73
|
-
dtrace_report[current_report] = Hash.new
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
39
|
+
c.consume_once do |d|
|
40
|
+
dtrace_data = d
|
77
41
|
end
|
78
42
|
rescue DtraceException => e
|
79
|
-
|
43
|
+
@logger.warn("DTrace end: #{e.message}")
|
44
|
+
end
|
45
|
+
|
46
|
+
if dtrace_data
|
47
|
+
return dtrace_data.data
|
48
|
+
else
|
49
|
+
return []
|
80
50
|
end
|
81
|
-
|
82
|
-
return dtrace_report
|
83
51
|
end
|
84
52
|
end
|
@@ -1,12 +1,20 @@
|
|
1
1
|
require 'drb'
|
2
2
|
|
3
3
|
class DtracerClient
|
4
|
+
attr_writer :logger
|
5
|
+
attr_reader :script
|
4
6
|
|
5
7
|
def initialize
|
6
8
|
DRb.start_service
|
7
9
|
@tracer = DRbObject.new(nil, 'druby://localhost:2999')
|
8
10
|
end
|
9
11
|
|
12
|
+
def script=(script)
|
13
|
+
@script = script
|
14
|
+
scriptdir = File.expand_path(File.dirname(__FILE__) + "/../scripts")
|
15
|
+
@tracer.dprogram = IO.read("#{scriptdir}/#{script}")
|
16
|
+
end
|
17
|
+
|
10
18
|
def start_dtrace(pid)
|
11
19
|
@tracer.start_dtrace(pid)
|
12
20
|
end
|
@@ -14,5 +22,5 @@ class DtracerClient
|
|
14
22
|
def end_dtrace
|
15
23
|
@tracer.end_dtrace
|
16
24
|
end
|
17
|
-
|
25
|
+
|
18
26
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
pid$1::mysql_real_query:entry
|
2
|
+
{
|
3
|
+
@queries[copyinstr(arg1)] = count();
|
4
|
+
}
|
5
|
+
|
6
|
+
ruby$1:::function-entry
|
7
|
+
{
|
8
|
+
@rbclasses[this->class = copyinstr(arg0)] = count();
|
9
|
+
this->sep = strjoin(this->class, "#");
|
10
|
+
@rbmethods[strjoin(this->sep, copyinstr(arg1))] = count();
|
11
|
+
}
|
12
|
+
|
13
|
+
syscall:::entry
|
14
|
+
/pid == $1/
|
15
|
+
{
|
16
|
+
@syscalls[probefunc] = count();
|
17
|
+
}
|
18
|
+
|
19
|
+
END
|
20
|
+
{
|
21
|
+
printf("MySQL Queries");
|
22
|
+
printa(@queries);
|
23
|
+
printf("System Calls");
|
24
|
+
printa(@syscalls);
|
25
|
+
printf("Ruby Classes");
|
26
|
+
printa(@rbclasses);
|
27
|
+
printf("Ruby Methods");
|
28
|
+
printa(@rbmethods);
|
29
|
+
}
|
@@ -1,56 +1,26 @@
|
|
1
1
|
<style type="text/css">
|
2
|
-
|
2
|
+
div#dtrace-report { background-color: #ddd; padding: 1em; margin: 1em; }
|
3
|
+
table.dtrace-report { background-color: #eee }
|
3
4
|
</style>
|
4
5
|
|
6
|
+
<% if controller.dtrace_report.length > 0 %>
|
5
7
|
<div id="dtrace-report">
|
6
|
-
<h2>DTrace Report
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
<% controller.dtrace_report['queries'].sort {|a,b| b[1] <=> a[1] }.each do |e| %>
|
12
|
-
<tr>
|
13
|
-
<td><%= e[0] %></td>
|
14
|
-
<td><%= e[1] %></td>
|
15
|
-
</tr>
|
16
|
-
<% end %>
|
17
|
-
</table>
|
18
|
-
<% end %>
|
19
|
-
|
20
|
-
<% if controller.dtrace_report['syscalls'] %>
|
21
|
-
<h3>System calls</h3>
|
22
|
-
<table class="report">
|
23
|
-
<% controller.dtrace_report['syscalls'].sort {|a,b| b[1] <=> a[1] }.each do |e| %>
|
24
|
-
<tr>
|
25
|
-
<td><%= e[0] %></td>
|
26
|
-
<td><%= e[1] %></td>
|
27
|
-
</tr>
|
28
|
-
<% end %>
|
29
|
-
</table>
|
30
|
-
<% end %>
|
31
|
-
|
32
|
-
<% if controller.dtrace_report['rbclasses'] %>
|
33
|
-
<h3>Ruby classes instantiated</h3>
|
34
|
-
<table class="report">
|
35
|
-
<% controller.dtrace_report['rbclasses'].sort {|a,b| b[1] <=> a[1] }.each do |e| %>
|
8
|
+
<h2>DTrace Report: <%=h controller.dtrace_script %></h2>
|
9
|
+
<% controller.dtrace_report.each do |r| %>
|
10
|
+
<table class="dtrace-report">
|
11
|
+
<% if r.respond_to? :data %>
|
12
|
+
<% r.data.sort {|a,b| b.value <=> a.value }.each do |agg| %>
|
36
13
|
<tr>
|
37
|
-
<td><%=
|
38
|
-
<td><%=
|
14
|
+
<td><%=h agg.tuple %></td>
|
15
|
+
<td><%=h agg.value %></td>
|
39
16
|
</tr>
|
40
17
|
<% end %>
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
<h3>Ruby methods called</h3>
|
46
|
-
<table class="report">
|
47
|
-
<% controller.dtrace_report['rbmethods'].sort {|a,b| b[1] <=> a[1] }.each do |e| %>
|
48
|
-
<tr>
|
49
|
-
<td><%= e[0] %></td>
|
50
|
-
<td><%= e[1] %></td>
|
51
|
-
</tr>
|
18
|
+
<% else %>
|
19
|
+
<% if r.value != 0 %>
|
20
|
+
<h3><%=h r.value %></h3>
|
21
|
+
<% end %>
|
52
22
|
<% end %>
|
53
23
|
</table>
|
54
24
|
<% end %>
|
55
|
-
|
56
25
|
</div>
|
26
|
+
<% end %>
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: ruby-dtrace
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0.
|
7
|
-
date: 2008-01-
|
6
|
+
version: 0.0.4
|
7
|
+
date: 2008-01-14 00:00:00 +00:00
|
8
8
|
summary: Ruby bindings for libdtrace
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -64,10 +64,11 @@ files:
|
|
64
64
|
- plugin/dtrace/init.rb
|
65
65
|
- plugin/dtrace/lib/dtrace_helper.rb
|
66
66
|
- plugin/dtrace/lib/dtrace_report.rb
|
67
|
-
- plugin/dtrace/lib/dtrace_tracer.rb
|
68
67
|
- plugin/dtrace/lib/dtracer.rb
|
69
68
|
- plugin/dtrace/lib/dtracer_client.rb
|
70
69
|
- plugin/dtrace/public/stylesheets/dtrace.css
|
70
|
+
- plugin/dtrace/scripts/default.d
|
71
|
+
- plugin/dtrace/scripts/rails_mysql.d
|
71
72
|
- plugin/dtrace/tasks/dtrace.rake
|
72
73
|
- plugin/dtrace/test/dtrace_test.rb
|
73
74
|
- plugin/dtrace/views/dtrace/_report.rhtml
|
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'dtrace'
|
2
|
-
|
3
|
-
class Dtracer
|
4
|
-
|
5
|
-
def start_dtrace(pid)
|
6
|
-
@d = Dtrace.new
|
7
|
-
@d.setopt("aggsize", "4m")
|
8
|
-
@d.setopt("bufsize", "4m")
|
9
|
-
progtext = 'ruby$1:::function-entry{ @[strjoin(strjoin(copyinstr(arg0),"."),copyinstr(arg1))] = count(); }'
|
10
|
-
begin
|
11
|
-
prog = @d.compile(progtext, pid.to_s)
|
12
|
-
prog.execute
|
13
|
-
@d.go
|
14
|
-
rescue DtraceException => e
|
15
|
-
puts "start: #{e.message}"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def end_dtrace(pid)
|
20
|
-
begin
|
21
|
-
@d.stop
|
22
|
-
@d.aggregate_snap
|
23
|
-
|
24
|
-
dtrace_report = Hash.new
|
25
|
-
@d.each_aggregate do |agg|
|
26
|
-
dtrace_report[agg[1].data] = agg[2].data
|
27
|
-
end
|
28
|
-
rescue DtraceException => e
|
29
|
-
puts "end: #{e.message}"
|
30
|
-
end
|
31
|
-
|
32
|
-
return dtrace_report
|
33
|
-
end
|
34
|
-
end
|