PREAMBLE: this technique is no longer necessary. As of Visual Studio 2015 Update 2, one can mix C++ exceptions with SEH.
The built-in crash reporting of Windows Phone 8 sucks. It doesn't report registers, stack, what modules were loaded at crash time... Definitely not something you can debug from.
In the managed world, things aren't so bad. There's an event called UnhandledException in the Application class that one is free to catch and process. No local variable access (that I know of), but at least you get to dump globals and the managed stack.
Enter native world. When native code crashes, it does so hard. Access violations, illegal instructions - the fun stuff. Some native exceptions are reported to the managed world as instances of SEHException, but not all of them, and the relevant debugging details are lost anyway.
I haven't so far found a way to catch all native crashes in my app, but here's the recipe for catching them on per-method basis. The ingredients are twofold: C++ lambdas and good old C-based structured exception handling.
In a typical mixed mode WP8 app, points of entry into the native world happen to be WinRT class methods (COM methods deep inside, but that's not the point). The idea is to wrap all method's functionality in a lambda with no parameters and no return value, and pass said lambda to a SEH-aware wrapper. The latter should be written in C - the compiler won't let you mix SEH exceptions with C++ exceptions.
The SEH-aware wrapper would call the underlying lambda, and if it crashes, execute some kind of a native exception handler, passing the debugging details. The handler can't do much, but at least it can dump a log into isolated storage.
On the consumption side, the method that used to be
public ref class CMyClass sealed
int MyMethod(int i, String ^s)
int Result = DoSomething(s);
would instead become:
int MyMethod(int i, String ^s)
SafeCall([i, s, &Result]()
Result = DoSomething(s);
The idea is that SafeCall() would catch native exceptions, if any, and log them for subsequent submission and analysis. Let's get to the implementation of SafeCall.
There are many ways to skin this particular cat. They all involve some degree of type unsafety that C is so famous for; I've tried to keep it to a minimum. In my implementation, type unsafety is only introduced on the C/C++ border. Since all lambdas are different, unrelated datatypes at compile time, some template magic is in order. Here goes the H file to be included wherever SafeCall() is being employed (it's called SafeCall.h in my app):
extern void SafeNativeCall(void *);
//Implemented in C; that's where the SEH is
extern void CallFunctorWrapper(void *pfw);
//Entry point back from the C side
friend void CallFunctorWrapper(void *);
virtual void Call() = 0;
class CFunctorWrapper: public CFunctorWrapperBase
const TFunctor &m_Functor;
//Interface for the SafeCall function
CFunctorWrapper(const TFunctor &f)
//This is the interface for callers.
void SafeCall(const TFunctor &f)
//Calling into the C world.
//Typecast to make sure class pointer to void* and back
//conversion is proper.
So SafeCall would call SafeNativeCall(), which is defined as extern "C" and takes a void* parameter. That's where the SEH takes place. The CPU context record is not available to an exception handler, but it's available to an exception filter, so we capture it there.
The following should be a C file, not CPP.
extern void CallFunctorWrapper(void *p);
//The gate back into the C++ world
extern void OnCrash(DWORD Code, void *Address, CONTEXT *Ctxt); //Crash callback; implement it in C++ as extern "C" if you wish
DWORD ExcFilter(DWORD Code, LPEXCEPTION_POINTERS ep, CONTEXT *Ctxt, void **Address)
if(Code == EXCEPTION_ACCESS_VIOLATION ||
Code == EXCEPTION_DATATYPE_MISALIGNMENT ||
Code == EXCEPTION_ARRAY_BOUNDS_EXCEEDED ||
Code == EXCEPTION_INT_DIVIDE_BY_ZERO ||
Code == EXCEPTION_INT_OVERFLOW ||
Code == EXCEPTION_PRIV_INSTRUCTION ||
Code == EXCEPTION_ILLEGAL_INSTRUCTION ||
Code == EXCEPTION_STACK_OVERFLOW)
//Capture them now. There won't be another chance
*Ctxt = *ep->ContextRecord;
*Address = ep->ExceptionRecord->ExceptionAddress;
void SafeNativeCall(void *p)
void *Address = 0;
CallFunctorWrapper(p); //Calling back into the C++ world
__except(ExcFilter(GetExceptionCode(), GetExceptionInformation(), &Ctxt, &Address))
OnCrash(GetExceptionCode(), Address, &Ctxt); //Oops...
The final bit of the puzzle is the implementation of CallFunctorWrapper. Its only purpose is to let C call a virtual method of a C++ object. It's implemented in a C++ file like this:
extern "C" void CallFunctorWrapper(void *pfw)
//Doubling back on the void* cast of above
The implementation of OnCrash() is another big topic; I won't go into that here. In my app, it dumps everything it deems relevant (stack, registers, app version, module info, some globals) into a text file in isolated storage, then terminates the app. On the next run, the app asks the user if they want to send a crash report home. Unlike the crash catching portion above, crash reporting would be somewhat architecture dependent - the contents of the CONTEXT structure vary between Intel and ARM. On the simulator, the former works, on a real device - the latter.
Naturally, on the other end of the submission there's a Web service and eventually a bug tracker.
The whole idea could've been implemented in less code (but with more sketchy pointer fiddling). Another avenue for improvement would involve parametrizing by return type, so that one doesn't have to capture the return value in a variable and may instead write:
}); //returns 17
Please don't read this as an advice to continue execution after a crash. All kinds of consequences might ensue if you do.