Fix line updates for methods.
authorMikhail Kurinnoi <m.kurinnoi@samsung.com>
Mon, 6 Feb 2023 14:16:15 +0000 (17:16 +0300)
committerGleb Balykov/Platform Lab /SRR/Staff Engineer/Samsung Electronics <g.balykov@samsung.com>
Wed, 15 Feb 2023 15:18:26 +0000 (18:18 +0300)
Will care about internal line updates for methods in case empty lines added in method's body.

src/debugger/breakpoints_func.cpp
src/managed/SymbolReader.cs
src/managed/interop.cpp
src/managed/interop.h
src/metadata/async_info.cpp
src/metadata/modules.cpp
src/metadata/modules.h
src/metadata/modules_sources.cpp
src/metadata/modules_sources.h
test-suite/MITestHotReloadBreakpoint/Program.cs

index 91e9fd9976a058615c5c2bb04ae3c741b7c7b4b3..36057a46546558d9bfda37cfcf65a5e122629d42 100644 (file)
@@ -273,9 +273,6 @@ HRESULT FuncBreakpoints::AddFuncBreakpoint(ManagedFuncBreakpoint &fbp, ResolvedF
         IfFailRet(pCode->CreateBreakpoint(ilNextOffset, &iCorFuncBreakpoint));\r
         IfFailRet(iCorFuncBreakpoint->Activate(fbp.enabled ? TRUE : FALSE));\r
 \r
-        CORDB_ADDRESS modAddress;\r
-        IfFailRet(entry.first->GetBaseAddress(&modAddress));\r
-\r
         fbp.funcBreakpoints.emplace_back(entry.second, currentVersion, iCorFuncBreakpoint.Detach());\r
     }\r
 \r
index bf7672179140c106e467590ea92d8c64aaa7ca00..ad2523fd587439c9aeb8ccb364672e0f2ecbb354 100644 (file)
@@ -391,6 +391,73 @@ namespace NetCoreDbg
             return RetCode.OK;
         }
 
+        /// <summary>
+        /// Get list of all sequence points for method.
+        /// </summary>
+        /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param>
+        /// <param name="methodToken">method token</param>
+        /// <param name="points">result - array of sequence points</param>
+        /// <param name="pointsCount">result - count of elements in array of sequence points</param>
+        /// <returns>"Ok" if information is available</returns>
+        private static RetCode GetSequencePoints(IntPtr symbolReaderHandle, int methodToken, out IntPtr points, out int pointsCount)
+        {
+            Debug.Assert(symbolReaderHandle != IntPtr.Zero);
+            var list = new List<DbgSequencePoint>();
+            pointsCount = 0;
+            points = IntPtr.Zero;
+
+            try
+            {
+                GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
+                MetadataReader reader = ((OpenedReader)gch.Target).Reader;
+
+                SequencePointCollection sequencePoints = GetSequencePointCollection(methodToken, reader);
+
+                foreach (SequencePoint p in sequencePoints)
+                {
+                    if (p.StartLine == 0 || p.StartLine == SequencePoint.HiddenLine)
+                        continue;
+
+                    string fileName = reader.GetString(reader.GetDocument(p.Document).Name);
+                    list.Add(new DbgSequencePoint()
+                    {
+                        document =  Marshal.StringToBSTR(fileName),
+                        startLine = p.StartLine,
+                        endLine = p.EndLine,
+                        startColumn = p.StartColumn,
+                        endColumn = p.EndColumn,
+                        offset = p.Offset
+                    });
+                }
+
+                if (list.Count == 0)
+                    return RetCode.Fail;
+
+                var structSize = Marshal.SizeOf<DbgSequencePoint>();
+                IntPtr allPoints = Marshal.AllocCoTaskMem(list.Count * structSize);
+                var currentPtr = allPoints;
+
+                foreach (var p in list)
+                {
+                    Marshal.StructureToPtr(p, currentPtr, false);
+                    currentPtr = currentPtr + structSize;
+                }
+
+                points = allPoints;
+                pointsCount = list.Count;
+            }
+            catch
+            {
+                foreach (var p in list)
+                {
+                    Marshal.FreeBSTR(p.document);
+                }
+                return RetCode.Exception;
+            }
+
+            return RetCode.OK;
+        }
+
         /// <summary>
         /// Find IL offset for next close user code sequence point by IL offset.
         /// </summary>
