}
}
- /// <summary>
- /// Find and return last method's offset for user code.
- /// </summary>
- /// <param name="assemblyPath">file path of the assembly or null if the module is in-memory or dynamic</param>
- /// <param name="methodToken">method token</param>
- /// <param name="LastIlOffset">return last found IL offset in user code</param>
- /// <returns>"Ok" if last IL offset was found</returns>
- internal static RetCode GetMethodLastIlOffset(IntPtr symbolReaderHandle, int methodToken, out uint LastIlOffset)
- {
- Debug.Assert(symbolReaderHandle != IntPtr.Zero);
-
- LastIlOffset = 0;
- bool foundOffset = false;
-
- try
- {
- GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
- MetadataReader reader = ((OpenedReader)gch.Target).Reader;
-
- // We don't use LINQ in order to reduce memory consumption for managed part, so, Reverse() usage not an option here.
- // Note, SequencePointCollection is IEnumerable based collections.
- foreach (SequencePoint p in GetSequencePointCollection(methodToken, reader))
- {
- if (p.StartLine == 0 || p.StartLine == SequencePoint.HiddenLine || p.Offset < 0)
- continue;
-
- // Method's IL start only from 0, use uint for IL offset.
- LastIlOffset = (uint)p.Offset;
- foundOffset = true;
- }
- }
- catch
- {
- return RetCode.Exception;
- }
-
- return foundOffset ? RetCode.OK : RetCode.Fail;
- }
-
[StructLayout(LayoutKind.Sequential)]
internal struct method_data_t
{
}
/// <summary>
- /// Helper method to return all async methods stepping information.
+ /// Helper method to return async method stepping information and return last method's offset for user code.
/// </summary>
/// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param>
- /// <param name="asyncInfo">array with all async methods stepping information</param>
+ /// <param name="methodToken">method token</param>
+ /// <param name="asyncInfo">array with all async method stepping information</param>
/// <param name="asyncInfoCount">entry's count in asyncInfo</param>
- internal static RetCode GetAsyncMethodsSteppingInfo(IntPtr symbolReaderHandle, out IntPtr asyncInfo, out int asyncInfoCount)
+ /// <param name="LastIlOffset">return last found IL offset in user code</param>
+ /// <returns>"Ok" if method have at least one await block and last IL offset was found</returns>
+ internal static RetCode GetAsyncMethodSteppingInfo(IntPtr symbolReaderHandle, int methodToken, out IntPtr asyncInfo, out int asyncInfoCount, out uint LastIlOffset)
{
Debug.Assert(symbolReaderHandle != IntPtr.Zero);
asyncInfo = IntPtr.Zero;
asyncInfoCount = 0;
+ LastIlOffset = 0;
+ bool foundOffset = false;
var list = new List<AsyncAwaitInfoBlock>();
try
GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
MetadataReader reader = ((OpenedReader)gch.Target).Reader;
+ Handle handle = MetadataTokens.Handle(methodToken);
+ if (handle.Kind != HandleKind.MethodDefinition)
+ return RetCode.Fail;
+
+ MethodDebugInformationHandle methodDebugInformationHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
+ var entityHandle = MetadataTokens.EntityHandle(MetadataTokens.GetToken(methodDebugInformationHandle.ToDefinitionHandle()));
+
// Guid is taken from Roslyn source code:
// https://github.com/dotnet/roslyn/blob/afd10305a37c0ffb2cfb2c2d8446154c68cfa87a/src/Dependencies/CodeAnalysis.Debugging/PortableCustomDebugInfoKinds.cs#L13
Guid asyncMethodSteppingInformationBlob = new Guid("54FD2AC5-E925-401A-9C2A-F94F171072F8");
- foreach (MethodDebugInformationHandle methodDebugInformationHandle in reader.MethodDebugInformation)
+ foreach (var cdiHandle in reader.GetCustomDebugInformation(entityHandle))
{
- var entityHandle = MetadataTokens.EntityHandle(MetadataTokens.GetToken(methodDebugInformationHandle.ToDefinitionHandle()));
+ var cdi = reader.GetCustomDebugInformation(cdiHandle);
- foreach (var cdiHandle in reader.GetCustomDebugInformation(entityHandle))
+ if (reader.GetGuid(cdi.Kind) == asyncMethodSteppingInformationBlob)
{
- var cdi = reader.GetCustomDebugInformation(cdiHandle);
-
- if (reader.GetGuid(cdi.Kind) == asyncMethodSteppingInformationBlob)
- {
- // Format of this blob is taken from Roslyn source code:
- // https://github.com/dotnet/roslyn/blob/afd10305a37c0ffb2cfb2c2d8446154c68cfa87a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs#L575
+ // Format of this blob is taken from Roslyn source code:
+ // https://github.com/dotnet/roslyn/blob/afd10305a37c0ffb2cfb2c2d8446154c68cfa87a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs#L575
- var blobReader = reader.GetBlobReader(cdi.Value);
- blobReader.ReadUInt32(); // skip catch_handler_offset
+ var blobReader = reader.GetBlobReader(cdi.Value);
+ blobReader.ReadUInt32(); // skip catch_handler_offset
- while (blobReader.Offset < blobReader.Length)
- {
- list.Add(new AsyncAwaitInfoBlock() {
- yield_offset = blobReader.ReadUInt32(),
- resume_offset = blobReader.ReadUInt32(),
- // explicit conversion from int into uint here, see:
- // https://docs.microsoft.com/en-us/dotnet/api/system.reflection.metadata.blobreader.readcompressedinteger
- token = (uint)blobReader.ReadCompressedInteger()
- });
- }
+ while (blobReader.Offset < blobReader.Length)
+ {
+ list.Add(new AsyncAwaitInfoBlock() {
+ yield_offset = blobReader.ReadUInt32(),
+ resume_offset = blobReader.ReadUInt32(),
+ // explicit conversion from int into uint here, see:
+ // https://docs.microsoft.com/en-us/dotnet/api/system.reflection.metadata.blobreader.readcompressedinteger
+ token = (uint)blobReader.ReadCompressedInteger()
+ });
}
}
}
if (list.Count == 0)
- return RetCode.OK;
+ return RetCode.Fail;
int structSize = Marshal.SizeOf<AsyncAwaitInfoBlock>();
asyncInfo = Marshal.AllocCoTaskMem(list.Count * structSize);
}
asyncInfoCount = list.Count;
+
+ // We don't use LINQ in order to reduce memory consumption for managed part, so, Reverse() usage not an option here.
+ // Note, SequencePointCollection is IEnumerable based collections.
+ foreach (SequencePoint p in GetSequencePointCollection(methodToken, reader))
+ {
+ if (p.StartLine == 0 || p.StartLine == SequencePoint.HiddenLine || p.Offset < 0)
+ continue;
+
+ // Method's IL start only from 0, use uint for IL offset.
+ LastIlOffset = (uint)p.Offset;
+ foundOffset = true;
+ }
+
+ if (!foundOffset)
+ {
+ if (asyncInfo != IntPtr.Zero)
+ Marshal.FreeCoTaskMem(asyncInfo);
+
+ asyncInfo = IntPtr.Zero;
+ return RetCode.Fail;
+ }
}
catch
{
typedef RetCode (*GetStepRangesFromIPDelegate)(PVOID, int32_t, mdMethodDef, uint32_t*, uint32_t*);
typedef RetCode (*GetModuleMethodsRangesDelegate)(PVOID, int32_t, PVOID, int32_t, PVOID, PVOID*);
typedef RetCode (*ResolveBreakPointsDelegate)(PVOID, int32_t, PVOID, int32_t, int32_t, int32_t*, PVOID*);
-typedef RetCode (*GetMethodLastIlOffsetDelegate)(PVOID, mdMethodDef, uint32_t*);
-typedef RetCode (*GetAsyncMethodsSteppingInfoDelegate)(PVOID, PVOID*, int32_t*);
+typedef RetCode (*GetAsyncMethodSteppingInfoDelegate)(PVOID, mdMethodDef, PVOID*, int32_t*, uint32_t*);
typedef RetCode (*GetSourceDelegate)(PVOID, const WCHAR*, int32_t*, PVOID*);
typedef RetCode (*CalculationDelegate)(PVOID, int32_t, PVOID, int32_t, int32_t, int32_t*, PVOID*, BSTR*);
typedef int (*GenerateStackMachineProgramDelegate)(const WCHAR*, PVOID*, BSTR*);
GetStepRangesFromIPDelegate getStepRangesFromIPDelegate = nullptr;
GetModuleMethodsRangesDelegate getModuleMethodsRangesDelegate = nullptr;
ResolveBreakPointsDelegate resolveBreakPointsDelegate = nullptr;
-GetMethodLastIlOffsetDelegate getMethodLastIlOffsetDelegate = nullptr;
-GetAsyncMethodsSteppingInfoDelegate getAsyncMethodsSteppingInfoDelegate = nullptr;
+GetAsyncMethodSteppingInfoDelegate getAsyncMethodSteppingInfoDelegate = nullptr;
GetSourceDelegate getSourceDelegate = nullptr;
GenerateStackMachineProgramDelegate generateStackMachineProgramDelegate = nullptr;
ReleaseStackMachineProgramDelegate releaseStackMachineProgramDelegate = nullptr;
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetStepRangesFromIP", (void **)&getStepRangesFromIPDelegate)) &&
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetModuleMethodsRanges", (void **)&getModuleMethodsRangesDelegate)) &&
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "ResolveBreakPoints", (void **)&resolveBreakPointsDelegate)) &&
- SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetMethodLastIlOffset", (void **)&getMethodLastIlOffsetDelegate)) &&
- SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetAsyncMethodsSteppingInfo", (void **)&getAsyncMethodsSteppingInfoDelegate)) &&
+ SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetAsyncMethodSteppingInfo", (void **)&getAsyncMethodSteppingInfoDelegate)) &&
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetSource", (void **)&getSourceDelegate)) &&
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, EvaluationClassName, "CalculationDelegate", (void **)&calculationDelegate)) &&
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, EvaluationClassName, "GenerateStackMachineProgram", (void **)&generateStackMachineProgramDelegate)) &&
getStepRangesFromIPDelegate &&
getModuleMethodsRangesDelegate &&
resolveBreakPointsDelegate &&
- getMethodLastIlOffsetDelegate &&
- getAsyncMethodsSteppingInfoDelegate &&
+ getAsyncMethodSteppingInfoDelegate &&
getSourceDelegate &&
generateStackMachineProgramDelegate &&
releaseStackMachineProgramDelegate &&
getStepRangesFromIPDelegate = nullptr;
getModuleMethodsRangesDelegate = nullptr;
resolveBreakPointsDelegate = nullptr;
- getMethodLastIlOffsetDelegate = nullptr;
- getAsyncMethodsSteppingInfoDelegate = nullptr;
+ getAsyncMethodSteppingInfoDelegate = nullptr;
getSourceDelegate = nullptr;
stringToUpperDelegate = nullptr;
coTaskMemAllocDelegate = nullptr;
return S_OK;
}
-HRESULT GetMethodLastIlOffset(PVOID pSymbolReaderHandle, mdMethodDef methodToken, ULONG32 *ilOffset)
-{
- std::unique_lock<Utility::RWLock::Reader> read_lock(CLRrwlock.reader);
- if (!getMethodLastIlOffsetDelegate || !pSymbolReaderHandle || !ilOffset)
- return E_FAIL;
-
- RetCode retCode = getMethodLastIlOffsetDelegate(pSymbolReaderHandle, methodToken, ilOffset);
- return retCode == RetCode::OK ? S_OK : E_FAIL;
-}
-
HRESULT GetModuleMethodsRanges(PVOID pSymbolReaderHandle, int32_t constrTokensNum, PVOID constrTokens, int32_t normalTokensNum, PVOID normalTokens, PVOID *data)
{
std::unique_lock<Utility::RWLock::Reader> read_lock(CLRrwlock.reader);
return retCode == RetCode::OK ? S_OK : E_FAIL;
}
-HRESULT GetAsyncMethodsSteppingInfo(PVOID pSymbolReaderHandle, std::vector<AsyncAwaitInfoBlock> &AsyncAwaitInfo)
+HRESULT GetAsyncMethodSteppingInfo(PVOID pSymbolReaderHandle, mdMethodDef methodToken, std::vector<AsyncAwaitInfoBlock> &AsyncAwaitInfo, ULONG32 *ilOffset)
{
std::unique_lock<Utility::RWLock::Reader> read_lock(CLRrwlock.reader);
- if (!getAsyncMethodsSteppingInfoDelegate || !pSymbolReaderHandle)
+ if (!getAsyncMethodSteppingInfoDelegate || !pSymbolReaderHandle || !ilOffset)
return E_FAIL;
AsyncAwaitInfoBlock *allocatedAsyncInfo = nullptr;
int32_t asyncInfoCount = 0;
- RetCode retCode = getAsyncMethodsSteppingInfoDelegate(pSymbolReaderHandle, (PVOID*)&allocatedAsyncInfo, &asyncInfoCount);
+ RetCode retCode = getAsyncMethodSteppingInfoDelegate(pSymbolReaderHandle, methodToken, (PVOID*)&allocatedAsyncInfo, &asyncInfoCount, ilOffset);
read_lock.unlock();
if (retCode != RetCode::OK)
WCHAR *localName, ULONG localNameLen, ULONG32 *pIlStart, ULONG32 *pIlEnd);
HRESULT GetHoistedLocalScopes(PVOID pSymbolReaderHandle, mdMethodDef methodToken, PVOID *data, int32_t &hoistedLocalScopesCount);
HRESULT GetStepRangesFromIP(PVOID pSymbolReaderHandle, ULONG32 ip, mdMethodDef MethodToken, ULONG32 *ilStartOffset, ULONG32 *ilEndOffset);
- HRESULT GetMethodLastIlOffset(PVOID pSymbolReaderHandle, mdMethodDef methodToken, ULONG32 *ilOffset);
HRESULT GetModuleMethodsRanges(PVOID pSymbolReaderHandle, int32_t constrTokensNum, PVOID constrTokens, int32_t normalTokensNum, PVOID normalTokens, PVOID *data);
HRESULT ResolveBreakPoints(PVOID pSymbolReaderHandle, int32_t tokenNum, PVOID Tokens, int32_t sourceLine, int32_t nestedToken, int32_t &Count, PVOID *data);
- HRESULT GetAsyncMethodsSteppingInfo(PVOID pSymbolReaderHandle, std::vector<AsyncAwaitInfoBlock> &AsyncAwaitInfo);
+ HRESULT GetAsyncMethodSteppingInfo(PVOID pSymbolReaderHandle, mdMethodDef methodToken, std::vector<AsyncAwaitInfoBlock> &AsyncAwaitInfo, ULONG32 *ilOffset);
HRESULT GetSource(PVOID symbolReaderHandle, const std::string fileName, PVOID *data, int32_t *length);
HRESULT CalculationDelegate(PVOID firstOp, int32_t firstType, PVOID secondOp, int32_t secondType, int32_t operationType, int32_t &resultType, PVOID *data, std::string &errorText);
HRESULT GenerateStackMachineProgram(const std::string &expr, PVOID *ppStackProgram, std::string &textOutput);
return S_OK;
}
-// Fill m_asyncMethodsSteppingInfo by data from module. Called on callback during module load.
-// [in] pModule - object that represents the CLR module;
-// [in] pSymbolReaderHandle - pointer to managed part GCHandle with preloaded PDB.
-HRESULT Modules::FillAsyncMethodsSteppingInfo(ICorDebugModule *pModule, PVOID pSymbolReaderHandle)
+// Caller must care about m_asyncMethodSteppingInfoMutex.
+HRESULT Modules::GetAsyncMethodSteppingInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken)
{
- HRESULT Status;
- CORDB_ADDRESS modAddress;
- IfFailRet(pModule->GetBaseAddress(&modAddress));
+ if (asyncMethodSteppingInfo.modAddress == modAddress &&
+ asyncMethodSteppingInfo.methodToken == methodToken)
+ return S_OK;
- std::vector<Interop::AsyncAwaitInfoBlock> AsyncAwaitInfo;
- IfFailRet(Interop::GetAsyncMethodsSteppingInfo(pSymbolReaderHandle, AsyncAwaitInfo));
+ if (!asyncMethodSteppingInfo.awaits.empty())
+ asyncMethodSteppingInfo.awaits.clear();
- const std::lock_guard<std::mutex> lock(m_asyncMethodsSteppingInfoMutex);
+ std::lock_guard<std::mutex> lock(m_modulesInfoMutex);
+ auto info_pair = m_modulesInfo.find(modAddress);
+ if (info_pair == m_modulesInfo.end())
+ {
+ return E_FAIL;
+ }
+
+ ModuleInfo &mdInfo = info_pair->second;
+
+ HRESULT Status;
+ std::vector<Interop::AsyncAwaitInfoBlock> AsyncAwaitInfo;
+ IfFailRet(Interop::GetAsyncMethodSteppingInfo(mdInfo.m_symbolReaderHandle, methodToken, AsyncAwaitInfo, &asyncMethodSteppingInfo.lastIlOffset));
for (const auto &entry : AsyncAwaitInfo)
{
- mdMethodDef realToken = mdMethodDefNil + entry.token;
- std::pair<CORDB_ADDRESS, mdMethodDef> newKey = std::make_pair(modAddress, realToken);
- m_asyncMethodsSteppingInfo[newKey].awaits.emplace_back(entry.yield_offset, entry.resume_offset);
-
- IfFailRet(Interop::GetMethodLastIlOffset(pSymbolReaderHandle, realToken, &m_asyncMethodsSteppingInfo[newKey].lastIlOffset));
+ asyncMethodSteppingInfo.awaits.emplace_back(entry.yield_offset, entry.resume_offset);
}
+ asyncMethodSteppingInfo.modAddress = modAddress;
+ asyncMethodSteppingInfo.methodToken = methodToken;
+
return S_OK;
}
// [in] methodToken - method token (from module with address modAddress).
bool Modules::IsMethodHaveAwait(CORDB_ADDRESS modAddress, mdMethodDef methodToken)
{
- const std::lock_guard<std::mutex> lock(m_asyncMethodsSteppingInfoMutex);
+ const std::lock_guard<std::mutex> lock(m_asyncMethodSteppingInfoMutex);
- auto searchAsyncInfo = m_asyncMethodsSteppingInfo.find(std::make_pair(modAddress, methodToken));
- return searchAsyncInfo != m_asyncMethodsSteppingInfo.end();
+ return SUCCEEDED(GetAsyncMethodSteppingInfo(modAddress, methodToken));
}
// Find await block after IL offset in particular async method and return await info, if present.
// [out] awaitInfo - result, next await info.
bool Modules::FindNextAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 ipOffset, AwaitInfo **awaitInfo)
{
- const std::lock_guard<std::mutex> lock(m_asyncMethodsSteppingInfoMutex);
+ const std::lock_guard<std::mutex> lock(m_asyncMethodSteppingInfoMutex);
- auto searchAsyncInfo = m_asyncMethodsSteppingInfo.find(std::make_pair(modAddress, methodToken));
- if (searchAsyncInfo == m_asyncMethodsSteppingInfo.end())
+ if (FAILED(GetAsyncMethodSteppingInfo(modAddress, methodToken)))
return false;
- for (auto &await : searchAsyncInfo->second.awaits)
+ for (auto &await : asyncMethodSteppingInfo.awaits)
{
if (ipOffset <= await.yield_offset)
{
// [out] lastIlOffset - result, IL offset for last user code line in async method.
bool Modules::FindLastIlOffsetAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 &lastIlOffset)
{
- const std::lock_guard<std::mutex> lock(m_asyncMethodsSteppingInfoMutex);
+ const std::lock_guard<std::mutex> lock(m_asyncMethodSteppingInfoMutex);
- auto searchAsyncInfo = m_asyncMethodsSteppingInfo.find(std::make_pair(modAddress, methodToken));
- if (searchAsyncInfo == m_asyncMethodsSteppingInfo.end())
+ if (FAILED(GetAsyncMethodSteppingInfo(modAddress, methodToken)))
return false;
- lastIlOffset = searchAsyncInfo->second.lastIlOffset;
+ lastIlOffset = asyncMethodSteppingInfo.lastIlOffset;
return true;
}
if (FAILED(FillSourcesCodeLinesForModule(pModule, pMDImport, pSymbolReaderHandle)))
LOGE("Could not load source lines related info from PDB file. Could produce failures during breakpoint's source path resolve in future.");
-
- if (FAILED(FillAsyncMethodsSteppingInfo(pModule, pSymbolReaderHandle)))
- LOGE("Could not load async methods related info from PDB file. Could produce failures during stepping in async methods in future.");
}
IfFailRet(GetModuleId(pModule, module.id));
{};
};
+ bool IsMethodHaveAwait(CORDB_ADDRESS modAddress, mdMethodDef methodToken);
+ bool FindNextAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 ipOffset, AwaitInfo **awaitInfo);
+ bool FindLastIlOffsetAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 &lastIlOffset);
+
+private:
+
struct AsyncMethodInfo
{
+ CORDB_ADDRESS modAddress;
+ mdMethodDef methodToken;
+
std::vector<AwaitInfo> awaits;
// Part of NotifyDebuggerOfWaitCompletion magic, see ManagedDebugger::SetupAsyncStep().
ULONG32 lastIlOffset;
AsyncMethodInfo() :
- awaits(), lastIlOffset(0)
+ modAddress(0), methodToken(mdMethodDefNil), awaits(), lastIlOffset(0)
{};
};
- bool IsMethodHaveAwait(CORDB_ADDRESS modAddress, mdMethodDef methodToken);
- bool FindNextAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 ipOffset, AwaitInfo **awaitInfo);
- bool FindLastIlOffsetAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 &lastIlOffset);
-
-private:
-
- struct PairHash
- {
- template <class T1, class T2>
- size_t operator()(const std::pair<T1, T2> &pair) const
- {
- return std::hash<T1>{}(pair.first) ^ std::hash<T2>{}(pair.second);
- }
- };
- // All async methods stepping information for all loaded (with symbols) modules.
- std::unordered_map<std::pair<CORDB_ADDRESS, mdMethodDef>, AsyncMethodInfo, PairHash> m_asyncMethodsSteppingInfo;
- std::mutex m_asyncMethodsSteppingInfoMutex;
- HRESULT FillAsyncMethodsSteppingInfo(ICorDebugModule *pModule, PVOID pSymbolReaderHandle);
+ AsyncMethodInfo asyncMethodSteppingInfo;
+ std::mutex m_asyncMethodSteppingInfoMutex;
+ // Note, result stored into asyncMethodSteppingInfo.
+ HRESULT GetAsyncMethodSteppingInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken);
};
} // namespace netcoredbg