CrackOps
Interview PrepTracksLinuxAWS LabSoonInterview RoomSoon
Sign inStart free
CrackOps

Interview prep for the people who keep production up.

[email protected]
ProductInterview PrepTracksDashboardInterview Room
RolesDevOpsSREPlatformCloud
CompanyAboutContactPrivacyTerms
© CrackOps
Linux / Debugging & Diagnostics / gdb

gdb

Debug crashes, core dumps and hung processes

Advanceddebuggingcrashescore-dumps

gdb, the GNU debugger, lets you stop a program and look inside it: the call stack, variables and memory at the moment it crashed. For a DevOps engineer its main jobs are reading a core dump after a segfault and getting stack traces from a hung or deadlocked process.

Syntaxgdb [-q] [-batch] [-ex CMD]... PROGRAM [CORE] | gdb -p PID | gdb --args PROGRAM [ARG]...

Watch it run

A small C tool you build segfaults on start. You want the exact line and the call path.

devops@devops-lab: ~/app — zsh — 96×28
➜ ~/app
Step 1 of 2
./bin/api --config /etc/app/app.conf

SIGSEGV: the program touched memory it doesn't own. The shell only tells you that it happened, not where.

Anatomy of a command

$
commandgdb

The GNU debugger. Controls the process through ptrace, like strace, but stops it completely.

How it works

gdb uses ptrace to control a live process: attach (PTRACE_ATTACH or as parent), stop it, read and write its registers and memory, and set breakpoints by replacing an instruction with int3. To make sense of raw memory it reads the program's symbol table and DWARF debug information (from the binary built with -g, or from separate debug files in /usr/lib/debug or via debuginfod), which map addresses to function names, source lines, variables and types. A core file is a snapshot of a dead process's memory and registers written by the kernel when it was killed by a signal like SIGSEGV; gdb loads it together with the original executable and the same shared libraries, so you can inspect the crash after the fact without the process running. Without debug info you still get function names from exported symbols, but no line numbers or variables. Attaching stops all threads until you detach.

Options you'll actually use

gdb PROG COREOpen a core dump with the binary that produced it
gdb -p PIDAttach to a running process (stops it until you detach or quit)
gdb --args PROG ARGSLoad a program with its arguments, then type run
-batch -ex CMDRun commands non-interactively and exit: good for scripts and tickets
bt / bt fullBacktrace: the call stack (full adds local variables)
thread apply all btBacktrace of every thread: finds deadlocks
frame N / up / downSelect a stack frame to inspect
print EXPR / info localsShow a variable or expression / all locals in the frame
break FILE:LINE / FUNCSet a breakpoint, then run, next, step, continue
info sharedlibraryWhich libraries are loaded, and whether their symbols were found
detach / quitLet the process continue / leave gdb

Common mistakes

Expecting a core file after a crash, but ulimit -c is 0

With a core size limit of 0 the kernel writes nothing. Many shells and services start with that limit.

ulimit -c unlimited for the shell, LimitCORE=infinity in the systemd unit, and check kernel.core_pattern
Opening a core with a newer build of the binary

Addresses no longer match the code, so gdb prints wrong functions or garbage ('core file may not match specified executable file').

Keep the exact binary and debug symbols for every release (build ID, debuginfod)
Attaching gdb interactively to a production process and going for coffee

Every thread is stopped while gdb is attached: requests time out and health checks fail.

gdb -batch -ex 'thread apply all bt' -p PID (in and out in a second), or gcore PID to snapshot it
Debugging an optimised (-O2) build and trusting every variable value

Optimised code keeps values in registers or removes them: gdb shows <optimized out> or stale values.

Reproduce with -O0 -g when you can, or reason from the backtrace and registers

Interview questions

Practice

Try these on a Linux machine of your own. Each task has a hint and a solution.

  1. 1

    Write a 3-line C program that dereferences a NULL pointer, compile it with -g, and find the crashing line with gdb.

  2. 2

    Make the same program leave a core dump in the current directory, then open it in gdb.

  3. 3

    Start 'sleep 600 &' and print its backtrace with gdb without stopping it for more than a moment.

Related commands

