I\'ve got a c++ app that wraps large parts of code in try blocks. When I catch exceptions I can return the user to a stable state, which is nice. But I\'m not longer recei
One trick that is compiler independent is to wrap the throw statement in a function. The function can perform other duties before throwing the exception, such as recording to a log file. It also makes a handy place to put a breakpoint. If you create a macro to call the function you can automatically include the __FILE__
and __LINE__
where the throw occurred.
This is possible with using SEH (structured exception handling). The point is that MSVC implements C++ exceptions via SEH. On the other hand the pure SEH is much more powerful and flexible.
That's what you should do. Instead of using pure C++ try/catch blocks like this:
try
{
DoSomething();
} catch(MyExc& exc)
{
// process the exception
}
You should wrap the inner code block DoSomething
with the SEH block:
void DoSomething()
{
__try {
DoSomethingInner();
}
__except (DumpExc(GetExceptionInformation()), EXCEPTION_CONTINUE_SEARCH) {
// never get there
}
}
void DumpEx(EXCEPTION_POINTERS* pExc)
{
// Call MiniDumpWriteDump to produce the needed dump file
}
That is, inside the C++ try/catch block we place another raw SEH block, which only dumps all the exceptions without catching them.
See here for an example of using MiniDumpWriteDump.
You can write dumps using MiniDumpWriteDump function.
If you're using C++ exceptions, then you can simply include file/line/function information (so you'll see it as text if you call std::exception.what()) in any place where you throw that exception (using ____FUNCTION____, ____FILE____ and ____LINE____ macros).
If you're trying to catch OS exceptions, then crashing the application will be probably a better choice.
What you need is to analyze the stack to figure out where the exception came from. For msvc there is a lib called dbghelp.dll that can help you log out the exceptions. In general what I do is to log out a minidump file and use this to replay the issue beside using the right program database (pdb file). This works on a customer systems that do not come with source code or to whom you won't want to give pdbs.
It's possible to design your exceptions to include source file names & line numbers. In order to do so, you need to create a class derived from std::exception
to contain the information. In the example below, I have a library of exceptions for my application including my_exception
. I also have a traced_error
which is a template exception class derived from my application-level exceptions. The traced_error
exception holds information about the filename & line number, and calls the application-level exception class' what()
method to get detailed error information.
#include <cstdlib>
#include <string>
#include <stdexcept>
#include <iostream>
using namespace std;
template<class EX>
class traced_error : virtual public std::exception, virtual public EX
{
public:
traced_error(const std::string& file, int line, const EX& ex)
: EX(ex),
line_(line),
file_(file)
{
}
const char* what() const
{
std::stringstream ss;
static std::string msg;
ss << "File: " << file_ << " Line: " << line_ << " Error: " << EX::what();
msg = ss.str().c_str();
return msg.c_str();
}
int line_;
std::string file_;
};
template<class EX> traced_error<EX> make_traced_error(const std::string& file, int line, const EX& ex)
{
return traced_error<EX>(file, line, ex);
}
class my_exception : virtual public std::exception
{
public:
my_exception() {};
const char* what() const
{
return "my_exception's what";
}
};
#define throwx(EX) (throw make_traced_error(__FILE__,__LINE__,EX))
int main()
{
try
{
throwx(my_exception());
}
catch( const std::exception& ex )
{
cout << ex.what();
}
return 0;
}
The output of this program is:
File: .\main.cpp Line: 57 Error: my_exception's what
You could also redesign this so that the application-level exceptions derive from traced_error
instead of the other way round, in case you would rather catch specific application-level exceptions. In your catch
, you can log the error to a log file & create a dump file using MiniDumpWriteDump().