rb-threadframe 0.33 → 0.34

Sign up to get free protection for your applications and to get access to all the features.
data/Makefile CHANGED
@@ -3,7 +3,6 @@
3
3
  .PHONY: all test
4
4
 
5
5
  all: test
6
- rake $@
7
6
 
8
7
  check:
9
8
  rake test
data/NEWS CHANGED
@@ -1,5 +1,17 @@
1
- October 27 2010 (0.33)
2
- - Revise rvm install script to be more roboust
1
+ December 10, 2010 (0.34) Giant Madagascar Day Release
2
+
3
+ - 1.9.2 patches:
4
+ * Save source string eval types
5
+ * Add RubyVM::OS_ARGV and RubyVM::OS_STARTUP_DIR to allow Ruby
6
+ programs to reliably restart themselves via exec().
7
+ * Save instruction sequence compile options
8
+
9
+ - Add routines for finding instruction sequences and offsets:
10
+ iseq#lines() iseq#find_iseq_with_line(), and
11
+ seq#locate_line_with_children()
12
+
13
+ October 27, 2010 (0.33)
14
+ - Revise rvm install script to be more rooust
3
15
  - Change bug report location so we don't spam Ruby's redmine
4
16
  - Add RubyVM::InstructionSequence#parent and #local_iseq fields
5
17
 
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # rb-threadframe
2
2
 
3
- rb-threadframe gives introspection access for frames of a thread.
3
+ rb-threadframe is a set of patches to Ruby 1.9.2 and a C extension
4
+ that gives introspection access for method call frames.
4
5
 
5
6
  ## Requirements
6
7
 
data/ext/extconf.rb CHANGED
@@ -4,9 +4,11 @@ fail "You need to install a threadframe-patched Ruby.
4
4
  See http://github.com/rocky/rb-threadframe/wiki/How-to-Install" unless
5
5
  RbConfig::CONFIG.member?('rb-threadframe')
6
6
 
7
- config_file = File.join(File.dirname(__FILE__), 'config_options')
7
+ # Allow use customization of compile options. For example, the
8
+ # following lines could be put in config_options:
9
+ # CONFIG['optflags'] = '' # Or -O3
10
+ # CONFIG['debugflags'] = '-g3 -ggdb'
11
+ config_file = File.join(File.dirname(__FILE__), 'config_options.rb')
8
12
  load config_file if File.exist?(config_file)
9
13
 
10
- # Temporary: to turn off optimization
11
- # $CFLAGS='-fno-strict-aliasing -g -fPIC'
12
14
  create_makefile("thread_frame")
