stacktrace 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.autotest CHANGED
@@ -1,12 +1,12 @@
1
1
  # -*- ruby -*-
2
-
3
2
  require 'autotest/restart'
4
3
 
5
-
6
4
  Autotest.add_hook :initialize do |at|
7
5
  at.add_mapping(/.*\.c/) do |f, _|
8
6
  at.files_matching(/test_.*rb$/)
9
7
  end
8
+ at.add_exception(%r{^\./Gemfile.lock})
9
+ at.add_exception("lib/stacktrace/stacktrace.bundle")
10
10
  end
11
11
 
12
12
  Autotest.add_hook :run_command do |at|
@@ -5,4 +5,6 @@ README.txt
5
5
  Rakefile
6
6
  bin/stacktrace
7
7
  lib/stacktrace.rb
8
+ lib/stackframe.rb
8
9
  test/test_stacktrace.rb
10
+ ext/stacktrace/stacktrace.c
data/README.txt CHANGED
@@ -15,18 +15,22 @@ I think it only works on 1.9.3 for now, need to test
15
15
  Thread.current.stacktrace
16
16
  stacktrace
17
17
 
18
- In a simple bench it is about 3 times faster than caller due to reduced information it is reporting.
18
+ stacktrace accepts 3 params: first is start, second is finish, last is flags
19
19
 
20
- == REQUIREMENTS:
20
+ so:
21
21
 
22
- * Ruby 1.9.3
22
+ stacktrace(0,0) returns only 1 frame (the frame you are in)
23
+ stacktrace(-1,-1) returns the earliest frame
24
+ stacktrace(0,-1,StackFrame::Flags::METHOD | StackFrame::Flags::KLASS) will return only class and method info
23
25
 
24
- == INSTALL:
26
+ == REQUIREMENTS:
25
27
 
26
- - TODO
28
+ * Ruby 1.9.3
27
29
 
28
30
  == DEVELOPERS:
29
31
 
32
+ Sam Saffron
33
+
30
34
  After checking out the source, run:
31
35
 
32
36
  $ rake newb
data/Rakefile CHANGED
@@ -10,6 +10,12 @@ require 'rake/extensiontask'
10
10
  # Hoe.plugin :racc
11
11
  # Hoe.plugin :rcov
12
12
  # Hoe.plugin :rubyforge
13
+ VERSION = "1.0.2"
14
+ spec = Gem::Specification.new do |s|
15
+ s.name = "stacktrace"
16
+ s.platform = Gem::Platform::RUBY
17
+ s.extensions = FileList["ext/**/extconf.rb"]
18
+ end
13
19
 
14
20
  Hoe.spec 'stacktrace' do
15
21
  developer('Sam Saffron', 'sam.saffron@gmail.com')
@@ -26,4 +32,9 @@ end
26
32
 
27
33
  Rake::Task[:test].prerequisites << :compile
28
34
 
35
+
36
+
37
+
38
+
39
+
29
40
  # vim: syntax=ruby
