History

Added basic operations for debugger - disassembly - thread switch - walk stack on thread X/all - print threads, modules, registers

arr committed 10 years ago

Commit e9ca2861 parent fe11a372 files · +429 −72Browse files

Showing the changes to src/exec/debugger.cpp (1 of 2 files). Show all

Unified · Split

src/exec/debugger.cpp+428−72
@@ -5,9 +5,12 @@ · 4 unmodified lines
55 //
66
77 #include <iostream>
8+#include <iomanip>
89 #include <sstream>
910 #include <string>
11+#include <algorithm>
1012 #include <locale>
13+#include <map>
1114 #include <vm_tools.h>
1215 #include <utils.h>
1316 #include <vm_memory.h>
@@ -19,6 +22,70 @@ using namespace disvm::runtime; · 5 unmodified lines
1922
2023 namespace
2124 {
25+ // https://en.wikipedia.org/wiki/ANSI_escape_code
26+ namespace console_modifiers
27+ {
28+#define CSI "\33["
29+
30+ const auto black_font = CSI "30m";
31+ const auto red_font = CSI "31m";
32+ const auto green_font = CSI "32m";
33+ const auto yellow_font = CSI "33m";
34+ const auto blue_font = CSI "34m";
35+ const auto magenta_font = CSI "35m";
36+ const auto cyan_font = CSI "36m";
37+ const auto white_font = CSI "37m";
38+
39+ const auto black_bkg = CSI "40m";
40+ const auto red_bkg = CSI "41m";
41+ const auto green_bkg = CSI "42m";
42+ const auto yellow_bkg = CSI "43m";
43+ const auto blue_bkg = CSI "44m";
44+ const auto magenta_bkg = CSI "45m";
45+ const auto cyan_bkg = CSI "46m";
46+ const auto white_bkg = CSI "47m";
47+
48+ const auto bold = CSI "1m";
49+
50+ const auto reset_all = CSI "0m";
51+
52+#undef CSI
53+ }
54+
55+ void debug_print_error(const std::string &str)
56+ {
57+ std::cout
58+ << console_modifiers::red_font
59+ << console_modifiers::bold
60+ << "Error: "
61+ << str
62+ << console_modifiers::reset_all
63+ << "\n";
64+ }
65+
66+ void debug_print_info(const std::string &str)
67+ {
68+ std::cout
69+ << console_modifiers::yellow_font
70+ << console_modifiers::bold
71+ << str
72+ << console_modifiers::reset_all
73+ << "\n";
74+ }
75+
76+ // Debug command exec operation
77+ using dbg_cmd_exec_t = bool(*)(const std::vector<std::string> &, const vm_t &, const vm_registers_t &);
78+
79+ // Debug command
80+ struct dbg_cmd_t
81+ {
82+ const char *cmd;
83+ const char *description;
84+ const char *syntax;
85+ const char *example;
86+ dbg_cmd_exec_t exec;
87+ };
88+
2289 std::string stack_to_string(const vm_registers_t &r)
2390 {
2491 auto msg = std::stringstream{};
@@ -41,10 +108,10 @@ namespace · 16 unmodified lines
41108
42109 register_string
43110 << "Registers:\n"
44− << " Thread ID: " << r.thread.get_thread_id() << "\n"
45− << " Module: " << r.module_ref->module->module_name->str() << "\n"
46− << "Program counter: " << r.pc << "\n"
47− << " Opcode: " << r.module_ref->code_section[r.pc].op << "\n";
111+ << " Thread ID: " << r.thread.get_thread_id() << "\n"
112+ << " Module: " << r.module_ref->module->module_name->str() << "\n"
113+ << " PC: " << r.pc << "\n"
114+ << " Opcode: " << r.module_ref->code_section[r.pc].op << "\n";
48115
49116 return register_string.str();
50117 }
@@ -52,8 +119,8 @@ namespace · 1 unmodified line
52119 std::ostream& operator<<(std::ostream &ss, const vm_string_t *s)
53120 {
54121 ss << "string\n"
55− << "\tlength: " << s->get_length() << "\n"
56− << "\tvalue: " << s->str() << "\n";
122+ << "\tlength: " << s->get_length() << "\n"
123+ << "\tvalue: " << s->str() << "\n";
57124
58125 return ss;
59126 }
@@ -61,7 +128,7 @@ namespace · 1 unmodified line
61128 std::ostream& operator<<(std::ostream &ss, const vm_array_t *a)
62129 {
63130 ss << "array\n"
64− << "\tlength: " << a->get_length() << "\n";
131+ << "\tlength: " << a->get_length() << "\n";
65132
66133 return ss;
67134 }
@@ -69,7 +136,7 @@ namespace · 1 unmodified line
69136 std::ostream& operator<<(std::ostream &ss, const vm_list_t *l)
70137 {
71138 ss << "list\n"
72− << "\tlength: " << l->get_length() << "\n";
139+ << "\tlength: " << l->get_length() << "\n";
73140
74141 return ss;
75142 }
@@ -77,15 +144,59 @@ namespace · 1 unmodified line
77144 std::ostream& operator<<(std::ostream &ss, const vm_channel_t *c)
78145 {
79146 ss << "channel\n"
80− << "\tbuffer size: " << c->get_buffer_size() << "\n";
147+ << "\tbuffer size: " << c->get_buffer_size() << "\n";
81148
82− return ss;
149+ return ss;
83150 }
84151
85152 std::ostream& operator<<(std::ostream &ss, const vm_alloc_t *a)
86153 {
87154 ss << "alloc\n"
88− << "\tsize: " << a->alloc_type->size_in_bytes << "\n";
155+ << "\tsize: " << a->alloc_type->size_in_bytes << "\n";
156+
157+ return ss;
158+ }
159+
160+ std::ostream& operator<<(std::ostream &ss, const vm_thread_state_t s)
161+ {
162+ switch (s)
163+ {
164+ case vm_thread_state_t::unknown:
165+ ss << "unknown";
166+ break;
167+ case vm_thread_state_t::blocked_in_alt:
168+ ss << "blocked-in-alt";
169+ break;
170+ case vm_thread_state_t::blocked_sending:
171+ ss << "blocked-sending";
172+ break;
173+ case vm_thread_state_t::blocked_receiving:
174+ ss << "blocked-receiving";
175+ break;
176+ case vm_thread_state_t::debug:
177+ ss << "debug";
178+ break;
179+ case vm_thread_state_t::ready:
180+ ss << "ready";
181+ break;
182+ case vm_thread_state_t::running:
183+ ss << "running";
184+ break;
185+ case vm_thread_state_t::release:
186+ ss << "release";
187+ break;
188+ case vm_thread_state_t::exiting:
189+ ss << "exiting";
190+ break;
191+ case vm_thread_state_t::empty_stack:
192+ ss << "empty-stack";
193+ break;
194+ case vm_thread_state_t::broken:
195+ ss << "broken";
196+ break;
197+ default:
198+ ss << "??";
199+ }
89200
90201 return ss;
91202 }
@@ -117,65 +228,317 @@ namespace · 25 unmodified lines
117228 return frame_string.str();
118229 }
119230
120− namespace console_modifiers
231+ bool cmd_continue(const std::vector<std::string> &, const vm_t &, const vm_registers_t &)
121232 {
122−#define CSI "\33["
233+ return false;
234+ }
123235
124− const auto black_font = CSI "30m";
125− const auto red_font = CSI "31m";
126− const auto green_font = CSI "32m";
127− const auto yellow_font = CSI "33m";
128− const auto blue_font = CSI "34m";
129− const auto magenta_font = CSI "35m";
130− const auto cyan_font = CSI "36m";
131− const auto white_font = CSI "37m";
236+ bool cmd_print_registers(const std::vector<std::string> &, const vm_t &, const vm_registers_t &r)
237+ {
238+ auto s = registers_to_string(r);
239+ debug_print_info(s);
132240
133− const auto black_bkg = CSI "40m";
134− const auto red_bkg = CSI "41m";
135− const auto green_bkg = CSI "42m";
136− const auto yellow_bkg = CSI "43m";
137− const auto blue_bkg = CSI "44m";
138− const auto magenta_bkg = CSI "45m";
139− const auto cyan_bkg = CSI "46m";
140− const auto white_bkg = CSI "47m";
241+ return true;
242+ }
141243
142− const auto bold = CSI "1m";
244+ bool cmd_print_threads(const std::vector<std::string> &, const vm_t &vm, const vm_registers_t &)
245+ {
246+ auto msg = std::stringstream{};
247+ msg << "Threads:\n";
248+ for (auto t : vm.get_scheduler_control().get_all_threads())
249+ msg << " " << std::setw(5) << t->get_thread_id() << " - " << t->get_state() << "\n";
143250
144− const auto reset_all = CSI "0m";
251+ debug_print_info(msg.str());
145252
146−#undef CSI
253+ return true;
147254 }
148255
149− void debug_print_error(const std::string &str)
256+ bool cmd_print_modules(const std::vector<std::string> &, const vm_t &vm, const vm_registers_t &)
150257 {
151− std::cout
152− << console_modifiers::red_font
153− << console_modifiers::bold
154− << "Error: "
155− << str
156− << console_modifiers::reset_all
157− << "\n";
258+ auto msg = std::stringstream{};
259+ msg << "Modules:\n";
260+ for (auto m : vm.get_loaded_modules())
261+ msg << " " << m->module_name->str() << "\n";
262+
263+ debug_print_info(msg.str());
264+
265+ return true;
158266 }
159267
160− void debug_print_info(const std::string &str)
268+ bool cmd_stacktrace(const std::vector<std::string> &a, const vm_t &vm, const vm_registers_t &r)
269+ {
270+ auto thread_registers = std::map<uint32_t, const vm_registers_t*>{};
271+ if (a.size() == 1)
272+ {
273+ thread_registers[r.thread.get_thread_id()] = &r;
274+ }
275+ else
276+ {
277+ uint32_t thread_id;
278+ auto all_threads = vm.get_scheduler_control().get_all_threads();
279+ if (a[1].compare("*") == 0)
280+ {
281+ thread_id = 0;
282+ }
283+ else
284+ {
285+ try
286+ {
287+ thread_id = std::stoi(a[1]);
288+ }
289+ catch (...)
290+ {
291+ debug_print_error("Invalid thread ID format");
292+ return true;
293+ }
294+ }
295+
296+ // Find the thread(s)
297+ for (auto t : all_threads)
298+ {
299+ const auto tid = t->get_thread_id();
300+ if (thread_id == 0 || tid == thread_id)
301+ {
302+ thread_registers[tid] = &t->get_registers();
303+
304+ // If we are looking for a single thread, we found it.
305+ if (thread_id != 0)
306+ break;
307+ }
308+ }
309+ }
310+
311+ if (thread_registers.empty())
312+ {
313+ debug_print_error("Unknown thread ID");
314+ return true;
315+ }
316+
317+ auto msg = std::stringstream{};
318+ for (auto t : thread_registers)
319+ {
320+ msg << "Thread " << t.first << ":\n";
321+ msg << stack_to_string(*t.second);
322+ msg << "\n";
323+ }
324+
325+ debug_print_info(msg.str());
326+
327+ return true;
328+ }
329+
330+ // Forward declaration
331+ void prompt(const vm_t &, const vm_registers_t &);
332+
333+ bool cmd_switchthread(const std::vector<std::string> &a, const vm_t &vm, const vm_registers_t &r)
334+ {
335+ if (a.size() == 1)
336+ {
337+ debug_print_error("Must supply a target thread ID");
338+ return true;
339+ }
340+
341+ uint32_t thread_id;
342+ try
343+ {
344+ thread_id = std::stoi(a[1]);
345+ }
346+ catch (...)
347+ {
348+ debug_print_error("Invalid thread ID format");
349+ return true;
350+ }
351+
352+ auto all_threads = vm.get_scheduler_control().get_all_threads();
353+
354+ // Find the thread
355+ for (auto t : all_threads)
356+ {
357+ const auto tid = t->get_thread_id();
358+ if (tid == thread_id)
359+ {
360+ prompt(vm, t->get_registers());
361+
362+ // If we are returning from a thread switch we are not continuing, so return false to indicate as such.
363+ return false;
364+ }
365+ }
366+
367+ debug_print_error("Unknown thread ID");
368+ return true;
369+ }
370+
371+ bool cmd_disassemble(const std::vector<std::string> &a, const vm_t &, const vm_registers_t &r)
372+ {
373+ auto disassemble_length = 1;
374+ if (a.size() > 1)
375+ {
376+ try
377+ {
378+ disassemble_length = std::stoi(a[1]);
379+ }
380+ catch (...)
381+ {
382+ debug_print_error("Invalid disassemble count format");
383+ return true;
384+ }
385+ }
386+
387+ const auto &code_section = r.module_ref->code_section;
388+
389+ auto begin_index = static_cast<std::size_t>(r.pc);
390+ auto end_index = static_cast<std::size_t>(r.pc + 1);
391+ if (disassemble_length < 0)
392+ {
393+ const auto begin_maybe = r.pc - (-disassemble_length) + 1;
394+ begin_index = static_cast<std::size_t>(std::max(begin_maybe, 0));
395+ }
396+ else
397+ {
398+ const auto end_maybe = static_cast<std::size_t>(r.pc + disassemble_length);
399+ end_index = std::min(end_maybe, code_section.size());
400+ }
401+
402+ auto msg = std::stringstream{};
403+ for (auto i = begin_index; i < end_index; ++i)
404+ msg << " " << std::setw(5) << i << ": " << code_section[i].op << "\n";
405+
406+ debug_print_info(msg.str());
407+
408+ return true;
409+ }
410+
411+ // Forward declaration
412+ void print_help();
413+
414+ bool cmd_help(const std::vector<std::string> &, const vm_t &, const vm_registers_t &)
415+ {
416+ print_help();
417+ return true;
418+ }
419+
420+ // Easter egg
421+ bool cmd_tlk_tilde_star(const std::vector<std::string> &, const vm_t &, const vm_registers_t &)
161422 {
162423 std::cout
424+ << console_modifiers::black_font
425+ << console_modifiers::white_bkg
426+ << "H"
427+ << console_modifiers::reset_all
428+ << console_modifiers::red_font
429+ << console_modifiers::bold
430+ << "e"
431+ << console_modifiers::green_font
432+ << console_modifiers::bold
433+ << "l"
163434 << console_modifiers::yellow_font
164435 << console_modifiers::bold
165− << str
436+ << "l"
437+ << console_modifiers::blue_font
438+ << console_modifiers::bold
439+ << "o"
440+ << console_modifiers::magenta_font
441+ << console_modifiers::bold
442+ << ", "
443+ << console_modifiers::black_font
444+ << console_modifiers::white_bkg
445+ << "W"
166446 << console_modifiers::reset_all
167− << "\n";
447+ << console_modifiers::red_font
448+ << console_modifiers::bold
449+ << "o"
450+ << console_modifiers::green_font
451+ << console_modifiers::bold
452+ << "r"
453+ << console_modifiers::yellow_font
454+ << console_modifiers::bold
455+ << "l"
456+ << console_modifiers::blue_font
457+ << console_modifiers::bold
458+ << "d"
459+ << console_modifiers::magenta_font
460+ << console_modifiers::bold
461+ << "!\n"
462+ << console_modifiers::reset_all;
463+
464+ return true;
168465 }
169466
170− auto prompt_help =
171−R"(Help:
172− c C - continue
173− r R - print registers
174− w W - print stack
175− ? - help
176−)";
467+ const dbg_cmd_t cmds[] =
468+ {
469+ { "c", "Continue", nullptr, nullptr, cmd_continue },
470+ { "r", "Print registers", nullptr, nullptr, cmd_print_registers },
471+ { "t", "Print all threads", nullptr, nullptr, cmd_print_threads },
472+ { "m", "Print all loaded modules", nullptr, nullptr, cmd_print_modules },
473+ { "sw", "Stack walk/back trace", "bt ([0-9]+|\\*)?", "bt, bt 34, bt *", cmd_stacktrace },
474+ { "st", "Switch to thread", "st [0-9]+", "st 42", cmd_switchthread },
475+ { "d", "Disassemble the next/previous N instructions", "d (-?[0-9]+)?", "d, d -4, d 5", cmd_disassemble },
476+ { "?", "Print help", nullptr, nullptr, cmd_help },
477+ { "tlk~*", nullptr, nullptr, nullptr, cmd_tlk_tilde_star }
478+ };
479+
480+ dbg_cmd_exec_t find_cmd(const std::string &cmd_maybe)
481+ {
482+ for (auto i = std::size_t{}; i < sizeof(cmds) / sizeof(cmds[0]); ++i)
483+ {
484+ if (cmd_maybe.compare(cmds[i].cmd) == 0)
485+ return cmds[i].exec;
486+ }
487+
488+ // [TODO] Didn't find the cmd, try ignoring case?
489+ return nullptr;
490+ }
177491
178− void prompt(const vm_registers_t &r)
492+ void print_help()
493+ {
494+ auto ss = std::stringstream{};
495+ ss << "Commands:\n";
496+ for (auto i = std::size_t{}; i < sizeof(cmds) / sizeof(cmds[0]); ++i)
497+ {
498+ auto &c = cmds[i];
499+
500+ // Ignore if undocumented
501+ if (c.description == nullptr)
502+ continue;
503+
504+ ss << " " << c.cmd << "\t" << c.description << "\n";
505+
506+ if (c.syntax != nullptr)
507+ ss << "\t Syntax: " << c.syntax << "\n";
508+
509+ if (c.example != nullptr)
510+ ss << "\t Example(s): " << c.example << "\n";
511+ }
512+
513+ debug_print_info(ss.str());
514+ }
515+
516+ // split the string using the supplied delimiter
517+ std::vector<std::string> split(const std::string &str, const char delim = ' ')
518+ {
519+ auto split_string = std::vector<std::string>{};
520+
521+ auto curr = str.begin();
522+ auto end = str.cend();
523+ while (curr != end)
524+ {
525+ auto curr_begin = curr;
526+ while (curr != end && *curr != delim)
527+ ++curr;
528+
529+ if (curr_begin != curr)
530+ split_string.push_back(std::move(std::string{ curr_begin, curr }));
531+
532+ if (curr == end)
533+ break;
534+
535+ ++curr;
536+ }
537+
538+ return split_string;
539+ }
540+
541+ void prompt(const vm_t &vm, const vm_registers_t &r)
179542 {
180543 std::cout
181544 << console_modifiers::green_font
@@ -198,29 +561,20 @@ R"(Help: · 16 unmodified lines
198561 if (cmd.empty())
199562 continue;
200563
201− auto c = std::toupper(cmd[0], loc);
202− if (c == 'R')
203− {
204− auto str = registers_to_string(r);
205− debug_print_info(str);
206− }
207− else if (c == 'W')
208− {
209− auto str = stack_to_string(r);
210− debug_print_info(str);
211− }
212− else if (c == 'C')
213− {
214− break;
215− }
216− else if (c == '?')
564+ const auto cmd_tokens = split(cmd);
565+ if (cmd_tokens.empty())
566+ continue;
567+
568+ auto cmd_exec = find_cmd(cmd_tokens[0]);
569+ if (cmd_exec != nullptr)
217570 {
218− debug_print_info(prompt_help);
571+ if (!cmd_exec(cmd_tokens, vm, r))
572+ break;
219573 }
220574 else
221575 {
222576 debug_print_error("Unknown command");
223− debug_print_info(prompt_help);
577+ print_help();
224578 }
225579 }
226580 }
@@ -229,6 +583,7 @@ R"(Help: · 2 unmodified lines
229583 void debugger::on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t tool_id)
230584 {
231585 _tool_id = tool_id;
586+ _vm = &vm;
232587
233588 _event_cookies.push_back(controller.subscribe_event(vm_event_t::thread_begin, [](vm_event_t, vm_event_context_t &cxt)
234589 {
@@ -264,15 +619,15 @@ void debugger::on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t t · 29 unmodified lines
264619 debug_print_info(ss.str());
265620 }));
266621
267− _event_cookies.push_back(controller.subscribe_event(vm_event_t::exception_unhandled, [](vm_event_t, vm_event_context_t &cxt)
622+ _event_cookies.push_back(controller.subscribe_event(vm_event_t::exception_unhandled, [&](vm_event_t, vm_event_context_t &cxt)
268623 {
269624 auto ss = std::stringstream{};
270625 ss << "Unhandled exception '"
271626 << cxt.value2.str->str()
272− << "' thrown.";
627+ << "'";
273628
274629 debug_print_error(ss.str());
275− prompt(*cxt.value1.registers);
630+ prompt(*_vm, *cxt.value1.registers);
276631 }));
277632
278633 _event_cookies.push_back(controller.subscribe_event(vm_event_t::module_load, [](vm_event_t, vm_event_context_t &cxt)
@@ -304,4 +659,5 @@ void debugger::on_unload() · 25 unmodified lines
304659 _controller->unsubscribe_event(ec);
305660
306661 _controller = nullptr;
662+ _vm = nullptr;
307663 }