Back to TILs

C++ gdb user defined commands

Date: 2023-02-26Last modified: 2024-02-26

Table of contents

Introduction

Often it is tedious to debug complex data structures. Debugging a linked list often requires printing a node by dereferencing the pointer to its struct, then accessing the next node and doing the same to it. This can become tedious and messy, resulting in commands like:

(gdb) print *(curr)
(gdb) print *(curr->next)
(gdb) print *(curr->next->next)
...

However, a command to print out a linked list can be created and placed inside the .gdbinit file.

Define a command

You can create a user defined command in the .gdbinit file:

define <command>
    <code>
end

Document a command

You can document a command in the .gdbinit file:

document <command>
    <information about the command>
end

This information appears when the help feature is used:

(gdb) help <command>
<information about the command>

Command parameters

When a user defined command is called in GBD, arguments can be passed in:

(gdb) <command> <arg0> <arg1> <arg2> ...

The number of arguments, and the arguments themselves can be referenced inside the command definition using the following variables:

$argc
$arg0
$arg1
$arg2
...

Example of user-defined command

To create a user-defined command, we use the GDB command define, and give it a command name, which in our example is bugreport followed by a set of GDB commands that you want to execute or capture the output from.

(gdb) define bugreport
> set pagination off
> thread apply all backtrace full
> shell uname -a
> end
(gdb)

To save the output to a file:

(gdb) define bugreport
> set pagination off
> set logging file /tmp/bugreport.txt
> set logging on
> thread apply all backtrace full
> shell uname -a
> set logging off
> end
(gdb)

Run it and display the file contents.

(gdb) bugreport
(gdb) shell cat /tmp/bugreport.txt
struct node {
  int          data;
  struct node *next;
};

struct node *create_node( int data );
struct node *create_list( int length );
void         print_list( struct node *list );

int main( void )
{
  struct node *list1 = create_list( 7 );
  print_list( list1 );

  return 0;
}

struct node *create_node( int data )
{
  struct node *newNode = (struct node *)malloc( sizeof( struct node ) );
  assert( newNode != NULL );
  newNode->data = data;
  newNode->next = NULL;
  return newNode;
}

struct node *create_list( int length )
{
  struct node *head = NULL;
  if( length > 0 ) {
    head              = create_node( 0 );
    int          i    = 1;
    struct node *curr = head;
    while( i < length ) {
      curr->next = create_node( i );
      curr       = curr->next;
      i++;
    }
  }
  return head;
}

void print_list( struct node *list )
{
  struct node *curr = list;

  while( curr != NULL ) {
    printf( "%d->", curr->data );
    curr = curr->next;
  }
  printf( "X\n" );
}

GDB commands

set verbose off
set pagination off

define p_generic_list
  set var $n = $arg0
  while $n
    print *($n)
    set var $n = $n->next
  end
end

document p_generic_list
        p_generic_list LIST_HEAD_POINTER
        Print all the fields of the nodes in the linked list pointed to by LIST_HEAD_POINTER. Assumes there is a next field in the struct.
end

define indentby
    printf "\n"
    set $i_$arg0 = $arg0
    while $i_$arg0 > 10
        set $i_$arg0 = $i_$arg0 - 1
        printf "%c", ' '
    end
end

start

set logging file output/gdb_user_defined_commands.gdb
set logging overwrite on
set logging on

# struct node *list1 = create_list( 7 );
next
p_generic_list list1

# See document p_generic_list
help p_generic_list

# print_list( list1 );
next

continue
quit

# vim: ft=gdb

GDB output

142	  print_list( list1 );
$1 = {
  data = 0,
  next = 0x555555b02230
}
$2 = {
  data = 1,
  next = 0x555555b00090
}
$3 = {
  data = 2,
  next = 0x555555b000b0
}
$4 = {
  data = 3,
  next = 0x555555b000d0
}
$5 = {
  data = 4,
  next = 0x555555b000f0
}
$6 = {
  data = 5,
  next = 0x555555b00110
}
$7 = {
  data = 6,
  next = 0x0
}
        p_generic_list LIST_HEAD_POINTER
        Print all the fields of the nodes in the linked list pointed to by LIST_HEAD_POINTER. Assumes there is a next field in the struct.
144	  return 0;
[Inferior 1 (process 330150) exited normally]

References