ruby-debug 0.1.5-mswin32 → 0.2-mswin32

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,69 @@
1
+ module Debugger
2
+ class LocalInterface
3
+ def read_command(prompt)
4
+ readline(prompt, true)
5
+ end
6
+
7
+ def confirm(prompt)
8
+ readline(prompt, false)
9
+ end
10
+
11
+ def print(*args)
12
+ STDOUT.printf(*args)
13
+ end
14
+
15
+ def close
16
+ end
17
+
18
+ private
19
+
20
+ begin
21
+ require 'readline'
22
+ def readline(prompt, hist)
23
+ Readline::readline(prompt, hist)
24
+ end
25
+ rescue LoadError
26
+ def readline(prompt, hist)
27
+ STDOUT.print prompt
28
+ STDOUT.flush
29
+ line = STDIN.gets
30
+ exit unless line
31
+ line.chomp!
32
+ line
33
+ end
34
+ USE_READLINE = false
35
+ end
36
+ end
37
+
38
+ class RemoteInterface
39
+ def initialize(socket)
40
+ @socket = socket
41
+ end
42
+
43
+ def read_command(prompt)
44
+ send_command "PROMPT #{prompt}"
45
+ end
46
+
47
+ def confirm(prompt)
48
+ send_command "CONFIRM #{prompt}"
49
+ end
50
+
51
+ def print(*args)
52
+ @socket.printf(*args)
53
+ end
54
+
55
+ def close
56
+ @socket.close
57
+ rescue Exception
58
+ end
59
+
60
+ private
61
+
62
+ def send_command(msg)
63
+ @socket.puts msg
64
+ result = @socket.gets
65
+ raise IOError unless result
66
+ result.chomp
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,627 @@
1
+ require 'ruby-debug/interface'
2
+
3
+ module Debugger
4
+ class CommandProcessor
5
+ DEBUG_LAST_CMD = []
6
+
7
+ attr_accessor :interface
8
+
9
+ def initialize(interface = LocalInterface.new)
10
+ @interface = interface
11
+ @display = []
12
+ @mutex = Mutex.new
13
+ end
14
+
15
+ def interface=(interface)
16
+ @mutex.synchronize do
17
+ @interface.close
18
+ @interface = interface
19
+ end
20
+ end
21
+
22
+ def at_breakpoint(context, breakpoint)
23
+ @mutex.synchronize do
24
+ return unless @interface
25
+ n = Debugger.breakpoints.index(breakpoint) + 1
26
+ print "Breakpoint %d at %s:%s\n", n, breakpoint.source, breakpoint.pos
27
+ end
28
+ rescue IOError
29
+ self.interface = nil
30
+ end
31
+
32
+ def at_catchpoint(context, excpt)
33
+ @mutex.synchronize do
34
+ frames = Debugger.current_context.frames
35
+ print "%s:%d: `%s' (%s)\n", frames[0].file, frames[0].line, excpt, excpt.class
36
+ fs = frames.size
37
+ tb = caller(0)[-fs..-1]
38
+ if tb
39
+ for i in tb
40
+ print "\tfrom %s\n", i
41
+ end
42
+ end
43
+ end
44
+ rescue IOError
45
+ self.interface = nil
46
+ end
47
+
48
+ def at_tracing(context, file, line)
49
+ @mutex.synchronize do
50
+ print "Tracing(%d):%s:%s %s", context.thnum, file, line, line_at(file, line)
51
+ end
52
+ rescue IOError
53
+ self.interface = nil
54
+ end
55
+
56
+ def at_line(*args)
57
+ @mutex.synchronize do
58
+ process_commands(*args)
59
+ end
60
+ rescue IOError
61
+ puts 'error'
62
+ self.interface = nil
63
+ end
64
+
65
+ private
66
+
67
+ def print(*args)
68
+ @interface.print(*args)
69
+ end
70
+
71
+ def process_commands(context, file, line, binding)
72
+ frame_pos = 0
73
+ binding_file = file
74
+ binding_line = line
75
+ previous_line = nil
76
+ print "%s:%d: %s", binding_file, binding_line, line_at(binding_file, binding_line)
77
+ display_expressions(binding)
78
+ prompt = true
79
+ while prompt and input = @interface.read_command("(rdb:%d) " % context.thnum)
80
+ catch(:debug_error) do
81
+ if input == ""
82
+ next unless DEBUG_LAST_CMD[0]
83
+ input = DEBUG_LAST_CMD[0]
84
+ else
85
+ DEBUG_LAST_CMD[0] = input
86
+ end
87
+
88
+ case input
89
+ when /^\s*s(?:tep)?(?:\s+(\d+))?$/
90
+ context.stop_next = $1 ? $1.to_i : 1
91
+ prompt = false
92
+
93
+ when /^\s*c(?:ont)?$|^\s*r(?:un)?$/
94
+ prompt = false
95
+
96
+ when /^\s*v(?:ar)?\s+/
97
+ debug_variable_info($', binding)
98
+
99
+ when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/
100
+ display_frames(context, frame_pos)
101
+
102
+ when /^\s*l(?:ist)?(?:\s+(.+))?$/
103
+ if not $1
104
+ b = previous_line ? previous_line + 10 : binding_line - 5
105
+ e = b + 9
106
+ elsif $1 == '-'
107
+ b = previous_line ? previous_line - 10 : binding_line - 5
108
+ e = b + 9
109
+ else
110
+ b, e = $1.split(/[-,]/)
111
+ if e
112
+ b = b.to_i
113
+ e = e.to_i
114
+ else
115
+ b = b.to_i - 5
116
+ e = b + 9
117
+ end
118
+ end
119
+ previous_line = b
120
+ display_list(b, e, binding_file, binding_line)
121
+
122
+ when /^\s*n(?:ext)?(?:\s+(\d+))?$/
123
+ steps = $1 ? $1.to_i : 1
124
+ context.step_over steps, context.frames.size - frame_pos
125
+ prompt = false
126
+
127
+ when /^\s*up(?:\s+(\d+))?$/
128
+ previous_line = nil
129
+ frame_pos += $1 ? $1.to_i : 1
130
+ if frame_pos >= context.frames.size
131
+ frame_pos = context.frames.size - 1
132
+ print "At toplevel"
133
+ end
134
+ frame = context.frames[frame_pos]
135
+ binding, binding_file, binding_line = frame.binding, frame.file, frame.line
136
+ print format_frame(frame, frame_pos)
137
+
138
+ when /^\s*down(?:\s+(\d+))?$/
139
+ previous_line = nil
140
+ frame_pos -= $1 ? $1.to_i : 1
141
+ if frame_pos < 0
142
+ frame_pos = 0
143
+ print "At stack bottom\n"
144
+ end
145
+ frame = context.frames[frame_pos]
146
+ binding, binding_file, binding_line = frame.binding, frame.file, frame.line
147
+ print format_frame(frame, frame_pos)
148
+
149
+ when /^\s*fin(?:ish)?$/
150
+ if frame_pos == context.frames.size
151
+ print "\"finish\" not meaningful in the outermost frame.\n"
152
+ else
153
+ context.stop_frame = context.frames.size - frame_pos
154
+ frame_pos = 0
155
+ prompt = false
156
+ end
157
+
158
+ when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:\s]+)\s*(?:\sif\s+(.+))?$/
159
+ pos = $2
160
+ expr = $3
161
+ b_file = file
162
+ if $1
163
+ klass = debug_silent_eval($1, binding)
164
+ if klass && !klass.kind_of?(Module)
165
+ print "Unknown class #$1\n"
166
+ throw :debug_error
167
+ end
168
+ klass = klass.name if klass
169
+ b_file = $1
170
+ end
171
+ if pos =~ /^\d+$/
172
+ pname = pos
173
+ pos = pos.to_i
174
+ else
175
+ pname = pos = pos.intern.id2name
176
+ end
177
+ b_file = File.basename(b_file)
178
+ Debugger.add_breakpoint klass || b_file, pos, expr
179
+ print "Set breakpoint %d at %s:%s\n", Debugger.breakpoints.size, klass || b_file, pname
180
+
181
+ when /^\s*b(?:reak)?\s+(.+)[#.]([^.:\s]+)(?:\s+if\s+(.+))?$/
182
+ pos = $2.intern.id2name
183
+ expr = $3
184
+ klass = debug_eval($1, binding)
185
+ if klass.nil? || !klass.kind_of?(Module)
186
+ print "Unknown class #$1\n"
187
+ throw :debug_error
188
+ end
189
+ Debugger.add_breakpoint klass.name, pos, expr
190
+ print "Set breakpoint %d at %s.%s\n", Debugger.breakpoints.size, klass, pos
191
+
192
+ when /^\s*b(?:reak)?$/
193
+ unless Debugger.breakpoints.empty?
194
+ print "Breakpoints:\n"
195
+ Debugger.breakpoints.each_with_index do |b, n|
196
+ if b.expr.nil?
197
+ print " %d %s:%s\n", n+1, b.source, b.pos
198
+ else
199
+ print " %d %s:%s if %s\n", n+1, b.source, b.pos, b.expr
200
+ end
201
+ end
202
+ print "\n"
203
+ else
204
+ print "No breakpoints\n"
205
+ end
206
+ when /^\s*del(?:ete)?(?:\s+(\d+))?$/
207
+ pos = $1
208
+ unless pos
209
+ input = @interface.confirm("Clear all breakpoints? (y/n) ")
210
+ if input == "y"
211
+ Debugger.breakpoints.clear
212
+ end
213
+ else
214
+ pos = pos.to_i
215
+ unless Debugger.breakpoints.delete_at(pos-1)
216
+ print "Breakpoint %d is not defined\n", pos
217
+ end
218
+ end
219
+
220
+ when /^\s*th(?:read)?\s+/
221
+ if debug_thread_info($') == :cont
222
+ prompt = false
223
+ end
224
+
225
+ when /^\s*m(?:ethod)?\s+/
226
+ debug_method_info($', binding)
227
+
228
+ when /^\s*pp\s+/
229
+ out = StringIO.new
230
+ PP.pp(debug_eval($', binding), out) rescue out.puts $!.message
231
+ print out.string
232
+
233
+ when /^\s*(\s*p|e(?:val)?)\s+/
234
+ print "%s\n", debug_eval($', binding).inspect
235
+
236
+ when /^\s*h(?:elp)?(?:\s+(.+))?$/
237
+ debug_print_help($1)
238
+
239
+ when /^\s*q(?:uit)?$/
240
+ input = @interface.confirm("Really quit? (y/n) ")
241
+ if input == "y"
242
+ exit! # exit -> exit!: No graceful way to stop threads...
243
+ end
244
+
245
+ when /^\s*disp(?:lay)?\s+(.+)$/
246
+ exp = $1
247
+ @display.push [true, exp]
248
+ print "%d: ", @display.size
249
+ display_expression(exp, binding)
250
+
251
+ when /^\s*disp(?:lay)?$/
252
+ display_expressions(binding)
253
+
254
+ when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/
255
+ pos = $1
256
+ unless pos
257
+ input = @interface.confirm("Clear all expressions? (y/n) ")
258
+ if input == "y"
259
+ for d in @display
260
+ d[0] = false
261
+ end
262
+ end
263
+ else
264
+ pos = pos.to_i
265
+ if @display[pos-1]
266
+ @display[pos-1][0] = false
267
+ else
268
+ print "Display expression %d is not defined\n", pos
269
+ end
270
+ end
271
+
272
+ when /^\s*cat(?:ch)?(?:\s+(.+))?$/
273
+ if $1
274
+ excn = $1
275
+ if excn == 'off'
276
+ Debugger.catchpoint = nil
277
+ print "Clear catchpoint.\n"
278
+ else
279
+ Debugger.catchpoint = excn
280
+ print "Set catchpoint %s.\n", excn
281
+ end
282
+ else
283
+ if Debugger.catchpoint
284
+ print "Catchpoint %s.\n", Debugger.catchpoint
285
+ else
286
+ print "No catchpoint.\n"
287
+ end
288
+ end
289
+
290
+ when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/
291
+ if defined?( $2 )
292
+ Debugger.tracing = $1 == 'on'
293
+ elsif defined?( $1 )
294
+ context.tracing = $1 == 'on'
295
+ end
296
+ if Debugger.tracing || context.tracing
297
+ print "Trace on.\n"
298
+ else
299
+ print "Trace off.\n"
300
+ end
301
+
302
+ else
303
+ print "Unknown command\n"
304
+ end
305
+ end
306
+ end
307
+ end
308
+
309
+ def display_expressions(binding)
310
+ n = 1
311
+ for d in @display
312
+ if d[0]
313
+ print "%d: ", n
314
+ display_expression(d[1], binding)
315
+ end
316
+ n += 1
317
+ end
318
+ end
319
+
320
+ def display_expression(exp, binding)
321
+ print "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s
322
+ end
323
+
324
+ def debug_eval(str, binding)
325
+ begin
326
+ val = eval(str, binding)
327
+ rescue StandardError, ScriptError => e
328
+ at = eval("caller(1)", binding)
329
+ print "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
330
+ for i in at
331
+ print "\tfrom %s\n", i
332
+ end
333
+ throw :debug_error
334
+ end
335
+ end
336
+
337
+ def debug_silent_eval(str, binding)
338
+ begin
339
+ eval(str, binding)
340
+ rescue StandardError, ScriptError
341
+ nil
342
+ end
343
+ end
344
+
345
+ def debug_variable_info(input, binding)
346
+ case input
347
+ when /^\s*g(?:lobal)?\s*$/
348
+ var_list(global_variables, binding)
349
+
350
+ when /^\s*l(?:ocal)?\s*$/
351
+ var_list(eval("local_variables", binding), binding)
352
+
353
+ when /^\s*i(?:nstance)?\s+/
354
+ obj = debug_eval($', binding)
355
+ var_list(obj.instance_variables, obj.instance_eval{binding()})
356
+
357
+ when /^\s*c(?:onst(?:ant)?)?\s+/
358
+ obj = debug_eval($', binding)
359
+ unless obj.kind_of? Module
360
+ print "Should be Class/Module: %s\n", $'
361
+ else
362
+ var_list(obj.constants, obj.module_eval{binding()})
363
+ end
364
+ end
365
+ end
366
+
367
+ def display_frames(context, pos)
368
+ context.frames.each_with_index do |frame, idx|
369
+ if idx == pos
370
+ print "--> "
371
+ else
372
+ print " "
373
+ end
374
+ print format_frame(frame, idx)
375
+ end
376
+ end
377
+
378
+ def format_frame(frame, pos)
379
+ file, line, id = frame.file, frame.line, frame.id
380
+ "#%d %s:%s%s\n" % [pos + 1, file, line, (id ? ":in `#{id.id2name}'" : "")]
381
+ end
382
+
383
+ def var_list(ary, binding)
384
+ ary.sort!
385
+ for v in ary
386
+ print " %s => %s\n", v, eval(v, binding).inspect
387
+ end
388
+ end
389
+
390
+ def display_list(b, e, file, line)
391
+ print "[%d, %d] in %s\n", b, e, file
392
+ if lines = SCRIPT_LINES__[file] and lines != true
393
+ n = 0
394
+ b.upto(e) do |n|
395
+ if n > 0 && lines[n-1]
396
+ if n == line
397
+ print "=> %d %s\n", n, lines[n-1].chomp
398
+ else
399
+ print " %d %s\n", n, lines[n-1].chomp
400
+ end
401
+ end
402
+ end
403
+ else
404
+ print "No sourcefile available for %s\n", file
405
+ end
406
+ end
407
+
408
+ def debug_method_info(input, binding)
409
+ case input
410
+ when /^i(:?nstance)?\s+/
411
+ obj = debug_eval($', binding)
412
+
413
+ len = 0
414
+ for v in obj.methods.sort
415
+ len += v.size + 1
416
+ if len > 70
417
+ len = v.size + 1
418
+ print "\n"
419
+ end
420
+ print "%s ", v
421
+ end
422
+ print "\n"
423
+
424
+ else
425
+ obj = debug_eval(input, binding)
426
+ unless obj.kind_of? Module
427
+ print "Should be Class/Module: %s\n", input
428
+ else
429
+ len = 0
430
+ for v in obj.instance_methods(false).sort
431
+ len += v.size + 1
432
+ if len > 70
433
+ len = v.size + 1
434
+ print "\n"
435
+ end
436
+ print "%s ", v
437
+ end
438
+ print "\n"
439
+ end
440
+ end
441
+ end
442
+
443
+ def display_context(c)
444
+ if c.thread == Thread.current
445
+ print "+"
446
+ else
447
+ print " "
448
+ end
449
+ print "%d ", c.thnum
450
+ print "%s\t", c.thread.inspect
451
+ last_frame = c.frames.first
452
+ if last_frame
453
+ print "%s:%d", last_frame.file, last_frame.line
454
+ end
455
+ print "\n"
456
+ end
457
+
458
+ def display_all_contexts
459
+ threads = Debugger.contexts.sort_by{|c| c.thnum}.each do |c|
460
+ display_context(c)
461
+ end
462
+ end
463
+
464
+ def get_context(thnum)
465
+ Debugger.contexts.find{|c| c.thnum == thnum}
466
+ end
467
+
468
+ def debug_thread_info(input)
469
+ case input
470
+ when /^l(?:ist)?/
471
+ display_all_contexts
472
+
473
+ when /^c(?:ur(?:rent)?)?$/
474
+ display_context(Debugger.current_context)
475
+
476
+ when /^(?:sw(?:itch)?\s+)?(\d+)/
477
+ c = get_context($1.to_i)
478
+ if c == Debugger.current_context
479
+ print "It's the current thread.\n"
480
+ else
481
+ display_context(c)
482
+ c.stop_next = 1
483
+ c.thread.run
484
+ return :cont
485
+ end
486
+
487
+ when /^stop\s+(\d+)/
488
+ c = get_context($1.to_i)
489
+ if c == Debugger.current_context
490
+ print "It's the current thread.\n"
491
+ elsif c.thread.stop?
492
+ print "Already stopped.\n"
493
+ else
494
+ display_context(c)
495
+ c.set_suspend
496
+ end
497
+
498
+ when /^resume\s+(\d+)/
499
+ c = get_context($1.to_i)
500
+ if c == Debugger.current_context
501
+ print "It's the current thread.\n"
502
+ elsif !c.thread.stop?
503
+ print "Already running."
504
+ else
505
+ display_context(c)
506
+ c.thread.run
507
+ end
508
+ end
509
+ end
510
+
511
+ def line_at(file, line)
512
+ lines = SCRIPT_LINES__[file]
513
+ if lines
514
+ return "\n" if lines == true
515
+ line = lines[line-1]
516
+ return "\n" unless line
517
+ return line.gsub(/^\s+/, '')
518
+ end
519
+ return "\n"
520
+ end
521
+
522
+ COMMANDS = {
523
+ 'break' => %{
524
+ b[reak]\tlist breakpoints
525
+ b[reak] [file|class(:|.)]<line|method> [if expr] -
526
+ set breakpoint to some position, (optionally) if expr == true
527
+ },
528
+ 'delete' => %{
529
+ del[ete][ nnn]\tdelete some or all breakpoints
530
+ },
531
+ 'catch' => %{
532
+ cat[ch]\t\t\tshow catchpoint
533
+ cat[ch] <an Exception>\tset catchpoint to an exception
534
+ },
535
+ 'display' => %{
536
+ disp[lay] <expression>\tadd expression into display expression list
537
+ },
538
+ 'undisplay' => %{
539
+ undisp[lay][ nnn]\tdelete one particular or all display expressions
540
+ },
541
+ 'cont' => %{
542
+ c[ont]\trun until program ends or hit breakpoint
543
+ },
544
+ 'run' => %{
545
+ r[un]\talias for cont
546
+ },
547
+ 'step' => %{
548
+ s[tep][ nnn]\tstep (into methods) one line or till line nnn
549
+ },
550
+ 'next' => %{
551
+ n[ext][ nnn]\tgo over one line or till line nnn
552
+ },
553
+ 'where' => %{
554
+ w[here]\tdisplay frames
555
+ },
556
+ 'frame' => %{
557
+ f[rame]\talias for where
558
+ },
559
+ 'list' => %{
560
+ l[ist][ (-|nn-mm)]\tlist program, '-' list backwards, nn-mm list given lines
561
+ },
562
+ 'up' => %{
563
+ up[ nn]\tmove to higher frame
564
+ },
565
+ 'down' => %{
566
+ down[ nn]\tmove to lower frame
567
+ },
568
+ 'finish' => %{
569
+ fin[ish]\treturn to outer frame
570
+ },
571
+ 'quit' => %{
572
+ q[uit]\texit from debugger
573
+ },
574
+ 'trace' => %{
575
+ tr[ace] (on|off)\tset trace mode of current thread
576
+ tr[ace] (on|off) all\tset trace mode of all threads
577
+ },
578
+ 'var' => %{
579
+ v[ar] g[lobal]\t\t\tshow global variables
580
+ v[ar] l[ocal]\t\t\tshow local variables
581
+ v[ar] i[nstance] <object>\tshow instance variables of object
582
+ v[ar] c[onst] <object>\t\tshow constants of object
583
+ },
584
+ 'method' => %{
585
+ m[ethod] i[nstance] <obj>\tshow methods of object
586
+ m[ethod] <class|module>\t\tshow instance methods of class or module
587
+ },
588
+ 'thread' => %{
589
+ th[read] l[ist]\t\t\tlist all threads
590
+ th[read] c[ur[rent]]\t\tshow current thread
591
+ th[read] [sw[itch]] <nnn>\tswitch thread context to nnn
592
+ th[read] stop <nnn>\t\tstop thread nnn
593
+ th[read] resume <nnn>\t\tresume thread nnn
594
+ },
595
+ 'p' => %{
596
+ p expression\tevaluate expression and print its value
597
+ },
598
+ 'eval' => %{
599
+ e[val] expression\tevaluate expression and print its value,
600
+ \t\t\talias for p
601
+ },
602
+ 'pp' => %{
603
+ pp expression\tevaluate expression and print its value
604
+ },
605
+ 'help' => %{
606
+ h[elp]\tprint this help
607
+ }
608
+ }
609
+
610
+ def debug_print_help(command)
611
+ print "ruby-debug help v.#{Debugger::VERSION}\n"
612
+ help = COMMANDS[command]
613
+ if help
614
+ print help.split("\n").map{|l| l.gsub(/^ +/, '')}.join("\n")
615
+ else
616
+ print "Available commands:\n"
617
+ require 'enumerator'
618
+ COMMANDS.keys.sort.enum_slice(12).each do |slice|
619
+ print slice.join(' ')
620
+ print "\n"
621
+ end
622
+ end
623
+ print "\n"
624
+ end
625
+
626
+ end
627
+ end