ToRelease<ICorDebugFunction> pFunc;\r
IfFailRet(pFrame->GetFunction(&pFunc));\r
\r
+ ToRelease<ICorDebugCode> pCode;\r
+ IfFailRet(pFunc->GetILCode(&pCode));\r
+ ULONG32 methodVersion;\r
+ IfFailRet(pCode->GetVersionNumber(&methodVersion));\r
+\r
ToRelease<ICorDebugModule> pModule;\r
IfFailRet(pFunc->GetModule(&pModule));\r
\r
\r
fullyQualifiedIlOffset.modAddress = modAddress;\r
fullyQualifiedIlOffset.methodToken = methodToken;\r
+ fullyQualifiedIlOffset.methodVersion = methodVersion;\r
fullyQualifiedIlOffset.ilOffset = ilOffset;\r
\r
return S_OK;\r
FullyQualifiedIlOffset_t fullyQualifiedIlOffset;\r
IfFailRet(GetFullyQualifiedIlOffset(pThread, fullyQualifiedIlOffset));\r
\r
- if (fullyQualifiedIlOffset.modAddress == 0 ||\r
- fullyQualifiedIlOffset.methodToken == 0 ||\r
- fullyQualifiedIlOffset.modAddress != m_lastStoppedIlOffset.modAddress ||\r
- fullyQualifiedIlOffset.methodToken != m_lastStoppedIlOffset.methodToken)\r
+ if (fullyQualifiedIlOffset.modAddress != m_lastStoppedIlOffset.modAddress ||\r
+ fullyQualifiedIlOffset.methodToken != m_lastStoppedIlOffset.methodToken ||\r
+ fullyQualifiedIlOffset.methodVersion != m_lastStoppedIlOffset.methodVersion)\r
return S_FALSE;\r
\r
Modules::SequencePoint lastSP;\r
IfFailRet(m_sharedModules->GetSequencePointByILOffset(m_lastStoppedIlOffset.modAddress, m_lastStoppedIlOffset.methodToken,\r
- m_lastStoppedIlOffset.ilOffset, lastSP));\r
+ m_lastStoppedIlOffset.methodVersion, m_lastStoppedIlOffset.ilOffset, lastSP));\r
\r
Modules::SequencePoint curSP;\r
IfFailRet(m_sharedModules->GetSequencePointByILOffset(fullyQualifiedIlOffset.modAddress, fullyQualifiedIlOffset.methodToken,\r
- fullyQualifiedIlOffset.ilOffset, curSP));\r
+ fullyQualifiedIlOffset.methodVersion, fullyQualifiedIlOffset.ilOffset, curSP));\r
\r
if (lastSP.startLine != curSP.startLine ||\r
lastSP.startColumn != curSP.startColumn ||\r
{\r
CORDB_ADDRESS modAddress = 0;\r
mdMethodDef methodToken = 0;\r
+ ULONG32 methodVersion = 0;\r
ULONG32 ilOffset = 0;\r
\r
void Reset()\r
{\r
modAddress = 0;\r
methodToken = 0;\r
+ methodVersion = 0;\r
ilOffset = 0;\r
}\r
};\r
\r
// Note, in case of async `MoveNext` method, user code don't start from 0 IL offset.\r
ULONG32 ilCloseOffset;\r
- IfFailRet(pModules->GetNextSequencePointInMethod(pModule, resultToken, 0, ilCloseOffset));\r
+ const ULONG32 currentVersion = 1; // In case entry breakpoint, this can be only base PDB, not delta PDB for sure.\r
+ IfFailRet(pModules->GetNextSequencePointInMethod(pModule, resultToken, currentVersion, 0, ilCloseOffset));\r
\r
entryPointToken = resultToken;\r
entryPointOffset = ilCloseOffset;\r
if (Status == S_OK) // S_FALSE - don't skip breakpoint\r
return S_OK;\r
\r
+ ToRelease<ICorDebugFunction> pFunc;\r
+ IfFailRet(entry.first->GetFunctionFromToken(entry.second, &pFunc));\r
+ ULONG32 currentVersion; // Note, new breakpoints could be setup for last code version only, since protocols (MI, VSCode, ...) provide method name (sig) only.\r
+ IfFailRet(pFunc->GetCurrentVersionNumber(¤tVersion));\r
+\r
ULONG32 ilCloseOffset;\r
- if (FAILED(m_sharedModules->GetNextSequencePointInMethod(entry.first, entry.second, 0, ilCloseOffset)))\r
+ if (FAILED(m_sharedModules->GetNextSequencePointInMethod(entry.first, entry.second, currentVersion, 0, ilCloseOffset)))\r
return S_OK;\r
\r
- ToRelease<ICorDebugFunction> pFunc;\r
- IfFailRet(entry.first->GetFunctionFromToken(entry.second, &pFunc));\r
ToRelease<ICorDebugCode> pCode;\r
IfFailRet(pFunc->GetILCode(&pCode));\r
\r
}
static HRESULT WalkGeneratedClassFields(IMetaDataImport *pMD, ICorDebugValue *pInputValue, ULONG32 currentIlOffset, std::unordered_set<WSTRING> &usedNames,
- mdMethodDef methodDef, Modules *pModules, ICorDebugModule *pModule, Evaluator::WalkStackVarsCallback cb)
+ mdMethodDef methodDef, ULONG32 methodVersion, Modules *pModules, ICorDebugModule *pModule, Evaluator::WalkStackVarsCallback cb)
{
HRESULT Status;
BOOL isNull = FALSE;
{
ToRelease<ICorDebugValue> iCorDisplayClassValue;
IfFailRet(getValue(&iCorDisplayClassValue, defaultEvalFlags));
- IfFailRet(WalkGeneratedClassFields(pMD, iCorDisplayClassValue, currentIlOffset, usedNames, methodDef, pModules, pModule, cb));
+ IfFailRet(WalkGeneratedClassFields(pMD, iCorDisplayClassValue, currentIlOffset, usedNames, methodDef, methodVersion, pModules, pModule, cb));
}
else if (generatedNameKind == GeneratedNameKind::HoistedLocalField)
{
if (hoistedLocalScopesCount == -1)
{
PVOID data = nullptr;
- if (SUCCEEDED(pModules->GetHoistedLocalScopes(pModule, methodDef, &data, hoistedLocalScopesCount)) && data)
+ if (SUCCEEDED(pModules->GetHoistedLocalScopes(pModule, methodDef, methodVersion, &data, hoistedLocalScopesCount)) && data)
hoistedLocalScopes.reset((hoisted_local_scope_t*)data);
else
hoistedLocalScopesCount = 0;
ToRelease<ICorDebugFunction> pFunction;
IfFailRet(pFrame->GetFunction(&pFunction));
+ ToRelease<ICorDebugCode> pCode;
+ IfFailRet(pFunction->GetILCode(&pCode));
+ ULONG32 methodVersion;
+ IfFailRet(pCode->GetVersionNumber(&methodVersion));
+
ToRelease<ICorDebugModule> pModule;
IfFailRet(pFunction->GetModule(&pModule));
WSTRING wLocalName;
ULONG32 ilStart;
ULONG32 ilEnd;
- if (FAILED(m_sharedModules->GetFrameNamedLocalVariable(pModule, methodDef, i, wLocalName, &ilStart, &ilEnd)))
+ if (FAILED(m_sharedModules->GetFrameNamedLocalVariable(pModule, methodDef, methodVersion, i, wLocalName, &ilStart, &ilEnd)))
continue;
if (currentIlOffset < ilStart || currentIlOffset >= ilEnd)
{
ToRelease<ICorDebugValue> iCorDisplayClassValue;
IfFailRet(getValue(&iCorDisplayClassValue, defaultEvalFlags));
- IfFailRet(WalkGeneratedClassFields(pMD, iCorDisplayClassValue, currentIlOffset, usedNames, methodDef, m_sharedModules.get(), pModule, cb));
+ IfFailRet(WalkGeneratedClassFields(pMD, iCorDisplayClassValue, currentIlOffset, usedNames, methodDef, methodVersion, m_sharedModules.get(), pModule, cb));
continue;
}
}
if (generatedCodeKind != GeneratedCodeKind::Normal)
- return WalkGeneratedClassFields(pMD, currentThis, currentIlOffset, usedNames, methodDef, m_sharedModules.get(), pModule, cb);
+ return WalkGeneratedClassFields(pMD, currentThis, currentIlOffset, usedNames, methodDef, methodVersion, m_sharedModules.get(), pModule, cb);
return S_OK;
}
return m_uniqueBreakpoints->AllBreakpointsActivate(act);
}
-HRESULT ManagedDebugger::GetFrameLocation(ICorDebugFrame *pFrame, ThreadId threadId, FrameLevel level, StackFrame &stackFrame)
+static HRESULT InternalGetFrameLocation(ICorDebugFrame *pFrame, Modules *pModules, bool hotReload, ThreadId threadId, FrameLevel level, StackFrame &stackFrame)
{
HRESULT Status;
+ ToRelease<ICorDebugFunction> pFunc;
+ IfFailRet(pFrame->GetFunction(&pFunc));
+
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pFunc->GetModule(&pModule));
+
+ if (hotReload)
+ {
+ // In case current (top) code version is 1, executed in this frame method version can't be not 1.
+ ULONG32 currentVersion = 1;
+ ULONG32 methodVersion = 1;
+ if (SUCCEEDED(pFunc->GetCurrentVersionNumber(¤tVersion)) && currentVersion != 1)
+ {
+ ToRelease<ICorDebugCode> pCode;
+ IfFailRet(pFunc->GetILCode(&pCode));
+ IfFailRet(pCode->GetVersionNumber(&methodVersion));
+ }
+
+ if (methodVersion != currentVersion)
+ {
+ std::string moduleNamePrefix;
+ WCHAR name[mdNameLen];
+ ULONG32 name_len = 0;
+ if (SUCCEEDED(pModule->GetName(_countof(name), &name_len, name)))
+ {
+ moduleNamePrefix = to_utf8(name);
+ std::size_t i = moduleNamePrefix.find_last_of("/\\");
+ if (i != std::string::npos)
+ moduleNamePrefix = moduleNamePrefix.substr(i + 1);
+ moduleNamePrefix += "!";
+ }
+
+ std::string methodName;
+ TypePrinter::GetMethodName(pFrame, methodName);
+ // [Outdated Code] module.dll!MethodName()
+ stackFrame = StackFrame(threadId, level, "[Outdated Code] " + moduleNamePrefix + methodName);
+
+ return S_OK;
+ }
+ }
+
stackFrame = StackFrame(threadId, level, "");
ULONG32 ilOffset;
Modules::SequencePoint sp;
- if (SUCCEEDED(m_sharedModules->GetFrameILAndSequencePoint(pFrame, ilOffset, sp)))
+ if (SUCCEEDED(pModules->GetFrameILAndSequencePoint(pFrame, ilOffset, sp)))
{
stackFrame.source = Source(sp.document);
stackFrame.line = sp.startLine;
mdMethodDef methodToken;
IfFailRet(pFrame->GetFunctionToken(&methodToken));
- ToRelease<ICorDebugFunction> pFunc;
- IfFailRet(pFrame->GetFunction(&pFunc));
-
- ToRelease<ICorDebugModule> pModule;
- IfFailRet(pFunc->GetModule(&pModule));
-
ULONG32 nOffset = 0;
ToRelease<ICorDebugNativeFrame> pNativeFrame;
IfFailRet(pFrame->QueryInterface(IID_ICorDebugNativeFrame, (LPVOID*) &pNativeFrame));
return S_OK;
}
+HRESULT ManagedDebugger::GetFrameLocation(ICorDebugFrame *pFrame, ThreadId threadId, FrameLevel level, StackFrame &stackFrame)
+{
+ return InternalGetFrameLocation(pFrame, m_sharedModules.get(), m_hotReload, threadId, level, stackFrame);
+}
+
HRESULT ManagedDebugger::GetStackTrace(ICorDebugThread *pThread, FrameLevel startFrame, unsigned maxFrames, std::vector<StackFrame> &stackFrames, int &totalFrames)
{
LogFuncEntry();
case FrameCLRManaged:
{
StackFrame stackFrame;
- GetFrameLocation(pFrame, threadId, FrameLevel{currentFrame}, stackFrame);
+ InternalGetFrameLocation(pFrame, m_sharedModules.get(), m_hotReload, threadId, FrameLevel{currentFrame}, stackFrame);
stackFrames.push_back(stackFrame);
}
break;
return Status;
}
+HRESULT ManagedDebugger::ApplyPdbDelta(const std::string &dllFileName, const std::string &deltaPDB)
+{
+ HRESULT Status;
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(m_sharedModules->GetModuleWithName(dllFileName, &pModule, true));
+
+ IfFailRet(m_sharedModules->ApplyPdbDelta(pModule, m_justMyCode, deltaPDB));
+
+ // TODO add check resolved breakpoints and added constructors.
+
+ return S_OK;
+}
+
HRESULT ManagedDebugger::HotReloadApplyDeltas(const std::string &dllFileName, const std::string &deltaMD, const std::string &deltaIL, const std::string &deltaPDB)
{
LogFuncEntry();
if (!m_iCorProcess)
return E_FAIL;
- return ApplyMetadataAndILDeltas(m_sharedModules.get(), dllFileName, deltaMD, deltaIL);
+ HRESULT Status;
+ IfFailRet(ApplyMetadataAndILDeltas(m_sharedModules.get(), dllFileName, deltaMD, deltaIL));
+
+ return ApplyPdbDelta(dllFileName, deltaPDB);
}
} // namespace netcoredbg
HRESULT GetSourceFile(const std::string &sourcePath, char** fileBuf, int* fileLen) override;
void FreeUnmanaged(PVOID mem) override;
HRESULT HotReloadApplyDeltas(const std::string &dllFileName, const std::string &deltaMD, const std::string &deltaIL, const std::string &deltaPDB) override;
+ HRESULT ApplyPdbDelta(const std::string &dllFileName, const std::string &deltaPDB) override;
void FindFileNames(string_view pattern, unsigned limit, SearchCallback) override;
void FindFunctions(string_view pattern, unsigned limit, SearchCallback) override;
IfFailRet(pFunc->GetModule(&pModule));\r
CORDB_ADDRESS modAddress;\r
IfFailRet(pModule->GetBaseAddress(&modAddress));\r
+ ULONG32 methodVersion;\r
+ IfFailRet(pCode->GetVersionNumber(&methodVersion));\r
\r
- if (!m_sharedModules->IsMethodHaveAwait(modAddress, methodToken))\r
+ if (!m_sharedModules->IsMethodHaveAwait(modAddress, methodToken, methodVersion))\r
return S_FALSE; // setup simple stepper\r
\r
ToRelease<ICorDebugILFrame> pILFrame;\r
// switch to step-out, so whole NotifyDebuggerOfWaitCompletion magic happens.\r
ULONG32 lastIlOffset;\r
if (stepType != IDebugger::StepType::STEP_OUT &&\r
- m_sharedModules->FindLastIlOffsetAwaitInfo(modAddress, methodToken, lastIlOffset) &&\r
+ m_sharedModules->FindLastIlOffsetAwaitInfo(modAddress, methodToken, methodVersion, lastIlOffset) &&\r
ipOffset >= lastIlOffset)\r
{\r
stepType = IDebugger::StepType::STEP_OUT;\r
}\r
\r
Modules::AwaitInfo *awaitInfo = nullptr;\r
- if (m_sharedModules->FindNextAwaitInfo(modAddress, methodToken, ipOffset, &awaitInfo))\r
+ if (m_sharedModules->FindNextAwaitInfo(modAddress, methodToken, methodVersion, ipOffset, &awaitInfo))\r
{\r
// We have step inside async function with await, setup breakpoint at closest await's yield_offset.\r
// Two possible cases here:\r
virtual HRESULT GetSourceFile(const std::string &sourcePath, char** fileBuf, int* fileLen) = 0;
virtual void FreeUnmanaged(PVOID mem) = 0;
virtual HRESULT HotReloadApplyDeltas(const std::string &dllFileName, const std::string &deltaMD, const std::string &deltaIL, const std::string &deltaPDB) = 0;
+ virtual HRESULT ApplyPdbDelta(const std::string &dllFileName, const std::string &deltaPDB) = 0;
typedef std::function<void(const char *)> SearchCallback;
virtual void FindFileNames(string_view pattern, unsigned limit, SearchCallback) = 0;
virtual void FindFunctions(string_view pattern, unsigned limit, SearchCallback) = 0;
return IntPtr.Zero;
}
+ /// <summary>
+ /// Maps global method token to a handle local to the current delta PDB.
+ /// Debug tables referring to methods currently use local handles, not global handles.
+ /// See https://github.com/dotnet/roslyn/issues/16286
+ /// </summary>
+ private static MethodDefinitionHandle GetDeltaRelativeMethodDefinitionHandle(MetadataReader reader, int methodToken)
+ {
+ var globalHandle = (MethodDefinitionHandle)MetadataTokens.EntityHandle(methodToken);
+
+ if (reader.GetTableRowCount(TableIndex.EncMap) == 0)
+ {
+ return globalHandle;
+ }
+
+ var globalDebugHandle = globalHandle.ToDebugInformationHandle();
+
+ int rowId = 1;
+ foreach (var handle in reader.GetEditAndContinueMapEntries())
+ {
+ if (handle.Kind == HandleKind.MethodDebugInformation)
+ {
+ if (handle == globalDebugHandle)
+ {
+ return MetadataTokens.MethodDefinitionHandle(rowId);
+ }
+
+ rowId++;
+ }
+ }
+
+ // compiler generated invalid EncMap table:
+ throw new BadImageFormatException();
+ }
+
+ /// <summary>
+ /// Load delta PDB file.
+ /// </summary>
+ /// <param name="isFileLayout">Delta PDB file path</param>
+ /// <returns>Symbol reader handle or zero if error</returns>
+ internal static IntPtr LoadDeltaPdb([MarshalAs(UnmanagedType.LPWStr)] string pdbPath, out IntPtr data, out int count)
+ {
+ data = IntPtr.Zero;
+ count = 0;
+
+ try
+ {
+ var pdbStream = TryOpenFile(pdbPath);
+ if (pdbStream == null)
+ return IntPtr.Zero;
+
+ var provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
+ var reader = provider.GetMetadataReader();
+
+ OpenedReader openedReader = new OpenedReader(provider, reader);
+ if (openedReader == null)
+ return IntPtr.Zero;
+
+ var list = new List<int>();
+ foreach (var handle in reader.GetEditAndContinueMapEntries())
+ {
+ if (handle.Kind == HandleKind.MethodDebugInformation)
+ {
+ var methodToken = MetadataTokens.GetToken(((MethodDebugInformationHandle)handle).ToDefinitionHandle());
+ list.Add(methodToken);
+ }
+ }
+ if (list.Count > 0)
+ {
+ var methodsArray = list.ToArray();
+
+ data = Marshal.AllocCoTaskMem(list.Count * 4);
+ IntPtr dataPtr = data;
+ foreach (var p in list)
+ {
+ Marshal.WriteInt32(dataPtr, p);
+ dataPtr = dataPtr + 4;
+ }
+ count = list.Count;
+ }
+
+ GCHandle gch = GCHandle.Alloc(openedReader);
+ return GCHandle.ToIntPtr(gch);
+ }
+ catch
+ {
+ }
+
+ return IntPtr.Zero;
+ }
+
/// <summary>
/// Cleanup and dispose of symbol reader handle
/// </summary>
internal static SequencePointCollection GetSequencePointCollection(int methodToken, MetadataReader reader)
{
- Handle handle = MetadataTokens.Handle(methodToken);
+ Handle handle = GetDeltaRelativeMethodDefinitionHandle(reader, methodToken);
if (handle.Kind != HandleKind.MethodDefinition)
- throw new System.ArgumentException();
+ return new SequencePointCollection();
MethodDebugInformationHandle methodDebugHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
if (methodDebugHandle.IsNil)
- throw new System.ArgumentException();
+ return new SequencePointCollection();
MethodDebugInformation methodDebugInfo = reader.GetMethodDebugInformation(methodDebugHandle);
return methodDebugInfo.GetSequencePoints();
}
}
+ if (ModuleData.Count == 0)
+ return RetCode.Fail;
+
int structModuleMethodsDataSize = Marshal.SizeOf<file_methods_data_t>();
module_methods_data_t managedData;
managedData.fileNum = ModuleData.Count;
/// <param name="Count">entry's count in data</param>
/// <param name="data">pointer to memory with result</param>
/// <returns>"Ok" if information is available</returns>
- internal static RetCode ResolveBreakPoints(IntPtr symbolReaderHandle, int tokenNum, IntPtr Tokens, int sourceLine, int nestedToken, out int Count, out IntPtr data)
+ internal static RetCode ResolveBreakPoints(IntPtr symbolReaderHandles, int tokenNum, IntPtr Tokens, int sourceLine, int nestedToken, out int Count, out IntPtr data)
{
- Debug.Assert(symbolReaderHandle != IntPtr.Zero);
+ Debug.Assert(symbolReaderHandles != IntPtr.Zero);
Count = 0;
data = IntPtr.Zero;
var list = new List<resolved_bp_t>();
try
{
-
- GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
- MetadataReader reader = ((OpenedReader)gch.Target).Reader;
-
// In case nestedToken + sourceLine is part of constructor (tokenNum > 1) we could have cases:
// 1. type FieldName1 = new Type();
// void MethodName() {}; type FieldName2 = new Type(); ... <-- sourceLine
// We need check if nestedToken's method code closer to sourceLine than code from methodToken's method.
// If sourceLine closer to nestedToken's method code - setup breakpoint in nestedToken's method.
- SequencePoint FirstSequencePointForSourceLine(int methodToken)
+ SequencePoint FirstSequencePointForSourceLine(ref MetadataReader reader, int methodToken)
{
// Note, SequencePoints ordered by IL offsets, not by line numbers.
// For example, infinite loop `while(true)` will have IL offset after cycle body's code.
}
int elementSize = 4;
- for (int i = 0; i < tokenNum * elementSize; i += elementSize)
+ for (int i = 0; i < tokenNum; i++)
{
- int methodToken = Marshal.ReadInt32(Tokens, i);
- SequencePoint current_p = FirstSequencePointForSourceLine(methodToken);
+ IntPtr symbolReaderHandle = Marshal.ReadIntPtr(symbolReaderHandles, i * IntPtr.Size);
+ GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
+ MetadataReader reader = ((OpenedReader)gch.Target).Reader;
+
+ int methodToken = Marshal.ReadInt32(Tokens, i * elementSize);
+ SequencePoint current_p = FirstSequencePointForSourceLine(ref reader, methodToken);
// Note, we don't check that current_p was found or not, since we know for sure, that sourceLine could be resolved in method.
// Same idea for nested_p below, if we have nestedToken - it will be resolved for sure.
if (nestedToken != 0)
{
- SequencePoint nested_p = FirstSequencePointForSourceLine(nestedToken);
+ SequencePoint nested_p = FirstSequencePointForSourceLine(ref reader, nestedToken);
if (current_p.EndLine > nested_p.EndLine || (current_p.EndLine == nested_p.EndLine && current_p.EndColumn > nested_p.EndColumn))
{
list.Add(new resolved_bp_t(nested_p.StartLine, nested_p.EndLine, nested_p.Offset, nestedToken));
GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
MetadataReader reader = ((OpenedReader)gch.Target).Reader;
- Handle handle = MetadataTokens.Handle(methodToken);
+ Handle handle = GetDeltaRelativeMethodDefinitionHandle(reader, methodToken);
if (handle.Kind != HandleKind.MethodDefinition)
return false;
GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
MetadataReader reader = ((OpenedReader)gch.Target).Reader;
- Handle handle = MetadataTokens.Handle(methodToken);
+ Handle handle = GetDeltaRelativeMethodDefinitionHandle(reader, methodToken);
if (handle.Kind != HandleKind.MethodDefinition)
return RetCode.Fail;
GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
MetadataReader reader = ((OpenedReader)gch.Target).Reader;
- Handle handle = MetadataTokens.Handle(methodToken);
+ Handle handle = GetDeltaRelativeMethodDefinitionHandle(reader, methodToken);
if (handle.Kind != HandleKind.MethodDefinition)
return false;
GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
MetadataReader reader = ((OpenedReader)gch.Target).Reader;
- Handle handle = MetadataTokens.Handle(methodToken);
+ Handle handle = GetDeltaRelativeMethodDefinitionHandle(reader, methodToken);
if (handle.Kind != HandleKind.MethodDefinition)
return RetCode.Fail;
typedef RetCode (*GetNextSequencePointByILOffsetDelegate)(PVOID, mdMethodDef, uint32_t, uint32_t*, int32_t*);
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 (*ResolveBreakPointsDelegate)(PVOID[], int32_t, PVOID, int32_t, int32_t, int32_t*, PVOID*);
typedef RetCode (*GetAsyncMethodSteppingInfoDelegate)(PVOID, mdMethodDef, PVOID*, int32_t*, uint32_t*);
typedef RetCode (*GetSourceDelegate)(PVOID, const WCHAR*, int32_t*, PVOID*);
+typedef PVOID (*LoadDeltaPdbDelegate)(const WCHAR*, PVOID*, int32_t*);
typedef RetCode (*CalculationDelegate)(PVOID, int32_t, PVOID, int32_t, int32_t, int32_t*, PVOID*, BSTR*);
typedef int (*GenerateStackMachineProgramDelegate)(const WCHAR*, PVOID*, BSTR*);
typedef void (*ReleaseStackMachineProgramDelegate)(PVOID);
ResolveBreakPointsDelegate resolveBreakPointsDelegate = nullptr;
GetAsyncMethodSteppingInfoDelegate getAsyncMethodSteppingInfoDelegate = nullptr;
GetSourceDelegate getSourceDelegate = nullptr;
+LoadDeltaPdbDelegate loadDeltaPdbDelegate = nullptr;
GenerateStackMachineProgramDelegate generateStackMachineProgramDelegate = nullptr;
ReleaseStackMachineProgramDelegate releaseStackMachineProgramDelegate = nullptr;
NextStackCommandDelegate nextStackCommandDelegate = nullptr;
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "ResolveBreakPoints", (void **)&resolveBreakPointsDelegate)) &&
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, SymbolReaderClassName, "LoadDeltaPdb", (void **)&loadDeltaPdbDelegate)) &&
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, EvaluationClassName, "CalculationDelegate", (void **)&calculationDelegate)) &&
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, EvaluationClassName, "GenerateStackMachineProgram", (void **)&generateStackMachineProgramDelegate)) &&
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, EvaluationClassName, "ReleaseStackMachineProgram", (void **)&releaseStackMachineProgramDelegate)) &&
resolveBreakPointsDelegate &&
getAsyncMethodSteppingInfoDelegate &&
getSourceDelegate &&
+ loadDeltaPdbDelegate &&
generateStackMachineProgramDelegate &&
releaseStackMachineProgramDelegate &&
nextStackCommandDelegate &&
resolveBreakPointsDelegate = nullptr;
getAsyncMethodSteppingInfoDelegate = nullptr;
getSourceDelegate = nullptr;
+ loadDeltaPdbDelegate = nullptr;
stringToUpperDelegate = nullptr;
coTaskMemAllocDelegate = nullptr;
coTaskMemFreeDelegate = nullptr;
return retCode == RetCode::OK ? S_OK : E_FAIL;
}
-HRESULT ResolveBreakPoints(PVOID pSymbolReaderHandle, int32_t tokenNum, PVOID Tokens, int32_t sourceLine, int32_t nestedToken, int32_t &Count, PVOID *data)
+HRESULT ResolveBreakPoints(PVOID pSymbolReaderHandles[], int32_t tokenNum, PVOID Tokens, int32_t sourceLine, int32_t nestedToken, int32_t &Count, PVOID *data)
{
std::unique_lock<Utility::RWLock::Reader> read_lock(CLRrwlock.reader);
- if (!resolveBreakPointsDelegate || !pSymbolReaderHandle || !Tokens || !data)
+ if (!resolveBreakPointsDelegate || !pSymbolReaderHandles || !Tokens || !data)
return E_FAIL;
- RetCode retCode = resolveBreakPointsDelegate(pSymbolReaderHandle, tokenNum, Tokens, sourceLine, nestedToken, &Count, data);
+ RetCode retCode = resolveBreakPointsDelegate(pSymbolReaderHandles, tokenNum, Tokens, sourceLine, nestedToken, &Count, data);
return retCode == RetCode::OK ? S_OK : E_FAIL;
}
return retCode == RetCode::OK ? S_OK : E_FAIL;
}
+HRESULT LoadDeltaPdb(const std::string &pdbPath, VOID **ppSymbolReaderHandle, std::unordered_set<mdMethodDef> &methodTokens)
+{
+ std::unique_lock<Utility::RWLock::Reader> read_lock(CLRrwlock.reader);
+ if (!loadDeltaPdbDelegate|| !ppSymbolReaderHandle || pdbPath.empty())
+ return E_FAIL;
+
+ PVOID pMethodTokens = nullptr;
+ int32_t tokensCount = 0;
+
+ *ppSymbolReaderHandle = loadDeltaPdbDelegate(to_utf16(pdbPath).c_str(), &pMethodTokens, &tokensCount);
+
+ if (tokensCount > 0 && pMethodTokens)
+ {
+ for(int i = 0; i < tokensCount; i++)
+ {
+ methodTokens.insert(((mdMethodDef*)pMethodTokens)[i]);
+ }
+ }
+
+ if (pMethodTokens)
+ CoTaskMemFree(pMethodTokens);
+
+ if (*ppSymbolReaderHandle == 0)
+ return E_FAIL;
+
+ return S_OK;
+}
+
} // namespace Interop
} // namespace netcoredbg
#include <string>
#include <vector>
#include <functional>
+#include <unordered_set>
namespace netcoredbg
HRESULT GetHoistedLocalScopes(PVOID pSymbolReaderHandle, mdMethodDef methodToken, PVOID *data, int32_t &hoistedLocalScopesCount);
HRESULT GetStepRangesFromIP(PVOID pSymbolReaderHandle, ULONG32 ip, mdMethodDef MethodToken, ULONG32 *ilStartOffset, ULONG32 *ilEndOffset);
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 ResolveBreakPoints(PVOID pSymbolReaderHandles[], int32_t tokenNum, PVOID Tokens, int32_t sourceLine, int32_t nestedToken, int32_t &Count, PVOID *data);
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 LoadDeltaPdb(const std::string &pdbPath, VOID **ppSymbolReaderHandle, std::unordered_set<mdMethodDef> &methodTokens);
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);
void ReleaseStackMachineProgram(PVOID pStackProgram);
// Apply the attribute directly to the 'Get' and 'Set' procedures as appropriate.
const char DebuggerAttribute::Hidden[] = "System.Diagnostics.DebuggerHiddenAttribute..ctor";
+static std::vector<std::string> typeAttrNames{DebuggerAttribute::NonUserCode, DebuggerAttribute::StepThrough};
+static std::vector<std::string> methodAttrNames{DebuggerAttribute::NonUserCode, DebuggerAttribute::StepThrough, DebuggerAttribute::Hidden};
+
bool ForEachAttribute(IMetaDataImport *pMD, mdToken tok, std::function<HRESULT(const std::string &AttrName)> cb)
{
bool found = false;
static HRESULT GetNonJMCMethodsForTypeDef(
IMetaDataImport *pMD,
- PVOID pSymbolReaderHandle,
mdTypeDef typeDef,
std::vector<mdToken> &excludeMethods)
{
- static std::vector<std::string> attrNames{DebuggerAttribute::NonUserCode, DebuggerAttribute::StepThrough, DebuggerAttribute::Hidden};
-
ULONG numMethods = 0;
HCORENUM fEnum = NULL;
mdMethodDef methodDef;
nullptr, nullptr, nullptr, nullptr, nullptr)))
continue;
- if (HasAttribute(pMD, methodDef, attrNames))
+ if (HasAttribute(pMD, methodDef, methodAttrNames))
excludeMethods.push_back(methodDef);
}
pMD->CloseEnum(fEnum);
return S_OK;
}
-static HRESULT GetNonJMCClassesAndMethods(ICorDebugModule *pModule, PVOID pSymbolReaderHandle, std::vector<mdToken> &excludeTokens)
+static HRESULT GetNonJMCClassesAndMethods(ICorDebugModule *pModule, std::vector<mdToken> &excludeTokens)
{
HRESULT Status;
IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
- static std::vector<std::string> attrNames{DebuggerAttribute::NonUserCode, DebuggerAttribute::StepThrough};
-
ULONG numTypedefs = 0;
HCORENUM fEnum = NULL;
mdTypeDef typeDef;
while(SUCCEEDED(pMD->EnumTypeDefs(&fEnum, &typeDef, 1, &numTypedefs)) && numTypedefs != 0)
{
- if (HasAttribute(pMD, typeDef, attrNames))
+ if (HasAttribute(pMD, typeDef, typeAttrNames))
excludeTokens.push_back(typeDef);
else
- GetNonJMCMethodsForTypeDef(pMD, pSymbolReaderHandle, typeDef, excludeTokens);
+ GetNonJMCMethodsForTypeDef(pMD, typeDef, excludeTokens);
}
pMD->CloseEnum(fEnum);
return S_OK;
}
-HRESULT DisableJMCByAttributes(ICorDebugModule *pModule, PVOID pSymbolReaderHandle)
+void DisableJMCForTokenList(ICorDebugModule *pModule, const std::vector<mdToken> &excludeTokens)
{
- std::vector<mdToken> excludeTokens;
-
- GetNonJMCClassesAndMethods(pModule, pSymbolReaderHandle, excludeTokens);
-
for (mdToken token : excludeTokens)
{
if (TypeFromToken(token) == mdtMethodDef)
pClass2->SetJMCStatus(FALSE);
}
}
+}
+
+HRESULT DisableJMCByAttributes(ICorDebugModule *pModule)
+{
+ HRESULT Status;
+ std::vector<mdToken> excludeTokens;
+ IfFailRet(GetNonJMCClassesAndMethods(pModule, excludeTokens));
+
+ DisableJMCForTokenList(pModule, excludeTokens);
+ return S_OK;
+}
+
+HRESULT DisableJMCByAttributes(ICorDebugModule *pModule, const std::unordered_set<mdMethodDef> &methodTokens)
+{
+ HRESULT Status;
+ std::vector<mdToken> excludeTokens;
+ std::unordered_set<mdToken> excludeTypeTokens;
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+ for (mdMethodDef methodToken : methodTokens)
+ {
+ // Note, in case of method we need check class attributes first, since class also could have it.
+ ToRelease<ICorDebugFunction> pFunction;
+ IfFailRet(pModule->GetFunctionFromToken(methodToken, &pFunction));
+ ToRelease<ICorDebugClass> pClass;
+ IfFailRet(pFunction->GetClass(&pClass));
+ mdToken typeToken;
+ IfFailRet(pClass->GetToken(&typeToken));
+
+ // In case class have "not user code" related attribute, no reason set JMC to false for each method, set it to class will be enough.
+ if (HasAttribute(pMD, typeToken, typeAttrNames))
+ {
+ excludeTypeTokens.emplace(typeToken);
+ }
+ else if (HasAttribute(pMD, methodToken, methodAttrNames))
+ {
+ excludeTokens.push_back(methodToken);
+ }
+ }
+ std::copy(excludeTypeTokens.begin(), excludeTypeTokens.end(), std::back_inserter(excludeTokens));
+ DisableJMCForTokenList(pModule, excludeTokens);
return S_OK;
}
}
- bool GetMethodTokensByLinuNumber(const std::vector<std::vector<method_data_t>> &methodBpData,
+ bool GetMethodTokensByLineNumber(const std::vector<std::vector<method_data_t>> &methodBpData,
const std::unordered_map<method_data_t, std::vector<mdMethodDef>, method_data_t_hash> &multiMethodBpData,
/*in,out*/ uint32_t &lineNum,
/*out*/ std::vector<mdMethodDef> &Tokens,
Modules::ModuleInfo::~ModuleInfo() noexcept
{
- if (m_symbolReaderHandle)
- Interop::DisposeSymbols(m_symbolReaderHandle);
+ for (auto symbolReaderHandle : m_symbolReaderHandles)
+ {
+ if (symbolReaderHandle != nullptr)
+ Interop::DisposeSymbols(symbolReaderHandle);
+ }
}
static bool IsTargetFunction(const std::vector<std::string> &fullName, const std::vector<std::string> &targetName)
ToRelease<ICorDebugFunction> pFunc;
IfFailRet(pFrame->GetFunction(&pFunc));
+ ToRelease<ICorDebugCode> pCode;
+ IfFailRet(pFunc->GetILCode(&pCode));
+ ULONG32 methodVersion;
+ IfFailRet(pCode->GetVersionNumber(&methodVersion));
+
ToRelease<ICorDebugILFrame> pILFrame;
IfFailRet(pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame));
}
ModuleInfo &mdInfo = info_pair->second;
- return GetSequencePointByILOffset(mdInfo.m_symbolReaderHandle, methodToken, ilOffset, &sequencePoint);
+ if (mdInfo.m_symbolReaderHandles.empty() || mdInfo.m_symbolReaderHandles.size() < methodVersion)
+ return E_FAIL;
+
+ return GetSequencePointByILOffset(mdInfo.m_symbolReaderHandles[methodVersion - 1], methodToken, ilOffset, &sequencePoint);
}
HRESULT Modules::GetFrameILAndNextUserCodeILOffset(
ToRelease<ICorDebugFunction> pFunc;
IfFailRet(pFrame->GetFunction(&pFunc));
+ ToRelease<ICorDebugCode> pCode;
+ IfFailRet(pFunc->GetILCode(&pCode));
+ ULONG32 methodVersion;
+ IfFailRet(pCode->GetVersionNumber(&methodVersion));
+
ToRelease<ICorDebugILFrame> pILFrame;
IfFailRet(pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame));
ToRelease<ICorDebugModule> pModule;
IfFailRet(pFunc->GetModule(&pModule));
- return GetNextSequencePointInMethod(pModule, methodToken, ilOffset, ilCloseOffset, noUserCodeFound);
+ return GetNextSequencePointInMethod(pModule, methodToken, methodVersion, ilOffset, ilCloseOffset, noUserCodeFound);
}
HRESULT Modules::GetStepRangeFromCurrentIP(ICorDebugThread *pThread, COR_DEBUG_STEP_RANGE *range)
ToRelease<ICorDebugFunction> pFunc;
IfFailRet(pFrame->GetFunction(&pFunc));
+ ToRelease<ICorDebugCode> pCode;
+ IfFailRet(pFunc->GetILCode(&pCode));
+ ULONG32 methodVersion;
+ IfFailRet(pCode->GetVersionNumber(&methodVersion));
+
ToRelease<ICorDebugModule> pModule;
IfFailRet(pFunc->GetModule(&pModule));
}
ModuleInfo &mdInfo = info_pair->second;
- IfFailRet(Interop::GetStepRangesFromIP(mdInfo.m_symbolReaderHandle, nOffset, methodToken, &ilStartOffset, &ilEndOffset));
+ if (mdInfo.m_symbolReaderHandles.empty() || mdInfo.m_symbolReaderHandles.size() < methodVersion)
+ return E_FAIL;
+
+ IfFailRet(Interop::GetStepRangesFromIP(mdInfo.m_symbolReaderHandles[methodVersion - 1], nOffset, methodToken, &ilStartOffset, &ilEndOffset));
}
if (ilStartOffset == ilEndOffset)
}
// Caller must care about m_asyncMethodSteppingInfoMutex.
-HRESULT Modules::GetAsyncMethodSteppingInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken)
+HRESULT Modules::GetAsyncMethodSteppingInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 methodVersion)
{
if (asyncMethodSteppingInfo.modAddress == modAddress &&
- asyncMethodSteppingInfo.methodToken == methodToken)
+ asyncMethodSteppingInfo.methodToken == methodToken &&
+ asyncMethodSteppingInfo.methodVersion == methodVersion)
return S_OK;
if (!asyncMethodSteppingInfo.awaits.empty())
}
ModuleInfo &mdInfo = info_pair->second;
+ if (mdInfo.m_symbolReaderHandles.empty() || mdInfo.m_symbolReaderHandles.size() < methodVersion)
+ return E_FAIL;
HRESULT Status;
std::vector<Interop::AsyncAwaitInfoBlock> AsyncAwaitInfo;
- IfFailRet(Interop::GetAsyncMethodSteppingInfo(mdInfo.m_symbolReaderHandle, methodToken, AsyncAwaitInfo, &asyncMethodSteppingInfo.lastIlOffset));
+ IfFailRet(Interop::GetAsyncMethodSteppingInfo(mdInfo.m_symbolReaderHandles[methodVersion - 1], methodToken, AsyncAwaitInfo, &asyncMethodSteppingInfo.lastIlOffset));
for (const auto &entry : AsyncAwaitInfo)
{
asyncMethodSteppingInfo.modAddress = modAddress;
asyncMethodSteppingInfo.methodToken = methodToken;
+ asyncMethodSteppingInfo.methodVersion = methodVersion;
return S_OK;
}
// Check if method have await block. In this way we detect async method with awaits.
// [in] modAddress - module address;
// [in] methodToken - method token (from module with address modAddress).
-bool Modules::IsMethodHaveAwait(CORDB_ADDRESS modAddress, mdMethodDef methodToken)
+bool Modules::IsMethodHaveAwait(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 methodVersion)
{
const std::lock_guard<std::mutex> lock(m_asyncMethodSteppingInfoMutex);
- return SUCCEEDED(GetAsyncMethodSteppingInfo(modAddress, methodToken));
+ return SUCCEEDED(GetAsyncMethodSteppingInfo(modAddress, methodToken, methodVersion));
}
// Find await block after IL offset in particular async method and return await info, if present.
// [in] methodToken - method token (from module with address modAddress).
// [in] ipOffset - IL offset;
// [out] awaitInfo - result, next await info.
-bool Modules::FindNextAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 ipOffset, AwaitInfo **awaitInfo)
+bool Modules::FindNextAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 methodVersion, ULONG32 ipOffset, AwaitInfo **awaitInfo)
{
const std::lock_guard<std::mutex> lock(m_asyncMethodSteppingInfoMutex);
- if (FAILED(GetAsyncMethodSteppingInfo(modAddress, methodToken)))
+ if (FAILED(GetAsyncMethodSteppingInfo(modAddress, methodToken, methodVersion)))
return false;
for (auto &await : asyncMethodSteppingInfo.awaits)
// [in] modAddress - module address;
// [in] methodToken - method token (from module with address modAddress).
// [out] lastIlOffset - result, IL offset for last user code line in async method.
-bool Modules::FindLastIlOffsetAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 &lastIlOffset)
+bool Modules::FindLastIlOffsetAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 methodVersion, ULONG32 &lastIlOffset)
{
const std::lock_guard<std::mutex> lock(m_asyncMethodSteppingInfoMutex);
- if (FAILED(GetAsyncMethodSteppingInfo(modAddress, methodToken)))
+ if (FAILED(GetAsyncMethodSteppingInfo(modAddress, methodToken, methodVersion)))
return false;
lastIlOffset = asyncMethodSteppingInfo.lastIlOffset;
// * DebuggerStepThroughAttribute tells the debugger to step through the code it's applied to, rather than step into the code.
// The .NET debugger considers all other code to be user code.
if (needJMC)
- DisableJMCByAttributes(pModule, pSymbolReaderHandle);
+ DisableJMCByAttributes(pModule);
}
else if (Status == CORDBG_E_CANT_SET_TO_JMC)
{
HRESULT Modules::GetFrameNamedLocalVariable(
ICorDebugModule *pModule,
mdMethodDef methodToken,
+ ULONG32 methodVersion,
ULONG localIndex,
WSTRING &localName,
ULONG32 *pIlStart,
}
ModuleInfo &mdInfo = info_pair->second;
- IfFailRet(Interop::GetNamedLocalVariableAndScope(mdInfo.m_symbolReaderHandle, methodToken, localIndex, wLocalName, _countof(wLocalName), pIlStart, pIlEnd));
+ if (mdInfo.m_symbolReaderHandles.empty() || mdInfo.m_symbolReaderHandles.size() < methodVersion)
+ return E_FAIL;
+
+ IfFailRet(Interop::GetNamedLocalVariableAndScope(mdInfo.m_symbolReaderHandles[methodVersion - 1], methodToken, localIndex, wLocalName, _countof(wLocalName), pIlStart, pIlEnd));
}
localName = wLocalName;
HRESULT Modules::GetHoistedLocalScopes(
ICorDebugModule *pModule,
mdMethodDef methodToken,
+ ULONG32 methodVersion,
PVOID *data,
int32_t &hoistedLocalScopesCount)
{
}
ModuleInfo &mdInfo = info_pair->second;
- IfFailRet(Interop::GetHoistedLocalScopes(mdInfo.m_symbolReaderHandle, methodToken, data, hoistedLocalScopesCount));
+ if (mdInfo.m_symbolReaderHandles.empty() || mdInfo.m_symbolReaderHandles.size() < methodVersion)
+ return E_FAIL;
+
+ IfFailRet(Interop::GetHoistedLocalScopes(mdInfo.m_symbolReaderHandles[methodVersion - 1], methodToken, data, hoistedLocalScopesCount));
return S_OK;
}
std::string path = GetModuleFileName(mdInfo.m_iCorModule);
- if (onlyWithPDB && !mdInfo.m_symbolReaderHandle)
+ if (onlyWithPDB && mdInfo.m_symbolReaderHandles.empty())
continue;
if (GetFileName(path) == name)
HRESULT Modules::GetNextSequencePointInMethod(
ICorDebugModule *pModule,
mdMethodDef methodToken,
+ ULONG32 methodVersion,
ULONG32 ilOffset,
ULONG32 &ilCloseOffset,
bool *noUserCodeFound)
}
ModuleInfo &mdInfo = info_pair->second;
- return Interop::GetNextSequencePointByILOffset(mdInfo.m_symbolReaderHandle, methodToken, ilOffset, ilCloseOffset, noUserCodeFound);
+ if (mdInfo.m_symbolReaderHandles.empty() || mdInfo.m_symbolReaderHandles.size() < methodVersion)
+ return E_FAIL;
+
+ return Interop::GetNextSequencePointByILOffset(mdInfo.m_symbolReaderHandles[methodVersion - 1], methodToken, ilOffset, ilCloseOffset, noUserCodeFound);
}
HRESULT Modules::GetSequencePointByILOffset(
HRESULT Modules::GetSequencePointByILOffset(
CORDB_ADDRESS modAddress,
mdMethodDef methodToken,
+ ULONG32 methodVersion,
ULONG32 ilOffset,
Modules::SequencePoint &sequencePoint)
{
}
ModuleInfo &mdInfo = info_pair->second;
- return GetSequencePointByILOffset(mdInfo.m_symbolReaderHandle, methodToken, ilOffset, &sequencePoint);
+ if (mdInfo.m_symbolReaderHandles.empty() || mdInfo.m_symbolReaderHandles.size() < methodVersion)
+ return E_FAIL;
+
+ return GetSequencePointByILOffset(mdInfo.m_symbolReaderHandles[methodVersion - 1], methodToken, ilOffset, &sequencePoint);
}
HRESULT Modules::ForEachModule(std::function<HRESULT(ICorDebugModule *pModule)> cb)
std::vector<mdMethodDef> Tokens;
uint32_t correctedStartLine = sourceLine;
mdMethodDef closestNestedToken = 0;
- if (!GetMethodTokensByLinuNumber(sourceData.methodsData, sourceData.multiMethodsData, correctedStartLine, Tokens, closestNestedToken))
+ if (!GetMethodTokensByLineNumber(sourceData.methodsData, sourceData.multiMethodsData, correctedStartLine, Tokens, closestNestedToken))
continue;
// correctedStartLine - in case line not belong any methods, if possible, will be "moved" to first line of method below sourceLine.
return E_FAIL;
}
+ ModuleInfo &mdInfo = info_pair->second;
+ if (mdInfo.m_symbolReaderHandles.empty())
+ continue;
+
+ // In case one source line (field/property initialization) compiled into all constructors, after Hot Reload, constructors may have different
+ // code version numbers, that mean debug info located in different symbol readers.
+ std::vector<PVOID> symbolReaderHandles;
+ symbolReaderHandles.reserve(Tokens.size());
+ for (auto methodToken : Tokens)
+ {
+ // Note, new breakpoints could be setup for last code version only, since protocols (MI, VSCode, ...) provide source:line data only.
+ ULONG32 currentVersion;
+ ToRelease<ICorDebugFunction> pFunction;
+ if (FAILED(info_pair->second.m_iCorModule->GetFunctionFromToken(methodToken, &pFunction)) ||
+ FAILED(pFunction->GetCurrentVersionNumber(¤tVersion)))
+ {
+ symbolReaderHandles.emplace_back(mdInfo.m_symbolReaderHandles[0]);
+ continue;
+ }
+
+ assert(mdInfo.m_symbolReaderHandles.size() >= currentVersion);
+ symbolReaderHandles.emplace_back(mdInfo.m_symbolReaderHandles[currentVersion - 1]);
+ }
+
PVOID data = nullptr;
int32_t Count = 0;
- if (FAILED(Interop::ResolveBreakPoints(info_pair->second.m_symbolReaderHandle, (int32_t)Tokens.size(), Tokens.data(),
+ if (FAILED(Interop::ResolveBreakPoints(symbolReaderHandles.data(), (int32_t)Tokens.size(), Tokens.data(),
correctedStartLine, closestNestedToken, Count, &data))
|| data == nullptr)
{
return S_OK;
}
+HRESULT Modules::ApplyPdbDelta(ICorDebugModule *pModule, bool needJMC, const std::string &deltaPDB)
+{
+ HRESULT Status;
+ CORDB_ADDRESS modAddress;
+ IfFailRet(pModule->GetBaseAddress(&modAddress));
+
+ std::lock_guard<std::mutex> lock(m_modulesInfoMutex);
+
+ auto info_pair = m_modulesInfo.find(modAddress);
+ if (info_pair == m_modulesInfo.end())
+ {
+ return E_FAIL; // Deltas could be applied for already loaded modules with PDB only.
+ }
+
+ ModuleInfo &mdInfo = info_pair->second;
+ if (mdInfo.m_symbolReaderHandles.empty())
+ return E_FAIL; // Deltas could be applied for already loaded modules with PDB only.
+
+ PVOID pSymbolReaderHandle = nullptr;
+ std::unordered_set<mdMethodDef> methodTokens;
+ IfFailRet(Interop::LoadDeltaPdb(deltaPDB, &pSymbolReaderHandle, methodTokens));
+ mdInfo.m_symbolReaderHandles.emplace_back(pSymbolReaderHandle);
+
+ if (needJMC)
+ DisableJMCByAttributes(pModule, methodTokens);
+
+
+ // TODO update line breakpoints resolve data.
+ // TODO check line and functional breakpoints (new lines and new methods could be added).
+
+
+ return S_OK;
+}
+
HRESULT Modules::GetSourceFullPathByIndex(unsigned index, std::string &fullPath)
{
std::lock_guard<std::mutex> lock(m_sourcesInfoMutex);
}
ModuleInfo &mdInfo = info_pair->second;
- return Interop::GetSource(mdInfo.m_symbolReaderHandle, sourcePath, (PVOID*)fileBuf, fileLen);
+ if (mdInfo.m_symbolReaderHandles.size() > 1)
+ {
+ LOGE("This feature not support simultaneous work with Hot Reload.");
+ return E_FAIL;
+ }
+
+ return mdInfo.m_symbolReaderHandles.empty() ?
+ E_FAIL :
+ Interop::GetSource(mdInfo.m_symbolReaderHandles[0], sourcePath, (PVOID*)fileBuf, fileLen);
}
} // namespace netcoredbg
bool HasAttribute(IMetaDataImport *pMD, mdToken tok, const char *attrName);
bool HasAttribute(IMetaDataImport *pMD, mdToken tok, std::vector<std::string> &attrNames);
-HRESULT DisableJMCByAttributes(ICorDebugModule *pModule, PVOID pSymbolReaderHandle);
+HRESULT DisableJMCByAttributes(ICorDebugModule *pModule);
+HRESULT DisableJMCByAttributes(ICorDebugModule *pModule, const std::unordered_set<mdMethodDef> &methodTokens);
struct method_input_data_t
{
{
struct ModuleInfo
{
- PVOID m_symbolReaderHandle = nullptr;
+ std::vector<PVOID> m_symbolReaderHandles;
ToRelease<ICorDebugModule> m_iCorModule;
ModuleInfo(PVOID Handle, ICorDebugModule *Module) :
- m_symbolReaderHandle(Handle),
m_iCorModule(Module)
- {}
+ {
+ if (Handle == nullptr)
+ return;
+
+ m_symbolReaderHandles.reserve(1);
+ m_symbolReaderHandles.emplace_back(Handle);
+ }
ModuleInfo(ModuleInfo&& other) noexcept :
- m_symbolReaderHandle(other.m_symbolReaderHandle),
+ m_symbolReaderHandles(std::move(other.m_symbolReaderHandles)),
m_iCorModule(std::move(other.m_iCorModule))
{
- other.m_symbolReaderHandle = nullptr;
}
ModuleInfo(const ModuleInfo&) = delete;
ModuleInfo& operator=(ModuleInfo&&) = delete;
HRESULT GetSourceFullPathByIndex(unsigned index, std::string &fullPath);
HRESULT GetIndexBySourceFullPath(std::string fullPath, unsigned &index);
+ HRESULT ApplyPdbDelta(ICorDebugModule *pModule, bool needJMC, const std::string &deltaPDB);
HRESULT GetModuleWithName(const std::string &name, ICorDebugModule **ppModule, bool onlyWithPDB = false);
HRESULT GetFrameNamedLocalVariable(
ICorDebugModule *pModule,
mdMethodDef methodToken,
+ ULONG32 methodVersion,
ULONG localIndex,
WSTRING &localName,
ULONG32 *pIlStart,
HRESULT GetHoistedLocalScopes(
ICorDebugModule *pModule,
mdMethodDef methodToken,
+ ULONG32 methodVersion,
PVOID *data,
int32_t &hoistedLocalScopesCount);
HRESULT GetNextSequencePointInMethod(
ICorDebugModule *pModule,
mdMethodDef methodToken,
+ ULONG32 methodVersion,
ULONG32 ilOffset,
ULONG32 &ilCloseOffset,
bool *noUserCodeFound = nullptr);
- HRESULT GetSequencePointByILOffset(
- PVOID pSymbolReaderHandle,
- mdMethodDef methodToken,
- ULONG32 ilOffset,
- SequencePoint *sequencePoint);
-
HRESULT GetSequencePointByILOffset(
CORDB_ADDRESS modAddress,
mdMethodDef methodToken,
+ ULONG32 methodVersion,
ULONG32 ilOffset,
SequencePoint &sequencePoint);
{};
};
- 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);
+ bool IsMethodHaveAwait(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 methodVersion);
+ bool FindNextAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 methodVersion, ULONG32 ipOffset, AwaitInfo **awaitInfo);
+ bool FindLastIlOffsetAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 methodVersion, ULONG32 &lastIlOffset);
private:
+ HRESULT GetSequencePointByILOffset(
+ PVOID pSymbolReaderHandle,
+ mdMethodDef methodToken,
+ ULONG32 ilOffset,
+ SequencePoint *sequencePoint);
+
struct AsyncMethodInfo
{
CORDB_ADDRESS modAddress;
mdMethodDef methodToken;
+ ULONG32 methodVersion;
std::vector<AwaitInfo> awaits;
// Part of NotifyDebuggerOfWaitCompletion magic, see ManagedDebugger::SetupAsyncStep().
ULONG32 lastIlOffset;
AsyncMethodInfo() :
- modAddress(0), methodToken(mdMethodDefNil), awaits(), lastIlOffset(0)
+ modAddress(0), methodToken(mdMethodDefNil), methodVersion(0), awaits(), lastIlOffset(0)
{};
};
AsyncMethodInfo asyncMethodSteppingInfo;
std::mutex m_asyncMethodSteppingInfoMutex;
// Note, result stored into asyncMethodSteppingInfo.
- HRESULT GetAsyncMethodSteppingInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken);
+ HRESULT GetAsyncMethodSteppingInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToken, ULONG32 methodVersion);
};
} // namespace netcoredbg