data/ext/iseq_extra.c CHANGED
@@ -10,8 +10,6 @@ VALUE rb_cIseq = rb_define_class_under(rb_cRubyVM, "InstructionSequence",
10
10
  #endif
11
11
 
12
12
  #include "../include/vm_core_mini.h" /* Pulls in ruby.h and node.h */
13
- #ifdef HAVE_COMPILE_OPTIONS
14
- #endif
15
13
  #include "iseq_mini.h" /* Pulls in ruby.h */
16
14
  #include "../include/ruby19_externs.h"
17
15
  #include <string.h> /* For strlen() */
@@ -22,7 +20,10 @@ struct iseq_insn_info_entry {
22
20
  unsigned short sp;
23
21
  };
24
22
 
25
- #ifdef HAVE_COMPILE_OPTIONS
23
+ #define COMPILE_OPTS_BOOL_SET_HASH(FIELD) \
24
+ rb_hash_aset(hash_opts, rb_str_new2(#FIELD), \
25
+ (compile_opts->FIELD) ? Qtrue : Qfalse)
26
+
26
27
  /*
27
28
  * Document-method: RubyVM::InstructionSequence::compile_options
28
29
  *
@@ -43,13 +44,17 @@ iseq_compile_options(VALUE iseqval)
43
44
  GetISeqPtr(iseqval, iseq);
44
45
  if (!iseq->compile_data) return Qnil;
45
46
  compile_opts = iseq->compile_data->option;
46
- rb_hash_aset(hash_opts, rb_str_new2("inline_const_cache"),
47
- (compile_opts->inline_const_cache) ? Qtrue : Qfalse);
47
+ COMPILE_OPTS_BOOL_SET_HASH(inline_const_cache);
48
+ COMPILE_OPTS_BOOL_SET_HASH(peephole_optimization);
49
+ COMPILE_OPTS_BOOL_SET_HASH(tailcall_optimization);
50
+ COMPILE_OPTS_BOOL_SET_HASH(specialized_instruction);
51
+ COMPILE_OPTS_BOOL_SET_HASH(operands_unification);
52
+ COMPILE_OPTS_BOOL_SET_HASH(stack_caching);
53
+ COMPILE_OPTS_BOOL_SET_HASH(trace_instruction);
54
+ COMPILE_OPTS_BOOL_SET_HASH(debug_level);
48
55
  return hash_opts;
49
56
  }
50
-
51
57
  }
52
- #endif
53
58
 
54
59
  /*
55
60
  * Document-method: RubyVM::InstructionSequence::encoded
@@ -410,9 +415,7 @@ Init_iseq_extra(void)
410
415
  rb_define_method(rb_cISeq, "arg_rest", iseq_arg_rest, 0) ;
411
416
  rb_define_method(rb_cISeq, "arg_simple", iseq_arg_simple, 0) ;
412
417
  rb_define_method(rb_cISeq, "argc", iseq_argc, 0) ;
413
- #ifdef HAVE_COMPILE_OPTIONS
414
418
  rb_define_method(rb_cISeq, "compile_options", iseq_compile_options, 0) ;
415
- #endif
416
419
  rb_define_method(rb_cISeq, "equal?", iseq_equal, 1) ;
417
420
  rb_define_method(rb_cISeq, "encoded", iseq_iseq_encoded, 0) ;
418
421
  rb_define_method(rb_cISeq, "iseq_size", iseq_iseq_size, 0) ;
data/ext/thread_frame.c CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  /* What release we got? */
9
- #define THREADFRAME_VERSION "0.33"
9
+ #define THREADFRAME_VERSION "0.34"
10
10
 
11
11
  #include <string.h>
12
12
  #include "../include/vm_core_mini.h" /* Pulls in ruby.h and node.h */
data/lib/iseq_extra.rb CHANGED
@@ -60,6 +60,86 @@ class RubyVM::InstructionSequence
60
60
  Digest::SHA1.hexdigest(encoded)
61
61
  end
62
62
 
63
+
64
+ ##
65
+ # Locates the instruction address offset of the first instruction on
66
+ # the specified line or nil if no match for the specified line is
67
+ # found.
68
+ #
69
+ # @return [Fixnum, NilClass] returns
70
+ # nil if nothing is found, else the first offset for the line
71
+ def locate_line(line)
72
+ offsetlines.each_pair do |offset, val|
73
+ return offset if val.member?(line)
74
+ end
75
+ nil
76
+ end
77
+
78
+ # iseq and instruction address offset of the first instruction on
79
+ # the specified line. This method recursively examines child
80
+ # compiled methods until an exact match for the searched line is
81
+ # found. It returns both the matching CompiledMethod and the OFFSET
82
+ # of the first instruction on the requested line, or nil if no match
83
+ # for the specified line is found.
84
+ #
85
+ # @return [(RubyVM::InstructionSequence, Fixnum), NilClass] returns
86
+ # nil if nothing is found, else an array of size 2 containing the method
87
+ # the line was found in and the offset pointing there.
88
+ def locate_line_with_children(line)
89
+ iseq = self
90
+ offset = iseq.locate_line(line)
91
+ p ['++++1', offset, iseq]
92
+ return iseq, offset if offset
93
+
94
+ # Didn't find line in this iseq, so check if a contained
95
+ # InstructionSequence encompasses the line searched for
96
+ until offset
97
+ child_iseq = iseq
98
+ iseq = iseq.parent
99
+ unless iseq
100
+ # child_iseq is the top-most scope. Search down from here.
101
+ top_iseq = child_iseq
102
+ top_iseq.child_iseqs.each do |child_iseq|
103
+ p ['++++2', offset, child_iseq, child_iseq.parent]
104
+ next if child_iseq.equal? top_iseq
105
+ if res = child_iseq.locate_line_with_children(line)
106
+ return res
107
+ end
108
+ end
109
+ # No child method is a match - fail
110
+ return nil
111
+ end
112
+ offset = iseq.locate_line(line)
113
+ end
114
+ return parent_iseq, offset
115
+ end
116
+
117
+ def lines
118
+ offsetlines.values.flatten.uniq
119
+ end
120
+
121
+ # Returns an InstructionSequence for the specified line. We search the
122
+ # current method +meth+ and then up the parent scope. If we hit
123
+ # the top and we can't find +line+ that way, then we
124
+ # reverse the search from the top and search down. This will add
125
+ # all siblings of ancestors of +meth+.
126
+ def find_iseq_with_line(line)
127
+
128
+ lines = self.lines
129
+ iseq = self
130
+ until lines.member?(line) do
131
+ child_iseq = iseq
132
+ iseq = iseq.parent
133
+ unless iseq
134
+ # child is the top-most scope. Search down from here.
135
+ pair = child_iseq.locate_line_with_children(line)
136
+ ## pair = iseq.locate_line(line)
137
+ return pair ? pair[0] : nil
138
+ end
139
+ lines = iseq.lines
140
+ end
141
+ return iseq
142
+ end
63
143
  end
64
144
 
65
145
  if __FILE__ == $0
@@ -85,5 +165,21 @@ if __FILE__ == $0
85
165
  end
86
166
  end
87
167
  show_type
168
+ puts '-' * 40
169
+
170
+ line = __LINE__
171
+ def find_line(line) # :nodoc
172
+ tf = RubyVM::ThreadFrame.current
173
+ puts "find_line has lines: #{tf.iseq.lines}"
174
+ p tf.iseq.find_iseq_with_line(line)
175
+ end
176
+
177
+ tf = RubyVM::ThreadFrame.current
178
+ puts tf.iseq.disassemble
179
+ puts("offset %d above should be at line %d" %
180
+ [tf.iseq.locate_line(line), line])
181
+ find_line(line+2)
182
+ find_line(line)
183
+ p tf.iseq.find_iseq_with_line(line+2)
88
184
  end
89
185
 
@@ -0,0 +1,20 @@
1
+ require 'test/unit'
2
+ require_relative '../../ext/thread_frame'
3
+
4
+ class TestISeqSave < Test::Unit::TestCase
5
+
6
+ def test_ISEQS__
7
+ Object.const_set("ISEQS__", {})
8
+ eval "def five; 5 end"
9
+ iseq = Object.const_get('ISEQS__')['five'][0]
10
+ assert_equal RubyVM::InstructionSequence, iseq.class
11
+ assert_equal Hash, iseq.compile_options.class
12
+ old_verbose = $VERBOSE
13
+ $VERBOSE = nil
14
+ Object.const_set("ISEQS__", nil)
15
+ $VERBOSE = old_verbose
16
+ end
17
+ end
18
+
19
+ # We want to double-check we didn't mess up any pointers somewhere.
20
+ at_exit { GC.start }
@@ -0,0 +1,55 @@
1
+ require 'test/unit'
2
+ require_relative '../../ext/thread_frame'
3
+ require_relative '../../lib/thread_frame'
4
+
5
+ $global_test_line = __LINE__
6
+
7
+ class TestLibISeq < Test::Unit::TestCase
8
+
9
+ TEST_LINE = __LINE__
10
+
11
+ def test_sha1
12
+ iseq1 = RubyVM::ThreadFrame::current.iseq
13
+ iseq2 = RubyVM::ThreadFrame::current.iseq
14
+ assert_equal(iseq1.sha1, iseq2.sha1,
15
+ "SHA1 for same threadframe should match")
16
+ end
17
+
18
+ def test_lines
19
+ line = __LINE__
20
+ iseq = RubyVM::ThreadFrame::current.iseq
21
+ assert_equal((line-1..__LINE__+2).to_a, iseq.lines,
22
+ "lines of test_lines() don't match")
23
+ end
24
+
25
+ def test_locate_line
26
+ line = __LINE__
27
+ iseq = RubyVM::ThreadFrame::current.iseq
28
+ assert iseq.locate_line(line)
29
+ assert_nil iseq.locate_line(line - 2)
30
+ end
31
+
32
+ def test_iseq_with_line
33
+ # FIXME: We get a more stringent test if we test of offset.
34
+ # It is lame how little we can do here.
35
+ line = __LINE__
36
+ def find_line(line) # :nodoc
37
+ tf = RubyVM::ThreadFrame.current
38
+ assert(tf.iseq.find_iseq_with_line(line),
39
+ "should have found line #{line}")
40
+ end
41
+ tf = RubyVM::ThreadFrame.current
42
+ find_line(line+2)
43
+ # line2 = nil
44
+ # 1.times do
45
+ # line2 = __LINE__
46
+ # end
47
+ # find_line(line2)
48
+ # find_line(TEST_LINE)
49
+ # find_line($global_test_line)
50
+ end
51
+
52
+ end
53
+
54
+ # We want to double-check we didn't mess up any pointers somewhere.
55
+ at_exit { GC.start }
metadata CHANGED
@@ -4,8 +4,8 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 33
8
- version: "0.33"
7
+ - 34
8
+ version: "0.34"
9
9
  platform: ruby
10
10
  authors:
11
11
  - R. Bernstein
@@ -13,7 +13,7 @@ autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
15
 
16
- date: 2010-10-27 00:00:00 -04:00
16
+ date: 2010-12-10 00:00:00 -05:00
17
17
  default_executable:
18
18
  dependencies: []
19
19
 
@@ -52,8 +52,10 @@ files:
52
52
  - ext/iseq_extra.h
53
53
  - ext/iseq_mini.h
54
54
  - test/unit/test-trace.rb
55
+ - test/unit/test-iseq-save.rb
55
56
  - test/unit/test-prev.rb
56
57
  - test/unit/test-source.rb
58
+ - test/unit/test-lib-iseq.rb
57
59
  - test/unit/test-return-stop.rb
58
60
  - test/unit/test-proc.rb
59
61
  - test/unit/test-invalid.rb
@@ -78,19 +80,19 @@ rdoc_options:
78
80
  - --main
79
81
  - README.md
80
82
  - --title
81
- - ThreadFrame 0.33 Documentation
83
+ - ThreadFrame 0.34 Documentation
82
84
  require_paths:
83
85
  - lib
84
86
  required_ruby_version: !ruby/object:Gem::Requirement
85
87
  none: false
86
88
  requirements:
87
- - - "="
89
+ - - ~>
88
90
  - !ruby/object:Gem::Version
89
91
  segments:
90
92
  - 1
91
93
  - 9
92
- - 2
93
- version: 1.9.2
94
+ - 2frame
95
+ version: 1.9.2frame
94
96
  required_rubygems_version: !ruby/object:Gem::Requirement
95
97
  none: false
96
98
  requirements: