诡异的bug异常,(__debugbreak()语句或类似调用)
楔子
项目里面出现一种奇怪的Bug ,直接Debug的时候能正常运行,编译的可执行文件也完全没有问题。当逐步断点调试到某一行到时候,却出现了错误。这个奇怪的错误,搞笑的吗?
异常地方如下
堆栈信息如下
KernelBase.dll!wil::details::DebugBreak(void) 未知
> coreclr.dll!CHECK::Setup(const char * message, const char * condition, const char * file, int line) 行 C++
coreclr.dll!CLRVectoredExceptionHandlerPhase3(_EXCEPTION_POINTERS * pExceptionInfo) 行 C++
coreclr.dll!CLRVectoredExceptionHandlerPhase2(_EXCEPTION_POINTERS * pExceptionInfo) 行 C++
coreclr.dll!CLRVectoredExceptionHandler(_EXCEPTION_POINTERS * pExceptionInfo) 行 C++
coreclr.dll!CLRVectoredExceptionHandlerShim(_EXCEPTION_POINTERS * pExceptionInfo) 行 C++
ntdll.dll!RtlpCallVectoredHandlers() 未知
ntdll.dll!RtlDispatchException() 未知
ntdll.dll!KiUserExceptionDispatch() 未知
coreclr.dll!_hpCodeHdr::GetNumberOfUnwindInfos() 行 C++
coreclr.dll!_hpCodeHdr::GetUnwindInfo(unsigned int iUnwindInfo) 行 C++
coreclr.dll!CEEJitInfo::WriteCode(EEJitManager * jitMgr) 行 C++
coreclr.dll!UnsafeJitFunction(PrepareCodeConfig * config, COR_ILMETHOD_DECODER * ILHeader, CORJIT_FLAGS flags, unsigned long * pSizeOfCode) 行 C++
coreclr.dll!MethodDesc::JitCompileCodeLocked(PrepareCodeConfig * pConfig, ListLockEntryBase<NativeCodeVersion> * pEntry, unsigned long * pSizeOfCode, CORJIT_FLAGS * pFlags) 行 C++
coreclr.dll!MethodDesc::JitCompileCodeLockedEventWrapper(PrepareCodeConfig * pConfig, ListLockEntryBase<NativeCodeVersion> * pEntry) 行 C++
coreclr.dll!MethodDesc::JitCompileCode(PrepareCodeConfig * pConfig) 行 C++
coreclr.dll!MethodDesc::PrepareILBasedCode(PrepareCodeConfig * pConfig) 行 C++
coreclr.dll!MethodDesc::PrepareCode(PrepareCodeConfig * pConfig) 行 C++
coreclr.dll!CodeVersionManager::PublishVersionableCodeIfNecessary(MethodDesc * pMethodDesc, CallerGCMode callerGCMode, bool * doBackpatchRef, bool * doFullBackpatchRef) 行 C++
coreclr.dll!MethodDesc::DoPrestub(MethodTable * pDispatchingMT, CallerGCMode callerGCMode) 行 C++
coreclr.dll!PreStubWorker(TransitionBlock * pTransitionBlock, MethodDesc * pMD) 行 C++
coreclr.dll!ThePreStub() 未知 00007ffca072040b() 未知 d312125d88() 未知 d312125d48() 未知 cccccccccccccccc() 未知 cccccccccccccccc() 未知 0000006f03f7d6e8() 未知
很明显错误是出现coreclr.dll!_hpCodeHdr::GetNumberOfUnwindInfos()这一段代码,这段代码函数内容如下:
PTR_RUNTIME_FUNCTION GetUnwindInfo(UINT iUnwindInfo)
{
SUPPORTS_DAC; _ASSERTE(iUnwindInfo < GetNumberOfUnwindInfos());
return dac_cast<PTR_RUNTIME_FUNCTION>(
PTR_TO_MEMBER_TADDR(RealCodeHeader, pRealCodeHeader, unwindInfos) + iUnwindInfo * sizeof(T_RUNTIME_FUNCTION));
}#if defined(FEATURE_EH_FUNCLETS)
UINT GetNumberOfUnwindInfos()
{
SUPPORTS_DAC;
return pRealCodeHeader->nUnwindInfos;
}
GetUnwindInfo函数调用了GetNumberOfUnwindInfos,后者里面报了异常。原因在于pRealCodeHeader的值为零。所以它报了异常。 这个pRealCodeHeader来自何处呢?是谁给他赋值的呢?导致了运行没有错误,逐步调试却报错。
推究
通过跟踪得知,调用GetUnwindInfo函数的是WriteCode函数,在WriteCode调用GetUnwindInfo函数之前调用了WriteCodeBytes函数。其代码如下:
void CEEJitInfo::WriteCodeBytes(){
LIMITED_METHOD_CONTRACT;#ifdef USE_INDIRECT_CODEHEADER
if (m_pRealCodeHeader != NULL)
{ // Restore the read only version of the real code header
m_CodeHeaderRW->SetRealCodeHeader(m_pRealCodeHeader);
m_pRealCodeHeader = NULL;
}#endif // USE_INDIRECT_CODEHEADER
if (m_CodeHeaderRW != m_CodeHeader)
{ ExecutableWriterHolder<void> codeWriterHolder((void *)m_CodeHeader, m_codeWriteBufferSize); memcpy(codeWriterHolder.GetRW(), m_CodeHeaderRW, m_codeWriteBufferSize);
}
}
这里面非常简单,主要做了两件事情,第一件事是实例化一个ExecutableWriterHolder的实例codeWriterHolder。注意看实例化codeWriterHolder的参数m_CodeHeader,它里面正是包含了上面异常空值pRealCodeHeader字段。只要知道m_CodeHeader是谁赋值的,就可以找出这个空值的源头了。
codeWriterHolder实例化里面做的事情是调用了MapViewOfFile,这个函数是把文件映射到内存。具体的就是通过一个内存地址(此处称为地址一),转换出另外一个内存地址(此处称为地址二),此后如果地址二的内存地址的值发生了变化,那么地址一内存地址值也会发生变化。
memcpy(codeWriterHolder.GetRW(), m_CodeHeaderRW, m_codeWriteBufferSize);codeWriterHolder里面包含了两个字段,第一个字段是m_CodeHeader的地址,第二个字段是m_CodeHeader被MapViewOfFile转换后的地址。这句代码主要是把m_CodeHeaderRW也就是被RyuJit编译之后的机器码拷贝到被转换的内存地址地方进行存储,同时也会改变前者。
问题就在于,当运行程序单步Debug的时候,这个被转换后的地址里面是有值的,但是未被转换的则是空值,所以异常出现了。
为什么会出现这种情况,其实现在还是没有解决,就当记录下吧。