thread_backtrace 0.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.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
+