#include "common.h"
-#include <sstream>
#include <mutex>
#include <memory>
-#include <unordered_map>
-#include <vector>
-#include <map>
-#include <mutex>
-#include <condition_variable>
+#include <unordered_set>
#include "debugger.h"
#include "modules.h"
-#include "breakpoints.h"
-
-
-static std::mutex g_breakMutex;
-static ULONG32 g_breakIndex = 1;
-
-struct ManagedBreakpoint {
- ULONG32 id;
- CORDB_ADDRESS modAddress;
- mdMethodDef methodToken;
- ULONG32 ilOffset;
- std::string fullname;
- int linenum;
- ToRelease<ICorDebugBreakpoint> breakpoint;
- bool enabled;
- ULONG32 times;
-
- bool IsResolved() const
- {
- return modAddress != 0;
- }
- ManagedBreakpoint() :
- id(0), modAddress(0), methodToken(0), ilOffset(0), linenum(0), breakpoint(nullptr), enabled(true), times(0) {}
- ~ManagedBreakpoint()
- {
- if (breakpoint)
- breakpoint->Activate(0);
- }
-
- void ToBreakpoint(Breakpoint &breakpoint)
- {
- breakpoint.id = this->id;
- breakpoint.verified = this->IsResolved();
- breakpoint.source = Source(this->fullname);
- breakpoint.line = this->linenum;
- breakpoint.hitCount = this->times;
- }
-
- ManagedBreakpoint(ManagedBreakpoint &&that) = default;
+Debugger::ManagedBreakpoint::ManagedBreakpoint() :
+ id(0), modAddress(0), methodToken(0), ilOffset(0), linenum(0), breakpoint(nullptr), enabled(true), times(0)
+{}
- ManagedBreakpoint(const ManagedBreakpoint &that) = delete;
-};
+Debugger::ManagedBreakpoint::~ManagedBreakpoint()
+{
+ if (breakpoint)
+ breakpoint->Activate(0);
+}
-static std::map<ULONG32, ManagedBreakpoint> g_breaks;
+void Debugger::ManagedBreakpoint::ToBreakpoint(Breakpoint &breakpoint)
+{
+ breakpoint.id = this->id;
+ breakpoint.verified = this->IsResolved();
+ breakpoint.source = Source(this->fullname);
+ breakpoint.line = this->linenum;
+ breakpoint.hitCount = this->times;
+}
-HRESULT GetCurrentBreakpoint(ICorDebugThread *pThread, Breakpoint &breakpoint)
+HRESULT Debugger::HitBreakpoint(ICorDebugThread *pThread, Breakpoint &breakpoint)
{
HRESULT Status;
IfFailRet(Modules::GetFrameLocation(pFrame, ilOffset, sp));
- std::lock_guard<std::mutex> lock(g_breakMutex);
+ std::lock_guard<std::mutex> lock(m_breakpointsMutex);
- for (auto &it : g_breaks)
- {
- ManagedBreakpoint &b = it.second;
-
- if (b.fullname == sp.document &&
- b.ilOffset == ilOffset &&
- b.methodToken == methodToken &&
- b.linenum == sp.startLine &&
- b.enabled)
- {
- b.ToBreakpoint(breakpoint);
- return S_OK;
- }
- }
-
- return E_FAIL;
-}
-
-HRESULT HitBreakpoint(ICorDebugThread *pThread, Breakpoint &breakpoint)
-{
- HRESULT Status;
-
- ULONG32 ilOffset;
- Modules::SequencePoint sp;
- mdMethodDef methodToken;
-
- ToRelease<ICorDebugFrame> pFrame;
- IfFailRet(pThread->GetActiveFrame(&pFrame));
- if (pFrame == nullptr)
+ auto breakpoints = m_breakpoints.find(sp.document);
+ if (breakpoints == m_breakpoints.end())
return E_FAIL;
- IfFailRet(pFrame->GetFunctionToken(&methodToken));
- IfFailRet(Modules::GetFrameLocation(pFrame, ilOffset, sp));
+ auto &breakpointsInSource = breakpoints->second;
+ auto it = breakpointsInSource.find(sp.startLine);
+ if (it == breakpointsInSource.end())
+ return E_FAIL;
- std::lock_guard<std::mutex> lock(g_breakMutex);
+ ManagedBreakpoint &b = it->second;
- for (auto &it : g_breaks)
+ if (b.ilOffset == ilOffset &&
+ b.methodToken == methodToken &&
+ b.enabled)
{
- ManagedBreakpoint &b = it.second;
-
- if (b.fullname == sp.document &&
- b.ilOffset == ilOffset &&
- b.methodToken == methodToken &&
- b.linenum == sp.startLine &&
- b.enabled)
- {
- ++b.times;
- b.ToBreakpoint(breakpoint);
- return S_OK;
- }
+ ++b.times;
+ b.ToBreakpoint(breakpoint);
+ return S_OK;
}
return E_FAIL;
}
-static void InsertBreakpoint(ManagedBreakpoint &bp, Breakpoint &breakpoint)
-{
- std::lock_guard<std::mutex> lock(g_breakMutex);
- ULONG32 id = g_breakIndex++;
- bp.id = id;
- g_breaks.insert(std::make_pair(id, std::move(bp)));
- bp.ToBreakpoint(breakpoint);
-}
-
-void InsertExceptionBreakpoint(const std::string &name, Breakpoint &breakpoint)
+void Debugger::InsertExceptionBreakpoint(const std::string &name, Breakpoint &breakpoint)
{
- ManagedBreakpoint bp;
- InsertBreakpoint(bp, breakpoint);
-}
-
-HRESULT DeleteBreakpoint(ULONG32 id)
-{
- std::lock_guard<std::mutex> lock(g_breakMutex);
-
- g_breaks.erase(id);
-
- return S_OK;
+ std::lock_guard<std::mutex> lock(m_breakpointsMutex);
+ m_nextBreakpointId++;
}
-void DeleteAllBreakpoints()
+void Debugger::DeleteAllBreakpoints()
{
- std::lock_guard<std::mutex> lock(g_breakMutex);
+ std::lock_guard<std::mutex> lock(m_breakpointsMutex);
- g_breaks.clear();
+ m_breakpoints.clear();
}
-static HRESULT ResolveBreakpointInModule(ICorDebugModule *pModule, ManagedBreakpoint &bp)
+HRESULT Debugger::ResolveBreakpointInModule(ICorDebugModule *pModule, ManagedBreakpoint &bp)
{
HRESULT Status;
std::string fullname;
IfFailRet(Modules::GetLocationInModule(
- pModule, bp.fullname,
+ pModule,
+ bp.fullname,
bp.linenum,
ilOffset,
methodToken,
return S_OK;
}
-static HRESULT ResolveBreakpoint(ManagedBreakpoint &bp)
+void Debugger::TryResolveBreakpointsForModule(ICorDebugModule *pModule)
+{
+ std::lock_guard<std::mutex> lock(m_breakpointsMutex);
+
+ for (auto &breakpoints : m_breakpoints)
+ {
+ for (auto &it : breakpoints.second)
+ {
+ ManagedBreakpoint &b = it.second;
+
+ if (b.IsResolved())
+ continue;
+
+ if (SUCCEEDED(ResolveBreakpointInModule(pModule, b)))
+ {
+ Breakpoint breakpoint;
+ b.ToBreakpoint(breakpoint);
+ m_protocol->EmitBreakpointEvent(BreakpointEvent(BreakpointChanged, breakpoint));
+ }
+ }
+ }
+}
+
+HRESULT Debugger::ResolveBreakpoint(ManagedBreakpoint &bp)
{
HRESULT Status;
return S_OK;
}
-void Debugger::TryResolveBreakpointsForModule(ICorDebugModule *pModule)
+HRESULT Debugger::SetBreakpoints(std::string filename,
+ const std::vector<int> &lines,
+ std::vector<Breakpoint> &breakpoints)
{
- std::lock_guard<std::mutex> lock(g_breakMutex);
+ std::lock_guard<std::mutex> lock(m_breakpointsMutex);
- for (auto &it : g_breaks)
+ if (lines.empty())
{
- ManagedBreakpoint &b = it.second;
+ auto it = m_breakpoints.find(filename);
+ if (it != m_breakpoints.end())
+ m_breakpoints.erase(it);
+ return S_OK;
+ }
- if (b.IsResolved())
- continue;
+ Source source(filename);
- if (SUCCEEDED(ResolveBreakpointInModule(pModule, b)))
- {
- Breakpoint breakpoint;
- b.ToBreakpoint(breakpoint);
- m_protocol->EmitBreakpointEvent(BreakpointEvent(BreakpointChanged, breakpoint));
- }
+ auto &breakpointsInSource = m_breakpoints[filename];
+
+ // Remove old breakpoints
+ std::unordered_set<int> unchangedLines;
+ for (int line : lines)
+ {
+ if (breakpointsInSource.find(line) != breakpointsInSource.end())
+ unchangedLines.insert(line);
}
-}
-HRESULT InsertBreakpointInProcess(ICorDebugProcess *pProcess, std::string filename, int linenum, Breakpoint &breakpoint)
-{
- ManagedBreakpoint bp;
- bp.fullname = filename;
- bp.linenum = linenum;
+ std::unordered_set<int> removedLines;
+ for (auto &b : breakpointsInSource)
+ if (unchangedLines.find(b.first) == unchangedLines.end())
+ removedLines.insert(b.first);
+
+ for (int line : removedLines)
+ breakpointsInSource.erase(line);
- if (pProcess)
- ResolveBreakpoint(bp);
+ // Export breakpoints
- InsertBreakpoint(bp, breakpoint);
+ for (int line : lines)
+ {
+ Breakpoint breakpoint;
+
+ auto b = breakpointsInSource.find(line);
+ if (b == breakpointsInSource.end())
+ {
+ // New breakpoint
+ ManagedBreakpoint bp;
+ bp.id = m_nextBreakpointId++;
+ bp.fullname = filename;
+ bp.linenum = line;
+
+ if (m_pProcess)
+ ResolveBreakpoint(bp);
+
+ bp.ToBreakpoint(breakpoint);
+ breakpointsInSource.insert(std::make_pair(line, std::move(bp)));
+ }
+ else
+ {
+ // Existing breakpoint
+ b->second.ToBreakpoint(breakpoint);
+ }
+
+ breakpoints.push_back(breakpoint);
+ }
return S_OK;
}
+++ /dev/null
-// Copyright (c) 2017 Samsung Electronics Co., LTD
-// Distributed under the MIT License.
-// See the LICENSE file in the project root for more information.
-
-HRESULT DeleteBreakpoint(ULONG32 id);
-HRESULT InsertBreakpointInProcess(ICorDebugProcess *pProcess, std::string filename, int linenum, Breakpoint &breakpoint);
-void InsertExceptionBreakpoint(const std::string &name, Breakpoint &breakpoint);
-HRESULT GetCurrentBreakpoint(ICorDebugThread *pThread, Breakpoint &breakpoint);
-
-void DeleteAllBreakpoints();
-HRESULT HitBreakpoint(ICorDebugThread *pThread, Breakpoint &breakpoint);
#include "platform.h"
#include "debugger.h"
#include "modules.h"
-#include "breakpoints.h"
#include "frames.h"
{
switch(event.reason)
{
- case StopBreakpoint:
+ case BreakpointChanged:
{
std::string output;
PrintBreakpoint(event.breakpoint, output);
return GetThreadsState(m_pProcess, threads);
}
-HRESULT Debugger::SetBreakpoint(std::string filename, int linenum, Breakpoint &breakpoint)
-{
- return InsertBreakpointInProcess(m_pProcess, filename, linenum, breakpoint);
-}
-
HRESULT Debugger::GetStackTrace(int threadId, int lowFrame, int highFrame, std::vector<StackFrame> &stackFrames)
{
HRESULT Status;
{
m_vars.clear();
m_varCounter = 0;
+ m_breakpoints.clear();
}
void MIProtocol::PrintChildren(std::vector<Variable> &children, int threadId, int print_values, bool has_more, std::string &output)
return S_OK;
}
+HRESULT MIProtocol::SetBreakpoint(const std::string &filename, int linenum, Breakpoint &breakpoint)
+{
+ HRESULT Status;
+
+ auto &breakpointsInSource = m_breakpoints[filename];
+ std::vector<int> lines;
+ for (auto it : breakpointsInSource)
+ {
+ lines.push_back(it.second);
+ }
+ lines.push_back(linenum);
+
+ std::vector<Breakpoint> breakpoints;
+ IfFailRet(m_debugger->SetBreakpoints(filename, lines, breakpoints));
+
+ breakpoint = breakpoints.back();
+ breakpointsInSource.insert(std::make_pair(breakpoint.id, linenum));
+
+ return S_OK;
+}
+
+void MIProtocol::DeleteBreakpoints(const std::unordered_set<uint32_t> &ids)
+{
+ for (auto &breakpointsIter : m_breakpoints)
+ {
+ std::vector<int> remainingLines;
+ for (auto it : breakpointsIter.second)
+ {
+ if (ids.find(it.first) == ids.end())
+ remainingLines.push_back(it.second);
+ }
+ if (remainingLines.size() == breakpointsIter.second.size())
+ continue;
+
+ std::string filename = breakpointsIter.first;
+
+ std::vector<Breakpoint> tmpBreakpoints;
+ m_debugger->SetBreakpoints(filename, remainingLines, tmpBreakpoints);
+ }
+}
+
void MIProtocol::EmitStoppedEvent(StoppedEvent event)
{
HRESULT Status;
ULONG32 id;
Breakpoint breakpoint;
if (ParseBreakpoint(args, filename, linenum)
- && SUCCEEDED(m_debugger->SetBreakpoint(filename, linenum, breakpoint)))
+ && SUCCEEDED(SetBreakpoint(filename, linenum, breakpoint)))
{
PrintBreakpoint(breakpoint, output);
return S_OK;
output = "Unknown breakpoint location format";
return E_FAIL;
} },
- { "break-delete", [](const std::vector<std::string> &args, std::string &) -> HRESULT {
+ { "break-delete", [this](const std::vector<std::string> &args, std::string &) -> HRESULT {
+ std::unordered_set<uint32_t> ids;
for (const std::string &idStr : args)
{
bool ok;
int id = ParseInt(idStr, ok);
if (ok)
- DeleteBreakpoint(id);
+ ids.insert(id);
}
+ DeleteBreakpoints(ids);
return S_OK;
} },
{ "exec-step", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
{ "interpreter-exec", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
return S_OK;
}},
- { "break-exception-insert", [](const std::vector<std::string> &args, std::string &output) -> HRESULT {
+ { "break-exception-insert", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
if (args.empty())
return E_FAIL;
size_t i = 1;
for (; i < args.size(); i++)
{
Breakpoint b;
- InsertExceptionBreakpoint(args.at(i), b);
+ m_debugger->InsertExceptionBreakpoint(args.at(i), b);
ss << sep;
sep = ",";
ss << "{number=\"" << b.id << "\"}";
#include "protocol.h"
#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+#include <condition_variable>
class ManagedCallback;
class Protocol;
void AddVariableReference(Variable &variable, uint64_t frameId, ICorDebugValue *value, ValueKind valueKind);
+ struct ManagedBreakpoint {
+ uint32_t id;
+ CORDB_ADDRESS modAddress;
+ mdMethodDef methodToken;
+ ULONG32 ilOffset;
+ std::string fullname;
+ int linenum;
+ ToRelease<ICorDebugBreakpoint> breakpoint;
+ bool enabled;
+ ULONG32 times;
+
+ bool IsResolved() const { return modAddress != 0; }
+
+ ManagedBreakpoint();
+ ~ManagedBreakpoint();
+
+ void ToBreakpoint(Breakpoint &breakpoint);
+
+ ManagedBreakpoint(ManagedBreakpoint &&that) = default;
+ private:
+ ManagedBreakpoint(const ManagedBreakpoint &that) = delete;
+ };
+
+ uint32_t m_nextBreakpointId;
+ std::mutex m_breakpointsMutex;
+ std::unordered_map<std::string, std::unordered_map<int, ManagedBreakpoint> > m_breakpoints;
+ HRESULT HitBreakpoint(ICorDebugThread *pThread, Breakpoint &breakpoint);
+ void DeleteAllBreakpoints();
+
+ HRESULT ResolveBreakpointInModule(ICorDebugModule *pModule, ManagedBreakpoint &bp);
+ HRESULT Debugger::ResolveBreakpoint(ManagedBreakpoint &bp);
+
HRESULT CheckNoProcess();
static VOID StartupCallback(IUnknown *pCordb, PVOID parameter, HRESULT hr);
HRESULT Continue();
HRESULT Pause();
HRESULT GetThreads(std::vector<Thread> &threads);
- HRESULT SetBreakpoint(std::string filename, int linenum, Breakpoint &breakpoint);
+ HRESULT SetBreakpoints(std::string filename, const std::vector<int> &lines, std::vector<Breakpoint> &breakpoints);
+ void InsertExceptionBreakpoint(const std::string &name, Breakpoint &breakpoint);
HRESULT GetStackTrace(int threadId, int lowFrame, int highFrame, std::vector<StackFrame> &stackFrames);
HRESULT StepCommand(int threadId, StepType stepType);
HRESULT GetScopes(uint64_t frameId, std::vector<Scope> &scopes);
unsigned int m_varCounter;
std::unordered_map<std::string, Variable> m_vars;
+ std::unordered_map<std::string, std::unordered_map<int32_t, int> > m_breakpoints;
public:
void SetDebugger(Debugger *debugger) { m_debugger = debugger; m_debugger->SetProtocol(this); }
static std::string EscapeMIValue(const std::string &str);
void PrintChildren(std::vector<Variable> &children, int threadId, int print_values, bool has_more, std::string &output);
void PrintNewVar(std::string varobjName, Variable &v, int threadId, int print_values, std::string &output);
HRESULT ListChildren(int threadId, int level, int childStart, int childEnd, const std::string &varName, int print_values, std::string &output);
+ HRESULT SetBreakpoint(const std::string &filename, int linenum, Breakpoint &breakpoints);
+ void DeleteBreakpoints(const std::unordered_set<uint32_t> &ids);
};
HRESULT DisableAllSteppers(ICorDebugProcess *pProcess);
#include "debugger.h"
#include "modules.h"
#include "valuewalk.h"
-#include "breakpoints.h"
-#include "valuewalk.h"
#include "frames.h"
#define __in
}
}
- // FIXME: Delete all or Release all?
- DeleteAllBreakpoints();
-
DisableAllSteppersInAppDomain(pAppDomain);
return S_OK;
pThread->GetID(&threadId);
StoppedEvent event(StopBreakpoint, threadId);
- HitBreakpoint(pThread, event.breakpoint);
+ m_debugger->HitBreakpoint(pThread, event.breakpoint);
ToRelease<ICorDebugFrame> pFrame;
if (SUCCEEDED(pThread->GetActiveFrame(&pFrame)) && pFrame != nullptr)
m_startupResult(S_OK),
m_unregisterToken(nullptr),
m_processId(0),
- m_nextVariableReference(1)
+ m_nextVariableReference(1),
+ m_nextBreakpointId(1)
{
m_managedCallback->m_debugger = this;
}
if (SUCCEEDED(m_pProcess->Stop(0)))
{
+ DeleteAllBreakpoints();
DisableAllBreakpointsAndSteppers(m_pProcess);
m_pProcess->Detach();
}
#include "platform.h"
#include <string>
+#include <vector>
// From https://github.com/Microsoft/vscode-debugadapter-node/blob/master/protocol/src/debugProtocol.ts