ZenHacks 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require 'time'
4
+
5
+ class Time
6
+ def self.zones
7
+ class << Time; ZoneOffset; end
8
+ end
9
+ end
10
+
11
+ p Time.zones
@@ -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