Introduction

gdb allows you to define your own commands which perform a sequence of actions. These can be useful for examining or dumping out complex data structures. This page has a few such commands which you might find useful.

Note that when dumping a large about of text, you may want to start gdb inside the script(1) command. Also, this gdb command turns off gdb's prompting for more output after every page of text:

set pagination off

Also if you're running gdb on one of the programs that uses the Boehm garbage collector, you probably want to let the signal used by the GC proceed unhindered. On Linux, the two signals normally used are SIGPWR and SIGXCPU, so in gdb you would use these commands.

handle SIGPWR pass nostop noprint
handle SIGXCPU pass nostop noprint

In other contexts the garbage collector might use different signals.

Dump a Table

This can be used to print out all the entries in an instance of the Table::Default class from the generics library.

define dumpTable
  set $table = $arg0
  set $bucket = 0
  while $bucket < (1 << $table->logBuckets)
    printf "bucket %d\n", $bucket
    set $elist = $table->buckets[$bucket]
    while $elist != 0
      print * $elist
      set $elist = $elist->tail
    end
    set $bucket = $bucket + 1
  end
end 

Note that this takes a pointer to a Table as its argument. So in a case like this:

(gdb) print dsidTable
$2 = (DirShortIdTable *) 0x5aa6310

You can pass the variable directly:

(gdb) dumpTable dsidTable
bucket 0
bucket 1
bucket 2
$827 = {tail = 0x773bfa0, key = {sid = 1519249933}, val = {sp = 270537073}}
$828 = {tail = 0x80990a0, key = {sid = 1416915778}, val = {sp = 514370065}}
$829 = {tail = 0x0, key = {sid = 1595247962}, val = {sp = 786996209}}
bucket 3
$830 = {tail = 0x0, key = {sid = 1939160287}, val = {sp = 121362745}}
bucket 4
...

However if the variable is an instance or a reference rather than a pointer you would need to take its address:

(gdb) dumpTable &vrtTable

Dump VDirChangeable blocks, entries

While they don't dump every piece of data you might be interested in, these can be useful for walking though a VDirChangeable block by block and entry by entry:

# Dump out useful information about a rep block

define dumpRepBlock
 set $moreOrBase = (*((unsigned char *) $arg0) >> 2) & 3
 if $moreOrBase == 1
  printf "more block = "
  x/1wx $arg0 + 1
 else
  printf "base block = "
  x/1wx $arg0 + 1
 end
 printf "firstEntry = "
 output ((Bit8 *) ($arg0 + 29))
 echo \n
end

# Dump out some useful information about a VDirChangeable entry

define dumpEntry
 if (*((unsigned char *) $arg0)) == 0xff
  echo endMark (go to next rep block)\n
 else
  printf "type = %d\n", (((*($arg0)) >> 4) & 0xf)
  set $sameAsBase = (*($arg0)) & 8
  if $sameAsBase
    printf "sameAsBase\n"
  end
  printf "value = "
  x/1wx $arg0 + 1
  set $hasEFP = (*($arg0)) & 4
  if $hasEFP
   set $arcLen = (*((unsigned char *) $arg0+25))
   printf "arcLen = %d\n", $arcLen
   if $arcLen > 0
    printf "arc = "
    output ((char *) ($arg0+26))
    echo \n
   end
   printf "nextEntry = "
   output ((Bit8 *) ($arg0 + $arcLen + 26))
   echo \n
  else
   set $arcLen = (*((unsigned char *) $arg0+9))
   printf "arcLen = %d\n", $arcLen
   if $arcLen > 0
    printf "arc = "
    output ((char *) ($arg0+10))
    echo \n
   end
   printf "nextEntry = "
   output ((Bit8 *) ($arg0 + $arcLen + 10))
   echo \n
  end
 end
end

# Get the next VDirChangeable entry

define nextEntry
 set $hasEFP = (*($arg0)) & 4
 if $hasEFP
  set $arcLen = (*((unsigned char *) $arg0+25))
  print ((Bit8 *) ($arg0 + $arcLen + 26))
 else
  set $arcLen = (*((unsigned char *) $arg0+9))
  print ((Bit8 *) ($arg0 + $arcLen + 10))
 end
end

Evaluator Context

In the evaluator code the "Context" type is used for bindings and variables. It's stored as a linked list, and if you want to find a particular entry it can be annoying.

If you have a debugger for a running process, you can use this to search for a particular entry:

define searchContext
  set $context = $arg0
  set $name = $arg1
  set $pair = $context.list
  while $pair != 0
    if strcmp($pair->first->name.s, $name) == 0
      print *($pair->first)
      set $pair = 0
    else
      set $pair = $pair->tail
    end
  end
end

Suppose you're stopped in ApplicationFromCache (in ApplyCache.C) and you want to search through the function arguments for the value of ./foo/bar. You could use searchContext to do this:

(gdb) searchContext argsCon "."
$92 = {name = {<Text> = {s = 0x2a95f85ea8 "."}, <No data fields>}, val = 0x2a98fa0e10}
(gdb) print ((Binding) $92.val)->elems
$93 = {list = 0x2a97c00730, last = 0x2a97c03080}
(gdb) searchContext $93 "foo"
$94 = {name = {<Text> = {s = 0x2a95f8e3b0 "foo"}, <No data fields>}, val = 0x2a98f93780}
(gdb) print ((Binding) $94.val)->elems
$95 = {list = 0x2a983ea990, last = 0x2a983ea830}
(gdb) searchContext $95 "expert"
$96 = {name = {<Text> = {s = 0x2a96ae2e68 "expert"}, <No data fields>}, val = 0x2a98f66cd0}

At this point $96.val is the pointer to the data structure for the value of ./foo/bar.

You can't use searchContext with a core dump though, since it calls strcmp. In that case you can still perform a search, but you'll need to know the pointer to the string you're looking for. Since Context elements use the Atom type, this isn't a huge burden. Here's an alternative that searches by string pointer:

define searchContext2
  set $context = $arg0
  set $name = $arg1
  set $pair = $context.list
  while $pair != 0
    if ($pair->first->name.s == $name)
      print *($pair->first)
    end
    set $pair = $pair->tail
  end
end

This simpler sequence will tell you the length of a Context:

define contextLength
  set $context = $arg0
  set $len = 0
  set $pair = $context.list
  while $pair != 0
    set $len = $len+1
    set $pair = $pair->tail
  end
  print $len
end

Here's an even simpler sequence to dump a context:

define dumpContext
  set $context = $arg0
  set $pair = $context.list
  while $pair != 0
    print *($pair->first)
    set $pair = $pair->tail
  end
end

PrefixTbl

The evaluator and cache use the PrefixTbl class for compressed encodings of multiple paths with shared prefixes. If you need to find a particular entry in a PrefixTbl, this can be used to search by arc in the table:

define searchPrefixTbl
  set $tbl = $arg0
  set $name = $arg1
  set $i = 0
  while $i < $tbl.numArcs
    if strcmp($tbl.arcArray[$i], $name) == 0
      printf "%d\n", $i
    end
   set $i = $i + 1
  end
end

So if you're looking for the path "./foo/bar" in the PrefixTbl "names.tbl" you could use:

searchPrefixTbl names.tbl "bar"

Note that this calls strcmp so it won't work with a core file.