Showing the changes to src/exec/debugger.cpp (1 of 2 files). Show all
Unified · Split
| @@ -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 | ||
| 20 | namespace | 23 | namespace |
| 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_string | 109 | 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_modifiers | 231 | 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 CSI | 253 | 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::cout | 258 | auto msg = std::stringstream{}; |
| 152 | << console_modifiers::red_font | 259 | msg << "Modules:\n"; |
| 153 | << console_modifiers::bold | 260 | for (auto m : vm.get_loaded_modules()) |
| 154 | << "Error: " | 261 | msg << " " << m->module_name->str() << "\n"; |
| 155 | << str | 262 | |
| 156 | << console_modifiers::reset_all | 263 | 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::cout | 423 | 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_font | 434 | << console_modifiers::yellow_font |
| 164 | << console_modifiers::bold | 435 | << 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" | ||
| 166 | << console_modifiers::reset_all | 446 | << 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[] = |
| 171 | R"(Help: | 468 | { |
| 172 | c C - continue | 469 | { "c", "Continue", nullptr, nullptr, cmd_continue }, |
| 173 | r R - print registers | 470 | { "r", "Print registers", nullptr, nullptr, cmd_print_registers }, |
| 174 | w W - print stack | 471 | { "t", "Print all threads", nullptr, nullptr, cmd_print_threads }, |
| 175 | ? - help | 472 | { "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::cout | 543 | std::cout |
| 181 | << console_modifiers::green_font | 544 | << 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 | else | 574 | 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 | |||
| 229 | void debugger::on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t tool_id) | 583 | void 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 | } |