********
Valgrind
********
`Valgrind `_ is a tool that can detect memory
leaks, using uninitialized variables, In essence, valgrind translates
the executable to an intermediate, architecture-independent form, and
the tools in valgrind interpret this code. As a result, it can
instrument the code with a lot of different checks, but this comes at
a performance cost.
Usually you will run your code through valgrind even if there are no
apparently bugs, just to ensure that you didn't miss anything. If you
know you have bugs, valgrind can help you find them.
We have not (yet) been doing manually memory management (because
modern C++ really makes it rare that we need to), so this greatly
reduces our chance of memory leaks. But if we did, valgrind would be
able to tell us if we didn't free up any memory.
Uninitialized variables
=======================
Consider the following example:
.. literalinclude:: ../../examples/valgrind/uninitialized.cpp
:language: c++
:caption: ``uninitialized.cpp``
here we are using the object ``b`` without initializing it. If we run this directly,
the code seems to work. Now if we instead run it via valgrind:
.. prompt::
g++ -g -o uninitialized uninitialized.cpp
valgrind ./uninitialized
we see that there are errors.
.. note::
When we compile, we added the ``-g`` flag. This adds debugging information to
the executable.
Here's the first:
::
==1457506== Conditional jump or move depends on uninitialised value(s)
==1457506== at 0x4BDC2C8: __printf_fp_l (printf_fp.c:397)
==1457506== by 0x4BF5FF7: __vfprintf_internal (vfprintf-internal.c:1646)
==1457506== by 0x4C06CD9: __vsnprintf_internal (vsnprintf.c:114)
==1457506== by 0x49664F2: std::__convert_from_v(__locale_struct* const&, char*, int, char const*, ...) (c++locale.h:92)
==1457506== by 0x4996400: std::ostreambuf_iterator > std::num_put > >::_M_insert_float(std::ostreambuf_iterator >, std::ios_base&, char, char, double) const (locale_facets.tcc:1023)
==1457506== by 0x49A4B4F: put (locale_facets.h:2458)
==1457506== by 0x49A4B4F: std::ostream& std::ostream::_M_insert(double) (ostream.tcc:73)
==1457506== by 0x401313: main (uninitialized.cpp:16)
==1457506==
That's where we use the resulting vector, but the tool also tells us to turn on ``--track-origins=yes`` to see
where the problem first arises:
.. prompt:: bash
valgrind --track-origins=yes ./uninitialized
::
==1457650== Conditional jump or move depends on uninitialised value(s)
==1457650== at 0x4BDC2C8: __printf_fp_l (printf_fp.c:397)
==1457650== by 0x4BF5FF7: __vfprintf_internal (vfprintf-internal.c:1646)
==1457650== by 0x4C06CD9: __vsnprintf_internal (vsnprintf.c:114)
==1457650== by 0x49664F2: std::__convert_from_v(__locale_struct* const&, char*, int, char const*, ...) (c++locale.h:92)
==1457650== by 0x4996400: std::ostreambuf_iterator > std::num_put > >::_M_insert_float(std::ostreambuf_iterator >, std::ios_base&, char, char, double) const (locale_facets.tcc:1023)
==1457650== by 0x49A4B4F: put (locale_facets.h:2458)
==1457650== by 0x49A4B4F: std::ostream& std::ostream::_M_insert(double) (ostream.tcc:73)
==1457650== by 0x401313: main (uninitialized.cpp:16)
==1457650== Uninitialised value was created by a stack allocation
==1457650== at 0x401226: main (uninitialized.cpp:4)
Now it is telling us that the uninitialized variable is on the stack. In our code, ``b`` is the only stack
variable.
Bounds checking
===============
Consider the following code:
.. literalinclude:: ../../examples/valgrind/bounds.cpp
:language: c++
:caption: ``bounds.cpp``
Here, we are accessing the ``vector a`` out of bounds. When we just run it on the command line, it works fine.
When we run it through valgrind, we see:
.. prompt:: bash
g++ -g -o bounds bounds.cpp
valgrind ./bounds
we see:
::
==1458835== Invalid read of size 4
==1458835== at 0x401281: main (bounds.cpp:8)
==1458835== Address 0x4da4c94 is 0 bytes after a block of size 20 alloc'd
==1458835== at 0x4844FF5: operator new(unsigned long) (vg_replace_malloc.c:422)
==1458835== by 0x401A17: __gnu_cxx::new_allocator::allocate(unsigned long, void const*) (new_allocator.h:127)
==1458835== by 0x4018DD: std::allocator_traits >::allocate(std::allocator&, unsigned long) (alloc_traits.h:464)
==1458835== by 0x401791: std::_Vector_base >::_M_allocate(unsigned long) (stl_vector.h:346)
==1458835== by 0x4015DE: void std::vector >::_M_range_initialize(int const*, int const*, std::forward_iterator_tag) (stl_vector.h:1582)
==1458835== by 0x4013DE: std::vector >::vector(std::initializer_list, std::allocator const&) (stl_vector.h:629)
==1458835== by 0x401263: main (bounds.cpp:6)
This shows us that on line 8, we did an invalid read of size 4 (bytes).