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 +37 -0
- data/LICENSE +25 -0
- data/NEWS +2 -0
- data/README +1 -0
- data/Rakefile +109 -0
- data/THANKS +2 -0
- data/lib/set_trace.rb +324 -0
- data/lib/set_trace.rbc +5346 -0
- data/test/test-settracefunc.rb +29 -0
- metadata +94 -0
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/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
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
|