I came across an interesting product bug while analyzing Watson crash dumps today. Our application was crashing due to an unhandled MissingMethodException on a background thread. Looking at the managed stack trace revealed the exception was coming from a method which uses the Dispatcher.Invoke(Delegate, params object[]) overload, added in the .NET Framework 3.5 SP1. This is an unsupported configuration; nonetheless we don’t want to be crashing hard.
When I investigated the offending method I was perplexed to find a top level exception handler wrapping all calls to Dispatcher.Invoke. This method is invoked on a background thread so we are careful not to allow an unhandled exception which would result in a crash. Needless to say I wasted a bit of time pouring over the version history, assembly versions, etc. Finally I took a look at the native callstack
0:004> kb
RetAddr : Args to Child : Call Site
00000000`74e196d0 : 00000000`1c25d650 00000000`1c25c8e8 00000000`00000100 00000000`00000000 : KERNEL32!RaiseException+0x39
00000000`771f54a1 : 00000000`00000000 00000000`1c25db20 00000000`00000000 00000000`00000000 : MSVCR80!_CxxCallCatchBlock+0x180
000007fe`f33ef38a : 00000000`1b18d6f0 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RcConsolidateFrames+0x3
000007fe`f33b5eaf : 00000000`00000000 00000002`00000000 00000000`00000000 00000000`1b190980 : mscorwks!MethodDesc::MakeJitWorker+0x1ea
000007fe`f33a560b : 00000000`00000000 00000000`00295200 00000000`00000000 00000000`02a21758 : mscorwks!MethodDesc::DoPrestub+0x116f
000007fe`f34e2207 : 00000000`02a22028 00000000`00000000 00000000`02a21fe8 00000000`1b185110 : mscorwks!PreStubWorker+0x1eb
000007ff`001903ce : 00000000`02a216b8 00000000`00000000 00000000`1c25f5c0 00000000`00000000 : mscorwks!ThePreStubAMD64+0x87
000007fe`f174174b : 00000000`02a216b8 00000000`00000000 00000000`1c25f5c0 00000000`00000000 : 0x7ff`001903ce
000007fe`f179d786 : 00000000`00000018 00000000`00000000 00000000`00250288 00000000`771f7b1a : mscorlib_ni+0x2f174b
000007fe`f34e2322 : 00000000`02a21758 000007fe`f3976ea0 ffffffff`fffffffe 00000000`00002400 : mscorlib_ni+0x34d786
000007fe`f33e3bb3 : 00000000`0000017e 000007fe`f18b8aa8 00000000`002a0588 00000000`00000000 : mscorwks!CallDescrWorker+0x82
000007fe`f33ebc46 : 00000000`1c25f9e8 000007fe`f33d353d ffffffff`fffffffe 00000000`00000000 : mscorwks!CallDescrWorkerWithHandler+0xd3
000007fe`f343bd7f : 000007fe`f14d5730 000007fe`f1451000 000007fe`f3af7c78 000007fe`f336c1d8 : mscorwks!DispatchCallDebuggerWrapper+0x3e
000007fe`f32f6a5e : 00000000`1c25fb78 00000000`1b185101 00000000`00000001 00000000`1b185110 : mscorwks!DispatchCallNoEH+0x5f
000007fe`f3327598 : 00000000`02a21758 00000000`02a21758 00000000`00000001 000007fe`f33e0f6a : mscorwks!AddTimerCallback_Worker+0x92
000007fe`f342eb85 : 00000000`00000001 00000000`00000000 ffffffff`fffffffe 00000000`1c25fb98 : mscorwks!Thread::DoADCallBack+0x488
000007fe`f331a515 : 00000000`002a0230 00000000`1b185110 00000000`1c25faa0 00000000`1b1822f0 : mscorwks!SVR::gc_heap::make_heap_segment+0x155
000007fe`f331487e : 00000000`1c25fb98 ffffffff`ffffffff 00000000`1b185110 00000000`1c25a8f0 : mscorwks!AssemblySecurityDescriptor::GetZone+0x169
000007fe`f32f5a7b : ffffffff`fffffffe 00000000`00000001 ffffffff`fffffffe 00000000`002c9920 : mscorwks!AddTimerCallbackEx+0xba
000007fe`f336dc77 : 00000000`00000000 00000000`00000001 00000000`00000000 00000000`00000001 : mscorwks!ThreadpoolMgr::AsyncTimerCallbackCompletion+0x53
It looks like the exception is actually being thrown from native code during the JIT. Here is a simple repro
public class BackgroundWorker { private Timer _timer; public BackgroundWorker() { _timer = new Timer((state) => DoWork(), null, 0, 1000); } public void DoWork() { // top level handler try { // ... // call missing method here // ... } catch (Exception e) { // some logging } } }
Before the thread pool thread executes the anonymous method it must be JITed.
private void <.ctor>b__0(object state) { this.DoWork(); } |
When the JITer encounters missing method inside of DoWork it throws a MissingMethodException. Since this takes place outside the try/catch block the process crashes.