rbx-tracer 0.0.1-universal-rubinius

Sign up to get free protection for your applications and to get access to all the features.
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