stacktrace 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ # -*- ruby -*-
2
+ require 'autotest/restart'
3
+
4
+ Autotest.add_hook :initialize do |at|
5
+ at.add_mapping(/.*\.c/) do |f, _|
6
+ at.files_matching(/test_.*rb$/)
7
+ end
8
+ at.add_exception(%r{^\./Gemfile.lock})
9
+ at.add_exception("lib/stacktrace/stacktrace.bundle")
10
+ end
11
+
12
+ Autotest.add_hook :run_command do |at|
13
+ system "rake clean compile"
14
+ end
15
+
16
+ # Autotest.add_hook :initialize do |at|
17
+ # at.extra_files << "../some/external/dependency.rb"
18
+ #
19
+ # at.libs << ":../some/external"
20
+ #
21
+ # at.add_exception 'vendor'
22
+ #
23
+ # at.add_mapping(/dependency.rb/) do |f, _|
24
+ # at.files_matching(/test_.*rb$/)
25
+ # end
26
+ #
27
+ # %w(TestA TestB).each do |klass|
28
+ # at.extra_class_map[klass] = "test/test_misc.rb"
29
+ # end
30
+ # end
31
+
32
+ # Autotest.add_hook :run_command do |at|
33
+ # system "rake build"
34
+ # end
File without changes
@@ -0,0 +1,5 @@
1
+ === 1.0.0 / 2012-06-01
2
+
3
+ * Initial version of stacktrace, only compatible with 1.9.3 for now
4
+
5
+
@@ -0,0 +1,10 @@
1
+ .autotest
2
+ History.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ bin/stacktrace
7
+ lib/stacktrace.rb
8
+ lib/stackframe.rb
9
+ test/test_stacktrace.rb
10
+ ext/stacktrace/stacktrace.c
@@ -0,0 +1,58 @@
1
+ = stacktrace
2
+
3
+ * https://github.com/SamSaffron/stacktrace
4
+
5
+ == DESCRIPTION:
6
+
7
+ The idea here is to have a more efficient and useful stacktrace than caller. Caller does not provide you with any class info.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ I think it only works on 1.9.3 for now, need to test
12
+
13
+ == SYNOPSIS:
14
+
15
+ Thread.current.stacktrace
16
+ stacktrace
17
+
18
+ In a simple bench it is about 3 times faster than caller due to reduced information it is reporting.
19
+
20
+ == REQUIREMENTS:
21
+
22
+ * Ruby 1.9.3
23
+
24
+ == DEVELOPERS:
25
+
26
+ Sam Saffron
27
+
28
+ After checking out the source, run:
29
+
30
+ $ rake newb
31
+
32
+ This task will install any missing dependencies, run the tests/specs,
33
+ and generate the RDoc.
34
+
35
+ == LICENSE:
36
+
37
+ (The MIT License)
38
+
39
+ Copyright (c) 2012 Sam Saffron
40
+
41
+ Permission is hereby granted, free of charge, to any person obtaining
42
+ a copy of this software and associated documentation files (the
43
+ 'Software'), to deal in the Software without restriction, including
44
+ without limitation the rights to use, copy, modify, merge, publish,
45
+ distribute, sublicense, and/or sell copies of the Software, and to
46
+ permit persons to whom the Software is furnished to do so, subject to
47
+ the following conditions:
48
+
49
+ The above copyright notice and this permission notice shall be
50
+ included in all copies or substantial portions of the Software.
51
+
52
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
53
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
54
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
55
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
56
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
57
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
58
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,39 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require 'rake/extensiontask'
6
+
7
+ # Hoe.plugin :compiler
8
+ # Hoe.plugin :gem_prelude_sucks
9
+ # Hoe.plugin :inline
10
+ # Hoe.plugin :racc
11
+ # Hoe.plugin :rcov
12
+ # Hoe.plugin :rubyforge
13
+ VERSION = "1.0.0"
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
19
+
20
+ Hoe.spec 'stacktrace' do
21
+ developer('Sam Saffron', 'sam.saffron@gmail.com')
22
+
23
+ self.rubyforge_name = 'stacktrace' # if different than 'stacktrace'
24
+ self.extra_dev_deps << ['rake-compiler', '>=0']
25
+ self.spec_extras = {:extensions => ["ext/stacktrace/extconf.rb"]}
26
+
27
+ Rake::ExtensionTask.new('stacktrace', spec) do |ext|
28
+ ext.lib_dir = File.join('lib', 'stacktrace')
29
+ end
30
+ end
31
+
32
+ Rake::Task[:test].prerequisites << :compile
33
+
34
+
35
+
36
+
37
+
38
+
39
+ # vim: syntax=ruby
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ abort "you need to write me"
@@ -0,0 +1,16 @@
1
+ require 'mkmf'
2
+ require 'ruby_core_source'
3
+
4
+ def add_define(name)
5
+ $defs.push("-D#{name}")
6
+ end
7
+
8
+ hdrs = proc {
9
+ have_header('vm_core.h')
10
+ have_header('iseq.h')
11
+ }
12
+ major, minor, patch = RUBY_VERSION.split('.').map!(&:to_i)
13
+ add_define 'RUBY19'
14
+ add_define 'RUBY193' if patch >= 3
15
+
16
+ Ruby_core_source::create_makefile_with_core(hdrs, 'stacktrace/stacktrace')
@@ -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
@@ -0,0 +1,2 @@
1
+ require 'stackframe'
2
+ require 'stacktrace/stacktrace'
@@ -0,0 +1,94 @@
1
+ require "test/unit"
2
+ require "stacktrace"
3
+ require "benchmark"
4
+
5
+ class Object
6
+ def metaclass; class << self; self; end; end
7
+ end
8
+
9
+ class TestStacktrace < Test::Unit::TestCase
10
+ def test_we_get_an_array
11
+ assert_equal Array, stacktrace.class
12
+ end
13
+
14
+ def test_we_get_items_in_our_array
15
+ assert stacktrace.length > 0
16
+ end
17
+
18
+ def self.test_singleton
19
+ stacktrace
20
+ end
21
+
22
+ def test_singleton_stacktrace
23
+ frame = TestStacktrace.test_singleton[0]
24
+ assert_equal frame.klass, TestStacktrace.metaclass
25
+ assert_equal frame.method, "test_singleton"
26
+ end
27
+
28
+ def test_thread_stacktrace
29
+ frame = Thread.current.stacktrace[0]
30
+ assert_equal frame.klass, TestStacktrace
31
+ assert_equal frame.method, "test_thread_stacktrace"
32
+ end
33
+
34
+ def test_c_func
35
+ frame = nil
36
+ [1].map{frame = stacktrace[1]}
37
+
38
+ assert_equal frame.method, "map"
39
+ assert_equal frame.klass, Array
40
+ end
41
+
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
93
+
94
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stacktrace
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sam Saffron
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rdoc
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.10'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '3.10'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake-compiler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: hoe
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '3.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ description: The idea here is to have a more efficient and useful stacktrace than
63
+ caller. Caller does not provide you with any class info.
64
+ email:
65
+ - sam.saffron@gmail.com
66
+ executables:
67
+ - stacktrace
68
+ extensions:
69
+ - ext/stacktrace/extconf.rb
70
+ extra_rdoc_files:
71
+ - History.txt
72
+ - Manifest.txt
73
+ - README.txt
74
+ files:
75
+ - .autotest
76
+ - History.txt
77
+ - Manifest.txt
78
+ - README.txt
79
+ - Rakefile
80
+ - bin/stacktrace
81
+ - lib/stacktrace.rb
82
+ - lib/stackframe.rb
83
+ - test/test_stacktrace.rb
84
+ - ext/stacktrace/stacktrace.c
85
+ - ext/stacktrace/extconf.rb
86
+ - .gemtest
87
+ homepage: https://github.com/SamSaffron/stacktrace
88
+ licenses: []
89
+ post_install_message:
90
+ rdoc_options:
91
+ - --main
92
+ - README.txt
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ segments:
102
+ - 0
103
+ hash: -3361579644017678061
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubyforge_project: stacktrace
112
+ rubygems_version: 1.8.24
113
+ signing_key:
114
+ specification_version: 3
115
+ summary: The idea here is to have a more efficient and useful stacktrace than caller
116
+ test_files:
117
+ - test/test_stacktrace.rb