andreyv's blog

By andreyv, 10 years ago, translation, In English

As you know, the C++ language assumes that the programmer is always correct. Therefore C++ compilers don't add additional checks to the program, such as checks for null pointer dereference or out-of-bounds array access. This is good, because C++ programs run as fast as possible, and this is bad, because sometimes we may spend a long time debugging some silly mistake. We would want that the compiler can find such mistakes automatically. And many compilers can! In this post I will show various GCC options that do this. Previously zakharvoit already wrote about this here.

All options that will follow should be added to the GCC command line. In various IDEs you can do it in IDE or compiler settings. Many of the options can also be used with Clang (for example, in Xcode). For MSVC++, I think, there is nothing better than Debug mode and /W4.  

GCC warnings

Of course, the first step to debugging is to enable compiler warnings. Even this alone often helps. As a bare minimum I should name -Wall -Wextra -O2. The last option is needed because some warnings are only enabled together with optimization. Below I will list some useful options that are not enabled with -Wall -Wextra.

  • -pedantic — warns about non-standard C++ language extensions. This way you can eliminate stuff that might not be supported by the testing server, and save time needed to rewrite the code. This is best used together with -std=c++03 or -std=c++11. For example, -pedantic -std=c++03 will warn about
printf("%lf\n", 1.0);

— the correct way is

printf("%f\n", 1.0);
  • -Wshadow — warns if a declared name shadows the same name in some outer level. For example, this will cause a warning:
int n;
void solve()
{
    // Solve the problem
}
int main()
{
    int n; cin >> n;
    solve();
}
  • -Wformat=2 — warns if an argument type in printf()/scanf() does not correspond to the format string. This is partially enabled by -Wall, but -Wformat=2 is more strict.

  • -Wfloat-equal — warns if two floating point values are compared directly: a == b. Usually the correct way is: fabs(a - b) < eps.

  • -Wconversion — warns if data can be lost in an implicit conversion.¹ Most often it is accidental assignment of a long long value to an int variable. I have this warning enabled since I failed a problem by writing pair<int, int> instead of pair<int, long long> :)

    ¹ An explicit cast (for example, (double)my_long_long_var) will not trigger this warning.

  • -Wlogical-op — warns about logical operators in places where GCC expects bitwise operators.

  • -Wshift-overflow=2 — warns about left shift overflows (GCC 6+).

  • -Wduplicated-cond — warns about repeated conditions in if (…) else if (…) (GCC 6+).

