ruby-debug 0.1.2

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