Add multiple PDB files (PDB deltas) support.
authorMikhail Kurinnoi <m.kurinnoi@samsung.com>
Thu, 21 Apr 2022 14:02:32 +0000 (17:02 +0300)
committerAlexander Soldatov/Platform Lab /SRR/Staff Engineer/Samsung Electronics <soldatov.a@samsung.com>
Mon, 23 May 2022 14:56:37 +0000 (17:56 +0300)
Also add multiple PDB support for:
* evaluation;
* async step;
* single step;
* scope variables related code;
* breakpoint (Debugger.Break());
* JMC;
* stack trace;

15 files changed:
src/debugger/breakpoint_break.cpp
src/debugger/breakpoint_break.h
src/debugger/breakpoint_entry.cpp
src/debugger/breakpoints_func.cpp
src/debugger/evaluator.cpp
src/debugger/manageddebugger.cpp
src/debugger/manageddebugger.h
src/debugger/stepper_async.cpp
src/interfaces/idebugger.h
src/managed/SymbolReader.cs
src/managed/interop.cpp
src/managed/interop.h
src/metadata/jmc.cpp
src/metadata/modules.cpp
src/metadata/modules.h

index 8399c01ccfa2859713571d9b22bf725093207988..121e39c037d6b6536af6d83912b5e14e60d37380 100644 (file)
@@ -24,6 +24,11 @@ HRESULT BreakBreakpoint::GetFullyQualifiedIlOffset(ICorDebugThread *pThread, Ful
     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
@@ -39,6 +44,7 @@ HRESULT BreakBreakpoint::GetFullyQualifiedIlOffset(ICorDebugThread *pThread, Ful
 \r
     fullyQualifiedIlOffset.modAddress = modAddress;\r
     fullyQualifiedIlOffset.methodToken = methodToken;\r
+    fullyQualifiedIlOffset.methodVersion = methodVersion;\r
     fullyQualifiedIlOffset.ilOffset = ilOffset;\r
 \r
     return S_OK;\r
@@ -97,19 +103,18 @@ HRESULT BreakBreakpoint::ManagedCallbackBreak(ICorDebugThread *pThread, const Th
     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
index 23fa15d843bfdaf7ff9df3cd935f0df782648ff8..596b0f2d0c08d60acfadbb7dcaacb07ca80af59c 100644 (file)
@@ -43,12 +43,14 @@ private:
     {\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
index cc68527a459e087e98914c2cc87d431cc74c7861..07ea9da027aa8ce47a9449fd563a7ea545aaa2ef 100644 (file)
@@ -154,7 +154,8 @@ static HRESULT TrySetupAsyncEntryBreakpoint(ICorDebugModule *pModule, IMetaDataI
 \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
index d0549b9491e07efa4885ec2e82295e37ca48b5be..297c74507624d6e028027d1c3e4a99fb9c84c50f 100644 (file)
@@ -201,12 +201,15 @@ HRESULT FuncBreakpoints::AddFuncBreakpoint(ManagedFuncBreakpoint &fbp, ResolvedF
         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(&currentVersion));\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
index f416e7dc0dd184d09cc68fb1253e7693d4e575ad..a93f532a499ae0842b5ee97fa2b118bf247d2614 100644 (file)
@@ -1216,7 +1216,7 @@ static HRESULT TryParseHoistedLocalName(const WSTRING &mdName, WSTRING &wLocalNa
 }
 
 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;
@@ -1273,14 +1273,14 @@ static HRESULT WalkGeneratedClassFields(IMetaDataImport *pMD, ICorDebugValue *pI
         {
             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;
@@ -1334,6 +1334,11 @@ HRESULT Evaluator::WalkStackVars(ICorDebugThread *pThread, FrameLevel frameLevel
     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));
 
@@ -1451,7 +1456,7 @@ HRESULT Evaluator::WalkStackVars(ICorDebugThread *pThread, FrameLevel frameLevel
         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)
@@ -1475,7 +1480,7 @@ HRESULT Evaluator::WalkStackVars(ICorDebugThread *pThread, FrameLevel frameLevel
         {
             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;
         }
 
@@ -1487,7 +1492,7 @@ HRESULT Evaluator::WalkStackVars(ICorDebugThread *pThread, FrameLevel frameLevel
     }
 
     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;
 }
index 5cc5439874406638b2542095d12c2518f25254c4..0830fce9a363f717897c7d182bcd5fc3c05878f9 100644 (file)
@@ -902,15 +902,56 @@ HRESULT ManagedDebugger::AllBreakpointsActivate(bool act)
     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(&currentVersion)) && 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;
@@ -922,12 +963,6 @@ HRESULT ManagedDebugger::GetFrameLocation(ICorDebugFrame *pFrame, ThreadId threa
     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));
@@ -946,6 +981,11 @@ HRESULT ManagedDebugger::GetFrameLocation(ICorDebugFrame *pFrame, ThreadId threa
     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();
@@ -1003,7 +1043,7 @@ HRESULT ManagedDebugger::GetStackTrace(ICorDebugThread *pThread, FrameLevel star
             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;
@@ -1257,6 +1297,19 @@ static HRESULT ApplyMetadataAndILDeltas(Modules *pModules, const std::string &dl
     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();
@@ -1266,7 +1319,10 @@ HRESULT ManagedDebugger::HotReloadApplyDeltas(const std::string &dllFileName, co
     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
index 2451c5039287218d25e70c225c24c376f0c60764..ebb208e694d46958808f8b325753821a6dece8a1 100644 (file)
@@ -168,6 +168,7 @@ public:
     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;
index 4c80a355dab91e49918f5e7bd744937d2b358dde..00565d0ce383f477f304e8beb744d64cad19ef4d 100644 (file)
@@ -263,8 +263,10 @@ HRESULT AsyncStepper::SetupStep(ICorDebugThread *pThread, IDebugger::StepType st
     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
@@ -278,7 +280,7 @@ HRESULT AsyncStepper::SetupStep(ICorDebugThread *pThread, IDebugger::StepType st
     // 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
@@ -292,7 +294,7 @@ HRESULT AsyncStepper::SetupStep(ICorDebugThread *pThread, IDebugger::StepType st
     }\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
index 129fc0df0303b2992d8a21f0faabdc29fca409fc..e6c75c83ae217bc73f93bfd60e7b98188f05d718 100644 (file)
@@ -104,6 +104,7 @@ public:
     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;
index 2f8ec8ab3bf784674971b4058fcf8e4d4f916223..8c962b73e9aada95dacfb9dc3d35dbbba12cccf3 100644 (file)
@@ -209,6 +209,96 @@ namespace NetCoreDbg
             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>
@@ -229,13 +319,13 @@ namespace NetCoreDbg
 
         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();
@@ -512,6 +602,9 @@ namespace NetCoreDbg
                     }
                 }
 
+                if (ModuleData.Count == 0)
+                    return RetCode.Fail;
+
                 int structModuleMethodsDataSize = Marshal.SizeOf<file_methods_data_t>();
                 module_methods_data_t managedData;
                 managedData.fileNum = ModuleData.Count;
@@ -588,19 +681,15 @@ namespace NetCoreDbg
         /// <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
@@ -615,7 +704,7 @@ namespace NetCoreDbg
                 // 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.
@@ -649,16 +738,20 @@ namespace NetCoreDbg
                 }
 
                 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));
@@ -796,7 +889,7 @@ namespace NetCoreDbg
             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;
 
@@ -843,7 +936,7 @@ namespace NetCoreDbg
                 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;
 
@@ -936,7 +1029,7 @@ namespace NetCoreDbg
                 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;
 
@@ -1248,7 +1341,7 @@ namespace NetCoreDbg
                 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;
 
index c652ecc2f05809b0e3fc6ccc87326094c975c9d5..971983f5ef3ab8dda53fc532a2abdcd58cc4b215 100644 (file)
@@ -64,9 +64,10 @@ typedef  RetCode (*GetSequencePointByILOffsetDelegate)(PVOID, mdMethodDef, uint3
 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);
@@ -88,6 +89,7 @@ GetModuleMethodsRangesDelegate getModuleMethodsRangesDelegate = nullptr;
 ResolveBreakPointsDelegate resolveBreakPointsDelegate = nullptr;
 GetAsyncMethodSteppingInfoDelegate getAsyncMethodSteppingInfoDelegate = nullptr;
 GetSourceDelegate getSourceDelegate = nullptr;
+LoadDeltaPdbDelegate loadDeltaPdbDelegate = nullptr;
 GenerateStackMachineProgramDelegate generateStackMachineProgramDelegate = nullptr;
 ReleaseStackMachineProgramDelegate releaseStackMachineProgramDelegate = nullptr;
 NextStackCommandDelegate nextStackCommandDelegate = nullptr;
@@ -231,6 +233,7 @@ void Init(const std::string &coreClrPath)
         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)) &&
@@ -255,6 +258,7 @@ void Init(const std::string &coreClrPath)
                               resolveBreakPointsDelegate &&
                               getAsyncMethodSteppingInfoDelegate &&
                               getSourceDelegate &&
+                              loadDeltaPdbDelegate &&
                               generateStackMachineProgramDelegate &&
                               releaseStackMachineProgramDelegate &&
                               nextStackCommandDelegate &&
@@ -293,6 +297,7 @@ void Shutdown()
     resolveBreakPointsDelegate = nullptr;
     getAsyncMethodSteppingInfoDelegate = nullptr;
     getSourceDelegate = nullptr;
+    loadDeltaPdbDelegate = nullptr;
     stringToUpperDelegate = nullptr;
     coTaskMemAllocDelegate = nullptr;
     coTaskMemFreeDelegate = nullptr;
@@ -406,13 +411,13 @@ HRESULT GetModuleMethodsRanges(PVOID pSymbolReaderHandle, int32_t constrTokensNu
     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;
 }
 
@@ -573,6 +578,34 @@ HRESULT GetSource(PVOID symbolReaderHandle, std::string fileName, PVOID *data, i
     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
index 2d530e1f7c32dcc65799aaebdd191948dce2d26b..aee784d1916a3d798ad89404cc5eb422e3971980 100644 (file)
@@ -12,6 +12,7 @@
 #include <string>
 #include <vector>
 #include <functional>
+#include <unordered_set>
 
 
 namespace netcoredbg
@@ -93,9 +94,10 @@ namespace Interop
     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);
index a216d816a241cbd4f9efb3d3be98f37a31b5cd9f..51e37b9900d80d8c30dbcaa35f960a8ccfaeebae 100644 (file)
@@ -31,6 +31,9 @@ const char DebuggerAttribute::StepThrough[] = "System.Diagnostics.DebuggerStepTh
 // 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;
@@ -72,12 +75,9 @@ bool HasAttribute(IMetaDataImport *pMD, mdToken tok, std::vector<std::string> &a
 
 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;
@@ -92,7 +92,7 @@ static HRESULT GetNonJMCMethodsForTypeDef(
                                        nullptr, nullptr, nullptr, nullptr, nullptr)))
             continue;
 
-        if (HasAttribute(pMD, methodDef, attrNames))
+        if (HasAttribute(pMD, methodDef, methodAttrNames))
             excludeMethods.push_back(methodDef);
     }
     pMD->CloseEnum(fEnum);
@@ -100,7 +100,7 @@ static HRESULT GetNonJMCMethodsForTypeDef(
     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;
 
@@ -109,29 +109,23 @@ static HRESULT GetNonJMCClassesAndMethods(ICorDebugModule *pModule, PVOID pSymbo
     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)
@@ -155,7 +149,52 @@ HRESULT DisableJMCByAttributes(ICorDebugModule *pModule, PVOID pSymbolReaderHand
             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;
 }
 
index d53a2f6f15541110def3b92a7b1edb7a04bca16a..7a29b056e49f5fd07a6b714fdc11810e43d12bbd 100644 (file)
@@ -133,7 +133,7 @@ namespace
     }
 
 
-    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,
@@ -232,8 +232,11 @@ namespace
 
 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)
