cpp

examples

examples.cpp⚙️
/**
 * GDB Debugging Examples
 * Compile with: g++ -g -O0 examples.cpp -o examples
 * Run with: gdb ./examples
 */

#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <thread>
#include <mutex>

using std::cout;
using std::endl;

// ============================================================================
// EXAMPLE 1: Basic Debugging - Variables and Stepping
// ============================================================================

void calculateSum() {
    int a = 10;
    int b = 20;
    int sum = a + b;
    
    cout << "Sum: " << sum << endl;
    
    // GDB Session:
    // (gdb) break calculateSum
    // (gdb) run
    // (gdb) next
    // (gdb) print a
    // $1 = 10
    // (gdb) print b
    // $2 = 20
    // (gdb) next
    // (gdb) print sum
    // $3 = 30
}

// ============================================================================
// EXAMPLE 2: Array/Vector Inspection
// ============================================================================

void inspectContainers() {
    int arr[5] = {10, 20, 30, 40, 50};
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    for (int i = 0; i < 5; ++i) {
        arr[i] *= 2;
    }
    
    for (auto& v : vec) {
        v *= 3;
    }
    
    // GDB Session:
    // (gdb) break inspectContainers
    // (gdb) run
    // (gdb) next  (until after the loops)
    // 
    // Print array:
    // (gdb) print arr
    // $1 = {20, 40, 60, 80, 100}
    // (gdb) print arr[0]@5
    // $2 = {20, 40, 60, 80, 100}
    //
    // Print vector:
    // (gdb) print vec
    // (gdb) print vec._M_impl._M_start[0]@5
    // Or with pretty printing:
    // (gdb) set print pretty on
    // (gdb) print vec
}

// ============================================================================
// EXAMPLE 3: Pointer Debugging
// ============================================================================

struct Node {
    int data;
    Node* next;
    
    Node(int d) : data(d), next(nullptr) {}
};

void debugPointers() {
    Node* head = new Node(1);
    head->next = new Node(2);
    head->next->next = new Node(3);
    
    // Traverse
    Node* current = head;
    while (current) {
        cout << current->data << " ";
        current = current->next;
    }
    cout << endl;
    
    // Cleanup
    while (head) {
        Node* temp = head;
        head = head->next;
        delete temp;
    }
    
    // GDB Session:
    // (gdb) break debugPointers
    // (gdb) run
    // (gdb) next  (until after building list)
    //
    // Examine linked list:
    // (gdb) print *head
    // $1 = {data = 1, next = 0x...}
    // (gdb) print *head->next
    // $2 = {data = 2, next = 0x...}
    // (gdb) print head->next->next->data
    // $3 = 3
}

// ============================================================================
// EXAMPLE 4: Stack Trace Analysis
// ============================================================================

void level3(int x) {
    int result = x * 2;
    cout << "Level 3: result = " << result << endl;
    // Set breakpoint here to see call stack
}

void level2(int x) {
    int processed = x + 10;
    level3(processed);
}

void level1(int x) {
    int modified = x * 3;
    level2(modified);
}

void demonstrateCallStack() {
    int initial = 5;
    level1(initial);
    
    // GDB Session:
    // (gdb) break level3
    // (gdb) run
    // (gdb) backtrace
    // #0  level3 (x=25) at examples.cpp:XX
    // #1  level2 (x=15) at examples.cpp:XX
    // #2  level1 (x=5) at examples.cpp:XX
    // #3  demonstrateCallStack () at examples.cpp:XX
    // #4  main () at examples.cpp:XX
    //
    // (gdb) frame 1
    // (gdb) info locals
    // processed = 15
    // (gdb) info args
    // x = 15
}

// ============================================================================
// EXAMPLE 5: Conditional Breakpoints
// ============================================================================

void processNumbers() {
    for (int i = 0; i < 100; ++i) {
        int value = i * i;
        
        // We want to stop only when value > 1000
        if (value > 1000) {
            cout << "Large value at i=" << i << ": " << value << endl;
        }
    }
    
    // GDB Session:
    // (gdb) break examples.cpp:XX if value > 1000
    // (gdb) run
    // Breakpoint hit when i=32, value=1024
    //
    // Alternative: break when i equals specific value
    // (gdb) break examples.cpp:XX if i == 50
}

// ============================================================================
// EXAMPLE 6: Watchpoints
// ============================================================================

