GDB Setup, Commands and Configurations¶
Disable printf when gdb is active¶
Debugging in C Programming¶
Creating a debug.h file with the following:¶
- This will help us to disable the printf function when debugging
#ifndef DEBUGING_H
#define DEBUGING_H
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600 // Ensure fileno and related functions are declared.
#endif
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L // Use this only if not already defined.
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
// Global variable to store the original stdout file descriptor.
static int saved_stdout_fd = -1;
// Function to check if a debugger (e.g., GDB) is attached
static inline bool is_debugger_attached(void) {
FILE *f = fopen("/proc/self/status", "r");
if (!f) return false;
char line[256];
bool attached = false;
while (fgets(line, sizeof(line), f)) {
if (strncmp(line, "TracerPid:", 10) == 0) {
int tracer_pid = atoi(line + 10);
if (tracer_pid > 0) {
attached = true;
}
break;
}
}
fclose(f);
return attached;
}
// Disables the printf function if gdb is active.
#undef printf
#define printf(...) ( is_debugger_attached() ? (void)0 : fprintf(stdout, __VA_ARGS__) )
// Function to disable stdout by redirecting it to /dev/null if a debugger is attached.
static inline void disable_stdout(void) {
if (is_debugger_attached()) {
if (saved_stdout_fd == -1) {
// Save the current stdout file descriptor.
saved_stdout_fd = dup(fileno(stdout));
}
// Redirect stdout to /dev/null.
freopen("/dev/null", "w", stdout);
}
}
// Function to re-enable stdout by restoring the original file descriptor.
static inline void enable_stdout(void) {
if (saved_stdout_fd != -1) {
fflush(stdout);
dup2(saved_stdout_fd, fileno(stdout));
close(saved_stdout_fd);
saved_stdout_fd = -1;
}
}
#endif // DEBUGING_H
To use it¶
- Simply add this under all your
#include
in every document you are printing in
//--------------------------------------//
// This will initialize the changes //
// within that file, this has to be //
// done in every file you where want //
// to disable the printf function. //
//--------------------------------------//
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif
#include <stdio.h>
#include "debug.h"
//--------------------------------------//
Debugging in CPP¶
Creating a debug.h file¶
#ifndef DEBUGGING_H
#define DEBUGGING_H
#include <iostream>
#include <fstream>
#include <streambuf>
#include <string>
#include <unistd.h>
struct NullBuffer : public std::streambuf {
int overflow(int c) override { return c; }
};
struct NullStream : public std::ostream {
NullStream() : std::ostream(&m_sb) {}
private:
NullBuffer m_sb;
};
// Function to check if GDB is attached
inline bool is_debugger_attached() {
std::ifstream status_file("/proc/self/status");
if (!status_file.is_open()) return false;
std::string line;
while (std::getline(status_file, line)) {
if (line.find("TracerPid:") == 0) {
int tracer_pid = std::stoi(line.substr(10));
return tracer_pid > 0; // If TracerPid > 0, a debugger is attached.
}
}
return false;
}
// Function to disable stdout if running in gdb
inline void disable_stdout() {
if (is_debugger_attached()) { // Now disables output only if GDB is attached.
static NullStream null_stream;
std::cout.rdbuf(null_stream.rdbuf());
}
}
// Function to enable stdout
inline void enable_stdout() {
static std::streambuf *orig_buf = std::cout.rdbuf();
std::cout.rdbuf(orig_buf);
}
#endif // DEBUGGING_H
To use it¶
GDB Commands¶
Setting up the debugger:¶
gdb ./<executable>
(starts the debugger)lay next
and then press Enter until you see only the C codeb main
sets a breakpoint inside themain
function (can be set in other functions too)run
(executes the program)
Using the debugger:¶
s
= Step in (enters a function)n
= Next (moves to the next line)p size
= Prints the value of the variablesize
info locals
= Prints all locally accessible variablesinfo variables <var-name-substring>
= Prints all global variables that start with<var-name-substring>
Printing Variables in Different Formats:¶
- Decimal (default):
p var
- Hexadecimal:
p/x var
- Binary:
p/t var
- Octal:
p/o var
- Character (if applicable):
p/c var
- Unsigned decimal:
p/u var
- Floating point:
p/f var
- Dereferencing pointers:
p *ptr
- Printing memory in different formats:
x/<format> <address>
- Example:
x/4xb &var
prints 4 bytes of memory atvar
in hex
This allows you to inspect variables and memory in different representations while debugging.
GDB Config File¶
- This you can set in
~/.gdbinit
to apply to your gdb.
Config file
# ~/.gdbinit - Custom GDB Configuration
# -----------------------------------------------------
# General GDB settings
# -----------------------------------------------------
# Disable pagination for smooth scrolling
set pagination off
# Enable TUI mode with layout next to show C source code
layout next
focus source
# Suppress extra GDB prints and confirmations
set verbose off
set print elements 0
set print repeats 0
set print pretty off
set print static-members off
set print object on
set confirm off
# -----------------------------------------------------
# Suppress the inferior's output via freopen() at main()
# -----------------------------------------------------
# Note: This redirect must occur before any output is flushed.
break main
commands
# Redirect stdout and stderr to /dev/null to suppress printf/puts output.
call freopen("/dev/null", "w", stdout)
call freopen("/dev/null", "w", stderr)
# Continue execution after setting up the redirection.
continue
end
# -----------------------------------------------------
# Define a silent_run command as an alternative method
# -----------------------------------------------------
# Note: This method sets the inferior's tty to /dev/null.
# It may not work as expected in TUI mode or on all systems.
define silent_run
set inferior-tty /dev/null
run
end
document silent_run
Runs the program with its output redirected to /dev/null.
(If this doesn’t work as expected, the break-at-main with freopen() is your best bet.)
end
# Inform the user that the configuration is loaded.
echo Custom GDB configuration loaded.\n
Additional GDB Configs¶
Switching between child and parent processes¶
When debugging programs that use fork()
, GDB, by default, follows the parent process. You can configure GDB to follow the child process or manually switch between them.
Set GDB to follow the child process after a fork:
Set GDB to follow the parent process after a fork:
List all processes in the debugging session:
Switch between parent and child process manually:
Replace<id>
with the process ID shown in info inferiors
.