.net core 处理异常的精髓部分

.net core 里面处理异常无论是在linux 或者是widnows都有一个异常入口函数processclrexception()

core里面的异常分为用户异常和硬件异常,前者是代码里面引发的异常,后面是CPU寄存器硬件等引发的异常

两者处理不同在于VEH扩展,当硬件异常的时候,会调用注册的VEH处理异常。而后进入processclrexception,用户异常则省略了VEH部分直接进入

当引发异常的时候,Jithelpers类会调用IL_throw进行处理。然后调用windows api raiseexception抛出异常,通过Windows处理机制进入异常入口函数

这个函数在 linux和windwos上通用,捕捉到异常之后,需要获取异常函数和调用来源,可以通过函数栈来递推循环处理得到来源。当获取到调用的

来源之后,就可以获取core clr 的异常处理,在 .net 里面每个函数都有一个异常处理表。获取到之后,可以枚举异常处理表一次调用 try ,catch ,finally

快的数据。以便处理异常。

IL_Throw 代码如下,主要是引入异常类的对象,然后RaiseTheExceptionInternalOnly函数,在这个函数里了调用了RaiseException(code, flags, argCount, args),

抛出异常代码,被processclrexception捕捉到。然后流程进入processclrexception。

IL_Throw代码如下:

 1 HCIMPL1(void, IL_Throw, Object* obj)
 2 {
 3 FCALL_CONTRACT;
 4 
 5 // This "violation" isn't a really a violation. 
 6 // We are calling a assembly helper that can't have an SO Tolerance contract
 7 CONTRACT_VIOLATION(SOToleranceViolation);
 8 /* Make no assumptions about the current machine state */
 9 ResetCurrentContext();
10 
11 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
12 
13 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
14 
15 OBJECTREF oref = ObjectToOBJECTREF(obj);
16 
17 #if defined(_DEBUG) && defined(_TARGET_X86_)
18 __helperframe.InsureInit(false, NULL);
19 g_ExceptionEIP = (LPVOID)__helperframe.GetReturnAddress();
20 #endif // defined(_DEBUG) && defined(_TARGET_X86_)
21 
22 
23 if (oref == 0)
24 COMPlusThrow(kNullReferenceException);
25 else
26 if (!IsException(oref->GetMethodTable()))
27 {
28 GCPROTECT_BEGIN(oref);
29 
30 WrapNonCompliantException(&oref);
31 
32 GCPROTECT_END();
33 }
34 else
35 { // We know that the object derives from System.Exception
36 if (g_CLRPolicyRequested &&
37 oref->GetMethodTable() == g_pOutOfMemoryExceptionClass)
38 {
39 EEPolicy::HandleOutOfMemory();
40 }
41 
42 // If the flag indicating ForeignExceptionRaise has been set,
43 // then do not clear the "_stackTrace" field of the exception object.
44 if (GetThread()->GetExceptionState()->IsRaisingForeignException())
45 {
46 ((EXCEPTIONREF)oref)->SetStackTraceString(NULL);
47 }
48 else
49 {
50 ((EXCEPTIONREF)oref)->ClearStackTracePreservingRemoteStackTrace();
51 }
52 }
53 
54 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
55 if (!g_pConfig->LegacyCorruptedStateExceptionsPolicy())
56 {
57 // Within the VM, we could have thrown and caught a managed exception. This is done by
58 // RaiseTheException that will flag that exception's corruption severity to be used
59 // incase it leaks out to managed code.
60 //
61 // If it does not leak out, but ends up calling into managed code that throws,
62 // we will come here. In such a case, simply reset the corruption-severity
63 // since we want the exception being thrown to have its correct severity set
64 // when CLR's managed code exception handler sets it.
65 
66 ThreadExceptionState *pExState = GetThread()->GetExceptionState();
67 pExState->SetLastActiveExceptionCorruptionSeverity(NotSet);
68 }
69 #endif // FEATURE_CORRUPTING_EXCEPTIONS
70 
71 //这个函数进入,调用抛出异常
72 
73 RaiseTheExceptionInternalOnly(oref, FALSE);
74 
75 HELPER_METHOD_FRAME_END();
76 }

