thread_backtrace 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown ADDED
@@ -0,0 +1,18 @@
1
+ **THIS IS VERY-VERY EXPERIMENTAL AND HACKISH. USE ON YOUR OWN RISK.**
2
+
3
+ Thread Backtrace
4
+ ================
5
+
6
+ It adds Thread#backtrace. Although I abuse private APIs of Ruby, no ruby patches are required. It **does not** work with 1.9.
7
+
8
+ Example
9
+ -------
10
+
11
+ >> require 'thread_backtrace'
12
+ => true
13
+ >> def a; b; end; def b; c; end; def c; sleep 10; end
14
+ => nil
15
+ >> Thread.fork { a }.backtrace
16
+ => ["(irb):2:in `c'", "(irb):2:in `b'", "(irb):2:in `a'", "(irb):3:in `irb_binding'", "(irb):3:in `fork'", "(irb):3:in `irb_binding'", "/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding'", "/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/irb/workspace.rb:52"]
17
+
18
+ Also it adds caller_for_all_threads method to kernel. But it's not the same as output of REE (it doesn't skip the first entry for the current thread).
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require 'rubygems' unless defined?(Gem)
2
+ require 'rake' unless defined?(Rake)
3
+ require 'rake/gempackagetask'
4
+
5
+ spec = Gem::Specification.new do |s|
6
+ s.name = 'thread_backtrace'
7
+ s.version = '0.1'
8
+ s.platform = Gem::Platform::RUBY
9
+ s.has_rdoc = true
10
+ s.extra_rdoc_files = ["README.markdown"]
11
+ s.summary = "Adds Thread\#backtrace"
12
+ s.description = "Adds Thread\#backtrace for inspecting bactrace of a thread. Only works on 1.8"
13
+ s.files = PKG_FILES = %w(README.markdown Rakefile) + Dir.glob("{ext,lib}/**/*.{rb,c}")
14
+ s.extensions << 'ext/thread_backtrace/extconf.rb'
15
+ s.require_paths = ["lib"]
16
+ s.required_ruby_version = "~>1.8"
17
+ end
18
+
19
+ Rake::GemPackageTask.new(spec) do |pkg|
20
+ pkg.gem_spec = spec
21
+ end
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+
3
+ create_makefile('thread_backtrace/thread_backtrace')
@@ -0,0 +1,73 @@
1
+ #include <ruby.h>
2
+ #include <node.h>
3
+ #include <env.h>
4
+
5
+ extern rb_thread_t rb_main_thread, rb_curr_thread;
6
+ extern struct FRAME * ruby_frame;
7
+
8
+ static rb_thread_t find_thread(VALUE thread)
9
+ {
10
+ rb_thread_t t = rb_main_thread;
11
+ do {
12
+ if (t->thread == thread) return t;
13
+
14
+ t = t->next;
15
+ } while (t != rb_main_thread);
16
+
17
+ // todo: raise exception
18
+ return 0;
19
+ }
20
+
21
+ static void * convert_pointer(rb_thread_t thread, void * ptr)
22
+ {
23
+ VALUE *p = (VALUE*)ptr;
24
+
25
+ if(thread == rb_curr_thread) return ptr;
26
+
27
+ if(p >= thread->stk_pos && p <= thread->stk_pos + thread->stk_len)
28
+ return (p - thread->stk_pos) + thread->stk_ptr;
29
+
30
+ return p;
31
+ }
32
+
33
+ static VALUE thread_backtrace(VALUE self)
34
+ {
35
+ rb_thread_t t = find_thread(self);
36
+ struct FRAME *frame, *prev_frame;
37
+ char buf[BUFSIZ];
38
+ VALUE ary;
39
+ NODE *n;
40
+
41
+ if (t == rb_curr_thread) {
42
+ frame = ruby_frame;
43
+ } else {
44
+ frame = convert_pointer(t, t->frame);
45
+ }
46
+
47
+ ary = rb_ary_new();
48
+ if (frame->last_func == ID_ALLOCATOR) {
49
+ frame = convert_pointer(t, frame->prev);
50
+ }
51
+
52
+ for (; frame && (n = frame->node); frame = prev_frame) {
53
+ prev_frame = convert_pointer(t, frame->prev);
54
+
55
+ if (prev_frame && prev_frame->last_func) {
56
+ if (prev_frame->node == n) {
57
+ if (prev_frame->last_func == frame->last_func) continue;
58
+ }
59
+ snprintf(buf, BUFSIZ, "%s:%d:in `%s'", n->nd_file, nd_line(n), rb_id2name(prev_frame->last_func));
60
+ } else {
61
+ snprintf(buf, BUFSIZ, "%s:%d", n->nd_file, nd_line(n));
62
+ }
63
+ rb_ary_push(ary, rb_str_new2(buf));
64
+ }
65
+
66
+ return ary;
67
+ }
68
+
69
+ void Init_thread_backtrace()
70
+ {
71
+ VALUE cThread = rb_define_class("Thread", rb_cObject);
72
+ rb_define_method(cThread, "backtrace", thread_backtrace, 0);
73
+ }
@@ -0,0 +1,11 @@
1
+ require 'thread_backtrace/thread_backtrace'
2
+
3
+ module Kernel
4
+ def caller_for_all_threads
5
+ c, Thread.critical = Thread.critical, true
6
+
7
+ Thread.list.inject({}) { |h, t| h.update t => t.backtrace }
8
+ ensure
9
+ Thread.critical = c
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thread_backtrace
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors: []
7
+
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-16 00:00:00 +03:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Adds Thread#backtrace for inspecting bactrace of a thread. Only works on 1.8
17
+ email:
18
+ executables: []
19
+
20
+ extensions:
21
+ - ext/thread_backtrace/extconf.rb
22
+ extra_rdoc_files:
23
+ - README.markdown
24
+ files:
25
+ - README.markdown
26
+ - Rakefile
27
+ - ext/thread_backtrace/extconf.rb
28
+ - ext/thread_backtrace/thread_backtrace.c
29
+ - lib/thread_backtrace.rb
30
+ has_rdoc: true
31
+ homepage:
32
+ licenses: []
33
+
34
+ post_install_message:
35
+ rdoc_options: []
36
+
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: "1.8"
44
+ version:
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ requirements: []
52
+
53
+ rubyforge_project:
54
+ rubygems_version: 1.3.5
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Adds Thread#backtrace
58
+ test_files: []
59
+