# GDB macros for use with Quagga.
#
# Macros in this file are not daemon specific. E.g., OS or FRR library
# APIs.
#
# The macro file can be loaded with 'source <filename>'. They can then be
# called by the user. Macros that explore more complicated structs generally
# take pointer arguments.
#
# E.g.:
# 
# (gdb) source ~paul/code/frr/gdb/lib.txt
# (gdb) break bgp_packet.c:613
# Breakpoint 3 at 0x7fa883033a32: file bgp_packet.c, line 613.
# (gdb) cont
# ...
# (gdb) cont
# Breakpoint 3, bgp_write_packet (peer=0x7fa885199080) at bgp_packet.c:614
# 614                     if (CHECK_FLAG (adv->path->peer->cap,PEER_CAP_RESTART_RCV)
# (gdb) dump_prefix4  &adv->rn->p
# IPv4:10.1.1.0/24
# (gdb) dump_prefix  &adv->rn->p
# IPv4:10.1.1.0/24
#


define def_ntohs
 set $data = (char *)$arg0
 set $i = 0
 
 set $_  = $data[$i++] << 8
 set $_ += $data[$i++]
end
document def_ntohs
Read a 2-byte short at the given pointed to area as big-endian and 
return it in $_

Argument: Pointer to a 2-byte, big-endian short word.
Returns: Integer value of that word in $_
end

define def_ntohl
 set $data = (char *)$arg0
 set $i = 0
 
 set $_  = $data[$i++] << 24
 set $_ += $data[$i++] << 16
 set $_ += $data[$i++] << 8
 set $_ += $data[$i++]
end
document def_ntohl
Read a 4-byte integer at the given pointed to area as big-endian and 
return it in $_

Argument: Pointer to a big-endian 4-byte word.
Returns: Integer value of that word in $_
end

# NB: This is in more complicated iterative form, rather than more
# conventional and simpler recursive form, because GDB has a recursion limit
# on macro calls (I think).
define walk_route_table_next
  # callee saves
  set $_top = $top
  set $_node = $node
  set $_prevl = $prevl
  
  set $top = (struct route_node *)$arg0
  set $node = (struct route_node *)$arg1
  set $prevl = $node
  
  # first try left
  #echo try left\n
  set $node = $prevl->link[0]
  
  # otherwise try right
  if ($node == 0)
    #echo left null, try right\n
    set $node = $prevl->link[1]
  end
  
  # otherwise go up, till we find the first right that
  # we havn't been to yet
  if ($node == 0)
    set $node = $prevl
    while ($node != $top)
       #echo right null, try up and right\n
       
       set $prevl = $node
       set $parent = $node->parent
       set $node = $parent->link[1]
       
       if ($node != 0 && $node != $prevl)
         #echo found node \n
         loop_break
       end
       
       #echo go up\n
       set $node = $parent       
    end
  end
  
  #printf "next node: 0x%x\n", $node
  
  set $_ = $node
  
  set $top = $_top
  set $node = $_node
  set $prevl = $_prevl
end
document walk_route_table_next
Return the next node to visit in the given route_table (or subset of) and
the given current node.

Arguments:
1st: (struct route_node *) to the top of the route_table to walk
2nd: (struct route_node *) to the current node

Returns: The (struct route_node *) for the next to visit in $_
end

define walk_route_table
  set $_visited = $visited
  set $_node = $node
  set $top = $_top
  
  set $node = (struct route_node *)$arg0
  set $top = (struct route_node *)$arg0
  set $visited = 0
  
  while ($node != 0)
    printf "Node: 0x%x", $node

    if ($node->info != 0)
      printf "\tinfo: 0x%x", $node->info
      set $visited = $visited + 1
    end
    
    printf "\n"
    
    walk_route_table_next $top $node
    set $node = $_
    
    # we've gotten back to the top, finish
    if ($node == $top)
      set $node = 0
    end
  end
  printf "Visited: %u\n", $visited
  
  set $top = $_top
  set $visited = $_visited
  set $node = $_node
end

document walk_route_table
Walk through a routing table (or subset thereof) and dump all the non-null
(struct route_node *)->info pointers.

Argument: A lib/hread.h::(struct route_node *) pointing to the route_node
under which all data should be dumped
end

