if (b.verified)
{
ss << "bkpt={number=\"" << b.id << "\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\","
- "func=\"\",fullname=\"" << Debugger::EscapeMIValue(b.source.path) << "\",line=\"" << b.line << "\"}";
+ "func=\"\",fullname=\"" << MIProtocol::EscapeMIValue(b.source.path) << "\",line=\"" << b.line << "\"}";
Status = S_OK;
}
else
return Status;
}
-void Debugger::EmitBreakpointEvent(BreakpointEvent event)
+void MIProtocol::EmitBreakpointEvent(BreakpointEvent event)
{
switch(event.reason)
{
{
std::string output;
PrintBreakpoint(event.breakpoint, output);
- Debugger::Printf("=breakpoint-modified,%s\n", output.c_str());
+ MIProtocol::Printf("=breakpoint-modified,%s\n", output.c_str());
return;
}
default:
return S_OK;
}
-HRESULT Debugger::StepCommand(ICorDebugProcess *pProcess,
- const std::vector<std::string> &args,
- std::string &output, StepType stepType)
+HRESULT Debugger::StepCommand(int threadId,
+ StepType stepType)
{
HRESULT Status;
ToRelease<ICorDebugThread> pThread;
- DWORD threadId = GetIntArg(args, "--thread", GetLastStoppedThreadId());
- IfFailRet(pProcess->GetThread(threadId, &pThread));
- DisableAllSteppers(pProcess);
+ IfFailRet(m_pProcess->GetThread(threadId, &pThread));
+ DisableAllSteppers(m_pProcess);
IfFailRet(SetupStep(pThread, stepType));
- IfFailRet(pProcess->Continue(0));
+ IfFailRet(m_pProcess->Continue(0));
+ return S_OK;
+}
+
+HRESULT MIProtocol::StepCommand(const std::vector<std::string> &args,
+ std::string &output,
+ Debugger::StepType stepType)
+{
+ HRESULT Status;
+ DWORD threadId = GetIntArg(args, "--thread", GetLastStoppedThreadId());
+ m_debugger->StepCommand(threadId, stepType);
output = "^running";
return S_OK;
}
if (!stackFrame.source.IsNull())
{
- ss << "file=\"" << Debugger::EscapeMIValue(stackFrame.source.name) << "\","
- << "fullname=\"" << Debugger::EscapeMIValue(stackFrame.source.path) << "\","
+ ss << "file=\"" << MIProtocol::EscapeMIValue(stackFrame.source.name) << "\","
+ << "fullname=\"" << MIProtocol::EscapeMIValue(stackFrame.source.path) << "\","
<< "line=\"" << stackFrame.line << "\","
<< "col=\"" << stackFrame.column << "\","
<< "end-line=\"" << stackFrame.endLine << "\","
for (const Thread& thread : threads)
{
ss << "{id=\"" << thread.id
- << "\",name=\"" << Debugger::EscapeMIValue(thread.name) << "\",state=\""
+ << "\",name=\"" << MIProtocol::EscapeMIValue(thread.name) << "\",state=\""
<< (thread.running ? "running" : "stopped") << "\"}" << sep;
sep = ",";
}
return S_OK;
}
-void Debugger::EmitStoppedEvent(StoppedEvent event)
+void MIProtocol::EmitStoppedEvent(StoppedEvent event)
{
HRESULT Status;
{
case StopBreakpoint:
{
- Debugger::Printf("*stopped,reason=\"breakpoint-hit\",thread-id=\"%i\",stopped-threads=\"all\",bkptno=\"%u\",times=\"%u\",frame={%s}\n",
+ MIProtocol::Printf("*stopped,reason=\"breakpoint-hit\",thread-id=\"%i\",stopped-threads=\"all\",bkptno=\"%u\",times=\"%u\",frame={%s}\n",
event.threadId, (unsigned int)event.breakpoint.id, (unsigned int)event.breakpoint.hitCount, frameLocation.c_str());
return;
}
case StopStep:
{
- Debugger::Printf("*stopped,reason=\"end-stepping-range\",thread-id=\"%i\",stopped-threads=\"all\",frame={%s}\n",
+ MIProtocol::Printf("*stopped,reason=\"end-stepping-range\",thread-id=\"%i\",stopped-threads=\"all\",frame={%s}\n",
event.threadId, frameLocation.c_str());
return;
}
{
std::string category = "clr";
std::string stage = "unhandled";
- Debugger::Printf("*stopped,reason=\"exception-received\",exception-name=\"%s\",exception=\"%s\",exception-stage=\"%s\",exception-category=\"%s\",thread-id=\"%i\",stopped-threads=\"all\",frame={%s}\n",
+ MIProtocol::Printf("*stopped,reason=\"exception-received\",exception-name=\"%s\",exception=\"%s\",exception-stage=\"%s\",exception-category=\"%s\",thread-id=\"%i\",stopped-threads=\"all\",frame={%s}\n",
event.text.c_str(),
- Debugger::EscapeMIValue(event.description).c_str(),
+ MIProtocol::EscapeMIValue(event.description).c_str(),
stage.c_str(),
category.c_str(),
event.threadId,
}
}
-void Debugger::EmitExitedEvent(ExitedEvent event)
+void MIProtocol::EmitExitedEvent(ExitedEvent event)
{
- Debugger::Printf("*stopped,reason=\"exited\",exit-code=\"%i\"\n", event.exitCode);
+ MIProtocol::Printf("*stopped,reason=\"exited\",exit-code=\"%i\"\n", event.exitCode);
}
-void Debugger::EmitThreadEvent(ThreadEvent event)
+void MIProtocol::EmitThreadEvent(ThreadEvent event)
{
const char *reasonText = "";
switch(event.reason)
reasonText = "thread-exited";
break;
}
- Debugger::Printf("=%s,id=\"%i\"\n", reasonText, event.threadId);
+ MIProtocol::Printf("=%s,id=\"%i\"\n", reasonText, event.threadId);
}
-void Debugger::EmitOutputEvent(OutputEvent event)
+void MIProtocol::EmitOutputEvent(OutputEvent event)
{
if (event.source.empty())
- Debugger::Printf("=message,text=\"%s\",send-to=\"output-window\"\"\n",
- Debugger::EscapeMIValue(event.output).c_str());
+ MIProtocol::Printf("=message,text=\"%s\",send-to=\"output-window\"\"\n",
+ MIProtocol::EscapeMIValue(event.output).c_str());
else
- Debugger::Printf("=message,text=\"%s\",send-to=\"output-window\",source=\"%s\"\n",
- Debugger::EscapeMIValue(event.output).c_str(),
- Debugger::EscapeMIValue(event.source).c_str());
+ MIProtocol::Printf("=message,text=\"%s\",send-to=\"output-window\",source=\"%s\"\n",
+ MIProtocol::EscapeMIValue(event.output).c_str(),
+ MIProtocol::EscapeMIValue(event.source).c_str());
}
-HRESULT Debugger::HandleCommand(std::string command,
- const std::vector<std::string> &args,
- std::string &output)
+HRESULT MIProtocol::HandleCommand(std::string command,
+ const std::vector<std::string> &args,
+ std::string &output)
{
static std::unordered_map<std::string, CommandCallback> commands {
{ "thread-info", ThreadInfoCommand },
}
return S_OK;
} },
- { "exec-step", std::bind(StepCommand, _1, _2, _3, STEP_IN) },
- { "exec-next", std::bind(StepCommand, _1, _2, _3, STEP_OVER) },
- { "exec-finish", std::bind(StepCommand, _1, _2, _3, STEP_OUT) },
+ { "exec-step", [this](ICorDebugProcess *, const std::vector<std::string> &args, std::string &output) -> HRESULT {
+ return StepCommand(args, output, Debugger::STEP_IN);
+ }},
+ { "exec-next", [this](ICorDebugProcess *, const std::vector<std::string> &args, std::string &output) -> HRESULT {
+ return StepCommand(args, output, Debugger::STEP_OVER);
+ }},
+ { "exec-finish", [this](ICorDebugProcess *, const std::vector<std::string> &args, std::string &output) -> HRESULT {
+ return StepCommand(args, output, Debugger::STEP_OUT);
+ }},
{ "exec-abort", [this](ICorDebugProcess *, const std::vector<std::string> &, std::string &output) -> HRESULT {
- this->TerminateProcess();
+ m_debugger->TerminateProcess();
return S_OK;
}},
{ "target-attach", [this](ICorDebugProcess *, const std::vector<std::string> &args, std::string &output) -> HRESULT {
bool ok;
int pid = ParseInt(args.at(0), ok);
if (!ok) return E_INVALIDARG;
- IfFailRet(this->AttachToProcess(pid));
+ IfFailRet(m_debugger->AttachToProcess(pid));
// TODO: print succeessful result
return S_OK;
}},
{ "target-detach", [this](ICorDebugProcess *, const std::vector<std::string> &, std::string &output) -> HRESULT {
- this->DetachFromProcess();
+ m_debugger->DetachFromProcess();
return S_OK;
}},
{ "stack-list-frames", [](ICorDebugProcess *pProcess, const std::vector<std::string> &args_orig, std::string &output) -> HRESULT {
{ "gdb-exit", [this](ICorDebugProcess *, const std::vector<std::string> &args, std::string &output) -> HRESULT {
this->m_exit = true;
- this->TerminateProcess();
+ m_debugger->TerminateProcess();
return S_OK;
}},
return S_OK;
}},
{ "exec-run", [this](ICorDebugProcess *, const std::vector<std::string> &args, std::string &output) -> HRESULT {
- HRESULT Status = RunProcess();
+ HRESULT Status = m_debugger->RunProcess(m_fileExec, m_execArgs);
if (SUCCEEDED(Status))
output = "^running";
return Status;
return E_FAIL;
}
- return command_it->second(m_pProcess, args, output);
+ return command_it->second(m_debugger->GetProcess(), args, output);
}
static std::vector<std::string> TokenizeString(const std::string &str, const char *delimiters = " \t\n\r")
return true;
}
-void Debugger::CommandLoop()
+void MIProtocol::CommandLoop()
{
static char inputBuffer[1024];
std::string token;
}
if (!m_exit)
- TerminateProcess();
+ m_debugger->TerminateProcess();
Printf("%s^exit\n", token.c_str());
}
class ManagedCallback;
+class Protocol;
class Debugger
{
+public:
+ enum StepType {
+ STEP_IN = 0,
+ STEP_OVER,
+ STEP_OUT
+ };
+
+private:
friend class ManagedCallback;
+ Protocol *m_protocol;
ManagedCallback *m_managedCallback;
ICorDebug *m_pDebug;
ICorDebugProcess *m_pProcess;
- bool m_exit;
static bool m_justMyCode;
- static std::mutex m_outMutex;
-
- std::string m_fileExec;
- std::vector<std::string> m_execArgs;
std::mutex m_startupMutex;
std::condition_variable m_startupCV;
HRESULT CheckNoProcess();
- HRESULT HandleCommand(std::string command,
- const std::vector<std::string> &args,
- std::string &output);
-
static VOID StartupCallback(IUnknown *pCordb, PVOID parameter, HRESULT hr);
HRESULT Startup(IUnknown *punk, int pid);
- HRESULT RunProcess();
-
void Cleanup();
- enum StepType {
- STEP_IN = 0,
- STEP_OVER,
- STEP_OUT
- };
-
static HRESULT SetupStep(ICorDebugThread *pThread, StepType stepType);
- static HRESULT StepCommand(ICorDebugProcess *pProcess,
- const std::vector<std::string> &args,
- std::string &output, StepType stepType);
-
- static void EmitStoppedEvent(StoppedEvent event);
- static void EmitExitedEvent(ExitedEvent event);
- static void EmitThreadEvent(ThreadEvent event);
- static void EmitOutputEvent(OutputEvent event);
public:
- static void EmitBreakpointEvent(BreakpointEvent event);
-
static bool IsJustMyCode() { return m_justMyCode; }
static void SetJustMyCode(bool enable) { m_justMyCode = enable; }
m_managedCallback(nullptr),
m_pDebug(nullptr),
m_pProcess(nullptr),
- m_exit(false),
m_startupReady(false),
m_startupResult(S_OK),
m_unregisterToken(nullptr),
~Debugger();
- void SetManagedCallback(ManagedCallback *managedCallback);
+ void TryResolveBreakpointsForModule(ICorDebugModule *pModule);
- static void Printf(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+ void SetProtocol(Protocol *protocol) { m_protocol = protocol; }
+ void SetManagedCallback(ManagedCallback *managedCallback);
- static std::string EscapeMIValue(const std::string &str);
+ HRESULT RunProcess(std::string fileExec, std::vector<std::string> execArgs);
HRESULT AttachToProcess(int pid);
HRESULT DetachFromProcess();
HRESULT TerminateProcess();
- void CommandLoop();
+ ICorDebugProcess *GetProcess() { return m_pProcess; }
+
+ HRESULT StepCommand(int threadId, StepType stepType);
+};
+
+class Protocol
+{
+public:
+ virtual void EmitStoppedEvent(StoppedEvent event) = 0;
+ virtual void EmitExitedEvent(ExitedEvent event) = 0;
+ virtual void EmitThreadEvent(ThreadEvent event) = 0;
+ virtual void EmitOutputEvent(OutputEvent event) = 0;
+ virtual void EmitBreakpointEvent(BreakpointEvent event) = 0;
+ virtual void CommandLoop() = 0;
+};
+
+class MIProtocol : public Protocol
+{
+ static std::mutex m_outMutex;
+ bool m_exit;
+ Debugger *m_debugger;
+
+ std::string m_fileExec;
+ std::vector<std::string> m_execArgs;
+public:
+ void SetDebugger(Debugger *debugger) { m_debugger = debugger; m_debugger->SetProtocol(this); }
+ static std::string EscapeMIValue(const std::string &str);
+
+ MIProtocol() : m_exit(false) {}
+ void EmitStoppedEvent(StoppedEvent event) override;
+ void EmitExitedEvent(ExitedEvent event) override;
+ void EmitThreadEvent(ThreadEvent event) override;
+ void EmitOutputEvent(OutputEvent event) override;
+ void EmitBreakpointEvent(BreakpointEvent event) override;
+ void CommandLoop() override;
+
+ static void Printf(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+
+private:
+ HRESULT HandleCommand(std::string command,
+ const std::vector<std::string> &args,
+ std::string &output);
+
+ HRESULT StepCommand(const std::vector<std::string> &args,
+ std::string &output,
+ Debugger::StepType stepType);
};
HRESULT DisableAllSteppers(ICorDebugProcess *pProcess);
return bRet;
}
-std::mutex Debugger::m_outMutex;
+std::mutex MIProtocol::m_outMutex;
-void Debugger::Printf(const char *fmt, ...)
+void MIProtocol::Printf(const char *fmt, ...)
{
std::lock_guard<std::mutex> lock(m_outMutex);
va_list arg;
}
// TODO: Merge with EscapeString
-std::string Debugger::EscapeMIValue(const std::string &str)
+std::string MIProtocol::EscapeMIValue(const std::string &str)
{
std::string s(str);
void HandleEvent(ICorDebugController *controller, const std::string &eventName)
{
std::string text = "Event received: '" + eventName + "'\n";
- m_debugger->EmitOutputEvent(OutputEvent(OutputConsole, text));
+ m_debugger->m_protocol->EmitOutputEvent(OutputEvent(OutputConsole, text));
controller->Continue(0);
}
GetFrameLocation(pFrame, threadId, 0, event.frame);
SetLastStoppedThread(pThread);
- m_debugger->EmitStoppedEvent(event);
+ m_debugger->m_protocol->EmitStoppedEvent(event);
return S_OK;
}
event.frame = stackFrame;
SetLastStoppedThread(pThread);
- m_debugger->EmitStoppedEvent(event);
+ m_debugger->m_protocol->EmitStoppedEvent(event);
}
return S_OK;
}
event.text = excType;
event.description = message.empty() ? details : message;
event.frame = stackFrame;
- m_debugger->EmitStoppedEvent(event);
+ m_debugger->m_protocol->EmitStoppedEvent(event);
};
if (FAILED(pThread->GetCurrentException(&pExceptionValue)) || FAILED(ObjectToString(pThread, pExceptionValue, emitFunc)))
std::string text = "Exception thrown: '" + excType + "' in " + excModule + "\n";
OutputEvent event(OutputConsole, text);
event.source = "target-exception";
- m_debugger->EmitOutputEvent(event);
+ m_debugger->m_protocol->EmitOutputEvent(event);
pAppDomain->Continue(0);
}
/* [in] */ ICorDebugProcess *pProcess)
{
NotifyEvalComplete(nullptr, nullptr);
- m_debugger->EmitExitedEvent(ExitedEvent(0));
+ m_debugger->m_protocol->EmitExitedEvent(ExitedEvent(0));
NotifyProcessExited();
return S_OK;
}
{
DWORD threadId = 0;
thread->GetID(&threadId);
- m_debugger->EmitThreadEvent(ThreadEvent(ThreadStarted, threadId));
+ m_debugger->m_protocol->EmitThreadEvent(ThreadEvent(ThreadStarted, threadId));
pAppDomain->Continue(0);
return S_OK;
}
NotifyEvalComplete(thread, nullptr);
DWORD threadId = 0;
thread->GetID(&threadId);
- m_debugger->EmitThreadEvent(ThreadEvent(ThreadExited, threadId));
+ m_debugger->m_protocol->EmitThreadEvent(ThreadEvent(ThreadExited, threadId));
pAppDomain->Continue(0);
return S_OK;
}
std::stringstream ss;
ss << "id=\"{" << id << "}\","
- << "target-name=\"" << Debugger::EscapeMIValue(name) << "\","
- << "host-name=\"" << Debugger::EscapeMIValue(name) << "\","
+ << "target-name=\"" << MIProtocol::EscapeMIValue(name) << "\","
+ << "host-name=\"" << MIProtocol::EscapeMIValue(name) << "\","
<< "symbols-loaded=\"" << symbolsLoaded << "\","
<< "base-address=\"0x" << std::hex << baseAddress << "\","
<< "size=\"" << std::dec << size << "\"";
- Debugger::Printf("=library-loaded,%s\n", ss.str().c_str());
+ MIProtocol::Printf("=library-loaded,%s\n", ss.str().c_str());
if (symbolsLoaded)
- TryResolveBreakpointsForModule(pModule);
+ m_debugger->TryResolveBreakpointsForModule(pModule);
pAppDomain->Continue(0);
return S_OK;
return S_OK;
}
-HRESULT Debugger::RunProcess()
+HRESULT Debugger::RunProcess(std::string fileExec, std::vector<std::string> execArgs)
{
static const auto startupCallbackWaitTimeout = std::chrono::milliseconds(5000);
HRESULT Status;
IfFailRet(CheckNoProcess());
std::stringstream ss;
- ss << "\"" << m_fileExec << "\"";
- for (std::string &arg : m_execArgs)
+ ss << "\"" << fileExec << "\"";
+ for (std::string &arg : execArgs)
{
- ss << " \"" << EscapeMIValue(arg) << "\"";
+ ss << " \"" << MIProtocol::EscapeMIValue(arg) << "\"";
}
std::string cmdString = ss.str();
std::unique_ptr<WCHAR[]> cmd(new WCHAR[cmdString.size() + 1]);
Debugger debugger;
debugger.SetManagedCallback(new ManagedCallback());
+ MIProtocol protocol;
+ protocol.SetDebugger(&debugger);
+
if (pidDebuggee != 0)
{
HRESULT Status = debugger.AttachToProcess(pidDebuggee);
}
}
- debugger.CommandLoop();
+ protocol.CommandLoop();
return EXIT_SUCCESS;
}