ruby-dtrace 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|