define dump_timeval 
  set $tv = (struct timeval *)$arg0
  set $day = 3600*24
  
  if $tv->tv_sec > $day
    printf "%d days, ", $tv->tv_sec / $day
  end
  if $tv->tv_sec > 3600
    printf "%dh", $tv->tv_sec / 3600
  end
  if ($tv->tv_sec % 3600) > 60
    printf "%dm", ($tv->tv_sec % 3600) / 60
  end
  printf "%d", $tv->tv_sec % 3600 % 60
  if $tv->tv_usec != 0
    printf ".%06d", $tv->tv_usec
  end
  printf "s"
end
document dump_timeval
Human readable dump of a (struct timeval *) argument
end

define dump_s_addr
  set $addr = (char *)$arg0
  
  printf "%d.%d.%d.%d", $addr[0], $addr[1], $addr[2], $addr[3]
end

define dump_s6_addr
  set $a6 = (char *)$arg0
  set $field = 0
  
  while ($field < 16)
    set $i1 = $field++
    set $i2 = $field++
    
    printf "%x%x", $a6[$i1], $a6[$i2]
    
    if ($field > 2 && ($field % 4 == 0))
      printf ":"
    end
  end
end
document dump_s6_addr
Interpret the memory starting at given address as an IPv6 s6_addr and
print in human readable form.
end

define dump_prefix4
  set $p = (struct prefix *) $arg0
  echo IPv4:
  dump_s_addr &($p->u.prefix4)
  printf "/%d\n", $p->prefixlen
end
document dump_prefix4
Textual dump of a (struct prefix4 *) argument.
end

define dump_prefix6
  set $p = (struct prefix *) $arg0
  echo IPv6:
  dump_s6_addr &($p->u.prefix6)
  printf "/%d\n", $p->prefixlen
end
document dump_prefix6
Textual dump of a (struct prefix6 *) argument.
end

define dump_prefix
  set $p = $arg0
  
  if ($p->family == 2)
    dump_prefix4 $p
  end
  if ($p->family == 10)
    dump_prefix6 $p
  end
end
document dump_prefix
Human readable dump of a (struct prefix *) argument.
end

define rn_next_down
  set $node = $arg0
  while ($node != 0)
    print/x $node
    if ($node->link[0] != 0)
      set $node = $node->link[0]
    else
      set $node = $node->link[1]
    end
  end
end

document rn_next_down
Walk left-down a given route table, dumping locations of route_nodes

Argument: A single (struct route_node *).
end

define rn_next_up
  set $top = (struct route_node *)$arg0
  set $node = (struct route_node *)$arg1
  
  while ($node != $top)
    echo walk up\n
    
    set $prevl = $node
    set $parent = $node->parent
    set $node = $parent->link[1]
    
    if ($node != 0 && $node != $prevl)
      echo found a node\n
      loop_break
    end
    
    echo going up\n
    set $node = $parent
  end
  output/x $node
  echo \n
end

document rn_next_up
Walk up-and-right from the given route_node to the next valid route_node
which is not the given "top" route_node

Arguments:
1st: A (struct route_node *) to the top of the route table.
2nd: The (struct route_node *) to walk up from
end

define mq_walk
  set $mg = (struct memgroup *)$arg0

  while ($mg)
     printf "showing active allocations in memory group %s\n", $mg->name
     set $mt = (struct memtype *)$mg->types
     while ($mt)
         printf "memstats: %s:%zu\n", $mt->name, $mt->n_alloc
         set $mt = $mt->next
     end
     set $mg = $mg->next
  end
end

document mq_walk
Walk the memory data structures to show what is holding memory.

Arguments:
1st: A (struct memgroup *) where to start the walk.  If you are not
     sure where to start pass it mg_first, which is a global DS for
     all memory allocated in FRR
end

define __darr_meta
  set $_ = ((struct darr_metadata *)$arg0) - 1
end
document __darr_meta
Store a pointer to the struct darr_metadata in $_ for the given dynamic array.

Argument: a pointer to a darr dynamic array.
Returns: pointer to the struct darr_metadata in $_.
end

define darr_meta
  __darr_meta $arg0
  p *$_
end
document darr_meta
Print the struct darr_metadata for the given dynamic array. Store the value
in $_ as well.

Argument: a pointer to a darr dynamic array.
Returns: pointer to the struct darr_metadata in $_.
end

define darr_len
  __darr_meta $arg0
  set $_ = $_->len
  p $_
end
document darr_len
Print the length of the given dynamic array, and store in $_.

Argument: a pointer to a darr dynamic array.
Returns: length of the array.
end

define darr_cap
  __darr_meta $arg0
  set $_ = $_->cap
  p $_
end
document darr_len
Print the capacity of the given dynamic array, and store in $_.

Argument: a pointer to a darr dynamic array.
Returns: capacity of the array.
end