coredumpctlFind and open core dumps captured by systemdstraceTrace the system calls a process makesdmesgRead the kernel's message logulimitShow and set per-process resource limits
PreviousperfNextcoredumpctl
Linux / Debugging & Diagnostics / gdb

gdb

Debug crashes, core dumps and hung processes

Advanceddebuggingcrashescore-dumps

gdb, the GNU debugger, lets you stop a program and look inside it: the call stack, variables and memory at the moment it crashed. For a DevOps engineer its main jobs are reading a core dump after a segfault and getting stack traces from a hung or deadlocked process.

Syntaxgdb [-q] [-batch] [-ex CMD]... PROGRAM [CORE] | gdb -p PID | gdb --args PROGRAM [ARG]...

Watch it run

A small C tool you build segfaults on start. You want the exact line and the call path.

devops@devops-lab: ~/app — zsh — 96×28
➜ ~/app
Step 1 of 2
./bin/api --config /etc/app/app.conf

SIGSEGV: the program touched memory it doesn't own. The shell only tells you that it happened, not where.

Anatomy of a command

$
commandgdb

The GNU debugger. Controls the process through ptrace, like strace, but stops it completely.

How it works

gdb uses ptrace to control a live process: attach (PTRACE_ATTACH or as parent), stop it, read and write its registers and memory, and set breakpoints by replacing an instruction with int3. To make sense of raw memory it reads the program's symbol table and DWARF debug information (from the binary built with -g, or from separate debug files in /usr/lib/debug or via debuginfod), which map addresses to function names, source lines, variables and types. A core file is a snapshot of a dead process's memory and registers written by the kernel when it was killed by a signal like SIGSEGV; gdb loads it together with the original executable and the same shared libraries, so you can inspect the crash after the fact without the process running. Without debug info you still get function names from exported symbols, but no line numbers or variables. Attaching stops all threads until you detach.

Options you'll actually use

gdb PROG COREOpen a core dump with the binary that produced it
gdb -p PIDAttach to a running process (stops it until you detach or quit)
gdb --args PROG ARGSLoad a program with its arguments, then type run
-batch -ex CMDRun commands non-interactively and exit: good for scripts and tickets
bt / bt fullBacktrace: the call stack (full adds local variables)
thread apply all btBacktrace of every thread: finds deadlocks
frame N / up / downSelect a stack frame to inspect
print EXPR / info localsShow a variable or expression / all locals in the frame
break FILE:LINE / FUNCSet a breakpoint, then run, next, step, continue
info sharedlibraryWhich libraries are loaded, and whether their symbols were found
detach / quitLet the process continue / leave gdb

Common mistakes

Expecting a core file after a crash, but ulimit -c is 0

With a core size limit of 0 the kernel writes nothing. Many shells and services start with that limit.

ulimit -c unlimited for the shell, LimitCORE=infinity in the systemd unit, and check kernel.core_pattern
Opening a core with a newer build of the binary

Addresses no longer match the code, so gdb prints wrong functions or garbage ('core file may not match specified executable file').

Keep the exact binary and debug symbols for every release (build ID, debuginfod)
Attaching gdb interactively to a production process and going for coffee

Every thread is stopped while gdb is attached: requests time out and health checks fail.

gdb -batch -ex 'thread apply all bt' -p PID (in and out in a second), or gcore PID to snapshot it
Debugging an optimised (-O2) build and trusting every variable value

Optimised code keeps values in registers or removes them: gdb shows <optimized out> or stale values.

Reproduce with -O0 -g when you can, or reason from the backtrace and registers

Interview questions

Practice

Try these on a Linux machine of your own. Each task has a hint and a solution.

  1. 1

    Write a 3-line C program that dereferences a NULL pointer, compile it with -g, and find the crashing line with gdb.

  2. 2

    Make the same program leave a core dump in the current directory, then open it in gdb.

  3. 3

    Start 'sleep 600 &' and print its backtrace with gdb without stopping it for more than a moment.

Related commands

coredumpctlFind and open core dumps captured by systemdstraceTrace the system calls a process makesdmesgRead the kernel's message logulimitShow and set per-process resource limits
PreviousperfNextcoredumpctl