index 97c2a94dc525d19aef0f6ad5f7c59885d9016412..92f9bc91cdaea8933de11025bd5ff4f2b5e03790 100644 (file)
@@ -61,6 +61,7 @@ typedef  void (*DisposeDelegate)(PVOID);
 typedef  RetCode (*GetLocalVariableNameAndScope)(PVOID, int32_t, int32_t, BSTR*, uint32_t*, uint32_t*);
 typedef  RetCode (*GetHoistedLocalScopes)(PVOID, int32_t, PVOID*, int32_t*);
 typedef  RetCode (*GetSequencePointByILOffsetDelegate)(PVOID, mdMethodDef, uint32_t, PVOID);
+typedef  RetCode (*GetSequencePointsDelegate)(PVOID, mdMethodDef, PVOID*, int32_t*);
 typedef  RetCode (*GetNextUserCodeILOffsetDelegate)(PVOID, mdMethodDef, uint32_t, uint32_t*, int32_t*);
 typedef  RetCode (*GetStepRangesFromIPDelegate)(PVOID, int32_t, mdMethodDef, uint32_t*, uint32_t*);
 typedef  RetCode (*GetModuleMethodsRangesDelegate)(PVOID, uint32_t, PVOID, uint32_t, PVOID, PVOID*);
@@ -83,6 +84,7 @@ DisposeDelegate disposeDelegate = nullptr;
 GetLocalVariableNameAndScope getLocalVariableNameAndScopeDelegate = nullptr;
 GetHoistedLocalScopes getHoistedLocalScopesDelegate = nullptr;
 GetSequencePointByILOffsetDelegate getSequencePointByILOffsetDelegate = nullptr;
+GetSequencePointsDelegate getSequencePointsDelegate = nullptr;
 GetNextUserCodeILOffsetDelegate getNextUserCodeILOffsetDelegate = nullptr;
 GetStepRangesFromIPDelegate getStepRangesFromIPDelegate = nullptr;
 GetModuleMethodsRangesDelegate getModuleMethodsRangesDelegate = nullptr;
@@ -227,6 +229,7 @@ void Init(const std::string &coreClrPath)
         SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetLocalVariableNameAndScope", (void **)&getLocalVariableNameAndScopeDelegate)) &&
         SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetHoistedLocalScopes", (void **)&getHoistedLocalScopesDelegate)) &&
         SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetSequencePointByILOffset", (void **)&getSequencePointByILOffsetDelegate)) &&
+        SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetSequencePoints", (void **)&getSequencePointsDelegate)) &&
         SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetNextUserCodeILOffset", (void **)&getNextUserCodeILOffsetDelegate)) &&
         SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetStepRangesFromIP", (void **)&getStepRangesFromIPDelegate)) &&
         SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetModuleMethodsRanges", (void **)&getModuleMethodsRangesDelegate)) &&
@@ -252,6 +255,7 @@ void Init(const std::string &coreClrPath)
                               getLocalVariableNameAndScopeDelegate &&
                               getHoistedLocalScopesDelegate &&
                               getSequencePointByILOffsetDelegate &&
+                              getSequencePointsDelegate &&
                               getNextUserCodeILOffsetDelegate &&
                               getStepRangesFromIPDelegate &&
                               getModuleMethodsRangesDelegate &&
@@ -291,6 +295,7 @@ void Shutdown()
     getLocalVariableNameAndScopeDelegate = nullptr;
     getHoistedLocalScopesDelegate = nullptr;
     getSequencePointByILOffsetDelegate = nullptr;
+    getSequencePointsDelegate = nullptr;
     getNextUserCodeILOffsetDelegate = nullptr;
     getStepRangesFromIPDelegate = nullptr;
     getModuleMethodsRangesDelegate = nullptr;
@@ -318,6 +323,17 @@ HRESULT GetSequencePointByILOffset(PVOID pSymbolReaderHandle, mdMethodDef method
     return retCode == RetCode::OK ? S_OK : E_FAIL;
 }
 
