|
|
(12 intermediate revisions by 3 users not shown) |
Line 1: |
Line 1: |
| <noinclude>{{documentation/versioncheck}}</noinclude> | | <noinclude>{{documentation/versioncheck}}</noinclude> |
− | Here are some tips to narrow down various issues such as crash, or memory leaks
| |
| | | |
− | =Disable features=
| + | {{documentation/banner |
− | Try running Slicer with as few features as possible:
| + | | text = [https://slicer.readthedocs.io/en/latest/user_guide/get_help.html#slicer-application-does-not-start This page has been moved to read-the-docs.] |
− | * Disable Slicer options via the command line
| + | | background-color = 8FBC8F }} |
− | *: <pre>$ ./Slicer --no-splash --ignore-slicerrc --disable-cli-module --disable-loadable-module --disable-scriptedmodule</pre>
| |
− | ** Look at all the possible options
| |
− | **: <pre>$ ./Slicer --help</pre>
| |
− | * Disable ITK plugins
| |
− | *: CLI modules silently load the ITK plugins in lib/Slicer-{{documentation/currentversion}}/ITKFactories. These plugins are used to share the volumes between Slicer and the ITK filter without having to copy them on disk.
| |
− | ** rename lib/Slicer-{{documentation/currentversion}}/ITKFactories into lib/Slicer-{{documentation/currentversion}}/ITKFactories-disabled
| |
− | * Disable Qt plugins
| |
− | ** rename lib/Slicer-{{documentation/currentversion}}/iconengine into lib/Slicer-{{documentation/currentversion}}/iconengine-disabled
| |
− | | |
− | =Track memory leaks=
| |
− | | |
− | == Prerequisites ==
| |
− | | |
− | <ol>
| |
− | <li> Turn ON the VTK_DEBUG_LEAKS CMake variable and build Slicer</li>
| |
− | <li> Create a test that reproduces the memory leak systematically </li>
| |
− | </ol>
| |
− | | |
− | After execution, the memory leaks are printed automatically by VTK on the standard output:
| |
− | 224: vtkDebugLeaks has detected LEAKS!
| |
− | 224: Class "vtkProperty2D" has 60 instances still around.
| |
− | ...
| |
− | Alternatively, you can simply run Slicer instead of a custom test.
| |
− | | |
− | == Find what specific instance of a class leaks.==
| |
− | | |
− | If the class is instantiated a large amount of time (here vtkProperty2D), it is hard to know what instance is leaking. By making verbose the constructor and destructor of the faulty class, you can infer which instance is leaking. Below are 2 techniques to print whenever the con/destructors are called.
| |
− | | |
− | === Method 1: By recompiling VTK ===
| |
− | | |
− | You can edit the constructor and destructor of the class (here vtkProperty2D::vtkProperty2D() and vtkProperty2D::~vtkProperty2D())
| |
− | vtkProperty2D::vtkProperty2D()
| |
− | {
| |
− | ...
| |
− | static int count = 0;
| |
− | std::cout << "CREATE vtkProperty2D instance #" << count++ << " " << this << std::endl;
| |
− | }
| |
− |
| |
− | vtkProperty2D::~vtkProperty2D()
| |
− | {
| |
− | ...
| |
− | static int count = 0;
| |
− | std::cout << "DELETE vtkProperty2D instance #" << count++ << " " << this << std::endl;
| |
− | }
| |
− | | |
− | Don't forget to rebuild VTK if the class is from VTK (no need to build Slicer inner build)
| |
− | After running the test, you should see outputs similar to
| |
− | ...
| |
− | CREATE vtkProperty2D instance #0 0x0123456
| |
− | ...
| |
− | CREATE vtkProperty2D instance #1 0x01234E5
| |
− | ...
| |
− | DELETE vtkProperty2D instance #0 0x0123456
| |
− | ...
| |
− | DELETE vtkProperty2D instance #1 0x01234E5
| |
− | ...
| |
− | CREATE vtkProperty2D instance #2 0x0123A23
| |
− | ...
| |
− | CREATE vtkProperty2D instance #3 0x0124312
| |
− | ...
| |
− | Copy/paste the listing in a text editor and ensure that for each CREATE of a pointer (e.g. 0x0123456) it exists a DELETE. If there isn't, you found what instance is leaking. Note the instance # (here instance #2 and #3)
| |
− | | |
− | === Method 2: Without recompiling any library but using the debugger ===
| |
− | | |
− | Any debugger with advanced breakpoint controls should work.
| |
− | * With Visual Studio
| |
− | <ol>
| |
− | <li>Set breakpoints in the class constructor and destructor methods.</li>
| |
− | <li>Right click on the breakpoint, select "When Hit..." and "Print a message" with "Function: $FUNCTION {this}". Make sure the "Continue execution" checkbox is selected.
| |
− | <li>Execute the test or Slicer</li>
| |
− | <li>Open the "Output" tab and copy paste the contents into an advanced file editor (not Visual Studio)</li>
| |
− | </ol>
| |
− | * With GDB
| |
− | <ol>
| |
− | <li>Start gdb</li>
| |
− | Using the launcher
| |
− | $ ./Slicer --gdb
| |
− | Or sometimes the following works as well
| |
− | $ gdb ./bin/SlicerApp-real | |
− | <li>Place breakpoints in the functions</li>
| |
− | (gdb) break vtkProperty2D::vtkProperty2D()
| |
− | (gdb) break vtkProperty2D::~vtkProperty2D()
| |
− | gdb will stop in those methods each time the program steps into. It will then print a line such as:
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x123456789) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | 22 vtkProperty2D::vtkProperty2D()
| |
− | <li>Automatically continue execution after each break</li>
| |
− | (gdb) commands 1
| |
− | > continue
| |
− | > end
| |
− | (gdb) commands 2
| |
− | > continue
| |
− | > end
| |
− | <li> Start the execution and copy paste the logs printed by gdb into an advanced file editor (e.g. emacs)</li>
| |
− | (gdb) run
| |
− | </ol>
| |
− | | |
− | After running the test(by recompiling or with debugger), you should see outputs similar to
| |
− | ...
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0123456) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | 22 vtkProperty2D::vtkProperty2D()
| |
− | ...
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x01234E5) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | 22 vtkProperty2D::vtkProperty2D()
| |
− | ...
| |
− | Breakpoint 2, vtkProperty2D::~vtkProperty2() (this=0x0123456) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:37
| |
− | 37 vtkProperty2D::vtkProperty2D()
| |
− | ...
| |
− | Breakpoint 2, vtkProperty2D::~vtkProperty2() (this=0x01234E5) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:37
| |
− | 37 vtkProperty2D::vtkProperty2D()
| |
− | ...
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0123A23) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | 22 vtkProperty2D::vtkProperty2D()
| |
− | ...
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0124312) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | 22 vtkProperty2D::vtkProperty2D()
| |
− | ...
| |
− | Breakpoint 2, vtkProperty2D::~vtkProperty2() (this=0x0123A23) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:37
| |
− | 37 vtkProperty2D::vtkProperty2D()
| |
− | ...
| |
− | | |
− | In an text editor, cleanup the logs by keeping only the "Breakpoint*" lines:
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0123456) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x01234E5) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 2, vtkProperty2D::~vtkProperty2() (this=0x0123456) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:37
| |
− | Breakpoint 2, vtkProperty2D::~vtkProperty2() (this=0x01234E5) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:37
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0123A23) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0124312) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 2, vtkProperty2D::~vtkProperty2() (this=0x0123A23) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:37
| |
− | | |
− | Save a copy of this file, and make the destructor lines similar to the constructor ones (using Replace tools):
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0123456) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x01234E5) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0123456) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x01234E5) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0123A23) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0124312) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0123A23) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | | |
− | Sort the file to make the constructor and destructor lines next to each other(emacs: M-x sort-lines, Notepad++: TextFX/TextFX Tools/Sort lines case sensitive):
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0124312) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0123456) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0123456) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x01234E5) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x01234E5) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0123A23) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0123A23) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | | |
− | Find the line that is not duplicated:
| |
− | * Using Notepad++ (version >=6):
| |
− | ** Open the search dialog
| |
− | ** Find what: "^(.*?)$\s+?^(?=.*^\1$)", Replace with: "dup ", toggle "Regular expression"
| |
− | ** Replace All
| |
− | ** The line without the "dup" prefix is the line we are looking for
| |
− | * Using emacs:
| |
− | ** C-M-%
| |
− | ** Replace regexp: \(.*\)<type C-q C-j>\1
| |
− | ** with: dup<type C-q C-j>
| |
− | | |
− | Extract the instance address from the line: e.g. 0x0124312
| |
− | | |
− | From the original logs, keep only the Constructor lines:
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0123456) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x01234E5) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0123A23) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− | Breakpoint 1, vtkProperty2D::vtkProperty2() (this=0x0124312) at /home/user/Slicer-superbuild/VTK/.../vtkProperty2D.cxx:22
| |
− |
| |
− | Search into the original saved file what line the address is: e.g. 4
| |
− | | |
− | | |
− | == Find the leaking instance at run-time ==
| |
− | | |
− | Run the test with the [[Documentation/4.0/Developers/Tutorials/Debug_Instructions|debugger]], set a breakpoint in the class constructor and ignore the break as many time as the instance number (say it's 2):
| |
− | * GDB
| |
− | (gdb) break vtkProperty2D::vtkProperty2D()
| |
− | Breakpoint 1 at 0x5b22d0e55d04296: file vtkProperty2D.cxx, line 22
| |
− | (gdb) ignore 1 2
| |
− | (gdb) run
| |
− | When the debugger stops, check the call stack:
| |
− | (gdb) backtrace
| |
− | *Visual Studio
| |
− | **Set a breakpoint in vtkProperty2D::vtkProperty2D()
| |
− | **Right click on the breakpoint and select HitCount, select "break when the hit count is equal to" and type '2'
| |
− | **Start the application
| |
− | By analyzing the trace, you should be able to find the faulty instance.
| |
− | | |
− | | |
− | == Analyze the code to see where the leak could be ==
| |
− | Here is a listing of the most common mistakes
| |
− | * this->MyXXX = vtkXXX::New();
| |
− | ** Is there a matching this->MyXXX->Delete() ?
| |
− | ** Are you sure <code>this->MyXXX</code> is <code>0</code> before being assigned a new pointer ? If not, then you need to add
| |
− | if (this->MyXXX != 0)
| |
− | {
| |
− | this->MyXXX->Delete();
| |
− | this->MyXXX = 0;
| |
− | }
| |
− | this->MyXXX = vtkXXX::New();
| |
− | </ol>
| |
− | {{ombox
| |
− | |type=content
| |
− | |text=To reduce memory leaks, use the following tools:
| |
− | * <code>vtkNew<vtkXXX> myXXX;</code>
| |
− | * <code>vtkSmartPointer<vtkXXX> myXXX = vtkSmartPointer<vtkXXX>::New()</code>
| |
− | * <code>vtkSmartPointer<vtkXXX> myXXX; myXXX.TakeReference(this->CreateObjAndCallerOwnsReference())</code>.
| |
− | }} | |
− | | |
− | ==Other resources==
| |
− | * [[Slicer3:VTK_Leak_Debugging|Slicer3 VTK leak debugging]]
| |
− | * [[Strategies_for_Writing_and_Debugging_Code_in_Slicer_3|Strategies for Writing and Debugging Code in Slicer3]]
| |
− | | |
− | =Track a crash while accessing already deleted object pointer=
| |
− | If the application crashes by accessing an invalid pointer. The goal here is to find when (and why) the pointer is deleted before it is accessed .
| |
− | <ol>
| |
− | <li>Before the crash, print the value of the pointer:</li>
| |
− | Add before the crash (i.e. <code>this->MyObject->update()</code>)
| |
− | std::cout << ">>>>>Object pointer: " << this->MyObject << std::endl;
| |
− | <li>Add a counter in the destructor:</li>
| |
− | Add in the object destructor (it can be in the base class (vtkObject or vtkMRMLNode) if you don't know the type):
| |
− | static int count = 0;
| |
− | std::cout << "#######Object Destructed: " << this << " " << count++ << std::endl;
| |
− | <li>Run the application and make it crash.</li>
| |
− | <li>In the logs you shall see something like that:</li>
| |
− | #######Object Destructed: 0x12345678 0
| |
− | #######Object Destructed: 0x12345679 1
| |
− | #######Object Destructed: 0x12345680 2
| |
− | >>>>>Object Pointer: 0x12345660
| |
− | >>>>>Object Pointer: 0x12345661
| |
− | >>>>>Object Pointer: 0x12345662
| |
− | #######Object Destructed: 0x12345660 3
| |
− | #######Object Destructed: 0x12345661 4
| |
− | #######Object Destructed: 0x12345662 5
| |
− | #######Object Destructed: 0x12345663 6
| |
− | >>>>>Object Pointer: 0x12345670
| |
− | >>>>>Object Pointer: 0x12345671
| |
− | >>>>>Object Pointer: 0x12345672
| |
− | #######Object Destructed: 0x12345660 7
| |
− | #######Object Destructed: 0x12345661 8
| |
− | #######Object Destructed: 0x12345662 9
| |
− | >>>>>Object Pointer: '''0x12345663'''
| |
− | Segfault
| |
− | <li> Search in the logs when the pointer before crash has been deleted. Set a conditional breakpoint in the debugger:</li>
| |
− | (gdb) break MyObj.cxx:103 if count == 6
| |
− | or
| |
− | (gdb) break MyObj.cxx:103
| |
− | (gdb) ignore 1 5
| |
− | or if you don't want to use a debugger, you can make it crash the 6th time:
| |
− | assert(count != 6);
| |
− | <li> Analyze the backtrace to understand why the pointer has been deleted without letting know the object that tries to access it.</li>
| |
− | </ol>
| |
− | | |
− | =Console output on Windows=
| |
− | On Windows, the application is built with no console output. A workaround for this issue is described in the following bug reports:
| |
− | * http://www.na-mic.org/Bug/view.php?id=2376
| |
− | * http://www.na-mic.org/Bug/view.php?id=2917
| |
− | To add console output, you need to compile Slicer application with Slicer_BUILD_WIN32_CONSOLE set to ON at the configure time (uninitialized/OFF by default).
| |