1285 lines · 40.1 KB · C++
RawDownload
            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, symbol::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)    {        auto absolute_pc = false;        auto disassemble_length = int{ 4 };        if (a.size() > 1)        {            auto disassemble_length_maybe = a[1];            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" };            }        }         auto &dbg = cxt.dbg;        const auto &r = *cxt.current_register;        const auto &code_section = r.module_ref->code_section;         // The current PC is actually the next PC to execute.        auto begin_pc = r.pc == 0 ? r.pc : r.pc - 1;        auto end_pc = begin_pc;        if (absolute_pc)        {            begin_pc = disassemble_length;            end_pc = disassemble_length;            disassemble_length = 0;             if (begin_pc < 0 || static_cast<int32_t>(code_section.size()) <= end_pc)                throw debug_cmd_error_t{ "Invalid absolute pc" };        }        else 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, symbol::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 (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 = 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::unordered_set<std::string> dbg_options =    {        { "bp-cmd" }    };     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 new_value = std::stringstream{};         // Reconstitute the parsed debug arguments - in-case of space breaks.        for (auto i = std::size_t{ 2 }; i < args.size(); ++i)        {            new_value << args[i];             // For the last parsed arg            if (i + 1 < args.size())                new_value << " ";        }         cxt.dbg.set_option(curr_option, new_value.str());    }     const dbg_cmd_t cmds[] =    {        { "c", "Continue", nullptr, nullptr, cmd_continue },        { "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 },        { "bl", "List breakpoints", nullptr, nullptr, cmd_breakpoint_list },         { "sw", "Stack walk for the current or supplied thread", "sw ([0-9]+|\\*)?", "sw, sw 34, sw *", cmd_stackwalk },        { "~", "Switch to thread", "~ [0-9]+", "~ 42", cmd_switchthread },        { "d", "Disassemble the next/previous N instructions (default N = 4)", "d (@?-?[0-9]+)?", "d, d -4, d 5, d @36", cmd_disassemble },        { "x", "Examine memory", "x [mp|fp] [0-9]+ ([0-9]+)?", "x mp 3, x fp 20 6", cmd_examine },         { "set" , "Set a debug option", "set [ _a-zA-Z0-9]*", "'set' for available options", set_value_cmd },        { "?", "Print help", nullptr, nullptr, cmd_help },         // Undocumented commands        { "tlk~*", nullptr, nullptr, nullptr, cmd_tlk_tilde_star }    };     const auto cmds_length = sizeof(cmds) / sizeof(cmds[0]);     dbg_cmd_exec_t find_cmd(const std::string &cmd_maybe)    {        for (auto i = std::size_t{}; i < cmds_length; ++i)        {            if (cmd_maybe.compare(cmds[i].cmd) == 0)                return cmds[i].exec;        }         // [TODO] Didn't find the cmd, try ignoring case?        throw debug_cmd_error_t{ "Unknown command. '?' for help." };    }     void print_help()    {        auto msg = std::stringstream{};        msg << "Commands:\n";        for (auto i = std::size_t{}; i < cmds_length; ++i)        {            auto &c = cmds[i];             // Ignore if undocumented            if (c.description == nullptr)                continue;             msg << "  " << c.cmd << "    " << c.description << "\n";             if (c.syntax != nullptr)                msg << "      Syntax: " << c.syntax << "\n";             if (c.example != nullptr)                msg << "      Example(s): " << c.example << "\n";        }         debug_print_info(msg.str());    }     void execute_single_command(dbg_cmd_cxt_t &cxt, std::string cmd)    {        const auto cmd_tokens = util::split(cmd);        if (cmd_tokens.empty())            return;         try        {            auto cmd_exec = find_cmd(cmd_tokens[0]);            cmd_exec(cmd_tokens, cxt);             cxt.last_successful_cmd = std::move(cmd);        }        catch (const debug_cmd_error_t &e)        {            debug_print_error(e.what());        }    }     void prompt(debugger &debugger, const vm_registers_t &r)    {        debugger.controller->suspend_all_threads();         std::cout            << console_modifiers::green_font            << console_modifiers::bold            << "'?' for help\n"            << console_modifiers::reset_all;         std::string cmd;         dbg_cmd_cxt_t cxt{ debugger, r };        for (;;)        {            std::cout                << console_modifiers::green_font                << console_modifiers::bold                << cxt.current_register->thread.get_thread_id()                << " >>> "                << console_modifiers::reset_all;             std::getline(std::cin, cmd);            if (cmd.empty())                cmd = cxt.last_successful_cmd;             assert(!cxt.exit_break && "Precondition on exit_break state before execute failed");            execute_single_command(cxt, std::move(cmd));            if (cxt.exit_break)                break;        }         debugger.controller->resume_all_threads();    }} std::ostream& operator<<(std::ostream &ss, const debugger_options o){    ss << "Break on enter: " << std::boolalpha << util::has_flag(o, debugger_options::break_on_enter) << "\n"

Lines 501–1000 of 1285. Previous lines