debug 0.2.1 → 1.0.0.alpha0

Sign up to get free protection for your applications and to get access to all the features.
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