1412 lines · 44.2 KB · C++
RawDownload
        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