.Net Core C++ 异常模型

 1 #define WIN32_LEAN_AND_MEAN
 2 
 3 #include <windows.h>
 4 #include <stdio.h>
 5 DWORD scratch;
 6 EXCEPTION_DISPOSITION
 7 __cdecl
 8 processclrexception(struct _EXCEPTION_RECORD* ExceptionRecord,
 9 void* EstablisherFrame,
10 struct _CONTEXT* ContextRecord,
11 void* DispatcherContext)
12 {
13 unsigned i;
14 // 指明是我们让流程转到我们的异常处理程序的
15 printf("Hello from an exception handler
");
16 // 改变CONTEXT结构中EAX的值,以便它指向可以成功进写操作的位置
17 ContextRecord->Eax = (DWORD)&scratch;
18 // 告诉操作系统重新执行出错的指令
19 return ExceptionContinueExecution;
20 }
21 int main()
22 {
23 DWORD handler = (DWORD)processclrexception
24 __asm
25 {
26 // 创建EXCEPTION_REGISTRATION结构:
27 push handler // handler函数的地址
28 push FS : [0] // 前一个handler函数的地址
29 mov FS : [0] , ESP // 安装新的EXECEPTION_REGISTRATION结构
30 }
31 RaiseException(EXCEPTION_ACCESS_VIOLATION
32 , EXCEPTION_NONCONTINUABLE, 0, NULL);
33 //__asm
34 //{
35 // mov eax, 0 // 将EAX清零
36 // mov[eax], 1 // 写EAX指向的内存从而故意引发一个错误
37 //}
38 printf("After writing!
");
39 __asm
40 {
41 // 移去我们的EXECEPTION_REGISTRATION结构
42 mov eax, [ESP] // 获取前一个结构
43 mov FS : [0] , EAX // 安装前一个结构
44 add esp, 8 // 将我们的EXECEPTION_REGISTRATION弹出堆栈
45 }
46 return 0;
47 
48 }

下面是ProcessCLRException,这个函数非常长,长达几千行,只贴出部分有用的

 1 ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord
 2 WIN64_ARG(IN ULONG64 MemoryStackFp)
 3 NOT_WIN64_ARG(IN ULONG MemoryStackFp),
 4 IN OUT PCONTEXT pContextRecord,
 5 IN OUT PDISPATCHER_CONTEXT pDispatcherContext
 6 )
 7 {
 8 
 9 DWORD   dwLastError     = GetLastError();//获取到当前的执行结果
10 
11 ExceptionTracker* pTracker = ExceptionTracker::GetOrCreateTracker( // 这个函数里面会实例化一个异常的托管类,比如ExceptionArgument类等。
12 pDispatcherContext->ControlPc,
13 sf,
14 pExceptionRecord,
15 pContextRecord,
16 bAsynchronousThreadStop,
17 !(dwExceptionFlags & EXCEPTION_UNWINDING),
18 &STState);
19 
20 status = pTracker->ProcessOSExceptionNotification( // 这个函数会调用 .net core里面的 异常的try块的代码
21 pExceptionRecord,
22 pContextRecord,
23 pDispatcherContext,
24 dwExceptionFlags,
25 sf,
26 pThread,
27 STState
28 #ifdef USE_PER_FRAME_PINVOKE_INIT
29 , (PVOID)pICFSetAsLimitFrame
30 #endif // USE_PER_FRAME_PINVOKE_INIT
31 );
32 
33 ClrUnwindEx(pExceptionRecord,// 这个函数调用了 windows api RtlUnwindEx 。RtlUnwindEx第二次调用ExceptionHandler,执行展开操作,清理资源等
34 (UINT_PTR)pThread,
35 INVALID_RESUME_ADDRESS,
36 pDispatcherContext->EstablisherFrame);
37 
38 // .net core里面的异常try 块被执行了,如果有finally ,在这里会被执行。调用了windwos api RtlRestoreContext(pContextRecord, pExceptionRecord);执行finally 块
39 
40 ExceptionTracker::ResumeExecution(pContextRecord,pExceptionRecord);
41 
42                                               NULL
43 
44                                               );
45 
46 }

以上就是大致的.net core 异常处理流程。有疑问可以一起交流 QQ群:676817308

原文地址:https://www.cnblogs.com/tangyanzhi1111/p/12830571.html