Thursday, May 6, 2010

gdb macros

gdb provides an amazingly versatile debugging platform supporting mechanisms to debug local and remote software.

One of gdb's less known features is its rich macro language. Although I had used it before working at Apple, that's where I was really exposed to how gdb can be used to the max.

OSX can be debugged from a remote machine via gdb remote. Navigating around the guts of a running OS is challenging. You need to locate the task structures etc that you are debugging by finding them in lists of running tasks. Doing this manually takes time and is error prone. So to make this easy, Apple provides a file of gdb macros that will do this for you.

While these macros won't work on your embedded system, they do provide examples for how you can construct complex macros containing ifs, while loops etc.

To give you a taster, here's a very simple example using a linked list of structures.

#include
#include

struct enu{
float x;
float y;
float z;
struct enu *next;
};

struct enu *addpoint(float x, float y, float z, struct enu *next)
{
struct enu * np;
np = malloc(sizeof(struct enu));
if(np){
np->x = x;
np->y = y;
np->z = z;
np->next = next;
}
return np;
}

main()
{
struct enu *list = NULL;
struct enu *i;

list = addpoint(1,2,3,list);
list = addpoint(1,2,4,list);
list = addpoint(1,2,5,list);
list = addpoint(1,2,6,list);

for(i = list; i; i=i->next)
printf("%f %f %f\n",i->x,i->y,i->z);
}
Here are a few macros

define dump_entry
set $l=(struct enu *)($arg0)
printf "Entry is %f %f %f\n", $l->x, $l->y, $l->z
end
document dump_entry
Print out a list entry
Usage: dump_entry pointer
end

define dump_list
set $list = $arg0
if(!$list)
printf "List is empty\n"
else
while($list)
dump_entry $list
set $list=$list->next
end
printf "End of list\n"
end
end
document dump_list
Print out a whole list
Usage: dump_list pointer
end

And here is a session using them:

gdb-demo$ gdb gdb-demo
...
Reading symbols from /home/charles/gdb-demo/gdb-demo...done.
(gdb) source demo.gdb <---- loading up the macros
(gdb) list main
23 }
24 return np;
25 }
26
27 main()
28 {
29 struct enu *list = NULL;
30 struct enu *i;
31
32 list = addpoint(1,2,3,list);
(gdb) break 32 Breakpoint 1 at 0x8048468: file gdb-demo.c, line 32.
(gdb) run Starting program: /home/charles/gdb-demo/gdb-demo
Breakpoint 1, main () at gdb-demo.c:32
32 list = addpoint(1,2,3,list);
(gdb) dump_list list
List is empty
(gdb) n
33 list = addpoint(1,2,4,list);
(gdb) n
34 list = addpoint(1,2,5,list);
(gdb) n
35 list = addpoint(1,2,6,list);
(gdb) dump_list list
Entry is 1.000000 2.000000 5.000000
Entry is 1.000000 2.000000 4.000000
Entry is 1.000000 2.000000 3.000000
End of list
(gdb)


In embedded systems, macros are really handy as a way to explore the state of RTOS tasks, semaphores etc as well as peripherals etc. Careful though... reading some peripheral registers can change their state.

Building infrastructure like this takes some time but pays off in the long run.

2 comments:

V said...

Hi Charles,

Came across your blog while reading about GDB macros. I myself use macros frequently at work but haven't figured out a way to include macros from other macro files similar to #include that we use in .C files. I have a set of macro methods that should be shared. Do you have any tips on that ?

القمر السعودى said...

شركة رش مبيدات بالاحساء
شركة رش دفان بالقطيف
شركة مكافحة النمل الابيض بالجبيل
شركة مكافحة البق بالجبيل
شركة مكافحة النمل الأبيض بالخبر