//// Dis VM// File: debugger.cpp// Author: arr// #include <iostream>#include <fstream>#include <iomanip>#include <sstream>#include <string>#include <algorithm>#include <map>#include <utils.h>#include <vm_memory.h>#include <vm_asm.h>#include <exceptions.h>#include "exec.h" using namespace disvm;using namespace disvm::runtime; namespace{ // https://en.wikipedia.org/wiki/ANSI_escape_code namespace console_modifiers {#define CSI "\33[" const auto black_font = CSI "30m"; const auto red_font = CSI "31m"; const auto green_font = CSI "32m"; const auto yellow_font = CSI "33m"; const auto blue_font = CSI "34m"; const auto magenta_font = CSI "35m"; const auto cyan_font = CSI "36m"; const auto white_font = CSI "37m"; const auto black_bkg = CSI "40m"; const auto red_bkg = CSI "41m"; const auto green_bkg = CSI "42m"; const auto yellow_bkg = CSI "43m"; const auto blue_bkg = CSI "44m"; const auto magenta_bkg = CSI "45m"; const auto cyan_bkg = CSI "46m"; const auto white_bkg = CSI "47m"; const auto bold = CSI "1m"; const auto reset_all = CSI "0m"; #undef CSI } struct debug_cmd_error_t : std::runtime_error { debug_cmd_error_t(const char *message) : std::runtime_error{ message } { } }; void debug_print_error(const char *str) { std::cout << console_modifiers::red_font << console_modifiers::bold << "Error: " << str << console_modifiers::reset_all << "\n"; } void debug_print_error(const std::string &str) { debug_print_error(str.c_str()); } void debug_print_info(const std::string &str) { std::cout << console_modifiers::yellow_font << console_modifiers::bold << str << console_modifiers::reset_all << "\n"; } // Debug command context struct dbg_cmd_cxt_t { dbg_cmd_cxt_t(debugger &d, const vm_registers_t &r) : dbg{ d } , exit_break{ false } , initial_register{ r } , vm{ d.controller->get_vm_instance() } { current_register = &initial_register; } bool exit_break; debugger &dbg; const vm_t &vm; const vm_registers_t *current_register; const vm_registers_t &initial_register; }; // Debug command exec operation using dbg_cmd_exec_t = void(*)(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt); // Debug command struct dbg_cmd_t { const char *cmd; const char *description; const char *syntax; const char *example; dbg_cmd_exec_t exec; }; std::string stack_to_string(const vm_registers_t &r, debugger &dbg) { auto msg = std::stringstream{}; walk_stack(r, [&msg, &dbg](const pointer_t, const vm_pc_t pc, const vm_module_ref_t &module_ref) { auto resolved_name = dbg.resolve_function_source_line(module_ref.module, pc); if (!resolved_name.empty()) { msg << resolved_name; } else { auto module_name = "<No Name>"; if (module_ref.module->module_name != nullptr) module_name = module_ref.module->module_name->str(); msg << module_name << "@" << pc; } msg << "\n"; return true; }); return msg.str(); } std::string registers_to_string(const vm_registers_t &r) { auto register_string = std::stringstream{}; register_string << " Thread ID: " << r.thread.get_thread_id() << "\n" << " PC: " << r.pc << " Module: " << r.module_ref->module->module_name->str() << "\n" << " Opcode: " << r.module_ref->code_section[r.pc].op << "\n"; return register_string.str(); } std::ostream& operator<<(std::ostream &ss, const vm_string_t *s) { auto escaped_string = std::stringstream{}; auto str = s->str(); if (str != nullptr) { while (*str != '\0') { switch (*str) { case '\\': escaped_string << "\\\\"; break; case '\a': escaped_string << "\\a"; break; case '\b': escaped_string << "\\b"; break; case '\f': escaped_string << "\\f"; break; case '\n': escaped_string << "\\n"; break; case '\r': escaped_string << "\\r"; break; case '\t': escaped_string << "\\t"; break; case '\v': escaped_string << "\\v"; break; default: escaped_string << *str; } str++; } } ss << "string\n" << "\tlength: " << s->get_length() << "\n" << "\tvalue: \"" << escaped_string.str() << "\"\n"; return ss; } std::ostream& operator<<(std::ostream &ss, const vm_array_t *a) { ss << "array\n" << "\tlength: " << a->get_length() << "\n"; return ss; } std::ostream& operator<<(std::ostream &ss, const vm_list_t *l) { ss << "list\n" << "\tlength: " << l->get_length() << "\n"; return ss; } std::ostream& operator<<(std::ostream &ss, const vm_channel_t *c) { ss << "channel\n" << "\tbuffer size: " << c->get_buffer_size() << "\n"; return ss; } std::ostream& operator<<(std::ostream &ss, const vm_alloc_t *alloc) { if (alloc == nullptr) { ss << "<nil>"; } else if (alloc->alloc_type == intrinsic_type_desc::type<vm_string_t>()) { ss << vm_alloc_t::from_allocation<vm_string_t>(alloc->get_allocation()); } else if (alloc->alloc_type == intrinsic_type_desc::type<vm_array_t>()) { ss << vm_alloc_t::from_allocation<vm_array_t>(alloc->get_allocation()); } else if (alloc->alloc_type == intrinsic_type_desc::type<vm_list_t>()) { ss << vm_alloc_t::from_allocation<vm_list_t>(alloc->get_allocation()); } else if (alloc->alloc_type == intrinsic_type_desc::type<vm_channel_t>()) { ss << vm_alloc_t::from_allocation<vm_channel_t>(alloc->get_allocation()); } else { ss << "alloc - size: " << alloc->alloc_type->size_in_bytes << "\n"; enum_pointer_fields(*alloc->alloc_type, alloc->get_allocation(), [&ss](pointer_t p, std::size_t o) { ss << " [" << o << "] " << vm_alloc_t::from_allocation(p); }); } return ss; } std::ostream& operator<<(std::ostream &ss, const vm_thread_state_t s) { switch (s) { case vm_thread_state_t::unknown: ss << "unknown"; break; case vm_thread_state_t::blocked_in_alt: ss << "blocked-in-alt"; break; case vm_thread_state_t::blocked_sending: ss << "blocked-sending"; break; case vm_thread_state_t::blocked_receiving: ss << "blocked-receiving"; break; case vm_thread_state_t::debug: ss << "debug"; break; case vm_thread_state_t::ready: ss << "ready"; break; case vm_thread_state_t::running: ss << "running"; break; case vm_thread_state_t::release: ss << "release"; break; case vm_thread_state_t::exiting: ss << "exiting"; break; case vm_thread_state_t::empty_stack: ss << "empty-stack"; break; case vm_thread_state_t::broken: ss << "broken"; break; default: ss << "??"; } return ss; } void cmd_continue(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) { cxt.exit_break = true; } void cmd_term(const std::vector<std::string> &, dbg_cmd_cxt_t &) { throw vm_term_request{}; } void cmd_print_registers(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) { auto s = registers_to_string(*cxt.current_register); debug_print_info(s); } void cmd_print_threads(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) { auto msg = std::stringstream{}; for (auto t : cxt.vm.get_scheduler_control().get_all_threads()) msg << " " << std::setw(5) << t->get_thread_id() << " - " << t->get_state() << "\n"; debug_print_info(msg.str()); } void cmd_print_modules(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) { auto msg = std::stringstream{}; cxt.vm.enum_loaded_modules([&msg](const loaded_vm_module_t &m) { auto module_instance = m.module.lock(); if (module_instance != nullptr) { msg << std::setw(3) << m.vm_id << ": " << std::setw(16) << module_instance->module_name->str() << std::setw(0) << " - " << m.origin->str() << "\n"; } return true; }); debug_print_info(msg.str()); } void cmd_breakpoint_set(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) { if (a.size() < 2) throw debug_cmd_error_t{ "Must supply an IP" }; vm_pc_t pc; try { pc = std::stoul(a[1]); } catch (...) { throw debug_cmd_error_t{ "Invalid IP format" }; } auto module = std::const_pointer_cast<vm_module_t>(cxt.current_register->module_ref->module); if (a.size() > 2) { module.reset(); uint32_t module_id; try { module_id = std::stoul(a[2]); } catch (...) { throw debug_cmd_error_t{ "Invalid module ID format" }; } cxt.vm.enum_loaded_modules([module_id, &module](const loaded_vm_module_t &m) { if (module_id != m.vm_id) return true; // Found the matching ID so stop module = m.module.lock(); return false; }); } if (module == nullptr) { throw debug_cmd_error_t{ "Invalid module ID" }; } else if (util::has_flag(module->header.runtime_flag, runtime_flags_t::builtin)) { throw debug_cmd_error_t{ "Unable to set breakpoint in built-in module" }; } if (pc < 0 || module->code_section.size() <= static_cast<std::size_t>(pc)) throw debug_cmd_error_t{ "Invalid IP for module" }; cxt.dbg.breakpoint_cookies.emplace(cxt.dbg.controller->set_breakpoint(module, pc)); } void cmd_breakpoint_clear(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) { if (a.size() == 1) throw debug_cmd_error_t{ "Must supply a breakpoint ID or wildcard" }; auto controller = cxt.dbg.controller; if (a[1].compare("*") == 0) { // Clear all breakpoints for (auto cookie : cxt.dbg.breakpoint_cookies) controller->clear_breakpoint(cookie); cxt.dbg.breakpoint_cookies.clear(); return; } // Clear specific breakpoint cookie_t breakpoint_cookie; try { breakpoint_cookie = static_cast<cookie_t>(std::stoul(a[1])); } catch (...) { throw debug_cmd_error_t{ "Invalid breakpoint ID format" }; } auto cookie_iter = std::find_if( std::begin(cxt.dbg.breakpoint_cookies), std::end(cxt.dbg.breakpoint_cookies), [breakpoint_cookie] (const cookie_t c) { return c == breakpoint_cookie; }); if (cookie_iter == cxt.dbg.breakpoint_cookies.end()) throw debug_cmd_error_t{ "Unknown breakpoint ID" }; // Clear the breakpoint controller->clear_breakpoint(breakpoint_cookie); // Remove the cookie from the list cxt.dbg.breakpoint_cookies.erase(cookie_iter); } void cmd_breakpoint_list(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) { auto msg = std::stringstream{}; for (auto cookie : cxt.dbg.breakpoint_cookies) { const auto details = cxt.dbg.controller->get_breakpoint_details(cookie); msg << std::setw(3) << cookie << ": " << std::setw(16) << details.module->module_name->str() << std::setw(0) << "@" << details.pc << "\n"; } debug_print_info(msg.str()); } void cmd_stackwalk(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) { auto thread_registers = std::map<uint32_t, const vm_registers_t*>{}; if (a.size() == 1) { thread_registers[cxt.current_register->thread.get_thread_id()] = cxt.current_register; } else { uint32_t thread_id; if (a[1].compare("*") == 0) { thread_id = 0; } else { try { thread_id = std::stoul(a[1]); } catch (...) { throw debug_cmd_error_t{ "Invalid thread ID format" }; } } // Find the thread(s) auto all_threads = cxt.vm.get_scheduler_control().get_all_threads(); for (auto t : all_threads) { const auto tid = t->get_thread_id(); if (thread_id == 0 || tid == thread_id) { thread_registers[tid] = &t->get_registers(); // If we are looking for a single thread, we found it.Lines 1–500 of 1094. Load more
// // Dis VM // File: debugger.cpp // Author: arr // #include <iostream> #include <fstream> #include <iomanip> #include <sstream> #include <string> #include <algorithm> #include <map> #include <utils.h> #include <vm_memory.h> #include <vm_asm.h> #include <exceptions.h> #include "exec.h" using namespace disvm; using namespace disvm::runtime; namespace { // https://en.wikipedia.org/wiki/ANSI_escape_code namespace console_modifiers { #define CSI "\33[" const auto black_font = CSI "30m"; const auto red_font = CSI "31m"; const auto green_font = CSI "32m"; const auto yellow_font = CSI "33m"; const auto blue_font = CSI "34m"; const auto magenta_font = CSI "35m"; const auto cyan_font = CSI "36m"; const auto white_font = CSI "37m"; const auto black_bkg = CSI "40m"; const auto red_bkg = CSI "41m"; const auto green_bkg = CSI "42m"; const auto yellow_bkg = CSI "43m"; const auto blue_bkg = CSI "44m"; const auto magenta_bkg = CSI "45m"; const auto cyan_bkg = CSI "46m"; const auto white_bkg = CSI "47m"; const auto bold = CSI "1m"; const auto reset_all = CSI "0m"; #undef CSI } struct debug_cmd_error_t : std::runtime_error { debug_cmd_error_t(const char *message) : std::runtime_error{ message } { } }; void debug_print_error(const char *str) { std::cout << console_modifiers::red_font << console_modifiers::bold << "Error: " << str << console_modifiers::reset_all << "\n"; } void debug_print_error(const std::string &str) { debug_print_error(str.c_str()); } void debug_print_info(const std::string &str) { std::cout << console_modifiers::yellow_font << console_modifiers::bold << str << console_modifiers::reset_all << "\n"; } // Debug command context struct dbg_cmd_cxt_t { dbg_cmd_cxt_t(debugger &d, const vm_registers_t &r) : dbg{ d } , exit_break{ false } , initial_register{ r } , vm{ d.controller->get_vm_instance() } { current_register = &initial_register; } bool exit_break; debugger &dbg; const vm_t &vm; const vm_registers_t *current_register; const vm_registers_t &initial_register; }; // Debug command exec operation using dbg_cmd_exec_t = void(*)(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt); // Debug command struct dbg_cmd_t { const char *cmd; const char *description; const char *syntax; const char *example; dbg_cmd_exec_t exec; }; std::string stack_to_string(const vm_registers_t &r, debugger &dbg) { auto msg = std::stringstream{}; walk_stack(r, [&msg, &dbg](const pointer_t, const vm_pc_t pc, const vm_module_ref_t &module_ref) { auto resolved_name = dbg.resolve_function_source_line(module_ref.module, pc); if (!resolved_name.empty()) { msg << resolved_name; } else { auto module_name = "<No Name>"; if (module_ref.module->module_name != nullptr) module_name = module_ref.module->module_name->str(); msg << module_name << "@" << pc; } msg << "\n"; return true; }); return msg.str(); } std::string registers_to_string(const vm_registers_t &r) { auto register_string = std::stringstream{}; register_string << " Thread ID: " << r.thread.get_thread_id() << "\n" << " PC: " << r.pc << " Module: " << r.module_ref->module->module_name->str() << "\n" << " Opcode: " << r.module_ref->code_section[r.pc].op << "\n"; return register_string.str(); } std::ostream& operator<<(std::ostream &ss, const vm_string_t *s) { auto escaped_string = std::stringstream{}; auto str = s->str(); if (str != nullptr) { while (*str != '\0') { switch (*str) { case '\\': escaped_string << "\\\\"; break; case '\a': escaped_string << "\\a"; break; case '\b': escaped_string << "\\b"; break; case '\f': escaped_string << "\\f"; break; case '\n': escaped_string << "\\n"; break; case '\r': escaped_string << "\\r"; break; case '\t': escaped_string << "\\t"; break; case '\v': escaped_string << "\\v"; break; default: escaped_string << *str; } str++; } } ss << "string\n" << "\tlength: " << s->get_length() << "\n" << "\tvalue: \"" << escaped_string.str() << "\"\n"; return ss; } std::ostream& operator<<(std::ostream &ss, const vm_array_t *a) { ss << "array\n" << "\tlength: " << a->get_length() << "\n"; return ss; } std::ostream& operator<<(std::ostream &ss, const vm_list_t *l) { ss << "list\n" << "\tlength: " << l->get_length() << "\n"; return ss; } std::ostream& operator<<(std::ostream &ss, const vm_channel_t *c) { ss << "channel\n" << "\tbuffer size: " << c->get_buffer_size() << "\n"; return ss; } std::ostream& operator<<(std::ostream &ss, const vm_alloc_t *alloc) { if (alloc == nullptr) { ss << "<nil>"; } else if (alloc->alloc_type == intrinsic_type_desc::type<vm_string_t>()) { ss << vm_alloc_t::from_allocation<vm_string_t>(alloc->get_allocation()); } else if (alloc->alloc_type == intrinsic_type_desc::type<vm_array_t>()) { ss << vm_alloc_t::from_allocation<vm_array_t>(alloc->get_allocation()); } else if (alloc->alloc_type == intrinsic_type_desc::type<vm_list_t>()) { ss << vm_alloc_t::from_allocation<vm_list_t>(alloc->get_allocation()); } else if (alloc->alloc_type == intrinsic_type_desc::type<vm_channel_t>()) { ss << vm_alloc_t::from_allocation<vm_channel_t>(alloc->get_allocation()); } else { ss << "alloc - size: " << alloc->alloc_type->size_in_bytes << "\n"; enum_pointer_fields(*alloc->alloc_type, alloc->get_allocation(), [&ss](pointer_t p, std::size_t o) { ss << " [" << o << "] " << vm_alloc_t::from_allocation(p); }); } return ss; } std::ostream& operator<<(std::ostream &ss, const vm_thread_state_t s) { switch (s) { case vm_thread_state_t::unknown: ss << "unknown"; break; case vm_thread_state_t::blocked_in_alt: ss << "blocked-in-alt"; break; case vm_thread_state_t::blocked_sending: ss << "blocked-sending"; break; case vm_thread_state_t::blocked_receiving: ss << "blocked-receiving"; break; case vm_thread_state_t::debug: ss << "debug"; break; case vm_thread_state_t::ready: ss << "ready"; break; case vm_thread_state_t::running: ss << "running"; break; case vm_thread_state_t::release: ss << "release"; break; case vm_thread_state_t::exiting: ss << "exiting"; break; case vm_thread_state_t::empty_stack: ss << "empty-stack"; break; case vm_thread_state_t::broken: ss << "broken"; break; default: ss << "??"; } return ss; } void cmd_continue(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) { cxt.exit_break = true; } void cmd_term(const std::vector<std::string> &, dbg_cmd_cxt_t &) { throw vm_term_request{}; } void cmd_print_registers(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) { auto s = registers_to_string(*cxt.current_register); debug_print_info(s); } void cmd_print_threads(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) { auto msg = std::stringstream{}; for (auto t : cxt.vm.get_scheduler_control().get_all_threads()) msg << " " << std::setw(5) << t->get_thread_id() << " - " << t->get_state() << "\n"; debug_print_info(msg.str()); } void cmd_print_modules(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) { auto msg = std::stringstream{}; cxt.vm.enum_loaded_modules([&msg](const loaded_vm_module_t &m) { auto module_instance = m.module.lock(); if (module_instance != nullptr) { msg << std::setw(3) << m.vm_id << ": " << std::setw(16) << module_instance->module_name->str() << std::setw(0) << " - " << m.origin->str() << "\n"; } return true; }); debug_print_info(msg.str()); } void cmd_breakpoint_set(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) { if (a.size() < 2) throw debug_cmd_error_t{ "Must supply an IP" }; vm_pc_t pc; try { pc = std::stoul(a[1]); } catch (...) { throw debug_cmd_error_t{ "Invalid IP format" }; } auto module = std::const_pointer_cast<vm_module_t>(cxt.current_register->module_ref->module); if (a.size() > 2) { module.reset(); uint32_t module_id; try { module_id = std::stoul(a[2]); } catch (...) { throw debug_cmd_error_t{ "Invalid module ID format" }; } cxt.vm.enum_loaded_modules([module_id, &module](const loaded_vm_module_t &m) { if (module_id != m.vm_id) return true; // Found the matching ID so stop module = m.module.lock(); return false; }); } if (module == nullptr) { throw debug_cmd_error_t{ "Invalid module ID" }; } else if (util::has_flag(module->header.runtime_flag, runtime_flags_t::builtin)) { throw debug_cmd_error_t{ "Unable to set breakpoint in built-in module" }; } if (pc < 0 || module->code_section.size() <= static_cast<std::size_t>(pc)) throw debug_cmd_error_t{ "Invalid IP for module" }; cxt.dbg.breakpoint_cookies.emplace(cxt.dbg.controller->set_breakpoint(module, pc)); } void cmd_breakpoint_clear(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) { if (a.size() == 1) throw debug_cmd_error_t{ "Must supply a breakpoint ID or wildcard" }; auto controller = cxt.dbg.controller; if (a[1].compare("*") == 0) { // Clear all breakpoints for (auto cookie : cxt.dbg.breakpoint_cookies) controller->clear_breakpoint(cookie); cxt.dbg.breakpoint_cookies.clear(); return; } // Clear specific breakpoint cookie_t breakpoint_cookie; try { breakpoint_cookie = static_cast<cookie_t>(std::stoul(a[1])); } catch (...) { throw debug_cmd_error_t{ "Invalid breakpoint ID format" }; } auto cookie_iter = std::find_if( std::begin(cxt.dbg.breakpoint_cookies), std::end(cxt.dbg.breakpoint_cookies), [breakpoint_cookie] (const cookie_t c) { return c == breakpoint_cookie; }); if (cookie_iter == cxt.dbg.breakpoint_cookies.end()) throw debug_cmd_error_t{ "Unknown breakpoint ID" }; // Clear the breakpoint controller->clear_breakpoint(breakpoint_cookie); // Remove the cookie from the list cxt.dbg.breakpoint_cookies.erase(cookie_iter); } void cmd_breakpoint_list(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) { auto msg = std::stringstream{}; for (auto cookie : cxt.dbg.breakpoint_cookies) { const auto details = cxt.dbg.controller->get_breakpoint_details(cookie); msg << std::setw(3) << cookie << ": " << std::setw(16) << details.module->module_name->str() << std::setw(0) << "@" << details.pc << "\n"; } debug_print_info(msg.str()); } void cmd_stackwalk(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) { auto thread_registers = std::map<uint32_t, const vm_registers_t*>{}; if (a.size() == 1) { thread_registers[cxt.current_register->thread.get_thread_id()] = cxt.current_register; } else { uint32_t thread_id; if (a[1].compare("*") == 0) { thread_id = 0; } else { try { thread_id = std::stoul(a[1]); } catch (...) { throw debug_cmd_error_t{ "Invalid thread ID format" }; } } // Find the thread(s) auto all_threads = cxt.vm.get_scheduler_control().get_all_threads(); for (auto t : all_threads) { const auto tid = t->get_thread_id(); if (thread_id == 0 || tid == thread_id) { thread_registers[tid] = &t->get_registers(); // If we are looking for a single thread, we found it.