6

I'm developing a game and when I do a specific action in the game, it crashes. So I went debugging and I saw my application crashed at simple C++ statements like if, return, ... Each time when I re-run, it crashes randomly at one of 3 lines and it never succeeds.

line 1:

if (dynamic) { ... } // dynamic is a bool member of my class

line 2:

return m_Fixture; // a line of the Box2D physical engine. m_Fixture is a pointer.

line 3:

return m_Density; // The body of a simple getter for an integer.

I get no errors from the app nor the OS...

Are there hints, tips or tricks to debug more efficient and get known what is going on?

That's why I love Java...

Thanks

Martijn Courteaux
  • 67,591
  • 47
  • 198
  • 287
  • 1
    If it crashed on an if-statement, perhaps one of the variables participating in the condition of the `if` is a null pointer. – FrustratedWithFormsDesigner Jul 07 '10 at 18:24
  • 2
    When the game crashes, do you get any error messages (from your app or from the OS)? Can you reliably reproduce these crashes? – bta Jul 07 '10 at 18:24
  • If you are using a third party lib, ensure you are compiling against the correct header files. If not, the program can crash, and if it is not enough, some mismatches in the debugging files could occur, and you end up seeing incorrect 'error lines'. – Diego Pereyra Jul 07 '10 at 18:36
  • Post some code where it's crashing. What platform are you debugging in? If it has a disassembly mode, find out exactly what instruction it's crashing on (and why). – Jim Buck Jul 07 '10 at 18:59
  • looks like your the `this` pointer is invalid.. – smerlin Jul 07 '10 at 20:53
  • http://stackoverflow.com/questions/91527/debugging-techniques – Vinko Vrsalovic Jul 07 '10 at 21:01
  • Has the object in question previously been deleted? I.e. are you calling methods on a ptr to a deleted object? – jon hanson Jul 07 '10 at 21:33
  • Yeah, looks to me like you're calling methods on a busted object. – Puppy Jul 07 '10 at 22:36

15 Answers15

11

Random crashes like this are usually caused by stack corruption, since these are branching instructions and thus are sensitive to the condition of the stack. These are somewhat hard to track down, but you should run valgrind and examine the call stack on each crash to try and identify common functions that might be the root cause of the error.

Adam Shiemke
  • 3,734
  • 2
  • 22
  • 23
5

These are mostly due to stack corruption, but heap corruption can also affect programs in this way.

stack corruption occurs most of the time because of "off by one errors".
heap corruption occurs because of new/delete not being handled carefully, like double delete.

Basically what happens is that the overflow/corruption overwrites an important instruction, then much much later on, when you try to execute the instruction, it will crash.

Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
Eric
  • 19,525
  • 19
  • 84
  • 147
5

Are there hints, tips or tricks to debug more efficient and get known what is going on?

  1. Run game in debugger, on the point of crash, check values of all arguments. Either using visual studio watch window or using gdb. Using "call stack" check parent routines, try to think what could go wrong.
  2. In suspicious(potentially related to crash) routines, consider dumping all arguments to stderr (if you're using libsdl or on *nixlike systems), or write a logfile, or send dupilcates of all error messages using (on Windows) OutputDebugString. This will make them visible in "output" window in visual studio or debugger. You can also write "traces" (log("function %s was called", __FUNCTION__))
  3. If you can't debug immediately, produce core dumps on crash. On windows it can be done using MiniDumpWriteDump, on linux it is set somewhere in configuration variables. core dumps can be handled by debugger. I'm not sure if VS express can deal with them on Windows, but you still can debug them using WinDBG.
  4. if crash happens within class, check *this argument. It could be invalid or zero.
  5. If the bug is truly evil (elusive stack corruption in multithreaded app that leads to delayed crash), write custom memory manager, that will override new/delete, provide alternative to malloc(if your app for some reason uses it, which may be possible), AND that locks all unused memory memory using VirtualProtect (windows) or OS-specific alternative. In this case all potentially dangerous operation will crash app instantly, which will allow you to debug the problem (if you have Just-In-Time debugger) and instantly find dangerous routine. I prefer such "custom memory manager" to boundschecker and such - since in my experience it was more useful. As an alternative you could try to use valgrind, which is available on linux only. Note, that if your app very frequently allocates memory, you'll need a large amount of RAM in order to be able to lock every unused memory block (because in order to be locked, block should be PAGE_SIZE bytes big).
  6. In areas where you need sanity check either use ASSERT, or (IMO better solution) write a routine that will crash the application (by throwing an std::exception with a meaningful message) if some condition isn't met.
  7. If you've identified a problematic routine, walk through it using debugger's step into/step over. Watch the arguments.
  8. If you've identified a problematic routine, but can't directly debug it for whatever reason, after every statement within that routine, dump all variables into stderr or logfile (fprintf or iostreams - your choice). Then analyze outputs and think how it could have happened. Make sure to flush logfile after every write, or you might miss the data right before the crash.

In general you should be happy that app crashes somewhere. Crash means a bug you can quickly find using debugger and exterminate. Bugs that don't crash the program are much more difficult (example of truly complex bug: given 100000 values of input, after few hundreds of manipulations with values, among thousands of outputs, app produces 1 absolutely incorrect result, which shouldn't have happened at all)

