Testing, Debugging & Sanitizers

টেস্টিং, ডিবাগিং ও sanitizer

Read: ~30 min 10 practice problems

1. Unit Testing with GoogleTest

test_math.cpp
#include <gtest/gtest.h>
#include "math.hpp"

TEST(MathTest, AddBasic) {
    EXPECT_EQ(add(2, 3), 5);
}

TEST(MathTest, SquareNegative) {
    EXPECT_EQ(square(-4), 16);
}

int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Alternatives: Catch2 (header-only, expressive), doctest (fastest compile).

2. Sanitizers — Cheap and Powerful

SanitizerFlagCatches
Address (ASan)-fsanitize=addressHeap/stack overflow, use-after-free, leaks
Undefined Behavior (UBSan)-fsanitize=undefinedSigned overflow, null deref, oob shifts
Thread (TSan)-fsanitize=threadData races
Memory (MSan)-fsanitize=memoryUninitialized reads (Clang only)
Recommended dev flag set -g -O1 -fsanitize=address,undefined -fno-omit-frame-pointer

3. gdb Quick Reference

gdb session
g++ -g -O0 app.cpp -o app
gdb ./app

(gdb) # Common commands
break main          # breakpoint at main
break file.cpp:42   # breakpoint at line
run                 # start program
next  / n           # next line (skip into calls)
step  / s           # step into call
print x  / p x      # print variable
backtrace / bt      # show call stack
continue / c        # resume
quit / q

4. assert & static_assert

assert.cpp
#include <cassert>

int divide(int a, int b) {
    assert(b != 0); // runtime check (debug only)
    return a / b;
}

static_assert(sizeof(int) == 4, "int must be 32-bit"); // compile-time

5. Profiling

  • perf (Linux): sampling profiler, perf record / perf report
  • callgrind (Valgrind): instruction-level call graph
  • gperftools: heap and CPU profiler
  • VTune (Intel): microarchitectural insights

6. Practice Problems

  1. Write a GoogleTest case for square(int).
    ✨ Show Answer

    See section 1.

  2. Compile flags to find a use-after-free?
    ✨ Show Answer
    g++ -fsanitize=address -g
  3. Compile flag for signed overflow detection?
    ✨ Show Answer
    g++ -fsanitize=undefined
  4. Set a breakpoint at line 42 of main.cpp in gdb.
    ✨ Show Answer
    break main.cpp:42
  5. Why is static_assert better than assert when possible?
    ✨ Show Answer

    Compile-time check — never runs at runtime, never costs anything, fails the build instead of the program.

  6. Why disable assert in release?
    ✨ Show Answer

    Defining NDEBUG turns assert into a no-op. Saves runtime cost. But: don't put side effects in assert!

  7. A test with EXPECT_NEAR for floating-point.
    ✨ Show Answer
    EXPECT_NEAR(computePi(), 3.14159, 1e-4);
  8. Find a data race with TSan — flag?
    ✨ Show Answer
    g++ -fsanitize=thread
  9. Get a stack trace in gdb after a crash.
    ✨ Show Answer
    (gdb) bt
  10. Why must tests be reproducible?
    ✨ Show Answer

    Flaky tests destroy trust. Use seeded RNGs, avoid timing-dependent assertions, mock external services.

Summary

Use a unit-testing framework (GoogleTest, Catch2). Compile dev builds with sanitizers — ASan + UBSan are nearly free and catch real bugs. Use gdb when symbols matter. static_assert for compile-time invariants. Profile with perf when performance matters.

Next Module → Capstone: Build a Real C++ System.