@@ -0,0 +1,153 @@
1
+ // much of this is based off reading MRI source code
2
+
3
+ #include <ruby.h>
4
+ #include <vm_core.h>
5
+ #include <iseq.h>
6
+ #ifdef RUBY193
7
+ #define ruby_current_thread ((rb_thread_t *)RTYPEDDATA_DATA(rb_thread_current()))
8
+ #endif
9
+
10
+ #define ST_F_METHOD 1
11
+ #define ST_F_KLASS 2
12
+ #define ST_F_FILENAME 4
13
+ #define ST_F_LINENUMBER 8
14
+ #define ST_F_ALL 15
15
+
16
+ VALUE c_StackFrame;
17
+
18
+ static VALUE stacktrace(int argc, VALUE* argv, rb_thread_t *th)
19
+ {
20
+ VALUE ary = rb_ary_new();
21
+
22
+ int start = 0;
23
+ int finish = -1;
24
+ int stack_size = 0;
25
+ const rb_control_frame_t *cfp = th->cfp;
26
+ const rb_control_frame_t *tcfp;
27
+ const rb_control_frame_t *limit_cfp = (void *)(th->stack + th->stack_size);
28
+ VALUE file = Qnil;
29
+ int line = 0;
30
+ rb_iseq_t *iseq = 0;
31
+ ID id;
32
+ VALUE frame;
33
+ extern VALUE ruby_engine_name;
34
+ int flags = ST_F_ALL;
35
+
36
+ if (argc > 0) {
37
+ start = NUM2INT(argv[0]);
38
+ }
39
+
40
+ if (argc > 1) {
41
+ finish = NUM2INT(argv[1]);
42
+ }
43
+
44
+ if (argc > 2) {
45
+ flags = NUM2INT(argv[2]);
46
+ }
47
+
48
+ cfp += 1;
49
+ limit_cfp -= 2;
50
+
51
+ if (finish > 0) {
52
+ finish--;
53
+ }
54
+
55
+ if (start < 0 || finish < 0) {
56
+ tcfp = cfp;
57
+ while (tcfp < limit_cfp)
58
+ {
59
+ if (tcfp->iseq != 0 && cfp->pc != 0) {
60
+ stack_size++;
61
+ }
62
+ else if (RUBYVM_CFUNC_FRAME_P(tcfp)) {
63
+ stack_size++;
64
+ }
65
+ tcfp++;
66
+ }
67
+
68
+ if (start < 0) {
69
+ start = stack_size + start;
70
+ }
71
+ if (finish < 0) {
72
+ finish = stack_size + finish;
73
+ }
74
+ }
75
+
76
+ // rb_warn("flags: %i", flags & ST_F_KLASS);
77
+ // rb_warn("test %i %i cfp: %i lcfp %i ss %i", start, finish, cfp, limit_cfp, stack_size);
78
+
79
+ while (cfp < limit_cfp) {
80
+ VALUE hash = 0;
81
+ if (cfp->iseq != 0 && cfp->pc != 0) {
82
+ if (start-- > 0) {cfp++; continue;}
83
+ if (finish-- < 0) break;
84
+ iseq = cfp->iseq;
85
+ frame = rb_class_new_instance(0, 0, c_StackFrame);
86
+ if (iseq->defined_method_id && ((flags & ST_F_KLASS) == ST_F_KLASS)) {
87
+ rb_iv_set(frame, "@klass", iseq->klass);
88
+ }
89
+ if ((flags & ST_F_METHOD) == ST_F_METHOD) {
90
+ rb_iv_set(frame, "@method", iseq->name);
91
+ }
92
+ if ((flags & ST_F_FILENAME) == ST_F_FILENAME) {
93
+ rb_iv_set(frame, "@filename", iseq->filename);
94
+ }
95
+ if ((flags & ST_F_LINENUMBER) == ST_F_LINENUMBER) {
96
+ line = rb_vm_get_sourceline(cfp);
97
+ rb_iv_set(frame, "@line_number", INT2FIX(line));
98
+ }
99
+ rb_ary_push(ary, frame);
100
+ }
101
+ else if (RUBYVM_CFUNC_FRAME_P(cfp)) {
102
+ if (start-- > 0) {cfp++; continue;}
103
+ if (finish-- < 0) break;
104
+ if (NIL_P(file)) file = ruby_engine_name;
105
+ if (cfp->me->def)
106
+ id = cfp->me->def->original_id;
107
+ else
108
+ id = cfp->me->called_id;
109
+ if (id != ID_ALLOCATOR) {
110
+ frame = rb_class_new_instance(0, 0, c_StackFrame);
111
+ if ((flags & ST_F_KLASS) == ST_F_KLASS) {
112
+ rb_iv_set(frame, "@klass", cfp->me->klass);
113
+ }
114
+ if ((flags & ST_F_METHOD) == ST_F_METHOD) {
115
+ rb_iv_set(frame, "@method", rb_id2str(id));
116
+ }
117
+ rb_ary_push(ary,frame);
118
+
119
+ }
120
+ }
121
+ cfp += 1;
122
+ }
123
+ return ary;
124
+ }
125
+
126
+ static VALUE rb_st_kernel_stacktrace(int argc, VALUE* argv)
127
+ {
128
+ return stacktrace(argc, argv, GET_THREAD());
129
+ }
130
+
131
+ static VALUE rb_st_thread_stacktrace(int argc, VALUE* argv, VALUE thval)
132
+ {
133
+ rb_thread_t *th = (rb_thread_t *)RTYPEDDATA_DATA(thval);
134
+
135
+ switch (th->status) {
136
+ case THREAD_RUNNABLE:
137
+ case THREAD_STOPPED:
138
+ case THREAD_STOPPED_FOREVER:
139
+ break;
140
+ case THREAD_TO_KILL:
141
+ case THREAD_KILLED:
142
+ return Qnil;
143
+ }
144
+
145
+ return stacktrace(argc, argv, th);
146
+ }
147
+
148
+ void Init_stacktrace()
149
+ {
150
+ rb_define_global_function("stacktrace", rb_st_kernel_stacktrace, -1);
151
+ rb_define_method(rb_cThread, "stacktrace", rb_st_thread_stacktrace, -1);
152
+ c_StackFrame = rb_eval_string("StackFrame");
153
+ }
@@ -0,0 +1,18 @@
1
+ class StackFrame
2
+ module Flags
3
+ METHOD = 1
4
+ KLASS = 2
5
+ FILENAME = 4
6
+ LINENUMBER = 8
7
+ ALL = 15
8
+ end
9
+
10
+ attr_accessor :line_number, :method, :klass, :filename
11
+
12
+ def ==(other)
13
+ line_number == other.line_number &&
14
+ self.method == other.method &&
15
+ klass == other.klass &&
16
+ filename == other.filename
17
+ end
18
+ end
@@ -1,4 +1,5 @@
1
+ require 'stackframe'
1
2
  require 'stacktrace/stacktrace'
