Tools/Debugging/Valgrind

From Blender Developer Wiki
Jump to: navigation, search

Valgrind

Valgrind can be used to detect memory errors that a debugger such as gdb would not otherwise find.

Starting Blender

install valgrind on your system.

OS X

http://blog.loudhush.ro/2010/02/compiling-valgrind-on-snow-leopard.html

Linux

On ubuntu/debian

  sudo apt-get install valgrind

Now you can go to the place you built blender and type...

valgrind --error-limit=no ./blender

This will run blender in valgrind and generate errors that you can use to find bugs before they crash blender or errors that may cause unpredictable behavior.

These options give more useful output but make blender run slower too.

valgrind --track-origins=yes  --error-limit=no ./blender

Example Output

==7847== Conditional jump or move depends on uninitialised value(s)
==7847==    at 0xC1A2CC: which_vfont (font.c:429)
==7847==    by 0xC1B476: BKE_text_to_curve (font.c:774)
==7847==    by 0xC4A3C2: do_makeDispListCurveTypes (displist.c:1692)
==7847==    by 0xC4AD04: makeDispListCurveTypes_forRender (displist.c:1870)
==7847==    by 0xA06554: init_render_curve (convertblender.c:2789)
==7847==    by 0xA0CAA9: init_render_object_data (convertblender.c:4392)
==7847==    by 0xA0CD65: add_render_object (convertblender.c:4441)
==7847==    by 0xA0D099: init_render_object (convertblender.c:4494)
==7847==    by 0xA0E3EF: database_init_objects (convertblender.c:4867)
==7847==    by 0xA0E923: RE_Database_FromScene (convertblender.c:4955)
==7847==    by 0xA7E0D1: do_render_3d (pipeline.c:1773)
==7847==    by 0xA7F001: do_render_fields_blur_3d (pipeline.c:2080)
==7847==  Uninitialised value was created by a heap allocation
==7847==    at 0x4C25FAD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7847==    by 0xE0F66B: MEM_mallocN (mallocn.c:308)
==7847==    by 0xD0B620: read_struct (readfile.c:1284)
==7847==    by 0xD193D6: read_data_into_oldnewmap (readfile.c:5395)
==7847==    by 0xD195E5: read_libblock (readfile.c:5453)
==7847==    by 0xD2DDF9: blo_read_file_internal (readfile.c:11249)
==7847==    by 0xD32CA8: BLO_read_from_file (readblenentry.c:252)
==7847==    by 0xC64399: BKE_read_file (blender.c:376)
==7847==    by 0x6F21C6: load_file (creator.c:880)
==7847==    by 0xD41164: BLI_argsParse (BLI_args.c:276)
==7847==    by 0x6F2B6A: main (creator.c:1136)

Analyzing Output

There are a few things to consider when reading the output,

  • un-initialized memory may be caused by external libraries, unless these libraries are compiled with debug symbols there wont be as useful info. This cases are easy enough to identify, its just good to be mindful that not all reports are caused by issues in blender.
  • Reports about un-initialized stack variables will show up at the function beginning even if the variable is defined within a block of the code.


False Positives

Valgrind tends to give errors with blender on startup which you can safely ignore.

  • X11: Anything related to X11/Xlib.
  • Anything relating to drivers (sound, video).
  • OpenGL: The OpenGL driver.
  • Python: Python uses its own allocator which valgrind detects errors in. (can be resolved with a custom Python build - see below)
  • SDL/Sound:
  • Blenders Undo System: Uses memory comparison which causes errors.

The kinds of errors you get also depend on how valgrind is installed, since some installations come with default suppression files.

As stated, these happen most when starting blender, so be prepared to ignore many errors on startup.

Compiler Options

Another source of false positives can stem from the options used when compiling blender. While not essential starting out. if you are spending a long time to track a bug down its better to be sure its reported correctly.

  • compile with debug enabled.
  • disable optimizations
  • disable in-lining

for gcc these flags are

-O0 -g3 -fno-inline


Python Compiler Options

As mentioned before, python can be the cause of many false positives.

This can be avoided by building python without its own malloc replacement '--without-pymalloc' and will quiet most false positives.

Example of building python with debug options and without pymalloc.

CFLAGS="-fno-inline -g3 -O0" ./configure --prefix=/opt/py33 --enable-shared --with-pydebug --without-pymalloc --with-valgrind
make
make install

If you really don't need python, you can also disable the build option WITH_PYTHON but this will remove most of the user interface.

Debugging

This requires using gdbserver and isn't essential for valgrind to be useful.

see: http://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.gdbserver-gdb

Error suppression

*Note*. setting suppressions is not strictly necessary but if you run valgrind a lot you may want to set it up - trying to find useful errors in thousands of reports is not really fun.

If you try running the command above you'll see meny errors, most of which are not useful. to address this you need to make a suppressions file.

Generic Suppressions

Note: heavily based on http://stackoverflow.com/questions/2375726/how-do-you-tell-valgrind-to-completely-suppress-a-particular-so-file.

We said above that we could ignore Python, OpenGL driver, etc. errors. You can tell valgrind to silently do that by using its --suppressions option and a text file defining ignore rules.

The general format is basically:

# A line starting with a '#' is a comment.

# Start of a rule
{
   # Name of the rule, you can use what you want here...
   Foobar

   # Memcheck specifies the type of error this rule will "silent".
   Memcheck:Cond

   # Then we have an optional stack of object files (i.e. libraries).
   # You should use the full lib paths.
   # The '...' are "frame wildcards", they say this rule is valid
   # for any object file before and after libpython.
   ...
   obj:/usr/lib/x86_64-linux-gnu/libpython3.3m.so.1.0
   ...

   # Then we have an optional stack of function calls.
   # Here again, the '...' are "frame wildcards", for functions this time.
   ...
   fun:gpu_buffer_setup
   fun:gpu_buffer_setup_type
   fun:gpu_buffer_setup_common
   ...

# Close the rule
}

Here is a file that suppress any error for a start & stop of Blender, on my machine. It should be easy to adapt (just check the lib paths, and replace the OpenGL driver part by your own version): blender_sup.txt. Simply use it like this:

valgrind --suppressions=~/blender_sup.txt ./blender

Warning: This file suppresses thousands of errors! In case you are investigating an area it “silents” (Blender's GPU draw code, and mostly Python and fglrx), please consider commenting out related rules.

Refining

Above file (once adapted to your system) should cover nearly all false positives. However, you may want to add more specific rules. In this case, use the following method:

valgrind --suppressions=~/blender_sup.txt --gen-suppressions=all ./blender 2> ~/blender_sup_TEMP.txt

This will add suppression rules to ~/blender_sup_TEMP.txt for all errors not already covered by your current ~/blender_sup.txt file. Open blender and do some operations generating the errors you want to get rid of…

Open ~/blender_sup_TEMP.txt, select the errors you want to ignore, and copy their content between the braces {...} into your ~/blender_sup.txt file. You should also add some comments about what/why you add those rules.

Now you can can use valgrind as usual (using the --suppressions=~/blender_sup.txt option), just keep in mind you disabled a bunch of errors, in case you can’t find what you are looking for!

Some errors may keep appearing and you can ignore them:

  • Undo - blender compares un-initialized memory when running undo, you cant supress this from 1 place because undo is called in many areas.
  • Python - Python does its own memory management which confuses valgrind, and is also called in many places.