~ubuntu-branches/ubuntu/hardy/ruby1.8/hardy-updates

« back to all changes in this revision

Viewing changes to lib/debug.rb

  • Committer: Bazaar Package Importer
  • Author(s): akira yamada
  • Date: 2007-03-13 22:11:58 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20070313221158-h3oql37brlaf2go2
Tags: 1.8.6-1
* new upstream version, 1.8.6.
* libruby1.8 conflicts with libopenssl-ruby1.8 (< 1.8.6) (closes: #410018)
* changed packaging style to cdbs from dbs.

Show diffs side-by-side

added added

removed removed

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