|
|
@@ -298,147 +298,212 @@ namespace Js
|
|
|
|
|
|
uint32 JavascriptStackWalker::GetByteCodeOffset() const
|
|
|
{
|
|
|
+ uint32 offset = 0;
|
|
|
if (this->IsJavascriptFrame())
|
|
|
{
|
|
|
- if (this->interpreterFrame
|
|
|
-#if ENABLE_NATIVE_CODEGEN
|
|
|
- && this->lastInternalFrameInfo.codeAddress == nullptr
|
|
|
-#endif
|
|
|
- )
|
|
|
+ if (this->interpreterFrame)
|
|
|
{
|
|
|
- uint32 offset = this->interpreterFrame->GetReader()->GetCurrentOffset();
|
|
|
- if (offset == 0)
|
|
|
+ if (this->TryGetByteCodeOffsetFromInterpreterFrame(offset))
|
|
|
{
|
|
|
- // This will be the case when we are broken on the debugger on very first statement (due to async break).
|
|
|
- // Or the interpreter loop can throw OOS on entrance before executing bytecode.
|
|
|
return offset;
|
|
|
}
|
|
|
- else
|
|
|
- {
|
|
|
- // Note : For many cases, we move the m_currentLocation of ByteCodeReader already to next available opcode.
|
|
|
- // This could create problem in binding the exception to proper line offset.
|
|
|
- // Reducing by 1 will make sure the current offset falls under, current executing opcode.
|
|
|
- return offset - 1;
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
#if ENABLE_NATIVE_CODEGEN
|
|
|
- DWORD_PTR pCodeAddr;
|
|
|
- uint loopNum = LoopHeader::NoLoop;
|
|
|
- if (this->lastInternalFrameInfo.codeAddress != nullptr)
|
|
|
+ if (TryGetByteCodeOffsetFromNativeFrame(offset))
|
|
|
{
|
|
|
- if (this->lastInternalFrameInfo.loopBodyFrameType == InternalFrameType_LoopBody)
|
|
|
- {
|
|
|
- AnalysisAssert(this->interpreterFrame);
|
|
|
- loopNum = this->interpreterFrame->GetCurrentLoopNum();
|
|
|
- Assert(loopNum != LoopHeader::NoLoop);
|
|
|
- }
|
|
|
-
|
|
|
- pCodeAddr = (DWORD_PTR)this->lastInternalFrameInfo.codeAddress;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- if (this->IsCurrentPhysicalFrameForLoopBody())
|
|
|
- {
|
|
|
- // Internal frame but codeAddress on lastInternalFrameInfo not set. We must be in an inlined frame in the loop body.
|
|
|
- Assert(this->tempInterpreterFrame);
|
|
|
- loopNum = this->tempInterpreterFrame->GetCurrentLoopNum();
|
|
|
- Assert(loopNum != LoopHeader::NoLoop);
|
|
|
- }
|
|
|
- pCodeAddr = (DWORD_PTR)this->GetCurrentCodeAddr();
|
|
|
+ return offset;
|
|
|
}
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ return offset;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool JavascriptStackWalker::TryGetByteCodeOffsetFromInterpreterFrame(uint32& offset) const
|
|
|
+ {
|
|
|
+#if ENABLE_NATIVE_CODEGEN
|
|
|
+ if (this->lastInternalFrameInfo.codeAddress != nullptr)
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ offset = this->interpreterFrame->GetReader()->GetCurrentOffset();
|
|
|
+ if (offset == 0)
|
|
|
+ {
|
|
|
+ // This will be the case when we are broken on the debugger on very first statement (due to async break).
|
|
|
+ // Or the interpreter loop can throw OOS on entrance before executing bytecode.
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Note : For many cases, we move the m_currentLocation of ByteCodeReader already to next available opcode.
|
|
|
+ // This could create problem in binding the exception to proper line offset.
|
|
|
+ // Reducing by 1 will make sure the current offset falls under, current executing opcode.
|
|
|
+ offset--;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
|
|
|
- // If the current instruction's return address is the beginning of the next statement then we will show error for the next line, which would be completely wrong.
|
|
|
- // The quick fix would be to look the address which is at least lesser than current return address.
|
|
|
+#if ENABLE_NATIVE_CODEGEN
|
|
|
+ bool JavascriptStackWalker::TryGetByteCodeOffsetFromNativeFrame(uint32& offset) const
|
|
|
+ {
|
|
|
+ DWORD_PTR pCodeAddr;
|
|
|
+ if (this->lastInternalFrameInfo.codeAddress != nullptr)
|
|
|
+ {
|
|
|
+ pCodeAddr = (DWORD_PTR)this->lastInternalFrameInfo.codeAddress;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ pCodeAddr = (DWORD_PTR)this->GetCurrentCodeAddr();
|
|
|
+ }
|
|
|
|
|
|
- // Assert to verify at what places this can happen.
|
|
|
- Assert(pCodeAddr);
|
|
|
+ // If the current instruction's return address is the beginning of the next statement then we will show error for the next line, which would be completely wrong.
|
|
|
+ // The quick fix would be to look the address which is at least lesser than current return address.
|
|
|
|
|
|
- if (pCodeAddr)
|
|
|
- {
|
|
|
+ // Assert to verify at what places this can happen.
|
|
|
+ Assert(pCodeAddr);
|
|
|
+
|
|
|
+ if (pCodeAddr)
|
|
|
+ {
|
|
|
#if defined(_M_ARM32_OR_ARM64)
|
|
|
- // Note that DWORD_PTR is not actually a pointer type (!) but is simple unsigned long/__int64 (see BaseTsd.h).
|
|
|
- // Thus, decrement would be by 1 byte and not 4 bytes as in pointer arithmetic. That's exactly what we need.
|
|
|
- // For ARM the 'return address' is always odd and is 'next instr addr' + 1 byte, so to get to the BLX instr, we need to subtract 2 bytes from it.
|
|
|
- AssertMsg(pCodeAddr % 2 == 1, "Got even number for pCodeAddr! It's expected to be return address, which should be odd.");
|
|
|
- pCodeAddr--;
|
|
|
+ // Note that DWORD_PTR is not actually a pointer type (!) but is simple unsigned long/__int64 (see BaseTsd.h).
|
|
|
+ // Thus, decrement would be by 1 byte and not 4 bytes as in pointer arithmetic. That's exactly what we need.
|
|
|
+ // For ARM the 'return address' is always odd and is 'next instr addr' + 1 byte, so to get to the BLX instr, we need to subtract 2 bytes from it.
|
|
|
+ AssertMsg(pCodeAddr % 2 == 1, "Got even number for pCodeAddr! It's expected to be return address, which should be odd.");
|
|
|
+ pCodeAddr--;
|
|
|
#endif
|
|
|
- pCodeAddr--;
|
|
|
+ pCodeAddr--;
|
|
|
+ }
|
|
|
+
|
|
|
+ uint loopNum = GetLoopNumber();
|
|
|
+
|
|
|
+ JavascriptFunction *function = nullptr;
|
|
|
+ FunctionBody *inlinee = nullptr;
|
|
|
+
|
|
|
+ if (this->interpreterFrame == nullptr) //Inlining is disabled in Jit Loopbody. Don't attempt to get the statement map from the inlined frame.
|
|
|
+ {
|
|
|
+ function = this->GetCurrentFunctionFromPhysicalFrame();
|
|
|
+
|
|
|
+ // If there are inlined frames on the stack, we have to be able to return the byte code offsets of those inlined calls
|
|
|
+ // from their respective callers. But, we can use the current native address as IP for only the topmost inlined frame.
|
|
|
+ // TryGetByteCodeOffsetOfInlinee takes care of these conditions and sets up the offset of an inlinee in 'offset', if the
|
|
|
+ // current inlinee frame is not the topmost of the inlinee frames.
|
|
|
+ if (HasInlinedFramesOnStack() && TryGetByteCodeOffsetOfInlinee(function, loopNum, pCodeAddr, &inlinee, offset))
|
|
|
+ {
|
|
|
+ return true;
|
|
|
}
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //Get the function from the interpreterFrame in jit loop body case
|
|
|
+ //This is exactly same as this->GetCurrentFunctionFromPhysicalFrame() if the interpreterFrame is not
|
|
|
+ //called from bailout path.
|
|
|
+ Assert(this->lastInternalFrameInfo.codeAddress);
|
|
|
+ function = this->interpreterFrame->GetJavascriptFunction();
|
|
|
+ }
|
|
|
|
|
|
- JavascriptFunction *function = nullptr;
|
|
|
- FunctionBody *inlinee = nullptr;
|
|
|
- StatementData data;
|
|
|
+ StatementData data;
|
|
|
+ if (function->GetFunctionBody() && function->GetFunctionBody()->GetMatchingStatementMapFromNativeAddress(pCodeAddr, data, loopNum, inlinee))
|
|
|
+ {
|
|
|
+ offset = data.bytecodeBegin;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- if (this->interpreterFrame == nullptr) //Inlining is disabled in Jit Loopbody. Don't attempt to get the statement map from the inlined frame.
|
|
|
+ uint JavascriptStackWalker::GetLoopNumber() const
|
|
|
+ {
|
|
|
+ uint loopNum = LoopHeader::NoLoop;
|
|
|
+ if (this->lastInternalFrameInfo.codeAddress != nullptr)
|
|
|
+ {
|
|
|
+ if (this->lastInternalFrameInfo.frameType == InternalFrameType_LoopBody)
|
|
|
{
|
|
|
- // For inlined frames, translation from native offset -> source code happens in two steps.
|
|
|
- // The native offset is first translated into a statement index using the physical frame's
|
|
|
- // source context info. This statement index is then looked up in the *inlinee*'s source
|
|
|
- // context info to get the bytecode offset.
|
|
|
- //
|
|
|
- // For all inlined frames contained within a physical frame we have only one offset == (IP - entry).
|
|
|
- // Since we can't use that to get the other inlined callers' IPs, we save the IP of all inlined
|
|
|
- // callers in its "callinfo" (See InlineeCallInfo). The top most inlined frame uses the IP
|
|
|
- // of the physical frame. All other inlined frames use the preceding inlined frame's offset.
|
|
|
- //
|
|
|
- function = this->GetCurrentFunctionFromPhysicalFrame();
|
|
|
- inlinee = inlinedFramesBeingWalked ? inlinedFrameWalker.GetFunctionObject()->GetFunctionBody() : nullptr;
|
|
|
- InlinedFrameWalker tmpFrameWalker;
|
|
|
- if (inlinedFramesBeingWalked)
|
|
|
- {
|
|
|
- // Inlined frames are being walked right now. The top most frame is where the IP is.
|
|
|
- if (!inlinedFrameWalker.IsTopMostFrame())
|
|
|
- {
|
|
|
- if (function->GetFunctionBody()->GetMatchingStatementMapFromNativeOffset(pCodeAddr,
|
|
|
- inlinedFrameWalker.GetCurrentInlineeOffset(),
|
|
|
- data,
|
|
|
- loopNum,
|
|
|
- inlinee))
|
|
|
- {
|
|
|
- return data.bytecodeBegin;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- else if (ScriptFunction::Is(function) &&
|
|
|
- InlinedFrameWalker::FromPhysicalFrame(tmpFrameWalker, currentFrame, ScriptFunction::FromVar(function), previousInterpreterFrameIsFromBailout, loopNum, this))
|
|
|
- {
|
|
|
- // Inlined frames are not being walked right now. However, if there
|
|
|
- // are inlined frames on the stack the InlineeCallInfo of the first inlined frame
|
|
|
- // has the native offset of the current physical frame.
|
|
|
- Assert(!inlinee);
|
|
|
- uint32 inlineeOffset = tmpFrameWalker.GetBottomMostInlineeOffset();
|
|
|
- tmpFrameWalker.Close();
|
|
|
-
|
|
|
- if (this->GetCurrentFunctionFromPhysicalFrame()->GetFunctionBody()->GetMatchingStatementMapFromNativeOffset(pCodeAddr,
|
|
|
- inlineeOffset,
|
|
|
- data,
|
|
|
- loopNum,
|
|
|
- inlinee))
|
|
|
- {
|
|
|
- return data.bytecodeBegin;
|
|
|
- }
|
|
|
- }
|
|
|
+ AnalysisAssert(this->interpreterFrame);
|
|
|
+ loopNum = this->interpreterFrame->GetCurrentLoopNum();
|
|
|
+ Assert(loopNum != LoopHeader::NoLoop);
|
|
|
}
|
|
|
- else
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (this->IsCurrentPhysicalFrameForLoopBody())
|
|
|
{
|
|
|
- //Get the function from the interpreterFrame in jit loop body case
|
|
|
- //This is exactly same as this->GetCurrentFunctionFromPhysicalFrame() if the interpreterFrame is not
|
|
|
- //called from bailout path.
|
|
|
- Assert(this->lastInternalFrameInfo.codeAddress);
|
|
|
- function = this->interpreterFrame->GetJavascriptFunction();
|
|
|
+ // Internal frame but codeAddress on lastInternalFrameInfo not set. We must be in an inlined frame in the loop body.
|
|
|
+ Assert(this->tempInterpreterFrame);
|
|
|
+ loopNum = this->tempInterpreterFrame->GetCurrentLoopNum();
|
|
|
+ Assert(loopNum != LoopHeader::NoLoop);
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ return loopNum;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool JavascriptStackWalker::TryGetByteCodeOffsetOfInlinee(Js::JavascriptFunction* function, uint loopNum, DWORD_PTR pCodeAddr, Js::FunctionBody** inlinee, uint32& offset) const
|
|
|
+ {
|
|
|
+ // For inlined frames, translation from native offset -> source code happens in two steps.
|
|
|
+ // The native offset is first translated into a statement index using the physical frame's
|
|
|
+ // source context info. This statement index is then looked up in the *inlinee*'s source
|
|
|
+ // context info to get the bytecode offset.
|
|
|
+ //
|
|
|
+ // For all inlined frames contained within a physical frame we have only one offset == (IP - entry).
|
|
|
+ // Since we can't use that to get the other inlined callers' IPs, we save the IP of all inlined
|
|
|
+ // callers in their "callinfo" (See InlineeCallInfo). The top most inlined frame uses the IP
|
|
|
+ // of the physical frame. All other inlined frames use the InlineeStartOffset stored in their call info
|
|
|
+ // to calculate the byte code offset of the callsite of the inlinee they called.
|
|
|
+
|
|
|
+ StatementData data;
|
|
|
+ uint32 inlineeOffset = 0;
|
|
|
+ *inlinee = InlinedFramesBeingWalked() ? inlinedFrameWalker.GetFunctionObject()->GetFunctionBody() : nullptr;
|
|
|
|
|
|
- if (function->GetFunctionBody() && function->GetFunctionBody()->GetMatchingStatementMapFromNativeAddress(pCodeAddr, data, loopNum, inlinee))
|
|
|
+ InlinedFrameWalker tmpFrameWalker;
|
|
|
+ if (InlinedFramesBeingWalked())
|
|
|
+ {
|
|
|
+ // Inlined frames are being walked right now. The top most frame is where the IP is.
|
|
|
+ if (!inlinedFrameWalker.IsTopMostFrame())
|
|
|
{
|
|
|
- return data.bytecodeBegin;
|
|
|
+ inlineeOffset = inlinedFrameWalker.GetCurrentInlineeOffset();
|
|
|
}
|
|
|
-#endif
|
|
|
+ }
|
|
|
+ else if (ScriptFunction::Is(function) && HasInlinedFramesOnStack())
|
|
|
+ {
|
|
|
+ // Inlined frames are not being walked right now. However, if there
|
|
|
+ // are inlined frames on the stack the InlineeCallInfo of the first inlined frame
|
|
|
+ // has the native offset of the current physical frame.
|
|
|
+ Assert(!*inlinee);
|
|
|
+ InlinedFrameWalker::FromPhysicalFrame(tmpFrameWalker, currentFrame, ScriptFunction::FromVar(function), previousInterpreterFrameIsFromBailout, loopNum, this);
|
|
|
+ inlineeOffset = tmpFrameWalker.GetBottomMostInlineeOffset();
|
|
|
+ tmpFrameWalker.Close();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (inlineeOffset != 0 &&
|
|
|
+ this->GetCurrentFunctionFromPhysicalFrame()->GetFunctionBody()->GetMatchingStatementMapFromNativeOffset(pCodeAddr, inlineeOffset, data, loopNum, *inlinee))
|
|
|
+ {
|
|
|
+ offset = data.bytecodeBegin;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool JavascriptStackWalker::InlinedFramesBeingWalked() const
|
|
|
+ {
|
|
|
+ if (lastInternalFrameInfo.codeAddress)
|
|
|
+ {
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
- return 0;
|
|
|
+ return inlinedFramesBeingWalked;
|
|
|
}
|
|
|
|
|
|
+ bool JavascriptStackWalker::HasInlinedFramesOnStack() const
|
|
|
+ {
|
|
|
+ if (lastInternalFrameInfo.codeAddress)
|
|
|
+ {
|
|
|
+ return lastInternalFrameInfo.hasInlinedFramesOnStack;
|
|
|
+ }
|
|
|
+
|
|
|
+ return this->hasInlinedFramesOnStack;
|
|
|
+ }
|
|
|
+#endif
|
|
|
|
|
|
bool JavascriptStackWalker::GetSourcePosition(const WCHAR** sourceFileName, ULONG* line, LONG* column)
|
|
|
{
|
|
|
@@ -471,59 +536,93 @@ namespace Js
|
|
|
// In case we have a cross site thunk, update the script context
|
|
|
Js::JavascriptFunction *function = this->GetCurrentFunction();
|
|
|
|
|
|
- // We might've bailed out of an inlinee, so check if there were any inlinees.
|
|
|
- if (this->interpreterFrame && (this->interpreterFrame->GetFlags() & InterpreterStackFrameFlags_FromBailOut))
|
|
|
+#if ENABLE_NATIVE_CODEGEN
|
|
|
+ bool isCurrentPhysicalFrameForLoopBody = this->IsCurrentPhysicalFrameForLoopBody();
|
|
|
+#endif
|
|
|
+ if (this->interpreterFrame)
|
|
|
{
|
|
|
- previousInterpreterFrameIsFromBailout = true;
|
|
|
#if ENABLE_NATIVE_CODEGEN
|
|
|
- bool isCurrentPhysicalFrameForLoopBody = this->IsCurrentPhysicalFrameForLoopBody();
|
|
|
- Assert(!inlinedFramesBeingWalked);
|
|
|
- if (includeInlineFrames)
|
|
|
+ if (lastInternalFrameInfo.codeAddress != nullptr)
|
|
|
{
|
|
|
- int loopNum = -1;
|
|
|
- if (isCurrentPhysicalFrameForLoopBody)
|
|
|
- {
|
|
|
- loopNum = this->tempInterpreterFrame->GetCurrentLoopNum();
|
|
|
- }
|
|
|
+ this->previousInterpreterFrameIsForLoopBody = true;
|
|
|
+ }
|
|
|
+ else
|
|
|
+#endif
|
|
|
+ {
|
|
|
+ this->previousInterpreterFrameIsForLoopBody = false;
|
|
|
+ }
|
|
|
|
|
|
- bool inlinedFramesOnStack = InlinedFrameWalker::FromPhysicalFrame(inlinedFrameWalker, currentFrame,
|
|
|
- ScriptFunction::FromVar(function), true /*fromBailout*/, loopNum, this);
|
|
|
- if (inlinedFramesOnStack)
|
|
|
+ // We might've bailed out of an inlinee, so check if there were any inlinees.
|
|
|
+ if (this->interpreterFrame->GetFlags() & InterpreterStackFrameFlags_FromBailOut)
|
|
|
+ {
|
|
|
+ previousInterpreterFrameIsFromBailout = true;
|
|
|
+
|
|
|
+#if ENABLE_NATIVE_CODEGEN
|
|
|
+ Assert(!inlinedFramesBeingWalked);
|
|
|
+ if (includeInlineFrames)
|
|
|
{
|
|
|
- inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
|
|
|
- Assert(inlinedFramesBeingWalked);
|
|
|
- Assert(StackScriptFunction::GetCurrentFunctionObject(this->interpreterFrame->GetJavascriptFunction()) == inlinedFrameWalker.GetFunctionObject());
|
|
|
- // We're now back in the state where currentFrame == physical frame of the inliner, but
|
|
|
- // since interpreterFrame != null, we'll pick values from the interpreterFrame (the bailout
|
|
|
- // frame of the inliner). Set a flag to tell the stack walker that it needs to start from the
|
|
|
- // inlinee frames on the stack when Walk() is called.
|
|
|
+ int loopNum = -1;
|
|
|
+ if (isCurrentPhysicalFrameForLoopBody)
|
|
|
+ {
|
|
|
+ loopNum = this->tempInterpreterFrame->GetCurrentLoopNum();
|
|
|
+ }
|
|
|
+
|
|
|
+ bool hasInlinedFramesOnStack = InlinedFrameWalker::FromPhysicalFrame(inlinedFrameWalker, currentFrame,
|
|
|
+ ScriptFunction::FromVar(function), true /*fromBailout*/, loopNum, this);
|
|
|
+ if (hasInlinedFramesOnStack)
|
|
|
+ {
|
|
|
+ // We're now back in the state where currentFrame == physical frame of the inliner, but
|
|
|
+ // since interpreterFrame != null, we'll pick values from the interpreterFrame (the bailout
|
|
|
+ // frame of the inliner). Set a flag to tell the stack walker that it needs to start from the
|
|
|
+ // inlinee frames on the stack when Walk() is called.
|
|
|
+ this->inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
|
|
|
+ this->hasInlinedFramesOnStack = hasInlinedFramesOnStack;
|
|
|
+ Assert(inlinedFramesBeingWalked);
|
|
|
+ Assert(StackScriptFunction::GetCurrentFunctionObject(this->interpreterFrame->GetJavascriptFunction()) == inlinedFrameWalker.GetFunctionObject());
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Assert(!isCurrentPhysicalFrameForLoopBody);
|
|
|
+ }
|
|
|
}
|
|
|
- else
|
|
|
+ else if (isCurrentPhysicalFrameForLoopBody)
|
|
|
{
|
|
|
- Assert(!isCurrentPhysicalFrameForLoopBody);
|
|
|
+ // Getting here is only possible when the current interpreterFrame is for a function which
|
|
|
+ // encountered a bailout after getting inlined in a jitted loop body. If we are not including
|
|
|
+ // inlined frames in the stack walk, we need to set the codeAddress on lastInternalFrameInfo,
|
|
|
+ // which would have otherwise been set upon closing the inlinedFrameWalker, now.
|
|
|
+ // Note that we already have an assert in CheckJavascriptFrame to ensure this.
|
|
|
+ SetCachedInternalFrameInfo(InternalFrameType_LoopBody, false /*hasInlinedFramesOnStack*/);
|
|
|
}
|
|
|
+#else
|
|
|
+ // How did we bail out when JIT was disabled?
|
|
|
+ Assert(false);
|
|
|
+#endif
|
|
|
}
|
|
|
- else if (isCurrentPhysicalFrameForLoopBody)
|
|
|
+ else
|
|
|
{
|
|
|
- // Getting here is only possible when the current interpreterFrame is for a function which
|
|
|
- // encountered a bailout after getting inlined in a jitted loop body. If we are not including
|
|
|
- // inlined frames in the stack walk, we need to set the codeAddress on lastInternalFrameInfo,
|
|
|
- // which would have otherwise been set upon closing the inlinedFrameWalker, now.
|
|
|
- // Note that we already have an assert in CheckJavascriptFrame to ensure this.
|
|
|
- SetCachedInternalFrameInfo(InternalFrameType_LoopBody, InternalFrameType_LoopBody);
|
|
|
+ Assert(StackScriptFunction::GetCurrentFunctionObject(this->interpreterFrame->GetJavascriptFunction()) == function);
|
|
|
+ previousInterpreterFrameIsFromBailout = false;
|
|
|
}
|
|
|
-#else
|
|
|
- // How did we bail out when JIT was disabled?
|
|
|
- Assert(false);
|
|
|
-#endif
|
|
|
}
|
|
|
- else
|
|
|
+ else if (!this->isNativeLibraryFrame)
|
|
|
{
|
|
|
- Assert(this->interpreterFrame == nullptr || StackScriptFunction::GetCurrentFunctionObject(this->interpreterFrame->GetJavascriptFunction()) == function);
|
|
|
- if (this->interpreterFrame)
|
|
|
+#if ENABLE_NATIVE_CODEGEN
|
|
|
+ Assert(!HasInlinedFramesOnStack() || (includeInlineFrames && isCurrentPhysicalFrameForLoopBody));
|
|
|
+
|
|
|
+ if (!HasInlinedFramesOnStack() && includeInlineFrames)
|
|
|
{
|
|
|
- previousInterpreterFrameIsFromBailout = false;
|
|
|
+ // Check whether there are inlined frames nested in this native frame. The corresponding check for
|
|
|
+ // a jitted loop body frame should have been done in CheckJavascriptFrame
|
|
|
+ Assert(lastInternalFrameInfo.codeAddress == nullptr);
|
|
|
+ if (InlinedFrameWalker::FromPhysicalFrame(inlinedFrameWalker, currentFrame, ScriptFunction::FromVar(function)))
|
|
|
+ {
|
|
|
+ this->inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
|
|
|
+ this->hasInlinedFramesOnStack = true;
|
|
|
+ Assert(inlinedFramesBeingWalked);
|
|
|
+ }
|
|
|
}
|
|
|
+#endif
|
|
|
}
|
|
|
this->scriptContext = function->GetScriptContext();
|
|
|
return function;
|
|
|
@@ -539,7 +638,7 @@ namespace Js
|
|
|
__declspec(noinline)
|
|
|
JavascriptStackWalker::JavascriptStackWalker(ScriptContext * scriptContext, bool useEERContext, PVOID returnAddress, bool _forceFullWalk /*=false*/) :
|
|
|
inlinedFrameCallInfo(CallFlags_None, 0), shouldDetectPartiallyInitializedInterpreterFrame(true), forceFullWalk(_forceFullWalk),
|
|
|
- previousInterpreterFrameIsFromBailout(false), ehFramesBeingWalkedFromBailout(false)
|
|
|
+ previousInterpreterFrameIsFromBailout(false), previousInterpreterFrameIsForLoopBody(false), hasInlinedFramesOnStack(false)
|
|
|
{
|
|
|
if (scriptContext == NULL)
|
|
|
{
|
|
|
@@ -590,14 +689,14 @@ namespace Js
|
|
|
this->interpreterFrame = NULL;
|
|
|
|
|
|
#if ENABLE_NATIVE_CODEGEN
|
|
|
+ if (lastInternalFrameInfo.codeAddress != nullptr && this->previousInterpreterFrameIsForLoopBody)
|
|
|
+ {
|
|
|
+ ClearCachedInternalFrameInfo();
|
|
|
+ }
|
|
|
+
|
|
|
if (inlinedFramesBeingWalked)
|
|
|
{
|
|
|
Assert(includeInlineFrames);
|
|
|
- if (this->lastInternalFrameInfo.frameConsumed)
|
|
|
- {
|
|
|
- ClearCachedInternalFrameInfo();
|
|
|
- }
|
|
|
-
|
|
|
inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
|
|
|
if (!inlinedFramesBeingWalked)
|
|
|
{
|
|
|
@@ -606,7 +705,7 @@ namespace Js
|
|
|
{
|
|
|
// Done walking inlined frames in a loop body, cache the native code address now
|
|
|
// in order to skip the loop body frame.
|
|
|
- this->SetCachedInternalFrameInfo(InternalFrameType_LoopBody, InternalFrameType_LoopBody);
|
|
|
+ this->SetCachedInternalFrameInfo(InternalFrameType_LoopBody, true /*hasInlinedFramesOnStack*/);
|
|
|
isJavascriptFrame = false;
|
|
|
}
|
|
|
}
|
|
|
@@ -614,7 +713,7 @@ namespace Js
|
|
|
return true;
|
|
|
}
|
|
|
#endif
|
|
|
-
|
|
|
+ this->hasInlinedFramesOnStack = false;
|
|
|
if (this->isInitialFrame)
|
|
|
{
|
|
|
this->isInitialFrame = false; // Only walk initial frame once
|
|
|
@@ -769,24 +868,19 @@ namespace Js
|
|
|
|
|
|
bool JavascriptStackWalker::CheckJavascriptFrame(bool includeInlineFrames)
|
|
|
{
|
|
|
-#if ENABLE_NATIVE_CODEGEN
|
|
|
- if (this->lastInternalFrameInfo.frameConsumed)
|
|
|
- {
|
|
|
- ClearCachedInternalFrameInfo();
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
this->isNativeLibraryFrame = false; // Clear previous result
|
|
|
|
|
|
void * codeAddr = this->currentFrame.GetInstructionPointer();
|
|
|
if (this->tempInterpreterFrame && codeAddr == this->tempInterpreterFrame->GetReturnAddress())
|
|
|
{
|
|
|
+ bool isBailoutInterpreter = (this->tempInterpreterFrame->GetFlags() & Js::InterpreterStackFrameFlags_FromBailOut) != 0;
|
|
|
+
|
|
|
// We need to skip over the first interpreter frame on the stack if it is the partially initialized frame
|
|
|
// otherwise it is a real frame and we should continue.
|
|
|
// For fully initialized frames (PushPopHelper was called) the thunk stack addr is equal or below addressOfReturnAddress
|
|
|
// as the latter one is obtained in InterpreterStackFrame::InterpreterThunk called by the thunk.
|
|
|
bool isPartiallyInitializedFrame = this->shouldDetectPartiallyInitializedInterpreterFrame &&
|
|
|
- this->currentFrame.GetAddressOfReturnAddress(false /*isCurrentContextNative*/, false /*shouldCheckForNativeAddr*/) < this->tempInterpreterFrame->GetAddressOfReturnAddress();
|
|
|
+ this->currentFrame.GetAddressOfReturnAddress(isBailoutInterpreter /*isCurrentContextNative*/, false /*shouldCheckForNativeAddr*/) < this->tempInterpreterFrame->GetAddressOfReturnAddress();
|
|
|
this->shouldDetectPartiallyInitializedInterpreterFrame = false;
|
|
|
|
|
|
if (isPartiallyInitializedFrame)
|
|
|
@@ -794,20 +888,13 @@ namespace Js
|
|
|
return false; // Skip it.
|
|
|
}
|
|
|
|
|
|
- void ** argv = this->currentFrame.GetArgv(false /*isCurrentContextNative*/, false /*shouldCheckForNativeAddr*/);
|
|
|
+ void ** argv = this->currentFrame.GetArgv(isBailoutInterpreter /*isCurrentContextNative*/, false /*shouldCheckForNativeAddr*/);
|
|
|
if (argv == nullptr)
|
|
|
{
|
|
|
// NOTE: When we switch to walking the stack ourselves and skip non engine frames, this should never happen.
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-#if defined(_M_AMD64)
|
|
|
- if (argv[JavascriptFunctionArgIndex_Function] == amd64_ReturnFromCallWithFakeFrame)
|
|
|
- {
|
|
|
- this->ehFramesBeingWalkedFromBailout = true;
|
|
|
- return false;
|
|
|
- }
|
|
|
-#endif
|
|
|
this->interpreterFrame = this->tempInterpreterFrame;
|
|
|
|
|
|
this->tempInterpreterFrame = this->interpreterFrame->GetPreviousFrame();
|
|
|
@@ -826,16 +913,6 @@ namespace Js
|
|
|
tmpFrameWalker.Close();
|
|
|
}
|
|
|
#endif //DBG
|
|
|
-
|
|
|
- if (!this->interpreterFrame->IsCurrentLoopNativeAddr(this->lastInternalFrameInfo.codeAddress))
|
|
|
- {
|
|
|
- ClearCachedInternalFrameInfo();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Assert(this->lastInternalFrameInfo.codeAddress);
|
|
|
- this->lastInternalFrameInfo.frameConsumed = true;
|
|
|
- }
|
|
|
#endif //ENABLE_NATIVE_CODEGEN
|
|
|
|
|
|
return true;
|
|
|
@@ -867,18 +944,6 @@ namespace Js
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-#if defined(_M_AMD64)
|
|
|
- if (argv[JavascriptFunctionArgIndex_Function] == amd64_ReturnFromCallWithFakeFrame)
|
|
|
- {
|
|
|
- // There could be nested internal frames in the case of try...catch..finally
|
|
|
- // let's not set the last internal frame address if it has already been set.
|
|
|
- if(!this->lastInternalFrameInfo.codeAddress && !this->ehFramesBeingWalkedFromBailout)
|
|
|
- {
|
|
|
- SetCachedInternalFrameInfo(InternalFrameType_EhFrame, InternalFrameType_None);
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-#endif
|
|
|
ScriptFunction* funcObj = Js::ScriptFunction::FromVar(argv[JavascriptFunctionArgIndex_Function]);
|
|
|
if (funcObj->GetFunctionBody()->GetIsAsmjsMode())
|
|
|
{
|
|
|
@@ -893,45 +958,16 @@ namespace Js
|
|
|
false /*fromBailout*/, this->tempInterpreterFrame->GetCurrentLoopNum(), this))
|
|
|
{
|
|
|
// Found inlined frames in a jitted loop body. We dont want to skip the inlined frames; walk all of them before setting codeAddress on lastInternalFrameInfo.
|
|
|
- inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
|
|
|
+ this->inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
|
|
|
+ this->hasInlinedFramesOnStack = true;
|
|
|
Assert(inlinedFramesBeingWalked);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- SetCachedInternalFrameInfo(InternalFrameType_LoopBody, InternalFrameType_LoopBody);
|
|
|
+ SetCachedInternalFrameInfo(InternalFrameType_LoopBody, false /*hasInlinedFramesOnStack*/);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- if (this->lastInternalFrameInfo.codeAddress)
|
|
|
- {
|
|
|
- this->lastInternalFrameInfo.frameConsumed = true;
|
|
|
- }
|
|
|
-
|
|
|
- if (includeInlineFrames &&
|
|
|
- InlinedFrameWalker::FromPhysicalFrame(inlinedFrameWalker, currentFrame, Js::ScriptFunction::FromVar(argv[JavascriptFunctionArgIndex_Function])))
|
|
|
- {
|
|
|
- inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
|
|
|
- Assert(inlinedFramesBeingWalked);
|
|
|
- }
|
|
|
-
|
|
|
- if (this->ehFramesBeingWalkedFromBailout)
|
|
|
- {
|
|
|
- AnalysisAssert(this->tempInterpreterFrame != nullptr);
|
|
|
- this->interpreterFrame = this->tempInterpreterFrame;
|
|
|
- this->tempInterpreterFrame = this->tempInterpreterFrame->GetPreviousFrame();
|
|
|
-
|
|
|
- if (!this->interpreterFrame->IsCurrentLoopNativeAddr(this->lastInternalFrameInfo.codeAddress))
|
|
|
- {
|
|
|
- ClearCachedInternalFrameInfo();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Assert(this->lastInternalFrameInfo.codeAddress);
|
|
|
- this->lastInternalFrameInfo.frameConsumed = true;
|
|
|
- }
|
|
|
- this->ehFramesBeingWalkedFromBailout = false;
|
|
|
- }
|
|
|
-
|
|
|
return true;
|
|
|
}
|
|
|
#endif
|
|
|
@@ -1035,13 +1071,12 @@ namespace Js
|
|
|
this->lastInternalFrameInfo.Clear();
|
|
|
}
|
|
|
|
|
|
- void JavascriptStackWalker::SetCachedInternalFrameInfo(InternalFrameType frameType, InternalFrameType loopBodyFrameType)
|
|
|
+ void JavascriptStackWalker::SetCachedInternalFrameInfo(InternalFrameType frameType, bool hasInlinedFramesOnStack)
|
|
|
{
|
|
|
if (!this->lastInternalFrameInfo.codeAddress)
|
|
|
{
|
|
|
- this->lastInternalFrameInfo.Set(this->GetCurrentCodeAddr(), this->currentFrame.GetFrame(), this->currentFrame.GetStackCheckCodeHeight(), frameType, loopBodyFrameType);
|
|
|
+ this->lastInternalFrameInfo.Set(this->GetCurrentCodeAddr(), this->currentFrame.GetFrame(), this->currentFrame.GetStackCheckCodeHeight(), frameType, hasInlinedFramesOnStack);
|
|
|
}
|
|
|
- this->lastInternalFrameInfo.loopBodyFrameType = loopBodyFrameType;
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
@@ -1391,7 +1426,7 @@ namespace Js
|
|
|
return inlinedFrame;
|
|
|
}
|
|
|
|
|
|
- void InternalFrameInfo::Set(void *codeAddress, void *framePointer, size_t stackCheckCodeHeight, InternalFrameType frameType, InternalFrameType loopBodyFrameType)
|
|
|
+ void InternalFrameInfo::Set(void *codeAddress, void *framePointer, size_t stackCheckCodeHeight, InternalFrameType frameType, bool hasInlinedFramesOnStack)
|
|
|
{
|
|
|
// We skip a jitted loop body's native frame when walking the stack and refer to the loop body's interpreter frame to get the function.
|
|
|
// However, if the loop body has inlinees, to retrieve inlinee frames we need to cache some info about the loop body's native frame.
|
|
|
@@ -1399,8 +1434,7 @@ namespace Js
|
|
|
this->framePointer = framePointer;
|
|
|
this->stackCheckCodeHeight = stackCheckCodeHeight;
|
|
|
this->frameType = frameType;
|
|
|
- this->loopBodyFrameType = loopBodyFrameType;
|
|
|
- this->frameConsumed = false;
|
|
|
+ this->hasInlinedFramesOnStack = hasInlinedFramesOnStack;
|
|
|
}
|
|
|
|
|
|
void InternalFrameInfo::Clear()
|
|
|
@@ -1409,8 +1443,7 @@ namespace Js
|
|
|
this->framePointer = nullptr;
|
|
|
this->stackCheckCodeHeight = (uint)-1;
|
|
|
this->frameType = InternalFrameType_None;
|
|
|
- this->loopBodyFrameType = InternalFrameType_None;
|
|
|
- this->frameConsumed = false;
|
|
|
+ this->hasInlinedFramesOnStack = false;
|
|
|
}
|
|
|
#endif
|
|
|
|