There are also -Wcast-qual и -Wcast-align, but they are less useful (though don't hurt). You can read more about GCC warnings here: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

Standard library tools

Besides the compiler itself, there is also the standard C/C++ library. It also has some options that aid debugging.

  • -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC — these options turn on a special debug mode in the GNU C++ standard library. In this mode standard containers and algorithms apply various checks. For example, this code
int main()
{
    vector<int> v(3);
    cout << v[7] << endl;
}

prints

 /usr/include/c++/4.9.2/debug/vector:357:error: attempt to subscript 
container with out-of-bounds index 7, but container only holds 3
elements.

And this code:

int main()
{
    int arr[] = { 3, 1, 2 };
    cout << binary_search(arr, arr+3, 2) << endl;
}

prints

 /usr/include/c++/4.9.2/bits/stl_algo.h:2267:error: elements in iterator
range [__first, __last) are not partitioned by the value __val.
  • -D_FORTIFY_SOURCE=2 (only on Linux/glibc) — this option inserts security checks, such as checks for buffer overflows, into the program. For example, this program:
int main()
{
    char s[9];
    strcpy(s, "too large");
    cout << s << endl;
}

when compiled with this option, prints

 *** buffer overflow detected ***: ./a.out terminated 

GCC tools

The GCC compiler also contains tools that help find mistakes in programs.

  • -fsanitize=address (only in GCC 4.8+ and Clang) — this option inserts memory access checks into the program, such as checks for out-of-bounds accesses. This program:
int arr[3];

int main()
{
    for (int i = 0; i <= 3; i++)
    {
        arr[i] = i;
        cout << arr[i] << " ";
    }
    cout << endl;
}

prints

 =================================================================
==15496==ERROR: AddressSanitizer: global-buffer-overflow on address 0x00000060152c at pc 0x400ad4 bp 0x7fffbac43e00 sp 0x7fffbac43df0
WRITE of size 4 at 0x00000060152c thread T0
. . .
  • -fsanitize=undefined -fno-sanitize-recover (only in GCC 4.9+ and Clang) — a similar option that catches undefined behavior, for example, a null pointer dereference. This code:
int main()
{
    int *p;
    cout << *p << endl;
}

prints

 x.cpp:12:14: runtime error: load of null pointer of type 'int' 

-fsanitize=undefined can also find divisions by zero, undefined integer shifts, signed integer overflows and leaving a function without a return value.

These two options are described in https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html. There is also the -fstack-protector option.

Everything together

Putting everything together, we get

-Wall -Wextra -pedantic -std=c++11 -O2 -Wshadow -Wformat=2 -Wfloat-equal -Wconversion -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -D_FORTIFY_SOURCE=2 -fsanitize=address -fsanitize=undefined -fno-sanitize-recover -fstack-protector

All this (or selected parts) can be added to GCC command line on the local computer. Therefore, we run a debug-enabled version on our computer, and the usual version on the server. Naturally, the local debug version will be slower — the biggest impact is from the -fsanitize options (can cause 2× and more slowdown). But you can easily account for this, and it is a small price to pay. Depending on the compiler version and operating system, not every option from this list will work — just take such options out.

I hope that this post will help people spend less time debugging and more time solving problems. Good luck :)

UPD1: There is a problem with -D_GLIBCXX_DEBUG, but a workaround is known. (If string s; cin >> s; fails for you, try removing -fwhole-program from the command line or adding s.reserve(...); in the code).

UPD2: Updated options:

  • Added -fno-sanitize-recover (GCC 5+)
  • Removed -lmcheck (replaced by -fsanitize=address)
  • Removed -ftrapv (replaced by -fsanitize=undefined)
  • Removed -fwhole-program (unstable and is not a debug option)

UPD3: Added -Wshift-overflow=2 and -Wduplicated-cond (GCC 6+).

  • Vote: I like it
  • +899
  • Vote: I do not like it

| Write comment?
»
10 years ago, # |
  Vote: I like it 0 Vote: I do not like it

Thank you... I did not know about this options... It is very useful...

»
10 years ago, # |
  Vote: I like it +13 Vote: I do not like it

Although these options is good in some aspects, there are some notable things. In real contests, such as IOI, APIO, or National contests, we won't have time to add all of these options. Therefore, we shouldn't overly depend on them.

  • »
    »
    10 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    I agree. I think only -Wall -Wextra -O2 or similar can be remembered and entered quickly. And of course using these options does not mean we should be careless when writing code.

    • »
      »
      »
      10 years ago, # ^ |
        Vote: I like it +16 Vote: I do not like it

      I believe one can remember much more(maybe not a full line) easily. It's much less then code template

  • »
    »
    10 years ago, # ^ |
    Rev. 2   Vote: I like it +5 Vote: I do not like it

    Anyway, we may do at home a lot of 'mistakes' that may go unnoticed (for example, giving you AC anyway, even when you use an undefined behaviour that just happens to work the way you intended in that case with the judge compiler).

    I believe that having the compiler always point out this stuff for you can teach you a lot and help you get rid of 'bad habits', improving your coding for live contests even if you won't have all those flags there.

»
10 years ago, # |
  Vote: I like it 0 Vote: I do not like it