2
3
  class StackTrace
3
- VERSION = '1.0.1'
4
+ VERSION = '1.0.2'
4
5
  end
@@ -21,26 +21,74 @@ class TestStacktrace < Test::Unit::TestCase
21
21
 
22
22
  def test_singleton_stacktrace
23
23
  frame = TestStacktrace.test_singleton[0]
24
- assert_equal frame[:klass], TestStacktrace.metaclass
25
- assert_equal frame[:method], "test_singleton"
24
+ assert_equal frame.klass, TestStacktrace.metaclass
25
+ assert_equal frame.method, "test_singleton"
26
26
  end
27
27
 
28
28
  def test_thread_stacktrace
29
29
  frame = Thread.current.stacktrace[0]
30
- assert_equal frame[:klass], TestStacktrace
31
- assert_equal frame[:method], "test_thread_stacktrace"
30
+ assert_equal frame.klass, TestStacktrace
31
+ assert_equal frame.method, "test_thread_stacktrace"
32
32
  end
33
33
 
34
34
  def test_c_func
35
- p [1].map{stacktrace[0]}
35
+ frame = nil
36
+ [1].map{frame = stacktrace[1]}
37
+
38
+ assert_equal frame.method, "map"
39
+ assert_equal frame.klass, Array
36
40
  end
37
41
 
38
- # stacktrace is MUCH faster
39
- #def test_demo_bench
40
- # Benchmark.bm(7) do |b|
41
- # b.report("caller: ") { 100000.times { caller } }
42
- # b.report("stacktrace: ") { 100000.times { stacktrace } }
43
- # end
44
- #end
42
+ def test_filename
43
+ name = stacktrace[0].filename
44
+ assert_equal name, __FILE__
45
+ end
46
+
47
+ def test_linenumber
48
+ line = stacktrace[0].line_number
49
+ assert_equal __LINE__ - 1, line
50
+ end
51
+
52
+ def test_skipping
53
+ assert_equal stacktrace(1)[0], stacktrace[(1..-1)][0]
54
+ end
55
+
56
+ def test_simple_range
57
+ assert_equal stacktrace[1..2], stacktrace(1,2)
58
+ end
59
+
60
+ def test_invalid_range
61
+ assert_equal stacktrace(1,-10000), []
62
+ end
63
+
64
+ def test_negative_start
65
+ assert_equal stacktrace(-1), [stacktrace[-1]]
66
+ end
67
+
68
+ def test_same_length_as_caller
69
+ # caller skips 1 by default
70
+ caller_length = caller(0).length
71
+ stack_length = stacktrace.length
72
+ assert_equal caller_length, stack_length
73
+ end
74
+
75
+ def test_filter_stack
76
+ frame = stacktrace(0, 0, StackFrame::Flags::METHOD)[0]
77
+
78
+ assert_equal nil, frame.klass
79
+ assert_equal nil, frame.line_number
80
+ assert_equal nil, frame.filename
81
+ end
82
+
83
+
84
+ #stacktrace has similar perf
85
+ def test_demo_bench
86
+ return
87
+ Benchmark.bm(7) do |b|
88
+ b.report("caller: ") { 100000.times { caller } }
89
+ b.report("stacktrace: ") { 100000.times { stacktrace } }
90
+ b.report("stacktrace: ") { 100000.times { stacktrace(0,-1, StackFrame::Flags::METHOD | StackFrame::Flags::KLASS) } }
91
+ end
92
+ end
45
93
 
46
94
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stacktrace
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -95,7 +95,9 @@ files:
95
95
  - Rakefile
96
96
  - bin/stacktrace
97
97
  - lib/stacktrace.rb
98
+ - lib/stackframe.rb
98
99
  - test/test_stacktrace.rb
100
+ - ext/stacktrace/stacktrace.c
99
101
  - ext/stacktrace/extconf.rb
100
102
  - .gemtest
101
103
  homepage: https://github.com/SamSaffron/stacktrace
@@ -112,6 +114,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
112
114
  - - ! '>='
113
115
  - !ruby/object:Gem::Version
114
116
  version: '0'
117
+ segments:
118
+ - 0
119
+ hash: 845160333
115
120
  required_rubygems_version: !ruby/object:Gem::Requirement
116
121
  none: false
117
122
  requirements: