stacktrace 1.0.0

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.
@@ -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