@@ -497,6 +500,11 @@ HRESULT Modules::GetFrameILAndSequencePoint(
     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));
 
@@ -517,7 +525,10 @@ HRESULT Modules::GetFrameILAndSequencePoint(
     }
 
     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(
@@ -534,6 +545,11 @@ 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));
 
@@ -543,7 +559,7 @@ HRESULT Modules::GetFrameILAndNextUserCodeILOffset(
     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)
@@ -560,6 +576,11 @@ HRESULT Modules::GetStepRangeFromCurrentIP(ICorDebugThread *pThread, COR_DEBUG_S
     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));
 
@@ -585,7 +606,10 @@ HRESULT Modules::GetStepRangeFromCurrentIP(ICorDebugThread *pThread, COR_DEBUG_S
         }
 
         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)
@@ -631,10 +655,11 @@ HRESULT GetModuleId(ICorDebugModule *pModule, std::string &id)
 }
 
 // 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())
@@ -648,10 +673,12 @@ HRESULT Modules::GetAsyncMethodSteppingInfo(CORDB_ADDRESS modAddress, mdMethodDe
     }
 
     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)
     {
@@ -660,6 +687,7 @@ HRESULT Modules::GetAsyncMethodSteppingInfo(CORDB_ADDRESS modAddress, mdMethodDe
 
     asyncMethodSteppingInfo.modAddress = modAddress;
     asyncMethodSteppingInfo.methodToken = methodToken;
+    asyncMethodSteppingInfo.methodVersion = methodVersion;
 
     return S_OK;
 }
@@ -667,11 +695,11 @@ HRESULT Modules::GetAsyncMethodSteppingInfo(CORDB_ADDRESS modAddress, mdMethodDe
 // 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.
@@ -680,11 +708,11 @@ bool Modules::IsMethodHaveAwait(CORDB_ADDRESS modAddress, mdMethodDef methodToke
 // [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)
@@ -711,11 +739,11 @@ bool Modules::FindNextAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef methodToke
 // [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;
@@ -790,7 +818,7 @@ HRESULT Modules::TryLoadModuleSymbols(ICorDebugModule *pModule, Module &module,
                 // * 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)
             {
@@ -825,6 +853,7 @@ HRESULT Modules::TryLoadModuleSymbols(ICorDebugModule *pModule, Module &module,
 HRESULT Modules::GetFrameNamedLocalVariable(
     ICorDebugModule *pModule,
     mdMethodDef methodToken,
+    ULONG32 methodVersion,
     ULONG localIndex,
     WSTRING &localName,
     ULONG32 *pIlStart,
@@ -846,7 +875,10 @@ HRESULT Modules::GetFrameNamedLocalVariable(
         }
 
         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;
@@ -857,6 +889,7 @@ HRESULT Modules::GetFrameNamedLocalVariable(
 HRESULT Modules::GetHoistedLocalScopes(
     ICorDebugModule *pModule,
     mdMethodDef methodToken,
+    ULONG32 methodVersion,
     PVOID *data,
     int32_t &hoistedLocalScopesCount)
 {
@@ -872,7 +905,10 @@ HRESULT Modules::GetHoistedLocalScopes(
     }
 
     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;
 }
 
@@ -886,7 +922,7 @@ HRESULT Modules::GetModuleWithName(const std::string &name, ICorDebugModule **pp
 
         std::string path = GetModuleFileName(mdInfo.m_iCorModule);
 
-        if (onlyWithPDB && !mdInfo.m_symbolReaderHandle)
+        if (onlyWithPDB && mdInfo.m_symbolReaderHandles.empty())
             continue;
 
         if (GetFileName(path) == name)
@@ -902,6 +938,7 @@ HRESULT Modules::GetModuleWithName(const std::string &name, ICorDebugModule **pp
 HRESULT Modules::GetNextSequencePointInMethod(
     ICorDebugModule *pModule,
     mdMethodDef methodToken,
+    ULONG32 methodVersion,
     ULONG32 ilOffset,
     ULONG32 &ilCloseOffset,
     bool *noUserCodeFound)
@@ -918,7 +955,10 @@ HRESULT Modules::GetNextSequencePointInMethod(
     }
 
     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(
@@ -946,6 +986,7 @@ HRESULT Modules::GetSequencePointByILOffset(
 HRESULT Modules::GetSequencePointByILOffset(
     CORDB_ADDRESS modAddress,
     mdMethodDef methodToken,
+    ULONG32 methodVersion,
     ULONG32 ilOffset,
     Modules::SequencePoint &sequencePoint)
 {
@@ -957,7 +998,10 @@ HRESULT Modules::GetSequencePointByILOffset(
     }
 
     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)
@@ -1240,7 +1284,7 @@ HRESULT Modules::ResolveBreakpoint(/*in*/ CORDB_ADDRESS modAddress, /*in*/ std::
         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.
 
@@ -1254,9 +1298,33 @@ HRESULT Modules::ResolveBreakpoint(/*in*/ CORDB_ADDRESS modAddress, /*in*/ std::
             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(&currentVersion)))
+            {
+                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)
         {
@@ -1275,6 +1343,40 @@ HRESULT Modules::ResolveBreakpoint(/*in*/ CORDB_ADDRESS modAddress, /*in*/ std::
     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);
@@ -1393,7 +1495,15 @@ HRESULT Modules::GetSource(ICorDebugModule *pModule, const std::string &sourcePa
     }
 
     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
index c7adeac039f4c96489632b792da3712d5c75d7cf..fca80fa077158f7f21c70e265b27bd55de85c879 100644 (file)
@@ -32,7 +32,8 @@ struct DebuggerAttribute
 
 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
 {
@@ -113,19 +114,23 @@ class Modules
 {
     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;
@@ -203,6 +208,7 @@ public:
 
     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);
 
@@ -246,6 +252,7 @@ public:
     HRESULT GetFrameNamedLocalVariable(
         ICorDebugModule *pModule,
         mdMethodDef methodToken,
+        ULONG32 methodVersion,
         ULONG localIndex,
         WSTRING &localName,
         ULONG32 *pIlStart,
@@ -254,25 +261,22 @@ public:
     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);
 
@@ -295,30 +299,37 @@ public:
         {};
     };
 
-    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