ruby-debug193 0.0.1

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 (40) hide show
  1. data/AUTHORS +10 -0
  2. data/LICENSE +23 -0
  3. data/bin/rdebug +398 -0
  4. data/cli/ruby-debug.rb +173 -0
  5. data/cli/ruby-debug/command.rb +228 -0
  6. data/cli/ruby-debug/commands/breakpoints.rb +153 -0
  7. data/cli/ruby-debug/commands/catchpoint.rb +55 -0
  8. data/cli/ruby-debug/commands/condition.rb +49 -0
  9. data/cli/ruby-debug/commands/continue.rb +38 -0
  10. data/cli/ruby-debug/commands/control.rb +107 -0
  11. data/cli/ruby-debug/commands/display.rb +120 -0
  12. data/cli/ruby-debug/commands/edit.rb +48 -0
  13. data/cli/ruby-debug/commands/enable.rb +202 -0
  14. data/cli/ruby-debug/commands/eval.rb +176 -0
  15. data/cli/ruby-debug/commands/finish.rb +42 -0
  16. data/cli/ruby-debug/commands/frame.rb +301 -0
  17. data/cli/ruby-debug/commands/help.rb +56 -0
  18. data/cli/ruby-debug/commands/info.rb +469 -0
  19. data/cli/ruby-debug/commands/irb.rb +123 -0
  20. data/cli/ruby-debug/commands/jump.rb +66 -0
  21. data/cli/ruby-debug/commands/kill.rb +51 -0
  22. data/cli/ruby-debug/commands/list.rb +94 -0
  23. data/cli/ruby-debug/commands/method.rb +84 -0
  24. data/cli/ruby-debug/commands/quit.rb +39 -0
  25. data/cli/ruby-debug/commands/reload.rb +40 -0
  26. data/cli/ruby-debug/commands/save.rb +90 -0
  27. data/cli/ruby-debug/commands/set.rb +237 -0
  28. data/cli/ruby-debug/commands/show.rb +253 -0
  29. data/cli/ruby-debug/commands/source.rb +36 -0
  30. data/cli/ruby-debug/commands/stepping.rb +81 -0
  31. data/cli/ruby-debug/commands/threads.rb +189 -0
  32. data/cli/ruby-debug/commands/tmate.rb +36 -0
  33. data/cli/ruby-debug/commands/trace.rb +57 -0
  34. data/cli/ruby-debug/commands/variables.rb +199 -0
  35. data/cli/ruby-debug/debugger.rb +5 -0
  36. data/cli/ruby-debug/helper.rb +69 -0
  37. data/cli/ruby-debug/interface.rb +232 -0
  38. data/cli/ruby-debug/processor.rb +474 -0
  39. data/rdbg.rb +33 -0
  40. metadata +144 -0
