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
5//5//
6 6
7#include <iostream>7#include <iostream>
8#include <iomanip>
8#include <sstream>9#include <sstream>
9#include <string>10#include <string>
11#include <algorithm>
10#include <locale>12#include <locale>
13#include <map>
11#include <vm_tools.h>14#include <vm_tools.h>
12#include <utils.h>15#include <utils.h>
13#include <vm_memory.h>16#include <vm_memory.h>
@@ -19,6 +22,70 @@ using namespace disvm::runtime; · 5 unmodified lines
19 22
20namespace23namespace
21{24{
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
22 std::string stack_to_string(const vm_registers_t &r)89 std::string stack_to_string(const vm_registers_t &r)
23 {90 {
24 auto msg = std::stringstream{};91 auto msg = std::stringstream{};
@@ -41,10 +108,10 @@ namespace · 16 unmodified lines
41 108
42 register_string109 register_string
43 << "Registers:\n"110 << "Registers:\n"
44 << " Thread ID: " << r.thread.get_thread_id() << "\n"111 << " Thread ID: " << r.thread.get_thread_id() << "\n"
45 << " Module: " << r.module_ref->module->module_name->str() << "\n"112 << " Module: " << r.module_ref->module->module_name->str() << "\n"
46 << "Program counter: " << r.pc << "\n"113 << " PC: " << r.pc << "\n"
47 << " Opcode: " << r.module_ref->code_section[r.pc].op << "\n";114 << " Opcode: " << r.module_ref->code_section[r.pc].op << "\n";
48 115
49 return register_string.str();116 return register_string.str();
50 }117 }
@@ -52,8 +119,8 @@ namespace · 1 unmodified line
52 std::ostream& operator<<(std::ostream &ss, const vm_string_t *s)119 std::ostream& operator<<(std::ostream &ss, const vm_string_t *s)
53 {120 {
54 ss << "string\n"121 ss << "string\n"
55 << "\tlength: " << s->get_length() << "\n"122 << "\tlength: " << s->get_length() << "\n"
56 << "\tvalue: " << s->str() << "\n";123 << "\tvalue: " << s->str() << "\n";
57 124
58 return ss;125 return ss;
59 }126 }
@@ -61,7 +128,7 @@ namespace · 1 unmodified line
61 std::ostream& operator<<(std::ostream &ss, const vm_array_t *a)128 std::ostream& operator<<(std::ostream &ss, const vm_array_t *a)
62 {129 {
63 ss << "array\n"130 ss << "array\n"
64 << "\tlength: " << a->get_length() << "\n";131 << "\tlength: " << a->get_length() << "\n";
65 132
66 return ss;133 return ss;
67 }134 }
@@ -69,7 +136,7 @@ namespace · 1 unmodified line
69 std::ostream& operator<<(std::ostream &ss, const vm_list_t *l)136 std::ostream& operator<<(std::ostream &ss, const vm_list_t *l)
70 {137 {
71 ss << "list\n"138 ss << "list\n"
72 << "\tlength: " << l->get_length() << "\n";139 << "\tlength: " << l->get_length() << "\n";
73 140
74 return ss;141 return ss;
75 }142 }
@@ -77,15 +144,59 @@ namespace · 1 unmodified line
77 std::ostream& operator<<(std::ostream &ss, const vm_channel_t *c)144 std::ostream& operator<<(std::ostream &ss, const vm_channel_t *c)
78 {145 {
79 ss << "channel\n"146 ss << "channel\n"
80 << "\tbuffer size: " << c->get_buffer_size() << "\n";147 << "\tbuffer size: " << c->get_buffer_size() << "\n";
81 148
82 return ss;149 return ss;
83 }150 }
84 151
85 std::ostream& operator<<(std::ostream &ss, const vm_alloc_t *a)152 std::ostream& operator<<(std::ostream &ss, const vm_alloc_t *a)
86 {153 {
87 ss << "alloc\n"154 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 }
89 200
90 return ss;201 return ss;
91 }202 }
@@ -117,65 +228,317 @@ namespace · 25 unmodified lines
117 return frame_string.str();228 return frame_string.str();
118 }229 }
119 230
120 namespace console_modifiers231 bool cmd_continue(const std::vector<std::string> &, const vm_t &, const vm_registers_t &)
121 {232 {
122#define CSI "\33["233 return false;
234 }
123 235
124 const auto black_font = CSI "30m";236 bool cmd_print_registers(const std::vector<std::string> &, const vm_t &, const vm_registers_t &r)
125 const auto red_font = CSI "31m";237 {
126 const auto green_font = CSI "32m";238 auto s = registers_to_string(r);
127 const auto yellow_font = CSI "33m";239 debug_print_info(s);
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";
132 240
133 const auto black_bkg = CSI "40m";241 return true;
134 const auto red_bkg = CSI "41m";242 }
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";
141 243
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";
143 250
144 const auto reset_all = CSI "0m";251 debug_print_info(msg.str());
145 252
146#undef CSI253 return true;
147 }254 }
148 255
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 &)
150 {257 {
151 std::cout258 auto msg = std::stringstream{};
152 << console_modifiers::red_font259 msg << "Modules:\n";
153 << console_modifiers::bold260 for (auto m : vm.get_loaded_modules())
154 << "Error: "261 msg << " " << m->module_name->str() << "\n";
155 << str262
156 << console_modifiers::reset_all263 debug_print_info(msg.str());
157 << "\n";264
265 return true;
158 }266 }
159 267
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 &)
161 {422 {
162 std::cout423 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"
163 << console_modifiers::yellow_font434 << console_modifiers::yellow_font
164 << console_modifiers::bold435 << console_modifiers::bold
165 << str436 << "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"
166 << console_modifiers::reset_all446 << 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;
168 }465 }
169 466
170 auto prompt_help =467 const dbg_cmd_t cmds[] =
171R"(Help:468 {
172 c C - continue469 { "c", "Continue", nullptr, nullptr, cmd_continue },
173 r R - print registers470 { "r", "Print registers", nullptr, nullptr, cmd_print_registers },
174 w W - print stack471 { "t", "Print all threads", nullptr, nullptr, cmd_print_threads },
175 ? - help472 { "m", "Print all loaded modules", nullptr, nullptr, cmd_print_modules },
176)";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 }
177 491
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)
179 {542 {
180 std::cout543 std::cout
181 << console_modifiers::green_font544 << console_modifiers::green_font
@@ -198,29 +561,20 @@ R"(Help: · 16 unmodified lines
198 if (cmd.empty())561 if (cmd.empty())
199 continue;562 continue;
200 563
201 auto c = std::toupper(cmd[0], loc);564 const auto cmd_tokens = split(cmd);
202 if (c == 'R')565 if (cmd_tokens.empty())
203 {566 continue;
204 auto str = registers_to_string(r);567
205 debug_print_info(str);568 auto cmd_exec = find_cmd(cmd_tokens[0]);
206 }569 if (cmd_exec != nullptr)
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 == '?')
217 {570 {
218 debug_print_info(prompt_help);571 if (!cmd_exec(cmd_tokens, vm, r))
572 break;
219 }573 }
220 else574 else
221 {575 {
222 debug_print_error("Unknown command");576 debug_print_error("Unknown command");
223 debug_print_info(prompt_help);577 print_help();
224 }578 }
225 }579 }
226 }580 }
@@ -229,6 +583,7 @@ R"(Help: · 2 unmodified lines
229void debugger::on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t tool_id)583void debugger::on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t tool_id)
230{584{
231 _tool_id = tool_id;585 _tool_id = tool_id;
586 _vm = &vm;
232 587
233 _event_cookies.push_back(controller.subscribe_event(vm_event_t::thread_begin, [](vm_event_t, vm_event_context_t &cxt)588 _event_cookies.push_back(controller.subscribe_event(vm_event_t::thread_begin, [](vm_event_t, vm_event_context_t &cxt)
234 {589 {
@@ -264,15 +619,15 @@ void debugger::on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t t · 29 unmodified lines
264 debug_print_info(ss.str());619 debug_print_info(ss.str());
265 }));620 }));
266 621
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)
268 {623 {
269 auto ss = std::stringstream{};624 auto ss = std::stringstream{};
270 ss << "Unhandled exception '"625 ss << "Unhandled exception '"
271 << cxt.value2.str->str()626 << cxt.value2.str->str()
272 << "' thrown.";627 << "'";
273 628
274 debug_print_error(ss.str());629 debug_print_error(ss.str());
275 prompt(*cxt.value1.registers);630 prompt(*_vm, *cxt.value1.registers);
276 }));631 }));
277 632
278 _event_cookies.push_back(controller.subscribe_event(vm_event_t::module_load, [](vm_event_t, vm_event_context_t &cxt)633 _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
304 _controller->unsubscribe_event(ec);659 _controller->unsubscribe_event(ec);
305 660
306 _controller = nullptr;661 _controller = nullptr;
662 _vm = nullptr;
307}663}