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.
Files changed (30) hide show
  1. data/README +65 -0
  2. data/bin/install_dtrace_on_ubuntu +30 -0
  3. data/bin/xray_profile_ruby_function_calls.d +54 -0
  4. data/bin/xray_top_10_busiest_code_path_for_process.d +12 -0
  5. data/bin/xray_top_10_busiest_code_path_on_system.d +35 -0
  6. data/bin/xray_trace_all_custom_ruby_probes.d +25 -0
  7. data/bin/xray_trace_all_ruby_probes.d +20 -0
  8. data/bin/xray_trace_memcached.d +20 -0
  9. data/bin/xray_trace_mysql.d +30 -0
  10. data/bin/xray_trace_rails_response_times.d +114 -0
  11. data/lib/xray.rb +2 -0
  12. data/lib/xray/dtrace/rails/action_controller_tracing_extension.rb +22 -0
  13. data/lib/xray/dtrace/rails/active_record_connection_tracing_extension.rb +15 -0
  14. data/lib/xray/dtrace/rails/enable_tracing.rb +25 -0
  15. data/lib/xray/dtrace/tracer.rb +86 -0
  16. data/lib/xray/dtrace/tracer/joyent.rb +25 -0
  17. data/lib/xray/dtrace/tracer/leopard.rb +28 -0
  18. data/lib/xray/dtrace/usdt/provider_extensions.rb +45 -0
  19. data/lib/xray/thread_dump_signal_handler.rb +31 -0
  20. data/patches_for_mri/caller_for_all_threads_patch_for_MRI_1.8.6.diff +229 -0
  21. data/patches_for_mri/caller_for_all_threads_patch_for_MRI_1.8.7.diff +229 -0
  22. data/patches_for_mri/patch-for-dtrace-instrumentation-of-matz-1.8.6-p114.diff +522 -0
  23. data/patches_for_mri/patch-for-joyent-mri-1.8.6-on-mac-os-x-leopard.diff +20645 -0
  24. data/rails/init.rb +2 -0
  25. data/test/all_tests.rb +1 -0
  26. data/test/functional/dtrace/tracer_test.rb +54 -0
  27. data/test/functional/tracer_script.rb +18 -0
  28. data/test/test_helper.rb +9 -0
  29. data/test/unit/xray/dtrace/tracer_test.rb +54 -0
  30. 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,12 @@
1
+ #!/usr/sbin/dtrace -s
2
+ #pragma D option quiet
3
+ #pragma D option aggsortrev
4
+ #pragma D option dynvarsize=64m
5
+ #pragma D option ustackframes=64
6
+ #pragma D option strsize=2048
7
+
8
+ profile-997
9
+ /pid == $target/
10
+ {
11
+ @num[ustack()] = count();
12
+ }
@@ -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
+
@@ -0,0 +1,2 @@
1
+ require File.dirname(__FILE__) + '/xray/thread_dump_signal_handler'
2
+ require File.dirname(__FILE__) + '/xray/dtrace/tracer'
@@ -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