|
|
First thing read thru this text, it often provides good enough information. Here windbg is warning you stored exception information in .excr
.
Generally you start with either .excr
command or start with !analyze -v
extension.
.cls
command clears the window if gets too cluttered
!analyze -v
dumps a lot of text, lets take it step by step:
|
|
Here is clearly highlighted that .excr
at line 15
has expection information. Lines 16-23
have register contents dumped. These are often helpful to make sense of what was being executed at the moment of error. With some basic assembly knowledge and patterns of code generated it can help pin-point the root cause in mins.
Line 24
points to the last stack-frame: ucrtbase!abort+0x4e:
, the error was thrown from function abort
in ucrtbase
and offset 0x4e
in that fn. The executed at that line is int 29h
, which is an instruction defined by [__fastfail
intrinsic] (https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail?view=vs-2019)
Now it analyze
extension tries to classify the error per MS classification system. Here it listed error as FATAL_APP_EXIT
, however the error code 0xc0000409
specified is not really what happened with this application. This is a case of unhandled exception from a background thread, eventually crashing the process not a stack-based buffer overflow.
|
|
I have pasted the entire output of this analyze -v
command here on gist.github.com. Towards the end of analyze
output it prints the call-stack for the thread from which the error was cached in .excr
. However that is just 1 thread.
To further check what is going on, I am going to query threads in this dump with command ~
:
0:009> ~
0 Id: 62c.1084 Suspend: 1 Teb: 00000073`bacb7000 Unfrozen
1 Id: 62c.3a48 Suspend: 1 Teb: 00000073`bacb9000 Unfrozen
2 Id: 62c.2828 Suspend: 1 Teb: 00000073`bacbb000 Unfrozen
3 Id: 62c.2850 Suspend: 1 Teb: 00000073`bacbd000 Unfrozen
4 Id: 62c.2b28 Suspend: 1 Teb: 00000073`bacbf000 Unfrozen
5 Id: 62c.41a0 Suspend: 1 Teb: 00000073`bacc1000 Unfrozen
6 Id: 62c.40c Suspend: 0 Teb: 00000073`bacc3000 Unfrozen
7 Id: 62c.854 Suspend: 0 Teb: 00000073`bacc5000 Unfrozen
8 Id: 62c.2ef4 Suspend: 0 Teb: 00000073`bacc7000 Unfrozen
. 9 Id: 62c.2a10 Suspend: 0 Teb: 00000073`bacc9000 Unfrozen
10 Id: 62c.1a58 Suspend: 0 Teb: 00000073`baccb000 Unfrozen
11 Id: 62c.890 Suspend: 0 Teb: 00000073`baccd000 Unfrozen
12 Id: 62c.17f0 Suspend: 0 Teb: 00000073`baccf000 Unfrozen
Here 62c
is process-id and 1084
, 3a48
and so on are thread-ids.
to check stack of thread 3a48
Thread#1, execute command: ~1 KB
:
0:009> ~1 KB
# RetAddr : Args to Child : Call Site
00 00007ffb`5c9b4060 : 00000000`00000000 00000000`00000000 000001f1`480639d0 00000000`00000002 : ntdll!NtWaitForWorkViaWorkerFactory+0x14
01 00007ffb`5afc7bd4 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!TppWorkerThread+0x300
02 00007ffb`5c9eced1 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0x14
03 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
To check stack of (upto 25 frames):
- all threads, execute command:
~* KB 25
- thread that caused exception:
~# K 25
~# kP 25
provides with lot more information to browse thru
Note:
K
command takes various parameters and prints stack with various level of details. I have found kP to be quite verbose and browsable, but for blog postsKC
option works best
Looking at call-stack:
0:009> ~# KC 25
# Call Site
00 ucrtbase!abort
01 ucrtbase!terminate
02 VCRUNTIME140!FindHandler<__FrameHandler3>
03 VCRUNTIME140!__InternalCxxFrameHandler<__FrameHandler3>
04 VCRUNTIME140!__CxxFrameHandler3
05 ntdll!RtlpExecuteHandlerForException
06 ntdll!RtlDispatchException
07 ntdll!RtlRaiseException
08 KERNELBASE!RaiseException
09 VCRUNTIME140!_CxxThrowException
0a msvcp140!std::_Xout_of_range
0b tbb_expt!std::vector<int,std::allocator<int> >::_Xrange
0c tbb_expt!std::vector<int,std::allocator<int> >::at
0d tbb_expt!parallelFor::__l5::<lambda_66c7355d0edda6973d22642add3a91c2>::operator()
0e tbb_expt!std::_For_each_ivdep
0f tbb_expt!std::_Static_partitioned_for_each2<int *,__int64,<lambda_66c7355d0edda6973d22642add3a91c2> >::_Process_chunk
10 tbb_expt!std::_Run_available_chunked_work<std::_Static_partitioned_for_each2<int *,__int64,<lambda_66c7355d0edda6973d22642add3a91c2> > >
11 tbb_expt!std::_Static_partitioned_for_each2<int *,__int64,<lambda_66c7355d0edda6973d22642add3a91c2> >::_Threadpool_callback
12 ntdll!TppWorkpExecuteCallback
13 ntdll!TppWorkerThread
14 kernel32!BaseThreadInitThunk
15 ntdll!RtlUserThreadStart
we can safely say that std::vector<int>::at()
threw an exception of for out of range: _Xout_of_range
(Frame 0c
and 0a
). This raised an error _CxxThrowException
at frame 09
and eventually lead to it trying to find exception handler to be executed: FindHandler<__FrameHandler3>
at frame 02
, which we did not register in first place: thus it called terminate
.
The code is to this effect:
|
|
Now the funny thing is: even if you change the execution policy to std::execution::seq
from std::execution::par_unseq
, it will still crash the application. The rational behind this decision is to keep behavior consistent for API whether you are running parallel or not.
That’s it for now..