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;
ManagedBreakpoint(const ManagedBreakpoint &that) = delete;
static std::map<ULONG32, ManagedBreakpoint> g_breaks;
-static HRESULT PrintBreakpoint(const ManagedBreakpoint &b, std::string &output)
+HRESULT GetCurrentBreakpoint(ICorDebugThread *pThread, Breakpoint &breakpoint)
{
HRESULT Status;
- std::stringstream ss;
+ ULONG32 ilOffset;
+ Modules::SequencePoint sp;
+ mdMethodDef methodToken;
- if (b.IsResolved())
- {
- ss << "bkpt={number=\"" << b.id << "\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\","
- "func=\"\",fullname=\"" << Debugger::EscapeMIValue(b.fullname) << "\",line=\"" << b.linenum << "\"}";
- Status = S_OK;
- }
- else
- {
- ss << "bkpt={number=\"" << b.id << "\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\","
- "warning=\"No executable code of the debugger's target code type is associated with this line.\"}";
- Status = S_FALSE;
- }
- output = ss.str();
- return Status;
-}
+ ToRelease<ICorDebugFrame> pFrame;
+ IfFailRet(pThread->GetActiveFrame(&pFrame));
+ if (pFrame == nullptr)
+ return E_FAIL;
+ IfFailRet(pFrame->GetFunctionToken(&methodToken));
+
+ IfFailRet(Modules::GetFrameLocation(pFrame, ilOffset, sp));
-HRESULT PrintBreakpoint(ULONG32 id, std::string &output)
-{
std::lock_guard<std::mutex> lock(g_breakMutex);
- auto it = g_breaks.find(id);
+ for (auto &it : g_breaks)
+ {
+ ManagedBreakpoint &b = it.second;
- if (it == g_breaks.end())
- return E_FAIL;
+ if (b.fullname == sp.document &&
+ b.ilOffset == ilOffset &&
+ b.methodToken == methodToken &&
+ b.linenum == sp.startLine &&
+ b.enabled)
+ {
+ b.ToBreakpoint(breakpoint);
+ return S_OK;
+ }
+ }
- return PrintBreakpoint(it->second, output);
+ return E_FAIL;
}
HRESULT HitBreakpoint(ICorDebugThread *pThread, ULONG32 &id, ULONG32 ×)
return E_FAIL;
}
-static ULONG32 InsertBreakpoint(ManagedBreakpoint &bp)
+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)));
- return id;
+ bp.ToBreakpoint(breakpoint);
}
-ULONG32 InsertExceptionBreakpoint(const std::string &name)
+void InsertExceptionBreakpoint(const std::string &name, Breakpoint &breakpoint)
{
ManagedBreakpoint bp;
- return InsertBreakpoint(bp);
+ InsertBreakpoint(bp, breakpoint);
}
HRESULT DeleteBreakpoint(ULONG32 id)
if (SUCCEEDED(ResolveBreakpointInModule(pModule, b)))
{
- std::string output;
- PrintBreakpoint(b, output);
- Debugger::Printf("=breakpoint-modified,%s\n", output.c_str());
+ Breakpoint breakpoint;
+ b.ToBreakpoint(breakpoint);
+ Debugger::EmitBreakpointEvent(BreakpointEvent(BreakpointChanged, breakpoint));
}
}
}
-static HRESULT CreateBreakpointInProcess(ManagedBreakpoint &bp, ULONG32 &id)
-{
- if (SUCCEEDED(ResolveBreakpoint(bp)))
- {
- id = InsertBreakpoint(bp);
- return S_OK;
- }
- return S_FALSE;
-}
-
-HRESULT InsertBreakpointInProcess(ICorDebugProcess *pProcess, std::string filename, int linenum, ULONG32 &id)
+HRESULT InsertBreakpointInProcess(ICorDebugProcess *pProcess, std::string filename, int linenum, Breakpoint &breakpoint)
{
ManagedBreakpoint bp;
bp.fullname = filename;
bp.linenum = linenum;
- HRESULT Status = pProcess ? CreateBreakpointInProcess(bp, id) : S_FALSE;
+ if (pProcess)
+ ResolveBreakpoint(bp);
- if (Status == S_FALSE)
- {
- // Add pending breakpoint
- id = InsertBreakpoint(bp);
- }
+ InsertBreakpoint(bp, breakpoint);
- return Status;
+ return S_OK;
}
// See the LICENSE file in the project root for more information.
HRESULT DeleteBreakpoint(ULONG32 id);
-HRESULT InsertBreakpointInProcess(ICorDebugProcess *pProcess, std::string filename, int linenum, ULONG32 &id);
-HRESULT PrintBreakpoint(ULONG32 id, std::string &output);
-ULONG32 InsertExceptionBreakpoint(const std::string &name);
-HRESULT CreateBreakpointInProcess(ICorDebugProcess *pProcess, std::string filename, int linenum, ULONG32 &id);
-HRESULT PrintBreakpoint(ULONG32 id, std::string &output);
+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, ULONG32 &id, ULONG32 ×);
return ok && linenum > 0;
}
+
+static HRESULT PrintBreakpoint(const Breakpoint &b, std::string &output)
+{
+ HRESULT Status;
+
+ std::stringstream ss;
+
+ if (b.verified)
+ {
+ ss << "bkpt={number=\"" << b.id << "\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\","
+ "func=\"\",fullname=\"" << Debugger::EscapeMIValue(b.source.path) << "\",line=\"" << b.line << "\"}";
+ Status = S_OK;
+ }
+ else
+ {
+ ss << "bkpt={number=\"" << b.id << "\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\","
+ "warning=\"No executable code of the debugger's target code type is associated with this line.\"}";
+ Status = S_FALSE;
+ }
+ output = ss.str();
+ return Status;
+}
+
+HRESULT Debugger::EmitBreakpointEvent(BreakpointEvent event)
+{
+ switch(event.reason)
+ {
+ case StopBreakpoint:
+ {
+ std::string output;
+ PrintBreakpoint(event.breakpoint, output);
+ Debugger::Printf("=breakpoint-modified,%s\n", output.c_str());
+ return S_OK;
+ }
+ default:
+ return S_OK;
+ }
+}
+
static HRESULT BreakInsertCommand(
ICorDebugProcess *pProcess,
const std::vector<std::string> &args,
std::string filename;
unsigned int linenum;
ULONG32 id;
+ Breakpoint breakpoint;
if (ParseBreakpoint(args, filename, linenum)
- && SUCCEEDED(InsertBreakpointInProcess(pProcess, filename, linenum, id)))
+ && SUCCEEDED(InsertBreakpointInProcess(pProcess, filename, linenum, breakpoint)))
{
- PrintBreakpoint(id, output);
+ PrintBreakpoint(breakpoint, output);
return S_OK;
}
return S_OK;
}
+HRESULT Debugger::EmitStoppedEvent(StoppedEvent event)
+{
+ HRESULT Status;
+ ToRelease<ICorDebugThread> pThread;
+
+ switch(event.reason)
+ {
+ case StopBreakpoint:
+ {
+ IfFailRet(m_pProcess->GetThread(event.threadId, &pThread));
+
+ StackFrame stackFrame;
+ ToRelease<ICorDebugFrame> pFrame;
+ if (SUCCEEDED(pThread->GetActiveFrame(&pFrame)) && pFrame != nullptr)
+ GetFrameLocation(pFrame, stackFrame);
+
+ Breakpoint b;
+ IfFailRet(GetCurrentBreakpoint(pThread, b));
+
+ std::string output;
+ PrintFrameLocation(stackFrame, output);
+ Debugger::Printf("*stopped,reason=\"breakpoint-hit\",thread-id=\"%i\",stopped-threads=\"all\",bkptno=\"%u\",times=\"%u\",frame={%s}\n",
+ event.threadId, (unsigned int)b.id, (unsigned int)b.hitCount, output.c_str());
+ return S_OK;
+ }
+ default:
+ return S_OK;
+ }
+}
+
HRESULT Debugger::HandleCommand(std::string command,
const std::vector<std::string> &args,
std::string &output)
ss << "bkpt=[";
for (; i < args.size(); i++)
{
- ULONG32 id = InsertExceptionBreakpoint(args.at(i));
+ Breakpoint b;
+ InsertExceptionBreakpoint(args.at(i), b);
ss << sep;
sep = ",";
- ss << "{number=\"" << id << "\"}";
+ ss << "{number=\"" << b.id << "\"}";
}
ss << "]";
output = ss.str();
static HRESULT StepCommand(ICorDebugProcess *pProcess,
const std::vector<std::string> &args,
std::string &output, StepType stepType);
+
+ HRESULT EmitStoppedEvent(StoppedEvent event);
+
+
public:
+ static HRESULT EmitBreakpointEvent(BreakpointEvent event);
+
static bool IsJustMyCode() { return m_justMyCode; }
static void SetJustMyCode(bool enable) { m_justMyCode = enable; }
- Debugger(ManagedCallback *cb) :
- m_managedCallback(cb),
+ Debugger() :
+ m_managedCallback(nullptr),
m_pDebug(nullptr),
m_pProcess(nullptr),
m_exit(false),
~Debugger();
+ void SetManagedCallback(ManagedCallback *managedCallback);
+
static void Printf(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
static void Message(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
static std::string EscapeMIValue(const std::string &str);
class ManagedCallback : public ICorDebugManagedCallback, ICorDebugManagedCallback2
{
+ friend class Debugger;
ULONG m_refCount;
+ Debugger *m_debugger;
public:
void HandleEvent(ICorDebugController *controller, const char *eventName)
controller->Continue(0);
}
- ManagedCallback() : m_refCount(1) {}
+ ManagedCallback() : m_refCount(1), m_debugger(nullptr) {}
virtual ~ManagedCallback() {}
// IUnknown
return S_OK;
}
- ULONG32 id = 0;
- ULONG32 times = 0;
- HitBreakpoint(pThread, id, times);
-
- StackFrame stackFrame;
- ToRelease<ICorDebugFrame> pFrame;
- if (SUCCEEDED(pThread->GetActiveFrame(&pFrame)) && pFrame != nullptr)
- GetFrameLocation(pFrame, stackFrame);
-
DWORD threadId = 0;
pThread->GetID(&threadId);
- std::string output;
- PrintFrameLocation(stackFrame, output);
- Debugger::Printf("*stopped,reason=\"breakpoint-hit\",thread-id=\"%i\",stopped-threads=\"all\",bkptno=\"%u\",times=\"%u\",frame={%s}\n",
- (int)threadId, (unsigned int)id, (unsigned int)times, output.c_str());
+ ULONG32 id = 0;
+ ULONG32 times = 0;
+ HitBreakpoint(pThread, id, times);
SetLastStoppedThread(pThread);
+ m_debugger->EmitStoppedEvent(StoppedEvent(StopBreakpoint, threadId));
return S_OK;
}
bool Debugger::m_justMyCode = true;
+void Debugger::SetManagedCallback(ManagedCallback *managedCallback)
+{
+ m_managedCallback = managedCallback;
+ m_managedCallback->m_debugger = this;
+}
+
Debugger::~Debugger()
{
m_managedCallback->Release();
}
}
- Debugger debugger(new ManagedCallback());
+ Debugger debugger;
+ debugger.SetManagedCallback(new ManagedCallback());
if (pidDebuggee != 0)
{
int endColumn;
std::string moduleId;
- ClrAddr clrAddr;
+ ClrAddr clrAddr; // exposed for MI protocol
StackFrame() :
id(0), line(0), column(0), endLine(0), endColumn(0) {}
StackFrame(uint64_t id, std::string name) : id(id), name(name) {}
};
+
+enum StopReason
+{
+ StopStep,
+ StopBreakpoint,
+ StopException,
+ StopPause,
+ StopEntry
+};
+
+struct StoppedEvent
+{
+ StopReason reason;
+ std::string description;
+ int threadId;
+ std::string text;
+ bool allThreadsStopped;
+
+ StoppedEvent(StopReason reason, int threadId = 0) : reason(reason), threadId(threadId), allThreadsStopped(true) {}
+};
+
+struct Breakpoint
+{
+ uint32_t id;
+ bool verified;
+ std::string message;
+ Source source;
+ int line;
+
+ uint32_t hitCount; // exposed for MI protocol
+
+ Breakpoint() : id(0), verified(false), line(0), hitCount(0) {}
+};
+
+enum BreakpointReason
+{
+ BreakpointChanged,
+ BreakpointNew,
+ BreakpointRemoved
+};
+
+struct BreakpointEvent
+{
+ BreakpointReason reason;
+ Breakpoint breakpoint;
+
+ BreakpointEvent(BreakpointReason reason, Breakpoint breakpoint) : reason(reason), breakpoint(breakpoint) {}
+};