8

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 receiving crash dumps. I'd really like to figure out where in the code the exception is taking place, so I can log it and fix it.

Being able to get a dump without halting the application would be ideal, but I'm not sure that's possible.

Is there some way I can figure out where the exception was thrown from within the catch block? If it's useful, I'm using native msvc++ on windows xp and higher. My plan is to simply log the crashes to a file on the various users' machines, and then upload the crashlogs once they get to a certain size.

Kate Gregory
  • 18,808
  • 8
  • 56
  • 85
tfinniga
  • 6,693
  • 3
  • 32
  • 37
  • 2
    possible duplicate of [C++ display stack trace on exception](http://stackoverflow.com/questions/691719/c-display-stack-trace-on-exception) – sth Jun 11 '10 at 22:32

5 Answers5

6

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.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
valdo
  • 12,632
  • 2
  • 37
  • 67
  • 1
    Using SEH directly is OK if you don't care about portability. Standard C++ exceptions are more portable (well, except for several compilers for embedded systems that don't support exceptions). – George Jun 13 '10 at 16:02
  • 2
    This is an effective way to handle C++ exceptions. If you want to get useful information on OS exceptions, such as stack overflow or access violation, you should arrange for `MiniDumpWriteDump` to be called from another process. – Ben Voigt Jun 13 '10 at 16:04
  • @George: There is currently no portable way to get stack traces in C++. I seem to recall that this is a situation that the new C++0x standard tries to improve. – Ben Voigt Jun 13 '10 at 16:06
  • Not necessarily. Well, if you talk about stack overflow, and you handle this exception before the stack unwind - you should call 'MiniDumpWriteDump' from another thread (since our thread has almost no stack). But there's no need to do things from another process. Unless you consider the exception as a bug, and don't rely on that process – valdo Jun 13 '10 at 16:08
  • @Ben Voigt: That's mostly true (there are some methods for doing this that work OK for win32 w/ mingw, cygwin & linux), but using SEH guarantees the code won't build on another platform unless you decide to get fancy with the preprocessor. – George Jun 13 '10 at 16:44
  • @valdo: `MiniDumpWriteDump` needs to load other DLLs, so if you encountered an exception while holding the loader lock, you'd better call it from out of process. Also, if you have memory corruption (a common cause of bugs you'd want to get a dump of), you again should call `MiniDumpWriteDump` out-of-process. – Ben Voigt Jun 13 '10 at 16:57
  • If `DoSomethingInner` calls WinApi `SendMessage()` and C++ exception is occurred in its handler (it's my code too), `DumpExc()` isn't fired and debugger shows warning about unhandled exception. Why? – 23W Oct 22 '17 at 09:27
  • 1
    @23W: are you talking about unhandled exception (which is a crash rather than warning), or 1st-chance exception debugger message? If latter is the case, then it's probably because `SendMessage` has its own SEH handler, which handles that exception. – valdo Oct 22 '17 at 11:27
  • @valdo, about 1st-chance exception debugger message – 23W Oct 22 '17 at 12:24
  • SEH imposes some serious restrictions on what the code inside the block can do. I haven't used SEH for a while, but last time I tried it didn't allow objects in the local frame that had destructors. – Wheezil Jul 05 '21 at 20:17
4

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().

John Dibling
  • 99,718
  • 31
  • 186
  • 324
2

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.

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

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.

jdehaan
  • 19,700
  • 6
  • 57
  • 97
1

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.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622