thread_backtrace 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +18 -0
- data/Rakefile +21 -0
- data/ext/thread_backtrace/extconf.rb +3 -0
- data/ext/thread_backtrace/thread_backtrace.c +73 -0
- data/lib/thread_backtrace.rb +11 -0
- metadata +59 -0
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,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
|
+
}
|
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
|
+
|