rbx-tracer 0.0.1-universal-rubinius

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/ChangeLog ADDED
@@ -0,0 +1,37 @@
1
+ 2010-12-23 rocky <rockyb@rubyforge.org>
2
+
3
+ * breakpoint.rb, frame.rb, set_trace.rb: runtime_context ->
4
+ vm_location
5
+
6
+ 2010-12-22 rocky <rockyb@rubyforge.org>
7
+
8
+ * breakpoint.rb, commands.rb, frame.rb, set_trace.rb: Remove more
9
+ (but not all) of the "command"-ness for step/continue that is not
10
+ needed in set_trace_func. location -> runtime_context.
11
+
12
+ 2010-12-21 rocky <rockyb@rubyforge.org>
13
+
14
+ * commands.rb, set_trace.rb: Handle set_trace_func nil.
15
+
16
+ 2010-12-21 rocky <rockyb@rubyforge.org>
17
+
18
+ * breakpoint.rb, commands.rb, frame.rb, set_trace.rb: Set call and
19
+ return events
20
+
21
+ 2010-12-21 rocky <rockyb@rubyforge.org>
22
+
23
+ * set_trace.rb: Closer to having all of the set_trace_func
24
+ parameters filled in.
25
+
26
+ 2010-12-21 rocky <rockyb@rubyforge.org>
27
+
28
+ * README: See above
29
+
30
+ 2010-12-21 rocky <rockyb@rubyforge.org>
31
+
32
+ * README: What we're about. github suggests this.
33
+
34
+ 2010-12-20 rvm <rocky-rvm@static-71-183-236-17.nycmny.fios.verizon.net>
35
+
36
+ * First Hope (of sometine useful).
37
+
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2010, Rocky Bernstein
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ * Neither the name of the Evan Phoenix nor the names of its contributors
13
+ may be used to endorse or promote products derived from this software
14
+ without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/NEWS ADDED
@@ -0,0 +1,2 @@
1
+ 2010-12-25 0.0.1
2
+ * First public release
data/README ADDED
@@ -0,0 +1 @@
1
+ Simulate Ruby 1.8, 1.9, JRuby's set_trace_func in Rubinius.
data/Rakefile ADDED
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env rake
2
+ # Are we Rubinius? We'll test by checking the specific function we need.
3
+ raise RuntimeError, 'This package is for Rubinius 1.2 or 1.2.1dev only!' unless
4
+ Object.constants.include?('Rubinius') &&
5
+ Rubinius.constants.include?('VM') &&
6
+ %w(1.2 1.2.1dev).member?(Rubinius::VERSION)
7
+
8
+ require 'rubygems'
9
+ require 'rake/gempackagetask'
10
+ require 'rake/rdoctask'
11
+ require 'rake/testtask'
12
+
13
+ ROOT_DIR = File.dirname(__FILE__)
14
+
15
+ def gemspec
16
+ @gemspec ||= eval(File.read('.gemspec'), binding, '.gemspec')
17
+ end
18
+
19
+ desc "Build the gem"
20
+ task :package=>:gem
21
+ task :gem=>:gemspec do
22
+ Dir.chdir(ROOT_DIR) do
23
+ sh "gem build .gemspec"
24
+ FileUtils.mkdir_p 'pkg'
25
+ FileUtils.mv("#{gemspec.name}-#{gemspec.version}-universal-rubinius.gem",
26
+ 'pkg')
27
+ end
28
+ end
29
+
30
+ desc "Install the gem locally"
31
+ task :install => :gem do
32
+ Dir.chdir(ROOT_DIR) do
33
+ sh %{gem install --local pkg/#{gemspec.name}-#{gemspec.version}}
34
+ end
35
+ end
36
+
37
+ require 'rbconfig'
38
+ RUBY_PATH = File.join(RbConfig::CONFIG['bindir'],
39
+ RbConfig::CONFIG['RUBY_INSTALL_NAME'])
40
+
41
+ def run_standalone_ruby_files(list)
42
+ puts '*' * 40
43
+ list.each do |ruby_file|
44
+ system(RUBY_PATH, ruby_file)
45
+ end
46
+ end
47
+
48
+ def run_standalone_ruby_file(directory)
49
+ puts ('*' * 10) + ' ' + directory + ' ' + ('*' * 10)
50
+ Dir.chdir(directory) do
51
+ Dir.glob('*.rb').each do |ruby_file|
52
+ puts(('-' * 20) + ' ' + ruby_file + ' ' + ('-' * 20))
53
+ system(RUBY_PATH, ruby_file)
54
+ end
55
+ end
56
+ end
57
+
58
+ desc 'Create a GNU-style ChangeLog via git2cl'
59
+ task :ChangeLog do
60
+ system('git log --pretty --numstat --summary | git2cl > ChangeLog')
61
+ end
62
+
63
+ desc 'the tests'
64
+ Rake::TestTask.new(:'test') do |t|
65
+ t.test_files = FileList['test/test-*.rb']
66
+ # t.pattern = 'test/**/*test-*.rb' # instead of above
67
+ t.verbose = true
68
+ end
69
+
70
+ desc 'Test everything - unit tests for now.'
71
+ task :default => :test
72
+
73
+ desc "Run each Ruby app file in standalone mode."
74
+ task :'check:app' do
75
+ run_standalone_ruby_file(File.join(%W(#{ROOT_DIR} app)))
76
+ end
77
+
78
+ desc "Generate the gemspec"
79
+ task :generate do
80
+ puts gemspec.to_ruby
81
+ end
82
+
83
+ desc "Validate the gemspec"
84
+ task :gemspec do
85
+ gemspec.validate
86
+ end
87
+
88
+ # --------- RDoc Documentation ------
89
+ desc "Generate rdoc documentation"
90
+ Rake::RDocTask.new("rdoc") do |rdoc|
91
+ rdoc.rdoc_dir = 'doc'
92
+ # rdoc.title = "rbx-trepaning #{Rubinius::SetTrace::VERSION} Documentation"
93
+
94
+ rdoc.rdoc_files.include(%w(lib/set_trace.rb app/*.rb))
95
+ end
96
+
97
+ desc "Same as rdoc"
98
+ task :doc => :rdoc
99
+
100
+ task :clobber_package do
101
+ FileUtils.rm_rf File.join(ROOT_DIR, 'pkg')
102
+ end
103
+
104
+ task :clobber_rdoc do
105
+ FileUtils.rm_rf File.join(ROOT_DIR, 'doc')
106
+ end
107
+
108
+ desc "Remove built files"
109
+ task :clean => [:clobber_package, :clobber_rdoc]
data/THANKS ADDED
@@ -0,0 +1,2 @@
1
+ This code is a distillation of the stepping code in the Rubinius
2
+ reference debugger written by Evan Phoenix and Brian Ford.
data/lib/set_trace.rb ADDED
@@ -0,0 +1,324 @@
1
+ require 'rubygems'; require 'require_relative'
2
+ require_relative '../app/frame'
3
+ require_relative '../app/stepping'
4
+ require_relative '../app/breakpoint'
5
+ require 'compiler/iseq'
6
+
7
+ #
8
+ # A Rubinius implementation of set_trace_func.
9
+ #
10
+ # This code is wired into the debugging APIs provided by Rubinius.
11
+ # It tries to isolate the complicated stepping logic as well as simulate
12
+ # Ruby's set_trace_func.
13
+ #
14
+
15
+ class Rubinius::SetTrace
16
+ VERSION = '0.0.1'
17
+
18
+ DEFAULT_SET_TRACE_FUNC_OPTS = {
19
+ :callback_style => :classic,
20
+ :step_to_parent => :true
21
+ }
22
+
23
+ # Create a new object. Stepping starts up a thread which is where
24
+ # the callback executes from. Other threads are told that their
25
+ # debugging thread is the stepping thread, and this control of execution
26
+ # is handled.
27
+ #
28
+ def initialize()
29
+ @file_lines = Hash.new do |hash, path|
30
+ if File.exists? path
31
+ hash[path] = File.readlines(path)
32
+ else
33
+ ab_path = File.join(@root_dir, path)
34
+ if File.exists? ab_path
35
+ hash[path] = File.readlines(ab_path)
36
+ else
37
+ hash[path] = []
38
+ end
39
+ end
40
+ end
41
+
42
+ @thread = nil
43
+ @frames = []
44
+ @user_variables = 0
45
+ @breakpoints = []
46
+ end
47
+
48
+ attr_reader :variables, :current_frame, :breakpoints, :user_variables
49
+ attr_reader :vm_locations
50
+
51
+ def self.global(opts={})
52
+ @global ||= new
53
+ end
54
+
55
+ def self.set_trace_func(callback_method, opts={})
56
+ opts = Rubinius::SetTrace::DEFAULT_SET_TRACE_FUNC_OPTS.merge(opts)
57
+ opts[:call_offset] ||= 1
58
+ global.set_trace_func(callback_method, opts)
59
+ end
60
+
61
+ # Startup the stepping, skipping back +opts[:call_offset]+
62
+ # frames. This lets you start stepping straight into caller's
63
+ # method.
64
+ #
65
+ def set_trace_func(callback_method, opts={})
66
+ if callback_method
67
+ @opts = opts
68
+ call_offset = @opts[:call_offset] || 0
69
+ @callback_method = callback_method
70
+ @tracing = true
71
+ spinup_thread
72
+
73
+ # Feed info to the stepping call-back thread!
74
+ @vm_locations = Rubinius::VM.backtrace(call_offset + 1, true)
75
+
76
+ method = Rubinius::CompiledMethod.of_sender
77
+
78
+ bp = BreakPoint.new "<start>", method, 0, 0
79
+ channel = Rubinius::Channel.new
80
+
81
+ @local_channel.send Rubinius::Tuple[bp, Thread.current, channel,
82
+ @vm_locations]
83
+
84
+ # wait for the callback to release us
85
+ channel.receive
86
+
87
+ Thread.current.set_debugger_thread @thread
88
+ self
89
+ else
90
+ @tracing = false
91
+ end
92
+ end
93
+
94
+ # Stop and wait for a debuggee thread to send us info about
95
+ # stopping at a breakpoint.
96
+ #
97
+ def listen(step_into=false)
98
+ while true
99
+ if @channel
100
+ if step_into
101
+ @channel << :step
102
+ else
103
+ @channel << true
104
+ end
105
+ end
106
+
107
+ # Wait for someone to stop
108
+ @breakpoint, @debugee_thread, @channel, @vm_locations =
109
+ @local_channel.receive
110
+
111
+ # Uncache all frames since we stopped at a new place
112
+ @frames = []
113
+
114
+ @current_frame = frame(0)
115
+
116
+ if @breakpoint
117
+ @event = @breakpoint.event
118
+ # Only break out if the hit was valid
119
+ break if @breakpoint.hit!(@vm_locations.first)
120
+ else
121
+ @event = 'call'
122
+ break
123
+ end
124
+ end
125
+
126
+ # puts
127
+ # puts "Breakpoint: #{@current_frame.describe}"
128
+ # show_code
129
+
130
+ end
131
+
132
+ # call callback
133
+ def call_callback
134
+ case @opts[:callback_style]
135
+ when :classic
136
+ line = @current_frame.line
137
+ file = @current_frame.file
138
+ meth = @current_frame.method
139
+ binding = @current_frame.binding
140
+ id = nil
141
+ classname = nil
142
+ @callback_method.call(@event, file, line, id, binding, classname)
143
+ else
144
+ @callback_method.call(@event, @vm_locations)
145
+ end
146
+ if @tracing
147
+ if @opts[:step_to_parent]
148
+ @opts[:step_to_parent] = false
149
+ step_to_parent
150
+ else
151
+ step_over_by(1)
152
+ end
153
+ end
154
+ listen
155
+ end
156
+
157
+ def frame(num)
158
+ @frames[num] ||= Frame.new(self, num, @vm_locations[num])
159
+ end
160
+
161
+ def delete_breakpoint(i)
162
+ bp = @breakpoints[i-1]
163
+
164
+ unless bp
165
+ STDERR.puts "Unknown breakpoint '#{i}'"
166
+ return
167
+ end
168
+
169
+ bp.delete!
170
+
171
+ @breakpoints[i-1] = nil
172
+ end
173
+
174
+ def send_between(exec, start, fin)
175
+ ss = Rubinius::InstructionSet.opcodes_map[:send_stack]
176
+ sm = Rubinius::InstructionSet.opcodes_map[:send_method]
177
+ sb = Rubinius::InstructionSet.opcodes_map[:send_stack_with_block]
178
+
179
+ iseq = exec.iseq
180
+
181
+ fin = iseq.size if fin < 0
182
+
183
+ i = start
184
+ while i < fin
185
+ op = iseq[i]
186
+ case op
187
+ when ss, sm, sb
188
+ return exec.literals[iseq[i + 1]]
189
+ else
190
+ op = Rubinius::InstructionSet[op]
191
+ i += (op.arg_count + 1)
192
+ end
193
+ end
194
+
195
+ return nil
196
+ end
197
+
198
+ def show_code(line=@current_frame.line)
199
+ path = @current_frame.method.active_path
200
+
201
+ if str = @file_lines[path][line - 1]
202
+ puts "#{line}: #{str}"
203
+ else
204
+ show_bytecode(line)
205
+ end
206
+ end
207
+
208
+ def decode_one
209
+ ip = @current_frame.ip
210
+
211
+ meth = @current_frame.method
212
+ decoder = Rubinius::InstructionDecoder.new(meth.iseq)
213
+ partial = decoder.decode_between(ip, ip+1)
214
+
215
+ partial.each do |ins|
216
+ op = ins.shift
217
+
218
+ ins.each_index do |i|
219
+ case op.args[i]
220
+ when :literal
221
+ ins[i] = meth.literals[ins[i]].inspect
222
+ when :local
223
+ if meth.local_names
224
+ ins[i] = meth.local_names[ins[i]]
225
+ end
226
+ end
227
+ end
228
+
229
+ display "ip #{ip} = #{op.opcode} #{ins.join(', ')}"
230
+ end
231
+ end
232
+
233
+ def show_bytecode(line=@current_frame.line)
234
+ meth = @current_frame.method
235
+ start = meth.first_ip_on_line(line)
236
+ fin = meth.first_ip_on_line(line+1)
237
+
238
+ if fin == -1
239
+ fin = meth.iseq.size
240
+ end
241
+
242
+ puts "Bytecode between #{start} and #{fin-1} for line #{line}"
243
+
244
+ decoder = Rubinius::InstructionDecoder.new(meth.iseq)
245
+ partial = decoder.decode_between(start, fin)
246
+
247
+ ip = start
248
+
249
+ partial.each do |ins|
250
+ op = ins.shift
251
+
252
+ ins.each_index do |i|
253
+ case op.args[i]
254
+ when :literal
255
+ ins[i] = meth.literals[ins[i]].inspect
256
+ when :local
257
+ if meth.local_names
258
+ ins[i] = meth.local_names[ins[i]]
259
+ end
260
+ end
261
+ end
262
+
263
+ puts " %4d: #{op.opcode} #{ins.join(', ')}" % ip
264
+
265
+ ip += (ins.size + 1)
266
+ end
267
+ end
268
+
269
+ def spinup_thread
270
+ return if @thread
271
+
272
+ @local_channel = Rubinius::Channel.new
273
+
274
+ @thread = Thread.new do
275
+ begin
276
+ listen
277
+ rescue Exception => e
278
+ e.render("Listening")
279
+ break
280
+ end
281
+
282
+ while true
283
+ begin
284
+ call_callback
285
+ rescue Exception => e
286
+ begin
287
+ e.render "Error in debugger"
288
+ rescue Exception => e2
289
+ STDERR.puts "Error rendering backtrace in debugger!"
290
+ end
291
+ end
292
+ end
293
+ end
294
+
295
+ @thread.setup_control!(@local_channel)
296
+ end
297
+
298
+ private :spinup_thread
299
+
300
+ end
301
+
302
+ module Kernel
303
+ def set_trace_func(callback_method, opts={})
304
+ opts = Rubinius::SetTrace::DEFAULT_SET_TRACE_FUNC_OPTS.merge(opts)
305
+ Rubinius::SetTrace.set_trace_func(callback_method, opts)
306
+ end
307
+ end
308
+
309
+ if __FILE__ == $0
310
+ if ARGV[0] == 'classic'
311
+ meth = lambda { |event, file, line, id, binding, classname|
312
+ puts "tracer: #{event} #{file}:#{line}"
313
+ }
314
+ set_trace_func meth
315
+ else
316
+ meth = lambda { |event, vm_locs|
317
+ puts "tracer: #{event} #{vm_locs[0].file}:#{vm_locs[0].line}"
318
+ }
319
+ set_trace_func(meth, {:callback_style => :new})
320
+ end
321
+ x = 1
322
+ y = 2
323
+ set_trace_func nil
324
+ end