wiki:OsgDebugging

Version 4 (modified by Torben Dannhauer, 14 years ago) (diff)

--

Short Introduction to Debugging and Hunting Memory Leaks on Windows and Linux

Debugging the application for memory leak is quite easy. This indroduction adressess debugging beginners but assumes your are familiar with basic debugging techniques (stop and go, watching values..)

Windows

The windows debugging how to is based on Microsoft Visual Studio 2008 Professional. Using another MSVC Version should be possible without modification. Should... :)

The standart program debugging regarding software errors is easy:

  • On Crashes: Start the software with the debugger, use it until it crashes. The debugger will stop and you can go back at the call stack and look for the first function located in your programm. This function is your first approach to search for your error :)
  • On program logic errors without crash: Place debug messages in your code, and set breakpoints at interesting lines so you can watch variable values when the debugger break at that points.

Memory Leak Debugging

While MSVC outputs memoryleaks automatically. it is very hard to modify it's behaviour to get useful leak reports.

What do wo want? A report of every leak with the following information

  • Filename whre the leak was caused.
  • Function name where the leak was caused.
  • Linenumer where the leak was caused.

todo: how to modify sourcecode to get the informations.

pre main()

#ifdef _DEBUG
        #ifdef WIN32
                // Declare this in header.
                #define _CRTDBG_MAP_ALLOC
                #include <stdlib.h>
                #include <crtdbg.h>
        #endif 
#endif 

Inside main()

#ifdef _DEBUG
        #ifdef WIN32
                #include <leakDetection.h>      // for main: must be inside function. In classes: headerfile inside calss is sufficient
                int tmp_flag;

                HANDLE log_file = CreateFile("mem_log.txt", GENERIC_WRITE,FILE_SHARE_WRITE,
                        NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

                _CrtSetReportMode(_CRT_ASSERT,_CRTDBG_MODE_FILE | _CRTDBG_MODE_WNDW |
                        _CRTDBG_MODE_DEBUG);
                _CrtSetReportMode(_CRT_WARN,_CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
                _CrtSetReportMode(_CRT_ERROR,_CRTDBG_MODE_FILE | _CRTDBG_MODE_WNDW |
                        _CRTDBG_MODE_DEBUG);

                // output to the file
                _CrtSetReportFile(_CRT_ASSERT, log_file);
                _CrtSetReportFile(_CRT_WARN, log_file);
                _CrtSetReportFile(_CRT_ERROR, log_file);

                tmp_flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
                tmp_flag |= _CRTDBG_ALLOC_MEM_DF;
                tmp_flag |= _CRTDBG_DELAY_FREE_MEM_DF;
                tmp_flag |= _CRTDBG_LEAK_CHECK_DF;

                _CrtSetDbgFlag(tmp_flag);
        #endif 
#endif

inside seperate include file:

#ifdef _DEBUG
        #ifdef WIN32
                #ifndef DBG_NEW
                        #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
                        #define new DBG_NEW
                #endif
        #endif
#endif

This include file has to be included in the header file of each class, because the define redefinition of new is file based.

todo: output.

todo: false positives

Set your breakpoints,

Linux

To debug osgVisual on Linux, be sure you have compiled and installed OSG with debug symbols (It is not interfering with the release build). Build osgVisual as Debug (osgVisuald) and start it with valgrind:

valgrind --error-limit=no --leak-check=full ./osgVisuald <yourParameters> /path/to/database.ive

In the output, Valgrind will output errors in osgVisual as well as in involved system libraries like libGL or X11.

For memory leaks, the "Heap Summary" is very interesting. Here Valgrin lists all possible and sure memory loss. For each incident it lists a call stack to allow to follow the program path over 10 steps to the function the memory loss happens. For all libraries with debug build, it also shows the line number, so it is quite easy to identify the relevant code. The following example shows a memory leak at the heap:

==4281== HEAP SUMMARY:                                                                                                                                                                                                                                                                                                      
==4281==    in use at exit: 32,183 bytes in 413 blocks                                                                                                                                                                                                                                                                     
==4281==   total heap usage: 3,086,029 allocs, 3,085,616 frees, 1,582,088,128 bytes allocated                                                                                                                                                                                                                               
==4281==

...

==4281== 128 (24 direct, 104 indirect) bytes in 1 blocks are definitely lost in loss record 52 of 77
==4281==    at 0x4C2596C: operator new(unsigned long) (vg_replace_malloc.c:220)
==4281==    by 0x6F85405: __gnu_cxx::new_allocator<std::_List_node<osg::ref_ptr<osg::Texture::TextureObject> > >::allocate(unsigned long, void const*) (new_allocator.h:89)
==4281==    by 0x6F8411D: std::_List_base<osg::ref_ptr<osg::Texture::TextureObject>, std::allocator<osg::ref_ptr<osg::Texture::TextureObject> > >::_M_get_node() (stl_list.h:316)
==4281==    by 0x6F834BE: std::list<osg::ref_ptr<osg::Texture::TextureObject>, std::allocator<osg::ref_ptr<osg::Texture::TextureObject> > >::_M_create_node(osg::ref_ptr<osg::Texture::TextureObject> const&) (stl_list.h:461)
==4281==    by 0x6F828AE: std::list<osg::ref_ptr<osg::Texture::TextureObject>, std::allocator<osg::ref_ptr<osg::Texture::TextureObject> > >::_M_insert(std::_List_iterator<osg::ref_ptr<osg::Texture::TextureObject> >, osg::ref_ptr<osg::Texture::TextureObject> const&) (stl_list.h:1407)
==4281==    by 0x6F81831: std::list<osg::ref_ptr<osg::Texture::TextureObject>, std::allocator<osg::ref_ptr<osg::Texture::TextureObject> > >::push_back(osg::ref_ptr<osg::Texture::TextureObject> const&) (stl_list.h:920)
==4281==    by 0x6F7A638: osg::Texture::TextureObjectSet::orphan(osg::Texture::TextureObject*) (Texture.cpp:641)
==4281==    by 0x6F7B14F: osg::Texture::TextureObjectManager::releaseTextureObject(osg::Texture::TextureObject*) (Texture.cpp:839)
==4281==    by 0x6F7B89F: osg::Texture::releaseTextureObject(unsigned int, osg::Texture::TextureObject*) (Texture.cpp:930)
==4281==    by 0x6F7D104: osg::Texture::dirtyTextureObject() (Texture.cpp:1103)
==4281==    by 0x6F7C2CA: osg::Texture::~Texture() (Texture.cpp:991)
==4281==    by 0x6F719C4: osg::Texture2D::~Texture2D() (Texture2D.cpp:50)

...

==4281== LEAK SUMMARY:
==4281==    definitely lost: 432 bytes in 6 blocks
==4281==    indirectly lost: 208 bytes in 2 blocks
==4281==      possibly lost: 0 bytes in 0 blocks
==4281==    still reachable: 31,223 bytes in 404 blocks
==4281==         suppressed: 0 bytes in 0 blocks
==4281== Reachable blocks (those to which a pointer was found) are not shown.
==4281== To see them, rerun with: --leak-check=full --show-reachable=yes
==4281==
==4281== For counts of detected and suppressed errors, rerun with: -v
==4281== ERROR SUMMARY: 692992 errors from 2368 contexts (suppressed: 7 from 7)

Tips & Troubleshooting

  • Because debugging large programs is very slow, debug osgVisual only with small databases. Sometimes, it is even sufficient to debug osgVisual without any terrain database loaded.