1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-11 18:20:26 +09:00
Satori/src/coreclr/debug/daccess/stack.cpp
Aaron Robinson a429b0ca4f
Events for IL methods without IL headers (#90826)
* Events for IL methods without IL headers

Dynamically generated methods like UnsafeAccessor
functions are marked as IL, but don't contain an IL
header. The lack of header is an indication the IL must
be generated at runtime.

Co-authored-by: Tlakaelel Axayakatl Ceja <tlakaelel.ceja@microsoft.com>
2023-08-19 08:03:03 -07:00

1439 lines
34 KiB
C++

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
//*****************************************************************************
// File: stack.cpp
//
//
// CLRData stack walking.
//
//*****************************************************************************
#include "stdafx.h"
//----------------------------------------------------------------------------
//
// ClrDataStackWalk.
//
//----------------------------------------------------------------------------
ClrDataStackWalk::ClrDataStackWalk(ClrDataAccess* dac,
Thread* thread,
ULONG32 flags)
{
m_dac = dac;
m_dac->AddRef();
m_instanceAge = m_dac->m_instanceAge;
m_thread = thread;
m_walkFlags = flags;
m_refs = 1;
m_stackPrev = 0;
INDEBUG( m_framesUnwound = 0; )
}
ClrDataStackWalk::~ClrDataStackWalk(void)
{
m_dac->Release();
}
STDMETHODIMP
ClrDataStackWalk::QueryInterface(THIS_
IN REFIID interfaceId,
OUT PVOID* iface)
{
if (IsEqualIID(interfaceId, IID_IUnknown) ||
IsEqualIID(interfaceId, __uuidof(IXCLRDataStackWalk)))
{
AddRef();
*iface = static_cast<IUnknown*>
(static_cast<IXCLRDataStackWalk*>(this));
return S_OK;
}
else
{
*iface = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG)
ClrDataStackWalk::AddRef(THIS)
{
return InterlockedIncrement(&m_refs);
}
STDMETHODIMP_(ULONG)
ClrDataStackWalk::Release(THIS)
{
SUPPORTS_DAC_HOST_ONLY;
LONG newRefs = InterlockedDecrement(&m_refs);
if (newRefs == 0)
{
delete this;
}
return newRefs;
}
HRESULT STDMETHODCALLTYPE
ClrDataStackWalk::GetContext(
/* [in] */ ULONG32 contextFlags,
/* [in] */ ULONG32 contextBufSize,
/* [out] */ ULONG32 *contextSize,
/* [size_is][out] */ BYTE contextBuf[ ])
{
HRESULT status;
if (contextSize)
{
*contextSize = ContextSizeForFlags(contextFlags);
}
if (!CheckContextSizeForFlags(contextBufSize, contextFlags))
{
return E_INVALIDARG;
}
DAC_ENTER_SUB(m_dac);
EX_TRY
{
if (!m_frameIter.IsValid())
{
status = S_FALSE;
}
else
{
T_CONTEXT tmpContext = m_context;
UpdateContextFromRegDisp(&m_regDisp, &tmpContext);
CopyMemory(contextBuf, &tmpContext, contextBufSize);
status = S_OK;
}
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataStackWalk::SetContext(
/* [in] */ ULONG32 contextSize,
/* [size_is][in] */ BYTE context[ ])
{
return SetContext2(m_frameIter.m_crawl.IsActiveFrame() ?
CLRDATA_STACK_SET_CURRENT_CONTEXT :
CLRDATA_STACK_SET_UNWIND_CONTEXT,
contextSize, context);
}
HRESULT STDMETHODCALLTYPE
ClrDataStackWalk::SetContext2(
/* [in] */ ULONG32 flags,
/* [in] */ ULONG32 contextSize,
/* [size_is][in] */ BYTE context[ ])
{
HRESULT status;
if ((flags & ~(CLRDATA_STACK_SET_CURRENT_CONTEXT |
CLRDATA_STACK_SET_UNWIND_CONTEXT)) != 0 ||
!CheckContextSizeForBuffer(contextSize, context))
{
return E_INVALIDARG;
}
DAC_ENTER_SUB(m_dac);
EX_TRY
{
// Copy the context to local state so
// that its lifetime extends beyond this call.
CopyMemory(&m_context, context, contextSize);
m_thread->FillRegDisplay(&m_regDisp, &m_context);
m_frameIter.ResetRegDisp(&m_regDisp, (flags & CLRDATA_STACK_SET_CURRENT_CONTEXT) != 0);
m_stackPrev = (TADDR)GetRegdisplaySP(&m_regDisp);
FilterFrames();
status = S_OK;
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataStackWalk::Next(void)
{
HRESULT status = E_FAIL;
INDEBUG( static const int kFrameToReturnForever = 56; )
DAC_ENTER_SUB(m_dac);
EX_TRY
{
if (!m_frameIter.IsValid())
{
status = S_FALSE;
}
else
#if defined(_DEBUG)
// m_framesUnwound is not incremented unless the special config value is set below in this function.
if (m_framesUnwound < kFrameToReturnForever)
#endif // defined(_DEBUG)
{
// Default the previous stack value.
m_stackPrev = (TADDR)GetRegdisplaySP(&m_regDisp);
StackWalkAction action = m_frameIter.Next();
switch(action)
{
case SWA_CONTINUE:
// We successfully unwound a frame so update
// the previous stack pointer before going into
// filtering to get the amount of stack skipped
// by the filtering.
m_stackPrev = (TADDR)GetRegdisplaySP(&m_regDisp);
FilterFrames();
status = m_frameIter.IsValid() ? S_OK : S_FALSE;
break;
case SWA_ABORT:
status = S_FALSE;
break;
default:
status = E_FAIL;
break;
}
}
#if defined(_DEBUG)
// Test hook: when testing on debug builds, we want an easy way to test that the target
// stack behaves as if it's smashed in a particular way. It would be very difficult to create
// a test that carefully broke the stack in a way that would force the stackwalker to report
// success on the same frame forever, and have that corruption be reliable over time. However, it's
// pretty easy for us to control the number of frames on the stack for tests that use this specific
// internal flag.
if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DumpGeneration_IntentionallyCorruptDataFromTarget))
{
if (m_framesUnwound >= kFrameToReturnForever)
{
status = S_OK;
}
else
{
m_framesUnwound++;
}
}
#endif // defined(_DEBUG)
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataStackWalk::GetStackSizeSkipped(
/* [out] */ ULONG64 *stackSizeSkipped)
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
if (m_stackPrev)
{
*stackSizeSkipped =
(TADDR)GetRegdisplaySP(&m_regDisp) - m_stackPrev;
status = S_OK;
}
else
{
status = S_FALSE;
}
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataStackWalk::GetFrameType(
/* [out] */ CLRDataSimpleFrameType *simpleType,
/* [out] */ CLRDataDetailedFrameType *detailedType)
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
if (m_frameIter.IsValid())
{
RawGetFrameType(simpleType, detailedType);
status = S_OK;
}
else
{
status = S_FALSE;
}
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataStackWalk::GetFrame(
/* [out] */ IXCLRDataFrame **frame)
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
ClrDataFrame* dataFrame = NULL;
if (!m_frameIter.IsValid())
{
status = E_INVALIDARG;
goto Exit;
}
CLRDataSimpleFrameType simpleType;
CLRDataDetailedFrameType detailedType;
RawGetFrameType(&simpleType, &detailedType);
dataFrame =
new (nothrow) ClrDataFrame(m_dac, simpleType, detailedType,
AppDomain::GetCurrentDomain(),
m_frameIter.m_crawl.GetFunction());
if (!dataFrame)
{
status = E_OUTOFMEMORY;
goto Exit;
}
dataFrame->m_context = m_context;
UpdateContextFromRegDisp(&m_regDisp, &dataFrame->m_context);
m_thread->FillRegDisplay(&dataFrame->m_regDisp,
&dataFrame->m_context);
*frame = static_cast<IXCLRDataFrame*>(dataFrame);
status = S_OK;
Exit: ;
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataStackWalk::Request(
/* [in] */ ULONG32 reqCode,
/* [in] */ ULONG32 inBufferSize,
/* [size_is][in] */ BYTE *inBuffer,
/* [in] */ ULONG32 outBufferSize,
/* [size_is][out] */ BYTE *outBuffer)
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
switch(reqCode)
{
case CLRDATA_REQUEST_REVISION:
if (inBufferSize != 0 ||
inBuffer ||
outBufferSize != sizeof(ULONG32))
{
status = E_INVALIDARG;
}
else
{
*(ULONG32*)outBuffer = 1;
status = S_OK;
}
break;
case CLRDATA_STACK_WALK_REQUEST_SET_FIRST_FRAME:
// This code should be removed once the Windows debuggers stop using the old DAC API.
if ((inBufferSize != sizeof(ULONG32)) ||
(outBufferSize != 0))
{
status = E_INVALIDARG;
break;
}
m_frameIter.SetIsFirstFrame(*(ULONG32 UNALIGNED *)inBuffer != 0);
status = S_OK;
break;
case DACSTACKPRIV_REQUEST_FRAME_DATA:
if ((inBufferSize != 0) ||
(inBuffer != NULL) ||
(outBufferSize != sizeof(DacpFrameData)))
{
status = E_INVALIDARG;
break;
}
if (!m_frameIter.IsValid())
{
status = E_INVALIDARG;
break;
}
DacpFrameData* frameData;
frameData = (DacpFrameData*)outBuffer;
frameData->frameAddr =
TO_CDADDR(PTR_HOST_TO_TADDR(m_frameIter.m_crawl.GetFrame()));
status = S_OK;
break;
default:
status = E_INVALIDARG;
break;
}
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT
ClrDataStackWalk::Init(void)
{
if (m_thread->IsUnstarted())
{
return E_FAIL;
}
if (m_thread->GetFilterContext())
{
m_context = *m_thread->GetFilterContext();
}
else
{
DacGetThreadContext(m_thread, &m_context);
}
m_thread->FillRegDisplay(&m_regDisp, &m_context);
m_stackPrev = (TADDR)GetRegdisplaySP(&m_regDisp);
ULONG32 iterFlags = NOTIFY_ON_NO_FRAME_TRANSITIONS;
// If the filter is only allowing method frames
// turn on the appropriate iterator flag.
if ((m_walkFlags & SIMPFRAME_ALL) ==
CLRDATA_SIMPFRAME_MANAGED_METHOD)
{
iterFlags |= FUNCTIONSONLY;
}
m_frameIter.Init(m_thread, NULL, &m_regDisp, iterFlags);
if (m_frameIter.GetFrameState() == StackFrameIterator::SFITER_UNINITIALIZED)
{
return E_FAIL;
}
FilterFrames();
return S_OK;
}
void
ClrDataStackWalk::FilterFrames(void)
{
//
// Advance to a state compatible with the
// current filtering flags.
//
while (m_frameIter.IsValid())
{
switch(m_frameIter.GetFrameState())
{
case StackFrameIterator::SFITER_FRAMELESS_METHOD:
if (m_walkFlags & CLRDATA_SIMPFRAME_MANAGED_METHOD)
{
return;
}
break;
case StackFrameIterator::SFITER_FRAME_FUNCTION:
case StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION:
case StackFrameIterator::SFITER_NO_FRAME_TRANSITION:
if (m_walkFlags & CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE)
{
return;
}
break;
default:
break;
}
m_frameIter.Next();
}
}
void
ClrDataStackWalk::RawGetFrameType(
/* [out] */ CLRDataSimpleFrameType* simpleType,
/* [out] */ CLRDataDetailedFrameType* detailedType)
{
if (simpleType)
{
switch(m_frameIter.GetFrameState())
{
case StackFrameIterator::SFITER_FRAMELESS_METHOD:
*simpleType = CLRDATA_SIMPFRAME_MANAGED_METHOD;
break;
case StackFrameIterator::SFITER_FRAME_FUNCTION:
case StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION:
*simpleType = CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE;
break;
default:
*simpleType = CLRDATA_SIMPFRAME_UNRECOGNIZED;
break;
}
}
if (detailedType)
{
if (m_frameIter.m_crawl.GetFrame() && m_frameIter.m_crawl.GetFrame()->GetFrameAttribs() & Frame::FRAME_ATTR_EXCEPTION)
*detailedType = CLRDATA_DETFRAME_EXCEPTION_FILTER;
else
*detailedType = CLRDATA_DETFRAME_UNRECOGNIZED;
}
}
//----------------------------------------------------------------------------
//
// ClrDataFrame.
//
//----------------------------------------------------------------------------
ClrDataFrame::ClrDataFrame(ClrDataAccess* dac,
CLRDataSimpleFrameType simpleType,
CLRDataDetailedFrameType detailedType,
AppDomain* appDomain,
MethodDesc* methodDesc)
{
m_dac = dac;
m_dac->AddRef();
m_instanceAge = m_dac->m_instanceAge;
m_simpleType = simpleType;
m_detailedType = detailedType;
m_appDomain = appDomain;
m_methodDesc = methodDesc;
m_refs = 1;
m_methodSig = NULL;
m_localSig = NULL;
}
ClrDataFrame::~ClrDataFrame(void)
{
delete m_methodSig;
delete m_localSig;
m_dac->Release();
}
STDMETHODIMP
ClrDataFrame::QueryInterface(THIS_
IN REFIID interfaceId,
OUT PVOID* iface)
{
if (IsEqualIID(interfaceId, IID_IUnknown) ||
IsEqualIID(interfaceId, __uuidof(IXCLRDataFrame)))
{
AddRef();
*iface = static_cast<IUnknown*>
(static_cast<IXCLRDataFrame*>(this));
return S_OK;
}
else if (IsEqualIID(interfaceId, __uuidof(IXCLRDataFrame2)))
{
AddRef();
*iface = static_cast<IUnknown*>
(static_cast<IXCLRDataFrame2*>(this));
return S_OK;
}
else
{
*iface = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG)
ClrDataFrame::AddRef(THIS)
{
return InterlockedIncrement(&m_refs);
}
STDMETHODIMP_(ULONG)
ClrDataFrame::Release(THIS)
{
SUPPORTS_DAC_HOST_ONLY;
LONG newRefs = InterlockedDecrement(&m_refs);
if (newRefs == 0)
{
delete this;
}
return newRefs;
}
HRESULT STDMETHODCALLTYPE
ClrDataFrame::GetContext(
/* [in] */ ULONG32 contextFlags,
/* [in] */ ULONG32 contextBufSize,
/* [out] */ ULONG32 *contextSize,
/* [size_is][out] */ BYTE contextBuf[ ])
{
HRESULT status;
if (contextSize)
{
*contextSize = ContextSizeForFlags(contextFlags);
}
if (!CheckContextSizeForFlags(contextBufSize, contextFlags))
{
return E_INVALIDARG;
}
DAC_ENTER_SUB(m_dac);
EX_TRY
{
CopyMemory(contextBuf, &m_context, contextBufSize);
status = S_OK;
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataFrame::GetFrameType(
/* [out] */ CLRDataSimpleFrameType *simpleType,
/* [out] */ CLRDataDetailedFrameType *detailedType)
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
*simpleType = m_simpleType;
*detailedType = m_detailedType;
status = S_OK;
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataFrame::GetAppDomain(
/* [out] */ IXCLRDataAppDomain **appDomain)
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
if (m_appDomain)
{
ClrDataAppDomain* dataAppDomain =
new (nothrow) ClrDataAppDomain(m_dac, m_appDomain);
if (!dataAppDomain)
{
status = E_OUTOFMEMORY;
}
else
{
*appDomain = static_cast<IXCLRDataAppDomain*>(dataAppDomain);
status = S_OK;
}
}
else
{
*appDomain = NULL;
status = S_FALSE;
}
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataFrame::GetNumArguments(
/* [out] */ ULONG32 *numArgs)
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
if (!m_methodDesc)
{
status = E_NOINTERFACE;
}
else
{
MetaSig* sig;
status = GetMethodSig(&sig, numArgs);
}
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataFrame::GetArgumentByIndex(
/* [in] */ ULONG32 index,
/* [out] */ IXCLRDataValue **arg,
/* [in] */ ULONG32 bufLen,
/* [out] */ ULONG32 *nameLen,
/* [size_is][out] */ _Out_writes_to_opt_(bufLen, *nameLen) WCHAR name[ ])
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
if (nameLen)
{
*nameLen = 0;
}
if (!m_methodDesc)
{
status = E_NOINTERFACE;
goto Exit;
}
MetaSig* sig;
ULONG32 numArgs;
if (FAILED(status = GetMethodSig(&sig, &numArgs)))
{
goto Exit;
}
if (index >= numArgs)
{
status = E_INVALIDARG;
goto Exit;
}
if ((bufLen && name) || nameLen)
{
if (index == 0 && sig->HasThis())
{
if (nameLen)
{
*nameLen = 5;
}
StringCchCopy(name, bufLen, W("this"));
}
else
{
if (!m_methodDesc->IsNoMetadata())
{
IMDInternalImport* mdImport = m_methodDesc->GetMDImport();
mdParamDef paramToken;
LPCSTR paramName;
USHORT seq;
DWORD attr;
// Param indexing is 1-based.
ULONG32 mdIndex = index + 1;
// 'this' doesn't show up in the signature but
// is present in the dac API indexing so adjust the
// index down for methods with 'this'.
if (sig->HasThis())
{
mdIndex--;
}
status = mdImport->FindParamOfMethod(
m_methodDesc->GetMemberDef(),
mdIndex,
&paramToken);
if (status == S_OK)
{
status = mdImport->GetParamDefProps(
paramToken,
&seq,
&attr,
&paramName);
if ((status == S_OK) && (paramName != NULL))
{
if ((status = ConvertUtf8(paramName,
bufLen, nameLen, name)) != S_OK)
{
goto Exit;
}
}
}
}
else
{
if (nameLen)
{
*nameLen = 1;
}
name[0] = 0;
}
}
}
status = ValueFromDebugInfo(sig, true, index, index, arg);
Exit: ;
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataFrame::GetNumLocalVariables(
/* [out] */ ULONG32 *numLocals)
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
if (!m_methodDesc)
{
status = E_NOINTERFACE;
}
else
{
MetaSig* sig;
status = GetLocalSig(&sig, numLocals);
}
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataFrame::GetLocalVariableByIndex(
/* [in] */ ULONG32 index,
/* [out] */ IXCLRDataValue **localVariable,
/* [in] */ ULONG32 bufLen,
/* [out] */ ULONG32 *nameLen,
/* [size_is][out] */ _Out_writes_to_opt_(bufLen, *nameLen) WCHAR name[ ])
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
if (!m_methodDesc)
{
status = E_NOINTERFACE;
goto Exit;
}
MetaSig* sig;
ULONG32 numLocals;
if (FAILED(status = GetLocalSig(&sig, &numLocals)))
{
goto Exit;
}
if (index >= numLocals)
{
status = E_INVALIDARG;
goto Exit;
}
MetaSig* argSig;
ULONG32 numArgs;
if (FAILED(status = GetMethodSig(&argSig, &numArgs)))
{
goto Exit;
}
// Can't get names for locals in the Whidbey runtime.
if (bufLen && name)
{
if (nameLen)
{
*nameLen = 1;
}
name[0] = 0;
}
// The locals are indexed immediately following the arguments
// in the NativeVarInfos.
status = ValueFromDebugInfo(sig, false, index, index + numArgs,
localVariable);
Exit: ;
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataFrame::GetNumTypeArguments(
/* [out] */ ULONG32 *numTypeArgs)
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
// XXX Microsoft.
status = E_NOTIMPL;
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataFrame::GetTypeArgumentByIndex(
/* [in] */ ULONG32 index,
/* [out] */ IXCLRDataTypeInstance **typeArg)
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
// XXX Microsoft.
status = E_NOTIMPL;
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataFrame::GetExactGenericArgsToken(
/* [out] */ IXCLRDataValue ** genericToken)
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
if (!m_methodDesc)
{
status = E_NOINTERFACE;
goto Exit;
}
MetaSig* sig;
ULONG32 numLocals;
if (FAILED(status = GetLocalSig(&sig, &numLocals)))
{
goto Exit;
}
// The locals are indexed immediately following the arguments
// in the NativeVarInfos.
status = ValueFromDebugInfo(sig, false, 1, (DWORD)ICorDebugInfo::TYPECTXT_ILNUM,
genericToken);
Exit: ;
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataFrame::GetCodeName(
/* [in] */ ULONG32 flags,
/* [in] */ ULONG32 bufLen,
/* [out] */ ULONG32 *symbolLen,
/* [size_is][out] */ _Out_writes_bytes_opt_(bufLen) WCHAR symbolBuf[ ])
{
HRESULT status = E_FAIL;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
TADDR pcAddr = PCODEToPINSTR(GetControlPC(&m_regDisp));
status = m_dac->
RawGetMethodName(TO_CDADDR(pcAddr), flags,
bufLen, symbolLen, symbolBuf,
NULL);
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataFrame::GetMethodInstance(
/* [out] */ IXCLRDataMethodInstance **method)
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
if (!m_methodDesc)
{
status = E_NOINTERFACE;
}
else
{
ClrDataMethodInstance* dataMethod =
new (nothrow) ClrDataMethodInstance(m_dac,
m_appDomain,
m_methodDesc);
*method = static_cast<IXCLRDataMethodInstance*>(dataMethod);
status = dataMethod ? S_OK : E_OUTOFMEMORY;
}
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT STDMETHODCALLTYPE
ClrDataFrame::Request(
/* [in] */ ULONG32 reqCode,
/* [in] */ ULONG32 inBufferSize,
/* [size_is][in] */ BYTE *inBuffer,
/* [in] */ ULONG32 outBufferSize,
/* [size_is][out] */ BYTE *outBuffer)
{
HRESULT status;
DAC_ENTER_SUB(m_dac);
EX_TRY
{
switch(reqCode)
{
case CLRDATA_REQUEST_REVISION:
if (inBufferSize != 0 ||
inBuffer ||
outBufferSize != sizeof(ULONG32))
{
status = E_INVALIDARG;
}
else
{
*(ULONG32*)outBuffer = 1;
status = S_OK;
}
break;
default:
status = E_INVALIDARG;
break;
}
}
EX_CATCH
{
if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status))
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
DAC_LEAVE();
return status;
}
HRESULT
ClrDataFrame::GetMethodSig(MetaSig** sig,
ULONG32* count)
{
if (!m_methodSig)
{
m_methodSig = new (nothrow) MetaSig(m_methodDesc);
if (!m_methodSig)
{
return E_OUTOFMEMORY;
}
}
*sig = m_methodSig;
*count = m_methodSig->NumFixedArgs() +
(m_methodSig->HasThis() ? 1 : 0);
return *count ? S_OK : S_FALSE;
}
HRESULT
ClrDataFrame::GetLocalSig(MetaSig** sig,
ULONG32* count)
{
HRESULT hr;
if (!m_localSig)
{
// It turns out we cannot really get rid of this check. Dynamic methods
// (including IL stubs) do not have their local sig's available after JIT time.
// IL methods with dynamically generated IL (for example, UnsafeAccessors) may
// not have an IL header.
COR_ILMETHOD* ilHeader = m_methodDesc->IsIL()
? m_methodDesc->GetILHeader()
: NULL;
if (ilHeader == NULL)
{
*sig = NULL;
*count = 0;
return E_FAIL;
}
COR_ILMETHOD_DECODER methodDecoder(ilHeader);
mdSignature localSig = methodDecoder.GetLocalVarSigTok() ?
methodDecoder.GetLocalVarSigTok() : mdSignatureNil;
if (localSig == mdSignatureNil)
{
*sig = NULL;
*count = 0;
return E_FAIL;
}
ULONG tokenSigLen;
PCCOR_SIGNATURE tokenSig;
IfFailRet(m_methodDesc->GetModule()->GetMDImport()->GetSigFromToken(
localSig,
&tokenSigLen,
&tokenSig));
SigTypeContext typeContext(m_methodDesc, TypeHandle());
m_localSig = new (nothrow)
MetaSig(tokenSig,
tokenSigLen,
m_methodDesc->GetModule(),
&typeContext,
MetaSig::sigLocalVars);
if (!m_localSig)
{
return E_OUTOFMEMORY;
}
}
*sig = m_localSig;
*count = m_localSig->NumFixedArgs();
return S_OK;
}
HRESULT
ClrDataFrame::ValueFromDebugInfo(MetaSig* sig,
bool isArg,
DWORD sigIndex,
DWORD varInfoSlot,
IXCLRDataValue** _value)
{
HRESULT status;
ULONG32 numVarInfo;
NewHolder<ICorDebugInfo::NativeVarInfo> varInfo(NULL);
ULONG32 codeOffset;
ULONG32 valueFlags;
ULONG32 i;
TADDR ip = PCODEToPINSTR(GetControlPC(&m_regDisp));
if ((status = m_dac->GetMethodVarInfo(m_methodDesc,
ip,
&numVarInfo,
&varInfo,
&codeOffset)) != S_OK)
{
// We have signature info indicating that there
// are values, but couldn't find any location info.
// Optimized routines may have eliminated all
// traditional variable locations, so just treat
// this as a no-location case just like not being
// able to find a matching lifetime.
numVarInfo = 0;
}
for (i = 0; i < numVarInfo; i++)
{
if (varInfo[i].startOffset <= codeOffset &&
varInfo[i].endOffset >= codeOffset &&
varInfo[i].varNumber == varInfoSlot &&
varInfo[i].loc.vlType != ICorDebugInfo::VLT_INVALID)
{
break;
}
}
ULONG64 baseAddr;
NativeVarLocation locs[MAX_NATIVE_VAR_LOCS];
ULONG32 numLocs;
if (i >= numVarInfo)
{
numLocs = 0;
}
else
{
numLocs = NativeVarLocations(varInfo[i].loc, &m_context,
ARRAY_SIZE(locs), locs);
}
if (numLocs == 1 && !locs[0].contextReg)
{
baseAddr = TO_CDADDR(locs[0].addr);
}
else
{
baseAddr = 0;
}
TypeHandle argType;
sig->Reset();
if (isArg && sigIndex == 0 && sig->HasThis())
{
argType = TypeHandle(m_methodDesc->GetMethodTable());
valueFlags = CLRDATA_VALUE_IS_REFERENCE;
}
else
{
// 'this' doesn't show up in the signature but
// is present in the indexing so adjust the
// index down for methods with 'this'.
if (isArg && sig->HasThis())
{
sigIndex--;
}
do
{
sig->NextArg();
}
while (sigIndex-- > 0);
// == FailIfNotLoaded
// Will also return null if type is not restored
argType = sig->GetLastTypeHandleThrowing(ClassLoader::DontLoadTypes);
if (argType.IsNull())
{
// XXX Microsoft - Sometimes types can't be looked
// up and this at least allows the value to be used,
// but is it the right behavior?
argType = TypeHandle(CoreLibBinder::GetElementType(ELEMENT_TYPE_U8));
valueFlags = 0;
}
else
{
valueFlags = GetTypeFieldValueFlags(argType, NULL, 0, false);
// If this is a primitive variable and the actual size is smaller than what we have been told,
// then lower the size so that we won't read in trash memory (e.g. reading 4 bytes for a short).
if ((valueFlags & CLRDATA_VALUE_IS_PRIMITIVE) != 0)
{
if (numLocs == 1)
{
UINT actualSize = argType.GetSize();
if (actualSize < locs[0].size)
{
locs[0].size = actualSize;
}
}
}
}
}
ClrDataValue* value = new (nothrow)
ClrDataValue(m_dac,
m_appDomain,
NULL,
valueFlags,
argType,
baseAddr,
numLocs,
locs);
if (!value)
{
return E_OUTOFMEMORY;
}
*_value = value;
return S_OK;
}