xray 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 +65 -0
- data/bin/install_dtrace_on_ubuntu +30 -0
- data/bin/xray_profile_ruby_function_calls.d +54 -0
- data/bin/xray_top_10_busiest_code_path_for_process.d +12 -0
- data/bin/xray_top_10_busiest_code_path_on_system.d +35 -0
- data/bin/xray_trace_all_custom_ruby_probes.d +25 -0
- data/bin/xray_trace_all_ruby_probes.d +20 -0
- data/bin/xray_trace_memcached.d +20 -0
- data/bin/xray_trace_mysql.d +30 -0
- data/bin/xray_trace_rails_response_times.d +114 -0
- data/lib/xray.rb +2 -0
- data/lib/xray/dtrace/rails/action_controller_tracing_extension.rb +22 -0
- data/lib/xray/dtrace/rails/active_record_connection_tracing_extension.rb +15 -0
- data/lib/xray/dtrace/rails/enable_tracing.rb +25 -0
- data/lib/xray/dtrace/tracer.rb +86 -0
- data/lib/xray/dtrace/tracer/joyent.rb +25 -0
- data/lib/xray/dtrace/tracer/leopard.rb +28 -0
- data/lib/xray/dtrace/usdt/provider_extensions.rb +45 -0
- data/lib/xray/thread_dump_signal_handler.rb +31 -0
- data/patches_for_mri/caller_for_all_threads_patch_for_MRI_1.8.6.diff +229 -0
- data/patches_for_mri/caller_for_all_threads_patch_for_MRI_1.8.7.diff +229 -0
- data/patches_for_mri/patch-for-dtrace-instrumentation-of-matz-1.8.6-p114.diff +522 -0
- data/patches_for_mri/patch-for-joyent-mri-1.8.6-on-mac-os-x-leopard.diff +20645 -0
- data/rails/init.rb +2 -0
- data/test/all_tests.rb +1 -0
- data/test/functional/dtrace/tracer_test.rb +54 -0
- data/test/functional/tracer_script.rb +18 -0
- data/test/test_helper.rb +9 -0
- data/test/unit/xray/dtrace/tracer_test.rb +54 -0
- metadata +84 -0
data/README
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
= XRay
|
2
|
+
|
3
|
+
* http://rubyforge.org/projects/xray
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
XRay provides a lightweight yet powerful toolbox for troubleshooting Ruby
|
8
|
+
applications when things stop making sense. XRay includes GDB and DTrace
|
9
|
+
tooling as well as a Thread Dump utility that can dump the stack trace
|
10
|
+
of all the thread in your Ruby VM when you send a +QUIT+ signal.
|
11
|
+
|
12
|
+
== GDB
|
13
|
+
|
14
|
+
Copy the +gdb_macros+ file provided in the gem as your ~/.gdbinit file.
|
15
|
+
You will find more details on how to use them, in my
|
16
|
+
{Troubleshooting Ruby Shortcut}[http://ph7spot.com/publications/troubleshooting_ruby_processes]
|
17
|
+
|
18
|
+
== Thread Dump
|
19
|
+
|
20
|
+
After patching your Ruby VM with {caller_for_all_threads_patch_for_MRI_1.8.6.diff}[http://xray.rubyforge.org/svn/patches_for_mri/caller_for_all_threads_patch_for_MRI_1.8.6.diff]
|
21
|
+
(or {caller_for_all_threads_patch_for_MRI_1.8.7.diff}[http://xray.rubyforge.org/svn/patches_for_mri/caller_for_all_threads_patch_for_MRI_1.8.7.diff])
|
22
|
+
as explained in {this document}[http://ph7spot.com/caller_for_all_threads], you can install a signal
|
23
|
+
handler in charge of dumping the stack trace for all the threads
|
24
|
+
in your Ruby VM with:
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "xray/thread_dump_signal_handler"
|
28
|
+
|
29
|
+
You can then trigger a thread dump at any time with
|
30
|
+
|
31
|
+
kill -QUIT <pid of your ruby process>
|
32
|
+
|
33
|
+
== DTrace
|
34
|
+
|
35
|
+
=== Fire DTrace Application Probes
|
36
|
+
|
37
|
+
See XRay::DTrace::Tracer
|
38
|
+
|
39
|
+
=== Out-of-the-box Rails DTrace Instrumentation ***
|
40
|
+
|
41
|
+
You are one require away from triggering automatically DTrace events for
|
42
|
+
Rails requests, database access and template rendering. As simple as
|
43
|
+
|
44
|
+
# environment.rb
|
45
|
+
Rails::Initializer.run do |config|
|
46
|
+
|
47
|
+
...
|
48
|
+
|
49
|
+
config.after_initialize do
|
50
|
+
require "rubygems"
|
51
|
+
require "xray/dtrace/rails/enable_tracing"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
See
|
56
|
+
* lib/xray/dtrace/railsenable_tracing.rb
|
57
|
+
* lib/xray/dtrace/action_controller_tracing_extension.rb
|
58
|
+
* lib/xray/dtrace/active_record_tracing_extension.rb
|
59
|
+
|
60
|
+
|
61
|
+
== Author
|
62
|
+
|
63
|
+
Philippe Hanrigou,
|
64
|
+
http://ph7spot.com
|
65
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
#
|
3
|
+
# Painless DTrace install for Ubuntu
|
4
|
+
#
|
5
|
+
|
6
|
+
DISTRIBUTION=dtrace-20081028
|
7
|
+
|
8
|
+
download()
|
9
|
+
{
|
10
|
+
sudo apt-get install curl
|
11
|
+
if [ ! -e ${DISTRIBUTION} ]; then
|
12
|
+
echo "\n==== Downloading DTrace for Linux ====\n"
|
13
|
+
curl -O ftp://crisp.dynalias.com/pub/release/website/dtrace/${DISTRIBUTION}.tar.bz2
|
14
|
+
tar jxvf ${DISTRIBUTION}.tar.bz2
|
15
|
+
fi
|
16
|
+
}
|
17
|
+
|
18
|
+
install_dependencies()
|
19
|
+
{
|
20
|
+
sudo apt-get install zlib1g-dev flex bison elfutils libelf-dev libc6-dev linux-libc-dev
|
21
|
+
}
|
22
|
+
|
23
|
+
build()
|
24
|
+
{
|
25
|
+
make -C ${DISTRIBUTION} clean all
|
26
|
+
}
|
27
|
+
|
28
|
+
download
|
29
|
+
install_dependencies
|
30
|
+
build
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/sbin/dtrace -s
|
2
|
+
#pragma D option quiet
|
3
|
+
#pragma D option aggsortrev
|
4
|
+
#pragma D option dynvarsize=64m
|
5
|
+
|
6
|
+
/*
|
7
|
+
* Trace all ruby function calls and display their total time, average time
|
8
|
+
* an invocation number (slower first).
|
9
|
+
*
|
10
|
+
* Usage:
|
11
|
+
*
|
12
|
+
* sudo /usr/bin/xray_profile_ruby_function_calls.d -p <a pid>
|
13
|
+
*
|
14
|
+
* sudo /usr/bin/xray_profile_ruby_function_calls.d -c "ruby -v"
|
15
|
+
*
|
16
|
+
* sudo dtrace -s /usr/bin/xray_profile_ruby_function_calls.d -p <a pid>
|
17
|
+
*/
|
18
|
+
|
19
|
+
this string str;
|
20
|
+
|
21
|
+
dtrace:::BEGIN
|
22
|
+
{
|
23
|
+
printf("Tracing, please be patient... Ctrl-C to interrupt.\n");
|
24
|
+
depth = 0;
|
25
|
+
}
|
26
|
+
|
27
|
+
ruby$target:::function-entry
|
28
|
+
{
|
29
|
+
self->depth++;
|
30
|
+
self->start[copyinstr(arg0), copyinstr(arg1), self->depth] = timestamp;
|
31
|
+
}
|
32
|
+
|
33
|
+
ruby$target:::function-return
|
34
|
+
/(this->class = copyinstr(arg0)) != NULL && \
|
35
|
+
(this->func = copyinstr(arg1)) != NULL && \
|
36
|
+
self->start[this->class, this->func, self->depth]/
|
37
|
+
{
|
38
|
+
this->elapsed = timestamp - self->start[this->class, this->func, self->depth];
|
39
|
+
@num[this->class, this->func] = count();
|
40
|
+
@eavg[this->class, this->func] = avg(this->elapsed);
|
41
|
+
@esum[this->class, this->func] = sum(this->elapsed);
|
42
|
+
self->start[this->class, this->func, self->depth] = 0;
|
43
|
+
self->depth--;
|
44
|
+
}
|
45
|
+
|
46
|
+
dtrace:::END
|
47
|
+
{
|
48
|
+
normalize(@eavg, 1000);
|
49
|
+
normalize(@esum, 1000);
|
50
|
+
setopt("aggsortpos", "0"); /* Sort on total time */
|
51
|
+
printf("%95s\n", "_______ ELAPSED ______");
|
52
|
+
printf("%-40s %-30s %12s %10s %6s\n", "Class", "Method", "Total (us)", "Avg (us)", "Count\n");
|
53
|
+
printa("%-40.40s %-30.30s %@12d %@10d %@6d\n", @esum, @eavg, @num);
|
54
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/sbin/dtrace -s
|
2
|
+
#pragma D option quiet
|
3
|
+
#pragma D option dynvarsize=64m
|
4
|
+
|
5
|
+
dtrace:::BEGIN
|
6
|
+
{
|
7
|
+
printf("Tracing... Hit Ctrl-C to end.\n");
|
8
|
+
depth = 0;
|
9
|
+
}
|
10
|
+
|
11
|
+
|
12
|
+
profile-997
|
13
|
+
{
|
14
|
+
@kernel[stack(20)]=count();
|
15
|
+
}
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
profile-997
|
20
|
+
/arg1/ /* user level PC. Make sure that we are in user space */
|
21
|
+
{
|
22
|
+
printf("%d\n", arg1);
|
23
|
+
@userspace[execname, ustack(10)]=count();
|
24
|
+
}
|
25
|
+
|
26
|
+
END {
|
27
|
+
trunc(@kernel, 10);
|
28
|
+
trunc(@userspace, 10);
|
29
|
+
|
30
|
+
printf("%s", "\n =========== Top 10 Busiest Kernel Path =============\n");
|
31
|
+
printa(@kernel);
|
32
|
+
|
33
|
+
printf("%s", "\n =========== Top 10 Busiest User Path =============\n");
|
34
|
+
printa(@userspace);
|
35
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/sbin/dtrace -s
|
2
|
+
#pragma D option quiet
|
3
|
+
|
4
|
+
/*
|
5
|
+
* Trace all the custom ruby probes defined in your application
|
6
|
+
* using XRay::DTrace, Apple's DTracer or Joyent's Tracer module.
|
7
|
+
*
|
8
|
+
* Usage:
|
9
|
+
*
|
10
|
+
* sudo /usr/bin/xray_trace_all_custom_ruby_probes.d -p <a pid>
|
11
|
+
*
|
12
|
+
* sudo /usr/bin/xray_trace_all_custom_ruby_probes.d -c "ruby -v"
|
13
|
+
*
|
14
|
+
* sudo dtrace -s /usr/bin/xray_trace_all_custom_ruby_probes.d -p <a pid>
|
15
|
+
*/
|
16
|
+
|
17
|
+
dtrace:::BEGIN
|
18
|
+
{
|
19
|
+
printf("Tracing... Hit Ctrl-C to end.\n");
|
20
|
+
}
|
21
|
+
|
22
|
+
ruby$target:::ruby-probe
|
23
|
+
{
|
24
|
+
printf("=> %d == cpu: %2d == %15s '%s'\n", timestamp, cpu, copyinstr(arg0), copyinstr(arg1))
|
25
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/sbin/dtrace -s
|
2
|
+
#pragma D option quiet
|
3
|
+
#pragma D option dynvarsize=64m
|
4
|
+
|
5
|
+
/*
|
6
|
+
* Trace all the probes of the ruby provider
|
7
|
+
*
|
8
|
+
* Usage:
|
9
|
+
*
|
10
|
+
* sudo /usr/bin/xray_trace_all_ruby_probes.d -p <a pid>
|
11
|
+
*
|
12
|
+
* sudo /usr/bin/xray_trace_all_ruby_probes.d -c "ruby -v"
|
13
|
+
*
|
14
|
+
* sudo dtrace -s /usr/bin/xray_trace_all_ruby_probes.d -p <a pid>
|
15
|
+
*/
|
16
|
+
|
17
|
+
ruby$target:::
|
18
|
+
{
|
19
|
+
printf("=> cpu %2d == %-15.15s == %-15.15s == %s\n", cpu, probename, copyinstr(arg0), copyinstr(arg1))
|
20
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/sbin/dtrace -s
|
2
|
+
#pragma D option quiet
|
3
|
+
#pragma D option dynvarsize=64m
|
4
|
+
|
5
|
+
|
6
|
+
pid*:memcached:*do_item_update*:entry {
|
7
|
+
printf("COMMAND == %s \n", probefunc);
|
8
|
+
}
|
9
|
+
|
10
|
+
pid*:memcached:process_get_command:entry {
|
11
|
+
self->conn_id = arg0;
|
12
|
+
/* self->pointer_to_string = copyin(arg1, 4); */
|
13
|
+
printf("GET == connection %d == update %p %d\n", arg0, arg1, arg2);
|
14
|
+
}
|
15
|
+
|
16
|
+
pid*:memcached:process_update_command:entry {
|
17
|
+
self->conn_id = arg0;
|
18
|
+
/* self->pointer_to_string = copyin(arg1, 4); */
|
19
|
+
printf("UPDATE == connection %d == update %p %d\n", arg0, arg1, arg2);
|
20
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/sbin/dtrace -s
|
2
|
+
#pragma D option quiet
|
3
|
+
#pragma D option dynvarsize=64m
|
4
|
+
|
5
|
+
|
6
|
+
/*
|
7
|
+
* Inspired by Joyent's http://www.joyeur.com/2007/10/03/using-dtrace-on-mysql
|
8
|
+
*/
|
9
|
+
|
10
|
+
/*
|
11
|
+
* MySQL query parsing (captures all incoming queries)
|
12
|
+
*/
|
13
|
+
|
14
|
+
/* TODO For example we can see what queries are executed the most: => Top 10 queries */
|
15
|
+
pid*::*mysql*:entry
|
16
|
+
{
|
17
|
+
printf("%Y %s\n", walltimestamp, copyinstr(arg1));
|
18
|
+
}
|
19
|
+
|
20
|
+
|
21
|
+
/*
|
22
|
+
* Now lets say we want to find out how long a query took to execute.
|
23
|
+
* The function mysql_execute_command does the actual execution of
|
24
|
+
* the queries so all we do here is subtract the entry and return
|
25
|
+
* timestamps of this function.
|
26
|
+
*/
|
27
|
+
pid*:mysqld:*mysql_execute_command*:entry
|
28
|
+
{
|
29
|
+
printf("OK");
|
30
|
+
}
|
@@ -0,0 +1,114 @@
|
|
1
|
+
#!/usr/sbin/dtrace -s
|
2
|
+
#pragma D option quiet
|
3
|
+
#pragma D option aggsortrev
|
4
|
+
#pragma D option dynvarsize=64m
|
5
|
+
|
6
|
+
/*
|
7
|
+
* Trace Rails controller actions and database response times.
|
8
|
+
*
|
9
|
+
* Report average response time, total elapsed time and invocation count
|
10
|
+
* for all controller actions and datase queries.
|
11
|
+
*
|
12
|
+
* Also print response time distribution diagrams for global and individual
|
13
|
+
* action / query.
|
14
|
+
*
|
15
|
+
* Usage:
|
16
|
+
*
|
17
|
+
* sudo /usr/bin/xray_trace_rails_response_times.d -p <a pid>
|
18
|
+
*
|
19
|
+
* sudo /usr/bin/xray_trace_rails_response_times.d -c "ruby -v"
|
20
|
+
*
|
21
|
+
* sudo dtrace -s /usr/bin/xray_trace_rails_response_times.d -p <a pid>
|
22
|
+
*/
|
23
|
+
this string name;
|
24
|
+
this string action;
|
25
|
+
this string query;
|
26
|
+
|
27
|
+
dtrace:::BEGIN
|
28
|
+
{
|
29
|
+
printf("Tracing... Hit Ctrl-C to end.\n");
|
30
|
+
depth = 0;
|
31
|
+
}
|
32
|
+
|
33
|
+
ruby$target::ruby_dtrace_probe:
|
34
|
+
/(this->name = copyinstr(arg0)) == "request-start"/
|
35
|
+
{
|
36
|
+
self->request_start[copyinstr(arg1)] = timestamp;
|
37
|
+
}
|
38
|
+
|
39
|
+
|
40
|
+
ruby$target::ruby_dtrace_probe:
|
41
|
+
/(this->name = copyinstr(arg0)) == "request-end" && self->request_start[(this->action = copyinstr(arg1))]/
|
42
|
+
{
|
43
|
+
this->elapsed = timestamp - self->request_start[this->action];
|
44
|
+
@re_count[this->action] = count();
|
45
|
+
@re_eavg[this->action] = avg(this->elapsed);
|
46
|
+
@re_esum[this->action] = sum(this->elapsed);
|
47
|
+
@re_edist[this->action] = quantize(this->elapsed);
|
48
|
+
@re_all_edist["All Requests"] = quantize(this->elapsed);
|
49
|
+
self->request_start[this->action] = 0;
|
50
|
+
}
|
51
|
+
|
52
|
+
ruby$target::ruby_dtrace_probe:
|
53
|
+
/(this->name = copyinstr(arg0)) == "db-start"/
|
54
|
+
{
|
55
|
+
self->db_start[copyinstr(arg1)] = timestamp;
|
56
|
+
}
|
57
|
+
|
58
|
+
|
59
|
+
ruby$target::ruby_dtrace_probe:
|
60
|
+
/(this->name = copyinstr(arg0)) == "db-end" && self->db_start[(this->query = copyinstr(arg1))]/
|
61
|
+
{
|
62
|
+
@db_count[this->query] = count();
|
63
|
+
this->elapsed = timestamp - self->db_start[this->query];
|
64
|
+
@db_eavg[this->query] = avg(this->elapsed);
|
65
|
+
@db_esum[this->query] = sum(this->elapsed);
|
66
|
+
@db_edist[this->query] = quantize(this->elapsed);
|
67
|
+
@db_all_edist["All Queries"] = quantize(this->elapsed);
|
68
|
+
self->db_start[this->query] = 0;
|
69
|
+
}
|
70
|
+
|
71
|
+
dtrace:::END
|
72
|
+
{
|
73
|
+
normalize(@re_eavg, 1000);
|
74
|
+
normalize(@re_esum, 1000);
|
75
|
+
normalize(@db_eavg, 1000);
|
76
|
+
normalize(@db_esum, 1000);
|
77
|
+
|
78
|
+
printf("\n================================================\n");
|
79
|
+
printf(" Controller Response Times\n");
|
80
|
+
printf("================================================\n\n");
|
81
|
+
|
82
|
+
setopt("aggsortpos", "0"); /* Sort on avg time */
|
83
|
+
printf("%-50s %s\n", " _________ Action _________", "_ Avg. (us) _ Total (us) _ Count _\n");
|
84
|
+
printa("%-50.50s %@12d %@12d %@6d\n", @re_eavg, @re_esum, @re_count);
|
85
|
+
|
86
|
+
|
87
|
+
printf("\n================================================\n");
|
88
|
+
printf(" Database Response Times\n");
|
89
|
+
printf("================================================\n");
|
90
|
+
|
91
|
+
setopt("aggsortpos", "0"); /* Sort on avg time */
|
92
|
+
printf("%-100s %s\n", " _________ Query _________", "_ Avg. (us) _ Total (us) _ Count _\n");
|
93
|
+
printa("%-100.100s %@12d %@12d %@6d\n", @db_eavg, @db_esum, @db_count);
|
94
|
+
|
95
|
+
printf("\n================================================\n");
|
96
|
+
printf(" Response Time Summary\n");
|
97
|
+
printf("================================================\n\n");
|
98
|
+
|
99
|
+
printa(@re_all_edist);
|
100
|
+
printa(@db_all_edist);
|
101
|
+
|
102
|
+
printf("\n================================================\n");
|
103
|
+
printf(" Controller Response Time Distribution\n");
|
104
|
+
printf("================================================\n\n");
|
105
|
+
|
106
|
+
printa(@re_edist);
|
107
|
+
|
108
|
+
printf("\n================================================\n");
|
109
|
+
printf(" DB Response Time Distribution\n");
|
110
|
+
printf("================================================\n\n");
|
111
|
+
|
112
|
+
printa(@db_edist);
|
113
|
+
}
|
114
|
+
|
data/lib/xray.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#
|
2
|
+
# Decorate ActionController::Base with request tracing
|
3
|
+
#
|
4
|
+
ActionController::Base.class_eval do
|
5
|
+
include XRay::DTrace::Tracer
|
6
|
+
|
7
|
+
def perform_action_with_tracing
|
8
|
+
firing('request', "#{self.class.to_s}##{action_name.to_s}") do
|
9
|
+
perform_action_without_tracing
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def render_with_tracing(options = nil, extra_options = {}, &block)
|
14
|
+
firing('render', options.to_s) do
|
15
|
+
render_without_tracing options, extra_options
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
alias_method_chain :perform_action, :tracing
|
20
|
+
alias_method_chain :render, :tracing
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#
|
2
|
+
# Decorate ActiveRecord connection with DB tracing
|
3
|
+
#
|
4
|
+
ActiveRecord::Base.connection.class.class_eval do
|
5
|
+
include XRay::DTrace::Tracer
|
6
|
+
|
7
|
+
def execute_with_tracing(sql, name=nil)
|
8
|
+
firing('db', sql) do
|
9
|
+
execute_without_tracing sql, name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
alias_method_chain :execute, :tracing
|
14
|
+
|
15
|
+
end
|