+HRESULT GetSequencePoints(PVOID pSymbolReaderHandle, mdMethodDef methodToken, SequencePoint **sequencePoints, int32_t &Count)
+{
+    std::unique_lock<Utility::RWLock::Reader> read_lock(CLRrwlock.reader);
+    if (!getSequencePointsDelegate || !pSymbolReaderHandle)
+        return E_FAIL;
+
+    RetCode retCode = getSequencePointsDelegate(pSymbolReaderHandle, methodToken, (PVOID*)sequencePoints, &Count);
+
+    return retCode == RetCode::OK ? S_OK : E_FAIL;
+}
+
 HRESULT GetNextUserCodeILOffset(PVOID pSymbolReaderHandle, mdMethodDef methodToken, ULONG32 ilOffset, ULONG32 &ilNextOffset, bool *noUserCodeFound)
 {
     std::unique_lock<Utility::RWLock::Reader> read_lock(CLRrwlock.reader);
index 8b3d20b16a532c603390e55222d8160f0e5c7b88..736f8492c3ec8537b8a00066ef64919b8afc5823 100644 (file)
@@ -88,6 +88,7 @@ namespace Interop
                                       ULONG64 inMemoryPdbAddress, ULONG64 inMemoryPdbSize, VOID **ppSymbolReaderHandle);
     void DisposeSymbols(PVOID pSymbolReaderHandle);
     HRESULT GetSequencePointByILOffset(PVOID pSymbolReaderHandle, mdMethodDef MethodToken, ULONG32 IlOffset, SequencePoint *sequencePoint);
+    HRESULT GetSequencePoints(PVOID pSymbolReaderHandle, mdMethodDef MethodToken, SequencePoint **sequencePoints, int32_t &Count);
     HRESULT GetNextUserCodeILOffset(PVOID pSymbolReaderHandle, mdMethodDef MethodToken, ULONG32 IlOffset, ULONG32 &ilNextOffset, bool *noUserCodeFound);
     HRESULT GetNamedLocalVariableAndScope(PVOID pSymbolReaderHandle, mdMethodDef methodToken, ULONG localIndex,
                                           WCHAR *localName, ULONG localNameLen, ULONG32 *pIlStart, ULONG32 *pIlEnd);
