Sample debug session

rocky edited this page Nov 7, 2010 · 23 revisions

The below is a sample trepan session (with bugs and misfeatures included) to show what’s available at this early stage.

First here’s the program /tmp/gcd.rb:

def gcd(a, b)
  # Make: a <= b
  if a > b
    a, b = [b, a]

  return nil if a >= 0

  if a == 1 or b-a == 0
    return a
  return gcd(b-a, a)

a, b = ARGV[0..1].map {|arg| arg.to_i}  # line 18
puts "The GCD of %d and %d is %d" % [a, b, gcd(a, b)]

Now let us run that…

$ ./bin/trepan tmp/gcd.rb 3 5
-- (/tmp/gcd.rb:4)
def gcd(a, b)
(trepan): info program
Program stop event: line; PC offset 2 of instruction sequence <top (tmp/gcd.rb)>

In an unmodified Ruby 1.9.2 the instruction name is the less helpful name top (required). Down the line I need to create a unique object-id-like name for the instruction sequence so that it can be referred to where needed.

The debugger command step (“step into”) goes to next event set via the debugger command set events . step+ steps to a different line. step< will step to the next call (or c-call if that is in the event set)

The “--” in “-- (temp/gcd.rb 3 5)” above, indicates we are stopped at a line event which is also shown in more detail when I run info program.

(trepan): step
-- (/tmp/gcd.rb:18)
a, b = ARGV[0..1].map {|arg| arg.to_i}
(trepan): step
-- (/tmp/gcd.rb:19)
puts "The GCD of %d and %d is %d" % [a, b, gcd(a, b)]

There is extensive on-line help. Below we just show the available commands and subcommands for show

(trepan): help *
All command names:
  alias        directory      finish    macro      reload     stepi    
  backtrace    disable        frame     next       restart    unalias  
  break        disassemble    help      nocache    save       undisplay
  condition    display        info      pr         set        up       
  continue     down           irb       ps         show   
  debug        enable         kill      quit       source 
  delete       exit           list      raise      step   
(trepan): help d.*
Command names matching /^d.*/:
  debug    delete    directory    disable    disassemble    display    down
(trepan): help show *
List of subcommands for command 'show':
  alias    auto        debug        events       macro    trace
  args     basename    different    hidelevel    max  
(trepan): show events
Trace events we may stop on:
	brkpt, call, class, end, insn, line, raise, return
(trepan): set events call return line raise
Trace events we may stop on:
	call, line, raise, return

Above we encountered the 2-character icons, -- which indicates a line event. You an find a list of event names and icons in Event Icons. Below we will see new one, -> for a call event.

(trepan): s
-> (./tmp/gcd.rb:4)
(trepan): where 
--> #0 METHOD Object#gcd(a, b) in file ./tmp/gcd.rb at line 4
    #1 TOP Object#<top (required)> in file ./tmp/gcd.rb at line 19
(trepan): [a, b]
[a, b]

Breakpoints are a bit different than ruby-debug. The main difference is that a breakpoint is associated with a particular VM instruction offset. The upside of this is that one can be very precise in specifying where to stop or where you are stopped. The downside is that the code you want to stop at needs to exist beforehand as Ruby VM instructions. Later on, I’ll probably have to add a symbolic breakpoint which is more or less how it is done in ruby-debug.

(trepan): break 10
Breakpoint 1 set at line 10
	in file /tmp/gcd.rb,
	VM offset 24 of instruction sequence gcd.

We tell you where the breakpoint has been set, VM offset 24 here. If you know the VM-instruction offset you can use that instead of instead of a line number by prefacing the number with an ‘O’; For example instead of ‘break 10’ above, ‘break O24’.

Recall that when I ran help * , there was a disassemble command shown. I’ll use that. And note that I am just disassembling the function I’m currently in.

(trepan): disassemble
== disasm: <RubyVM::InstructionSequence:gcd@/tmp/gcd.rb>
local table (size: 3, argc: 2 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 3] a<Arg>     [ 2] b<Arg>     
     0000 trace            8                                               (   4)
 --> 0002 trace            1                                               (   6)
     0004 getlocal         a
     0006 getlocal         b
     0008 opt_gt           <ic>
     0010 branchunless     22
     0012 trace            1                                               (   7)
     0014 getlocal         b
     0016 getlocal         a
     0018 setlocal         b
     0020 setlocal         a
     0022 trace            1                                               (  10)
B    0024 getlocal         a
     0026 putobject        0

Above the —> indicates VM offset the VM program counter will run next. A ‘B’ marks where a breakpoint is set. At the assembly language level it is not known whether a breakpoint is temporary, perminant or enabled or what breakpoint number we have at that point.

However in source listings via the list command, breakpoint status is shown:

(trepan): list
  4  ->	def gcd(a, b)
  5    	  # Make: a <= b
  6    	  if a > b
  7    	    a, b = [b, a]
  8    	  end
 10 B01	  return nil if a <= 0
 12    	  if a == 1 or b-a == 0
 13    	    return a
 14    	  end
(trepan): disable 1
(trepan): list 4
  1    	#!/usr/bin/env ruby
  3    	# GCD. We assume positive numbers
  4  ->	def gcd(a, b)
  5    	  # Make: a <= b
  6    	  if a > b
  7    	    a, b = [b, a]
  8    	  end
 10 b01	  return nil if a <= 0

And finally we show restarting execution and quitting.

(trepan): R
Restart args:
	["/usr/local/bin/ruby", "./bin/trepan", "tmp/gcd.rb", "3", "5" ] 
Restart (exec)? (N/y) 
-- (/tmp/gcd.rb:4)
def gcd(a, b)
(trepan): exit
Really quit? (N/y) Y

Use q! to quit without prompting.