How to Detect and Analyze Linux Deadlocks with pstack and gdb
This article explains what a deadlock is, outlines its four necessary conditions, provides a sample pthread program that deadlocks, and shows step‑by‑step how to use pstack and gdb on Linux to identify the stuck threads and understand the root cause.
Introduction
A deadlock occurs when two or more processes (or threads) each hold a resource and wait indefinitely for another resource held by another, causing the system to halt because no external action can break the cycle.
Four Necessary Conditions
Mutual exclusion – a resource can be used by only one thread at a time.
Hold and wait – a thread holding one resource may request another.
No preemption – a held resource cannot be forcibly taken away.
Circular wait – threads form a closed chain of resource requests.
Example Deadlock Diagram
The diagram shows two threads each holding one mutex (A and B) while waiting for the other, creating a classic cross‑lock deadlock.
Tools: pstack and gdb
pstack prints the stack trace of all threads in a running process, making it easy to see which threads are blocked on which functions.
gdb is the GNU debugger for C/C++ programs; it can attach to a live process, list threads, and display detailed call stacks and variable values.
Test Program
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex4 = PTHREAD_MUTEX_INITIALIZER;
static int sequence1 = 0;
static int sequence2 = 0;
int func1() {
pthread_mutex_lock(&mutex1);
++sequence1;
sleep(1);
pthread_mutex_lock(&mutex2);
++sequence2;
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
return sequence1;
}
int func2() {
pthread_mutex_lock(&mutex2);
++sequence2;
sleep(1);
pthread_mutex_lock(&mutex1);
++sequence1;
pthread_mutex_unlock(&mutex1);
pthread_mutex_unlock(&mutex2);
return sequence2;
}
void* thread1(void* arg) {
while (1) {
int v = func1();
if (v == 100000) pthread_exit(NULL);
}
return NULL;
}
void* thread2(void* arg) {
while (1) {
int v = func2();
if (v == 100000) pthread_exit(NULL);
}
return NULL;
}
void* thread3(void* arg) {
while (1) {
sleep(1);
char buf[128];
memset(buf, 0, sizeof(buf));
strcpy(buf, "thread3");
}
return NULL;
}
void* thread4(void* arg) {
while (1) {
sleep(1);
char buf[128];
memset(buf, 0, sizeof(buf));
strcpy(buf, "thread3");
}
return NULL;
}
int main() {
pthread_t tid[4];
if (pthread_create(&tid[0], NULL, thread1, NULL)) _exit(1);
if (pthread_create(&tid[1], NULL, thread2, NULL)) _exit(1);
if (pthread_create(&tid[2], NULL, thread3, NULL)) _exit(1);
if (pthread_create(&tid[3], NULL, thread4, NULL)) _exit(1);
sleep(5);
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
pthread_join(tid[2], NULL);
pthread_join(tid[3], NULL);
pthread_mutex_destroy(&mutex1);
pthread_mutex_destroy(&mutex2);
pthread_mutex_destroy(&mutex3);
pthread_mutex_destroy(&mutex4);
return 0;
}Compilation and Execution
Compile with debugging symbols: g++ -g lock.cpp -o lock -lpthread Run the program and obtain its PID:
ps -ef | grep lockAnalyzing with pstack
Run pstack <pid> twice. The first output shows threads 5 and 4 blocked inside pthread_mutex_lock. The second output shows the same threads still in the lock call, confirming they are deadlocked.
Analyzing with gdb
Attach to the process: gdb -p <pid> List threads ( info thread) and switch to the suspected ones ( thread 5, thread 4). The backtrace ( where) reveals that thread 5 is waiting on mutex2 inside func1, while thread 4 is waiting on mutex1 inside func2. Examining the mutex structures shows each mutex is owned by the other thread, confirming a classic cross‑lock deadlock.
Conclusion
The article demonstrates a practical method for diagnosing deadlocks on Linux using pstack and gdb. Understanding the four necessary conditions helps developers design systems that avoid deadlock by preventing circular wait, using ordered lock acquisition, or employing algorithms such as the Banker's algorithm for safe resource allocation.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
