Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix unhandled exception line number issues #2269

Merged
merged 5 commits into from
Jan 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/coreclr/src/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3633,7 +3633,7 @@ void DacDbiInterfaceImpl::GetStackFramesFromException(VMPTR_Object vmObject, Dac
currentFrame.vmDomainFile.SetHostPtr(pDomainFile);
currentFrame.ip = currentElement.ip;
currentFrame.methodDef = currentElement.pFunc->GetMemberDef();
currentFrame.isLastForeignExceptionFrame = currentElement.fIsLastFrameFromForeignStackTrace;
currentFrame.isLastForeignExceptionFrame = (currentElement.flags & STEF_LAST_FRAME_FROM_FOREIGN_STACK_TRACE) != 0;
}
}
}
Expand Down
8 changes: 5 additions & 3 deletions src/coreclr/src/debug/daccess/task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4091,9 +4091,11 @@ ClrDataMethodInstance::GetILOffsetsByAddress(
for (ULONG32 i = 0; i < numMap; i++)
{
if (codeOffset >= map[i].nativeStartOffset &&
(((LONG)map[i].ilOffset == ICorDebugInfo::EPILOG &&
!map[i].nativeEndOffset) ||
codeOffset < map[i].nativeEndOffset))
// Found the entry if it is the epilog or the last entry and nativeEndOffset == 0. For methods that don't
// have a epilog (i.e. IL_Throw is the last instruction) the last map entry has a valid IL offset (not EPILOG)
// and nativeEndOffset == 0.
((((LONG)map[i].ilOffset == ICorDebugInfo::EPILOG || i == (numMap - 1)) && map[i].nativeEndOffset == 0) ||
codeOffset < map[i].nativeEndOffset))
{
hits++;

Expand Down
17 changes: 14 additions & 3 deletions src/coreclr/src/vm/clrex.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,23 @@ class AssemblySpec;
class PEFile;
class PEAssembly;

enum StackTraceElementFlags
{
// Set if this element represents the last frame of the foreign exception stack trace
STEF_LAST_FRAME_FROM_FOREIGN_STACK_TRACE = 0x0001,

// Set if the "ip" field has already been adjusted (decremented)
STEF_IP_ADJUSTED = 0x0002,
};

// This struct is used by SOS in the diagnostic repo.
// See: https://github.com/dotnet/diagnostics/blob/9ff35f13af2f03a68a166cfd53f1a4bb32425f2f/src/SOS/Strike/strike.cpp#L2245
struct StackTraceElement
{
UINT_PTR ip;
UINT_PTR sp;
PTR_MethodDesc pFunc;
// TRUE if this element represents the last frame of the foreign
// exception stack trace.
BOOL fIsLastFrameFromForeignStackTrace;
INT flags; // This is StackTraceElementFlags but it needs to be "int" sized for compatibility with SOS.
mikem8361 marked this conversation as resolved.
Show resolved Hide resolved

bool operator==(StackTraceElement const & rhs) const
{
Expand All @@ -44,6 +53,8 @@ struct StackTraceElement
}
};

// This struct is used by SOS in the diagnostic repo.
// See: https://github.com/dotnet/diagnostics/blob/9ff35f13af2f03a68a166cfd53f1a4bb32425f2f/src/SOS/Strike/strike.cpp#L2669
class StackTraceInfo
{
private:
Expand Down
52 changes: 39 additions & 13 deletions src/coreclr/src/vm/debugdebugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ FCIMPL4(void, DebugStackTrace::GetStackFramesInternal,
// Set the BOOL indicating if the frame represents the last frame from a foreign exception stack trace.
U1 *pIsLastFrameFromForeignExceptionStackTraceU1 = (U1 *)((BOOLARRAYREF)pStackFrameHelper->rgiLastFrameFromForeignExceptionStackTrace)
->GetDirectPointerToNonObjectElements();
pIsLastFrameFromForeignExceptionStackTraceU1 [iNumValidFrames] = (U1) data.pElements[i].fIsLastFrameFromForeignStackTrace;
pIsLastFrameFromForeignExceptionStackTraceU1 [iNumValidFrames] = (U1)(data.pElements[i].flags & STEF_LAST_FRAME_FROM_FOREIGN_STACK_TRACE);
}

MethodDesc *pMethod = data.pElements[i].pFunc;
Expand Down Expand Up @@ -939,7 +939,7 @@ void DebugStackTrace::GetStackFramesHelper(Frame *pStartFrame,

// Do a 2nd pass outside of any locks.
// This will compute IL offsets.
for(INT32 i = 0; i < pData->cElements; i++)
for (INT32 i = 0; i < pData->cElements; i++)
{
pData->pElements[i].InitPass2();
}
Expand Down Expand Up @@ -1026,14 +1026,17 @@ StackWalkAction DebugStackTrace::GetStackFramesCallback(CrawlFrame* pCf, VOID* d
dwNativeOffset = 0;
}

// Pass on to InitPass2 that the IP has already been adjusted (decremented by 1)
INT flags = pCf->IsIPadjusted() ? STEF_IP_ADJUSTED : 0;

pData->pElements[pData->cElements].InitPass1(
dwNativeOffset,
pFunc,
ip);
ip,
flags);

// We'll init the IL offsets outside the TSL lock.


++pData->cElements;

// Since we may be asynchronously walking another thread's stack,
Expand Down Expand Up @@ -1109,7 +1112,7 @@ void DebugStackTrace::GetStackFramesFromException(OBJECTREF * e,
// If we come across any frame representing foreign exception stack trace,
// then set the flag indicating so. This will be used to allocate the
// corresponding array in StackFrameHelper.
if (cur.fIsLastFrameFromForeignStackTrace)
if ((cur.flags & STEF_LAST_FRAME_FROM_FOREIGN_STACK_TRACE) != 0)
{
pData->fDoWeHaveAnyFramesFromForeignStackTrace = TRUE;
}
Expand All @@ -1134,9 +1137,11 @@ void DebugStackTrace::GetStackFramesFromException(OBJECTREF * e,
dwNativeOffset = 0;
}

pData->pElements[i].InitPass1(dwNativeOffset, pMD, (PCODE) cur.ip
, cur.fIsLastFrameFromForeignStackTrace
);
pData->pElements[i].InitPass1(
dwNativeOffset,
pMD,
(PCODE)cur.ip,
cur.flags);
#ifndef DACCESS_COMPILE
pData->pElements[i].InitPass2();
#endif
Expand All @@ -1156,8 +1161,8 @@ void DebugStackTrace::GetStackFramesFromException(OBJECTREF * e,
void DebugStackTrace::DebugStackTraceElement::InitPass1(
DWORD dwNativeOffset,
MethodDesc *pFunc,
PCODE ip
, BOOL fIsLastFrameFromForeignStackTrace /*= FALSE*/
PCODE ip,
INT flags
)
{
LIMITED_METHOD_CONTRACT;
Expand All @@ -1169,7 +1174,7 @@ void DebugStackTrace::DebugStackTraceElement::InitPass1(
this->pFunc = pFunc;
this->dwOffset = dwNativeOffset;
this->ip = ip;
this->fIsLastFrameFromForeignStackTrace = fIsLastFrameFromForeignStackTrace;
this->flags = flags;
}

#ifndef DACCESS_COMPILE
Expand All @@ -1188,14 +1193,35 @@ void DebugStackTrace::DebugStackTraceElement::InitPass2()

_ASSERTE(!ThreadStore::HoldingThreadStore());

bool bRes = false;
bool bRes = false;

#ifdef DEBUGGING_SUPPORTED
// Calculate the IL offset using the debugging services
if ((this->ip != NULL) && g_pDebugInterface)
{
// To get the source line number of the actual code that threw an exception, the dwOffset needs to be
// adjusted in certain cases when calculating the IL offset.
//
// The dwOffset of the stack frame points to either:
//
// 1) The instruction that caused a hardware exception (div by zero, null ref, etc).
// 2) The instruction after the call to an internal runtime function (FCALL like IL_Throw, IL_Rethrow,
// JIT_OverFlow, etc.) that caused a software exception.
// 3) The instruction after the call to a managed function (non-leaf node).
//
// #2 and #3 are the cases that need to adjust dwOffset because they point after the call instruction
// and may point to the next (incorrect) IL instruction/source line. If STEF_IP_ADJUSTED is set,
// IP/dwOffset has already be decremented so don't decrement it again.
//
// When the dwOffset needs to be adjusted it is a lot simpler to decrement instead of trying to figure out
// the beginning of the instruction. It is enough for GetILOffsetFromNative to return the IL offset of the
// instruction throwing the exception.
bool fAdjustOffset = (this->flags & STEF_IP_ADJUSTED) == 0 && this->dwOffset >= STACKWALK_CONTROLPC_ADJUST_OFFSET;
bRes = g_pDebugInterface->GetILOffsetFromNative(
pFunc, (LPCBYTE) this->ip, this->dwOffset, &this->dwILOffset);
pFunc,
(LPCBYTE)this->ip,
fAdjustOffset ? this->dwOffset - STACKWALK_CONTROLPC_ADJUST_OFFSET : this->dwOffset,
&this->dwILOffset);
}

#endif // !DEBUGGING_SUPPORTED
Expand Down
14 changes: 6 additions & 8 deletions src/coreclr/src/vm/debugdebugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,22 +96,20 @@ class DebugStackTrace
private:
#endif // DACCESS_COMPILE
struct DebugStackTraceElement {
DWORD dwOffset; // native offset
DWORD dwOffset; // native offset
DWORD dwILOffset;
MethodDesc *pFunc;
PCODE ip;
// TRUE if this element represents the last frame of the foreign
// exception stack trace.
BOOL fIsLastFrameFromForeignStackTrace;
INT flags; // StackStackElementFlags

// Initialization done under TSL.
// This is used when first collecting the stack frame data.
void InitPass1(
DWORD dwNativeOffset,
MethodDesc *pFunc,
PCODE ip
, BOOL fIsLastFrameFromForeignStackTrace = FALSE
);
PCODE ip,
INT flags // StackStackElementFlags
);

// Initialization done outside the TSL.
// This will init the dwILOffset field (and potentially anything else
Expand All @@ -131,7 +129,7 @@ class DebugStackTrace
DebugStackTraceElement* pElements;
THREADBASEREF TargetThread;
AppDomain *pDomain;
BOOL fDoWeHaveAnyFramesFromForeignStackTrace;
BOOL fDoWeHaveAnyFramesFromForeignStackTrace;


GetStackFramesData() : skip(0),
Expand Down
5 changes: 3 additions & 2 deletions src/coreclr/src/vm/excep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2367,7 +2367,7 @@ void StackTraceInfo::SaveStackTrace(BOOL bAllowAllocMem, OBJECTHANDLE hThrowable
// "numCurrentFrames" can be zero if the user created an EDI using
// an unthrown exception.
StackTraceElement & refLastElementFromForeignStackTrace = gc.stackTrace[numCurrentFrames - 1];
refLastElementFromForeignStackTrace.fIsLastFrameFromForeignStackTrace = TRUE;
refLastElementFromForeignStackTrace.flags |= STEF_LAST_FRAME_FROM_FOREIGN_STACK_TRACE;
}
}

Expand Down Expand Up @@ -3555,14 +3555,15 @@ BOOL StackTraceInfo::AppendElement(BOOL bAllowAllocMem, UINT_PTR currentIP, UINT
// When we are building stack trace as we encounter managed frames during exception dispatch,
// then none of those frames represent a stack trace from a foreign exception (as they represent
// the current exception). Hence, set the corresponding flag to FALSE.
pStackTraceElem->fIsLastFrameFromForeignStackTrace = FALSE;
pStackTraceElem->flags = 0;

// This is a workaround to fix the generation of stack traces from exception objects so that
// they point to the line that actually generated the exception instead of the line
// following.
if (!(pCf->HasFaulted() || pCf->IsIPadjusted()) && pStackTraceElem->ip != 0)
{
pStackTraceElem->ip -= 1;
pStackTraceElem->flags |= STEF_IP_ADJUSTED;
}

++m_dFrameCount;
Expand Down
12 changes: 11 additions & 1 deletion src/coreclr/src/vm/exceptionhandling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1333,7 +1333,12 @@ void ExceptionTracker::InitializeCrawlFrameForExplicitFrame(CrawlFrame* pcfThisF

INDEBUG(memset(pcfThisFrame, 0xCC, sizeof(*pcfThisFrame)));

// Clear various flags
pcfThisFrame->isFrameless = false;
pcfThisFrame->isInterrupted = false;
pcfThisFrame->hasFaulted = false;
pcfThisFrame->isIPadjusted = false;

pcfThisFrame->pFrame = pFrame;
pcfThisFrame->pFunc = pFrame->GetFunction();

Expand Down Expand Up @@ -1417,6 +1422,12 @@ void ExceptionTracker::InitializeCrawlFrame(CrawlFrame* pcfThisFrame, Thread* pT
INDEBUG(memset(pcfThisFrame, 0xCC, sizeof(*pcfThisFrame)));
pcfThisFrame->pRD = pRD;

// Clear various flags
pcfThisFrame->pFunc = NULL;
pcfThisFrame->isInterrupted = false;
pcfThisFrame->hasFaulted = false;
pcfThisFrame->isIPadjusted = false;

#ifdef FEATURE_INTERPRETER
pcfThisFrame->pFrame = NULL;
#endif // FEATURE_INTERPRETER
Expand Down Expand Up @@ -1571,7 +1582,6 @@ void ExceptionTracker::InitializeCrawlFrame(CrawlFrame* pcfThisFrame, Thread* pT
}

pcfThisFrame->pThread = pThread;
pcfThisFrame->hasFaulted = false;

Frame* pTopFrame = pThread->GetFrame();
pcfThisFrame->isIPadjusted = (FRAME_TOP != pTopFrame) && (pTopFrame->GetVTablePtr() != FaultingExceptionFrame::GetMethodFrameVPtr());
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/src/vm/stackwalk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1499,7 +1499,7 @@ void StackFrameIterator::ResetCrawlFrame()
m_crawl.isFirst = true;
m_crawl.isInterrupted = false;
m_crawl.hasFaulted = false;
m_crawl.isIPadjusted = false; // can be removed
m_crawl.isIPadjusted = false;

m_crawl.isNativeMarker = false;
m_crawl.isProfilerDoStackSnapshot = !!(this->m_flags & PROFILER_DO_STACK_SNAPSHOT);
Expand Down
Loading