debug 0.2.1 → 1.0.0.alpha0

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/lib/debug.rb CHANGED
@@ -1,1120 +1,15 @@
1
- # frozen_string_literal: true
2
- # Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
3
- # Copyright (C) 2000 Information-technology Promotion Agency, Japan
4
- # Copyright (C) 2000-2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
1
+ require_relative 'debug/repl'
5
2
 
6
- if $SAFE > 0
7
- STDERR.print "-r debug.rb is not available in safe mode\n"
8
- exit 1
3
+ # default break points
4
+ DEBUGGER__.add_catch_breakpoint 'RuntimeError'
5
+ class Binding
6
+ DEBUGGER__.add_line_breakpoint __FILE__, __LINE__ + 1
7
+ def bp; nil; end
9
8
  end
10
9
 
11
- require 'tracer'
12
- require 'pp'
13
-
14
- class Tracer # :nodoc:
15
- def Tracer.trace_func(*vars)
16
- Single.trace_func(*vars)
17
- end
18
- end
19
-
20
- SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__ # :nodoc:
21
-
22
- ##
23
- # This library provides debugging functionality to Ruby.
24
- #
25
- # To add a debugger to your code, start by requiring +debug+ in your
26
- # program:
27
- #
28
- # def say(word)
29
- # require 'debug'
30
- # puts word
31
- # end
32
- #
33
- # This will cause Ruby to interrupt execution and show a prompt when the +say+
34
- # method is run.
35
- #
36
- # Once you're inside the prompt, you can start debugging your program.
37
- #
38
- # (rdb:1) p word
39
- # "hello"
40
- #
41
- # == Getting help
42
- #
43
- # You can get help at any time by pressing +h+.
44
- #
45
- # (rdb:1) h
46
- # Debugger help v.-0.002b
47
- # Commands
48
- # b[reak] [file:|class:]<line|method>
49
- # b[reak] [class.]<line|method>
50
- # set breakpoint to some position
51
- # wat[ch] <expression> set watchpoint to some expression
52
- # cat[ch] (<exception>|off) set catchpoint to an exception
53
- # b[reak] list breakpoints
54
- # cat[ch] show catchpoint
55
- # del[ete][ nnn] delete some or all breakpoints
56
- # disp[lay] <expression> add expression into display expression list
57
- # undisp[lay][ nnn] delete one particular or all display expressions
58
- # c[ont] run until program ends or hit breakpoint
59
- # s[tep][ nnn] step (into methods) one line or till line nnn
60
- # n[ext][ nnn] go over one line or till line nnn
61
- # w[here] display frames
62
- # f[rame] alias for where
63
- # l[ist][ (-|nn-mm)] list program, - lists backwards
64
- # nn-mm lists given lines
65
- # up[ nn] move to higher frame
66
- # down[ nn] move to lower frame
67
- # fin[ish] return to outer frame
68
- # tr[ace] (on|off) set trace mode of current thread
69
- # tr[ace] (on|off) all set trace mode of all threads
70
- # q[uit] exit from debugger
71
- # v[ar] g[lobal] show global variables
72
- # v[ar] l[ocal] show local variables
73
- # v[ar] i[nstance] <object> show instance variables of object
74
- # v[ar] c[onst] <object> show constants of object
75
- # m[ethod] i[nstance] <obj> show methods of object
76
- # m[ethod] <class|module> show instance methods of class or module
77
- # th[read] l[ist] list all threads
78
- # th[read] c[ur[rent]] show current thread
79
- # th[read] [sw[itch]] <nnn> switch thread context to nnn
80
- # th[read] stop <nnn> stop thread nnn
81
- # th[read] resume <nnn> resume thread nnn
82
- # p expression evaluate expression and print its value
83
- # h[elp] print this help
84
- # <everything else> evaluate
85
- #
86
- # == Usage
87
- #
88
- # The following is a list of common functionalities that the debugger
89
- # provides.
90
- #
91
- # === Navigating through your code
92
- #
93
- # In general, a debugger is used to find bugs in your program, which
94
- # often means pausing execution and inspecting variables at some point
95
- # in time.
96
- #
97
- # Let's look at an example:
98
- #
99
- # def my_method(foo)
100
- # require 'debug'
101
- # foo = get_foo if foo.nil?
102
- # raise if foo.nil?
103
- # end
104
- #
105
- # When you run this program, the debugger will kick in just before the
106
- # +foo+ assignment.
107
- #
108
- # (rdb:1) p foo
109
- # nil
110
- #
111
- # In this example, it'd be interesting to move to the next line and
112
- # inspect the value of +foo+ again. You can do that by pressing +n+:
113
- #
114
- # (rdb:1) n # goes to next line
115
- # (rdb:1) p foo
116
- # nil
117
- #
118
- # You now know that the original value of +foo+ was nil, and that it
119
- # still was nil after calling +get_foo+.
120
- #
121
- # Other useful commands for navigating through your code are:
122
- #
123
- # +c+::
124
- # Runs the program until it either exists or encounters another breakpoint.
125
- # You usually press +c+ when you are finished debugging your program and
126
- # want to resume its execution.
127
- # +s+::
128
- # Steps into method definition. In the previous example, +s+ would take you
129
- # inside the method definition of +get_foo+.
130
- # +r+::
131
- # Restart the program.
132
- # +q+::
133
- # Quit the program.
134
- #
135
- # === Inspecting variables
136
- #
137
- # You can use the debugger to easily inspect both local and global variables.
138
- # We've seen how to inspect local variables before:
139
- #
140
- # (rdb:1) p my_arg
141
- # 42
142
- #
143
- # You can also pretty print the result of variables or expressions:
144
- #
145
- # (rdb:1) pp %w{a very long long array containing many words}
146
- # ["a",
147
- # "very",
148
- # "long",
149
- # ...
150
- # ]
151
- #
152
- # You can list all local variables with +v l+:
153
- #
154
- # (rdb:1) v l
155
- # foo => "hello"
156
- #
157
- # Similarly, you can show all global variables with +v g+:
158
- #
159
- # (rdb:1) v g
160
- # all global variables
161
- #
162
- # Finally, you can omit +p+ if you simply want to evaluate a variable or
163
- # expression
164
- #
165
- # (rdb:1) 5**2
166
- # 25
167
- #
168
- # === Going beyond basics
169
- #
170
- # Ruby Debug provides more advanced functionalities like switching
171
- # between threads, setting breakpoints and watch expressions, and more.
172
- # The full list of commands is available at any time by pressing +h+.
173
- #
174
- # == Staying out of trouble
175
- #
176
- # Make sure you remove every instance of +require 'debug'+ before
177
- # shipping your code. Failing to do so may result in your program
178
- # hanging unpredictably.
179
- #
180
- # Debug is not available in safe mode.
181
-
182
- class DEBUGGER__
183
- MUTEX = Thread::Mutex.new # :nodoc:
184
- CONTINUATIONS_SUPPORTED = RUBY_ENGINE == 'ruby'
185
-
186
- require 'continuation' if CONTINUATIONS_SUPPORTED
187
-
188
- class Context # :nodoc:
189
- DEBUG_LAST_CMD = []
190
-
191
- begin
192
- require 'readline'
193
- def readline(prompt, hist)
194
- Readline::readline(prompt, hist)
195
- end
196
- rescue LoadError
197
- def readline(prompt, hist)
198
- STDOUT.print prompt
199
- STDOUT.flush
200
- line = STDIN.gets
201
- exit unless line
202
- line.chomp!
203
- line
204
- end
205
- USE_READLINE = false
206
- end
207
-
208
- def initialize
209
- if Thread.current == Thread.main
210
- @stop_next = 1
211
- else
212
- @stop_next = 0
213
- end
214
- @last_file = nil
215
- @file = nil
216
- @line = nil
217
- @no_step = nil
218
- @frames = []
219
- @finish_pos = 0
220
- @trace = false
221
- @catch = "StandardError"
222
- @suspend_next = false
223
- end
224
-
225
- def stop_next(n=1)
226
- @stop_next = n
227
- end
228
-
229
- def set_suspend
230
- @suspend_next = true
231
- end
232
-
233
- def clear_suspend
234
- @suspend_next = false
235
- end
236
-
237
- def suspend_all
238
- DEBUGGER__.suspend
239
- end
240
-
241
- def resume_all
242
- DEBUGGER__.resume
243
- end
244
-
245
- def check_suspend
246
- while MUTEX.synchronize {
247
- if @suspend_next
248
- DEBUGGER__.waiting.push Thread.current
249
- @suspend_next = false
250
- true
251
- end
252
- }
253
- end
254
- end
255
-
256
- def trace?
257
- @trace
258
- end
259
-
260
- def set_trace(arg)
261
- @trace = arg
262
- end
263
-
264
- def stdout
265
- DEBUGGER__.stdout
266
- end
267
-
268
- def break_points
269
- DEBUGGER__.break_points
270
- end
271
-
272
- def display
273
- DEBUGGER__.display
274
- end
275
-
276
- def context(th)
277
- DEBUGGER__.context(th)
278
- end
279
-
280
- def set_trace_all(arg)
281
- DEBUGGER__.set_trace(arg)
282
- end
283
-
284
- def set_last_thread(th)
285
- DEBUGGER__.set_last_thread(th)
286
- end
287
-
288
- def debug_eval(str, binding)
289
- begin
290
- eval(str, binding)
291
- rescue StandardError, ScriptError => e
292
- at = eval("caller(1)", binding)
293
- stdout.printf "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
294
- for i in at
295
- stdout.printf "\tfrom %s\n", i
296
- end
297
- throw :debug_error
298
- end
299
- end
300
-
301
- def debug_silent_eval(str, binding)
302
- begin
303
- eval(str, binding)
304
- rescue StandardError, ScriptError
305
- nil
306
- end
307
- end
308
-
309
- def var_list(ary, binding)
310
- ary.sort!
311
- for v in ary
312
- stdout.printf " %s => %s\n", v, eval(v.to_s, binding).inspect
313
- end
314
- end
315
-
316
- def debug_variable_info(input, binding)
317
- case input
318
- when /^\s*g(?:lobal)?\s*$/
319
- var_list(global_variables, binding)
320
-
321
- when /^\s*l(?:ocal)?\s*$/
322
- var_list(eval("local_variables", binding), binding)
323
-
324
- when /^\s*i(?:nstance)?\s+/
325
- obj = debug_eval($', binding)
326
- var_list(obj.instance_variables, obj.instance_eval{binding()})
327
-
328
- when /^\s*c(?:onst(?:ant)?)?\s+/
329
- obj = debug_eval($', binding)
330
- unless obj.kind_of? Module
331
- stdout.print "Should be Class/Module: ", $', "\n"
332
- else
333
- var_list(obj.constants, obj.module_eval{binding()})
334
- end
335
- end
336
- end
337
-
338
- def debug_method_info(input, binding)
339
- case input
340
- when /^i(:?nstance)?\s+/
341
- obj = debug_eval($', binding)
342
-
343
- len = 0
344
- for v in obj.methods.sort
345
- len += v.size + 1
346
- if len > 70
347
- len = v.size + 1
348
- stdout.print "\n"
349
- end
350
- stdout.print v, " "
351
- end
352
- stdout.print "\n"
353
-
354
- else
355
- obj = debug_eval(input, binding)
356
- unless obj.kind_of? Module
357
- stdout.print "Should be Class/Module: ", input, "\n"
358
- else
359
- len = 0
360
- for v in obj.instance_methods(false).sort
361
- len += v.size + 1
362
- if len > 70
363
- len = v.size + 1
364
- stdout.print "\n"
365
- end
366
- stdout.print v, " "
367
- end
368
- stdout.print "\n"
369
- end
370
- end
371
- end
372
-
373
- def thnum
374
- num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
375
- unless num
376
- DEBUGGER__.make_thread_list
377
- num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
378
- end
379
- num
380
- end
381
-
382
- def debug_command(file, line, id, binding)
383
- MUTEX.lock
384
- if CONTINUATIONS_SUPPORTED
385
- unless defined?($debugger_restart) and $debugger_restart
386
- callcc{|c| $debugger_restart = c}
387
- end
388
- end
389
- set_last_thread(Thread.current)
390
- frame_pos = 0
391
- binding_file = file
392
- binding_line = line
393
- previous_line = nil
394
- if ENV['EMACS']
395
- stdout.printf "\032\032%s:%d:\n", binding_file, binding_line
396
- else
397
- stdout.printf "%s:%d:%s", binding_file, binding_line,
398
- line_at(binding_file, binding_line)
399
- end
400
- @frames[0] = [binding, file, line, id]
401
- display_expressions(binding)
402
- prompt = true
403
- while prompt and input = readline("(rdb:%d) "%thnum(), true)
404
- catch(:debug_error) do
405
- if input == ""
406
- next unless DEBUG_LAST_CMD[0]
407
- input = DEBUG_LAST_CMD[0]
408
- stdout.print input, "\n"
409
- else
410
- DEBUG_LAST_CMD[0] = input
411
- end
412
-
413
- case input
414
- when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/
415
- if defined?( $2 )
416
- if $1 == 'on'
417
- set_trace_all true
418
- else
419
- set_trace_all false
420
- end
421
- elsif defined?( $1 )
422
- if $1 == 'on'
423
- set_trace true
424
- else
425
- set_trace false
426
- end
427
- end
428
- if trace?
429
- stdout.print "Trace on.\n"
430
- else
431
- stdout.print "Trace off.\n"
432
- end
433
-
434
- when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:]+)$/
435
- pos = $2
436
- if $1
437
- klass = debug_silent_eval($1, binding)
438
- file = File.expand_path($1)
439
- end
440
- if pos =~ /^\d+$/
441
- pname = pos
442
- pos = pos.to_i
443
- else
444
- pname = pos = pos.intern.id2name
445
- end
446
- break_points.push [true, 0, klass || file, pos]
447
- stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, klass || file, pname
448
-
449
- when /^\s*b(?:reak)?\s+(.+)[#.]([^.:]+)$/
450
- pos = $2.intern.id2name
451
- klass = debug_eval($1, binding)
452
- break_points.push [true, 0, klass, pos]
453
- stdout.printf "Set breakpoint %d at %s.%s\n", break_points.size, klass, pos
454
-
455
- when /^\s*wat(?:ch)?\s+(.+)$/
456
- exp = $1
457
- break_points.push [true, 1, exp]
458
- stdout.printf "Set watchpoint %d:%s\n", break_points.size, exp
459
-
460
- when /^\s*b(?:reak)?$/
461
- if break_points.find{|b| b[1] == 0}
462
- n = 1
463
- stdout.print "Breakpoints:\n"
464
- break_points.each do |b|
465
- if b[0] and b[1] == 0
466
- stdout.printf " %d %s:%s\n", n, b[2], b[3]
467
- end
468
- n += 1
469
- end
470
- end
471
- if break_points.find{|b| b[1] == 1}
472
- n = 1
473
- stdout.print "\n"
474
- stdout.print "Watchpoints:\n"
475
- for b in break_points
476
- if b[0] and b[1] == 1
477
- stdout.printf " %d %s\n", n, b[2]
478
- end
479
- n += 1
480
- end
481
- end
482
- if break_points.size == 0
483
- stdout.print "No breakpoints\n"
484
- else
485
- stdout.print "\n"
486
- end
487
-
488
- when /^\s*del(?:ete)?(?:\s+(\d+))?$/
489
- pos = $1
490
- unless pos
491
- input = readline("Clear all breakpoints? (y/n) ", false)
492
- if input == "y"
493
- for b in break_points
494
- b[0] = false
495
- end
496
- end
497
- else
498
- pos = pos.to_i
499
- if break_points[pos-1]
500
- break_points[pos-1][0] = false
501
- else
502
- stdout.printf "Breakpoint %d is not defined\n", pos
503
- end
504
- end
505
-
506
- when /^\s*disp(?:lay)?\s+(.+)$/
507
- exp = $1
508
- display.push [true, exp]
509
- stdout.printf "%d: ", display.size
510
- display_expression(exp, binding)
511
-
512
- when /^\s*disp(?:lay)?$/
513
- display_expressions(binding)
514
-
515
- when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/
516
- pos = $1
517
- unless pos
518
- input = readline("Clear all expressions? (y/n) ", false)
519
- if input == "y"
520
- for d in display
521
- d[0] = false
522
- end
523
- end
524
- else
525
- pos = pos.to_i
526
- if display[pos-1]
527
- display[pos-1][0] = false
528
- else
529
- stdout.printf "Display expression %d is not defined\n", pos
530
- end
531
- end
532
-
533
- when /^\s*c(?:ont)?$/
534
- prompt = false
535
-
536
- when /^\s*s(?:tep)?(?:\s+(\d+))?$/
537
- if $1
538
- lev = $1.to_i
539
- else
540
- lev = 1
541
- end
542
- @stop_next = lev
543
- prompt = false
544
-
545
- when /^\s*n(?:ext)?(?:\s+(\d+))?$/
546
- if $1
547
- lev = $1.to_i
548
- else
549
- lev = 1
550
- end
551
- @stop_next = lev
552
- @no_step = @frames.size - frame_pos
553
- prompt = false
554
-
555
- when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/
556
- display_frames(frame_pos)
557
-
558
- when /^\s*l(?:ist)?(?:\s+(.+))?$/
559
- if not $1
560
- b = previous_line ? previous_line + 10 : binding_line - 5
561
- e = b + 9
562
- elsif $1 == '-'
563
- b = previous_line ? previous_line - 10 : binding_line - 5
564
- e = b + 9
565
- else
566
- b, e = $1.split(/[-,]/)
567
- if e
568
- b = b.to_i
569
- e = e.to_i
570
- else
571
- b = b.to_i - 5
572
- e = b + 9
573
- end
574
- end
575
- previous_line = b
576
- display_list(b, e, binding_file, binding_line)
577
-
578
- when /^\s*up(?:\s+(\d+))?$/
579
- previous_line = nil
580
- if $1
581
- lev = $1.to_i
582
- else
583
- lev = 1
584
- end
585
- frame_pos += lev
586
- if frame_pos >= @frames.size
587
- frame_pos = @frames.size - 1
588
- stdout.print "At toplevel\n"
589
- end
590
- binding, binding_file, binding_line = @frames[frame_pos]
591
- stdout.print format_frame(frame_pos)
592
-
593
- when /^\s*down(?:\s+(\d+))?$/
594
- previous_line = nil
595
- if $1
596
- lev = $1.to_i
597
- else
598
- lev = 1
599
- end
600
- frame_pos -= lev
601
- if frame_pos < 0
602
- frame_pos = 0
603
- stdout.print "At stack bottom\n"
604
- end
605
- binding, binding_file, binding_line = @frames[frame_pos]
606
- stdout.print format_frame(frame_pos)
607
-
608
- when /^\s*fin(?:ish)?$/
609
- if frame_pos == @frames.size
610
- stdout.print "\"finish\" not meaningful in the outermost frame.\n"
611
- else
612
- @finish_pos = @frames.size - frame_pos
613
- frame_pos = 0
614
- prompt = false
615
- end
616
-
617
- when /^\s*cat(?:ch)?(?:\s+(.+))?$/
618
- if $1
619
- excn = $1
620
- if excn == 'off'
621
- @catch = nil
622
- stdout.print "Clear catchpoint.\n"
623
- else
624
- @catch = excn
625
- stdout.printf "Set catchpoint %s.\n", @catch
626
- end
627
- else
628
- if @catch
629
- stdout.printf "Catchpoint %s.\n", @catch
630
- else
631
- stdout.print "No catchpoint.\n"
632
- end
633
- end
634
-
635
- when /^\s*q(?:uit)?$/
636
- input = readline("Really quit? (y/n) ", false)
637
- if input == "y"
638
- exit! # exit -> exit!: No graceful way to stop threads...
639
- end
640
-
641
- when /^\s*v(?:ar)?\s+/
642
- debug_variable_info($', binding)
643
-
644
- when /^\s*m(?:ethod)?\s+/
645
- debug_method_info($', binding)
646
-
647
- when /^\s*th(?:read)?\s+/
648
- if DEBUGGER__.debug_thread_info($', binding) == :cont
649
- prompt = false
650
- end
651
-
652
- when /^\s*pp\s+/
653
- PP.pp(debug_eval($', binding), stdout)
654
-
655
- when /^\s*p\s+/
656
- stdout.printf "%s\n", debug_eval($', binding).inspect
657
-
658
- when /^\s*r(?:estart)?$/
659
- if CONTINUATIONS_SUPPORTED
660
- $debugger_restart.call
661
- else
662
- stdout.print "Restart requires continuations.\n"
663
- end
664
-
665
- when /^\s*h(?:elp)?$/
666
- debug_print_help()
667
-
668
- else
669
- v = debug_eval(input, binding)
670
- stdout.printf "%s\n", v.inspect
671
- end
672
- end
673
- end
674
- MUTEX.unlock
675
- resume_all
676
- end
677
-
678
- def debug_print_help
679
- stdout.print <<EOHELP
680
- Debugger help v.-0.002b
681
- Commands
682
- b[reak] [file:|class:]<line|method>
683
- b[reak] [class.]<line|method>
684
- set breakpoint to some position
685
- wat[ch] <expression> set watchpoint to some expression
686
- cat[ch] (<exception>|off) set catchpoint to an exception
687
- b[reak] list breakpoints
688
- cat[ch] show catchpoint
689
- del[ete][ nnn] delete some or all breakpoints
690
- disp[lay] <expression> add expression into display expression list
691
- undisp[lay][ nnn] delete one particular or all display expressions
692
- c[ont] run until program ends or hit breakpoint
693
- s[tep][ nnn] step (into methods) one line or till line nnn
694
- n[ext][ nnn] go over one line or till line nnn
695
- w[here] display frames
696
- f[rame] alias for where
697
- l[ist][ (-|nn-mm)] list program, - lists backwards
698
- nn-mm lists given lines
699
- up[ nn] move to higher frame
700
- down[ nn] move to lower frame
701
- fin[ish] return to outer frame
702
- tr[ace] (on|off) set trace mode of current thread
703
- tr[ace] (on|off) all set trace mode of all threads
704
- q[uit] exit from debugger
705
- v[ar] g[lobal] show global variables
706
- v[ar] l[ocal] show local variables
707
- v[ar] i[nstance] <object> show instance variables of object
708
- v[ar] c[onst] <object> show constants of object
709
- m[ethod] i[nstance] <obj> show methods of object
710
- m[ethod] <class|module> show instance methods of class or module
711
- th[read] l[ist] list all threads
712
- th[read] c[ur[rent]] show current thread
713
- th[read] [sw[itch]] <nnn> switch thread context to nnn
714
- th[read] stop <nnn> stop thread nnn
715
- th[read] resume <nnn> resume thread nnn
716
- pp expression evaluate expression and pretty_print its value
717
- p expression evaluate expression and print its value
718
- r[estart] restart program
719
- h[elp] print this help
720
- <everything else> evaluate
721
- EOHELP
722
- end
723
-
724
- def display_expressions(binding)
725
- n = 1
726
- for d in display
727
- if d[0]
728
- stdout.printf "%d: ", n
729
- display_expression(d[1], binding)
730
- end
731
- n += 1
732
- end
733
- end
734
-
735
- def display_expression(exp, binding)
736
- stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s
737
- end
738
-
739
- def frame_set_pos(file, line)
740
- if @frames[0]
741
- @frames[0][1] = file
742
- @frames[0][2] = line
743
- end
744
- end
745
-
746
- def display_frames(pos)
747
- 0.upto(@frames.size - 1) do |n|
748
- if n == pos
749
- stdout.print "--> "
750
- else
751
- stdout.print " "
752
- end
753
- stdout.print format_frame(n)
754
- end
755
- end
756
-
757
- def format_frame(pos)
758
- _, file, line, id = @frames[pos]
759
- sprintf "#%d %s:%s%s\n", pos + 1, file, line,
760
- (id ? ":in `#{id.id2name}'" : "")
761
- end
762
-
763
- def script_lines(file, line)
764
- unless (lines = SCRIPT_LINES__[file]) and lines != true
765
- Tracer::Single.get_line(file, line) if File.exist?(file)
766
- lines = SCRIPT_LINES__[file]
767
- lines = nil if lines == true
768
- end
769
- lines
770
- end
771
-
772
- def display_list(b, e, file, line)
773
- if lines = script_lines(file, line)
774
- stdout.printf "[%d, %d] in %s\n", b, e, file
775
- b.upto(e) do |n|
776
- if n > 0 && lines[n-1]
777
- if n == line
778
- stdout.printf "=> %d %s\n", n, lines[n-1].chomp
779
- else
780
- stdout.printf " %d %s\n", n, lines[n-1].chomp
781
- end
782
- end
783
- end
784
- else
785
- stdout.printf "No sourcefile available for %s\n", file
786
- end
787
- end
788
-
789
- def line_at(file, line)
790
- lines = script_lines(file, line)
791
- if lines and line = lines[line-1]
792
- return line
793
- end
794
- return "\n"
795
- end
796
-
797
- def debug_funcname(id)
798
- if id.nil?
799
- "toplevel"
800
- else
801
- id.id2name
802
- end
803
- end
804
-
805
- def check_break_points(file, klass, pos, binding, id)
806
- return false if break_points.empty?
807
- n = 1
808
- for b in break_points
809
- if b[0] # valid
810
- if b[1] == 0 # breakpoint
811
- if (b[2] == file and b[3] == pos) or
812
- (klass and b[2] == klass and b[3] == pos)
813
- stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
814
- return true
815
- end
816
- elsif b[1] == 1 # watchpoint
817
- if debug_silent_eval(b[2], binding)
818
- stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
819
- return true
820
- end
821
- end
822
- end
823
- n += 1
824
- end
825
- return false
826
- end
827
-
828
- def excn_handle(file, line, id, binding)
829
- if $!.class <= SystemExit
830
- set_trace_func nil
831
- exit
832
- end
833
-
834
- if @catch and ($!.class.ancestors.find { |e| e.to_s == @catch })
835
- stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.class
836
- fs = @frames.size
837
- tb = caller(0)[-fs..-1]
838
- if tb
839
- for i in tb
840
- stdout.printf "\tfrom %s\n", i
841
- end
842
- end
843
- suspend_all
844
- debug_command(file, line, id, binding)
845
- end
846
- end
847
-
848
- def trace_func(event, file, line, id, binding, klass)
849
- Tracer.trace_func(event, file, line, id, binding, klass) if trace?
850
- context(Thread.current).check_suspend
851
- @file = file
852
- @line = line
853
- case event
854
- when 'line'
855
- frame_set_pos(file, line)
856
- if !@no_step or @frames.size == @no_step
857
- @stop_next -= 1
858
- @stop_next = -1 if @stop_next < 0
859
- elsif @frames.size < @no_step
860
- @stop_next = 0 # break here before leaving...
861
- else
862
- # nothing to do. skipped.
863
- end
864
- if @stop_next == 0 or check_break_points(file, nil, line, binding, id)
865
- @no_step = nil
866
- suspend_all
867
- debug_command(file, line, id, binding)
868
- end
869
-
870
- when 'call'
871
- @frames.unshift [binding, file, line, id]
872
- if check_break_points(file, klass, id.id2name, binding, id)
873
- suspend_all
874
- debug_command(file, line, id, binding)
875
- end
876
-
877
- when 'c-call'
878
- frame_set_pos(file, line)
879
-
880
- when 'class'
881
- @frames.unshift [binding, file, line, id]
882
-
883
- when 'return', 'end'
884
- if @frames.size == @finish_pos
885
- @stop_next = 1
886
- @finish_pos = 0
887
- end
888
- @frames.shift
889
-
890
- when 'raise'
891
- excn_handle(file, line, id, binding)
892
-
893
- end
894
- @last_file = file
895
- end
896
- end
897
-
898
- trap("INT") { DEBUGGER__.interrupt }
899
- @last_thread = Thread::main
900
- @max_thread = 1
901
- @thread_list = {Thread::main => 1}
902
- @break_points = []
903
- @display = []
904
- @waiting = []
905
- @stdout = STDOUT
906
-
907
- class << DEBUGGER__
908
- # Returns the IO used as stdout. Defaults to STDOUT
909
- def stdout
910
- @stdout
911
- end
912
-
913
- # Sets the IO used as stdout. Defaults to STDOUT
914
- def stdout=(s)
915
- @stdout = s
916
- end
917
-
918
- # Returns the display expression list
919
- #
920
- # See DEBUGGER__ for more usage
921
- def display
922
- @display
923
- end
924
-
925
- # Returns the list of break points where execution will be stopped.
926
- #
927
- # See DEBUGGER__ for more usage
928
- def break_points
929
- @break_points
930
- end
931
-
932
- # Returns the list of waiting threads.
933
- #
934
- # When stepping through the traces of a function, thread gets suspended, to
935
- # be resumed later.
936
- def waiting
937
- @waiting
938
- end
939
-
940
- def set_trace( arg )
941
- MUTEX.synchronize do
942
- make_thread_list
943
- for th, in @thread_list
944
- context(th).set_trace arg
945
- end
946
- end
947
- arg
948
- end
949
-
950
- def set_last_thread(th)
951
- @last_thread = th
952
- end
953
-
954
- def suspend
955
- MUTEX.synchronize do
956
- make_thread_list
957
- for th, in @thread_list
958
- next if th == Thread.current
959
- context(th).set_suspend
960
- end
961
- end
962
- # Schedule other threads to suspend as soon as possible.
963
- Thread.pass
964
- end
965
-
966
- def resume
967
- MUTEX.synchronize do
968
- make_thread_list
969
- @thread_list.each do |th,|
970
- next if th == Thread.current
971
- context(th).clear_suspend
972
- end
973
- waiting.each do |th|
974
- th.run
975
- end
976
- waiting.clear
977
- end
978
- # Schedule other threads to restart as soon as possible.
979
- Thread.pass
980
- end
981
-
982
- def context(thread=Thread.current)
983
- c = thread[:__debugger_data__]
984
- unless c
985
- thread[:__debugger_data__] = c = Context.new
986
- end
987
- c
988
- end
989
-
990
- def interrupt
991
- context(@last_thread).stop_next
992
- end
993
-
994
- def get_thread(num)
995
- th = @thread_list.key(num)
996
- unless th
997
- @stdout.print "No thread ##{num}\n"
998
- throw :debug_error
999
- end
1000
- th
1001
- end
1002
-
1003
- def thread_list(num)
1004
- th = get_thread(num)
1005
- if th == Thread.current
1006
- @stdout.print "+"
1007
- else
1008
- @stdout.print " "
1009
- end
1010
- @stdout.printf "%d ", num
1011
- @stdout.print th.inspect, "\t"
1012
- file = context(th).instance_eval{@file}
1013
- if file
1014
- @stdout.print file,":",context(th).instance_eval{@line}
1015
- end
1016
- @stdout.print "\n"
1017
- end
1018
-
1019
- # Prints all threads in @thread_list to @stdout. Returns a sorted array of
1020
- # values from the @thread_list hash.
1021
- #
1022
- # While in the debugger you can list all of
1023
- # the threads with: <b>DEBUGGER__.thread_list_all</b>
1024
- #
1025
- # (rdb:1) DEBUGGER__.thread_list_all
1026
- # +1 #<Thread:0x007fb2320c03f0 run> debug_me.rb.rb:3
1027
- # 2 #<Thread:0x007fb23218a538 debug_me.rb.rb:3 sleep>
1028
- # 3 #<Thread:0x007fb23218b0f0 debug_me.rb.rb:3 sleep>
1029
- # [1, 2, 3]
1030
- #
1031
- # Your current thread is indicated by a <b>+</b>
1032
- #
1033
- # Additionally you can list all threads with <b>th l</b>
1034
- #
1035
- # (rdb:1) th l
1036
- # +1 #<Thread:0x007f99328c0410 run> debug_me.rb:3
1037
- # 2 #<Thread:0x007f9932938230 debug_me.rb:3 sleep> debug_me.rb:3
1038
- # 3 #<Thread:0x007f9932938e10 debug_me.rb:3 sleep> debug_me.rb:3
1039
- #
1040
- # See DEBUGGER__ for more usage.
1041
-
1042
- def thread_list_all
1043
- for th in @thread_list.values.sort
1044
- thread_list(th)
1045
- end
1046
- end
1047
-
1048
- def make_thread_list
1049
- hash = {}
1050
- for th in Thread::list
1051
- if @thread_list.key? th
1052
- hash[th] = @thread_list[th]
1053
- else
1054
- @max_thread += 1
1055
- hash[th] = @max_thread
1056
- end
1057
- end
1058
- @thread_list = hash
1059
- end
1060
-
1061
- def debug_thread_info(input, binding)
1062
- case input
1063
- when /^l(?:ist)?/
1064
- make_thread_list
1065
- thread_list_all
1066
-
1067
- when /^c(?:ur(?:rent)?)?$/
1068
- make_thread_list
1069
- thread_list(@thread_list[Thread.current])
1070
-
1071
- when /^(?:sw(?:itch)?\s+)?(\d+)/
1072
- make_thread_list
1073
- th = get_thread($1.to_i)
1074
- if th == Thread.current
1075
- @stdout.print "It's the current thread.\n"
1076
- else
1077
- thread_list(@thread_list[th])
1078
- context(th).stop_next
1079
- th.run
1080
- return :cont
1081
- end
1082
-
1083
- when /^stop\s+(\d+)/
1084
- make_thread_list
1085
- th = get_thread($1.to_i)
1086
- if th == Thread.current
1087
- @stdout.print "It's the current thread.\n"
1088
- elsif th.stop?
1089
- @stdout.print "Already stopped.\n"
1090
- else
1091
- thread_list(@thread_list[th])
1092
- context(th).suspend
1093
- end
1094
-
1095
- when /^resume\s+(\d+)/
1096
- make_thread_list
1097
- th = get_thread($1.to_i)
1098
- if th == Thread.current
1099
- @stdout.print "It's the current thread.\n"
1100
- elsif !th.stop?
1101
- @stdout.print "Already running."
1102
- else
1103
- thread_list(@thread_list[th])
1104
- th.run
1105
- end
1106
- end
1107
- end
1108
- end
1109
-
1110
- stdout.printf "Debug.rb\n"
1111
- stdout.printf "Emacs support available.\n\n"
1112
- if defined?(RubyVM::InstructionSequence)
1113
- RubyVM::InstructionSequence.compile_option = {
1114
- trace_instruction: true
1115
- }
1116
- end
1117
- set_trace_func proc { |event, file, line, id, binding, klass, *rest|
1118
- DEBUGGER__.context.trace_func event, file, line, id, binding, klass
1119
- }
10
+ if $0 == __FILE__
11
+ # DEBUGGER__.add_line_breakpoint __dir__ + '/target.rb', 1
12
+ # load __dir__ + '/target.rb'
13
+ else
14
+ DEBUGGER__.add_line_breakpoint $0, 1
1120
15
  end