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.
@@ -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