return E_FAIL;\r
\r
// Note, in case of async `MoveNext` method, user code don't start from 0 IL offset.\r
- ULONG32 ilCloseOffset;\r
+ ULONG32 ilNextOffset;\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
+ IfFailRet(pModules->GetNextUserCodeILOffsetInMethod(pModule, resultToken, currentVersion, 0, ilNextOffset));\r
\r
entryPointToken = resultToken;\r
- entryPointOffset = ilCloseOffset;\r
+ entryPointOffset = ilNextOffset;\r
return S_OK;\r
}\r
\r
ULONG32 currentVersion; // Note, new breakpoints could be setup for last code version only, since protocols (MI, VSCode, ...) provide method name (sig) only.\r
IfFailRet(pFunc->GetCurrentVersionNumber(¤tVersion));\r
\r
- ULONG32 ilCloseOffset;\r
- if (FAILED(m_sharedModules->GetNextSequencePointInMethod(entry.first, entry.second, currentVersion, 0, ilCloseOffset)))\r
+ ULONG32 ilNextOffset;\r
+ if (FAILED(m_sharedModules->GetNextUserCodeILOffsetInMethod(entry.first, entry.second, currentVersion, 0, ilNextOffset)))\r
return S_OK;\r
\r
ToRelease<ICorDebugCode> pCode;\r
IfFailRet(pFunc->GetILCode(&pCode));\r
\r
ToRelease<ICorDebugFunctionBreakpoint> iCorFuncBreakpoint;\r
- IfFailRet(pCode->CreateBreakpoint(ilCloseOffset, &iCorFuncBreakpoint));\r
+ IfFailRet(pCode->CreateBreakpoint(ilNextOffset, &iCorFuncBreakpoint));\r
IfFailRet(iCorFuncBreakpoint->Activate(fbp.enabled ? TRUE : FALSE));\r
\r
CORDB_ADDRESS modAddress;\r
static HRESULT ApplyMetadataAndILDeltas(Modules *pModules, const std::string &dllFileName, const std::string &deltaMD, const std::string &deltaIL)
{
- HRESULT Status = S_OK;
+ HRESULT Status;
std::ifstream deltaILFileStream(deltaIL, std::ios::in | std::ios::binary | std::ios::ate);
std::ifstream deltaMDFileStream(deltaMD, std::ios::in | std::ios::binary | std::ios::ate);
- if (deltaILFileStream.is_open() && deltaMDFileStream.is_open())
- {
- auto deltaILSize = deltaILFileStream.tellg();
- if (deltaILSize < 0)
- return E_FAIL;
- BYTE *deltaILMemBlock = new BYTE[(size_t)deltaILSize];
- deltaILFileStream.seekg(0, std::ios::beg);
- deltaILFileStream.read((char*)deltaILMemBlock, deltaILSize);
+ if (!deltaILFileStream.is_open() || !deltaMDFileStream.is_open())
+ return COR_E_FILENOTFOUND;
- auto deltaMDSize = deltaMDFileStream.tellg();
- if (deltaMDSize < 0)
- return E_FAIL;
- BYTE *deltaMDMemBlock = new BYTE[(size_t)deltaMDSize];
- deltaMDFileStream.seekg(0, std::ios::beg);
- deltaMDFileStream.read((char*)deltaMDMemBlock, deltaMDSize);
-
- ToRelease<ICorDebugModule> pModule;
- IfFailRet(pModules->GetModuleWithName(dllFileName, &pModule, true));
- ToRelease<ICorDebugModule2> pModule2;
- IfFailRet(pModule->QueryInterface(IID_ICorDebugModule2, (LPVOID *)&pModule2));
- IfFailRet(pModule2->ApplyChanges((ULONG)deltaMDSize, deltaMDMemBlock, (ULONG)deltaILSize, deltaILMemBlock));
- }
- else
- {
- Status = COR_E_FILENOTFOUND;
- }
+ auto deltaILSize = deltaILFileStream.tellg();
+ if (deltaILSize < 0)
+ return E_FAIL;
+ std::unique_ptr<BYTE[]> deltaILMemBlock(new BYTE[(size_t)deltaILSize]);
+ deltaILFileStream.seekg(0, std::ios::beg);
+ deltaILFileStream.read((char*)deltaILMemBlock.get(), deltaILSize);
- if (deltaILFileStream.is_open())
- deltaILFileStream.close();
- if (deltaMDFileStream.is_open())
- deltaMDFileStream.close();
+ auto deltaMDSize = deltaMDFileStream.tellg();
+ if (deltaMDSize < 0)
+ return E_FAIL;
+ std::unique_ptr<BYTE[]> deltaMDMemBlock(new BYTE[(size_t)deltaMDSize]);
+ deltaMDFileStream.seekg(0, std::ios::beg);
+ deltaMDFileStream.read((char*)deltaMDMemBlock.get(), deltaMDSize);
- return Status;
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pModules->GetModuleWithName(dllFileName, &pModule, true));
+ ToRelease<ICorDebugModule2> pModule2;
+ IfFailRet(pModule->QueryInterface(IID_ICorDebugModule2, (LPVOID *)&pModule2));
+ IfFailRet(pModule2->ApplyChanges((ULONG)deltaMDSize, deltaMDMemBlock.get(), (ULONG)deltaILSize, deltaILMemBlock.get()));
+
+ return S_OK;
}
-HRESULT ManagedDebugger::ApplyPdbDelta(const std::string &dllFileName, const std::string &deltaPDB)
+HRESULT ManagedDebugger::ApplyPdbDeltaAndLineUpdates(const std::string &dllFileName, const std::string &deltaPDB, const std::string &lineUpdates)
{
HRESULT Status;
ToRelease<ICorDebugModule> pModule;
IfFailRet(m_sharedModules->GetModuleWithName(dllFileName, &pModule, true));
std::unordered_set<mdMethodDef> pdbMethodTokens;
- IfFailRet(m_sharedModules->ApplyPdbDelta(pModule, m_justMyCode, deltaPDB, pdbMethodTokens));
+ IfFailRet(m_sharedModules->ApplyPdbDeltaAndLineUpdates(pModule, m_justMyCode, deltaPDB, lineUpdates, pdbMethodTokens));
// Since we could have new code lines and new methods added, check all breakpoints again.
std::vector<BreakpointEvent> events;
return S_OK;
}
-HRESULT ManagedDebugger::HotReloadApplyDeltas(const std::string &dllFileName, const std::string &deltaMD, const std::string &deltaIL, const std::string &deltaPDB)
+HRESULT ManagedDebugger::HotReloadApplyDeltas(const std::string &dllFileName, const std::string &deltaMD, const std::string &deltaIL,
+ const std::string &deltaPDB, const std::string &lineUpdates)
{
LogFuncEntry();
IfFailRet(m_iCorProcess->Stop(0));
IfFailRet(ApplyMetadataAndILDeltas(m_sharedModules.get(), dllFileName, deltaMD, deltaIL));
- IfFailRet(ApplyPdbDelta(dllFileName, deltaPDB));
+ IfFailRet(ApplyPdbDeltaAndLineUpdates(dllFileName, deltaPDB, lineUpdates));
if (procRunning == TRUE)
IfFailRet(m_iCorProcess->Continue(0));
HRESULT GetExceptionInfo(ThreadId threadId, ExceptionInfo &exceptionInfo) override;
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;
+ HRESULT HotReloadApplyDeltas(const std::string &dllFileName, const std::string &deltaMD, const std::string &deltaIL,
+ const std::string &deltaPDB, const std::string &lineUpdates) override;
+ HRESULT ApplyPdbDeltaAndLineUpdates(const std::string &dllFileName, const std::string &deltaPDB, const std::string &lineUpdates) override;
void FindFileNames(string_view pattern, unsigned limit, SearchCallback) override;
void FindFunctions(string_view pattern, unsigned limit, SearchCallback) override;
\r
// Same behaviour as MS vsdbg and MSVS C# debugger have - step only for code with PDB loaded (no matter JMC enabled or not by user).\r
ULONG32 ipOffset;\r
- ULONG32 ilCloseUserCodeOffset;\r
+ ULONG32 ilNextUserCodeOffset;\r
bool noUserCodeFound = false; // Must be initialized with `false`, since GetFrameILAndNextUserCodeILOffset call could be failed before delegate call.\r
- if (SUCCEEDED(Status = m_sharedModules->GetFrameILAndNextUserCodeILOffset(iCorFrame, ipOffset, ilCloseUserCodeOffset, &noUserCodeFound)))\r
+ if (SUCCEEDED(Status = m_sharedModules->GetFrameILAndNextUserCodeILOffset(iCorFrame, ipOffset, ilNextUserCodeOffset, &noUserCodeFound)))\r
{\r
// Current IL offset less than IL offset of next close user code line.\r
- if (ipOffset < ilCloseUserCodeOffset)\r
+ if (ipOffset < ilNextUserCodeOffset)\r
{\r
IfFailRet(m_simpleStepper->SetupStep(pThread, IDebugger::StepType::STEP_OVER));\r
return S_OK;\r
virtual HRESULT GetExceptionInfo(ThreadId threadId, ExceptionInfo &exceptionInfo) = 0;
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;
+ virtual HRESULT HotReloadApplyDeltas(const std::string &dllFileName, const std::string &deltaMD, const std::string &deltaIL,
+ const std::string &deltaPDB, const std::string &lineUpdates) = 0;
+ virtual HRESULT ApplyPdbDeltaAndLineUpdates(const std::string &dllFileName, const std::string &deltaPDB, const std::string &lineUpdates) = 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;
/// <param name="sequencePoint">sequence point return</param>
/// <param name="noUserCodeFound">return 1 in case all sequence points checked and no user code was found, otherwise return 0</param>
/// <returns>"Ok" if information is available</returns>
- private static RetCode GetNextSequencePointByILOffset(IntPtr symbolReaderHandle, int methodToken, uint ilOffset, out uint ilCloseOffset, out int noUserCodeFound)
+ private static RetCode GetNextUserCodeILOffset(IntPtr symbolReaderHandle, int methodToken, uint ilOffset, out uint ilNextOffset, out int noUserCodeFound)
{
Debug.Assert(symbolReaderHandle != IntPtr.Zero);
- ilCloseOffset = 0;
+ ilNextOffset = 0;
noUserCodeFound = 0;
try
if (point.Offset >= ilOffset)
{
- ilCloseOffset = (uint)point.Offset;
+ ilNextOffset = (uint)point.Offset;
return RetCode.OK;
}
}
startColumn = startColumn_;
endColumn = endColumn_;
}
- public void SetRangeEnd(int endLine_, int endColumn_)
- {
- endLine = endLine_;
- endColumn = endColumn_;
- }
public void ExtendRange(int startLine_, int endLine_, int startColumn_, int endColumn_)
{
if (startLine > startLine_)
{
int methodToken = Marshal.ReadInt32(constrTokens, i);
method_data_t currentData = new method_data_t(methodToken, 0, 0, 0, 0);
- DocumentHandle currentDocHandle = new DocumentHandle();
foreach (SequencePoint p in GetSequencePointCollection(methodToken, reader))
{
if (p.StartLine == 0 || p.StartLine == SequencePoint.HiddenLine)
continue;
- if (currentData.startLine == 0)
- {
- currentData.SetRange(p.StartLine, p.EndLine, p.StartColumn, p.EndColumn);
- currentDocHandle = p.Document;
- }
- // same segment only in case same file and on next line or on same line but on the right
- else if ((p.StartLine == currentData.endLine + 1 ||
- (p.StartLine == currentData.endLine && p.StartColumn > currentData.endColumn)) &&
- currentDocHandle == p.Document )
- {
- currentData.SetRangeEnd(p.EndLine, p.EndColumn);
- }
- else // SequencePoint from another segment
- {
- if (!ModuleData.ContainsKey(currentDocHandle))
- ModuleData[currentDocHandle] = new List<method_data_t>();
-
- ModuleData[currentDocHandle].Add(currentData);
- currentData.SetRange(p.StartLine, p.EndLine, p.StartColumn, p.EndColumn);
- currentDocHandle = p.Document;
- }
- }
+ if (!ModuleData.ContainsKey(p.Document))
+ ModuleData[p.Document] = new List<method_data_t>();
- if (currentData.startLine != 0)
- {
- if (!ModuleData.ContainsKey(currentDocHandle))
- ModuleData[currentDocHandle] = new List<method_data_t>();
-
- ModuleData[currentDocHandle].Add(currentData);
+ currentData.SetRange(p.StartLine, p.EndLine, p.StartColumn, p.EndColumn);
+ ModuleData[p.Document].Add(currentData);
}
}
/// <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 symbolReaderHandles, 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, [MarshalAs(UnmanagedType.LPWStr)] string sourcePath, out IntPtr data)
{
Debug.Assert(symbolReaderHandles != IntPtr.Zero);
Count = 0;
if (p.StartLine == 0 || p.StartLine == SequencePoint.HiddenLine || p.EndLine < sourceLine)
continue;
+ // Note, in case of constructors, we must care about source too, since we may have situation when field/property have same line in another source.
+ var fileName = reader.GetString(reader.GetDocument(p.Document).Name);
+ if (fileName != sourcePath)
+ continue;
+
// first access, assign to first user code sequence point
if (nearestSP.StartLine == 0)
{
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 (*GetNextSequencePointByILOffsetDelegate)(PVOID, mdMethodDef, uint32_t, uint32_t*, 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*);
-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*, const WCHAR*, 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*);
GetLocalVariableNameAndScope getLocalVariableNameAndScopeDelegate = nullptr;
GetHoistedLocalScopes getHoistedLocalScopesDelegate = nullptr;
GetSequencePointByILOffsetDelegate getSequencePointByILOffsetDelegate = nullptr;
-GetNextSequencePointByILOffsetDelegate getNextSequencePointByILOffsetDelegate = nullptr;
+GetNextUserCodeILOffsetDelegate getNextUserCodeILOffsetDelegate = nullptr;
GetStepRangesFromIPDelegate getStepRangesFromIPDelegate = nullptr;
GetModuleMethodsRangesDelegate getModuleMethodsRangesDelegate = nullptr;
ResolveBreakPointsDelegate resolveBreakPointsDelegate = nullptr;
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, "GetNextSequencePointByILOffset", (void **)&getNextSequencePointByILOffsetDelegate)) &&
+ 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)) &&
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "ResolveBreakPoints", (void **)&resolveBreakPointsDelegate)) &&
getLocalVariableNameAndScopeDelegate &&
getHoistedLocalScopesDelegate &&
getSequencePointByILOffsetDelegate &&
- getNextSequencePointByILOffsetDelegate &&
+ getNextUserCodeILOffsetDelegate &&
getStepRangesFromIPDelegate &&
getModuleMethodsRangesDelegate &&
resolveBreakPointsDelegate &&
getLocalVariableNameAndScopeDelegate = nullptr;
getHoistedLocalScopesDelegate = nullptr;
getSequencePointByILOffsetDelegate = nullptr;
- getNextSequencePointByILOffsetDelegate = nullptr;
+ getNextUserCodeILOffsetDelegate = nullptr;
getStepRangesFromIPDelegate = nullptr;
getModuleMethodsRangesDelegate = nullptr;
resolveBreakPointsDelegate = nullptr;
return retCode == RetCode::OK ? S_OK : E_FAIL;
}
-HRESULT GetNextSequencePointByILOffset(PVOID pSymbolReaderHandle, mdMethodDef methodToken, ULONG32 ilOffset, ULONG32 &ilCloseOffset, bool *noUserCodeFound)
+HRESULT GetNextUserCodeILOffset(PVOID pSymbolReaderHandle, mdMethodDef methodToken, ULONG32 ilOffset, ULONG32 &ilNextOffset, bool *noUserCodeFound)
{
std::unique_lock<Utility::RWLock::Reader> read_lock(CLRrwlock.reader);
- if (!getNextSequencePointByILOffsetDelegate || !pSymbolReaderHandle)
+ if (!getNextUserCodeILOffsetDelegate || !pSymbolReaderHandle)
return E_FAIL;
int32_t NoUserCodeFound = 0;
// Sequence points with startLine equal to 0xFEEFEE marker are filtered out on the managed side.
- RetCode retCode = getNextSequencePointByILOffsetDelegate(pSymbolReaderHandle, methodToken, ilOffset, &ilCloseOffset, &NoUserCodeFound);
+ RetCode retCode = getNextUserCodeILOffsetDelegate(pSymbolReaderHandle, methodToken, ilOffset, &ilNextOffset, &NoUserCodeFound);
if (noUserCodeFound)
*noUserCodeFound = NoUserCodeFound == 1;
return retCode == RetCode::OK ? S_OK : E_FAIL;
}
-HRESULT ResolveBreakPoints(PVOID pSymbolReaderHandles[], 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, const std::string &sourcePath, PVOID *data)
{
std::unique_lock<Utility::RWLock::Reader> read_lock(CLRrwlock.reader);
if (!resolveBreakPointsDelegate || !pSymbolReaderHandles || !Tokens || !data)
return E_FAIL;
- RetCode retCode = resolveBreakPointsDelegate(pSymbolReaderHandles, tokenNum, Tokens, sourceLine, nestedToken, &Count, data);
+ RetCode retCode = resolveBreakPointsDelegate(pSymbolReaderHandles, tokenNum, Tokens, sourceLine, nestedToken, &Count, to_utf16(sourcePath).c_str(), data);
return retCode == RetCode::OK ? S_OK : E_FAIL;
}
ULONG64 inMemoryPdbAddress, ULONG64 inMemoryPdbSize, VOID **ppSymbolReaderHandle);
void DisposeSymbols(PVOID pSymbolReaderHandle);
HRESULT GetSequencePointByILOffset(PVOID pSymbolReaderHandle, mdMethodDef MethodToken, ULONG32 IlOffset, SequencePoint *sequencePoint);
- HRESULT GetNextSequencePointByILOffset(PVOID pSymbolReaderHandle, mdMethodDef MethodToken, ULONG32 IlOffset, ULONG32 &ilCloseOffset, bool *noUserCodeFound);
+ 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);
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, uint32_t constrTokensNum, PVOID constrTokens, uint32_t normalTokensNum, PVOID normalTokens, PVOID *data);
- HRESULT ResolveBreakPoints(PVOID pSymbolReaderHandles[], 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, const std::string &sourcePath, 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);
#include <map>
#include <iomanip>
#include <algorithm>
+#include <fstream>
#include "managed/interop.h"
#include "utils/platform.h"
return ResolveMethodInModule(pModule, funcname, cb);
}
+template <class T>
+static void LineUpdatesForwardCorrection(unsigned fullPathIndex, mdMethodDef methodToken, method_block_updates_t &methodBlockUpdates, T &block)
+{
+ auto findSourceUpdate = methodBlockUpdates.find(methodToken);
+ if (findSourceUpdate == methodBlockUpdates.end())
+ return;
+
+ for (const auto &entry : findSourceUpdate->second)
+ {
+ if (entry.fullPathIndex != fullPathIndex ||
+ entry.oldLine > block.startLine ||
+ entry.oldLine + entry.endLineOffset < block.startLine)
+ continue;
+
+ int32_t offset = entry.newLine - entry.oldLine;
+ block.startLine += offset;
+ block.endLine += offset;
+ break;
+ }
+}
+
+static void LineUpdatesBackwardCorrection(unsigned fullPathIndex, mdMethodDef methodToken, method_block_updates_t &methodBlockUpdates, int32_t &startLine)
+{
+ auto findSourceUpdate = methodBlockUpdates.find(methodToken);
+ if (findSourceUpdate != methodBlockUpdates.end())
+ {
+ for (const auto &entry : findSourceUpdate->second)
+ {
+ if (entry.fullPathIndex != fullPathIndex ||
+ entry.newLine > startLine ||
+ entry.newLine + entry.endLineOffset < startLine)
+ continue;
+
+ startLine += entry.oldLine - entry.newLine;
+ break;
+ }
+ }
+}
+
HRESULT Modules::GetFrameILAndSequencePoint(
ICorDebugFrame *pFrame,
ULONG32 &ilOffset,
CORDB_ADDRESS modAddress;
IfFailRet(pModule->GetBaseAddress(&modAddress));
- std::lock_guard<std::mutex> lock(m_modulesInfoMutex);
+ std::lock_guard<std::mutex> lockModulesInfo(m_modulesInfoMutex);
auto info_pair = m_modulesInfo.find(modAddress);
if (info_pair == m_modulesInfo.end())
{
if (mdInfo.m_symbolReaderHandles.empty() || mdInfo.m_symbolReaderHandles.size() < methodVersion)
return E_FAIL;
- return GetSequencePointByILOffset(mdInfo.m_symbolReaderHandles[methodVersion - 1], methodToken, ilOffset, &sequencePoint);
+ IfFailRet(GetSequencePointByILOffset(mdInfo.m_symbolReaderHandles[methodVersion - 1], methodToken, ilOffset, &sequencePoint));
+
+ // In case Hot Reload we may have line updates that we must take into account.
+ unsigned fullPathIndex;
+ IfFailRet(GetIndexBySourceFullPath(sequencePoint.document, fullPathIndex));
+ LineUpdatesForwardCorrection(fullPathIndex, methodToken, mdInfo.m_methodBlockUpdates, sequencePoint);
+
+ return S_OK;
}
HRESULT Modules::GetFrameILAndNextUserCodeILOffset(
ICorDebugFrame *pFrame,
ULONG32 &ilOffset,
- ULONG32 &ilCloseOffset,
+ ULONG32 &ilNextOffset,
bool *noUserCodeFound)
{
HRESULT Status;
ToRelease<ICorDebugModule> pModule;
IfFailRet(pFunc->GetModule(&pModule));
- return GetNextSequencePointInMethod(pModule, methodToken, methodVersion, ilOffset, ilCloseOffset, noUserCodeFound);
+ return GetNextUserCodeILOffsetInMethod(pModule, methodToken, methodVersion, ilOffset, ilNextOffset, noUserCodeFound);
}
HRESULT Modules::GetStepRangeFromCurrentIP(ICorDebugThread *pThread, COR_DEBUG_STEP_RANGE *range)
return E_FAIL;
}
-HRESULT Modules::GetNextSequencePointInMethod(
+HRESULT Modules::GetNextUserCodeILOffsetInMethod(
ICorDebugModule *pModule,
mdMethodDef methodToken,
ULONG32 methodVersion,
ULONG32 ilOffset,
- ULONG32 &ilCloseOffset,
+ ULONG32 &ilNextOffset,
bool *noUserCodeFound)
{
HRESULT Status;
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);
+ return Interop::GetNextUserCodeILOffset(mdInfo.m_symbolReaderHandles[methodVersion - 1], methodToken, ilOffset, ilNextOffset, noUserCodeFound);
}
HRESULT Modules::GetSequencePointByILOffset(
return S_OK;
}
-HRESULT Modules::UpdateSourcesCodeLinesForModule(ICorDebugModule *pModule, IMetaDataImport *pMDImport, std::unordered_set<mdMethodDef> methodTokens, PVOID pSymbolReaderHandle)
+static HRESULT LineUpdatesForMethodData(unsigned fullPathIndex, method_data_t &methodData, const std::vector<block_update_t> &blockUpdate, method_block_updates_t &methodBlockUpdates)
+{
+ for (const auto &block : blockUpdate)
+ {
+ if (block.oldLine < 0 || block.endLineOffset < 0 || methodData.endLine < 0)
+ return E_INVALIDARG;
+
+ // 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))
+ continue;
+
+ const int32_t codeLineOffset = block.newLine - block.oldLine;
+
+ auto updateCashe = [&]() -> bool
+ {
+ auto findMethod = methodBlockUpdates.find(methodData.methodDef);
+ if (findMethod == methodBlockUpdates.end())
+ return false;
+
+ for (auto &entry : findMethod->second)
+ {
+ if (fullPathIndex == entry.fullPathIndex &&
+ methodData.startLine == entry.newLine &&
+ methodData.endLine == entry.newLine + entry.endLineOffset)
+ {
+ // 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;
+ }
+ }
+ 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;
+
+ break;
+ }
+
+ return S_OK;
+}
+
+HRESULT Modules::UpdateSourcesCodeLinesForModule(ICorDebugModule *pModule, IMetaDataImport *pMDImport, std::unordered_set<mdMethodDef> methodTokens,
+ src_block_updates_t &srcBlockUpdates, PVOID pSymbolReaderHandle, method_block_updates_t &methodBlockUpdates)
{
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));
- if (inputData == nullptr)
+
+ struct src_update_data_t
+ {
+ std::vector<block_update_t> blockUpdate;
+ int32_t methodNum = 0;
+ method_data_t *methodsData = nullptr;
+ };
+ std::unordered_map<unsigned, src_update_data_t> srcUpdateData;
+
+ if (inputData)
+ {
+ for (int i = 0; i < inputData->fileNum; i++)
+ {
+ unsigned fullPathIndex;
+ IfFailRet(GetFullPathIndex(inputData->moduleMethodsData[i].document, fullPathIndex));
+
+ srcUpdateData[fullPathIndex].methodNum = inputData->moduleMethodsData[i].methodNum;
+ srcUpdateData[fullPathIndex].methodsData = inputData->moduleMethodsData[i].methodsData;
+ }
+ }
+ for (const auto &entry : srcBlockUpdates)
+ {
+ srcUpdateData[entry.first].blockUpdate = entry.second;
+ }
+
+ if (srcUpdateData.empty())
return S_OK;
CORDB_ADDRESS modAddress;
IfFailRet(pModule->GetBaseAddress(&modAddress));
- for (int i = 0; i < inputData->fileNum; i++)
+ for (const auto &updateData : srcUpdateData)
{
- unsigned fullPathIndex;
- IfFailRet(GetFullPathIndex(inputData->moduleMethodsData[i].document, fullPathIndex));
+ const unsigned fullPathIndex = updateData.first;
std::map<size_t, std::set<method_data_t>> inputMethodsData;
if (m_sourcesMethodsData[fullPathIndex].empty())
auto &tmpFileMethodsData = m_sourcesMethodsData[fullPathIndex].back();
tmpFileMethodsData.modAddress = modAddress;
- for (int j = 0; j < inputData->moduleMethodsData[i].methodNum; j++)
+ for (int j = 0; j < updateData.second.methodNum; j++)
{
- AddMethodData(inputMethodsData, tmpFileMethodsData.multiMethodsData, inputData->moduleMethodsData[i].methodsData[j], 0);
+ AddMethodData(inputMethodsData, tmpFileMethodsData.multiMethodsData, updateData.second.methodsData[j], 0);
}
}
else
{
- // Move multiMethodsData first (since this is constructors and all will be on level 0 for sure).
- // Use std::unordered_set here instead array for fast search.
std::unordered_set<mdMethodDef> inputMetodDefSet;
- for (int j = 0; j < inputData->moduleMethodsData[i].methodNum; j++)
+ for (int j = 0; j < updateData.second.methodNum; j++)
{
- inputMetodDefSet.insert(inputData->moduleMethodsData[i].methodsData[j].methodDef);
+ 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);
}
+ // Move multiMethodsData first (since this is constructors and all will be on level 0 for sure).
+ // Use std::unordered_set here instead array for fast search.
auto &tmpFileMethodsData = m_sourcesMethodsData[fullPathIndex].back();
std::vector<method_data_t> tmpMultiMethodsData;
-
- for (auto entryData : tmpFileMethodsData.multiMethodsData)
+ for (const auto &entryData : tmpFileMethodsData.multiMethodsData)
{
auto findData = inputMetodDefSet.find(entryData.first.methodDef);
if (findData == inputMetodDefSet.end())
tmpMultiMethodsData.emplace_back(entryData.first);
- for (auto entryMethodDef : entryData.second)
+ for (const auto &entryMethodDef : entryData.second)
{
findData = inputMetodDefSet.find(entryMethodDef);
if (findData == inputMetodDefSet.end())
}
tmpFileMethodsData.multiMethodsData.clear();
- for (auto methodData : tmpMultiMethodsData)
+ for (auto &methodData : tmpMultiMethodsData)
{
+ IfFailRet(LineUpdatesForMethodData(fullPathIndex, methodData, updateData.second.blockUpdate, methodBlockUpdates));
AddMethodData(inputMethodsData, tmpFileMethodsData.multiMethodsData, methodData, 0);
}
// Move normal methods.
- for (auto methodsData : tmpFileMethodsData.methodsData)
+ for (auto &methodsData : tmpFileMethodsData.methodsData)
{
- for (auto methodData : methodsData)
+ for (auto &methodData : methodsData)
{
auto findData = inputMetodDefSet.find(methodData.methodDef);
if (findData == inputMetodDefSet.end())
+ {
+ IfFailRet(LineUpdatesForMethodData(fullPathIndex, methodData, updateData.second.blockUpdate, methodBlockUpdates));
AddMethodData(inputMethodsData, tmpFileMethodsData.multiMethodsData, methodData, 0);
+ }
}
}
tmpFileMethodsData.methodsData.clear();
// Move new and modified methods.
- for (int j = 0; j < inputData->moduleMethodsData[i].methodNum; j++)
+ for (int j = 0; j < updateData.second.methodNum; j++)
{
- AddMethodData(inputMethodsData, tmpFileMethodsData.multiMethodsData, inputData->moduleMethodsData[i].methodsData[j], 0);
+ AddMethodData(inputMethodsData, tmpFileMethodsData.multiMethodsData, updateData.second.methodsData[j], 0);
}
}
IfFailRet(Interop::StringToUpper(filename));
#endif
- std::lock_guard<std::mutex> lock(m_sourcesInfoMutex);
+ std::lock_guard<std::mutex> lockModulesInfo(m_modulesInfoMutex);
+ std::lock_guard<std::mutex> lockSourcesInfo(m_sourcesInfoMutex);
auto findIndex = m_sourcePathToIndex.find(filename);
if (findIndex == m_sourcePathToIndex.end())
symbolReaderHandles.emplace_back(mdInfo.m_symbolReaderHandles[currentVersion - 1]);
}
+ // In case Hot Reload we may have line updates that we must take into account.
+ LineUpdatesBackwardCorrection(findIndex->second, Tokens[0], mdInfo.m_methodBlockUpdates, correctedStartLine);
+
PVOID data = nullptr;
int32_t Count = 0;
+#ifndef _WIN32
+ std::string fullName = m_sourceIndexToPath[findIndex->second];
+#else
+ std::string fullName = m_sourceIndexToInitialFullPath[findIndex->second];
+#endif
if (FAILED(Interop::ResolveBreakPoints(symbolReaderHandles.data(), (int32_t)Tokens.size(), Tokens.data(),
- correctedStartLine, closestNestedToken, Count, &data))
+ correctedStartLine, closestNestedToken, Count, fullName, &data))
|| data == nullptr)
{
continue;
for (int32_t i = 0; i < Count; i++)
{
info_pair->second.m_iCorModule->AddRef();
+
+ // In case Hot Reload we may have line updates that we must take into account.
+ LineUpdatesForwardCorrection(findIndex->second, inputData.get()[i].methodToken, mdInfo.m_methodBlockUpdates, inputData.get()[i]);
+
resolvedPoints.emplace_back(resolved_bp_t(inputData.get()[i].startLine, inputData.get()[i].endLine, inputData.get()[i].ilOffset,
inputData.get()[i].methodToken, info_pair->second.m_iCorModule.GetPtr()));
}
return S_OK;
}
-HRESULT Modules::ApplyPdbDelta(ICorDebugModule *pModule, bool needJMC, const std::string &deltaPDB, std::unordered_set<mdMethodDef> &methodTokens)
+static HRESULT LoadLineUpdatesFile(Modules *pModules, const std::string &lineUpdates, src_block_updates_t &srcBlockUpdates)
+{
+ std::ifstream lineUpdatesFileStream(lineUpdates, std::ios::in | std::ios::binary);
+ if (!lineUpdatesFileStream.is_open())
+ return COR_E_FILENOTFOUND;
+
+ HRESULT Status;
+ uint32_t sourcesCount = 0;
+ lineUpdatesFileStream.read((char*)&sourcesCount, 4);
+ if (lineUpdatesFileStream.fail())
+ return E_FAIL;
+
+ struct line_update_t
+ {
+ int32_t newLine;
+ int32_t oldLine;
+ };
+ std::unordered_map<unsigned /*source fullPathIndex*/, std::vector<line_update_t>> lineUpdatesData;
+
+ for (uint32_t i = 0; i < sourcesCount; i++)
+ {
+ uint32_t stringSize = 0;
+ lineUpdatesFileStream.read((char*)&stringSize, 4);
+ if (lineUpdatesFileStream.fail())
+ return E_FAIL;
+
+ std::string fullPath;
+ fullPath.resize(stringSize);
+ lineUpdatesFileStream.read((char*)fullPath.data(), stringSize);
+ if (lineUpdatesFileStream.fail())
+ return E_FAIL;
+
+ unsigned fullPathIndex;
+ IfFailRet(pModules->GetIndexBySourceFullPath(fullPath, fullPathIndex));
+
+ uint32_t updatesCount = 0;
+ lineUpdatesFileStream.read((char*)&updatesCount, 4);
+ if (lineUpdatesFileStream.fail())
+ return E_FAIL;
+
+ if (updatesCount == 0)
+ continue;
+
+ std::vector<line_update_t> &lineUpdates = lineUpdatesData[fullPathIndex];
+ lineUpdates.resize(updatesCount);
+
+ lineUpdatesFileStream.read((char*)lineUpdates.data(), updatesCount * sizeof(line_update_t));
+ if (lineUpdatesFileStream.fail())
+ return E_FAIL;
+ }
+
+ line_update_t startBlock;
+ const int32_t empty = -1;
+ for (const auto &updateFileData : lineUpdatesData)
+ {
+ startBlock.newLine = empty;
+ for (const auto &lineUpdates : updateFileData.second)
+ {
+ if (lineUpdates.newLine != lineUpdates.oldLine)
+ {
+ assert(startBlock.newLine == empty);
+
+ startBlock.newLine = lineUpdates.newLine;
+ startBlock.oldLine = lineUpdates.oldLine;
+ }
+ else
+ {
+ // We use (newLine == oldLine) entry in LineUpdates as "end line" marker for moved region.
+ if (startBlock.newLine != empty)
+ srcBlockUpdates[updateFileData.first].emplace_back(startBlock.newLine + 1, startBlock.oldLine + 1, lineUpdates.oldLine - 1 - startBlock.oldLine);
+
+ startBlock.newLine = empty;
+ }
+ }
+ // In case this is last method in source file, Roslyn don't provide "end line" in LineUpdates, use max source line as "end line".
+ if (startBlock.newLine != empty)
+ srcBlockUpdates[updateFileData.first].emplace_back(startBlock.newLine + 1, startBlock.oldLine + 1, std::numeric_limits<int32_t>::max());
+ }
+
+ return S_OK;
+}
+
+HRESULT Modules::ApplyPdbDeltaAndLineUpdates(ICorDebugModule *pModule, bool needJMC, const std::string &deltaPDB,
+ const std::string &lineUpdates, std::unordered_set<mdMethodDef> &methodTokens)
{
HRESULT Status;
CORDB_ADDRESS modAddress;
// Note, even if methodTokens is empty, pSymbolReaderHandle must be added into vector (we use indexes that correspond to il/metadata apply number + will care about release it in proper way).
mdInfo.m_symbolReaderHandles.emplace_back(pSymbolReaderHandle);
- if (methodTokens.empty())
+ src_block_updates_t srcBlockUpdates;
+ IfFailRet(LoadLineUpdatesFile(this, lineUpdates, srcBlockUpdates));
+
+ if (methodTokens.empty() && srcBlockUpdates.empty())
return S_OK;
- if (needJMC)
+ if (needJMC && !methodTokens.empty())
DisableJMCByAttributes(pModule, methodTokens);
ToRelease<IUnknown> pMDUnknown;
ToRelease<IMetaDataImport> pMDImport;
IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMDImport));
- return UpdateSourcesCodeLinesForModule(pModule, pMDImport, methodTokens, pSymbolReaderHandle);
+ return UpdateSourcesCodeLinesForModule(pModule, pMDImport, methodTokens, srcBlockUpdates, pSymbolReaderHandle, mdInfo.m_methodBlockUpdates);
}
HRESULT Modules::GetSourceFullPathByIndex(unsigned index, std::string &fullPath)
std::string GetModuleFileName(ICorDebugModule *pModule);
HRESULT IsModuleHaveSameName(ICorDebugModule *pModule, const std::string &Name, bool isFullPath);
+struct block_update_t
+{
+ int32_t newLine;
+ int32_t oldLine;
+ int32_t endLineOffset;
+ block_update_t(int32_t newLine_, int32_t oldLine_, int32_t endLineOffset_) :
+ newLine(newLine_), oldLine(oldLine_), endLineOffset(endLineOffset_)
+ {}
+};
+typedef std::unordered_map<unsigned /*source fullPathIndex*/, std::vector<block_update_t>> src_block_updates_t;
+
+struct file_block_update_t
+{
+ unsigned fullPathIndex;
+ int32_t newLine;
+ int32_t oldLine;
+ int32_t endLineOffset;
+ file_block_update_t(unsigned fullPathIndex_, int32_t newLine_, int32_t oldLine_, int32_t endLineOffset_) :
+ fullPathIndex(fullPathIndex_), newLine(newLine_), oldLine(oldLine_), endLineOffset(endLineOffset_)
+ {}
+};
+typedef std::unordered_map<mdMethodDef, std::vector<file_block_update_t>> method_block_updates_t;
+
class Modules
{
struct ModuleInfo
{
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)
HRESULT GetFullPathIndex(BSTR document, unsigned &fullPathIndex);
HRESULT FillSourcesCodeLinesForModule(ICorDebugModule *pModule, IMetaDataImport *pMDImport, PVOID pSymbolReaderHandle);
- HRESULT UpdateSourcesCodeLinesForModule(ICorDebugModule *pModule, IMetaDataImport *pMDImport,
- std::unordered_set<mdMethodDef> methodTokens, PVOID pSymbolReaderHandle);
+ HRESULT UpdateSourcesCodeLinesForModule(ICorDebugModule *pModule, IMetaDataImport *pMDImport, std::unordered_set<mdMethodDef> methodTokens,
+ src_block_updates_t &blockUpdates, PVOID pSymbolReaderHandle, method_block_updates_t &methodBlockUpdates);
HRESULT ResolveRelativeSourceFileName(std::string &filename);
#ifdef WIN32
HRESULT GetSourceFullPathByIndex(unsigned index, std::string &fullPath);
HRESULT GetIndexBySourceFullPath(std::string fullPath, unsigned &index);
- HRESULT ApplyPdbDelta(ICorDebugModule *pModule, bool needJMC, const std::string &deltaPDB, std::unordered_set<mdMethodDef> &methodTokens);
+ HRESULT ApplyPdbDeltaAndLineUpdates(ICorDebugModule *pModule, bool needJMC, const std::string &deltaPDB,
+ const std::string &lineUpdates, std::unordered_set<mdMethodDef> &methodTokens);
HRESULT GetModuleWithName(const std::string &name, ICorDebugModule **ppModule, bool onlyWithPDB = false);
HRESULT GetFrameILAndNextUserCodeILOffset(
ICorDebugFrame *pFrame,
ULONG32 &ilOffset,
- ULONG32 &ilCloseOffset,
+ ULONG32 &ilNextOffset,
bool *noUserCodeFound);
HRESULT ResolveFuncBreakpointInAny(
PVOID *data,
int32_t &hoistedLocalScopesCount);
- HRESULT GetNextSequencePointInMethod(
+ HRESULT GetNextUserCodeILOffsetInMethod(
ICorDebugModule *pModule,
mdMethodDef methodToken,
ULONG32 methodVersion,
ULONG32 ilOffset,
- ULONG32 &ilCloseOffset,
+ ULONG32 &ilNextOffset,
bool *noUserCodeFound = nullptr);
HRESULT GetSequencePointByILOffset(
{ "apply-deltas", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
HRESULT Status;
- if (args.size() != 4)
+ if (args.size() != 5)
{
- output = "Command requires 4 arguments";
+ output = "Command requires 5 arguments";
return E_FAIL;
}
std::string deltaMD = args.at(1);
std::string deltaIL = args.at(2);
std::string deltaPDB = args.at(3);
+ std::string lineUpdates = args.at(4);
- IfFailRet(m_sharedDebugger->HotReloadApplyDeltas(dllFileName, deltaMD, deltaIL, deltaPDB));
+ IfFailRet(m_sharedDebugger->HotReloadApplyDeltas(dllFileName, deltaMD, deltaIL, deltaPDB, lineUpdates));
return S_OK;
}},
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".metadata", targetPath);\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".il", targetPath);\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".pdb", targetPath);\r
+ SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".bin", targetPath);\r
}\r
\r
public void GetDelta(string caller_trace, string source, string sourceFileName)\r
string targetPath = Path.Combine(@"/tmp", fileName);\r
string targetAssemblyName = Path.GetFileName(ControlInfo.TargetAssemblyPath);\r
Assert.Equal(MIResultClass.Done,\r
- MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb").Class,\r
+ MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb " + targetPath + ".bin").Class,\r
@"__FILE__:__LINE__"+"\n"+caller_trace);\r
}\r
\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".metadata", targetPath);\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".il", targetPath);\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".pdb", targetPath);\r
+ SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".bin", targetPath);\r
}\r
\r
public void GetDelta(string caller_trace, string source, string sourceFileName)\r
string targetPath = Path.Combine(@"/tmp", fileName);\r
string targetAssemblyName = Path.GetFileName(ControlInfo.TargetAssemblyPath);\r
Assert.Equal(MIResultClass.Done,\r
- MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb").Class,\r
+ MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb " + targetPath + ".bin").Class,\r
@"__FILE__:__LINE__"+"\n"+caller_trace);\r
}\r
\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".metadata", targetPath);\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".il", targetPath);\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".pdb", targetPath);\r
+ SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".bin", targetPath);\r
}\r
\r
public void GetDelta(string caller_trace, string source, string sourceFileName)\r
string targetPath = Path.Combine(@"/tmp", fileName);\r
string targetAssemblyName = Path.GetFileName(ControlInfo.TargetAssemblyPath);\r
Assert.Equal(MIResultClass.Done,\r
- MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb").Class,\r
+ MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb " + targetPath + ".bin").Class,\r
@"__FILE__:__LINE__"+"\n"+caller_trace);\r
}\r
\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".metadata", targetPath);\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".il", targetPath);\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".pdb", targetPath);\r
+ SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".bin", targetPath);\r
}\r
\r
public void GetDelta(string caller_trace, string source, string sourceFileName)\r
string targetPath = Path.Combine(@"/tmp", fileName);\r
string targetAssemblyName = Path.GetFileName(ControlInfo.TargetAssemblyPath);\r
Assert.Equal(MIResultClass.Done,\r
- MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb").Class,\r
+ MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb " + targetPath + ".bin").Class,\r
@"__FILE__:__LINE__"+"\n"+caller_trace);\r
}\r
\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".metadata", targetPath);\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".il", targetPath);\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".pdb", targetPath);\r
+ SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".bin", targetPath);\r
}\r
\r
public void GetDelta(string caller_trace, string source, string sourceFileName)\r
string targetPath = Path.Combine(@"/tmp", fileName);\r
string targetAssemblyName = Path.GetFileName(ControlInfo.TargetAssemblyPath);\r
Assert.Equal(MIResultClass.Done,\r
- MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb").Class,\r
+ MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb " + targetPath + ".bin").Class,\r
@"__FILE__:__LINE__"+"\n"+caller_trace);\r
}\r
\r
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">\r
+\r
+ <ItemGroup>\r
+ <ProjectReference Include="..\NetcoreDbgTest\NetcoreDbgTest.csproj" />\r
+ <ProjectReference Include="..\TestAppHotReload\TestAppHotReload.csproj" />\r
+ </ItemGroup>\r
+\r
+ <PropertyGroup>\r
+ <OutputType>Exe</OutputType>\r
+ <TargetFramework>netcoreapp3.1</TargetFramework>\r
+ </PropertyGroup>\r
+\r
+</Project>\r
--- /dev/null
+using System;\r
+using System.IO;\r
+using System.Runtime.InteropServices;\r
+\r
+using NetcoreDbgTest;\r
+using NetcoreDbgTest.MI;\r
+using NetcoreDbgTest.Script;\r
+using NetcoreDbgTest.GetDeltaApi;\r
+\r
+namespace NetcoreDbgTest.Script\r
+{\r
+ class Context\r
+ {\r
+ public void Prepare(string caller_trace)\r
+ {\r
+ Assert.Equal(MIResultClass.Done,\r
+ MIDebugger.Request("-gdb-set enable-hot-reload 1").Class,\r
+ @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+\r
+ Assert.Equal(MIResultClass.Done,\r
+ MIDebugger.Request("-file-exec-and-symbols " + ControlInfo.CorerunPath).Class,\r
+ @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+\r
+ Assert.Equal(MIResultClass.Done,\r
+ MIDebugger.Request("-exec-arguments " + ControlInfo.TargetAssemblyPath).Class,\r
+ @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+\r
+ Assert.Equal(MIResultClass.Running,\r
+ MIDebugger.Request("-exec-run").Class,\r
+ @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ bool IsStoppedEvent(MIOutOfBandRecord record)\r
+ {\r
+ if (record.Type != MIOutOfBandRecordType.Async) {\r
+ return false;\r
+ }\r
+\r
+ var asyncRecord = (MIAsyncRecord)record;\r
+\r
+ if (asyncRecord.Class != MIAsyncRecordClass.Exec ||\r
+ asyncRecord.Output.Class != MIAsyncOutputClass.Stopped) {\r
+ return false;\r
+ }\r
+\r
+ return true;\r
+ }\r
+\r
+ public void WasEntryPointHit(string realNamespace, string caller_trace)\r
+ {\r
+ Func<MIOutOfBandRecord, bool> filter = (record) => {\r
+ if (!IsStoppedEvent(record)) {\r
+ return false;\r
+ }\r
+\r
+ var output = ((MIAsyncRecord)record).Output;\r
+ var reason = (MIConst)output["reason"];\r
+\r
+ if (reason.CString != "entry-point-hit") {\r
+ return false;\r
+ }\r
+\r
+ var frame = (MITuple)output["frame"];\r
+ var func = (MIConst)frame["func"];\r
+ if (func.CString == realNamespace + ".Program.Main()") {\r
+ return true;\r
+ }\r
+\r
+ return false;\r
+ };\r
+\r
+ Assert.True(MIDebugger.IsEventReceived(filter), @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void WasBreakpointHit(string caller_trace, string bpFileName, int bpNumLine)\r
+ {\r
+ Func<MIOutOfBandRecord, bool> filter = (record) => {\r
+ if (!IsStoppedEvent(record)) {\r
+ return false;\r
+ }\r
+\r
+ var output = ((MIAsyncRecord)record).Output;\r
+ var reason = (MIConst)output["reason"];\r
+\r
+ if (reason.CString != "breakpoint-hit") {\r
+ return false;\r
+ }\r
+\r
+ var frame = (MITuple)output["frame"];\r
+ var fileName = (MIConst)frame["file"];\r
+ var line = ((MIConst)frame["line"]).Int;\r
+\r
+ if (fileName.CString == bpFileName &&\r
+ line == bpNumLine) {\r
+ return true;\r
+ }\r
+\r
+ return false;\r
+ };\r
+\r
+ Assert.True(MIDebugger.IsEventReceived(filter),\r
+ @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void WasExit(string caller_trace)\r
+ {\r
+ Func<MIOutOfBandRecord, bool> filter = (record) => {\r
+ if (!IsStoppedEvent(record)) {\r
+ return false;\r
+ }\r
+\r
+ var output = ((MIAsyncRecord)record).Output;\r
+ var reason = (MIConst)output["reason"];\r
+\r
+ if (reason.CString != "exited") {\r
+ return false;\r
+ }\r
+\r
+ var exitCode = (MIConst)output["exit-code"];\r
+\r
+ if (exitCode.CString == "0") {\r
+ return true;\r
+ }\r
+\r
+ return false;\r
+ };\r
+\r
+ Assert.True(MIDebugger.IsEventReceived(filter), @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void DebuggerExit(string caller_trace)\r
+ {\r
+ Assert.Equal(MIResultClass.Exit,\r
+ MIDebugger.Request("-gdb-exit").Class,\r
+ @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void EnableBreakpoint(string caller_trace, string bpFileName, int bpNumLine)\r
+ {\r
+ Assert.Equal(MIResultClass.Done,\r
+ MIDebugger.Request("-break-insert -f " + bpFileName + ":" + bpNumLine).Class,\r
+ @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void EnableFuncBreakpoint(string caller_trace, string funcName)\r
+ {\r
+ Assert.Equal(MIResultClass.Done,\r
+ MIDebugger.Request("-break-insert -f " + funcName).Class,\r
+ @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void Continue(string caller_trace)\r
+ {\r
+ Assert.Equal(MIResultClass.Running,\r
+ MIDebugger.Request("-exec-continue").Class,\r
+ @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void CheckHostRuntimeVersion(string caller_trace)\r
+ {\r
+ Assert.True(GetDeltaApi.CheckRuntimeVersion(), @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void CheckHostOS(string caller_trace)\r
+ {\r
+ Assert.True(RuntimeInformation.IsOSPlatform(OSPlatform.Linux), @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void CheckTargetRuntimeVersion(string caller_trace)\r
+ {\r
+ var res = MIDebugger.Request("-var-create - * System.Environment.Version.Major>=6");\r
+ Assert.Equal(MIResultClass.Done, res.Class, @"__FILE__:__LINE__"+"\n" + caller_trace);\r
+ Assert.Equal("true", ((MIConst)res["value"]).CString, @"__FILE__:__LINE__"+"\n" + caller_trace);\r
+ }\r
+\r
+ public void StartGenDeltaSession(string caller_trace)\r
+ {\r
+ string projectPath = Path.GetDirectoryName(ControlInfo.SourceFilesPath);\r
+ Assert.True(GetDeltaApi.StartGenDeltaSession(projectPath), @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void EndGenDeltaSession(string caller_trace)\r
+ {\r
+ Assert.True(GetDeltaApi.EndGenDeltaSession(), @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void SdbPush(string caller_trace, string hostFullPath, string targetPath)\r
+ {\r
+ System.Diagnostics.Process process = new System.Diagnostics.Process();\r
+ process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;\r
+ process.StartInfo.RedirectStandardInput = true;\r
+ process.StartInfo.RedirectStandardOutput = true;\r
+ process.StartInfo.CreateNoWindow = true;\r
+ process.StartInfo.UseShellExecute = false;\r
+ \r
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))\r
+ {\r
+ process.StartInfo.FileName = "bash";\r
+ process.StartInfo.Arguments = "-c \"sdb push " + hostFullPath + " " + targetPath + "\"";\r
+ }\r
+ else\r
+ throw new Exception("Host OS not supported. " + @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+\r
+ process.Start();\r
+ process.WaitForExit();\r
+ Assert.Equal(0, process.ExitCode, @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void WriteDeltas(string caller_trace, string fileName)\r
+ {\r
+ string hostPath;\r
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))\r
+ hostPath = Path.Combine(@"/tmp", fileName);\r
+ else\r
+ throw new Exception("Host OS not supported. " + @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+\r
+ string targetPath = @"/tmp";\r
+ Assert.True(GetDeltaApi.WriteDeltas(hostPath), @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".metadata", targetPath);\r
+ SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".il", targetPath);\r
+ SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".pdb", targetPath);\r
+ SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".bin", targetPath);\r
+ }\r
+\r
+ public void GetDelta(string caller_trace, string source, string sourceFileName)\r
+ {\r
+ Assert.True(GetDeltaApi.GetDeltas(source, sourceFileName, "", false), @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void ErrorGetDelta(string caller_trace, string source, string sourceFileName)\r
+ {\r
+ Assert.False(GetDeltaApi.GetDeltas(source, sourceFileName, "", false), @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void ApplyDeltas(string caller_trace, string fileName)\r
+ {\r
+ string targetPath = Path.Combine(@"/tmp", fileName);\r
+ string targetAssemblyName = Path.GetFileName(ControlInfo.TargetAssemblyPath);\r
+ Assert.Equal(MIResultClass.Done,\r
+ MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb " + targetPath + ".bin").Class,\r
+ @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public Context(ControlInfo controlInfo, NetcoreDbgTestCore.DebuggerClient debuggerClient)\r
+ {\r
+ ControlInfo = controlInfo;\r
+ MIDebugger = new MIDebugger(debuggerClient);\r
+ GetDeltaApi = new GetDeltaApi.GetDeltaApi();\r
+ }\r
+\r
+ ControlInfo ControlInfo;\r
+ MIDebugger MIDebugger;\r
+ GetDeltaApi.GetDeltaApi GetDeltaApi;\r
+ }\r
+}\r
+\r
+namespace MITestHotReloadPDB\r
+{\r
+ class Program\r
+ {\r
+ static void Main(string[] args)\r
+ {\r
+ Label.Checkpoint("init", "pdb_test1", (Object context) => {\r
+ Context Context = (Context)context;\r
+ Context.CheckHostRuntimeVersion(@"__FILE__:__LINE__");\r
+ Context.CheckHostOS(@"__FILE__:__LINE__");\r
+ Context.Prepare(@"__FILE__:__LINE__");\r
+ Context.WasEntryPointHit("TestAppHotReload", @"__FILE__:__LINE__");\r
+ // Note, target Hot Reload check must be after debuggee process start and stop at entry breakpoint.\r
+ Context.CheckTargetRuntimeVersion(@"__FILE__:__LINE__");\r
+ Context.StartGenDeltaSession(@"__FILE__:__LINE__");\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! Main updated."");\r
+ HotReloadTest();\r
+ }\r
+ static void HotReloadTest()\r
+ {\r
+ Console.WriteLine(""Updated string."");\r
+ HotReloadBreakpointTest1();\r
+ HotReloadBreakpointTest1();\r
+ }\r
+ static void HotReloadBreakpointTest1()\r
+ {\r
+ Console.WriteLine(""Added string."");\r
+ }\r
+ }\r
+ }", @"Program.cs");\r
+ Context.WriteDeltas(@"__FILE__:__LINE__", "tmp_delta1");\r
+ Context.ApplyDeltas(@"__FILE__:__LINE__", "tmp_delta1");\r
+\r
+\r
+ // Test method move without code changes.\r
+\r
+ Context.GetDelta(@"__FILE__:__LINE__",\r
+ @"using System;\r
+ namespace TestAppHotReload\r
+ {\r
+ class Program\r
+ {\r
+ static void HotReloadBreakpointTest1()\r
+ {\r
+ Console.WriteLine(""Added string."");\r
+ }\r
+ static void Main(string[] args)\r
+ {\r
+ Console.WriteLine(""Hello World! Main updated."");\r
+ HotReloadTest();\r
+ }\r
+ static void HotReloadTest()\r
+ {\r
+ Console.WriteLine(""Updated string."");\r
+ HotReloadBreakpointTest1();\r
+ HotReloadBreakpointTest1();\r
+ }\r
+ }\r
+ }", @"Program.cs");\r
+ Context.WriteDeltas(@"__FILE__:__LINE__", "tmp_delta2");\r
+ Context.ApplyDeltas(@"__FILE__:__LINE__", "tmp_delta2");\r
+\r
+ // Test syntax error in new code (deltas generation routine test).\r
+\r
+ Context.ErrorGetDelta(@"__FILE__:__LINE__",\r
+ @"using System;\r
+ namespace TestAppHotReload\r
+ {\r
+ class Program\r
+ {\r
+ static void HotReloadBreakpointTest1()\r
+ {\r
+ sdfdsf\r
+ }\r
+ static void HotReloadTest()\r
+ {\r
+ }\r
+ static void Main(string[] args)\r
+ {\r
+ Console.WriteLine(""Hello World! Main updated."");\r
+ HotReloadTest();\r
+ }\r
+ }\r
+ }", @"Program.cs");\r
+\r
+\r
+ // Test methods delete error in new code (deltas generation routine test).\r
+\r
+ Context.ErrorGetDelta(@"__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! Main updated."");\r
+ HotReloadTest();\r
+ }\r
+ }\r
+ }", @"Program.cs");\r
+\r
+\r
+ // Test method move with code changes.\r
+\r
+ Context.GetDelta(@"__FILE__:__LINE__",\r
+ @"using System;\r
+ namespace TestAppHotReload\r
+ {\r
+ class Program\r
+ {\r
+ static void HotReloadBreakpointTest1()\r
+ {\r
+ Console.WriteLine(""Added string."");\r
+ }\r
+\r
+ static void HotReloadTest()\r
+ {\r
+ Console.WriteLine(""Updated string. 2"");\r
+ HotReloadBreakpointTest1();\r
+ }\r
+\r
+ static void Main(string[] args)\r
+ {\r
+ Console.WriteLine(""Hello World! Main updated."");\r
+ HotReloadTest();\r
+ }\r
+ }\r
+ }", @"Program.cs");\r
+ Context.WriteDeltas(@"__FILE__:__LINE__", "tmp_delta3");\r
+ Context.ApplyDeltas(@"__FILE__:__LINE__", "tmp_delta3");\r
+\r
+ Context.GetDelta(@"__FILE__:__LINE__",\r
+ @"using System;\r
+ namespace TestAppHotReload\r
+ {\r
+ class Program\r
+ {\r
+ \r
+ static void HotReloadTest()\r
+ {\r
+ Console.WriteLine(""Updated string. 2"");\r
+ HotReloadBreakpointTest1();\r
+ }\r
+\r
+\r
+ static void HotReloadBreakpointTest1()\r
+ {\r
+ Console.WriteLine(""Added string."");\r
+ }\r
+\r
+\r
+\r
+\r
+\r
+\r
+ static void Main(string[] args)\r
+ {\r
+ Console.WriteLine(""Hello World! Main updated."");\r
+ HotReloadTest();\r
+ }\r
+ }\r
+ }", @"Program.cs");\r
+ Context.WriteDeltas(@"__FILE__:__LINE__", "tmp_delta4");\r
+ Context.ApplyDeltas(@"__FILE__:__LINE__", "tmp_delta4");\r
+\r
+ Context.GetDelta(@"__FILE__:__LINE__",\r
+ @"using System;\r
+ namespace TestAppHotReload\r
+ {\r
+ class Program\r
+ {\r
+\r
+\r
+ static void HotReloadBreakpointTest1()\r
+ { // line 9\r
+ Console.WriteLine(""Added string."");\r
+ }\r
+ static void HotReloadTest()\r
+ { // line 14\r
+ Console.WriteLine(""Updated string. 2"");\r
+ HotReloadBreakpointTest1();\r
+ }\r
+ static void Main(string[] args)\r
+ {\r
+ Console.WriteLine(""Hello World! Main updated."");\r
+ HotReloadTest();\r
+ }\r
+ }\r
+ }", @"Program.cs");\r
+ Context.WriteDeltas(@"__FILE__:__LINE__", "tmp_delta5");\r
+ Context.ApplyDeltas(@"__FILE__:__LINE__", "tmp_delta5");\r
+\r
+ Context.EnableBreakpoint(@"__FILE__:__LINE__", @"Program.cs", 9);\r
+ Context.EnableBreakpoint(@"__FILE__:__LINE__", @"Program.cs", 14);\r
+ Context.Continue(@"__FILE__:__LINE__");\r
+ });\r
+\r
+ // Test breakpoints in moved methods.\r
+ Label.Checkpoint("pdb_test1", "finish", (Object context) => {\r
+ Context Context = (Context)context;\r
+ Context.WasBreakpointHit(@"__FILE__:__LINE__", @"Program.cs", 14);\r
+ Context.Continue(@"__FILE__:__LINE__");\r
+ Context.WasBreakpointHit(@"__FILE__:__LINE__", @"Program.cs", 9);\r
+ Context.Continue(@"__FILE__:__LINE__");\r
+ });\r
+\r
+ Label.Checkpoint("finish", "", (Object context) => {\r
+ Context Context = (Context)context;\r
+ Context.EndGenDeltaSession(@"__FILE__:__LINE__");\r
+ Context.WasExit(@"__FILE__:__LINE__");\r
+ Context.DebuggerExit(@"__FILE__:__LINE__");\r
+ });\r
+ }\r
+ }\r
+}\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".metadata", targetPath);\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".il", targetPath);\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".pdb", targetPath);\r
+ SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".bin", targetPath);\r
}\r
\r
public void GetDelta(string caller_trace, string source, string sourceFileName)\r
string targetPath = Path.Combine(@"/tmp", fileName);\r
string targetAssemblyName = Path.GetFileName(ControlInfo.TargetAssemblyPath);\r
Assert.Equal(MIResultClass.Done,\r
- MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb").Class,\r
+ MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb " + targetPath + ".bin").Class,\r
@"__FILE__:__LINE__"+"\n"+caller_trace);\r
}\r
\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".metadata", targetPath);\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".il", targetPath);\r
SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".pdb", targetPath);\r
+ SdbPush(@"__FILE__:__LINE__"+"\n"+caller_trace, hostPath + ".bin", targetPath);\r
}\r
\r
public void GetDelta(string caller_trace, string source, string sourceFileName)\r
string targetPath = Path.Combine(@"/tmp", fileName);\r
string targetAssemblyName = Path.GetFileName(ControlInfo.TargetAssemblyPath);\r
Assert.Equal(MIResultClass.Done,\r
- MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb").Class,\r
+ MIDebugger.Request("-apply-deltas " + targetAssemblyName + " " + targetPath + ".metadata " + targetPath + ".il " + targetPath + ".pdb " + targetPath + ".bin").Class,\r
@"__FILE__:__LINE__"+"\n"+caller_trace);\r
}\r
\r
\r
public test_constructors()\r
{\r
-\r
+ int i = 5; // bp here! make sure you correct code (test constructor)!\r
}\r
\r
public test_constructors(int i)\r
{\r
- \r
+ int j = 5;\r
}\r
}\r
\r
Context Context = (Context)context;\r
Context.WasBreakpointHit(@"__FILE__:__LINE__", "bp23");\r
\r
- Context.ManualEnableBreakpoint(@"__FILE__:__LINE__", "Program.cs", 223);\r
+ Context.ManualEnableBreakpoint(@"__FILE__:__LINE__", "Program.cs", 223); // line number with "int test_field = 5;" code\r
+ Context.ManualEnableBreakpoint(@"__FILE__:__LINE__", "Program.cs", 227); // line number with "int i = 5;" code\r
+ Context.Continue(@"__FILE__:__LINE__");\r
+ Context.WasManualBreakpointHit(@"__FILE__:__LINE__", "Program.cs", 223); // line number with "int test_field = 5;" code\r
Context.Continue(@"__FILE__:__LINE__");\r
- Context.WasManualBreakpointHit(@"__FILE__:__LINE__", "Program.cs", 223);\r
+ Context.WasManualBreakpointHit(@"__FILE__:__LINE__", "Program.cs", 227); // line number with "int i = 5;" code\r
Context.Continue(@"__FILE__:__LINE__");\r
- Context.WasManualBreakpointHit(@"__FILE__:__LINE__", "Program.cs", 223);\r
+ Context.WasManualBreakpointHit(@"__FILE__:__LINE__", "Program.cs", 223); // line number with "int test_field = 5;" code\r
Context.Continue(@"__FILE__:__LINE__");\r
});\r
\r
\r
public test_constructors()\r
{\r
+ int i = 5; // bp here! make sure you correct code (test constructor)!\r
}\r
\r
public test_constructors(int i)\r
{\r
+ int j = 5;\r
}\r
}\r
\r
Context.WasBreakpointHit(@"__FILE__:__LINE__", "bp23");\r
\r
Context.AddManualBreakpoint(@"__FILE__:__LINE__", "Program.cs", 287); // line number with "int test_field = 5;" code\r
+ Context.AddManualBreakpoint(@"__FILE__:__LINE__", "Program.cs", 291); // line number with "int i = 5;" code\r
Context.SetBreakpoints(@"__FILE__:__LINE__");\r
Context.Continue(@"__FILE__:__LINE__");\r
Context.WasManualBreakpointHit(@"__FILE__:__LINE__", "Program.cs", 287); // line number with "int test_field = 5;" code\r
Context.Continue(@"__FILE__:__LINE__");\r
+ Context.WasManualBreakpointHit(@"__FILE__:__LINE__", "Program.cs", 291); // line number with "int i = 5;" code\r
+ Context.Continue(@"__FILE__:__LINE__");\r
Context.WasManualBreakpointHit(@"__FILE__:__LINE__", "Program.cs", 287); // line number with "int test_field = 5;" code\r
Context.Continue(@"__FILE__:__LINE__");\r
});\r
"MITestHotReloadJMC"
"MITestHotReloadWithoutBreak"
"MITestGeneric"
+ "MITestHotReloadPDB"
"VSCodeExampleTest"
"VSCodeTestBreakpoint"
"VSCodeTestFuncBreak"