void modifyVariable() {
    int counter = 0;
    
    for (int i = 0; i < 10; ++i) {
        counter += i;  // Watch this variable
    }
    
    cout << "Final counter: " << counter << endl;
    
    // GDB Session:
    // (gdb) break modifyVariable
    // (gdb) run
    // (gdb) next  (until counter is declared)
    // (gdb) watch counter
    // Hardware watchpoint 2: counter
    // (gdb) continue
    // Hardware watchpoint 2: counter
    // Old value = 0
    // New value = 0
    // (gdb) continue
    // Old value = 0  
    // New value = 1
    // ...
}

// ============================================================================
// EXAMPLE 7: Segmentation Fault Debugging
// ============================================================================

void causeSegfault(bool trigger = false) {
    int* ptr = nullptr;
    
    if (trigger) {
        *ptr = 42;  // This will crash!
    } else {
        cout << "No crash - trigger was false" << endl;
    }
    
    // GDB Session (when trigger=true):
    // (gdb) run
    // Program received signal SIGSEGV, Segmentation fault.
    // 0x00000000004... in causeSegfault (trigger=true) at examples.cpp:XX
    // XX      *ptr = 42;
    //
    // (gdb) print ptr
    // $1 = (int *) 0x0
    // (gdb) backtrace
    // Shows exact location of crash
}

// ============================================================================
// EXAMPLE 8: Class/Object Debugging
// ============================================================================

class Rectangle {
private:
    double width;
    double height;
    std::string name;
    
public:
    Rectangle(double w, double h, std::string n) 
        : width(w), height(h), name(std::move(n)) {}
    
    double area() const { return width * height; }
    double perimeter() const { return 2 * (width + height); }
    
    void scale(double factor) {
        width *= factor;
        height *= factor;
    }
};

void debugObjects() {
    Rectangle rect(5.0, 3.0, "MyRect");
    
    cout << "Area: " << rect.area() << endl;
    rect.scale(2.0);
    cout << "New area: " << rect.area() << endl;
    
    // GDB Session:
    // (gdb) break debugObjects
    // (gdb) run
    // (gdb) next  (after construction)
    //
    // (gdb) print rect
    // $1 = {width = 5, height = 3, name = "MyRect"}
    //
    // (gdb) print rect.width
    // $2 = 5
    //
    // (gdb) call rect.area()
    // $3 = 15
    //
    // (gdb) ptype rect
    // type = class Rectangle {
    //   private:
    //     double width;
    //     double height;
    //     std::string name;
    //   public:
    //     Rectangle(double, double, std::string);
    //     double area(void) const;
    //     ...
    // }
}

// ============================================================================
// EXAMPLE 9: Smart Pointer Debugging
// ============================================================================

void debugSmartPointers() {
    auto uniquePtr = std::make_unique<int>(42);
    auto sharedPtr1 = std::make_shared<std::string>("Hello");
    auto sharedPtr2 = sharedPtr1;
    
    *uniquePtr = 100;
    *sharedPtr1 = "World";
    
    cout << "unique: " << *uniquePtr << endl;
    cout << "shared: " << *sharedPtr2 << endl;
    
    // GDB Session:
    // (gdb) break debugSmartPointers
    // (gdb) run
    // (gdb) next (until pointers are created)
    //
    // For unique_ptr:
    // (gdb) print uniquePtr
    // (gdb) print *uniquePtr._M_ptr
    // $1 = 42
    //
    // For shared_ptr:
    // (gdb) print sharedPtr1.use_count()
    // (gdb) print *sharedPtr1._M_ptr
    //
    // With pretty printing:
    // (gdb) set print pretty on
    // (gdb) print uniquePtr
}

// ============================================================================
// EXAMPLE 10: Multi-threaded Debugging
// ============================================================================

std::mutex mtx;
int sharedCounter = 0;

void threadFunction(int id) {
    for (int i = 0; i < 5; ++i) {
        std::lock_guard<std::mutex> lock(mtx);
        ++sharedCounter;
        cout << "Thread " << id << ": counter = " << sharedCounter << endl;
    }
}

void debugThreads() {
    std::thread t1(threadFunction, 1);
    std::thread t2(threadFunction, 2);
    
    t1.join();
    t2.join();
    
    cout << "Final counter: " << sharedCounter << endl;
    
    // GDB Session:
    // (gdb) break threadFunction
    // (gdb) run
    //
    // (gdb) info threads
    //   Id   Target Id         Frame
    // * 1    Thread 0x... "examples" main () at examples.cpp:XX
    //   2    Thread 0x... "examples" threadFunction (id=1) at examples.cpp:XX
    //   3    Thread 0x... "examples" threadFunction (id=2) at examples.cpp:XX
    //
    // (gdb) thread 2
    // (gdb) backtrace
    //
    // (gdb) thread apply all bt
    // Shows backtraces for all threads
    //
    // (gdb) set scheduler-locking on
    // Only current thread runs
}

