stacktrace 1.0.1 → 1.0.2

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/.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: