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 (disvm::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); auto func_maybe = cxt.dbg.resolve_to_function_source_line(details.module->vm_id, details.pc, details.pc, function_name_format_t::name); if (!func_maybe.empty()) { assert(func_maybe.size() == 1); msg << "!" << func_maybe[0] << " "; } msg << "@" << 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. if (thread_id != 0) break; } } } if (thread_registers.empty()) throw debug_cmd_error_t{ "Unknown thread ID" }; auto msg = std::stringstream{}; for (auto t : thread_registers) { msg << "Thread " << t.first << ":\n"; msg << stack_to_string(*t.second, cxt.dbg); msg << "\n"; } debug_print_info(msg.str()); } void cmd_switchthread(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) { if (a.size() == 1) throw debug_cmd_error_t{ "Must supply a target thread ID" }; uint32_t thread_id; try { thread_id = std::stoul(a[1]); } catch (...) { throw debug_cmd_error_t{ "Invalid thread ID format" }; } auto all_threads = cxt.vm.get_scheduler_control().get_all_threads(); // Find the thread for (auto t : all_threads) { const auto tid = t->get_thread_id(); if (tid == thread_id) { cxt.current_register = &t->get_registers(); return; } } throw debug_cmd_error_t{ "Unknown thread ID" }; } void cmd_disassemble(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) { const auto &r = *cxt.current_register; auto begin_pc = r.pc; const auto default_disassemble_length = int{ 4 }; auto disassemble_length = default_disassemble_length; for (auto i = std::size_t{ 1 }; i < a.size(); ++i) { auto absolute_pc = false; auto disassemble_length_maybe = a[i]; if (disassemble_length_maybe[0] == '@') { absolute_pc = true; disassemble_length_maybe.erase(0, 1); } try { disassemble_length = std::stoi(disassemble_length_maybe); } catch (...) { throw debug_cmd_error_t{ "Invalid disassemble count format" }; } if (absolute_pc) { begin_pc = disassemble_length; disassemble_length = default_disassemble_length; } } auto end_pc = begin_pc; auto &dbg = cxt.dbg; const auto &code_section = r.module_ref->code_section; if (begin_pc < 0 || static_cast<int32_t>(code_section.size()) <= end_pc) throw debug_cmd_error_t{ "Invalid absolute pc" }; if (disassemble_length < 0) { const auto begin_maybe = begin_pc - (-disassemble_length); begin_pc = std::max(begin_maybe, 0); } else { const auto end_maybe = begin_pc + disassemble_length; end_pc = std::min(end_maybe, static_cast<int32_t>(code_section.size() - 1)); } auto resolved_pc = dbg.resolve_to_function_source_line(r.module_ref->module->vm_id, begin_pc, end_pc, function_name_format_t::name); assert(resolved_pc.empty() || resolved_pc.size() == (end_pc - begin_pc + 1) && "Resolved PCs should have failed or match the PC count"); auto msg = std::stringstream{}; for (auto curr_pc = begin_pc; curr_pc <= end_pc; ++curr_pc) { auto op = code_section[curr_pc].op; auto op_prefix = " "; // If the opcode is a breakpoint then try and replace it with the real opcode if (op.opcode == opcode_t::brkpt) { // Look up each of the known cookies to try and find the matching PC and module for (auto bp_cookie : dbg.breakpoint_cookies) { const auto details = dbg.controller->get_breakpoint_details(bp_cookie); if (details.pc == curr_pc && details.module == r.module_ref->module) { op.opcode = details.original_opcode; op_prefix = " b "; break; } } } // Print source location if resolved if (!resolved_pc.empty()) { const auto &rpc = resolved_pc[curr_pc - begin_pc]; if (!rpc.empty()) msg << rpc << "\n"; } msg << op_prefix << std::setw(5) << curr_pc << ": " << op << "\n"; } debug_print_info(msg.str()); } void cmd_examine(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) { if (a.size() < 3) throw debug_cmd_error_t{ "Invalid number of arguments" }; std::shared_ptr<const type_descriptor_t> base_type; word_t *base_pointer; auto &base_ptr_id = a[1]; if (base_ptr_id.compare("mp") == 0) { base_type = cxt.current_register->mp_base->alloc_type; base_pointer = reinterpret_cast<word_t *>(cxt.current_register->mp_base->get_allocation()); } else if (base_ptr_id.compare("fp") == 0) { auto frame = cxt.current_register->stack.peek_frame(); if (frame == nullptr) throw debug_cmd_error_t{ "Invalid frame pointer " }; base_type = frame->frame_type; base_pointer = reinterpret_cast<word_t *>(frame->base()); } else { throw debug_cmd_error_t{ "Invalid memory base pointer" }; } assert(base_type != nullptr && base_pointer != nullptr); const auto pointer_size = static_cast<int>(sizeof(pointer_t)); auto byte_offset1 = int{ 0 }; auto byte_offset2 = int{ -1 }; try { byte_offset1 = std::stoi(a[2]); // The 4th argument is optional if (a.size() >= 4) byte_offset2 = std::stoi(a[3]); } catch (...) { throw debug_cmd_error_t{ "Invalid memory offset format" }; } if (0 > byte_offset1 || byte_offset1 >= base_type->size_in_bytes) throw debug_cmd_error_t{ "First offset out of range" }; auto msg = std::stringstream{}; if (byte_offset2 != -1) msg << byte_offset2 << "(" << byte_offset1 << "(" << a[1] << ")):\n"; else msg << byte_offset1 << "(" << a[1] << "):\n"; auto value_is_pointer = bool{ false }; const auto pointer_offset1 = byte_offset1 / pointer_size; auto value_to_examine = reinterpret_cast<pointer_t>(base_pointer[pointer_offset1]); if (disvm::runtime::is_offset_pointer(*base_type, pointer_offset1)) { value_is_pointer = true; auto alloc_to_examine = vm_alloc_t::from_allocation(value_to_examine); if (byte_offset2 != -1) { if (alloc_to_examine == nullptr) throw debug_cmd_error_t{ "First offset is nil" }; if (0 > byte_offset2 || byte_offset2 >= alloc_to_examine->alloc_type->size_in_bytes) throw debug_cmd_error_t{ "Second offset out of range" }; // Update data pointer for 2nd indirection base_pointer = reinterpret_cast<word_t *>(alloc_to_examine->get_allocation()); base_type = alloc_to_examine->alloc_type; const auto pointer_offset2 = byte_offset2 / pointer_size; value_to_examine = reinterpret_cast<pointer_t>(base_pointer[pointer_offset2]); value_is_pointer = disvm::runtime::is_offset_pointer(*base_type, pointer_offset2); } } if (value_is_pointer) msg << " " << vm_alloc_t::from_allocation(value_to_examine); else msg << " " << std::setw(8) << std::showbase << std::hex << value_to_examine << std::dec; debug_print_info(msg.str()); } // Forward declaration void print_help(); void cmd_help(const std::vector<std::string> &, dbg_cmd_cxt_t &) { print_help(); } // ee void cmd_tlk_tilde_star(const std::vector<std::string> &, dbg_cmd_cxt_t &) { std::cout << console_modifiers::black_font << console_modifiers::white_bkg << "H" << console_modifiers::reset_all << console_modifiers::red_font << console_modifiers::bold << "e" << console_modifiers::green_font << console_modifiers::bold << "l" << console_modifiers::yellow_font << console_modifiers::bold << "l" << console_modifiers::blue_font << console_modifiers::bold << "o" << console_modifiers::magenta_font << console_modifiers::bold << ", " << console_modifiers::black_font << console_modifiers::white_bkg << "W" << console_modifiers::reset_all << console_modifiers::red_font << console_modifiers::bold << "o" << console_modifiers::green_font << console_modifiers::bold << "r" << console_modifiers::yellow_font << console_modifiers::bold << "l" << console_modifiers::blue_font << console_modifiers::bold << "d" << console_modifiers::magenta_font << console_modifiers::bold << "!\n" << console_modifiers::reset_all; } std::set<std::string> dbg_options = { { "bp-cmd" }, { "step-cmd" }, { "vm-dbg" } }; void set_value_cmd(const std::vector<std::string> &args, dbg_cmd_cxt_t &cxt) { auto msg = std::stringstream{}; if (args.size() == 1) { msg << "Debug options\n"; for (auto &option : dbg_options) { auto v = cxt.dbg.get_option(option); msg << std::setw(16) << option << ": " << (v.empty() ? "<none>" : v) << "\n"; } debug_print_info(msg.str()); return; } auto &curr_option = args[1]; if (std::cend(dbg_options) == dbg_options.find(curr_option)) throw debug_cmd_error_t{ "Invalid debug option" }; auto ss_value = std::stringstream{}; // Reconstitute the parsed debug arguments - in-case of space breaks. for (auto i = std::size_t{ 2 }; i < args.size(); ++i) { ss_value << args[i]; // For the last parsed arg if (i + 1 < args.size()) ss_value << " "; } auto new_value = ss_value.str(); if (std::strcmp(curr_option.c_str(), "vm-dbg") == 0) { vm_debug_enabled = !new_value.empty(); if (vm_debug_enabled) new_value = "true"; else new_value = ""; } cxt.dbg.set_option(curr_option, new_value); } const dbg_cmd_t cmds[] = { { "c", "Continue", nullptr, nullptr, cmd_continue }, { "s", "Step over", nullptr, nullptr, cmd_step }, { "term", "Terminate the VM instance", nullptr, nullptr, cmd_term }, { "r", "Print registers", nullptr, nullptr, cmd_print_registers }, { "t", "Print all threads", nullptr, nullptr, cmd_print_threads }, { "m", "Print all loaded modules", nullptr, nullptr, cmd_print_modules }, { "sym", "Print symbols for the supplied module", "sym [0-9]+ <partial function name>?", "sym 2 foo (Print symbols in module 2 containing 'foo')", cmd_print_symbols }, { "bs", "Set breakpoint at the specified IP in a loaded module", "bs [0-9]+ [0-9]*", "bs 3 5 (Breakpoint at IP 3 in module 5), bs 4 (Breakpoint at IP 4 in current module)", cmd_breakpoint_set }, { "bc", "Clear breakpoint(s)", "bc ([0-9]+|\\*)", "bc 3, bc *", cmd_breakpoint_clear },Lines 501–1000 of 1412. Previous lines Load more
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 (disvm::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); auto func_maybe = cxt.dbg.resolve_to_function_source_line(details.module->vm_id, details.pc, details.pc, function_name_format_t::name); if (!func_maybe.empty()) { assert(func_maybe.size() == 1); msg << "!" << func_maybe[0] << " "; } msg << "@" << 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. if (thread_id != 0) break; } } } if (thread_registers.empty()) throw debug_cmd_error_t{ "Unknown thread ID" }; auto msg = std::stringstream{}; for (auto t : thread_registers) { msg << "Thread " << t.first << ":\n"; msg << stack_to_string(*t.second, cxt.dbg); msg << "\n"; } debug_print_info(msg.str()); } void cmd_switchthread(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) { if (a.size() == 1) throw debug_cmd_error_t{ "Must supply a target thread ID" }; uint32_t thread_id; try { thread_id = std::stoul(a[1]); } catch (...) { throw debug_cmd_error_t{ "Invalid thread ID format" }; } auto all_threads = cxt.vm.get_scheduler_control().get_all_threads(); // Find the thread for (auto t : all_threads) { const auto tid = t->get_thread_id(); if (tid == thread_id) { cxt.current_register = &t->get_registers(); return; } } throw debug_cmd_error_t{ "Unknown thread ID" }; } void cmd_disassemble(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) { const auto &r = *cxt.current_register; auto begin_pc = r.pc; const auto default_disassemble_length = int{ 4 }; auto disassemble_length = default_disassemble_length; for (auto i = std::size_t{ 1 }; i < a.size(); ++i) { auto absolute_pc = false; auto disassemble_length_maybe = a[i]; if (disassemble_length_maybe[0] == '@') { absolute_pc = true; disassemble_length_maybe.erase(0, 1); } try { disassemble_length = std::stoi(disassemble_length_maybe); } catch (...) { throw debug_cmd_error_t{ "Invalid disassemble count format" }; } if (absolute_pc) { begin_pc = disassemble_length; disassemble_length = default_disassemble_length; } } auto end_pc = begin_pc; auto &dbg = cxt.dbg; const auto &code_section = r.module_ref->code_section; if (begin_pc < 0 || static_cast<int32_t>(code_section.size()) <= end_pc) throw debug_cmd_error_t{ "Invalid absolute pc" }; if (disassemble_length < 0) { const auto begin_maybe = begin_pc - (-disassemble_length); begin_pc = std::max(begin_maybe, 0); } else { const auto end_maybe = begin_pc + disassemble_length; end_pc = std::min(end_maybe, static_cast<int32_t>(code_section.size() - 1)); } auto resolved_pc = dbg.resolve_to_function_source_line(r.module_ref->module->vm_id, begin_pc, end_pc, function_name_format_t::name); assert(resolved_pc.empty() || resolved_pc.size() == (end_pc - begin_pc + 1) && "Resolved PCs should have failed or match the PC count"); auto msg = std::stringstream{}; for (auto curr_pc = begin_pc; curr_pc <= end_pc; ++curr_pc) { auto op = code_section[curr_pc].op; auto op_prefix = " "; // If the opcode is a breakpoint then try and replace it with the real opcode if (op.opcode == opcode_t::brkpt) { // Look up each of the known cookies to try and find the matching PC and module for (auto bp_cookie : dbg.breakpoint_cookies) { const auto details = dbg.controller->get_breakpoint_details(bp_cookie); if (details.pc == curr_pc && details.module == r.module_ref->module) { op.opcode = details.original_opcode; op_prefix = " b "; break; } } } // Print source location if resolved if (!resolved_pc.empty()) { const auto &rpc = resolved_pc[curr_pc - begin_pc]; if (!rpc.empty()) msg << rpc << "\n"; } msg << op_prefix << std::setw(5) << curr_pc << ": " << op << "\n"; } debug_print_info(msg.str()); } void cmd_examine(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) { if (a.size() < 3) throw debug_cmd_error_t{ "Invalid number of arguments" }; std::shared_ptr<const type_descriptor_t> base_type; word_t *base_pointer; auto &base_ptr_id = a[1]; if (base_ptr_id.compare("mp") == 0) { base_type = cxt.current_register->mp_base->alloc_type; base_pointer = reinterpret_cast<word_t *>(cxt.current_register->mp_base->get_allocation()); } else if (base_ptr_id.compare("fp") == 0) { auto frame = cxt.current_register->stack.peek_frame(); if (frame == nullptr) throw debug_cmd_error_t{ "Invalid frame pointer " }; base_type = frame->frame_type; base_pointer = reinterpret_cast<word_t *>(frame->base()); } else { throw debug_cmd_error_t{ "Invalid memory base pointer" }; } assert(base_type != nullptr && base_pointer != nullptr); const auto pointer_size = static_cast<int>(sizeof(pointer_t)); auto byte_offset1 = int{ 0 }; auto byte_offset2 = int{ -1 }; try { byte_offset1 = std::stoi(a[2]); // The 4th argument is optional if (a.size() >= 4) byte_offset2 = std::stoi(a[3]); } catch (...) { throw debug_cmd_error_t{ "Invalid memory offset format" }; } if (0 > byte_offset1 || byte_offset1 >= base_type->size_in_bytes) throw debug_cmd_error_t{ "First offset out of range" }; auto msg = std::stringstream{}; if (byte_offset2 != -1) msg << byte_offset2 << "(" << byte_offset1 << "(" << a[1] << ")):\n"; else msg << byte_offset1 << "(" << a[1] << "):\n"; auto value_is_pointer = bool{ false }; const auto pointer_offset1 = byte_offset1 / pointer_size; auto value_to_examine = reinterpret_cast<pointer_t>(base_pointer[pointer_offset1]); if (disvm::runtime::is_offset_pointer(*base_type, pointer_offset1)) { value_is_pointer = true; auto alloc_to_examine = vm_alloc_t::from_allocation(value_to_examine); if (byte_offset2 != -1) { if (alloc_to_examine == nullptr) throw debug_cmd_error_t{ "First offset is nil" }; if (0 > byte_offset2 || byte_offset2 >= alloc_to_examine->alloc_type->size_in_bytes) throw debug_cmd_error_t{ "Second offset out of range" }; // Update data pointer for 2nd indirection base_pointer = reinterpret_cast<word_t *>(alloc_to_examine->get_allocation()); base_type = alloc_to_examine->alloc_type; const auto pointer_offset2 = byte_offset2 / pointer_size; value_to_examine = reinterpret_cast<pointer_t>(base_pointer[pointer_offset2]); value_is_pointer = disvm::runtime::is_offset_pointer(*base_type, pointer_offset2); } } if (value_is_pointer) msg << " " << vm_alloc_t::from_allocation(value_to_examine); else msg << " " << std::setw(8) << std::showbase << std::hex << value_to_examine << std::dec; debug_print_info(msg.str()); } // Forward declaration void print_help(); void cmd_help(const std::vector<std::string> &, dbg_cmd_cxt_t &) { print_help(); } // ee void cmd_tlk_tilde_star(const std::vector<std::string> &, dbg_cmd_cxt_t &) { std::cout << console_modifiers::black_font << console_modifiers::white_bkg << "H" << console_modifiers::reset_all << console_modifiers::red_font << console_modifiers::bold << "e" << console_modifiers::green_font << console_modifiers::bold << "l" << console_modifiers::yellow_font << console_modifiers::bold << "l" << console_modifiers::blue_font << console_modifiers::bold << "o" << console_modifiers::magenta_font << console_modifiers::bold << ", " << console_modifiers::black_font << console_modifiers::white_bkg << "W" << console_modifiers::reset_all << console_modifiers::red_font << console_modifiers::bold << "o" << console_modifiers::green_font << console_modifiers::bold << "r" << console_modifiers::yellow_font << console_modifiers::bold << "l" << console_modifiers::blue_font << console_modifiers::bold << "d" << console_modifiers::magenta_font << console_modifiers::bold << "!\n" << console_modifiers::reset_all; } std::set<std::string> dbg_options = { { "bp-cmd" }, { "step-cmd" }, { "vm-dbg" } }; void set_value_cmd(const std::vector<std::string> &args, dbg_cmd_cxt_t &cxt) { auto msg = std::stringstream{}; if (args.size() == 1) { msg << "Debug options\n"; for (auto &option : dbg_options) { auto v = cxt.dbg.get_option(option); msg << std::setw(16) << option << ": " << (v.empty() ? "<none>" : v) << "\n"; } debug_print_info(msg.str()); return; } auto &curr_option = args[1]; if (std::cend(dbg_options) == dbg_options.find(curr_option)) throw debug_cmd_error_t{ "Invalid debug option" }; auto ss_value = std::stringstream{}; // Reconstitute the parsed debug arguments - in-case of space breaks. for (auto i = std::size_t{ 2 }; i < args.size(); ++i) { ss_value << args[i]; // For the last parsed arg if (i + 1 < args.size()) ss_value << " "; } auto new_value = ss_value.str(); if (std::strcmp(curr_option.c_str(), "vm-dbg") == 0) { vm_debug_enabled = !new_value.empty(); if (vm_debug_enabled) new_value = "true"; else new_value = ""; } cxt.dbg.set_option(curr_option, new_value); } const dbg_cmd_t cmds[] = { { "c", "Continue", nullptr, nullptr, cmd_continue }, { "s", "Step over", nullptr, nullptr, cmd_step }, { "term", "Terminate the VM instance", nullptr, nullptr, cmd_term }, { "r", "Print registers", nullptr, nullptr, cmd_print_registers }, { "t", "Print all threads", nullptr, nullptr, cmd_print_threads }, { "m", "Print all loaded modules", nullptr, nullptr, cmd_print_modules }, { "sym", "Print symbols for the supplied module", "sym [0-9]+ <partial function name>?", "sym 2 foo (Print symbols in module 2 containing 'foo')", cmd_print_symbols }, { "bs", "Set breakpoint at the specified IP in a loaded module", "bs [0-9]+ [0-9]*", "bs 3 5 (Breakpoint at IP 3 in module 5), bs 4 (Breakpoint at IP 4 in current module)", cmd_breakpoint_set }, { "bc", "Clear breakpoint(s)", "bc ([0-9]+|\\*)", "bc 3, bc *", cmd_breakpoint_clear },