public static unsafe int RhpCalculateStackTraceWorker(IntPtr[] outputBuffer) { int nFrames = 0; bool success = (outputBuffer != null); StackFrameIterator frameIter = new StackFrameIterator(); bool isValid = frameIter.Init(null); for (; isValid; isValid = frameIter.Next()) { if (outputBuffer != null) { if (nFrames < outputBuffer.Length) { outputBuffer[nFrames] = new IntPtr(frameIter.ControlPC); } else { success = false; } } nFrames++; } return(success ? nFrames : -nFrames); }
private static unsafe int RhpCalculateStackTraceWorker(IntPtr *pOutputBuffer, uint outputBufferLength) { uint nFrames = 0; bool success = true; StackFrameIterator frameIter = new StackFrameIterator(); bool isValid = frameIter.Init(null); Debug.Assert(isValid, "Missing RhGetCurrentThreadStackTrace frame"); // Note that the while loop will skip RhGetCurrentThreadStackTrace frame while (frameIter.Next()) { if (nFrames < outputBufferLength) { pOutputBuffer[nFrames] = new IntPtr(frameIter.ControlPC); } else { success = false; } nFrames++; } return(success ? (int)nFrames : -(int)nFrames); }
private static bool FindFirstPassHandler(object exception, uint idxStart, ref StackFrameIterator frameIter, out uint tryRegionIdx, out byte *pHandler) { pHandler = null; tryRegionIdx = MaxTryRegionIdx; EHEnum ehEnum; byte * pbMethodStartAddress; if (!InternalCalls.RhpEHEnumInitFromStackFrameIterator(ref frameIter, &pbMethodStartAddress, &ehEnum)) { return(false); } byte *pbControlPC = frameIter.ControlPC; uint codeOffset = (uint)(pbControlPC - pbMethodStartAddress); uint lastTryStart = 0, lastTryEnd = 0; // Search the clauses for one that contains the current offset. RhEHClause ehClause; for (uint curIdx = 0; InternalCalls.RhpEHEnumNext(&ehEnum, &ehClause); curIdx++) { // // Skip to the starting try region. This is used by collided unwinds and rethrows to pickup where // the previous dispatch left off. // if (idxStart != MaxTryRegionIdx) { if (curIdx <= idxStart) { lastTryStart = ehClause._tryStartOffset; lastTryEnd = ehClause._tryEndOffset; continue; } // Now, we continue skipping while the try region is identical to the one that invoked the // previous dispatch. if ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd)) { continue; } } RhEHClauseKind clauseKind = ehClause._clauseKind; if (((clauseKind != RhEHClauseKind.RH_EH_CLAUSE_TYPED) && (clauseKind != RhEHClauseKind.RH_EH_CLAUSE_FILTER)) || !ehClause.ContainsCodeOffset(codeOffset)) { continue; } // Found a containing clause. Because of the order of the clauses, we know this is the // most containing. if (clauseKind == RhEHClauseKind.RH_EH_CLAUSE_TYPED) { if (ShouldTypedClauseCatchThisException(exception, (EEType *)ehClause._pTargetType)) { pHandler = ehClause._handlerAddress; tryRegionIdx = curIdx; return(true); } } else { byte *pFilterFunclet = ehClause._filterAddress; bool shouldInvokeHandler = InternalCalls.RhpCallFilterFunclet(exception, pFilterFunclet, frameIter.RegisterSet); if (shouldInvokeHandler) { pHandler = ehClause._handlerAddress; tryRegionIdx = curIdx; return(true); } } } return(false); }
private static void DispatchEx(ref StackFrameIterator frameIter, ref ExInfo exInfo, uint startIdx) { Debug.Assert(exInfo._passNumber == 1, "expected asm throw routine to set the pass"); object exceptionObj = exInfo.ThrownException; // ------------------------------------------------ // // First pass // // ------------------------------------------------ UIntPtr handlingFrameSP = MaxSP; byte * pCatchHandler = null; uint catchingTryRegionIdx = MaxTryRegionIdx; bool isFirstRethrowFrame = (startIdx != MaxTryRegionIdx); bool isFirstFrame = true; byte * prevControlPC = null; UIntPtr prevFramePtr = UIntPtr.Zero; bool unwoundReversePInvoke = false; bool isValid = frameIter.Init(exInfo._pExContext); Debug.Assert(isValid, "RhThrowEx called with an unexpected context"); DebuggerNotify.BeginFirstPass(exceptionObj, frameIter.ControlPC, frameIter.SP); for (; isValid; isValid = frameIter.Next(out startIdx, out unwoundReversePInvoke)) { // For GC stackwalking, we'll happily walk across native code blocks, but for EH dispatch, we // disallow dispatching exceptions across native code. if (unwoundReversePInvoke) { break; } prevControlPC = frameIter.ControlPC; DebugScanCallFrame(exInfo._passNumber, frameIter.ControlPC, frameIter.SP); // A debugger can subscribe to get callbacks at a specific frame of exception dispatch // exInfo._notifyDebuggerSP can be populated by the debugger from out of process // at any time. if (exInfo._notifyDebuggerSP == frameIter.SP) { DebuggerNotify.FirstPassFrameEntered(exceptionObj, frameIter.ControlPC, frameIter.SP); } UpdateStackTrace(exceptionObj, ref exInfo, ref isFirstRethrowFrame, ref prevFramePtr, ref isFirstFrame); byte *pHandler; if (FindFirstPassHandler(exceptionObj, startIdx, ref frameIter, out catchingTryRegionIdx, out pHandler)) { handlingFrameSP = frameIter.SP; pCatchHandler = pHandler; DebugVerifyHandlingFrame(handlingFrameSP); break; } } DebuggerNotify.EndFirstPass(exceptionObj, pCatchHandler, handlingFrameSP); if (pCatchHandler == null) { UnhandledExceptionFailFastViaClasslib( RhFailFastReason.PN_UnhandledException, exceptionObj, (IntPtr)prevControlPC, // IP of the last frame that did not handle the exception ref exInfo); } // We FailFast above if the exception goes unhandled. Therefore, we cannot run the second pass // without a catch handler. Debug.Assert(pCatchHandler != null, "We should have a handler if we're starting the second pass"); DebuggerNotify.BeginSecondPass(); // ------------------------------------------------ // // Second pass // // ------------------------------------------------ // Due to the stackwalker logic, we cannot tolerate triggering a GC from the dispatch code once we // are in the 2nd pass. This is because the stackwalker applies a particular unwind semantic to // 'collapse' funclets which gets confused when we walk out of the dispatch code and encounter the // 'main body' without first encountering the funclet. The thunks used to invoke 2nd-pass // funclets will always toggle this mode off before invoking them. InternalCalls.RhpSetThreadDoNotTriggerGC(); exInfo._passNumber = 2; startIdx = MaxTryRegionIdx; isValid = frameIter.Init(exInfo._pExContext); for (; isValid && ((byte *)frameIter.SP <= (byte *)handlingFrameSP); isValid = frameIter.Next(out startIdx)) { Debug.Assert(isValid, "second-pass EH unwind failed unexpectedly"); DebugScanCallFrame(exInfo._passNumber, frameIter.ControlPC, frameIter.SP); if (frameIter.SP == handlingFrameSP) { // invoke only a partial second-pass here... InvokeSecondPass(ref exInfo, startIdx, catchingTryRegionIdx); break; } InvokeSecondPass(ref exInfo, startIdx); } // ------------------------------------------------ // // Call the handler and resume execution // // ------------------------------------------------ exInfo._idxCurClause = catchingTryRegionIdx; InternalCalls.RhpCallCatchFunclet( exceptionObj, pCatchHandler, frameIter.RegisterSet, ref exInfo); // currently, RhpCallCatchFunclet will resume after the catch Debug.Assert(false, "unreachable"); FallbackFailFast(RhFailFastReason.InternalError, null); }
public static unsafe int RhpCalculateStackTraceWorker(IntPtr[] outputBuffer) { int nFrames = 0; bool success = (outputBuffer != null); StackFrameIterator frameIter = new StackFrameIterator(); bool isValid = frameIter.Init(null); for (; isValid; isValid = frameIter.Next()) { if (outputBuffer != null) { if (nFrames < outputBuffer.Length) outputBuffer[nFrames] = new IntPtr(frameIter.ControlPC); else success = false; } nFrames++; } return success ? nFrames : -nFrames; }
internal static extern bool RhpSfiNext(ref StackFrameIterator pThis, out uint uExCollideClauseIdx, out bool fUnwoundReversePInvoke);
internal static extern unsafe bool RhpSfiInit(ref StackFrameIterator pThis, void* pStackwalkCtx);
internal unsafe extern static bool RhpEHEnumInitFromStackFrameIterator(ref StackFrameIterator pFrameIter, byte** pMethodStartAddress, void* pEHEnum);
internal static extern bool RhpSfiNext(ref StackFrameIterator pThis, out uint uExCollideClauseIdx, out bool fUnwoundReversePInvoke);
internal static extern unsafe bool RhpSfiInit(ref StackFrameIterator pThis, void *pStackwalkCtx, bool instructionFault);
internal extern static unsafe bool RhpEHEnumInitFromStackFrameIterator(ref StackFrameIterator pFrameIter, byte **pMethodStartAddress, void *pEHEnum);
private static bool FindFirstPassHandler(object exception, uint idxStart, ref StackFrameIterator frameIter, out uint tryRegionIdx, out byte* pHandler) { pHandler = null; tryRegionIdx = MaxTryRegionIdx; EHEnum ehEnum; byte* pbMethodStartAddress; if (!InternalCalls.RhpEHEnumInitFromStackFrameIterator(ref frameIter, &pbMethodStartAddress, &ehEnum)) return false; byte* pbControlPC = frameIter.ControlPC; uint codeOffset = (uint)(pbControlPC - pbMethodStartAddress); uint lastTryStart = 0, lastTryEnd = 0; // Search the clauses for one that contains the current offset. RhEHClause ehClause; for (uint curIdx = 0; InternalCalls.RhpEHEnumNext(&ehEnum, &ehClause); curIdx++) { // // Skip to the starting try region. This is used by collided unwinds and rethrows to pickup where // the previous dispatch left off. // if (idxStart != MaxTryRegionIdx) { if (curIdx <= idxStart) { lastTryStart = ehClause._tryStartOffset; lastTryEnd = ehClause._tryEndOffset; continue; } // Now, we continue skipping while the try region is identical to the one that invoked the // previous dispatch. if ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd)) continue; // We are done skipping. This is required to handle empty finally block markers that are used // to separate runs of different try blocks with same native code offsets. idxStart = MaxTryRegionIdx; } RhEHClauseKind clauseKind = ehClause._clauseKind; if (((clauseKind != RhEHClauseKind.RH_EH_CLAUSE_TYPED) && (clauseKind != RhEHClauseKind.RH_EH_CLAUSE_FILTER)) || !ehClause.ContainsCodeOffset(codeOffset)) { continue; } // Found a containing clause. Because of the order of the clauses, we know this is the // most containing. if (clauseKind == RhEHClauseKind.RH_EH_CLAUSE_TYPED) { if (ShouldTypedClauseCatchThisException(exception, (EEType*)ehClause._pTargetType)) { pHandler = ehClause._handlerAddress; tryRegionIdx = curIdx; return true; } } else { byte* pFilterFunclet = ehClause._filterAddress; bool shouldInvokeHandler = InternalCalls.RhpCallFilterFunclet(exception, pFilterFunclet, frameIter.RegisterSet); if (shouldInvokeHandler) { pHandler = ehClause._handlerAddress; tryRegionIdx = curIdx; return true; } } } return false; }
private static void DispatchEx(ref StackFrameIterator frameIter, ref ExInfo exInfo, uint startIdx) { Debug.Assert(exInfo._passNumber == 1, "expected asm throw routine to set the pass"); object exceptionObj = exInfo.ThrownException; // ------------------------------------------------ // // First pass // // ------------------------------------------------ UIntPtr handlingFrameSP = MaxSP; byte* pCatchHandler = null; uint catchingTryRegionIdx = MaxTryRegionIdx; bool isFirstRethrowFrame = (startIdx != MaxTryRegionIdx); bool isFirstFrame = true; byte* prevControlPC = null; UIntPtr prevFramePtr = UIntPtr.Zero; bool unwoundReversePInvoke = false; bool isValid = frameIter.Init(exInfo._pExContext); Debug.Assert(isValid, "RhThrowEx called with an unexpected context"); DebuggerNotify.BeginFirstPass(exceptionObj, frameIter.ControlPC, frameIter.SP); for (; isValid; isValid = frameIter.Next(out startIdx, out unwoundReversePInvoke)) { // For GC stackwalking, we'll happily walk across native code blocks, but for EH dispatch, we // disallow dispatching exceptions across native code. if (unwoundReversePInvoke) break; prevControlPC = frameIter.ControlPC; DebugScanCallFrame(exInfo._passNumber, frameIter.ControlPC, frameIter.SP); // A debugger can subscribe to get callbacks at a specific frame of exception dispatch // exInfo._notifyDebuggerSP can be populated by the debugger from out of process // at any time. if (exInfo._notifyDebuggerSP == frameIter.SP) DebuggerNotify.FirstPassFrameEntered(exceptionObj, frameIter.ControlPC, frameIter.SP); UpdateStackTrace(exceptionObj, ref exInfo, ref isFirstRethrowFrame, ref prevFramePtr, ref isFirstFrame); byte* pHandler; if (FindFirstPassHandler(exceptionObj, startIdx, ref frameIter, out catchingTryRegionIdx, out pHandler)) { handlingFrameSP = frameIter.SP; pCatchHandler = pHandler; DebugVerifyHandlingFrame(handlingFrameSP); break; } } DebuggerNotify.EndFirstPass(exceptionObj, pCatchHandler, handlingFrameSP); if (pCatchHandler == null) { UnhandledExceptionFailFastViaClasslib( RhFailFastReason.PN_UnhandledException, exceptionObj, (IntPtr)prevControlPC, // IP of the last frame that did not handle the exception ref exInfo); } // We FailFast above if the exception goes unhandled. Therefore, we cannot run the second pass // without a catch handler. Debug.Assert(pCatchHandler != null, "We should have a handler if we're starting the second pass"); DebuggerNotify.BeginSecondPass(); // ------------------------------------------------ // // Second pass // // ------------------------------------------------ // Due to the stackwalker logic, we cannot tolerate triggering a GC from the dispatch code once we // are in the 2nd pass. This is because the stackwalker applies a particular unwind semantic to // 'collapse' funclets which gets confused when we walk out of the dispatch code and encounter the // 'main body' without first encountering the funclet. The thunks used to invoke 2nd-pass // funclets will always toggle this mode off before invoking them. InternalCalls.RhpSetThreadDoNotTriggerGC(); exInfo._passNumber = 2; startIdx = MaxTryRegionIdx; isValid = frameIter.Init(exInfo._pExContext); for (; isValid && ((byte*)frameIter.SP <= (byte*)handlingFrameSP); isValid = frameIter.Next(out startIdx)) { Debug.Assert(isValid, "second-pass EH unwind failed unexpectedly"); DebugScanCallFrame(exInfo._passNumber, frameIter.ControlPC, frameIter.SP); if (frameIter.SP == handlingFrameSP) { // invoke only a partial second-pass here... InvokeSecondPass(ref exInfo, startIdx, catchingTryRegionIdx); break; } InvokeSecondPass(ref exInfo, startIdx); } // ------------------------------------------------ // // Call the handler and resume execution // // ------------------------------------------------ exInfo._idxCurClause = catchingTryRegionIdx; InternalCalls.RhpCallCatchFunclet( exceptionObj, pCatchHandler, frameIter.RegisterSet, ref exInfo); // currently, RhpCallCatchFunclet will resume after the catch Debug.Assert(false, "unreachable"); FallbackFailFast(RhFailFastReason.InternalError, null); }
private static unsafe int RhpCalculateStackTraceWorker(IntPtr * pOutputBuffer, uint outputBufferLength) { uint nFrames = 0; bool success = true; StackFrameIterator frameIter = new StackFrameIterator(); bool isValid = frameIter.Init(null); Debug.Assert(isValid, "Missing RhGetCurrentThreadStackTrace frame"); // Note that the while loop will skip RhGetCurrentThreadStackTrace frame while (frameIter.Next()) { if (nFrames < outputBufferLength) pOutputBuffer[nFrames] = new IntPtr(frameIter.ControlPC); else success = false; nFrames++; } return success ? (int)nFrames : -(int)nFrames; }
internal static extern unsafe bool RhpSfiNext(ref StackFrameIterator pThis, uint *uExCollideClauseIdx, bool *fUnwoundReversePInvoke);
internal static extern unsafe bool RhpSfiInit(ref StackFrameIterator pThis, void *pStackwalkCtx);