1 // Copyright (c) 2017 Samsung Electronics Co., LTD
2 // Distributed under the MIT License.
3 // See the LICENSE file in the project root for more information.
8 #include "miprotocol.h"
19 using namespace std::placeholders;
21 typedef std::function<HRESULT(
22 const std::vector<std::string> &args,
23 std::string &output)> CommandCallback;
26 static int ParseInt(const std::string &s, bool &ok)
31 int result = std::stoi(s);
35 catch(std::invalid_argument e)
38 catch (std::out_of_range e)
44 // Remove all --name value
45 static void StripArgs(std::vector<std::string> &args)
47 auto it = args.begin();
49 while (it != args.end())
51 if (it->find("--") == 0 && it + 1 != args.end())
52 it = args.erase(args.erase(it));
58 static int GetIntArg(const std::vector<std::string> &args, const std::string name, int defaultValue)
60 auto it = std::find(args.begin(), args.end(), name);
71 int val = ParseInt(*it, ok);
72 return ok ? val : defaultValue;
75 static bool GetIndices(const std::vector<std::string> &args, int &index1, int &index2)
81 int val1 = ParseInt(args.at(args.size() - 2), ok);
84 int val2 = ParseInt(args.at(args.size() - 1), ok);
93 const std::vector<std::string> &args_orig,
94 std::string &filename,
95 unsigned int &linenum,
96 std::string &condition)
98 std::vector<std::string> args = args_orig;
104 if (args.at(0) == "-f")
106 args.erase(args.begin());
111 if (args.at(0) == "-c")
115 condition = args.at(1);
116 args.erase(args.begin(), args.begin() + 2);
119 std::size_t i = args.at(0).rfind(':');
121 if (i == std::string::npos)
124 filename = args.at(0).substr(0, i);
125 std::string slinenum = args.at(0).substr(i + 1);
128 linenum = ParseInt(slinenum, ok);
129 return ok && linenum > 0;
133 HRESULT MIProtocol::PrintBreakpoint(const Breakpoint &b, std::string &output)
137 std::ostringstream ss;
141 ss << "bkpt={number=\"" << b.id << "\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",func=\"\","
142 "file=\"" << MIProtocol::EscapeMIValue(b.source.name) << "\","
143 "fullname=\"" << MIProtocol::EscapeMIValue(b.source.path) << "\","
144 "line=\"" << b.line << "\"}";
149 ss << "bkpt={number=\"" << b.id << "\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\","
150 "warning=\"No executable code of the debugger's target code type is associated with this line.\"}";
157 void MIProtocol::EmitBreakpointEvent(BreakpointEvent event)
163 case BreakpointChanged:
166 PrintBreakpoint(event.breakpoint, output);
167 MIProtocol::Printf("=breakpoint-modified,%s\n", output.c_str());
175 HRESULT MIProtocol::StepCommand(const std::vector<std::string> &args,
177 Debugger::StepType stepType)
179 DWORD threadId = GetIntArg(args, "--thread", m_debugger->GetLastStoppedThreadId());
180 m_debugger->StepCommand(threadId, stepType);
185 static std::string AddrToString(uint64_t addr)
187 std::ostringstream ss;
188 ss << "0x" << std::setw(2 * sizeof(void*)) << std::setfill('0') << std::hex << addr;
192 HRESULT MIProtocol::PrintFrameLocation(const StackFrame &stackFrame, std::string &output)
194 std::ostringstream ss;
196 if (!stackFrame.source.IsNull())
198 ss << "file=\"" << MIProtocol::EscapeMIValue(stackFrame.source.name) << "\","
199 << "fullname=\"" << MIProtocol::EscapeMIValue(stackFrame.source.path) << "\","
200 << "line=\"" << stackFrame.line << "\","
201 << "col=\"" << stackFrame.column << "\","
202 << "end-line=\"" << stackFrame.endLine << "\","
203 << "end-col=\"" << stackFrame.endColumn << "\",";
206 if (stackFrame.clrAddr.methodToken != 0)
208 ss << "clr-addr={module-id=\"{" << stackFrame.moduleId << "}\","
209 << "method-token=\"0x"
210 << std::setw(8) << std::setfill('0') << std::hex << stackFrame.clrAddr.methodToken << "\","
211 << "il-offset=\"" << std::dec << stackFrame.clrAddr.ilOffset
212 << "\",native-offset=\"" << stackFrame.clrAddr.nativeOffset << "\"},";
215 ss << "func=\"" << stackFrame.name << "\"";
216 if (stackFrame.id != 0)
217 ss << ",addr=\"" << AddrToString(stackFrame.addr) << "\"";
221 return stackFrame.source.IsNull() ? S_FALSE : S_OK;
224 HRESULT MIProtocol::PrintFrames(int threadId, std::string &output, int lowFrame, int highFrame)
227 std::ostringstream ss;
230 std::vector<StackFrame> stackFrames;
231 IfFailRet(m_debugger->GetStackTrace(threadId, lowFrame, highFrame - lowFrame, stackFrames, totalFrames));
233 int currentFrame = lowFrame;
236 const char *sep = "";
238 for (const StackFrame &stackFrame : stackFrames)
243 std::string frameLocation;
244 PrintFrameLocation(stackFrame, frameLocation);
246 ss << "frame={level=\"" << currentFrame << "\"";
247 if (!frameLocation.empty())
248 ss << "," << frameLocation;
260 HRESULT MIProtocol::PrintVariables(const std::vector<Variable> &variables, std::string &output)
262 const bool printValues = true;
263 const bool printTypes = false;
265 std::ostringstream ss;
267 const char *sep = "";
269 for (const Variable &var : variables)
274 ss << "{name=\"" << EscapeMIValue(var.name) << "\"";
275 ss << ",value=\"" << EscapeMIValue(var.value) << "\"";
284 bool MIProtocol::IsEditable(const std::string &type)
303 void MIProtocol::PrintVar(const std::string &varobjName, Variable &v, int threadId, int print_values, std::string &output)
305 std::ostringstream ss;
307 std::string editable;
308 if (IsEditable(v.type))
309 editable = "editable";
311 editable = "noneditable";
313 ss << "name=\"" << varobjName << "\",";
316 ss << "value=\"" << MIProtocol::EscapeMIValue(v.value) << "\",";
318 ss << "attributes=\"" << editable << "\",";
319 ss << "exp=\"" << (v.name.empty() ? v.evaluateName : v.name) << "\",";
320 ss << "numchild=\"" << v.namedVariables << "\",";
321 ss << "type=\"" << v.type << "\",";
322 ss << "thread-id=\"" << threadId << "\"";
328 void MIProtocol::PrintNewVar(std::string varobjName, Variable &v, int threadId, int print_values, std::string &output)
330 if (varobjName.empty() || varobjName == "-")
332 varobjName = "var" + std::to_string(m_varCounter++);
335 m_vars[varobjName] = v;
337 PrintVar(varobjName, v, threadId, print_values, output);
340 HRESULT MIProtocol::CreateVar(int threadId, int level, const std::string &varobjName, const std::string &expression, std::string &output)
344 uint64_t frameId = StackFrame(threadId, level, "").id;
347 IfFailRet(m_debugger->Evaluate(frameId, expression, variable, output));
349 int print_values = 1;
350 PrintNewVar(varobjName, variable, threadId, print_values, output);
355 HRESULT MIProtocol::DeleteVar(const std::string &varobjName)
357 return m_vars.erase(varobjName) == 0 ? E_FAIL : S_OK;
360 HRESULT MIProtocol::FindVar(const std::string &varobjName, Variable &variable)
362 auto it = m_vars.find(varobjName);
363 if (it == m_vars.end())
366 variable = it->second;
371 void MIProtocol::Cleanup()
375 m_breakpoints.clear();
378 void MIProtocol::PrintChildren(std::vector<Variable> &children, int threadId, int print_values, bool has_more, std::string &output)
380 std::ostringstream ss;
381 ss << "numchild=\"" << children.size() << "\"";
383 if (children.empty())
390 const char *sep = "";
391 for (auto &child : children)
394 PrintNewVar("-", child, threadId, print_values, varout);
398 ss << "child={" << varout << "}";
402 ss << ",has_more=\"" << (has_more ? 1 : 0) << "\"";
406 HRESULT MIProtocol::ListChildren(int threadId, int level, int childStart, int childEnd, const std::string &varName, int print_values, std::string &output)
410 StackFrame stackFrame(threadId, level, "");
412 std::vector<Variable> variables;
414 auto it = m_vars.find(varName);
415 if (it == m_vars.end())
418 uint32_t variablesReference = it->second.variablesReference;
420 bool has_more = false;
422 if (variablesReference > 0)
424 IfFailRet(m_debugger->GetVariables(variablesReference, VariablesNamed, childStart, childEnd - childStart, variables));
425 has_more = childEnd < m_debugger->GetNamedVariables(variablesReference);
428 PrintChildren(variables, threadId, print_values, has_more, output);
433 HRESULT MIProtocol::SetBreakpoint(
434 const std::string &filename,
436 const std::string &condition,
437 Breakpoint &breakpoint)
441 auto &breakpointsInSource = m_breakpoints[filename];
442 std::vector<SourceBreakpoint> srcBreakpoints;
443 for (auto it : breakpointsInSource)
445 srcBreakpoints.push_back(it.second);
447 srcBreakpoints.emplace_back(linenum, condition);
449 std::vector<Breakpoint> breakpoints;
450 IfFailRet(m_debugger->SetBreakpoints(filename, srcBreakpoints, breakpoints));
452 for (const Breakpoint& b : breakpoints)
454 if (b.line != linenum)
457 breakpointsInSource.insert(std::make_pair(b.id, SourceBreakpoint{ b.line, b.condition }));
465 HRESULT MIProtocol::SetBreakpointCondition(uint32_t id, const std::string &condition)
468 for (auto &breakpointsIter : m_breakpoints)
470 std::unordered_map<uint32_t, SourceBreakpoint> &fileBreakpoints = breakpointsIter.second;
472 // Find breakpoint with specified id in this file
473 const auto &sbIter = fileBreakpoints.find(id);
474 if (sbIter == fileBreakpoints.end())
477 // Modify breakpoint condition
478 sbIter->second.condition = condition;
480 // Gather all breakpoints in this file
481 std::vector<SourceBreakpoint> existingBreakpoints;
482 for (const auto &it : fileBreakpoints)
483 existingBreakpoints.emplace_back(it.second);
485 // Update breakpoints data for this file
486 const std::string &filename = breakpointsIter.first;
487 std::vector<Breakpoint> tmpBreakpoints;
488 return m_debugger->SetBreakpoints(filename, existingBreakpoints, tmpBreakpoints);
494 void MIProtocol::DeleteBreakpoints(const std::unordered_set<uint32_t> &ids)
496 for (auto &breakpointsIter : m_breakpoints)
498 std::vector<SourceBreakpoint> remainingBreakpoints;
499 for (auto it : breakpointsIter.second)
501 if (ids.find(it.first) == ids.end())
502 remainingBreakpoints.push_back(it.second);
504 if (remainingBreakpoints.size() == breakpointsIter.second.size())
507 std::string filename = breakpointsIter.first;
509 std::vector<Breakpoint> tmpBreakpoints;
510 m_debugger->SetBreakpoints(filename, remainingBreakpoints, tmpBreakpoints);
514 void MIProtocol::EmitStoppedEvent(StoppedEvent event)
518 std::string frameLocation;
519 PrintFrameLocation(event.frame, frameLocation);
525 MIProtocol::Printf("*stopped,reason=\"breakpoint-hit\",thread-id=\"%i\",stopped-threads=\"all\",bkptno=\"%u\",times=\"%u\",frame={%s}\n",
526 event.threadId, (unsigned int)event.breakpoint.id, (unsigned int)event.breakpoint.hitCount, frameLocation.c_str());
531 MIProtocol::Printf("*stopped,reason=\"end-stepping-range\",thread-id=\"%i\",stopped-threads=\"all\",frame={%s}\n",
532 event.threadId, frameLocation.c_str());
537 std::string category = "clr";
538 std::string stage = "unhandled";
539 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",
541 MIProtocol::EscapeMIValue(event.description).c_str(),
545 frameLocation.c_str());
550 // When async break happens, this should be reason="interrupted".
551 // But MIEngine in Visual Studio accepts only reason="signal-received",signal-name="SIGINT".
552 MIProtocol::Printf("*stopped,reason=\"signal-received\",signal-name=\"SIGINT\",thread-id=\"%i\",stopped-threads=\"all\",frame={%s}\n",
553 event.threadId, frameLocation.c_str());
558 MIProtocol::Printf("*stopped,reason=\"entry-point-hit\",thread-id=\"%i\",stopped-threads=\"all\",frame={%s}\n",
559 event.threadId, frameLocation.c_str());
566 MIProtocol::Printf("(gdb)\n");
569 void MIProtocol::EmitExitedEvent(ExitedEvent event)
573 MIProtocol::Printf("*stopped,reason=\"exited\",exit-code=\"%i\"\n", event.exitCode);
574 MIProtocol::Printf("(gdb)\n");
577 void MIProtocol::EmitContinuedEvent()
582 void MIProtocol::EmitThreadEvent(ThreadEvent event)
586 const char *reasonText = "";
590 reasonText = "thread-created";
593 reasonText = "thread-exited";
596 MIProtocol::Printf("=%s,id=\"%i\"\n", reasonText, event.threadId);
599 void MIProtocol::EmitModuleEvent(ModuleEvent event)
607 std::ostringstream ss;
608 ss << "id=\"{" << event.module.id << "}\","
609 << "target-name=\"" << MIProtocol::EscapeMIValue(event.module.path) << "\","
610 << "host-name=\"" << MIProtocol::EscapeMIValue(event.module.path) << "\","
611 << "symbols-loaded=\"" << (event.module.symbolStatus == SymbolsLoaded) << "\","
612 << "base-address=\"0x" << std::hex << event.module.baseAddress << "\","
613 << "size=\"" << std::dec << event.module.size << "\"";
614 Printf("=library-loaded,%s\n", ss.str().c_str());
622 void MIProtocol::EmitOutputEvent(OutputEvent event)
626 if (event.source.empty())
627 MIProtocol::Printf("=message,text=\"%s\",send-to=\"output-window\"\n",
628 MIProtocol::EscapeMIValue(event.output).c_str());
630 MIProtocol::Printf("=message,text=\"%s\",send-to=\"output-window\",source=\"%s\"\n",
631 MIProtocol::EscapeMIValue(event.output).c_str(),
632 MIProtocol::EscapeMIValue(event.source).c_str());
635 HRESULT MIProtocol::HandleCommand(std::string command,
636 const std::vector<std::string> &args,
639 static std::unordered_map<std::string, CommandCallback> commands {
640 { "thread-info", [this](const std::vector<std::string> &, std::string &output){
641 HRESULT Status = S_OK;
643 std::vector<Thread> threads;
644 IfFailRet(m_debugger->GetThreads(threads));
646 std::ostringstream ss;
650 const char *sep = "";
651 for (const Thread& thread : threads)
653 ss << sep << "{id=\"" << thread.id
654 << "\",name=\"" << MIProtocol::EscapeMIValue(thread.name) << "\",state=\""
655 << (thread.running ? "running" : "stopped") << "\"}";
663 { "exec-continue", [this](const std::vector<std::string> &, std::string &output){
665 IfFailRet(m_debugger->Continue());
669 { "exec-interrupt", [this](const std::vector<std::string> &, std::string &output){
671 IfFailRet(m_debugger->Pause());
675 { "break-insert", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
676 std::string filename;
677 unsigned int linenum;
678 std::string condition;
679 Breakpoint breakpoint;
680 if (ParseBreakpoint(args, filename, linenum, condition)
681 && SUCCEEDED(SetBreakpoint(filename, linenum, condition, breakpoint)))
683 PrintBreakpoint(breakpoint, output);
687 output = "Unknown breakpoint location format";
690 { "break-delete", [this](const std::vector<std::string> &args, std::string &) -> HRESULT {
691 std::unordered_set<uint32_t> ids;
692 for (const std::string &idStr : args)
695 int id = ParseInt(idStr, ok);
699 DeleteBreakpoints(ids);
702 { "break-condition", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
705 output = "Command requires at least 2 arguments";
710 int id = ParseInt(args.at(0), ok);
713 output = "Unknown breakpoint id";
717 return SetBreakpointCondition(id, args.at(1));
719 { "exec-step", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
720 return StepCommand(args, output, Debugger::STEP_IN);
722 { "exec-next", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
723 return StepCommand(args, output, Debugger::STEP_OVER);
725 { "exec-finish", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
726 return StepCommand(args, output, Debugger::STEP_OUT);
728 { "exec-abort", [this](const std::vector<std::string> &, std::string &output) -> HRESULT {
729 m_debugger->Disconnect(Debugger::DisconnectTerminate);
732 { "target-attach", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
734 if (args.size() != 1)
736 output = "Command requires an argument";
740 int pid = ParseInt(args.at(0), ok);
741 if (!ok) return E_INVALIDARG;
743 m_debugger->Initialize();
744 IfFailRet(m_debugger->Attach(pid));
745 IfFailRet(m_debugger->ConfigurationDone());
746 // TODO: print succeessful result
749 { "target-detach", [this](const std::vector<std::string> &, std::string &output) -> HRESULT {
750 m_debugger->Disconnect(Debugger::DisconnectDetach);
753 { "stack-list-frames", [this](const std::vector<std::string> &args_orig, std::string &output) -> HRESULT {
754 std::vector<std::string> args = args_orig;
755 DWORD threadId = GetIntArg(args, "--thread", m_debugger->GetLastStoppedThreadId());
757 int highFrame = INT_MAX;
759 GetIndices(args, lowFrame, highFrame);
760 return PrintFrames(threadId, output, lowFrame, highFrame);
762 { "stack-list-variables", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
765 int threadId = GetIntArg(args, "--thread", m_debugger->GetLastStoppedThreadId());
766 StackFrame stackFrame(threadId, GetIntArg(args, "--frame", 0), "");
767 std::vector<Scope> scopes;
768 std::vector<Variable> variables;
769 IfFailRet(m_debugger->GetScopes(stackFrame.id, scopes));
770 if (!scopes.empty() && scopes[0].variablesReference != 0)
772 IfFailRet(m_debugger->GetVariables(scopes[0].variablesReference, VariablesNamed, 0, 0, variables));
775 PrintVariables(variables, output);
779 { "var-create", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
782 output = "Command requires at least 2 arguments";
786 int threadId = GetIntArg(args, "--thread", m_debugger->GetLastStoppedThreadId());
787 int level = GetIntArg(args, "--frame", 0);
789 std::string varName = args.at(0);
790 std::string varExpr = args.at(1);
791 if (varExpr == "*" && args.size() >= 3)
792 varExpr = args.at(2);
794 return CreateVar(threadId, level, varName, varExpr, output);
796 { "var-list-children", [this](const std::vector<std::string> &args_orig, std::string &output) -> HRESULT {
797 std::vector<std::string> args = args_orig;
799 int print_values = 0;
802 auto first_arg_it = args.begin();
803 if (*first_arg_it == "1" || *first_arg_it == "--all-values")
806 args.erase(first_arg_it);
808 else if (*first_arg_it == "2" || *first_arg_it == "--simple-values")
811 args.erase(first_arg_it);
817 output = "Command requires an argument";
821 int threadId = GetIntArg(args, "--thread", m_debugger->GetLastStoppedThreadId());
822 int level = GetIntArg(args, "--frame", 0);
825 int childEnd = INT_MAX;
827 GetIndices(args, childStart, childEnd);
828 std::string varName = args.at(0);
830 return ListChildren(threadId, level, childStart, childEnd, varName, print_values, output);
832 { "var-delete", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
835 output = "Command requires at least 1 argument";
838 return DeleteVar(args.at(0));
840 { "gdb-exit", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
843 m_debugger->Disconnect(Debugger::DisconnectTerminate);
847 { "file-exec-and-symbols", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
850 m_fileExec = args.at(0);
853 { "exec-arguments", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
857 { "exec-run", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
859 m_debugger->Initialize();
860 IfFailRet(m_debugger->Launch(m_fileExec, m_execArgs, true));
861 Status = m_debugger->ConfigurationDone();
862 if (SUCCEEDED(Status))
866 { "environment-cd", [](const std::vector<std::string> &args, std::string &output) -> HRESULT {
869 return SetWorkDir(args.at(0)) ? S_OK : E_FAIL;
871 { "handshake", [](const std::vector<std::string> &args, std::string &output) -> HRESULT {
872 if (!args.empty() && args.at(0) == "init")
873 output = "request=\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\"";
877 { "gdb-set", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
878 if (args.size() == 2)
880 if (args.at(0) == "just-my-code")
882 m_debugger->SetJustMyCode(args.at(1) == "1");
887 { "interpreter-exec", [](const std::vector<std::string> &args, std::string &output) -> HRESULT {
890 { "break-exception-insert", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
894 if (args.at(0) == "--mda")
897 std::ostringstream ss;
898 const char *sep = "";
900 for (; i < args.size(); i++)
903 m_debugger->InsertExceptionBreakpoint(args.at(i), b);
906 ss << "{number=\"" << b.id << "\"}";
913 { "var-show-attributes", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
916 std::string varName = args.at(0);
917 std::string editable;
919 IfFailRet(FindVar(varName, variable));
920 if (IsEditable(variable.type))
921 editable = "editable";
923 editable = "noneditable";
925 output = "status=\"" + editable + "\"";
928 { "var-assign", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
933 output = "Command requires at least 2 arguments";
937 std::string varName = args.at(0);
938 std::string varExpr = args.at(1);
940 if (varExpr.size() >= 2 && varExpr.front() == '"' && varExpr.back() == '"')
941 varExpr = varExpr.substr(1, varExpr.size() - 2);
943 int threadId = GetIntArg(args, "--thread", m_debugger->GetLastStoppedThreadId());
944 int level = GetIntArg(args, "--frame", 0);
945 uint64_t frameId = StackFrame(threadId, level, "").id;
948 IfFailRet(FindVar(varName, variable));
951 IfFailRet(m_debugger->SetVariableByExpression(frameId, variable.evaluateName, varExpr, output));
953 output = "value=\"" + MIProtocol::EscapeMIValue(output) + "\"";
959 auto command_it = commands.find(command);
961 if (command_it == commands.end())
963 output = "Unknown command: " + command;
967 return command_it->second(args, output);
973 std::string m_delimiters;
977 Tokenizer(const std::string &str, const std::string &delimiters = " \t\n\r")
978 : m_str(str), m_delimiters(delimiters), m_next(0)
980 m_str.erase(m_str.find_last_not_of(m_delimiters) + 1);
983 bool Next(std::string &token)
987 if (m_next >= m_str.size())
995 } state = StateSpace;
997 for (; m_next < m_str.size(); m_next++)
999 char c = m_str.at(m_next);
1003 if (m_delimiters.find(c) != std::string::npos)
1007 state = c == '"' ? StateQuotedToken : StateToken;
1008 if (state != StateQuotedToken)
1012 if (m_delimiters.find(c) != std::string::npos)
1017 case StateQuotedToken:
1019 state = StateEscape;
1027 state = StateQuotedToken;
1031 return state != StateEscape || token.empty();
1034 std::string Remain() const
1036 return m_str.substr(m_next);
1040 static bool ParseLine(const std::string &str,
1043 std::vector<std::string> &args)
1049 Tokenizer tokenizer(str);
1052 if (!tokenizer.Next(result) || result.empty())
1055 std::size_t i = result.find_first_not_of("0123456789");
1056 if (i == std::string::npos)
1059 if (result.at(i) != '-')
1062 token = result.substr(0, i);
1063 cmd = result.substr(i + 1);
1065 if (cmd == "var-assign")
1067 tokenizer.Next(result);
1068 args.push_back(result); // name
1069 args.push_back(tokenizer.Remain()); // expression
1072 else if (cmd == "break-condition")
1074 tokenizer.Next(result);
1075 args.push_back(result); // id
1076 args.push_back(tokenizer.Remain()); // expression
1080 while (tokenizer.Next(result))
1081 args.push_back(result);
1086 void MIProtocol::CommandLoop()
1097 std::getline(std::cin, input);
1098 if (input.empty() && std::cin.eof())
1101 std::vector<std::string> args;
1102 std::string command;
1103 if (!ParseLine(input, token, command, args))
1105 Printf("%s^error,msg=\"Failed to parse input\"\n", token.c_str());
1110 HRESULT hr = HandleCommand(command, args, output);
1117 const char *resultClass;
1119 resultClass = "^done";
1120 else if (output.at(0) == '^')
1123 resultClass = "^done,";
1125 Printf("%s%s%s\n", token.c_str(), resultClass, output.c_str());
1131 Printf("%s^error,msg=\"Error: 0x%08x\"\n", token.c_str(), hr);
1135 Printf("%s^error,msg=\"%s\"\n", token.c_str(), MIProtocol::EscapeMIValue(output).c_str());
1142 m_debugger->Disconnect(Debugger::DisconnectTerminate);
1144 Printf("%s^exit\n", token.c_str());
1148 std::mutex MIProtocol::m_outMutex;
1150 void MIProtocol::Printf(const char *fmt, ...)
1152 std::string strbuffer;
1155 const char *out = nullptr;
1160 int n = vsnprintf(buffer, sizeof(buffer), fmt, arg);
1166 if (n >= sizeof(buffer))
1168 strbuffer.resize(n);
1171 n = vsnprintf(&strbuffer[0], strbuffer.size() + 1, fmt, arg);
1174 if (n < 0 || n > strbuffer.size())
1176 out = strbuffer.c_str();
1184 std::lock_guard<std::mutex> lock(m_outMutex);
1189 std::string MIProtocol::EscapeMIValue(const std::string &str)
1193 for (std::size_t i = 0; i < s.size(); ++i)
1199 case '\"': count = 1; s.insert(i, count, '\\'); s[i + count] = '\"'; break;
1200 case '\\': count = 1; s.insert(i, count, '\\'); s[i + count] = '\\'; break;
1201 case '\0': count = 1; s.insert(i, count, '\\'); s[i + count] = '0'; break;
1202 case '\a': count = 1; s.insert(i, count, '\\'); s[i + count] = 'a'; break;
1203 case '\b': count = 1; s.insert(i, count, '\\'); s[i + count] = 'b'; break;
1204 case '\f': count = 1; s.insert(i, count, '\\'); s[i + count] = 'f'; break;
1205 case '\n': count = 1; s.insert(i, count, '\\'); s[i + count] = 'n'; break;
1206 case '\r': count = 1; s.insert(i, count, '\\'); s[i + count] = 'r'; break;
1207 case '\t': count = 1; s.insert(i, count, '\\'); s[i + count] = 't'; break;
1208 case '\v': count = 1; s.insert(i, count, '\\'); s[i + count] = 'v'; break;