Thursday March 05, 2009
Adding debugging functions to your dbx session
Have you ever been sitting in a debugging session thinking: "arg, why don't I have a function doing xyz"? I know I have! Luckily for us we don't have to terminate the debugging session, build a new version of the application with the function available and try to recreate the debugging session. There is an easy way to extend our debugging session with new functions :-)
Talking is one thing, but developers don't believe anything before they see the code. So let's go ahead and create an example.
trond@opensolaris> nl -ba main.c
1 #include <stdio.h>
2
3 struct item {
4 /* the interesting data */
5 struct item* next;
6 };
7
8 int main(int argc, char** argv) {
9 struct item items[10];
10 for (int ii = 0; ii < 10; ++ii) {
11 items[ii].next = items + ii + 1;
12 }
13 /* terminate the list */
14 items[9].next = NULL;
15
16 /* let's create a loop */
17 items[9].next = &items[7];
18
19 return 0;
20 }
trond@opensolaris> cc -o testprogram -g main.c -ldl
So let's start a debugging session, and set a breakpoint at line 17 in main.c:
trond@opensolaris> dbx testprogram Reading testprogram Reading ld.so.1 Reading libdl.so.1 Reading libc.so.1 (dbx) stop at 17 (2) stop at "main.c":17 (dbx) run Running: testprogram (process id 17090) stopped in main at line 17 in file "main.c" 17 items[9].next = &items[7]; (dbx)
So how do we verify that we don't have a loop in this list??? The first thing we need to do is to create a small C-function to do loop detection and compile it into a shared object:
trond@opensolaris> nl -ba looptest.c
1 #include <stdio.h>
2
3 struct item {
4 /* the interesting data */
5 struct item* next;
6 };
7
8 int looptest(struct item* root) {
9 struct item* lookahead = root;
10
11 while (root != NULL) {
12 if (lookahead != NULL && lookahead->next != NULL) {
13 lookahead = lookahead->next->next;
14 } else {
15 lookahead = NULL;
16 }
17 if (root == lookahead) {
18 /* loop detected */
19 return 1;
20 } else {
21 root = root->next;
22 }
23 }
24
25 /* no loop */
26 return 0;
27 }
trond@opensolaris> cc -o looptest.so -G -KPIC -g looptest.c
The trick is that we can use the call command in dbx to call a function from within the process we are debugging, and the function we want to call is dlopen. Why? dlopen will load the functions in the shared object into the address space of the process so that we can call them. Let's jump back to the debugging session:
(dbx) call dlopen("./looptest.so", 0x102)
Reading looptest.so
stopped in main at line 17 in file "main.c"
17 items[9].next = &items[7];
So what is 0x102? Well that is result of RTLD_NOW | RTLD_GLOBAL (check the dlopen manual page for more info). Now we can call the looptest function from our debugging session:
(dbx) print looptest(items) looptest(items) = 0
So let's continue the debugging and execute the next line that creates a loop in the list:
(dbx) next stopped in main at line 19 in file "main.c" 19 return 0; (dbx) print looptest(items) looptest(items) = 1
One small caveat is that you have to link your application with -ldl for this to work....
Posted at 12:04PM Mar 05, 2009 by trond in OpenSolaris | Comments[0]