I am affected by the bug of -D_GLIBCXX_DEBUG causing crash in cin or getline to an empty string. The bug happens to me on Windows( Cygwin32 ,Cygwin64 and MinGW32) but not Linux and not Windows Nuwen MinGW . I wrote a question on stackoverflow (http://stackoverflow.com/questions/28708802/cin-not-working-with-empty-string-when-glibcxx-debug-on-windows) . Then I discovered that GCC 4.2 on OS X suffered the same bug (http://lists.apple.com/archives/cocoa-dev/2009/Sep/msg01096.html) and (http://stackoverflow.com/questions/1962685/xcode-stl-c-debug-compile-error?lq=1)

Does anyone know if this bug is reported to GCC or not? or what is the root cause of it? If anyone has good information about this bug please report if it is not reported.

»
10 years ago, # |
  Vote: I like it 0 Vote: I do not like it

On Code::blocks these command-line flags goes to:

Settings Menu ->Compiler ->Compiler Settings-> Other options tab

and another copy of the same flags to:

Settings Menu ->Compiler ->Linker Settings->Other linker options textbox

  • »
    »
    5 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    Thanks, I couldn't compile without add them to linker options. Can you please explain why is it necessary?

»
8 years ago, # |
  Vote: I like it +18 Vote: I do not like it

We should clearly state that those flags are not for ordinary usage and are targeted at "finding errors".

Some of those flags should be used with caution and with lucid understanding of the consequences. It can be important outside of competitive programming, and, maybe, inside.

  1. glibcxx debug mode, ASAN / UBSAN yield a significant slowdown and also in glibcxx debug mode many algorithm complexity requirements are not satisfied (e.g. sorting could become O(n^2)).
  2. ASAN / UBSAN seeks for -O1, not -O2, otherwise the reporting might be reduced.
»
8 years ago, # |
Rev. 2   Vote: I like it 0 Vote: I do not like it

Can anyone explain why

vector<int> x={2,1,3};
cout<<binary_search(x.begin(),x.end(),3);

does not show error and instead prints 1 even with -D_GLIBCXX_DEBUG and -D_GLIBCXX_DEBUG_PEDANTIC (while binary searching 2 does show error)? How is checking done if the container is sorted or not?

  • »
    »
    8 years ago, # ^ |
      Vote: I like it +3 Vote: I do not like it

    I believe the formal answer is "because GCC has no obligation whatsoever to do so".

    Deeper reason is probably that debug checks inside binary_search ensure order of elements which are looked at by the binary search (e.g. the middle element), and nothing else. It may be done in order to preserve logarithmic complexity of binary search. And that may be done in order to prevent significant slowdown of applications in debug mode.

    • »
      »
      »
      8 years ago, # ^ |
        Vote: I like it 0 Vote: I do not like it

      I experimented with binary_search with debug mode on, and it looks like O(n). Another hypothesis I can put up is, it checks if the container is partitioned by the element to be searched. That is, if x[i]<p<x[j], then j>i where p is the value to be searched. This is probably what makes it O(n). To check this, I shuffled x[0..k) and x[k+1..n) and searched for x[k] and it worked fine. For other elements, it raised an error.

  • »
    »
    6 years ago, # ^ |
      Vote: I like it +5 Vote: I do not like it

    binary_search does not require the range to be sorted, only that it is partitioned with respect to the argument, which makes perfect sense. More info here.

»
7 years ago, # |
  Vote: I like it 0 Vote: I do not like it

Can anyone help me, where shall I add above debug flags mentioned in blog, in sublime text ? I use Sublime Text 3 build system for compiling my code on Ubuntu 16.04

  • »
    »
    6 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    In the custom build system, there should be an array "cmd". Put these flags inside it.

»
6 years ago, # |
  Vote: I like it +5 Vote: I do not like it

One very useful option that is not listed in this post is -Wsign-conversion. Just -Wconversion doesn't warn about implicit conversion between unsigned and signed types.

»
6 years ago, # |
  Vote: I like it +3 Vote: I do not like it

I want to make a small correction to the blog with information that I checked in my machine:

-fsanitize=undefined does not find divisions by zero and leaving a function without a return value,-Wall -Wextra -O2 does this. But -fsanitize=undefined does warn about integer overflow.

Also just to add some extra information that may be helpful for others:

-fsanitize=address checks for out of bounds in both arrays and STL containers

-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC checks for out of bounds only in stl containers, not in arrays. But it also does other stuff regarding STL though.

  • »
    »
    6 years ago, # ^ |
      Vote: I like it +4 Vote: I do not like it

    Is it possible to get the line number in the code when the program raises an exception? While it is definitely helpful to know that there is a bug, it would be extremely valuable to know where it has occurred.

    • »
      »
      »
      6 years ago, # ^ |
        Vote: I like it +8 Vote: I do not like it

      Try adding debug info with the -g option. The sanitizers will pick this up and show line numbers in their error messages.

      That doesn't work for _GLIBCXX_DEBUG, but in that case, you can run the program under a debugger and print the backtrace there.

      andreyv can you please add this info to the article?

»
5 years ago, # |
  Vote: I like it +17 Vote: I do not like it

Two notes about -Wconversion:

  • For int MOD;, the expression int x = y*(long long)z % MOD; will raise a warning. (reported at https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92220 )
  • Conversions in system headers won't be reported. While this is expected (because the C++ library is not warning-free), some real bugs can be hidden.
    Example: std::array<long long,1> b{{111111111111LL}}; std::count_if(begin(b), end(b), [](int x){ return x > 0; }); will not raise any warning although long long is silently converted to int when passed to the lambda.
»
5 years ago, # |
Rev. 2   Vote: I like it -8 Vote: I do not like it

In my pc(Windows 10, i5-7th gen, 8GB RAM) -fsanitize=address -fsanitize=undefined -fno-sanitize-recover -fstack-protector is not working . It shows :

c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe:
cannot find lasan                                       
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: 
cannot find -lubsan                                      
collect2.exe: error: ld returned 1 exit status                                                                

Please help me how should I solve this problem. I use FAR MANAGER.

  • »
    »
    5 years ago, # ^ |
    Rev. 3   Vote: I like it 0 Vote: I do not like it

    same. any help :( ? or what is the reason?

  • »
    »
    4 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    Also getting the same error on GEANY

  • »
    »
    4 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    Seems that -fsanitize=address is not support by some version of GCC on windows.

    I've tested that before, and it's usable when using GCC on WSL.

    So I guess that is the platform's problem.

»
5 years ago, # |
  Vote: I like it 0 Vote: I do not like it

GCC 10 supports static analyzer (read more) which be enabled by -fanalyzer flag.

Sample output: catching of double free (taken from the link above).

$ gcc -c -fanalyzer double-free-1.c
double-free-1.c: In function ‘test’:
double-free-1.c:6:3: warning: double-‘free’ of ‘ptr’ [CWE-415] [-Wanalyzer-double-free]
    6 |   free(ptr);
      |   ^~~~~~~~~
  ‘test’: events 1-2
    |
    |    5 |   free(ptr);
    |      |   ^~~~~~~~~
    |      |   |
    |      |   (1) first ‘free’ here
    |    6 |   free(ptr);
    |      |   ~~~~~~~~~
    |      |   |
    |      |   (2) second ‘free’ here; first ‘free’ was at (1)
    |
»
4 years ago, # |
  Vote: I like it +5 Vote: I do not like it

-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC: Is it possible to print the line from the source code where out of bounds access is happening to the console? It currently prints the line number from vector file.

»
4 years ago, # |
  Vote: I like it 0 Vote: I do not like it

Can someone share their sublime build file for these flags on windows, specifially the one that Errichto uses on his geany setup on linux ?

Here is the link to his setup -> https://github.com/Errichto/youtube/wiki/Linux-setup

I want mainly those sanitizers in the sublime build for c++.

It would be a great help if someone can possibly share it.

»
4 years ago, # |
Rev. 6   Vote: I like it 0 Vote: I do not like it

If anyone is using using Linux terminal to compile their code probably like this: g++ program.cpp or gcc program.c Then we can compile same program with above flags using simple syntax below:

compile program.cpp

Method Here

  • »
    »
    4 years ago, # ^ |
      Vote: I like it +12 Vote: I do not like it

    Or, the same thing can be achieved by declaring some alias in the ~/.bashrc file.