4.3.5. Debugging Individual Threads
GDB has the ability to debug individual threads, and to manipulate and examine them independently. This functionality is not enabled by default. To do so use
set non-stop on
and set target-async on
. These can be added to .gdbinit
. Once that functionality is turned on, GDB is ready to conduct thread debugging.
For example, the following program creates two threads. These two threads, along with the original thread executing main makes a total of three threads.
three-threads.c
#include <stdio.h> #include <pthread.h> #include <unistd.h> pthread_t thread; void* thread3 (void* d) { int count3 = 0; while(count3 < 1000){ sleep(10); printf("Thread 3: %d\n", count3++); } return NULL; } void* thread2 (void* d) { int count2 = 0; while(count2 < 1000){ printf("Thread 2: %d\n", count2++); } return NULL; } int main (){ pthread_create (&thread, NULL, thread2, NULL); pthread_create (&thread, NULL, thread3, NULL); //Thread 1 int count1 = 0; while(count1 < 1000){ printf("Thread 1: %d\n", count1++); } pthread_join(thread,NULL); return 0; }
Compile this program in order to examine it under GDB.
gcc -g three-threads.c -o three-threads -lpthread gdb ./three-threads
First set breakpoints on all thread functions; thread1, thread2, and main.
(gdb) break thread3 Breakpoint 1 at 0x4006c0: file three-threads.c, line 9. (gdb) break thread2 Breakpoint 2 at 0x40070c: file three-threads.c, line 20. (gdb) break main Breakpoint 3 at 0x40074a: file three-threads.c, line 30.
Then run the program.
(gdb) run [...] Breakpoint 3, main () at three-threads.c:30 30 pthread_create (&thread, NULL, thread2, NULL); [...] (gdb) info threads * 1 Thread 0x7ffff7fd5720 (LWP 4620) main () at three-threads.c:30 (gdb)
Note that the command
info threads
provides a summary of the program's threads and some details about their current state. In this case there is only one thread that has been created so far.
Continue execution some more.
(gdb) next [New Thread 0x7ffff7fd3710 (LWP 4687)] 31 pthread_create (&thread, NULL, thread3, NULL); (gdb) Breakpoint 2, thread2 (d=0x0) at three-threads.c:20 20 int count2 = 0; next [New Thread 0x7ffff75d2710 (LWP 4688)] 34 int count1 = 0; (gdb) Breakpoint 1, thread3 (d=0x0) at three-threads.c:9 9 int count3 = 0; info threads 3 Thread 0x7ffff75d2710 (LWP 4688) thread3 (d=0x0) at three-threads.c:9 2 Thread 0x7ffff7fd3710 (LWP 4687) thread2 (d=0x0) at three-threads.c:20 * 1 Thread 0x7ffff7fd5720 (LWP 4620) main () at three-threads.c:34
Here, two more threads are created. The star indicates the thread currently under focus. Also, the newly created threads have hit the breakpoint set for them in their initialization functions. Namely, thread2() and thread3().
To begin real thread debugging, use the
thread <thread number>
command to switch the focus to another thread.
(gdb) thread 2 [Switching to thread 2 (Thread 0x7ffff7fd3710 (LWP 4687))]#0 thread2 (d=0x0) at three-threads.c:20 20 int count2 = 0; (gdb) list 15 return NULL; 16 } 17 18 void* thread2 (void* d) 19 { 20 int count2 = 0; 21 22 while(count2 < 1000){ 23 printf("Thread 2: %d\n", count2++); 24 }
Thread 2 stopped at line 20 in its function thread2().
(gdb) next 22 while(count2 < 1000){ (gdb) print count2 $1 = 0 (gdb) next 23 printf("Thread 2: %d\n", count2++); (gdb) next Thread 2: 0 22 while(count2 < 1000){ (gdb) next 23 printf("Thread 2: %d\n", count2++); (gdb) print count2 $2 = 1 (gdb) info threads 3 Thread 0x7ffff75d2710 (LWP 4688) thread3 (d=0x0) at three-threads.c:9 * 2 Thread 0x7ffff7fd3710 (LWP 4687) thread2 (d=0x0) at three-threads.c:23 1 Thread 0x7ffff7fd5720 (LWP 4620) main () at three-threads.c:34 (gdb)
Above, a few lines of thread2 printed the counter count2 and left thread 2 at line 23 as is seen by the output of 'info threads'.
Now thread3.
(gdb) thread 3 [Switching to thread 3 (Thread 0x7ffff75d2710 (LWP 4688))]#0 thread3 (d=0x0) at three-threads.c:9 9 int count3 = 0; (gdb) list 4 5 pthread_t thread; 6 7 void* thread3 (void* d) 8 { 9 int count3 = 0; 10 11 while(count3 < 1000){ 12 sleep(10); 13 printf("Thread 3: %d\n", count3++); (gdb)
Thread three is a little different in that it has a sleep statement and executes slowly. Think of it as a representation of an uninteresting IO thread. Because this thread is uninteresting, continue its execution uninterrupted, using the
continue
.
(gdb) continue & (gdb) Thread 3: 0 Thread 3: 1 Thread 3: 2 Thread 3: 3
Take note of the & at the end of the
continue
. This allows the GDB prompt to return so other commands can be executed. Using the interrupt
, execution can be stopped should thread 3 become interesting again.
(gdb) interrupt [Thread 0x7ffff75d2710 (LWP 4688)] #3 stopped. 0x000000343f4a6a6d in nanosleep () at ../sysdeps/unix/syscall-template.S:82 82 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
It is also possible to go back to the original main thread and examine it some more.
(gdb) thread 1 [Switching to thread 1 (Thread 0x7ffff7fd5720 (LWP 4620))]#0 main () at three-threads.c:34 34 int count1 = 0; (gdb) next 36 while(count1 < 1000){ (gdb) next 37 printf("Thread 1: %d\n", count1++); (gdb) next Thread 1: 0 36 while(count1 < 1000){ (gdb) next 37 printf("Thread 1: %d\n", count1++); (gdb) next Thread 1: 1 36 while(count1 < 1000){ (gdb) next 37 printf("Thread 1: %d\n", count1++); (gdb) next Thread 1: 2 36 while(count1 < 1000){ (gdb) print count1 $3 = 3 (gdb) info threads 3 Thread 0x7ffff75d2710 (LWP 4688) 0x000000343f4a6a6d in nanosleep () at ../sysdeps/unix/syscall-template.S:82 2 Thread 0x7ffff7fd3710 (LWP 4687) thread2 (d=0x0) at three-threads.c:23 * 1 Thread 0x7ffff7fd5720 (LWP 4620) main () at three-threads.c:36 (gdb)
As can be seen from the output of info threads, the other threads are where they were left, unaffected by the debugging of thread 1.