That's why I love Java...

Excuse me, if you can't deal with language, it is entirely your fault. If you can't handle the tool, either pick another one or improve your skill. It is possible to make game in java, by the way.

SigTerm
  • 26,089
  • 6
  • 66
  • 115
4

I generally like to take a second to step back and think through the code, trying to catch any logic errors.

You might try commenting out different parts of the code and seeing if it affects how the program is compiled.

Besides those two things you could try using a debugger like Visual Studio or Eclipse etc...

Lastly you could try to post your code and the error you are getting on a website with a community that knows programming and could help you work through the error (read: stackoverflow)

Jordan
  • 4,928
  • 4
  • 26
  • 39
  • 2
    +1, sometimes taking a step back and thinking through what your code is doing is the best way to find an error. – Silvae Jul 07 '10 at 18:46
  • 1
    Posting the code won't work, as some code far off the actual crashsite may modify some data it shouldn't modify and not make it into the question. – tstenner Jul 07 '10 at 19:32
3

Crashes / Seg faults usually happen when you access a memory location that it is not allowed to access, or you attempt to access a memory location in a way that is not allowed (for example, attempting to write to a read-only location).

There are many memory analyzer tools, for example I use Valgrind which is really great in telling what the issue is (not only the line number, but also what's causing the crash).

Luca Matteis
  • 29,161
  • 19
  • 114
  • 169
2

There are no simple C++ statements. An if is only as simple as the condition you evaluate. A return is only as simple as the expression you return.

You should use a debugger and/or post some of the crashing code. Can't be of much use with "my app crashed" as information.

Puppy
  • 144,682
  • 38
  • 256
  • 465
1

I had problems like this before. I was trying to refresh the GUI from different threads.

kist
  • 590
  • 2
  • 8
1

If the if statements involve dereferencing pointers, you're almost certainly corrupting the stack (this explains why an innocent return 0 would crash...)

This can happen, for instance, by going out of bounds in an array (you should be using std::vector!), trying to strcpy a char[]-based string missing the ending '\0' (you should be using std::string!), passing a bad size to memcpy (you should be using copy-constructors!), etc.

Try to figure out a way to reproduce it reliably, then place a watch on the corrupted pointer. Run through the code line-by-line until you find the very line that corrupts the pointer.

BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
1

Look at the disassembly. Almost any C/C++ debugger will be happy to show you the machine code and the registers where the program crashed. The registers include the Instruction Pointer (EIP or RIP on x86/x64) which is where the program was when it stopped. The other registers usually have memory addresses or data. If the memory address is 0 or a bad pointer, there is your problem.

Then you just have to work backward to find out how it got that way. Hardware breakpoints on memory changes are very helpful here.

On a Linux/BSD/Mac, using GDB's scripting features can help a lot here. You can script things so that after the breakpoint is hit 20 times it enables a hardware watch on the address of array element 17. Etc.

You can also write debugging into your program. Use the assert() function. Everywhere!

Use assert to check the arguments to every function. Use assert to check the state of every object before you exit the function. In a game, assert that the player is on the map, that the player has health between 0 and 100, assert everything that you can think of. For complicated objects write verify() or validate() functions into the object itself that checks everything about it and then call those from an assert().

Another way to write in debugging is to have the program use signal() in Linux or asm int 3 in Windows to break into the debugger from the program. Then you can write temporary code into the program to check if it is on iteration 1117321 of the main loop. That can be useful if the bug always happens at 1117322. The program will execute much faster this way than to use a debugger breakpoint.

Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
1

some tips :
- run your application under a debugger, with the symbol files (PDB) together.
- How to set Visual Studio as the default post-mortem debugger?
- set default debugger for WinDbg Just-in-time Debugging
- check memory allocations Overriding new and delete, and Overriding malloc and free

Community
  • 1
  • 1
lsalamon
  • 7,998
  • 6
  • 50
  • 63
1

One other trick: turn off code optimization and see if the crash points make more sense. Optimization is allowed to float little bits of your code to surprising places; mapping that back to source code lines can be less than perfect.

jackr
  • 1,407
  • 1
  • 14
  • 29
0

Check pointers. At a guess, you're dereferencing a null pointer.

Paul Nathan
  • 39,638
  • 28
  • 112
  • 212
0

I've found 'random' crashes when there are some reference to a deleted object. As the memory is not necessarily overwritten, in many cases you don't notice it and the program works correctly, and than crashes after the memory was updated and is not valid anymore.

JUST FOR DEBUGGING PURPOSES, try commenting out some suspicious 'deletes'. Then, if it doesn't crash anymore, there you are.

Diego Pereyra
  • 417
  • 3
  • 10
0

use the GNU Debugger

adhanlon
  • 6,407
  • 13
  • 43
  • 41
-1

Refactoring.

Scan all the code, make it clearer if not clear at first read, try to understand what you wrote and immediately fix what seems incorrect.

You'll certainly discover the problem(s) this way and fix a lot of other problems too.

Klaim
  • 67,274
  • 36
  • 133
  • 188