How To Code In Linux

How To Code In Linux

Reading time1 min
#Linux#Programming#Debugging#gdb#strace#ltrace

Mastering Command-Line Debugging: Effective Linux Coding with Native Tools

Reliance on GUI debuggers breaks down on headless nodes, CI agents, or in incident response over SSH at 2am. When real reliability is required, native Linux command-line tools—gdb, strace, ltrace—become indispensable.


Case: Diagnosing a Segmentation Fault with Core Tools

Typical scenario: a C binary segfaults on production (Ubuntu 22.04, gcc 11.3.0), no X server available.

Minimal reproducible example:

#include <stdio.h>

void crash(int t) {
    int *p = NULL;
    if (t > 0)
        *p = 1; // deliberate segfault
}

int main() {
    crash(2);
    puts("done");
    return 0;
}

Build with debug symbols:

gcc -g -O0 crash.c -o crash

Note: Omitting -O0 can result in optimized-out variables, making debugging harder.


Workflow: Native Debugging

1. Pinpoint Crash Location (gdb)

gdb ./crash
(gdb) run

Results:

Program received signal SIGSEGV, Segmentation fault.
0x0000000000401136 in crash (t=2) at crash.c:6
6           *p = 1;

Check stack trace and variable state:

(gdb) bt
#0  crash (t=2) at crash.c:6
#1  0x0000000000401150 in main () at crash.c:11
(gdb) print p
$1 = (int *) 0x0

Known issue: optimized binaries (-O2 or higher) can obfuscate call stacks and variable visibility.

2. System Call Tracing (strace)

Useful when a process fails due to permissions, missing files, or system resource exhaustion. Consider:

strace -f -e trace=process,network,open,read,write ./crash

Output will display all file/network operations. Example failure when open() fails:

openat(AT_FDCWD, "/etc/nonexistent", O_RDONLY) = -1 ENOENT (No such file or directory)

Tip: Store trace output for post-mortem review:

strace -o crash.strace ./crash

3. Library Call Analysis (ltrace)

To trace calls to dynamically linked library functions:

ltrace ./crash

Typical for debugging memory issues or incorrect library usage. Example output:

puts("done")                                    = 5
__libc_start_main(0x401140, 1, 0x7fffffffe378, ...) = 0

Side note: ltrace may not work correctly with statically linked or stripped binaries.


Advanced Tactics

  • Conditional breakpoints in gdb:
    break crash if t == 2
    
  • Inspect memory at fault:
    x/4x $sp
    
  • Attach to a live process:
    pidof myservice
    gdb -p <PID>
    
  • Filter noisy trace:
    strace -e open,read,write ./crash
    
  • Disassemble at crash point:
    disas crash
    

Practical Observations

  • Native debugging tools are mandatory on remote targets, within minimal Docker containers, or during CI/CD troubleshooting.
  • They reveal low-level detail: syscalls, ABI handshakes, and unexpected errno values.
  • Common mistakes: omitting -g, forgetting symbols are stripped from packaged binaries, ignoring ASLR side effects.

Gotcha: Debian derivatives often strip debug symbols in /usr/bin; install corresponding -dbgsym packages to restore introspection.


Conclusion (Placed Early for Emphasis)

Native command-line debugging is less about nostalgia and more about capability: when GUIs are unavailable, precision matters, or performance must be investigated to the syscall or malloc() boundary.


Reference Table

ToolAreaCommand ExampleNote
gdbSource-level debuggdb -q ./mybinUse -g flag when compiling
straceSyscall tracestrace -e open ./mybinUse -o out.log to save trace
ltraceLibrary call traceltrace ./mybinLimited on statically linked binaries

Non-obvious Tip

When debugging memory corruption where errors occur long after corruption (e.g., double free, use-after-free), pair valgrind with gdb via:

valgrind --vgdb=yes --vgdb-error=0 ./crash

Then attach gdb as Valgrind pauses execution, allowing for stepwise inspection.


Successful Linux programming means more than getting a binary to run. It requires visibility into every layer—source, syscalls, and symbols—especially when standard dev tools are unavailable. Invest the time to master these, and you'll recover faster when the inevitable failure lands in your lap.