Debug Packages and Symbols in Production
Why Debug Symbols Change Everything
A core dump without symbols often shows meaningless function names, missing line numbers, and unreadable stack traces. Debug symbols let you map memory addresses back to real functions and source lines. This turns “it crashed” into “it crashed here, for this reason”.
Symptom
- Core dump backtrace shows “??” frames
- Stack traces contain only memory addresses
- Native crashes cannot be attributed to a library or function
- Post-mortem debugging provides no actionable detail
Root Cause
- Binary stripped of symbols (common in production builds)
- Debug packages not installed
- Separate debuginfo repository not enabled
- Symbols not matching the exact binary version
Investigation
1) Confirm If a Binary Is Stripped
file /usr/bin/nginx
If it says “stripped”, debug symbols are not present.
2) Check If You Have a Core Dump Without Symbols
coredumpctl list coredumpctl gdb PID
Inside gdb:
bt
If frames show “??”, symbols are missing.
Mitigation: Acquire Matching Debug Symbols
Key rule: symbols must match the exact package version of the binary and its libraries.
Debian/Ubuntu: dbgsym Packages
Ubuntu and Debian often provide separate dbgsym packages via a debug symbol repository.
Check package version:
dpkg -l | grep nginx
Install symbols (example pattern):
sudo apt install nginx-dbgsym
For libraries involved in the crash, install their dbgsym packages as well.
RHEL-based: debuginfo Packages
Enable debuginfo repositories and install debuginfo:
sudo dnf debuginfo-install nginx
List installed debuginfo:
rpm -qa | grep debuginfo
Operational Safety in Production
- Do not permanently bloat production nodes with debuginfo unless required
- Prefer reproducing in staging with the same build and symbols
- If production analysis is required, install symbols temporarily and remove after investigation
Debug Symbols for Your Own Binaries
If you build native binaries, keep separate symbol files and store them in artifact storage.
Example build mindset:
- Production binary can be stripped
- Symbols stored separately and fetched when needed
- Symbols versioned with build ID
Mitigation Workflow: Turning “Crash” Into “Fix”
- Capture core dump
- Confirm binary version and library versions
- Install matching debug symbols (or fetch from artifact store)
- Re-run gdb backtrace
- Identify the crashing function and call chain
- Correlate with deploy changes and inputs
Verification Checklist
After installing symbols, gdb backtrace should become readable:
coredumpctl gdb PID
Inside gdb:
bt info threads
- Stack frames show function names
- Thread context visible
- Crash location attributable to a library or module
Hardening Strategy
- Maintain symbol artifacts for every production build
- Ensure symbol/binary version matching discipline
- Use staging reproduction as primary analysis environment
- Keep debuginfo installation as an emergency-only step on prod
Why This Matters in Real Infrastructure
Many “random production crashes” are solvable, but only if you can see where they happen. Debug symbols turn post-mortem debugging into a reliable engineering loop. Without symbols, teams restart services blindly and accept recurring outages as fate.