index f71b60aeb82d1a77b7cfc89ea266aa87ba92beb1..8aab0ec5ebf882899cdfc4c48de3d5a9681a51e5 100644 (file)
@@ -21,7 +21,7 @@ HRESULT AsyncInfo::GetAsyncMethodSteppingInfo(CORDB_ADDRESS modAddress, mdMethod
     if (!asyncMethodSteppingInfo.awaits.empty())
         asyncMethodSteppingInfo.awaits.clear();
 
-    return m_sharedModules->GetModuleInfo(modAddress, [&](Modules::ModuleInfo &mdInfo) -> HRESULT
+    return m_sharedModules->GetModuleInfo(modAddress, [&](ModuleInfo &mdInfo) -> HRESULT
     {
         if (mdInfo.m_symbolReaderHandles.empty() || mdInfo.m_symbolReaderHandles.size() < methodVersion)
             return E_FAIL;
index 546bbc1438ec1d4a6fe7d3a58e618e333ea70e73..f7611b4ca93bb45f98f60e76b55afdd589b858f8 100644 (file)
@@ -17,7 +17,7 @@
 namespace netcoredbg
 {
 
-Modules::ModuleInfo::~ModuleInfo() noexcept
+ModuleInfo::~ModuleInfo() noexcept
 {
     for (auto symbolReaderHandle : m_symbolReaderHandles)
     {
index b9d74b490b345956626e560f73d015bd7c16eb26..47705fcb71580bf824bd3687a0bda094ce3bad14 100644 (file)
@@ -25,37 +25,37 @@ HRESULT GetModuleId(ICorDebugModule *pModule, std::string &id);
 std::string GetModuleFileName(ICorDebugModule *pModule);
 HRESULT IsModuleHaveSameName(ICorDebugModule *pModule, const std::string &Name, bool isFullPath);
 
-class Modules
+struct ModuleInfo
 {
-public:
+    std::vector<PVOID> m_symbolReaderHandles;
+    ToRelease<ICorDebugModule> m_iCorModule;
+    // Cache for LineUpdates data for all methods in this module (Hot Reload related).
+    method_block_updates_t m_methodBlockUpdates;
 
-    struct ModuleInfo
+    ModuleInfo(PVOID Handle, ICorDebugModule *Module) :
+        m_iCorModule(Module)
     {
-        std::vector<PVOID> m_symbolReaderHandles;
-        ToRelease<ICorDebugModule> m_iCorModule;
-        // Cache for LineUpdates data for all methods in this module (Hot Reload related).
-        method_block_updates_t m_methodBlockUpdates;
-
-        ModuleInfo(PVOID Handle, ICorDebugModule *Module) :
-            m_iCorModule(Module)
-        {
-            if (Handle == nullptr)
-                return;
-
-            m_symbolReaderHandles.reserve(1);
-            m_symbolReaderHandles.emplace_back(Handle);
-        }
-
-        ModuleInfo(ModuleInfo&& other) noexcept :
-            m_symbolReaderHandles(std::move(other.m_symbolReaderHandles)),
-            m_iCorModule(std::move(other.m_iCorModule))
-        {
-        }
-        ModuleInfo(const ModuleInfo&) = delete;
-        ModuleInfo& operator=(ModuleInfo&&) = delete;
-        ModuleInfo& operator=(const ModuleInfo&) = delete;
-        ~ModuleInfo() noexcept;
-    };
+        if (Handle == nullptr)
+            return;
+
+        m_symbolReaderHandles.reserve(1);
+        m_symbolReaderHandles.emplace_back(Handle);
+    }
+
+    ModuleInfo(ModuleInfo&& other) noexcept :
+        m_symbolReaderHandles(std::move(other.m_symbolReaderHandles)),
+        m_iCorModule(std::move(other.m_iCorModule))
+    {
+    }
+    ModuleInfo(const ModuleInfo&) = delete;
+    ModuleInfo& operator=(ModuleInfo&&) = delete;
+    ModuleInfo& operator=(const ModuleInfo&) = delete;
+    ~ModuleInfo() noexcept;
+};
+
+class Modules
+{
+public:
 
     struct SequencePoint {
         int32_t startLine;
index 4ef8ae6bcb5203567c6d2bc7f82bee5dfe7249c1..ef4ba4ec5ca2ea28b2c3f62108b2d07bdb8fa1c1 100644 (file)
@@ -359,60 +359,123 @@ HRESULT ModulesSources::FillSourcesCodeLinesForModule(ICorDebugModule *pModule,
     return S_OK;
 }
 
-static HRESULT LineUpdatesForMethodData(unsigned fullPathIndex, method_data_t &methodData, const std::vector<block_update_t> &blockUpdate, method_block_updates_t &methodBlockUpdates)
+HRESULT ModulesSources::LineUpdatesForMethodData(ICorDebugModule *pModule, unsigned fullPathIndex, method_data_t &methodData,
+                                                 const std::vector<block_update_t> &blockUpdate, ModuleInfo &mdInfo)
 {
+    int32_t startLineOffset = 0;
+    int32_t endLineOffset = 0;
+    std::unordered_map<std::size_t, int32_t> methodBlockOffsets;
+
     for (const auto &block : blockUpdate)
     {
         if (block.oldLine < 0 || block.endLineOffset < 0 || methodData.endLine < 0)
             return E_INVALIDARG;
 
+        const int32_t lineOffset = block.newLine - block.oldLine;
+
         // Note, endLineOffset could be std::numeric_limits<int32_t>::max() (max line number in C# source), so, we forced to cast it first.
         // Also, this is why we test that method within range and after that negate result.
-        if (!(methodData.startLine >= block.oldLine &&
-              (uint32_t)methodData.endLine <= (uint32_t)block.oldLine + (uint32_t)block.endLineOffset))
+        if ((uint32_t)methodData.startLine <= (uint32_t)block.oldLine + (uint32_t)block.endLineOffset && methodData.startLine >= block.oldLine)
+        {
+            startLineOffset = lineOffset;
+        }
+
+        if ((uint32_t)methodData.endLine <= (uint32_t)block.oldLine + (uint32_t)block.endLineOffset && methodData.endLine >= block.oldLine)
+        {
+            endLineOffset = lineOffset;
+        }
+
+        if ((uint32_t)methodData.startLine > (uint32_t)block.oldLine + (uint32_t)block.endLineOffset || methodData.endLine < block.oldLine)
             continue;
 
-        const int32_t codeLineOffset = block.newLine - block.oldLine;
+        // update methodBlockUpdates
 
-        auto updateCashe = [&]() -> bool
+        auto findMethod = mdInfo.m_methodBlockUpdates.find(methodData.methodDef);
+        if (findMethod == mdInfo.m_methodBlockUpdates.end())
         {
-            auto findMethod = methodBlockUpdates.find(methodData.methodDef);
-            if (findMethod == methodBlockUpdates.end())
-                return false;
-
-            for (auto &entry : findMethod->second)
+            HRESULT Status;
+            ToRelease<ICorDebugFunction> iCorFunction;
+            IfFailRet(pModule->GetFunctionFromToken(methodData.methodDef, &iCorFunction));
+            ToRelease<ICorDebugFunction2> iCorFunction2;
+            IfFailRet(iCorFunction->QueryInterface(IID_ICorDebugFunction2, (LPVOID*) &iCorFunction2));
+            ULONG32 methodVersion;
+            IfFailRet(iCorFunction2->GetVersionNumber(&methodVersion));
+
+            if (mdInfo.m_symbolReaderHandles.empty() || mdInfo.m_symbolReaderHandles.size() < methodVersion)
+                return E_FAIL;
+
+            Interop::SequencePoint *sequencePoints = nullptr;
+            int32_t count = 0;
+            if (FAILED(Status = Interop::GetSequencePoints(mdInfo.m_symbolReaderHandles[methodVersion - 1], methodData.methodDef, &sequencePoints, count)))
             {
-                if (fullPathIndex == entry.fullPathIndex &&
-                    methodData.startLine == entry.newLine &&
-                    methodData.endLine == entry.newLine + entry.endLineOffset)
+                if (sequencePoints)
                 {
-                    // All we need for previous stored data is change newLine, since oldLine will be the same (PDB for this method version was not changed).
-                    entry.newLine += codeLineOffset;
-                    return true;
+                    for (int i = 0; i < count; i++)
+                    {
+                        Interop::SysFreeString(sequencePoints[i].document);
+                    }
+                    Interop::CoTaskMemFree(sequencePoints);
                 }
+
+                return Status;
             }
-            return false;
-        };
-        if (!updateCashe())
-            methodBlockUpdates[methodData.methodDef].emplace_back(fullPathIndex, methodData.startLine + codeLineOffset, methodData.startLine, methodData.endLine - methodData.startLine);
 
-        methodData.startLine += codeLineOffset;
-        methodData.endLine += codeLineOffset;
+            for (int i = 0; i < count; i++)
+            {
+                unsigned index;
+                IfFailRet(GetFullPathIndex(sequencePoints[i].document, index));
+                Interop::SysFreeString(sequencePoints[i].document);
+
+                mdInfo.m_methodBlockUpdates[methodData.methodDef].emplace_back(index, sequencePoints[i].startLine, sequencePoints[i].startLine, sequencePoints[i].endLine - sequencePoints[i].startLine);
+
+            }
+
+            if (sequencePoints)
+                Interop::CoTaskMemFree(sequencePoints);
+        }
+
+        for (std::size_t i = 0; i < mdInfo.m_methodBlockUpdates[methodData.methodDef].size(); ++i)
+        {
+            auto &entry = mdInfo.m_methodBlockUpdates[methodData.methodDef][i];
+
+            if (entry.fullPathIndex != fullPathIndex ||
+                entry.newLine < block.oldLine ||
+                (uint32_t)entry.newLine + (uint32_t)entry.endLineOffset > (uint32_t)block.oldLine + (uint32_t)block.endLineOffset)
+                continue;
+
+            // Line updates file can have only one entry for each line.
+            methodBlockOffsets[i] = lineOffset;
+        }
+    }
+
+    if (!methodBlockOffsets.empty())
+    {
+        auto findMethod = mdInfo.m_methodBlockUpdates.find(methodData.methodDef);
+        assert(findMethod != mdInfo.m_methodBlockUpdates.end());
 
-        break;
+        for (const auto &entry : methodBlockOffsets)
+        {
+            // All we need for previous stored data is change newLine, since oldLine will be the same (PDB for this method version was not changed).
+            findMethod->second[entry.first].newLine += entry.second;
+        }
     }
 
+    if (startLineOffset == 0 && endLineOffset == 0)
+        return S_OK;
+
+    methodData.startLine += startLineOffset;
+    methodData.endLine += endLineOffset;
     return S_OK;
 }
 
 HRESULT ModulesSources::UpdateSourcesCodeLinesForModule(ICorDebugModule *pModule, IMetaDataImport *pMDImport, std::unordered_set<mdMethodDef> methodTokens,
-                                                        src_block_updates_t &srcBlockUpdates, PVOID pSymbolReaderHandle, method_block_updates_t &methodBlockUpdates)
+                                                        src_block_updates_t &srcBlockUpdates, ModuleInfo &mdInfo)
 {
     std::lock_guard<std::mutex> lock(m_sourcesInfoMutex);
 
     HRESULT Status;
     std::unique_ptr<module_methods_data_t, module_methods_data_t_deleter> inputData;
-    IfFailRet(GetPdbMethodsRanges(pMDImport, pSymbolReaderHandle, &methodTokens, inputData));
+    IfFailRet(GetPdbMethodsRanges(pMDImport, mdInfo.m_symbolReaderHandles.back(), &methodTokens, inputData));
 
     struct src_update_data_t
     {
@@ -467,7 +530,7 @@ HRESULT ModulesSources::UpdateSourcesCodeLinesForModule(ICorDebugModule *pModule
             {
                 inputMetodDefSet.insert(updateData.second.methodsData[j].methodDef);
                 // All sequence points related to this method were updated and provide proper lines from PDB directly.
-                methodBlockUpdates.erase(updateData.second.methodsData[j].methodDef);
+                mdInfo.m_methodBlockUpdates.erase(updateData.second.methodsData[j].methodDef);
             }
 
             // Move multiMethodsData first (since this is constructors and all will be on level 0 for sure).
@@ -492,7 +555,7 @@ HRESULT ModulesSources::UpdateSourcesCodeLinesForModule(ICorDebugModule *pModule
             tmpFileMethodsData.multiMethodsData.clear();
             for (auto &methodData : tmpMultiMethodsData)
             {
-                IfFailRet(LineUpdatesForMethodData(fullPathIndex, methodData, updateData.second.blockUpdate, methodBlockUpdates));
+                IfFailRet(LineUpdatesForMethodData(pModule, fullPathIndex, methodData, updateData.second.blockUpdate, mdInfo));
                 AddMethodData(inputMethodsData, tmpFileMethodsData.multiMethodsData, methodData, 0);
             }
 
@@ -504,7 +567,7 @@ HRESULT ModulesSources::UpdateSourcesCodeLinesForModule(ICorDebugModule *pModule
                     auto findData = inputMetodDefSet.find(methodData.methodDef);
                     if (findData == inputMetodDefSet.end())
                     {
-                        IfFailRet(LineUpdatesForMethodData(fullPathIndex, methodData, updateData.second.blockUpdate, methodBlockUpdates));
+                        IfFailRet(LineUpdatesForMethodData(pModule, fullPathIndex, methodData, updateData.second.blockUpdate, mdInfo));
                         AddMethodData(inputMethodsData, tmpFileMethodsData.multiMethodsData, methodData, 0);
                     }
                 }
@@ -623,6 +686,9 @@ HRESULT ModulesSources::ResolveRelativeSourceFileName(std::string &filename)
     return E_FAIL;
 }
 
+// Note, this is breakpoint only backward correction, that will care for "closest next executable code line" in PDB stored data.
+// We can't map line from new to old PDB location, since impossible map new added line data to PDB data that don't have this line.
+// Plus, methodBlockUpdates store sequence points only data.
 static void LineUpdatesBackwardCorrection(unsigned fullPathIndex, mdMethodDef methodToken, method_block_updates_t &methodBlockUpdates, int32_t &startLine)
 {
     auto findSourceUpdate = methodBlockUpdates.find(methodToken);
@@ -631,11 +697,10 @@ static void LineUpdatesBackwardCorrection(unsigned fullPathIndex, mdMethodDef me
         for (const auto &entry : findSourceUpdate->second)
         {
             if (entry.fullPathIndex != fullPathIndex ||
-                entry.newLine > startLine ||
                 entry.newLine + entry.endLineOffset < startLine)
                 continue;
 
-            startLine += entry.oldLine - entry.newLine;
+            startLine = entry.oldLine; // <- closest executable code line for requested line in old PDB data
             break;
         }
     }
@@ -704,7 +769,7 @@ HRESULT ModulesSources::ResolveBreakpoint(/*in*/ Modules *pModules, /*in*/ CORDB
             return E_FAIL;
         }
 
-        Modules::ModuleInfo *pmdInfo; // Note, pmdInfo must be covered by m_modulesInfoMutex.
+        ModuleInfo *pmdInfo; // Note, pmdInfo must be covered by m_modulesInfoMutex.
         IfFailRet(pModules->GetModuleInfo(sourceData.modAddress, &pmdInfo)); // we must have it, since we loaded data from it
         if (pmdInfo->m_symbolReaderHandles.empty())
             continue;
@@ -822,7 +887,8 @@ static HRESULT LoadLineUpdatesFile(ModulesSources *pModulesSources, const std::s
         {
             if (lineUpdates.newLine != lineUpdates.oldLine)
             {
-                assert(startBlock.newLine == empty);
+                if (startBlock.newLine != empty)
+                    srcBlockUpdates[updateFileData.first].emplace_back(startBlock.newLine + 1, startBlock.oldLine + 1, lineUpdates.oldLine - 1 - startBlock.oldLine);
 
                 startBlock.newLine = lineUpdates.newLine;
                 startBlock.oldLine = lineUpdates.oldLine;
@@ -851,7 +917,7 @@ HRESULT ModulesSources::ApplyPdbDeltaAndLineUpdates(Modules *pModules, ICorDebug
     CORDB_ADDRESS modAddress;
     IfFailRet(pModule->GetBaseAddress(&modAddress));
 
-    return pModules->GetModuleInfo(modAddress, [&](Modules::ModuleInfo &mdInfo) -> HRESULT
+    return pModules->GetModuleInfo(modAddress, [&](ModuleInfo &mdInfo) -> HRESULT
     {
         if (mdInfo.m_symbolReaderHandles.empty())
             return E_FAIL; // Deltas could be applied for already loaded modules with PDB only.
@@ -875,7 +941,7 @@ HRESULT ModulesSources::ApplyPdbDeltaAndLineUpdates(Modules *pModules, ICorDebug
         ToRelease<IMetaDataImport> pMDImport;
         IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMDImport));
 
-        return UpdateSourcesCodeLinesForModule(pModule, pMDImport, methodTokens, srcBlockUpdates, pSymbolReaderHandle, mdInfo.m_methodBlockUpdates);
+        return UpdateSourcesCodeLinesForModule(pModule, pMDImport, methodTokens, srcBlockUpdates, mdInfo);
     });
 }
 
index f92448f8d83723095c1238c095eb9f27bf690112..9ca6fba565a46fccc9f9b888104e67f3b5171187 100644 (file)
@@ -126,6 +126,7 @@ void LineUpdatesForwardCorrection(unsigned fullPathIndex, mdMethodDef methodToke
 }
 
 class Modules;
+struct ModuleInfo;
 
 class ModulesSources
 {
@@ -191,8 +192,10 @@ private:
 
     HRESULT GetFullPathIndex(BSTR document, unsigned &fullPathIndex);
     HRESULT UpdateSourcesCodeLinesForModule(ICorDebugModule *pModule, IMetaDataImport *pMDImport, std::unordered_set<mdMethodDef> methodTokens,
-                                            src_block_updates_t &blockUpdates, PVOID pSymbolReaderHandle, method_block_updates_t &methodBlockUpdates);
+                                            src_block_updates_t &blockUpdates, ModuleInfo &mdInfo);
     HRESULT ResolveRelativeSourceFileName(std::string &filename);
+    HRESULT LineUpdatesForMethodData(ICorDebugModule *pModule, unsigned fullPathIndex, method_data_t &methodData,
+                                     const std::vector<block_update_t> &blockUpdate, ModuleInfo &mdInfo);
 
 #ifdef WIN32
     // on Windows OS, all files names converted to uppercase in containers above, but this vector hold initial full path names
index 5502eddab3295acb5c2ce72e33ae8b1c0cc480f2..952eb2a3e8bd9e6ba49ace9a88d3825276496eb7 100644 (file)
@@ -306,7 +306,7 @@ namespace MITestHotReloadBreakpoint
                 // Setup breakpoints before apply delta.\r
                 Context.EnableBreakpoint(@"__FILE__:__LINE__", @"Program.cs", 16);\r
                 Context.EnableBreakpoint(@"__FILE__:__LINE__", @"Program.cs", 19);\r
-                Context.EnableBreakpoint(@"__FILE__:__LINE__", @"Program.cs", 25);\r
+                Context.EnableBreakpoint(@"__FILE__:__LINE__", @"Program.cs", 27);\r
                 Context.EnableFuncBreakpoint(@"__FILE__:__LINE__", "Program.HotReloadBreakpointTest1");\r
                 Context.EnableFuncBreakpoint(@"__FILE__:__LINE__", "Program.HotReloadBreakpointTest2");\r
 \r
@@ -335,13 +335,97 @@ namespace MITestHotReloadBreakpoint
                         static void HotReloadBreakpointTest2()\r
                         {\r
                             Console.WriteLine(""Another added string."");\r
-                            Console.WriteLine(""One more added string."");                                   // line 25\r
+                            int i=0;\r
+                            i++;\r
+                            Console.WriteLine(""One more added string."");                                   // line 27\r
                         }\r
                     }\r
                 }", @"Program.cs");\r
                 Context.WriteDeltas(@"__FILE__:__LINE__", "tmp_delta2");\r
                 Context.ApplyDeltas(@"__FILE__:__LINE__", "tmp_delta2");\r
 \r
+                Context.GetDelta(@"__FILE__:__LINE__",\r
+                @"using System;\r
+                namespace TestAppHotReload\r
+                {\r
+                    class Program\r
+                    {\r
+                        static void Main(string[] args)\r
+                        {\r
+                            Console.WriteLine(""Hello World!"");\r
+                            HotReloadTest();\r
+                        }\r
+                        static void HotReloadTest()\r
+                        {\r
+                            Console.WriteLine(""Updated string."");\r
+                            HotReloadBreakpointTest1();\r
+                            HotReloadBreakpointTest1();                                                      // line 15\r
+                        }\r
+                        static void HotReloadBreakpointTest1()\r
+                        {\r
+                            Console.WriteLine(""Updated added string."");                                    // line 19\r
+                            HotReloadBreakpointTest2();\r
+                        }\r
+\r
+                        static void HotReloadBreakpointTest2()\r
+                        {\r
+                            Console.WriteLine(""Another added string."");\r
+                            int i=0;\r
+\r
+                            i++;\r
+\r
+\r
+                            Console.WriteLine(""One more added string."");\r
+                        }\r
+                    }\r
+                }", @"Program.cs");\r
+                Context.WriteDeltas(@"__FILE__:__LINE__", "tmp_delta3");\r
+                Context.ApplyDeltas(@"__FILE__:__LINE__", "tmp_delta3");\r
+\r
+\r
+\r
+                Context.GetDelta(@"__FILE__:__LINE__",\r
+                @"using System;\r
+                namespace TestAppHotReload\r
+                {\r
+                    class Program\r
+                    {\r
+                        static void Main(string[] args)\r
+                        {\r
+                            Console.WriteLine(""Hello World!"");\r
+                            HotReloadTest();\r
+                        }\r
+                        static void HotReloadTest()\r
+                        {\r
+                            Console.WriteLine(""Updated string."");\r
+                            HotReloadBreakpointTest1();\r
+                            HotReloadBreakpointTest1();                                                      // line 15\r
+                        }\r
+                        static void HotReloadBreakpointTest1()\r
+                        {\r
+                            Console.WriteLine(""Updated added string."");                                    // line 19\r
+                            HotReloadBreakpointTest2();\r
+                        }\r
+\r
+\r
+                        static void HotReloadBreakpointTest2()\r
+                        {\r
+                            Console.WriteLine(""Another added string."");\r
+\r
+\r
+                            int i=0;\r
+                            i++;\r
+\r
+\r
+                            Console.WriteLine(""One more added string."");                                   // line 33\r
+                        }\r
+                    }\r
+                }", @"Program.cs");\r
+                Context.WriteDeltas(@"__FILE__:__LINE__", "tmp_delta4");\r
+                Context.ApplyDeltas(@"__FILE__:__LINE__", "tmp_delta4");\r
+\r
+\r
+\r
                 Context.Continue(@"__FILE__:__LINE__");\r
             });\r
 \r
@@ -359,13 +443,13 @@ namespace MITestHotReloadBreakpoint
 \r
             Label.Checkpoint("bp_test_func_added", "bp_test_line_added", (Object context) => {\r
                 Context Context = (Context)context;\r
-                Context.WasBreakpointHit(@"__FILE__:__LINE__", @"Program.cs", 23); // Program.HotReloadBreakpointTest2\r
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", @"Program.cs", 25); // Program.HotReloadBreakpointTest2\r
                 Context.Continue(@"__FILE__:__LINE__");\r
             });\r
 \r
             Label.Checkpoint("bp_test_line_added", "bp_test_line_not_changed", (Object context) => {\r
                 Context Context = (Context)context;\r
-                Context.WasBreakpointHit(@"__FILE__:__LINE__", @"Program.cs", 25);\r
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", @"Program.cs", 29);\r
                 Context.Continue(@"__FILE__:__LINE__");\r
             });\r
 \r