Sunday, October 19, 2008

Debug in Release

hi, this time i talk about something i "bump into" while trying to run my engine in release mode (after i changed all engine class design and such) and get a crash into my face, oops not good.
at first i thought it something i mess with, i had no clue where to begin because the change was so big so i start shooting all over with no luck.
so i created exception handler lib which will catch exceptions and just before it will crash it will generate two files:
1) log file about the stack, modules, user, and the must important is the eip, which is the instruction pointer where the crash was happened.
2) mini dump file which is generated using dbghelp.dll, this is basically a snapshot of the program before it crashed, and it can be opened via vs or windbg and it will show you the exact line that cause the crash (very useful)
after having this kind of tool i started debugging and check what the hack cause the crash, i saw that there was uninitialized pointers such as d3ddevice interface, which wasn't make any sense because in the log i saw that textures and other data was loaded.
this turn on the red alert, what is wrong with my code, and why it is working in debug and not in release (and it was working!!)
i watch the engine log and see that the constructor of some managers was called twice which wasn't possible and make sens because they are singletons!! after digging a bit i notice that if you create static inline singletons (scott mayers singletongs) the compiler (which is vs2003 not 2005/2008) generating wrong code when optimization is on, and make the constructor called twice or more if you include the header in more then one object file and you call the static inline function that return the singleton instance/object.
if you move the implementation to the .cpp code the compiler generate "good" code.
because those managers are heavily used i didn't want the overhead of the function calls, so i dig a little bit more and i found a trick that works.
NOTE: this only happened on vc6 and 2003, in 2005 they fix the problem.
so if you are used to create singletons like this:
class CTest
{
public:
static CTest &GetInstance()
{
static CTest instance;
return instance;
}
}
you should now use this trick:
class CTest
{
public:
struct tDummy
{
static CTest &getInst()
{
static CTest instance;
return instance;
}
}
static CTest &GetInstance()
{
static tDummy instance;
return instance.getInst();
}
}
vc2003 deals fine with static var which contained in a non-static inline method.
so what i found is that i didn't mess anything and the code was fine and the compiler wasn't
after all it was good practice to wrote the tools and i think they will be useful in the future - i hope i wont need to use them :)

3 comments:

Ofek Shilon said...

Oren - this is not a compiler bug. static variable included in an inline function implemented in the header causes exactly what you see.
the 'inline' modifier is practically in effect only while optimizing. when indeed inlining, each compiled cpp inlines the function, thereby creating its *own* copy of the static variable the function contains. (and in particular, the ctor is called many times). thus, its not a singleton anymore. anyway, meyers singleton does not call for an *inline* accessor.


There's actually some referring to that in the standard: http://www.cpptalk.net/static-local-variables-in-inline-functions-vt22965.html

you can google "static variable inside inline function" for some more discussions around this.

cheers man!

Ofek Shilon said...

also, i just learnt from that link that the issue could have been resolved with addition of an 'extern' modifier to your inline static function.

orenk2k said...

hi
this turns to be compiler issue/bug because by checking the same code on few compilers the code works, while in vs2003 compiler it didin't!
also note that any function (definition) placed within the class definition consider to be inline function, even if you did not wrote "inline".
btw: putting "extern" keyword will say that i need to put the function in the cpp or such, which cost me the function overhead.
i also need it to be compact and simple so i could define a simple MACRO and place it inside class def to make it singleton without doing a lot of extra work.
the link you wrote talks about something else, its static inline functions defined within header files and not within class def. those functions will get their own static instance everywhere you include the header file.
anyway, this problem was solved and work.
10x and bye