Fundamentals 14 min read

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.

ITPUB
ITPUB
ITPUB
How to Detect and Analyze Linux Deadlocks with pstack and gdb

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

Deadlock diagram
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 lock

Analyzing 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

deadlockpthreadgdbpstack
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.