Блог пользователя oweMeEternity

Автор oweMeEternity, 4 месяца назад, По-английски

We have all been there. You submit your solution, wait anxiously, and then see the dreaded Runtime Error on test 2. Is it an array out-of-bounds index? A signed integer overflow? A null pointer dereference? Usually, you have to stare at your code or add a dozen printf statements to find out. But there is a better way.

By using compiler sanitizers and debug flags, you can make your compiler tell you exactly which line caused the error and why. Here is how to set up a powerful debugging environment on both Linux and Windows.

For Linux Users

Linux makes this incredibly easy with GCC. Instead of your standard compilation command, use the following flags to catch errors at runtime. g++ -g -O2 -fsanitize=undefined -fsanitize=address -fno-omit-frame-pointer -D_GLIBCXX_DEBUG main.cpp -o main.out && timeout 10s ./main.out

What do these flags do?

  • -g: Generates debug information. This ensures that when the program crashes, the error message points to the specific line number in your code.
  • -O2: Optimizes the code. (Note: You can also use -Og which is specifically designed for debugging, but -O2 mimics the judging environment closer).
  • -fsanitize=undefined (UBSan): Catches Undefined Behavior, such as signed integer overflows, division by zero, and invalid bitwise shifts.
  • -fsanitize=address (ASan): The MVP of debugging. It catches memory errors like out-of-bounds array access, use-after-free, and stack-buffer-overflow.
  • -fno-omit-frame-pointer: Helps the sanitizer generate better stack traces so you can see the chain of function calls leading to the crash.
  • -D_GLIBCXX_DEBUG: Enables debug mode for the C++ Standard Library (STL). This checks for things like accessing vector[i] where i is out of size, or dereferencing invalid iterators.
  • timeout 10s: Ensures the code runs no more than 10 seconds.

For Windows Users

Getting sanitizers (specifically AddressSanitizer) to work on Windows with MinGW is notoriously difficult. However, we can achieve a perfect setup using Clang and MSYS2.

Don't worry—this setup fully supports bits/stdc++.h and ext/pb_ds(policy-based data structures).

Step 0: Install MSYS2

Download and install MSYS2 (msys2-x86_64.exe) from msys2 website.

Add the binary path to your Windows Environment Variables (Path): C:\msys64\ucrt64\bin

Step 1: Install Compilers and Tools

Open the MSYS2 UCRT64 terminal and run the following to install Clang, LLVM, and necessary libraries:

pacman -S mingw-w64-ucrt-x86_64-clang mingw-w64-ucrt-x86_64-lld mingw-w64-ucrt-x86_64-compiler-rt mingw-w64-ucrt-x86_64-libc++ mingw-w64-clang-x86_64-compiler-rt mingw-w64-ucrt-x86_64-llvm

Step 2: Configure Sanitizer Runtime

We need to copy specific sanitizer libraries to where the linker expects them. Run this entire block in your MSYS2 UCRT64 shell:

mkdir -p /c/msys64/ucrt64/lib/clang/21/lib/x86_64-w64-windows-gnu; \
for f in /c/msys64/clang64/lib/clang/21/lib/windows/*; do cp "$f" \
"/c/msys64/ucrt64/lib/clang/21/lib/x86_64-w64-windows-gnu/$(basename \
"${f/-x86_64/}")";
done
cp /c/msys64/clang64/bin/libclang_rt.asan_dynamic-x86_64.dll /c/msys64/ucrt64/bin/

(Note, if this command fails, replace 21 with the installed version. write echo $(clang -dumpversion | cut -d. -f1) to get it)

Step 3: Set Default Linker:

Run in MSYS2 UCRT64 shell:

echo "-fuse-ld=lld" > "/ucrt64/bin/$(clang++ -dumpmachine).cfg"

Step 4: Precompile Header (Speed Boost)

Compiling with clang++ can be slow. To speed this up, we precompile bits/stdc++.h.

Make sure to use the exact same flags here as you will when compiling your code later.

Run in MSYS2 UCRT64 shell:

clang++ -x c++-header \
 -std=c++20 -g -O2 -fsanitize=undefined -fsanitize=address -fno-omit-frame-pointer -fno-sanitize-recover=all -D_GLIBCXX_DEBUG \
 -fpch-instantiate-templates /c/msys64/ucrt64/include/c++/$(g++ -dumpversion)/x86_64-w64-mingw32/bits/stdc++.h -o /c/msys64/stdc++.h.pch

Step 5: Compile and Run

Now, you can compile your C++ code with full runtime error checking using the precompiled header (from any terminal or IDE).

But make sure to use exact same flags which were used to precompile the header!

clang++ -std=c++20 -g -O2 -fsanitize=undefined -fsanitize=address -fno-omit-frame-pointer -fno-sanitize-recover=all -D_GLIBCXX_DEBUG \
-include-pch C:/msys64/stdc++.h.pch \
main.cpp -o main.exe

Bonus: Measuring Time on Windows

Linux users have the handy /usr/bin/time command. We can get similar functionality on Windows via MSYS2.

Install the time package: pacman -S time

Run with timeout: Use this command to run your executable. It limits execution time (e.g., 10 seconds) and prints time taken.

C:/msys64/usr/bin/time -f "%esec" C:/msys64/usr/bin/timeout 10s ./main.exe

** Remember to use UCRT64 shell for compiler setup steps. You may use CMD / Powershell / Sublime / Vim / VS Code while compiling.
Happy debugging!

  • Проголосовать: нравится
  • +30
  • Проголосовать: не нравится

»
4 месяца назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

Auto comment: topic has been updated by oweMeEternity (previous revision, new revision, compare).

»
4 месяца назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

Auto comment: topic has been updated by oweMeEternity (previous revision, new revision, compare).

»
4 месяца назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

For Mac User?

»
4 месяца назад, скрыть # |
 
Проголосовать: нравится +5 Проголосовать: не нравится

A note on the time commands: regular time is a shell built-in, not an external program. What's known as /usr/bin/time is an external program that's just typically installed in the folder /usr/bin. If you don't have it (which is quite common), you need to install it. If you have a shell that has the time builtin, you need to type the full path or the weak builtin will be used.