Fix: Add "(gdb)\n" token after each result record
[sdk/tools/netcoredbg.git] / src / debug / netcoredbg / miprotocol.cpp
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.
4
5 #include "frames.h"
6 #include "platform.h"
7 #include "torelease.h"
8 #include "miprotocol.h"
9
10 #include <sstream>
11 #include <functional>
12 #include <algorithm>
13 #include <iostream>
14 #include <iomanip>
15
16 #include "logger.h"
17
18
19 using namespace std::placeholders;
20
21 typedef std::function<HRESULT(
22     const std::vector<std::string> &args,
23     std::string &output)> CommandCallback;
24
25
26 static int ParseInt(const std::string &s, bool &ok)
27 {
28     ok = false;
29     try
30     {
31         int result = std::stoi(s);
32         ok = true;
33         return result;
34     }
35     catch(std::invalid_argument e)
36     {
37     }
38     catch (std::out_of_range  e)
39     {
40     }
41     return 0;
42 }
43
44 // Remove all --name value
45 static void StripArgs(std::vector<std::string> &args)
46 {
47     auto it = args.begin();
48
49     while (it != args.end())
50     {
51         if (it->find("--") == 0 && it + 1 != args.end())
52             it = args.erase(args.erase(it));
53         else
54             ++it;
55     }
56 }
57
58 static int GetIntArg(const std::vector<std::string> &args, const std::string name, int defaultValue)
59 {
60     auto it = std::find(args.begin(), args.end(), name);
61
62     if (it == args.end())
63         return defaultValue;
64
65     ++it;
66
67     if (it == args.end())
68         return defaultValue;
69
70     bool ok;
71     int val = ParseInt(*it, ok);
72     return ok ? val : defaultValue;
73 }
74
75 static bool GetIndices(const std::vector<std::string> &args, int &index1, int &index2)
76 {
77     if (args.size() < 2)
78         return false;
79
80     bool ok;
81     int val1 = ParseInt(args.at(args.size() - 2), ok);
82     if (!ok)
83         return false;
84     int val2 = ParseInt(args.at(args.size() - 1), ok);
85     if (!ok)
86         return false;
87     index1 = val1;
88     index2 = val2;
89     return true;
90 }
91
92 bool ParseBreakpoint(
93     const std::vector<std::string> &args_orig,
94     std::string &filename,
95     unsigned int &linenum,
96     std::string &condition)
97 {
98     std::vector<std::string> args = args_orig;
99     StripArgs(args);
100
101     if (args.empty())
102         return false;
103
104     if (args.at(0) == "-f")
105     {
106         args.erase(args.begin());
107         if (args.empty())
108             return false;
109     }
110
111     if (args.at(0) == "-c")
112     {
113         if (args.size() < 3)
114             return false;
115         condition = args.at(1);
116         args.erase(args.begin(), args.begin() + 2);
117     }
118
119     std::size_t i = args.at(0).rfind(':');
120
121     if (i == std::string::npos)
122         return false;
123
124     filename = args.at(0).substr(0, i);
125     std::string slinenum = args.at(0).substr(i + 1);
126
127     bool ok;
128     linenum = ParseInt(slinenum, ok);
129     return ok && linenum > 0;
130 }
131
132
133 HRESULT MIProtocol::PrintBreakpoint(const Breakpoint &b, std::string &output)
134 {
135     HRESULT Status;
136
137     std::ostringstream ss;
138
139     if (b.verified)
140     {
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 << "\"}";
145         Status = S_OK;
146     }
147     else
148     {
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.\"}";
151         Status = S_FALSE;
152     }
153     output = ss.str();
154     return Status;
155 }
156
157 void MIProtocol::EmitBreakpointEvent(BreakpointEvent event)
158 {
159     LogFuncEntry();
160
161     switch(event.reason)
162     {
163         case BreakpointChanged:
164         {
165             std::string output;
166             PrintBreakpoint(event.breakpoint, output);
167             MIProtocol::Printf("=breakpoint-modified,%s\n", output.c_str());
168             return;
169         }
170         default:
171             break;
172     }
173 }
174
175 HRESULT MIProtocol::StepCommand(const std::vector<std::string> &args,
176                                 std::string &output,
177                                 Debugger::StepType stepType)
178 {
179     DWORD threadId = GetIntArg(args, "--thread", m_debugger->GetLastStoppedThreadId());
180     m_debugger->StepCommand(threadId, stepType);
181     output = "^running";
182     return S_OK;
183 }
184
185 static std::string AddrToString(uint64_t addr)
186 {
187     std::ostringstream ss;
188     ss << "0x" << std::setw(2 * sizeof(void*)) << std::setfill('0') << std::hex << addr;
189     return ss.str();
190 }
191
192 HRESULT MIProtocol::PrintFrameLocation(const StackFrame &stackFrame, std::string &output)
193 {
194     std::ostringstream ss;
195
196     if (!stackFrame.source.IsNull())
197     {
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 << "\",";
204     }
205
206     if (stackFrame.clrAddr.methodToken != 0)
207     {
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 << "\"},";
213     }
214
215     ss << "func=\"" << stackFrame.name << "\"";
216     if (stackFrame.id != 0)
217         ss << ",addr=\"" << AddrToString(stackFrame.addr) << "\"";
218
219     output = ss.str();
220
221     return stackFrame.source.IsNull() ? S_FALSE : S_OK;
222 }
223
224 HRESULT MIProtocol::PrintFrames(int threadId, std::string &output, int lowFrame, int highFrame)
225 {
226     HRESULT Status;
227     std::ostringstream ss;
228
229     int totalFrames = 0;
230     std::vector<StackFrame> stackFrames;
231     IfFailRet(m_debugger->GetStackTrace(threadId, lowFrame, highFrame - lowFrame, stackFrames, totalFrames));
232
233     int currentFrame = lowFrame;
234
235     ss << "stack=[";
236     const char *sep = "";
237
238     for (const StackFrame &stackFrame : stackFrames)
239     {
240         ss << sep;
241         sep = ",";
242
243         std::string frameLocation;
244         PrintFrameLocation(stackFrame, frameLocation);
245
246         ss << "frame={level=\"" << currentFrame << "\"";
247         if (!frameLocation.empty())
248             ss << "," << frameLocation;
249         ss << "}";
250         currentFrame++;
251     }
252
253     ss << "]";
254
255     output = ss.str();
256
257     return S_OK;
258 }
259
260 HRESULT MIProtocol::PrintVariables(const std::vector<Variable> &variables, std::string &output)
261 {
262     const bool printValues = true;
263     const bool printTypes = false;
264
265     std::ostringstream ss;
266     ss << "variables=[";
267     const char *sep = "";
268
269     for (const Variable &var : variables)
270     {
271         ss << sep;
272         sep = ",";
273
274         ss << "{name=\"" << EscapeMIValue(var.name) << "\"";
275         ss << ",value=\"" << EscapeMIValue(var.value) << "\"";
276         ss << "}";
277     }
278
279     ss << "]";
280     output = ss.str();
281     return S_OK;
282 }
283
284 bool MIProtocol::IsEditable(const std::string &type)
285 {
286     if (type == "int"
287         || type == "bool"
288         || type == "char"
289         || type == "byte"
290         || type == "sbyte"
291         || type == "short"
292         || type == "ushort"
293         || type == "uint"
294         || type == "long"
295         || type == "ulong"
296         || type == "decimal"
297         || type == "string")
298         return true;
299
300     return false;
301 }
302
303 void MIProtocol::PrintVar(const std::string &varobjName, Variable &v, int threadId, int print_values, std::string &output)
304 {
305     std::ostringstream ss;
306
307     std::string editable;
308     if (IsEditable(v.type))
309         editable = "editable";
310     else
311         editable = "noneditable";
312
313     ss << "name=\"" << varobjName << "\",";
314     if (print_values)
315     {
316         ss << "value=\"" << MIProtocol::EscapeMIValue(v.value) << "\",";
317     }
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 << "\"";
323     //,has_more="0"}
324
325     output = ss.str();
326 }
327
328 void MIProtocol::PrintNewVar(std::string varobjName, Variable &v, int threadId, int print_values, std::string &output)
329 {
330     if (varobjName.empty() || varobjName == "-")
331     {
332         varobjName = "var" + std::to_string(m_varCounter++);
333     }
334
335     m_vars[varobjName] = v;
336
337     PrintVar(varobjName, v, threadId, print_values, output);
338 }
339
340 HRESULT MIProtocol::CreateVar(int threadId, int level, const std::string &varobjName, const std::string &expression, std::string &output)
341 {
342     HRESULT Status;
343
344     uint64_t frameId = StackFrame(threadId, level, "").id;
345
346     Variable variable;
347     IfFailRet(m_debugger->Evaluate(frameId, expression, variable, output));
348
349     int print_values = 1;
350     PrintNewVar(varobjName, variable, threadId, print_values, output);
351
352     return S_OK;
353 }
354
355 HRESULT MIProtocol::DeleteVar(const std::string &varobjName)
356 {
357     return m_vars.erase(varobjName) == 0 ? E_FAIL : S_OK;
358 }
359
360 HRESULT MIProtocol::FindVar(const std::string &varobjName, Variable &variable)
361 {
362     auto it = m_vars.find(varobjName);
363     if (it == m_vars.end())
364         return E_FAIL;
365
366     variable = it->second;
367
368     return S_OK;
369 }
370
371 void MIProtocol::Cleanup()
372 {
373     m_vars.clear();
374     m_varCounter = 0;
375     m_breakpoints.clear();
376 }
377
378 void MIProtocol::PrintChildren(std::vector<Variable> &children, int threadId, int print_values, bool has_more, std::string &output)
379 {
380     std::ostringstream ss;
381     ss << "numchild=\"" << children.size() << "\"";
382
383     if (children.empty())
384     {
385         output = ss.str();
386         return;
387     }
388     ss << ",children=[";
389
390     const char *sep = "";
391     for (auto &child : children)
392     {
393         std::string varout;
394         PrintNewVar("-", child, threadId, print_values, varout);
395
396         ss << sep;
397         sep = ",";
398         ss << "child={" << varout << "}";
399     }
400
401     ss << "]";
402     ss << ",has_more=\"" << (has_more ? 1 : 0) << "\"";
403     output = ss.str();
404 }
405
406 HRESULT MIProtocol::ListChildren(int threadId, int level, int childStart, int childEnd, const std::string &varName, int print_values, std::string &output)
407 {
408     HRESULT Status;
409
410     StackFrame stackFrame(threadId, level, "");
411
412     std::vector<Variable> variables;
413
414     auto it = m_vars.find(varName);
415     if (it == m_vars.end())
416         return E_FAIL;
417
418     uint32_t variablesReference = it->second.variablesReference;
419
420     bool has_more = false;
421
422     if (variablesReference > 0)
423     {
424         IfFailRet(m_debugger->GetVariables(variablesReference, VariablesNamed, childStart, childEnd - childStart, variables));
425         has_more = childEnd < m_debugger->GetNamedVariables(variablesReference);
426     }
427
428     PrintChildren(variables, threadId, print_values, has_more, output);
429
430     return S_OK;
431 }
432
433 HRESULT MIProtocol::SetBreakpoint(
434     const std::string &filename,
435     int linenum,
436     const std::string &condition,
437     Breakpoint &breakpoint)
438 {
439     HRESULT Status;
440
441     auto &breakpointsInSource = m_breakpoints[filename];
442     std::vector<SourceBreakpoint> srcBreakpoints;
443     for (auto it : breakpointsInSource)
444     {
445         srcBreakpoints.push_back(it.second);
446     }
447     srcBreakpoints.emplace_back(linenum, condition);
448
449     std::vector<Breakpoint> breakpoints;
450     IfFailRet(m_debugger->SetBreakpoints(filename, srcBreakpoints, breakpoints));
451
452     for (const Breakpoint& b : breakpoints)
453     {
454         if (b.line != linenum)
455             continue;
456
457         breakpointsInSource.insert(std::make_pair(b.id, SourceBreakpoint{ b.line, b.condition }));
458         breakpoint = b;
459         return S_OK;
460     }
461
462     return E_FAIL;
463 }
464
465 HRESULT MIProtocol::SetBreakpointCondition(uint32_t id, const std::string &condition)
466 {
467     // For each file
468     for (auto &breakpointsIter : m_breakpoints)
469     {
470         std::unordered_map<uint32_t, SourceBreakpoint> &fileBreakpoints = breakpointsIter.second;
471
472         // Find breakpoint with specified id in this file
473         const auto &sbIter = fileBreakpoints.find(id);
474         if (sbIter == fileBreakpoints.end())
475             continue;
476
477         // Modify breakpoint condition
478         sbIter->second.condition = condition;
479
480         // Gather all breakpoints in this file
481         std::vector<SourceBreakpoint> existingBreakpoints;
482         for (const auto &it : fileBreakpoints)
483             existingBreakpoints.emplace_back(it.second);
484
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);
489     }
490
491     return E_FAIL;
492 }
493
494 void MIProtocol::DeleteBreakpoints(const std::unordered_set<uint32_t> &ids)
495 {
496     for (auto &breakpointsIter : m_breakpoints)
497     {
498         std::vector<SourceBreakpoint> remainingBreakpoints;
499         for (auto it : breakpointsIter.second)
500         {
501             if (ids.find(it.first) == ids.end())
502                 remainingBreakpoints.push_back(it.second);
503         }
504         if (remainingBreakpoints.size() == breakpointsIter.second.size())
505             continue;
506
507         std::string filename = breakpointsIter.first;
508
509         std::vector<Breakpoint> tmpBreakpoints;
510         m_debugger->SetBreakpoints(filename, remainingBreakpoints, tmpBreakpoints);
511     }
512 }
513
514 void MIProtocol::EmitStoppedEvent(StoppedEvent event)
515 {
516     LogFuncEntry();
517
518     std::string frameLocation;
519     PrintFrameLocation(event.frame, frameLocation);
520
521     switch(event.reason)
522     {
523         case StopBreakpoint:
524         {
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());
527             break;
528         }
529         case StopStep:
530         {
531             MIProtocol::Printf("*stopped,reason=\"end-stepping-range\",thread-id=\"%i\",stopped-threads=\"all\",frame={%s}\n",
532                 event.threadId, frameLocation.c_str());
533             break;
534         }
535         case StopException:
536         {
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",
540                 event.text.c_str(),
541                 MIProtocol::EscapeMIValue(event.description).c_str(),
542                 stage.c_str(),
543                 category.c_str(),
544                 event.threadId,
545                 frameLocation.c_str());
546             break;
547         }
548         case StopPause:
549         {
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());
554             break;
555         }
556         case StopEntry:
557         {
558             MIProtocol::Printf("*stopped,reason=\"entry-point-hit\",thread-id=\"%i\",stopped-threads=\"all\",frame={%s}\n",
559                 event.threadId, frameLocation.c_str());
560             break;
561         }
562         default:
563             return;
564     }
565
566     MIProtocol::Printf("(gdb)\n");
567 }
568
569 void MIProtocol::EmitExitedEvent(ExitedEvent event)
570 {
571     LogFuncEntry();
572
573     MIProtocol::Printf("*stopped,reason=\"exited\",exit-code=\"%i\"\n", event.exitCode);
574     MIProtocol::Printf("(gdb)\n");
575 }
576
577 void MIProtocol::EmitContinuedEvent()
578 {
579     LogFuncEntry();
580 }
581
582 void MIProtocol::EmitThreadEvent(ThreadEvent event)
583 {
584     LogFuncEntry();
585
586     const char *reasonText = "";
587     switch(event.reason)
588     {
589         case ThreadStarted:
590             reasonText = "thread-created";
591             break;
592         case ThreadExited:
593             reasonText = "thread-exited";
594             break;
595     }
596     MIProtocol::Printf("=%s,id=\"%i\"\n", reasonText, event.threadId);
597 }
598
599 void MIProtocol::EmitModuleEvent(ModuleEvent event)
600 {
601     LogFuncEntry();
602
603     switch(event.reason)
604     {
605         case ModuleNew:
606         {
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());
615             break;
616         }
617         default:
618             break;
619     }
620 }
621
622 void MIProtocol::EmitOutputEvent(OutputEvent event)
623 {
624     LogFuncEntry();
625
626     if (event.source.empty())
627         MIProtocol::Printf("=message,text=\"%s\",send-to=\"output-window\"\n",
628             MIProtocol::EscapeMIValue(event.output).c_str());
629     else
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());
633 }
634
635 HRESULT MIProtocol::HandleCommand(std::string command,
636                                   const std::vector<std::string> &args,
637                                   std::string &output)
638 {
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;
642
643         std::vector<Thread> threads;
644         IfFailRet(m_debugger->GetThreads(threads));
645
646         std::ostringstream ss;
647
648         ss << "threads=[";
649
650         const char *sep = "";
651         for (const Thread& thread : threads)
652         {
653             ss << sep << "{id=\"" << thread.id
654                << "\",name=\"" << MIProtocol::EscapeMIValue(thread.name) << "\",state=\""
655                << (thread.running ? "running" : "stopped") << "\"}";
656             sep = ",";
657         }
658
659         ss << "]";
660         output = ss.str();
661         return S_OK;
662     } },
663     { "exec-continue", [this](const std::vector<std::string> &, std::string &output){
664         HRESULT Status;
665         IfFailRet(m_debugger->Continue());
666         output = "^running";
667         return S_OK;
668     } },
669     { "exec-interrupt", [this](const std::vector<std::string> &, std::string &output){
670         HRESULT Status;
671         IfFailRet(m_debugger->Pause());
672         output = "^done";
673         return S_OK;
674     } },
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)))
682         {
683             PrintBreakpoint(breakpoint, output);
684             return S_OK;
685         }
686
687         output = "Unknown breakpoint location format";
688         return E_FAIL;
689     } },
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)
693         {
694             bool ok;
695             int id = ParseInt(idStr, ok);
696             if (ok)
697                 ids.insert(id);
698         }
699         DeleteBreakpoints(ids);
700         return S_OK;
701     } },
702     { "break-condition", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
703         if (args.size() < 2)
704         {
705             output = "Command requires at least 2 arguments";
706             return E_FAIL;
707         }
708
709         bool ok;
710         int id = ParseInt(args.at(0), ok);
711         if (!ok)
712         {
713             output = "Unknown breakpoint id";
714             return E_FAIL;
715         }
716
717         return SetBreakpointCondition(id, args.at(1));
718     } },
719     { "exec-step", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
720         return StepCommand(args, output, Debugger::STEP_IN);
721     }},
722     { "exec-next", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
723         return StepCommand(args, output, Debugger::STEP_OVER);
724     }},
725     { "exec-finish", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
726         return StepCommand(args, output, Debugger::STEP_OUT);
727     }},
728     { "exec-abort", [this](const std::vector<std::string> &, std::string &output) -> HRESULT {
729         m_debugger->Disconnect(Debugger::DisconnectTerminate);
730         return S_OK;
731     }},
732     { "target-attach", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
733         HRESULT Status;
734         if (args.size() != 1)
735         {
736             output = "Command requires an argument";
737             return E_INVALIDARG;
738         }
739         bool ok;
740         int pid = ParseInt(args.at(0), ok);
741         if (!ok) return E_INVALIDARG;
742
743         m_debugger->Initialize();
744         IfFailRet(m_debugger->Attach(pid));
745         IfFailRet(m_debugger->ConfigurationDone());
746         // TODO: print succeessful result
747         return S_OK;
748     }},
749     { "target-detach", [this](const std::vector<std::string> &, std::string &output) -> HRESULT {
750         m_debugger->Disconnect(Debugger::DisconnectDetach);
751         return S_OK;
752     }},
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());
756         int lowFrame = 0;
757         int highFrame = INT_MAX;
758         StripArgs(args);
759         GetIndices(args, lowFrame, highFrame);
760         return PrintFrames(threadId, output, lowFrame, highFrame);
761     }},
762     { "stack-list-variables", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
763         HRESULT Status;
764
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)
771         {
772             IfFailRet(m_debugger->GetVariables(scopes[0].variablesReference, VariablesNamed, 0, 0, variables));
773         }
774
775         PrintVariables(variables, output);
776
777         return S_OK;
778     }},
779     { "var-create", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
780         if (args.size() < 2)
781         {
782             output = "Command requires at least 2 arguments";
783             return E_FAIL;
784         }
785
786         int threadId = GetIntArg(args, "--thread", m_debugger->GetLastStoppedThreadId());
787         int level = GetIntArg(args, "--frame", 0);
788
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);
793
794         return CreateVar(threadId, level, varName, varExpr, output);
795     }},
796     { "var-list-children", [this](const std::vector<std::string> &args_orig, std::string &output) -> HRESULT {
797         std::vector<std::string> args = args_orig;
798
799         int print_values = 0;
800         if (!args.empty())
801         {
802             auto first_arg_it = args.begin();
803             if (*first_arg_it == "1" || *first_arg_it == "--all-values")
804             {
805                 print_values = 1;
806                 args.erase(first_arg_it);
807             }
808             else if (*first_arg_it == "2" || *first_arg_it == "--simple-values")
809             {
810                 print_values = 2;
811                 args.erase(first_arg_it);
812             }
813         }
814
815         if (args.empty())
816         {
817             output = "Command requires an argument";
818             return E_FAIL;
819         }
820
821         int threadId = GetIntArg(args, "--thread", m_debugger->GetLastStoppedThreadId());
822         int level = GetIntArg(args, "--frame", 0);
823
824         int childStart = 0;
825         int childEnd = INT_MAX;
826         StripArgs(args);
827         GetIndices(args, childStart, childEnd);
828         std::string varName = args.at(0);
829
830         return ListChildren(threadId, level, childStart, childEnd, varName, print_values, output);
831     }},
832     { "var-delete", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
833         if (args.size() < 1)
834         {
835             output = "Command requires at least 1 argument";
836             return E_FAIL;
837         }
838         return DeleteVar(args.at(0));
839     }},
840     { "gdb-exit", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
841         this->m_exit = true;
842
843         m_debugger->Disconnect(Debugger::DisconnectTerminate);
844
845         return S_OK;
846     }},
847     { "file-exec-and-symbols", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
848         if (args.empty())
849             return E_INVALIDARG;
850         m_fileExec = args.at(0);
851         return S_OK;
852     }},
853     { "exec-arguments", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
854         m_execArgs = args;
855         return S_OK;
856     }},
857     { "exec-run", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
858         HRESULT Status;
859         m_debugger->Initialize();
860         IfFailRet(m_debugger->Launch(m_fileExec, m_execArgs, true));
861         Status = m_debugger->ConfigurationDone();
862         if (SUCCEEDED(Status))
863             output = "^running";
864         return Status;
865     }},
866     { "environment-cd", [](const std::vector<std::string> &args, std::string &output) -> HRESULT {
867         if (args.empty())
868             return E_INVALIDARG;
869         return SetWorkDir(args.at(0)) ? S_OK : E_FAIL;
870     }},
871     { "handshake", [](const std::vector<std::string> &args, std::string &output) -> HRESULT {
872         if (!args.empty() && args.at(0) == "init")
873             output = "request=\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\"";
874
875         return S_OK;
876     }},
877     { "gdb-set", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
878         if (args.size() == 2)
879         {
880             if (args.at(0) == "just-my-code")
881             {
882                 m_debugger->SetJustMyCode(args.at(1) == "1");
883             }
884         }
885         return S_OK;
886     }},
887     { "interpreter-exec", [](const std::vector<std::string> &args, std::string &output) -> HRESULT {
888         return S_OK;
889     }},
890     { "break-exception-insert", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
891         if (args.empty())
892             return E_FAIL;
893         size_t i = 1;
894         if (args.at(0) == "--mda")
895             i = 2;
896
897         std::ostringstream ss;
898         const char *sep = "";
899         ss << "bkpt=[";
900         for (; i < args.size(); i++)
901         {
902             Breakpoint b;
903             m_debugger->InsertExceptionBreakpoint(args.at(i), b);
904             ss << sep;
905             sep = ",";
906             ss << "{number=\"" << b.id << "\"}";
907         }
908         ss << "]";
909         output = ss.str();
910
911         return S_OK;
912     }},
913     { "var-show-attributes", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
914         HRESULT Status;
915         Variable variable;
916         std::string varName = args.at(0);
917         std::string editable;
918
919         IfFailRet(FindVar(varName, variable));
920         if (IsEditable(variable.type))
921             editable = "editable";
922         else
923             editable = "noneditable";
924
925         output = "status=\"" + editable + "\"";
926         return S_OK;
927     }},
928     { "var-assign", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
929         HRESULT Status;
930
931         if (args.size() < 2)
932         {
933             output = "Command requires at least 2 arguments";
934             return E_FAIL;
935         }
936
937         std::string varName = args.at(0);
938         std::string varExpr = args.at(1);
939
940         if (varExpr.size() >= 2 && varExpr.front() == '"' && varExpr.back() == '"')
941             varExpr = varExpr.substr(1, varExpr.size() - 2);
942
943         int threadId = GetIntArg(args, "--thread", m_debugger->GetLastStoppedThreadId());
944         int level = GetIntArg(args, "--frame", 0);
945         uint64_t frameId = StackFrame(threadId, level, "").id;
946
947         Variable variable;
948         IfFailRet(FindVar(varName, variable));
949
950
951         IfFailRet(m_debugger->SetVariableByExpression(frameId, variable.evaluateName, varExpr, output));
952
953         output = "value=\"" + MIProtocol::EscapeMIValue(output) + "\"";
954
955         return S_OK;
956     }},
957     };
958
959     auto command_it = commands.find(command);
960
961     if (command_it == commands.end())
962     {
963         output = "Unknown command: " + command;
964         return E_FAIL;
965     }
966
967     return command_it->second(args, output);
968 }
969
970 class Tokenizer
971 {
972     std::string m_str;
973     std::string m_delimiters;
974     size_t m_next;
975 public:
976
977     Tokenizer(const std::string &str, const std::string &delimiters = " \t\n\r")
978         : m_str(str), m_delimiters(delimiters), m_next(0)
979     {
980         m_str.erase(m_str.find_last_not_of(m_delimiters) + 1);
981     }
982
983     bool Next(std::string &token)
984     {
985         token = "";
986
987         if (m_next >= m_str.size())
988             return false;
989
990         enum {
991             StateSpace,
992             StateToken,
993             StateQuotedToken,
994             StateEscape
995         } state = StateSpace;
996
997         for (; m_next < m_str.size(); m_next++)
998         {
999             char c = m_str.at(m_next);
1000             switch(state)
1001             {
1002                 case StateSpace:
1003                     if (m_delimiters.find(c) != std::string::npos)
1004                         continue;
1005                     if (!token.empty())
1006                         return true;
1007                     state = c == '"' ? StateQuotedToken : StateToken;
1008                     if (state != StateQuotedToken)
1009                         token +=c;
1010                     break;
1011                 case StateToken:
1012                     if (m_delimiters.find(c) != std::string::npos)
1013                         state = StateSpace;
1014                     else
1015                         token += c;
1016                     break;
1017                 case StateQuotedToken:
1018                     if (c == '\\')
1019                         state = StateEscape;
1020                     else if (c == '"')
1021                         state = StateSpace;
1022                     else
1023                         token += c;
1024                     break;
1025                 case StateEscape:
1026                     token += c;
1027                     state = StateQuotedToken;
1028                     break;
1029             }
1030         }
1031         return state != StateEscape || token.empty();
1032     }
1033
1034     std::string Remain() const
1035     {
1036         return m_str.substr(m_next);
1037     }
1038 };
1039
1040 static bool ParseLine(const std::string &str,
1041                       std::string &token,
1042                       std::string &cmd,
1043                       std::vector<std::string> &args)
1044 {
1045     token.clear();
1046     cmd.clear();
1047     args.clear();
1048
1049     Tokenizer tokenizer(str);
1050     std::string result;
1051
1052     if (!tokenizer.Next(result) || result.empty())
1053         return false;
1054
1055     std::size_t i = result.find_first_not_of("0123456789");
1056     if (i == std::string::npos)
1057         return false;
1058
1059     if (result.at(i) != '-')
1060         return false;
1061
1062     token = result.substr(0, i);
1063     cmd = result.substr(i + 1);
1064
1065     if (cmd == "var-assign")
1066     {
1067         tokenizer.Next(result);
1068         args.push_back(result); // name
1069         args.push_back(tokenizer.Remain()); // expression
1070         return true;
1071     }
1072     else if (cmd == "break-condition")
1073     {
1074         tokenizer.Next(result);
1075         args.push_back(result); // id
1076         args.push_back(tokenizer.Remain()); // expression
1077         return true;
1078     }
1079
1080     while (tokenizer.Next(result))
1081         args.push_back(result);
1082
1083     return true;
1084 }
1085
1086 void MIProtocol::CommandLoop()
1087 {
1088     std::string token;
1089
1090     Printf("(gdb)\n");
1091
1092     while (!m_exit)
1093     {
1094         token.clear();
1095         std::string input;
1096
1097         std::getline(std::cin, input);
1098         if (input.empty() && std::cin.eof())
1099             break;
1100
1101         std::vector<std::string> args;
1102         std::string command;
1103         if (!ParseLine(input, token, command, args))
1104         {
1105             Printf("%s^error,msg=\"Failed to parse input\"\n", token.c_str());
1106             continue;
1107         }
1108
1109         std::string output;
1110         HRESULT hr = HandleCommand(command, args, output);
1111
1112         if (m_exit)
1113             break;
1114
1115         if (SUCCEEDED(hr))
1116         {
1117             const char *resultClass;
1118             if (output.empty())
1119                 resultClass = "^done";
1120             else if (output.at(0) == '^')
1121                 resultClass = "";
1122             else
1123                 resultClass = "^done,";
1124
1125             Printf("%s%s%s\n", token.c_str(), resultClass, output.c_str());
1126         }
1127         else
1128         {
1129             if (output.empty())
1130             {
1131                 Printf("%s^error,msg=\"Error: 0x%08x\"\n", token.c_str(), hr);
1132             }
1133             else
1134             {
1135                 Printf("%s^error,msg=\"%s\"\n", token.c_str(), MIProtocol::EscapeMIValue(output).c_str());
1136             }
1137         }
1138         Printf("(gdb)\n");
1139     }
1140
1141     if (!m_exit)
1142         m_debugger->Disconnect(Debugger::DisconnectTerminate);
1143
1144     Printf("%s^exit\n", token.c_str());
1145     Printf("(gdb)\n");
1146 }
1147
1148 std::mutex MIProtocol::m_outMutex;
1149
1150 void MIProtocol::Printf(const char *fmt, ...)
1151 {
1152     std::string strbuffer;
1153     char buffer[32];
1154
1155     const char *out = nullptr;
1156
1157     va_list arg;
1158
1159     va_start(arg, fmt);
1160     int n = vsnprintf(buffer, sizeof(buffer), fmt, arg);
1161     va_end(arg);
1162
1163     if (n < 0)
1164         return;
1165
1166     if (n >= sizeof(buffer))
1167     {
1168         strbuffer.resize(n);
1169
1170         va_start(arg, fmt);
1171         n = vsnprintf(&strbuffer[0], strbuffer.size() + 1, fmt, arg);
1172         va_end(arg);
1173
1174         if (n < 0 || n > strbuffer.size())
1175             return;
1176         out = strbuffer.c_str();
1177     }
1178     else
1179     {
1180         buffer[n] = '\0';
1181         out = buffer;
1182     }
1183
1184     std::lock_guard<std::mutex> lock(m_outMutex);
1185     std::cout << out;
1186     std::cout.flush();
1187 }
1188
1189 std::string MIProtocol::EscapeMIValue(const std::string &str)
1190 {
1191     std::string s(str);
1192
1193     for (std::size_t i = 0; i < s.size(); ++i)
1194     {
1195         int count = 0;
1196         char c = s.at(i);
1197         switch (c)
1198         {
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;
1209         }
1210         i += count;
1211     }
1212
1213     return s;
1214 }