By default, libstdc++ is built with efficiency in mind, and therefore performs little or no error checking that is not required by the C++ standard. This means that programs that incorrectly use the C++ standard library will exhibit behavior that is not portable and may not even be predictable, because they tread into implementation-specific or undefined behavior. This includes uses of memory that has already been freed and other memory-smashing bugs. To detect some of these errors before they can become problematic, libstdc++ offers a debug mode that provides additional checking of library facilities, and will report errors in the use of libstdc++ as soon as they can be detected by emitting a description of the problem to standard error and aborting the program.
The libstdc++ debug mode performs checking for many areas of the C++ standard, but the focus is on checking interactions among standard iterators, containers, and algorithms, including:
sort
algorithm requires
that its iterator parameters first
and last
form a valid iterator range, and that the
predicate is a strict weak ordering; the libstdc++ debug mode will
detect an error if either of these conditions can be shown not to
hold.To use the libstdc++ debug mode, compile your application with the
compiler flag -D_GLIBCXX_DEBUG
. Note that this flag
changes the sizes and behavior of standard class templates such
as std::vector
, and therefore you can only link code
compiled with debug mode and code compiled without debug mode if no
instantiation of a container is passed between the two translation
units. This does not mean that you are required to
recompile your entire application: for instance, if one source file
uses std::vector
but std::vector
doesn't
occur in its public interface, that file can be recompiled in debug
mode even if the rest of the program is not compiled in debug
mode.
How much should you recompile to use debug mode? Generally, you'll want to recompile your entire application to enable the debug mode's extra run-time checking globally. However, if there is a particular self-contained module that needs checking, recompile only that module. If in fact the module was not self-contained, the result will be a link error.
When it is not feasible to recompile your application, or
only specific containers need checking, debugging containers are
available as GNU extensions. These debugging containers are
functionally equivalent to the containers used in debug mode,
e.g., __gnu_debug::vector<int>
is equivalent
to std::vector<int>
in debug mode, but
the __gnu_debug
versions can be used in either release
or debug mode without changing semantics. However, unlike the
containers in namespace
std
, these containers may not be specialized, because
they are introduced into namespace std
with a using
declaration. Note that the std::basic_string
template (of
which std::string
and std::wstring
are
instantiations) differs from the other containers in this area,
because using __gnu_debug::basic_string
(resp. __gnu_debug::string
and __gnu_debug::wstring
) is the only way to get a
fully-debugging string, and is therefore not equivalent to
std::basic_string
in debug mode. The following table
provides the names and headers of the debugging containers:
Container | Header | Debug container | Debug header |
---|---|---|---|
std::bitset | <bitset> | __gnu_debug::bitset | <debug/bitset> |
std::deque | <deque> | __gnu_debug::deque | <debug/deque> |
std::list | <list> | __gnu_debug::list | <debug/list> |
std::map | <map> | __gnu_debug::map | <debug/map> |
std::multimap | <map> | __gnu_debug::multimap | <debug/map> |
std::multiset | <set> | __gnu_debug::multiset | <debug/set> |
std::set | <set> | __gnu_debug::set | <debug/set> |
std::string | <string> | __gnu_debug::string | <debug/string> |
std::wstring | <string> | __gnu_debug::wstring | <debug/string> |
std::basic_string | <string> | __gnu_debug::basic_string | <debug/string> |
std::vector | <vector> | __gnu_debug::vector | <debug/vector> |
__gnu_cxx::hash_map | <ext/hash_map> | __gnu_debug::hash_map | <debug/hash_map> |
__gnu_cxx::hash_multimap | <ext/hash_map> | __gnu_debug::hash_multimap | <debug/hash_map> |
__gnu_cxx::hash_set | <ext/hash_set> | __gnu_debug::hash_set | <debug/hash_set> |
__gnu_cxx::hash_multiset | <ext/hash_set> | __gnu_debug::hash_multiset | <debug/hash_set> |
A program that does uses the C++ standard library correctly
will maintain the same semantics under debug mode as it had with
the normal (release) library. All functional and exception-handling
guarantees made by the normal library also hold for the debug mode
library, with one exception: performance guarantees made by the
normal library may not hold in the debug mode library. For
instance, erasing an element in a std::list
is a
constant-time operation in normal library, but in debug mode it is
linear in the number of iterators that reference that particular
list. So while your (correct) program won't change its results, it
is likely to execute more slowly.
libstdc++ includes many extensions to the C++ standard library. In
some cases the extensions are obvious, such as the hashed
associative containers, whereas other extensions give predictable
results to behavior that would otherwise be undefined, such as
throwing an exception when a std::basic_string
is
constructed from a NULL character pointer. This latter category also
includes implementation-defined and unspecified semantics, such as
the growth rate of a vector. Use of these extensions is not
considered incorrect, so code that relies on them will not be
rejected by debug mode. However, use of these extensions may affect
the portability of code to other implementations of the C++ standard
library, and is therefore somewhat hazardous. For this reason, the
libstdc++ debug mode offers a "pedantic" mode (similar to
GCC's -pedantic
compiler flag) that attempts to emulate
the semantics guaranteed by the C++ standard. In pedantic mode, for
instance, constructing a std::basic_string
with a NULL
character pointer would result in an exception under normal mode or
non-pedantic debug mode (this is a libstdc++ extension), whereas
under pedantic debug mode libstdc++ would signal an error. To enable
the pedantic debug mode, compile your program with
both -D_GLIBCXX_DEBUG
and -D_GLIBCXX_DEBUG_PEDANTIC
. Note
that _GLIBCXX_DEBUG_PEDANTIC
also affects the
containers in the __gnu_debug
namespace, so
specifying _GLIBCXX_DEBUG_PEDANTIC
without -D_GLIBCXX_DEBUG
will make
the __gnu_debug
containers more pedantic but will
leave the containers in namespace std
unchecked.
The following library components provide extra debugging capabilities in debug mode:
std::bitset
std::deque
__gnu_cxx::hash_map
__gnu_cxx::hash_multimap
__gnu_cxx::hash_multiset
__gnu_cxx::hash_set
std::list
std::map
std::multimap
std::multiset
std::set
std::vector
There are various third party memory tracing and debug utilities
that can be used to provide detailed memory allocation information
about C++ code. An exhaustive list of tools is not going to be
attempted, but includes mtrace
, valgrind
,
mudflap
, and purify
. Also highly
recommended are libcwd
and some other one that I
forget right now.
Regardless of the memory debugging tool being used, there is one
thing of great importance to keep in mind when debugging C++ code
that uses new
and delete
:
there are different kinds of allocation schemes that can be used by
std::allocator
. For implementation details, see
this document and look specifically for
GLIBCPP_FORCE_NEW
.
In a nutshell, the default allocator used by
std::allocator
is a high-performance pool allocator, and can
give the mistaken impression that memory is being leaked, when in
reality the memory is still being used by the library and is reclaimed
after program termination.
For valgrind, there are some specific items to keep in mind. First of all, use a version of valgrind that will work with current GNU C++ tools: the first that can do this is valgrind 1.0.4, but later versions should work at least as well. Second of all, use a completely unoptimized build to avoid confusing valgrind. Third, use GLIBCPP_FORCE_NEW to keep extraneous pool allocation noise from cluttering debug information.
Fourth, it may be necessary to force deallocation in other
libraries as well, namely the "C" library. On linux, this can be
accomplished with the appropriate use of the
__cxa_atexit
or atexit
functions.
#include <cstdlib> extern "C" void __libc_freeres(void); void do_something() { } int main() { atexit(__libc_freeres); do_something(); return 0; }
or, using __cxa_atexit
:
extern "C" void __libc_freeres(void); extern "C" int __cxa_atexit(void (*func) (void *), void *arg, void *d); void do_something() { } int main() { extern void* __dso_handle __attribute__ ((__weak__)); __cxa_atexit((void (*) (void *)) __libc_freeres, NULL, &__dso_handle ? __dso_handle : NULL); do_test(); return 0; }
Suggested valgrind flags, given the suggestions above about setting up the runtime environment, library, and test file, might be:
valgrind -v --num-callers=20 --leak-check=yes --leak-resolution=high --show-reachable=yes a.out
Many options are available for gdb itself: please see "GDB features for C++" in the gdb documentation. Also recommended: the other parts of this manual.
These settings can either be switched on in at the gdb command line, or put into a .gdbint file to establish default debugging characteristics, like so:
set print pretty on set print object on set print static-members on set print vtbl on set print demangle on set demangle-style gnu-v3
The verbose termination handler gives information about uncaught exceptions which are killing the program. It is described in the linked-to page.
Return to the top of the page or to the libstdc++ homepage.
See license.html for copying conditions. Comments and suggestions are welcome, and may be sent to the libstdc++ mailing list.