🔒 Locked
Call stack log on crash?
Advertisement
Original Post
Hey, I'm wondering how would it be possible to have a system in my application that would log ( in a .txt file for example ) the call stack, when the application crash? So I can see where does my application crashes on other people computers. My application is written in C++. Thanks a lot
CodeProject: Several classes for exception handling
I'm implementing the same thing in my app as well. :)
I'm implementing the same thing in my app as well. :)
I do something like:
then:
to use it:
I have a preprocessor flag to simply define PushCall/PopCall as empty, as I'm not sure what kind of performance hit they incur. I haven't noticed much of a speed difference it causes, so it can't be that bad, but it does work, and I like the flexibility to leave out helper functions that always appear in the same place in the call stack.
I got the idea looking at some source code quite a long time ago, I think it may have been Unreal's mod headers. if it was unreal, they leave this enabled in their games, so its probably not too bad of a speed hit. Of course, the call stack their errors spit out might be from the scripting system, and not this hackery.
class CException{ ... virtual CString GetMessage();};class CStackTraceException : public CException{ CString m_StackTrace;public: ... void Push(const TCHAR * Function); // add function to stack here, I do m_StackTrace += Function + '\n'; CString GetMessage() { /* Build and return message with call stack here */ }};then:
#define PushCall(FuncName) const TCHAR FUNCTION_NAME[] = FuncName; try { #define PopCall() } catch(CStackTraceException & Ex) { Ex.Push(FUNCTION_NAME); throw Ex; }to use it:
void FuncA(){ PushCall("FuncA"); throw CStackTraceException("I farted"); PopCall();}void FuncB(){ PushCall("FuncB"); FuncA(); PopCall();}void FuncC(){ PushCall("FuncC"); FuncB(); PopCall();}int main(int ArgC, char ** argv){ try { PushCall("main"); FuncC(); PopCall(); } catch(CException & Ex) { MessageBox(Ex.GetMessage()); }}/*which prints out:I Farted:FuncAFuncBFuncCmain*/I have a preprocessor flag to simply define PushCall/PopCall as empty, as I'm not sure what kind of performance hit they incur. I haven't noticed much of a speed difference it causes, so it can't be that bad, but it does work, and I like the flexibility to leave out helper functions that always appear in the same place in the call stack.
I got the idea looking at some source code quite a long time ago, I think it may have been Unreal's mod headers. if it was unreal, they leave this enabled in their games, so its probably not too bad of a speed hit. Of course, the call stack their errors spit out might be from the scripting system, and not this hackery.
On the offhand chance the OP isn't refering to windows,
Linux, BSD, and I assume MacOS:
tell your users to enable core dumps, and send the core file to you. It will contain a call stack and the entire environment at the time of the crash.
I believe that on a bash console, it can be done with
ulimit -c unlimited
Or you could enable core dumps yourself within your application using the rlimit syscall (iirc)
Linux, BSD, and I assume MacOS:
tell your users to enable core dumps, and send the core file to you. It will contain a call stack and the entire environment at the time of the crash.
I believe that on a bash console, it can be done with
ulimit -c unlimited
Or you could enable core dumps yourself within your application using the rlimit syscall (iirc)
Quote:
Original post by antareus
Kibble, that assumes that control always leaves the function through the 'bottom' of it. This is almost always the case, but it seems like its easy to forget. Additionally, you can't really use something like a scope guard to do it for you.
true, but when would you want the call stack when you haven't terminated the program and are making an error message? and, just to be stupid:
extern CString g_CallStack;#define PushCall(FuncName) { g_CallStack += FuncName + "\n"; static int FUNCTION_NAME_LENGTH = strlen(FuncName); #define PopCall() g_CallStack[g_CallStack.GetLength() - FUNCTION_NAME_LENGTH] = '\0'; }definitely not fast though...
Quote:
Original post by antareus
Kibble, that assumes that control always leaves the function through the 'bottom' of it. This is almost always the case, but it seems like its easy to forget. Additionally, you can't really use something like a scope guard to do it for you.
You could define a macro to push the stack and return, I suppose.
I made a small utility function a while ago that uses the StackWalk function to walk down the stack by a specified number of levels to get the symbol information about that level of the stack. Might be of use to someone:
http://www.sc3d.com/thought/snippets/debugging/GetFunctionCaller/
The main difference compared to similar stuff I found on the internet is that it doesn't need to be called from within an exception handler (getting EIP is the tricky part).
http://www.sc3d.com/thought/snippets/debugging/GetFunctionCaller/
The main difference compared to similar stuff I found on the internet is that it doesn't need to be called from within an exception handler (getting EIP is the tricky part).
Quote:
Original post by C-Junkie
or um, you could make jsut one macro...
stackg_calls;
class asdf { asdf(string s) {g_calls.push(s);} ~asdf(){g_calls.pop();} };
#define FUNCTION(x) asdf asldkfjl(#x)
void function() {
FUNCTION(function);
//blah blah blah
} // destructor pops the stack
Nope. If an exception is thrown the call stack will be destroyed when the exception leaves the scope because that will fire the local's destructor.
BTW most compilers can inject the name of the current function in there, too. :)
S1CA that code snippet is AWESOME, do you mind if I rip it out, modify it to dynamically load dbghelp.dll, and put it in a public domain library? Full credit will be left in the files.
Quote:Oh, that what you guys were trying to do. Should have read those earlier posts.
Original post by antareus
Nope. If an exception is thrown the call stack will be destroyed when the exception leaves the scope because that will fire the local's destructor.
That's why I hate exceptions. abort() works much better.
Quote:
Original post by antareus Quote:
Original post by C-Junkie
or um, you could make jsut one macro...
stackg_calls;
class asdf { asdf(string s) {g_calls.push(s);} ~asdf(){g_calls.pop();} };
#define FUNCTION(x) asdf asldkfjl(#x)
void function() {
FUNCTION(function);
//blah blah blah
} // destructor pops the stack
Nope. If an exception is thrown the call stack will be destroyed when the exception leaves the scope because that will fire the local's destructor.
If you wanted to use that method, you would have to have the exception's constructor grab the current call stack, and store it for when it pops out the bottom of the stack.
Quote:
Original post by antareus Quote:
Original post by C-Junkie
or um, you could make jsut one macro...
stackg_calls;
class asdf { asdf(string s) {g_calls.push(s);} ~asdf(){g_calls.pop();} };
#define FUNCTION(x) asdf asldkfjl(#x)
void function() {
FUNCTION(function);
//blah blah blah
} // destructor pops the stack
Nope. If an exception is thrown the call stack will be destroyed when the exception leaves the scope because that will fire the local's destructor.
BTW most compilers can inject the name of the current function in there, too. :)
S1CA that code snippet is AWESOME, do you mind if I rip it out, modify it to dynamically load dbghelp.dll, and put it in a public domain library? Full credit will be left in the files.
No worries, do anything you want with it, including commercial use - its not significant enough to impose any restrictions on![smile]
The legal statement at the top is really mainly just to disclaim any liability (e.g. if NASA use it in their next bit of kit and it doesn't work as expected, don't sue me! [smile])
There's a few things you might want to do with it though:
- proper error handling (it was more of an experiment to help with a specific problem than a robust library).
- some pre-processor #ifdefs so it will compile with older versions of the Platform SDK - it uses the 64bit compatible stack tracing functions. The PSDK which comes with MSVC 6 for example probably won't contain those.
- make it 64-bit CPU (and non-x86 Windows) friendly - the EIP/ESP stuff isn't likely to play nicely on a 64-bit CPU running in native 64-bit mode. One idea might be to set a temporary exception handler and deliberately throw an exception so that Windows works out the EIP/ESP/EBP for you in a CPU specific way for you.
Unix (and linux) produce core files when you crash that contain the entire call stack as well as the value of all your variables on the stack and so forth. I'd think that there must be a way to create core files in windows too. It seems like a waste to create complicated functions to print out your stack when the system will do it automatically.
Quote:
S1CA that code snippet is AWESOME, do you mind if I rip it out, modify it to dynamically load dbghelp.dll, and put it in a public domain library? Full credit will be left in the files.
It worked for you? It wouldn't for me. The SizeOfStruct and MaxNameLen members of the SYMBOL_INFO needs to be set appropriately, and the FileName member of IMAGEHLP_LINE64 needs to point to a buffer.
Setting AddrStack is unnecessary. Retrieving EBP and EIP can be done through calling GetThreadContext(GetCurrentThread(), ...). Though this is documented as not returning meaningful data, those particular members of the CONTEXT object seem to be properly populated. Failing that, EIP can be retrieved by calling the compiler intrinsic _ReturnAddress() (which is equivalent to the asm, but built-in). EBP's value can be obtained from _AddressOfReturnAddress() - 4.
DrPizza:
Like I said earlier, "it was more of an experiment to help with a specific problem than a robust library" [smile].
That code was actually written late at night to help track down who was making anonymous memory allocations in a large project I was debugging rather than for any production use or even tutorial use.
If someone wants to fix it and make it production quality, or make their own code to do the same publicly available they're more than welcome [smile].
Yep, you're right about the SizeOfStruct and MaxNameLen members of SYMBOL_INFO. However you're wrong about the FileName pointer in IMAGEHLP_LINE64 - SymGetLineFromAddr64() always sets that pointer, regardless of its value (and will overwrite any pointer you specify).
Personally I'd be extremely wary about relying on GetThreadContext() returning valid data for the currently running thread - its documented as not returning valid information in that case, I'd trust that to be true (though my plain asm is probably risky too for other reasons). There's too many things to bite you when relying on undocumented "features" (hyper-threading, SMP etc).
I wasn't aware of the _ReturnAddress and _AddressOfReturnAddress intrinsics - thanks for enlightening me!. I'll bear them in mind if I'm ever doing anything like this again.
BTW: the code runs fine on the WindowsXP and Windows2000 machines I used it on (built with .NET 2003 and an up to date PSDK).
Like I said earlier, "it was more of an experiment to help with a specific problem than a robust library" [smile].
That code was actually written late at night to help track down who was making anonymous memory allocations in a large project I was debugging rather than for any production use or even tutorial use.
If someone wants to fix it and make it production quality, or make their own code to do the same publicly available they're more than welcome [smile].
Yep, you're right about the SizeOfStruct and MaxNameLen members of SYMBOL_INFO. However you're wrong about the FileName pointer in IMAGEHLP_LINE64 - SymGetLineFromAddr64() always sets that pointer, regardless of its value (and will overwrite any pointer you specify).
Personally I'd be extremely wary about relying on GetThreadContext() returning valid data for the currently running thread - its documented as not returning valid information in that case, I'd trust that to be true (though my plain asm is probably risky too for other reasons). There's too many things to bite you when relying on undocumented "features" (hyper-threading, SMP etc).
I wasn't aware of the _ReturnAddress and _AddressOfReturnAddress intrinsics - thanks for enlightening me!. I'll bear them in mind if I'm ever doing anything like this again.
BTW: the code runs fine on the WindowsXP and Windows2000 machines I used it on (built with .NET 2003 and an up to date PSDK).
On Windows, generating minidumps scales better and is more reliable. You don't need symbols on the machine that crashed; you can even spawn a separate process to inspect the crashed application's memory and generate the minidump.
Couple that with the freely available debugging tools from Microsoft, most notably the symbols server, you'll be able to easily debug crashes from remote machines with a minimal hit on runtime dependencies.
MSN
Couple that with the freely available debugging tools from Microsoft, most notably the symbols server, you'll be able to easily debug crashes from remote machines with a minimal hit on runtime dependencies.
MSN
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement