ZenHacks 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.
- data/History.txt +4 -0
- data/Manifest.txt +29 -0
- data/README.txt +84 -0
- data/bin/macgraph +18 -0
- data/bin/parse_tree_graph +161 -0
- data/bin/test_stats +42 -0
- data/fixloops-demo.sh +3 -0
- data/lib/OrderedHash.rb +37 -0
- data/lib/class-path.rb +20 -0
- data/lib/discover.rb +15 -0
- data/lib/fixloops.rb +79 -0
- data/lib/graph.rb +66 -0
- data/lib/muffdaddy.rb +84 -0
- data/lib/r2c_hacks.rb +36 -0
- data/lib/ruby2ruby.rb +306 -0
- data/lib/timezones.rb +11 -0
- data/lib/zendebug.rb +1037 -0
- data/lib/zenoptimize.rb +149 -0
- data/lib/zenprofile.rb +170 -0
- data/misc/factorial.rb +26 -0
- data/misc/find_c_methods +49 -0
- data/misc/fixloops-bad.rb +62 -0
- data/r2c_hacks-demo.rb +23 -0
- data/test/TestOrderedHash.rb +43 -0
- data/test/r2ctestcase.rb +1076 -0
- data/test/test_parse_tree_graph.rb +47 -0
- data/zendebug-demo.sh +86 -0
- data/zenoptimize-demo.sh +22 -0
- data/zenprofile-demo.sh +29 -0
- metadata +69 -0
data/lib/timezones.rb
ADDED
data/lib/zendebug.rb
ADDED
@@ -0,0 +1,1037 @@
|
|
1
|
+
# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
|
2
|
+
# Copyright (C) 2000 Information-technology Promotion Agency, Japan
|
3
|
+
# Copyright (C) 2000-2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
|
4
|
+
|
5
|
+
require 'inline'
|
6
|
+
|
7
|
+
if $SAFE > 0
|
8
|
+
STDERR.print "-r debug.rb is not available in safe mode\n"
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'tracer'
|
13
|
+
|
14
|
+
class Tracer
|
15
|
+
def Tracer.trace_func(*vars)
|
16
|
+
Single.trace_func(*vars)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
|
21
|
+
|
22
|
+
class ZenDebugger
|
23
|
+
class Mutex
|
24
|
+
def initialize
|
25
|
+
@locker = nil
|
26
|
+
@waiting = []
|
27
|
+
@locked = false;
|
28
|
+
end
|
29
|
+
|
30
|
+
def locked?
|
31
|
+
@locked
|
32
|
+
end
|
33
|
+
|
34
|
+
def lock
|
35
|
+
return if Thread.critical
|
36
|
+
return if @locker == Thread.current
|
37
|
+
while (Thread.critical = true; @locked)
|
38
|
+
@waiting.push Thread.current
|
39
|
+
Thread.stop
|
40
|
+
end
|
41
|
+
@locked = true
|
42
|
+
@locker = Thread.current
|
43
|
+
Thread.critical = false
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def unlock
|
48
|
+
return if Thread.critical
|
49
|
+
return unless @locked
|
50
|
+
unless @locker == Thread.current
|
51
|
+
raise RuntimeError, "unlocked by other"
|
52
|
+
end
|
53
|
+
Thread.critical = true
|
54
|
+
t = @waiting.shift
|
55
|
+
@locked = false
|
56
|
+
@locker = nil
|
57
|
+
Thread.critical = false
|
58
|
+
t.run if t
|
59
|
+
self
|
60
|
+
end
|
61
|
+
end # class Mutex
|
62
|
+
MUTEX = Mutex.new
|
63
|
+
|
64
|
+
class Context
|
65
|
+
DEBUG_LAST_CMD = []
|
66
|
+
|
67
|
+
begin
|
68
|
+
require 'readline'
|
69
|
+
def readline(prompt, hist)
|
70
|
+
Readline::readline(prompt, hist)
|
71
|
+
end
|
72
|
+
rescue LoadError
|
73
|
+
def readline(prompt, hist)
|
74
|
+
STDOUT.print prompt
|
75
|
+
STDOUT.flush
|
76
|
+
line = STDIN.gets
|
77
|
+
exit unless line
|
78
|
+
line.chomp!
|
79
|
+
line
|
80
|
+
end
|
81
|
+
USE_READLINE = false
|
82
|
+
end
|
83
|
+
|
84
|
+
def initialize
|
85
|
+
if Thread.current == Thread.main
|
86
|
+
@stop_next = 1
|
87
|
+
else
|
88
|
+
@stop_next = 0
|
89
|
+
end
|
90
|
+
@last_file = nil
|
91
|
+
@file = nil
|
92
|
+
@line = nil
|
93
|
+
@no_step = nil
|
94
|
+
@frames = []
|
95
|
+
@finish_pos = 0
|
96
|
+
@trace = false
|
97
|
+
@catch = "StandardError"
|
98
|
+
@suspend_next = false
|
99
|
+
end
|
100
|
+
|
101
|
+
def stop_next(n=1)
|
102
|
+
@stop_next = n
|
103
|
+
end
|
104
|
+
|
105
|
+
def set_suspend
|
106
|
+
@suspend_next = true
|
107
|
+
end
|
108
|
+
|
109
|
+
def clear_suspend
|
110
|
+
@suspend_next = false
|
111
|
+
end
|
112
|
+
|
113
|
+
def suspend_all
|
114
|
+
ZenDebugger.suspend
|
115
|
+
end
|
116
|
+
|
117
|
+
def resume_all
|
118
|
+
ZenDebugger.resume
|
119
|
+
end
|
120
|
+
|
121
|
+
def check_suspend
|
122
|
+
return if Thread.critical
|
123
|
+
while (Thread.critical = true; @suspend_next)
|
124
|
+
ZenDebugger.waiting.push Thread.current
|
125
|
+
@suspend_next = false
|
126
|
+
Thread.stop
|
127
|
+
end
|
128
|
+
Thread.critical = false
|
129
|
+
end
|
130
|
+
|
131
|
+
def trace?
|
132
|
+
@trace
|
133
|
+
end
|
134
|
+
|
135
|
+
def set_trace(arg)
|
136
|
+
@trace = arg
|
137
|
+
end
|
138
|
+
|
139
|
+
def stdout
|
140
|
+
ZenDebugger.stdout
|
141
|
+
end
|
142
|
+
|
143
|
+
def break_points
|
144
|
+
ZenDebugger.break_points
|
145
|
+
end
|
146
|
+
|
147
|
+
def display
|
148
|
+
ZenDebugger.display
|
149
|
+
end
|
150
|
+
|
151
|
+
def context(th)
|
152
|
+
ZenDebugger.context(th)
|
153
|
+
end
|
154
|
+
|
155
|
+
def set_trace_all(arg)
|
156
|
+
ZenDebugger.set_trace(arg)
|
157
|
+
end
|
158
|
+
|
159
|
+
def set_last_thread(th)
|
160
|
+
ZenDebugger.set_last_thread(th)
|
161
|
+
end
|
162
|
+
|
163
|
+
def debug_eval(str, binding)
|
164
|
+
begin
|
165
|
+
val = eval(str, binding)
|
166
|
+
rescue StandardError, ScriptError => e
|
167
|
+
at = eval("caller(1)", binding)
|
168
|
+
stdout.printf "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
|
169
|
+
for i in at
|
170
|
+
stdout.printf "\tfrom %s\n", i
|
171
|
+
end
|
172
|
+
throw :debug_error
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def debug_silent_eval(str, binding)
|
177
|
+
begin
|
178
|
+
eval(str, binding)
|
179
|
+
rescue StandardError, ScriptError
|
180
|
+
nil
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def var_list(ary, binding)
|
185
|
+
ary.sort!
|
186
|
+
for v in ary
|
187
|
+
stdout.printf " %s => %s\n", v, eval(v, binding).inspect
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def debug_variable_info(input, binding)
|
192
|
+
case input
|
193
|
+
when /^\s*g(?:lobal)?\s*$/
|
194
|
+
var_list(global_variables, binding)
|
195
|
+
|
196
|
+
when /^\s*l(?:ocal)?\s*$/
|
197
|
+
var_list(eval("local_variables", binding), binding)
|
198
|
+
|
199
|
+
when /^\s*i(?:nstance)?\s+/
|
200
|
+
obj = debug_eval($', binding)
|
201
|
+
var_list(obj.instance_variables, obj.instance_eval{binding()})
|
202
|
+
|
203
|
+
when /^\s*c(?:onst(?:ant)?)?\s+/
|
204
|
+
obj = debug_eval($', binding)
|
205
|
+
unless obj.kind_of? Module
|
206
|
+
stdout.print "Should be Class/Module: ", $', "\n"
|
207
|
+
else
|
208
|
+
var_list(obj.constants, obj.module_eval{binding()})
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def debug_method_info(input, binding)
|
214
|
+
case input
|
215
|
+
when /^i(:?nstance)?\s+/
|
216
|
+
obj = debug_eval($', binding)
|
217
|
+
|
218
|
+
len = 0
|
219
|
+
for v in obj.methods.sort
|
220
|
+
len += v.size + 1
|
221
|
+
if len > 70
|
222
|
+
len = v.size + 1
|
223
|
+
stdout.print "\n"
|
224
|
+
end
|
225
|
+
stdout.print v, " "
|
226
|
+
end
|
227
|
+
stdout.print "\n"
|
228
|
+
|
229
|
+
else
|
230
|
+
obj = debug_eval(input, binding)
|
231
|
+
unless obj.kind_of? Module
|
232
|
+
stdout.print "Should be Class/Module: ", input, "\n"
|
233
|
+
else
|
234
|
+
len = 0
|
235
|
+
for v in obj.instance_methods(false).sort
|
236
|
+
len += v.size + 1
|
237
|
+
if len > 70
|
238
|
+
len = v.size + 1
|
239
|
+
stdout.print "\n"
|
240
|
+
end
|
241
|
+
stdout.print v, " "
|
242
|
+
end
|
243
|
+
stdout.print "\n"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def thnum
|
249
|
+
num = ZenDebugger.instance_eval{@thread_list[Thread.current]}
|
250
|
+
unless num
|
251
|
+
ZenDebugger.make_thread_list
|
252
|
+
num = ZenDebugger.instance_eval{@thread_list[Thread.current]}
|
253
|
+
end
|
254
|
+
num
|
255
|
+
end
|
256
|
+
|
257
|
+
def debug_command(file, line, id, binding)
|
258
|
+
MUTEX.lock
|
259
|
+
set_last_thread(Thread.current)
|
260
|
+
frame_pos = 0
|
261
|
+
binding_file = file
|
262
|
+
binding_line = line
|
263
|
+
previous_line = nil
|
264
|
+
if ENV['EMACS']
|
265
|
+
stdout.printf "\032\032%s:%d:\n", binding_file, binding_line
|
266
|
+
else
|
267
|
+
stdout.printf "%s:%d:%s", binding_file, binding_line,
|
268
|
+
line_at(binding_file, binding_line)
|
269
|
+
end
|
270
|
+
@frames[0] = [binding, file, line, id]
|
271
|
+
display_expressions(binding)
|
272
|
+
prompt = true
|
273
|
+
while prompt and input = readline("(rdb:%d) "%thnum(), true)
|
274
|
+
catch(:debug_error) do
|
275
|
+
if input == ""
|
276
|
+
next unless DEBUG_LAST_CMD[0]
|
277
|
+
input = DEBUG_LAST_CMD[0]
|
278
|
+
stdout.print input, "\n"
|
279
|
+
else
|
280
|
+
DEBUG_LAST_CMD[0] = input
|
281
|
+
end
|
282
|
+
|
283
|
+
case input
|
284
|
+
when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/
|
285
|
+
if defined? $2 then
|
286
|
+
set_trace_all $1 == 'on'
|
287
|
+
elsif defined? $1 then
|
288
|
+
set_trace $1 == 'on'
|
289
|
+
end
|
290
|
+
|
291
|
+
if trace?
|
292
|
+
stdout.print "Trace on.\n"
|
293
|
+
else
|
294
|
+
stdout.print "Trace off.\n"
|
295
|
+
end
|
296
|
+
|
297
|
+
when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:]+)$/
|
298
|
+
pos = $2
|
299
|
+
if $1
|
300
|
+
klass = debug_silent_eval($1, binding)
|
301
|
+
file = $1
|
302
|
+
end
|
303
|
+
if pos =~ /^\d+$/
|
304
|
+
pname = pos
|
305
|
+
pos = pos.to_i
|
306
|
+
else
|
307
|
+
pname = pos = pos.intern.id2name
|
308
|
+
end
|
309
|
+
break_points.push [true, 0, klass || file, pos]
|
310
|
+
stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, klass || file, pname
|
311
|
+
|
312
|
+
when /^\s*b(?:reak)?\s+(.+)[#.]([^.:]+)$/
|
313
|
+
pos = $2.intern.id2name
|
314
|
+
klass = debug_eval($1, binding)
|
315
|
+
break_points.push [true, 0, klass, pos]
|
316
|
+
stdout.printf "Set breakpoint %d at %s.%s\n", break_points.size, klass, pos
|
317
|
+
|
318
|
+
when /^\s*wat(?:ch)?\s+(.+)$/
|
319
|
+
exp = $1
|
320
|
+
break_points.push [true, 1, exp]
|
321
|
+
stdout.printf "Set watchpoint %d\n", break_points.size, exp
|
322
|
+
|
323
|
+
when /^\s*b(?:reak)?$/
|
324
|
+
if break_points.find{|b| b[1] == 0}
|
325
|
+
n = 1
|
326
|
+
stdout.print "Breakpoints:\n"
|
327
|
+
for b in break_points
|
328
|
+
if b[0] and b[1] == 0
|
329
|
+
stdout.printf " %d %s:%s\n", n, b[2], b[3]
|
330
|
+
end
|
331
|
+
n += 1
|
332
|
+
end
|
333
|
+
end
|
334
|
+
if break_points.find{|b| b[1] == 1}
|
335
|
+
n = 1
|
336
|
+
stdout.print "\n"
|
337
|
+
stdout.print "Watchpoints:\n"
|
338
|
+
for b in break_points
|
339
|
+
if b[0] and b[1] == 1
|
340
|
+
stdout.printf " %d %s\n", n, b[2]
|
341
|
+
end
|
342
|
+
n += 1
|
343
|
+
end
|
344
|
+
end
|
345
|
+
if break_points.size == 0
|
346
|
+
stdout.print "No breakpoints\n"
|
347
|
+
else
|
348
|
+
stdout.print "\n"
|
349
|
+
end
|
350
|
+
|
351
|
+
when /^\s*del(?:ete)?(?:\s+(\d+))?$/
|
352
|
+
pos = $1
|
353
|
+
unless pos
|
354
|
+
input = readline("Clear all breakpoints? (y/n) ", false)
|
355
|
+
if input == "y"
|
356
|
+
for b in break_points
|
357
|
+
b[0] = false
|
358
|
+
end
|
359
|
+
end
|
360
|
+
else
|
361
|
+
pos = pos.to_i
|
362
|
+
if break_points[pos-1]
|
363
|
+
break_points[pos-1][0] = false
|
364
|
+
else
|
365
|
+
stdout.printf "Breakpoint %d is not defined\n", pos
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
when /^\s*disp(?:lay)?\s+(.+)$/
|
370
|
+
exp = $1
|
371
|
+
display.push [true, exp]
|
372
|
+
stdout.printf "%d: ", display.size
|
373
|
+
display_expression(exp, binding)
|
374
|
+
|
375
|
+
when /^\s*disp(?:lay)?$/
|
376
|
+
display_expressions(binding)
|
377
|
+
|
378
|
+
when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/
|
379
|
+
pos = $1
|
380
|
+
unless pos
|
381
|
+
input = readline("Clear all expressions? (y/n) ", false)
|
382
|
+
if input == "y"
|
383
|
+
for d in display
|
384
|
+
d[0] = false
|
385
|
+
end
|
386
|
+
end
|
387
|
+
else
|
388
|
+
pos = pos.to_i
|
389
|
+
if display[pos-1]
|
390
|
+
display[pos-1][0] = false
|
391
|
+
else
|
392
|
+
stdout.printf "Display expression %d is not defined\n", pos
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
when /^\s*c(?:ont)?$/
|
397
|
+
prompt = false
|
398
|
+
|
399
|
+
when /^\s*s(?:tep)?(?:\s+(\d+))?$/
|
400
|
+
if $1
|
401
|
+
lev = $1.to_i
|
402
|
+
else
|
403
|
+
lev = 1
|
404
|
+
end
|
405
|
+
@stop_next = lev
|
406
|
+
prompt = false
|
407
|
+
|
408
|
+
when /^\s*n(?:ext)?(?:\s+(\d+))?$/
|
409
|
+
if $1
|
410
|
+
lev = $1.to_i
|
411
|
+
else
|
412
|
+
lev = 1
|
413
|
+
end
|
414
|
+
@stop_next = lev
|
415
|
+
@no_step = @frames.size - frame_pos
|
416
|
+
prompt = false
|
417
|
+
|
418
|
+
when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/
|
419
|
+
display_frames(frame_pos)
|
420
|
+
|
421
|
+
when /^\s*l(?:ist)?(?:\s+(.+))?$/
|
422
|
+
if not $1
|
423
|
+
b = previous_line ? previous_line + 10 : binding_line - 5
|
424
|
+
e = b + 9
|
425
|
+
elsif $1 == '-'
|
426
|
+
b = previous_line ? previous_line - 10 : binding_line - 5
|
427
|
+
e = b + 9
|
428
|
+
else
|
429
|
+
b, e = $1.split(/[-,]/)
|
430
|
+
if e
|
431
|
+
b = b.to_i
|
432
|
+
e = e.to_i
|
433
|
+
else
|
434
|
+
b = b.to_i - 5
|
435
|
+
e = b + 9
|
436
|
+
end
|
437
|
+
end
|
438
|
+
previous_line = b
|
439
|
+
display_list(b, e, binding_file, binding_line)
|
440
|
+
|
441
|
+
when /^\s*up(?:\s+(\d+))?$/
|
442
|
+
previous_line = nil
|
443
|
+
if $1
|
444
|
+
lev = $1.to_i
|
445
|
+
else
|
446
|
+
lev = 1
|
447
|
+
end
|
448
|
+
frame_pos += lev
|
449
|
+
if frame_pos >= @frames.size
|
450
|
+
frame_pos = @frames.size - 1
|
451
|
+
stdout.print "At toplevel\n"
|
452
|
+
end
|
453
|
+
binding, binding_file, binding_line = @frames[frame_pos]
|
454
|
+
stdout.print format_frame(frame_pos)
|
455
|
+
|
456
|
+
when /^\s*down(?:\s+(\d+))?$/
|
457
|
+
previous_line = nil
|
458
|
+
if $1
|
459
|
+
lev = $1.to_i
|
460
|
+
else
|
461
|
+
lev = 1
|
462
|
+
end
|
463
|
+
frame_pos -= lev
|
464
|
+
if frame_pos < 0
|
465
|
+
frame_pos = 0
|
466
|
+
stdout.print "At stack bottom\n"
|
467
|
+
end
|
468
|
+
binding, binding_file, binding_line = @frames[frame_pos]
|
469
|
+
stdout.print format_frame(frame_pos)
|
470
|
+
|
471
|
+
when /^\s*fin(?:ish)?$/
|
472
|
+
if frame_pos == @frames.size
|
473
|
+
stdout.print "\"finish\" not meaningful in the outermost frame.\n"
|
474
|
+
else
|
475
|
+
@finish_pos = @frames.size - frame_pos
|
476
|
+
frame_pos = 0
|
477
|
+
prompt = false
|
478
|
+
end
|
479
|
+
|
480
|
+
when /^\s*cat(?:ch)?(?:\s+(.+))?$/
|
481
|
+
if $1
|
482
|
+
excn = $1
|
483
|
+
if excn == 'off'
|
484
|
+
@catch = nil
|
485
|
+
stdout.print "Clear catchpoint.\n"
|
486
|
+
else
|
487
|
+
@catch = excn
|
488
|
+
stdout.printf "Set catchpoint %s.\n", @catch
|
489
|
+
end
|
490
|
+
else
|
491
|
+
if @catch
|
492
|
+
stdout.printf "Catchpoint %s.\n", @catch
|
493
|
+
else
|
494
|
+
stdout.print "No catchpoint.\n"
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
when /^\s*q(?:uit)?$/
|
499
|
+
input = readline("Really quit? (y/n) ", false)
|
500
|
+
if input == "y"
|
501
|
+
exit! # exit -> exit!: No graceful way to stop threads...
|
502
|
+
end
|
503
|
+
|
504
|
+
when /^\s*v(?:ar)?\s+/
|
505
|
+
debug_variable_info($', binding)
|
506
|
+
|
507
|
+
when /^\s*m(?:ethod)?\s+/
|
508
|
+
debug_method_info($', binding)
|
509
|
+
|
510
|
+
when /^\s*th(?:read)?\s+/
|
511
|
+
if ZenDebugger.debug_thread_info($', binding) == :cont
|
512
|
+
prompt = false
|
513
|
+
end
|
514
|
+
|
515
|
+
when /^\s*p\s+/
|
516
|
+
stdout.printf "%s\n", debug_eval($', binding).inspect
|
517
|
+
|
518
|
+
when /^\s*h(?:elp)?$/
|
519
|
+
debug_print_help()
|
520
|
+
|
521
|
+
else
|
522
|
+
v = debug_eval(input, binding)
|
523
|
+
stdout.printf "%s\n", v.inspect
|
524
|
+
end
|
525
|
+
end
|
526
|
+
end
|
527
|
+
MUTEX.unlock
|
528
|
+
resume_all
|
529
|
+
end # def debug_command
|
530
|
+
|
531
|
+
def debug_print_help
|
532
|
+
stdout.print '
|
533
|
+
Debugger help v.-0.002b
|
534
|
+
Commands
|
535
|
+
b[reak] [file:|class:]<line|method>
|
536
|
+
b[reak] [class.]<line|method>
|
537
|
+
set breakpoint to some position
|
538
|
+
wat[ch] <expression> set watchpoint to some expression
|
539
|
+
cat[ch] <an Exception> set catchpoint to an exception
|
540
|
+
b[reak] list breakpoints
|
541
|
+
cat[ch] show catchpoint
|
542
|
+
del[ete][ nnn] delete some or all breakpoints
|
543
|
+
disp[lay] <expression> add expression into display expression list
|
544
|
+
undisp[lay][ nnn] delete one particular or all display expressions
|
545
|
+
c[ont] run until program ends or hit breakpoint
|
546
|
+
s[tep][ nnn] step (into methods) one line or till line nnn
|
547
|
+
n[ext][ nnn] go over one line or till line nnn
|
548
|
+
w[here] display frames
|
549
|
+
f[rame] alias for where
|
550
|
+
l[ist][ (-|nn-mm)] list program, - lists backwards
|
551
|
+
nn-mm lists given lines
|
552
|
+
up[ nn] move to higher frame
|
553
|
+
down[ nn] move to lower frame
|
554
|
+
fin[ish] return to outer frame
|
555
|
+
tr[ace] (on|off) set trace mode of current thread
|
556
|
+
tr[ace] (on|off) all set trace mode of all threads
|
557
|
+
q[uit] exit from debugger
|
558
|
+
v[ar] g[lobal] show global variables
|
559
|
+
v[ar] l[ocal] show local variables
|
560
|
+
v[ar] i[nstance] <object> show instance variables of object
|
561
|
+
v[ar] c[onst] <object> show constants of object
|
562
|
+
m[ethod] i[nstance] <obj> show methods of object
|
563
|
+
m[ethod] <class|module> show instance methods of class or module
|
564
|
+
th[read] l[ist] list all threads
|
565
|
+
th[read] c[ur[rent]] show current thread
|
566
|
+
th[read] [sw[itch]] <nnn> switch thread context to nnn
|
567
|
+
th[read] stop <nnn> stop thread nnn
|
568
|
+
th[read] resume <nnn> resume thread nnn
|
569
|
+
p expression evaluate expression and print its value
|
570
|
+
h[elp] print this help
|
571
|
+
<everything else> evaluate
|
572
|
+
'
|
573
|
+
end
|
574
|
+
|
575
|
+
def display_expressions(binding)
|
576
|
+
n = 1
|
577
|
+
for d in display
|
578
|
+
if d[0]
|
579
|
+
stdout.printf "%d: ", n
|
580
|
+
display_expression(d[1], binding)
|
581
|
+
end
|
582
|
+
n += 1
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
def display_expression(exp, binding)
|
587
|
+
stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s
|
588
|
+
end
|
589
|
+
|
590
|
+
def display_frames(pos)
|
591
|
+
0.upto(@frames.size - 1) do |n|
|
592
|
+
if n == pos
|
593
|
+
stdout.print "--> "
|
594
|
+
else
|
595
|
+
stdout.print " "
|
596
|
+
end
|
597
|
+
stdout.print format_frame(n)
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
def format_frame(pos)
|
602
|
+
bind, file, line, id = @frames[pos]
|
603
|
+
sprintf "#%d %s:%s%s\n", pos + 1, file, line,
|
604
|
+
(id ? ":in `#{id.id2name}'" : "")
|
605
|
+
end
|
606
|
+
|
607
|
+
def display_list(b, e, file, line)
|
608
|
+
stdout.printf "[%d, %d] in %s\n", b, e, file
|
609
|
+
if lines = SCRIPT_LINES__[file] and lines != true
|
610
|
+
n = 0
|
611
|
+
b.upto(e) do |n|
|
612
|
+
if n > 0 && lines[n-1]
|
613
|
+
if n == line
|
614
|
+
stdout.printf "=> %d %s\n", n, lines[n-1].chomp
|
615
|
+
else
|
616
|
+
stdout.printf " %d %s\n", n, lines[n-1].chomp
|
617
|
+
end
|
618
|
+
end
|
619
|
+
end
|
620
|
+
else
|
621
|
+
stdout.printf "No sourcefile available for %s\n", file
|
622
|
+
end
|
623
|
+
end
|
624
|
+
|
625
|
+
def line_at(file, line)
|
626
|
+
lines = SCRIPT_LINES__[file]
|
627
|
+
if lines
|
628
|
+
return "\n" if lines == true
|
629
|
+
line = lines[line-1]
|
630
|
+
return "\n" unless line
|
631
|
+
return line
|
632
|
+
end
|
633
|
+
return "\n"
|
634
|
+
end
|
635
|
+
|
636
|
+
def debug_funcname(id)
|
637
|
+
if id.nil?
|
638
|
+
"toplevel"
|
639
|
+
else
|
640
|
+
id.id2name
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
def check_break_points(file, klass, pos, binding, id)
|
645
|
+
return false if break_points.empty?
|
646
|
+
n = 1
|
647
|
+
for b in break_points
|
648
|
+
if b[0] # valid
|
649
|
+
if b[1] == 0 # breakpoint
|
650
|
+
if (b[2] == file and b[3] == pos) or
|
651
|
+
(klass and b[2] == klass and b[3] == pos)
|
652
|
+
stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
|
653
|
+
return true
|
654
|
+
end
|
655
|
+
elsif b[1] == 1 # watchpoint
|
656
|
+
if debug_silent_eval(b[2], binding)
|
657
|
+
stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
|
658
|
+
return true
|
659
|
+
end
|
660
|
+
end
|
661
|
+
end
|
662
|
+
n += 1
|
663
|
+
end
|
664
|
+
return false
|
665
|
+
end
|
666
|
+
|
667
|
+
def excn_handle(file, line, id, binding)
|
668
|
+
if $!.class <= SystemExit
|
669
|
+
ZenDebugger.stop_debugging
|
670
|
+
exit
|
671
|
+
end
|
672
|
+
|
673
|
+
if @catch and ($!.class.ancestors.find { |e| e.to_s == @catch })
|
674
|
+
stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.class
|
675
|
+
fs = @frames.size
|
676
|
+
tb = caller(0)[-fs..-1]
|
677
|
+
if tb
|
678
|
+
for i in tb
|
679
|
+
stdout.printf "\tfrom %s\n", i
|
680
|
+
end
|
681
|
+
end
|
682
|
+
suspend_all
|
683
|
+
debug_command(file, line, id, binding)
|
684
|
+
end
|
685
|
+
end
|
686
|
+
|
687
|
+
############################################################
|
688
|
+
# Beginning of broken up trace methods:
|
689
|
+
|
690
|
+
def trace_func_line(file, line, id, binding, klass)
|
691
|
+
frame_set_pos(file, line)
|
692
|
+
frame_size = @frames.size
|
693
|
+
if !@no_step or frame_size == @no_step
|
694
|
+
@stop_next -= 1
|
695
|
+
@stop_next = -1 if @stop_next < 0
|
696
|
+
elsif frame_size < @no_step
|
697
|
+
@stop_next = 0 # break here before leaving...
|
698
|
+
end
|
699
|
+
if @stop_next == 0 or check_break_points(file, nil, line, binding, id)
|
700
|
+
@no_step = nil
|
701
|
+
suspend_all
|
702
|
+
debug_command(file, line, id, binding)
|
703
|
+
end
|
704
|
+
end
|
705
|
+
|
706
|
+
def trace_func_call(file, line, id, binding, klass)
|
707
|
+
@frames.unshift [binding, file, line, id]
|
708
|
+
if check_break_points(file, klass, id.id2name, binding, id)
|
709
|
+
suspend_all
|
710
|
+
debug_command(file, line, id, binding)
|
711
|
+
end
|
712
|
+
end
|
713
|
+
|
714
|
+
def trace_func_c_call(file, line, id, binding, klass)
|
715
|
+
frame_set_pos(file, line)
|
716
|
+
end
|
717
|
+
|
718
|
+
def trace_func_class(file, line, id, binding, klass)
|
719
|
+
@frames.unshift [binding, file, line, id]
|
720
|
+
end
|
721
|
+
|
722
|
+
def trace_func_return(file, line, id, binding, klass)
|
723
|
+
if @frames.size == @finish_pos
|
724
|
+
@stop_next = 1
|
725
|
+
@finish_pos = 0
|
726
|
+
end
|
727
|
+
@frames.shift
|
728
|
+
end
|
729
|
+
|
730
|
+
def trace_func_end(file, line, id, binding, klass)
|
731
|
+
@frames.shift
|
732
|
+
end
|
733
|
+
|
734
|
+
def trace_func_raise(file, line, id, binding, klass)
|
735
|
+
excn_handle(file, line, id, binding)
|
736
|
+
end
|
737
|
+
|
738
|
+
inline(:C) do |builder|
|
739
|
+
builder.add_type_converter('VALUE', '', '')
|
740
|
+
|
741
|
+
builder.c <<-'EOF'
|
742
|
+
void frame_set_pos(VALUE file, VALUE line) {
|
743
|
+
VALUE f = rb_ary_entry(rb_iv_get(self, "@frames"), 0);
|
744
|
+
if (RTEST(f)) {
|
745
|
+
rb_ary_store(f, 1, file);
|
746
|
+
rb_ary_store(f, 2, line);
|
747
|
+
}
|
748
|
+
}
|
749
|
+
EOF
|
750
|
+
end # inline
|
751
|
+
end # class Context
|
752
|
+
|
753
|
+
trap("INT") { ZenDebugger.interrupt }
|
754
|
+
@last_thread = Thread::main
|
755
|
+
@max_thread = 1
|
756
|
+
@thread_list = {Thread::main => 1}
|
757
|
+
@break_points = []
|
758
|
+
@display = []
|
759
|
+
@waiting = []
|
760
|
+
@stdout = STDOUT
|
761
|
+
|
762
|
+
class << ZenDebugger
|
763
|
+
def stdout
|
764
|
+
@stdout
|
765
|
+
end
|
766
|
+
|
767
|
+
def stdout=(s)
|
768
|
+
@stdout = s
|
769
|
+
end
|
770
|
+
|
771
|
+
def display
|
772
|
+
@display
|
773
|
+
end
|
774
|
+
|
775
|
+
def break_points
|
776
|
+
@break_points
|
777
|
+
end
|
778
|
+
|
779
|
+
def waiting
|
780
|
+
@waiting
|
781
|
+
end
|
782
|
+
|
783
|
+
def set_trace( arg )
|
784
|
+
saved_crit = Thread.critical
|
785
|
+
Thread.critical = true
|
786
|
+
make_thread_list
|
787
|
+
for th, in @thread_list
|
788
|
+
context(th).set_trace arg
|
789
|
+
end
|
790
|
+
Thread.critical = saved_crit
|
791
|
+
arg
|
792
|
+
end
|
793
|
+
|
794
|
+
def set_last_thread(th)
|
795
|
+
@last_thread = th
|
796
|
+
end
|
797
|
+
|
798
|
+
def suspend
|
799
|
+
saved_crit = Thread.critical
|
800
|
+
Thread.critical = true
|
801
|
+
make_thread_list
|
802
|
+
for th, in @thread_list
|
803
|
+
next if th == Thread.current
|
804
|
+
context(th).set_suspend
|
805
|
+
end
|
806
|
+
Thread.critical = saved_crit
|
807
|
+
# Schedule other threads to suspend as soon as possible.
|
808
|
+
Thread.pass unless Thread.critical
|
809
|
+
end
|
810
|
+
|
811
|
+
def resume
|
812
|
+
saved_crit = Thread.critical
|
813
|
+
Thread.critical = true
|
814
|
+
make_thread_list
|
815
|
+
for th, in @thread_list
|
816
|
+
next if th == Thread.current
|
817
|
+
context(th).clear_suspend
|
818
|
+
end
|
819
|
+
waiting.each do |th|
|
820
|
+
th.run
|
821
|
+
end
|
822
|
+
waiting.clear
|
823
|
+
Thread.critical = saved_crit
|
824
|
+
# Schedule other threads to restart as soon as possible.
|
825
|
+
Thread.pass
|
826
|
+
end
|
827
|
+
|
828
|
+
def context(thread=Thread.current)
|
829
|
+
c = thread[:__debugger_data__]
|
830
|
+
unless c
|
831
|
+
thread[:__debugger_data__] = c = Context.new
|
832
|
+
end
|
833
|
+
c
|
834
|
+
end
|
835
|
+
|
836
|
+
def interrupt
|
837
|
+
context(@last_thread).stop_next
|
838
|
+
end
|
839
|
+
|
840
|
+
def get_thread(num)
|
841
|
+
th = @thread_list.index(num)
|
842
|
+
unless th
|
843
|
+
@stdout.print "No thread ##{num}\n"
|
844
|
+
throw :debug_error
|
845
|
+
end
|
846
|
+
th
|
847
|
+
end
|
848
|
+
|
849
|
+
def thread_list(num)
|
850
|
+
th = get_thread(num)
|
851
|
+
if th == Thread.current
|
852
|
+
@stdout.print "+"
|
853
|
+
else
|
854
|
+
@stdout.print " "
|
855
|
+
end
|
856
|
+
@stdout.printf "%d ", num
|
857
|
+
@stdout.print th.inspect, "\t"
|
858
|
+
file = context(th).instance_eval{@file}
|
859
|
+
if file
|
860
|
+
@stdout.print file,":",context(th).instance_eval{@line}
|
861
|
+
end
|
862
|
+
@stdout.print "\n"
|
863
|
+
end
|
864
|
+
|
865
|
+
def thread_list_all
|
866
|
+
for th in @thread_list.values.sort
|
867
|
+
thread_list(th)
|
868
|
+
end
|
869
|
+
end
|
870
|
+
|
871
|
+
def make_thread_list
|
872
|
+
hash = {}
|
873
|
+
for th in Thread::list
|
874
|
+
if @thread_list.key? th
|
875
|
+
hash[th] = @thread_list[th]
|
876
|
+
else
|
877
|
+
@max_thread += 1
|
878
|
+
hash[th] = @max_thread
|
879
|
+
end
|
880
|
+
end
|
881
|
+
@thread_list = hash
|
882
|
+
end
|
883
|
+
|
884
|
+
def debug_thread_info(input, binding)
|
885
|
+
case input
|
886
|
+
when /^l(?:ist)?/
|
887
|
+
make_thread_list
|
888
|
+
thread_list_all
|
889
|
+
|
890
|
+
when /^c(?:ur(?:rent)?)?$/
|
891
|
+
make_thread_list
|
892
|
+
thread_list(@thread_list[Thread.current])
|
893
|
+
|
894
|
+
when /^(?:sw(?:itch)?\s+)?(\d+)/
|
895
|
+
make_thread_list
|
896
|
+
th = get_thread($1.to_i)
|
897
|
+
if th == Thread.current
|
898
|
+
@stdout.print "It's the current thread.\n"
|
899
|
+
else
|
900
|
+
thread_list(@thread_list[th])
|
901
|
+
context(th).stop_next
|
902
|
+
th.run
|
903
|
+
return :cont
|
904
|
+
end
|
905
|
+
|
906
|
+
when /^stop\s+(\d+)/
|
907
|
+
make_thread_list
|
908
|
+
th = get_thread($1.to_i)
|
909
|
+
if th == Thread.current
|
910
|
+
@stdout.print "It's the current thread.\n"
|
911
|
+
elsif th.stop?
|
912
|
+
@stdout.print "Already stopped.\n"
|
913
|
+
else
|
914
|
+
thread_list(@thread_list[th])
|
915
|
+
context(th).suspend
|
916
|
+
end
|
917
|
+
|
918
|
+
when /^resume\s+(\d+)/
|
919
|
+
make_thread_list
|
920
|
+
th = get_thread($1.to_i)
|
921
|
+
if th == Thread.current
|
922
|
+
@stdout.print "It's the current thread.\n"
|
923
|
+
elsif !th.stop?
|
924
|
+
@stdout.print "Already running."
|
925
|
+
else
|
926
|
+
thread_list(@thread_list[th])
|
927
|
+
th.run
|
928
|
+
end
|
929
|
+
end
|
930
|
+
end
|
931
|
+
|
932
|
+
def start_debugging
|
933
|
+
self.context.add_event_hook
|
934
|
+
end
|
935
|
+
|
936
|
+
def stop_debugging
|
937
|
+
self.context.remove_event_hook
|
938
|
+
end
|
939
|
+
|
940
|
+
inline(:C) do |builder|
|
941
|
+
|
942
|
+
builder.add_type_converter("rb_event_t", '', '')
|
943
|
+
builder.add_type_converter("ID", '', '')
|
944
|
+
|
945
|
+
builder.include '"ruby.h"'
|
946
|
+
builder.include '"node.h"'
|
947
|
+
|
948
|
+
builder.add_type_converter('NODE *', '', '')
|
949
|
+
|
950
|
+
builder.c_raw <<-'EOF'
|
951
|
+
static void
|
952
|
+
debugger_event_hook(rb_event_t event, NODE *node,
|
953
|
+
VALUE xself, ID mid, VALUE klass) {
|
954
|
+
|
955
|
+
static int debugging = 0;
|
956
|
+
static VALUE debugger_klass = Qnil;
|
957
|
+
static VALUE ztracer_klass = Qnil;
|
958
|
+
|
959
|
+
if (mid == ID_ALLOCATOR) return;
|
960
|
+
if (debugging) return;
|
961
|
+
debugging++;
|
962
|
+
|
963
|
+
if (NIL_P(debugger_klass)) debugger_klass = rb_path2class("ZenDebugger");
|
964
|
+
if (NIL_P(ztracer_klass)) ztracer_klass = rb_path2class("Tracer");
|
965
|
+
|
966
|
+
VALUE file, line;
|
967
|
+
|
968
|
+
if (node) {
|
969
|
+
file = rb_str_new2(node->nd_file);
|
970
|
+
line = INT2NUM(nd_line(node));
|
971
|
+
} else {
|
972
|
+
file = rb_str_new2("Unknown");
|
973
|
+
line = INT2NUM(-1);
|
974
|
+
}
|
975
|
+
|
976
|
+
VALUE binding = xself ? rb_funcall(xself, rb_intern("binding"), 0) : Qnil;
|
977
|
+
VALUE context = rb_funcall(debugger_klass, rb_intern("context"), 0);
|
978
|
+
|
979
|
+
if (RTEST(rb_iv_get(context, "@trace"))) {
|
980
|
+
rb_funcall(ztracer_klass, rb_intern("trace_func"), 6,
|
981
|
+
event, file, line, mid, binding, klass);
|
982
|
+
}
|
983
|
+
|
984
|
+
rb_funcall(context, rb_intern("check_suspend"), 0);
|
985
|
+
|
986
|
+
rb_ivar_set(context, rb_intern("@file"), file);
|
987
|
+
rb_ivar_set(context, rb_intern("@line"), line);
|
988
|
+
|
989
|
+
VALUE id = ID2SYM(mid);
|
990
|
+
|
991
|
+
switch (event) {
|
992
|
+
case RUBY_EVENT_NONE:
|
993
|
+
break;
|
994
|
+
case RUBY_EVENT_LINE:
|
995
|
+
rb_funcall(context, rb_intern("trace_func_line"), 5, file, line, id, binding, klass);
|
996
|
+
break;
|
997
|
+
case RUBY_EVENT_CLASS:
|
998
|
+
rb_funcall(context, rb_intern("trace_func_class"), 5, file, line, id, binding, klass);
|
999
|
+
break;
|
1000
|
+
case RUBY_EVENT_RETURN:
|
1001
|
+
case RUBY_EVENT_END:
|
1002
|
+
rb_funcall(context, rb_intern("trace_func_return"), 5, file, line, id, binding, klass);
|
1003
|
+
break;
|
1004
|
+
case RUBY_EVENT_CALL:
|
1005
|
+
rb_funcall(context, rb_intern("trace_func_call"), 5, file, line, id, binding, klass);
|
1006
|
+
break;
|
1007
|
+
case RUBY_EVENT_C_CALL:
|
1008
|
+
rb_funcall(context, rb_intern("trace_func_c_call"), 5, file, line, id, binding, klass);
|
1009
|
+
break;
|
1010
|
+
case RUBY_EVENT_RAISE:
|
1011
|
+
rb_funcall(context, rb_intern("trace_func_raise"), 5, file, line, id, binding, klass);
|
1012
|
+
break;
|
1013
|
+
}
|
1014
|
+
rb_ivar_set(context, rb_intern("@last_file"), file);
|
1015
|
+
|
1016
|
+
debugging--;
|
1017
|
+
}
|
1018
|
+
EOF
|
1019
|
+
|
1020
|
+
builder.c <<-'EOF'
|
1021
|
+
void add_event_hook() {
|
1022
|
+
rb_add_event_hook(debugger_event_hook, RUBY_EVENT_ALL);
|
1023
|
+
}
|
1024
|
+
EOF
|
1025
|
+
|
1026
|
+
builder.c <<-'EOF'
|
1027
|
+
void remove_event_hook() {
|
1028
|
+
rb_remove_event_hook(debugger_event_hook);
|
1029
|
+
}
|
1030
|
+
EOF
|
1031
|
+
end # inline
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
stdout.printf "zendebug.rb\n"
|
1035
|
+
stdout.printf "Emacs support available.\n\n"
|
1036
|
+
ZenDebugger.start_debugging
|
1037
|
+
end # class ZenDebugger
|