// ============================================================================
// EXAMPLE 11: Memory Examination
// ============================================================================

void examineMemory() {
    char buffer[16] = "Hello, GDB!";
    int numbers[4] = {0x41, 0x42, 0x43, 0x44};  // A, B, C, D in ASCII
    
    cout << "Buffer: " << buffer << endl;
    
    // GDB Session:
    // (gdb) break examineMemory
    // (gdb) run
    // (gdb) next (until buffer is initialized)
    //
    // Examine as bytes (hex):
    // (gdb) x/16xb buffer
    // 0x...: 0x48 0x65 0x6c 0x6c 0x6f 0x2c 0x20 0x47
    // 0x...: 0x44 0x42 0x21 0x00 0x00 0x00 0x00 0x00
    //
    // Examine as string:
    // (gdb) x/s buffer
    // 0x...: "Hello, GDB!"
    //
    // Examine as characters:
    // (gdb) x/16cb buffer
    // 'H' 'e' 'l' 'l' 'o' ',' ' ' 'G' 'D' 'B' '!' '\0' ...
    //
    // Examine integers:
    // (gdb) x/4xw numbers
    // 0x...: 0x00000041 0x00000042 0x00000043 0x00000044
}

// ============================================================================
// EXAMPLE 12: Debugging Loops
// ============================================================================

int sumArray(const int* arr, size_t size) {
    int sum = 0;
    for (size_t i = 0; i < size; ++i) {
        sum += arr[i];
    }
    return sum;
}

void debugLoop() {
    int data[] = {10, 20, 30, 40, 50};
    int result = sumArray(data, 5);
    cout << "Sum: " << result << endl;
    
    // GDB Session:
    // (gdb) break sumArray
    // (gdb) run
    //
    // Step through loop iterations:
    // (gdb) display i
    // (gdb) display sum
    // (gdb) next
    // 1: i = 0
    // 2: sum = 0
    // (gdb) next
    // 1: i = 0
    // 2: sum = 10
    // (gdb) next
    // 1: i = 1
    // 2: sum = 10
    //
    // Skip to end of loop:
    // (gdb) until XX  (line after loop)
}

// ============================================================================
// MAIN
// ============================================================================

int main(int argc, char* argv[]) {
    cout << "╔══════════════════════════════════════════════════════════════╗" << endl;
    cout << "║                  GDB DEBUGGING EXAMPLES                       ║" << endl;
    cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
    
    cout << "\n=== Example 1: Basic Variables ===" << endl;
    calculateSum();
    
    cout << "\n=== Example 2: Containers ===" << endl;
    inspectContainers();
    
    cout << "\n=== Example 3: Pointers ===" << endl;
    debugPointers();
    
    cout << "\n=== Example 4: Call Stack ===" << endl;
    demonstrateCallStack();
    
    cout << "\n=== Example 5: Conditional Breakpoints ===" << endl;
    processNumbers();
    
    cout << "\n=== Example 6: Watchpoints ===" << endl;
    modifyVariable();
    
    cout << "\n=== Example 7: Segfault (not triggered) ===" << endl;
    causeSegfault(false);  // Change to true to see segfault
    
    cout << "\n=== Example 8: Objects ===" << endl;
    debugObjects();
    
    cout << "\n=== Example 9: Smart Pointers ===" << endl;
    debugSmartPointers();
    
    cout << "\n=== Example 10: Threads ===" << endl;
    debugThreads();
    
    cout << "\n=== Example 11: Memory Examination ===" << endl;
    examineMemory();
    
    cout << "\n=== Example 12: Loop Debugging ===" << endl;
    debugLoop();
    
    cout << "\n═══════════════════════════════════════════════════════════════" << endl;
    cout << "To debug this program:" << endl;
    cout << "  g++ -g -O0 examples.cpp -o examples -pthread" << endl;
    cout << "  gdb ./examples" << endl;
    cout << "  (gdb) break main" << endl;
    cout << "  (gdb) run" << endl;
    
    return 0;
}
Examples - C++ Tutorial | DeepML