@@ -0,0 +1,56 @@
1
+ module Debugger
2
+
3
+ # Implements debugger "help" command.
4
+ class HelpCommand < Command
5
+ self.allow_in_control = true
6
+
7
+ def regexp
8
+ /^\s* h(?:elp)? (?:\s+(.+))? $/x
9
+ end
10
+
11
+ def execute
12
+ if @match[1]
13
+ args = @match[1].split
14
+ cmds = @state.commands.select do |cmd|
15
+ [cmd.help_command].flatten.include?(args[0])
16
+ end
17
+ else
18
+ args = @match[1]
19
+ cmds = []
20
+ end
21
+ unless cmds.empty?
22
+ help = cmds.map{ |cmd| cmd.help(args) }.join
23
+ help = help.split("\n").map{|l| l.gsub(/^ +/, '')}
24
+ help.shift if help.first && help.first.empty?
25
+ help.pop if help.last && help.last.empty?
26
+ print help.join("\n")
27
+ else
28
+ if args and args[0]
29
+ errmsg "Undefined command: \"#{args[0]}\". Try \"help\"."
30
+ else
31
+ print "ruby-debug help v#{Debugger::VERSION}\n" unless
32
+ self.class.settings[:debuggertesting]
33
+ print "Type 'help <command-name>' for help on a specific command\n\n"
34
+ print "Available commands:\n"
35
+ cmds = @state.commands.map{ |cmd| cmd.help_command }
36
+ cmds = cmds.flatten.uniq.sort
37
+ print columnize(cmds, self.class.settings[:width])
38
+ end
39
+ end
40
+ print "\n"
41
+ end
42
+
43
+ class << self
44
+ def help_command
45
+ 'help'
46
+ end
47
+
48
+ def help(cmd)
49
+ %{
50
+ h[elp]\t\tprint this help
51
+ h[elp] command\tprint help on command
52
+ }
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,469 @@
1
+ module Debugger
2
+ module InfoFunctions # :nodoc:
3
+ def info_catch(*args)
4
+ unless @state.context
5
+ print "No frame selected.\n"
6
+ return
7
+ end
8
+ if Debugger.catchpoints and not Debugger.catchpoints.empty?
9
+ # FIXME: show whether Exception is valid or not
10
+ # print "Exception: is_a?(Class)\n"
11
+ Debugger.catchpoints.each do |exception, hits|
12
+ # print "#{exception}: #{exception.is_a?(Class)}\n"
13
+ print "#{exception}\n"
14
+ end
15
+ else
16
+ print "No exceptions set to be caught.\n"
17
+ end
18
+ end
19
+ end
20
+
21
+ # Implements debugger "info" command.
22
+ class InfoCommand < Command
23
+ self.allow_in_control = true
24
+ Subcommands =
25
+ [
26
+ ['args', 1, 'Argument variables of current stack frame'],
27
+ ['breakpoints', 1, 'Status of user-settable breakpoints',
28
+ 'Without argument, list info about all breakpoints. With an
29
+ integer argument, list info on that breakpoint.'],
30
+ ['catch', 3, 'Exceptions that can be caught in the current stack frame'],
31
+ ['display', 2, 'Expressions to display when program stops'],
32
+ ['file', 4, 'Info about a particular file read in',
33
+ '
34
+ After the file name is supplied, you can list file attributes that
35
+ you wish to see.
36
+
37
+ Attributes include: "all", "basic", "breakpoint", "lines", "mtime", "path"
38
+ and "sha1".'],
39
+ ['files', 5, 'File names and timestamps of files read in'],
40
+ ['global_variables', 2, 'Global variables'],
41
+ ['instance_variables', 2,
42
+ 'Instance variables of the current stack frame'],
43
+ ['line', 2,
44
+ 'Line number and file name of current position in source file'],
45
+ ['locals', 2, 'Local variables of the current stack frame'],
46
+ ['program', 2, 'Execution status of the program'],
47
+ ['stack', 2, 'Backtrace of the stack'],
48
+ ['thread', 6, 'List info about thread NUM', '
49
+ If no thread number is given, we list info for all threads. \'terse\' and \'verbose\'
50
+ options are possible. If terse, just give summary thread name information. See
51
+ "help info threads" for more detail about this summary information.
52
+
53
+ If \'verbose\' appended to the end of the command, then the entire
54
+ stack trace is given for each thread.'],
55
+ ['threads', 7, 'information of currently-known threads', '
56
+ This information includes whether the thread is current (+), if it is
57
+ suspended ($), or ignored (!). The thread number and the top stack
58
+ item. If \'verbose\' is given then the entire stack frame is shown.'],
59
+ ['variables', 1,
60
+ 'Local and instance variables of the current stack frame']
61
+ ].map do |name, min, short_help, long_help|
62
+ SubcmdStruct.new(name, min, short_help, long_help)
63
+ end unless defined?(Subcommands)
64
+
65
+ InfoFileSubcommands =
66
+ [
67
+ ['all', 1,
68
+ 'All file information available - breakpoints, lines, mtime, path, and sha1'],
69
+ ['basic', 2,
70
+ 'basic information - path, number of lines'],
71
+ ['breakpoints', 2, 'Show trace line numbers',
72
+ 'These are the line number where a breakpoint can be set.'],
73
+ ['lines', 1, 'Show number of lines in the file'],
74
+ ['mtime', 1, 'Show modification time of file'],
75
+ ['path', 4, 'Show full file path name for file'],
76
+ ['sha1', 1, 'Show SHA1 hash of contents of the file']
77
+ ].map do |name, min, short_help, long_help|
78
+ SubcmdStruct.new(name, min, short_help, long_help)
79
+ end unless defined?(InfoFileSubcommands)
80
+
81
+ InfoThreadSubcommands =
82
+ [
83
+ ['terse', 1, 'summary information'],
84
+ ['verbose', 1, 'summary information and stack frame info'],
85
+ ].map do |name, min, short_help, long_help|
86
+ SubcmdStruct.new(name, min, short_help, long_help)
87
+ end unless defined?(InfoThreadSubcommands)
88
+
89
+ def regexp
90
+ /^\s* i(?:nfo)? (?:\s+(.*))?$/ix
91
+ end
92
+
93
+ def execute
94
+ if !@match[1]
95
+ errmsg "\"info\" must be followed by the name of an info command:\n"
96
+ print "List of info subcommands:\n\n"
97
+ for subcmd in Subcommands do
98
+ print "info #{subcmd.name} -- #{subcmd.short_help}\n"
99
+ end
100
+ else
101
+ args = @match[1].split(/[ \t]+/)
102
+ param = args.shift
103
+ subcmd = find(Subcommands, param)
104
+ if subcmd
105
+ send("info_#{subcmd.name}", *args)
106
+ else
107
+ errmsg "Unknown info command #{param}\n"
108
+ end
109
+ end
110
+ end
111
+
112
+ def info_args(*args)
113
+ unless @state.context
114
+ print "No frame selected.\n"
115
+ return
116
+ end
117
+ locals = @state.context.frame_locals(@state.frame_pos)
118
+ args = @state.context.frame_args(@state.frame_pos)
119
+ args.each do |name|
120
+ s = "#{name} = #{locals[name].inspect}"
121
+ if s.size > self.class.settings[:width]
122
+ s[self.class.settings[:width]-3 .. -1] = "..."
123
+ end
124
+ print "#{s}\n"
125
+ end
126
+ end
127
+
128
+ def info_breakpoints(*args)
129
+ unless @state.context
130
+ print "info breakpoints not available here.\n"
131
+ return
132
+ end
133
+ unless Debugger.breakpoints.empty?
134
+ brkpts = Debugger.breakpoints.sort_by{|b| b.id}
135
+ unless args.empty?
136
+ a = args.map{|a| a.to_i}
137
+ brkpts = brkpts.select{|b| a.member?(b.id)}
138
+ if brkpts.empty?
139
+ errmsg "No breakpoints found among list given.\n"
140
+ return
141
+ end
142
+ end
143
+ print "Num Enb What\n"
144
+ brkpts.each do |b|
145
+ if b.expr.nil?
146
+ print "%3d %s at %s:%s\n",
147
+ b.id, (b.enabled? ? 'y' : 'n'), b.source, b.pos
148
+ else
149
+ print "%3d %s at %s:%s if %s\n",
150
+ b.id, (b.enabled? ? 'y' : 'n'), b.source, b.pos, b.expr
151
+ end
152
+ hits = b.hit_count
153
+ if hits > 0
154
+ s = (hits > 1) ? 's' : ''
155
+ print "\tbreakpoint already hit #{hits} time#{s}\n"
156
+ end
157
+ end
158
+ else
159
+ print "No breakpoints.\n"
160
+ end
161
+ end
162
+
163
+ def info_display(*args)
164
+ unless @state.context
165
+ print "info display not available here.\n"
166
+ return
167
+ end
168
+ if @state.display.find{|d| d[0]}
169
+ print "Auto-display expressions now in effect:\n"
170
+ print "Num Enb Expression\n"
171
+ n = 1
172
+ for d in @state.display
173
+ print "%3d: %s %s\n", n, (d[0] ? 'y' : 'n'), d[1] if
174
+ d[0] != nil
175
+ n += 1
176
+ end
177
+ else
178
+ print "There are no auto-display expressions now.\n"
179
+ end
180
+ end
181
+
182
+ def info_file(*args)
183
+ unless args[0]
184
+ info_files
185
+ return
186
+ end
187
+ file = args[0]
188
+ param = args[1]
189
+
190
+ param = 'basic' unless param
191
+ subcmd = find(InfoFileSubcommands, param)
192
+ unless subcmd
193
+ errmsg "Invalid parameter #{param}\n"
194
+ return
195
+ end
196
+
197
+ unless LineCache::cached?(file)
198
+ unless LineCache::cached_script?(file)
199
+ print "File #{file} is not cached\n"
200
+ return
201
+ end
202
+ LineCache::cache(file, Command.settings[:reload_source_on_change])
203
+ end
204
+
205
+ print "File %s", file
206
+ path = LineCache.path(file)
207
+ if %w(all basic path).member?(subcmd.name) and path != file
208
+ print " - %s\n", path
209
+ else
210
+ print "\n"
211
+ end
212
+
213
+ if %w(all basic lines).member?(subcmd.name)
214
+ lines = LineCache.size(file)
215
+ print "\t %d lines\n", lines if lines
216
+ end
217
+
218
+ if %w(all breakpoints).member?(subcmd.name)
219
+ breakpoints = LineCache.trace_line_numbers(file)
220
+ if breakpoints
221
+ print "\tbreakpoint line numbers:\n"
222
+ print columnize(breakpoints.to_a.sort, self.class.settings[:width])
223
+ end
224
+ end
225
+
226
+ if %w(all mtime).member?(subcmd.name)
227
+ stat = LineCache.stat(file)
228
+ print "\t%s\n", stat.mtime if stat
229
+ end
230
+ if %w(all sha1).member?(subcmd.name)
231
+ print "\t%s\n", LineCache.sha1(file)
232
+ end
233
+ end
234
+
235
+ def info_files(*args)
236
+ files = LineCache::cached_files
237
+ files += SCRIPT_LINES__.keys unless 'stat' == args[0]
238
+ files.uniq.sort.each do |file|
239
+ stat = LineCache::stat(file)
240
+ path = LineCache::path(file)
241
+ print "File %s", file
242
+ if path and path != file
243
+ print " - %s\n", path
244
+ else
245
+ print "\n"
246
+ end
247
+ print "\t%s\n", stat.mtime if stat
248
+ end
249
+ end
250
+
251
+ def info_instance_variables(*args)
252
+ unless @state.context
253
+ print "info instance_variables not available here.\n"
254
+ return
255
+ end
256
+ obj = debug_eval('self')
257
+ var_list(obj.instance_variables)
258
+ end
259
+
260
+ def info_line(*args)
261
+ unless @state.context
262
+ errmsg "info line not available here.\n"
263
+ return
264
+ end
265
+ print "Line %d of \"%s\"\n", @state.line, @state.file
266
+ end
267
+
268
+ def info_locals(*args)
269
+ unless @state.context
270
+ errmsg "info line not available here.\n"
271
+ return
272
+ end
273
+ locals = @state.context.frame_locals(@state.frame_pos)
274
+ locals.keys.sort.each do |name|
275
+ ### FIXME: make a common routine
276
+ begin
277
+ s = "#{name} = #{locals[name].inspect}"
278
+ rescue
279
+ begin
280
+ s = "#{name} = #{locals[name].to_s}"
281
+ rescue
282
+ s = "*Error in evaluation*"
283
+ end
284
+ end
285
+ if s.size > self.class.settings[:width]
286
+ s[self.class.settings[:width]-3 .. -1] = "..."
287
+ end
288
+ print "#{s}\n"
289
+ end
290
+ end
291
+
292
+ def info_program(*args)
293
+ if not @state.context
294
+ print "The program being debugged is not being run.\n"
295
+ return
296
+ elsif @state.context.dead?
297
+ print "The program crashed.\n"
298
+ if Debugger.last_exception
299
+ print("Exception: #{Debugger.last_exception.inspect}\n")
300
+ end
301
+ return
302
+ end
303
+
304
+ print "Program stopped. "
305
+ case @state.context.stop_reason
306
+ when :step
307
+ print "It stopped after stepping, next'ing or initial start.\n"
308
+ when :breakpoint
309
+ print("It stopped at a breakpoint.\n")
310
+ when :catchpoint
311
+ print("It stopped at a catchpoint.\n")
312
+ when :catchpoint
313
+ print("It stopped at a catchpoint.\n")
314
+ else
315
+ print "unknown reason: %s\n" % @state.context.stop_reason.to_s
316
+ end
317
+ end
318
+
319
+ def info_stack(*args)
320
+ if not @state.context
321
+ errmsg "info stack not available here.\n"
322
+ return
323
+ end
324
+ (0...@state.context.stack_size).each do |idx|
325
+ if idx == @state.frame_pos
326
+ print "--> "
327
+ else
328
+ print " "
329
+ end
330
+ print_frame(idx)
331
+ end
332
+ end
333
+
334
+ def info_thread_preamble(arg)
335
+ if not @state.context
336
+ errmsg "info threads not available here.\n"
337
+ return false, false
338
+ end
339
+ verbose = if arg
340
+ subcmd = find(InfoThreadSubcommands, arg)
341
+ unless subcmd
342
+ errmsg "'terse' or 'verbose' expected. Got '#{arg}'\n"
343
+ return false, false
344
+ end
345
+ 'verbose' == subcmd.name
346
+ else
347
+ false
348
+ end
349
+ return true, verbose
350
+ end
351
+ private :info_thread_preamble
352
+
353
+ def info_threads(*args)
354
+ ok, verbose = info_thread_preamble(args[0])
355
+ return unless ok
356
+ threads = Debugger.contexts.sort_by{|c| c.thnum}.each do |c|
357
+ display_context(c, !verbose)
358
+ if verbose and not c.ignored?
359
+ (0...c.stack_size).each do |idx|
360
+ print "\t"
361
+ print_frame(idx, false, c)
362
+ end
363
+ end
364
+ end
365
+ end
366
+
367
+ def info_thread(*args)
368
+ unless args[0]
369
+ info_threads(args[0])
370
+ return
371
+ end
372
+ ok, verbose = info_thread_preamble(args[1])
373
+ return unless ok
374
+ c = parse_thread_num("info thread" , args[0])
375
+ return unless c
376
+ display_context(c, !verbose)
377
+ if verbose and not c.ignored?
378
+ (0...c.stack_size).each do |idx|
379
+ print "\t"
380
+ print_frame(idx, false, c)
381
+ end
382
+ end
383
+ end
384
+
385
+ def info_global_variables(*args)
386
+ unless @state.context
387
+ errmsg "info global_variables not available here.\n"
388
+ return
389
+ end
390
+ var_list(global_variables)
391
+ end
392
+
393
+ def info_variables(*args)
394
+ if not @state.context
395
+ errmsg "info variables not available here.\n"
396
+ return
397
+ end
398
+ obj = debug_eval('self')
399
+ locals = @state.context.frame_locals(@state.frame_pos)
400
+ locals['self'] = @state.context.frame_self(@state.frame_pos)
401
+ locals.keys.sort.each do |name|
402
+ next if name =~ /^__dbg_/ # skip debugger pollution
403
+ ### FIXME: make a common routine
404
+ begin
405
+ s = "#{name} = #{locals[name].inspect}"
406
+ rescue
407
+ begin
408
+ s = "#{name} = #{locals[name].to_s}"
409
+ rescue
410
+ s = "#{name} = *Error in evaluation*"
411
+ end
412
+ end
413
+ if s.size > self.class.settings[:width]
414
+ s[self.class.settings[:width]-3 .. -1] = "..."
415
+ end
416
+ s.gsub!('%', '%%') # protect against printf format strings
417
+ print "#{s}\n"
418
+ end
419
+ var_list(obj.instance_variables, obj.instance_eval{binding()})
420
+ var_class_self
421
+ end
422
+
423
+ class << self
424
+ def help_command
425
+ 'info'
426
+ end
427
+
428
+ def help(args)
429
+ if args[1]
430
+ s = args[1]
431
+ subcmd = Subcommands.find do |try_subcmd|
432
+ (s.size >= try_subcmd.min) and
433
+ (try_subcmd.name[0..s.size-1] == s)
434
+ end
435
+ if subcmd
436
+ str = subcmd.short_help + '.'
437
+ if 'file' == subcmd.name and args[2]
438
+ s = args[2]
439
+ subsubcmd = InfoFileSubcommands.find do |try_subcmd|
440
+ (s.size >= try_subcmd.min) and
441
+ (try_subcmd.name[0..s.size-1] == s)
442
+ end
443
+ if subsubcmd
444
+ str += "\n" + subsubcmd.short_help + '.'
445
+ else
446
+ str += "\nInvalid file attribute #{args[2]}."
447
+ end
448
+ else
449
+ str += "\n" + subcmd.long_help if subcmd.long_help
450
+ end
451
+ return str
452
+ else
453
+ return "Invalid 'info' subcommand '#{args[1]}'."
454
+ end
455
+ end
456
+ s = %{
457
+ Generic command for showing things about the program being debugged.
458
+ --
459
+ List of info subcommands:
460
+ --
461
+ }
462
+ for subcmd in Subcommands do
463
+ s += "info #{subcmd.name} -- #{subcmd.short_help}\n"
464
+ end
465
+ return s
466
+ end
467
+ end
468
+ end
469
+ end