1203 lines · 37.5 KB · C++
RawDownload
//// 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 func_maybe = dbg.resolve_to_function_source_line(module_ref.module->vm_id, pc, pc, symbol::function_name_format_t::name);            if (!func_maybe.empty())            {                assert(func_maybe.size() == 1);                msg << module_ref.module->module_name->str() << "!" << func_maybe[0];            }            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, &cxt](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();                 if (cxt.dbg.symbols_exist(m.vm_id))                    msg << " [symbols loaded]";                 msg << "\n";            }             return true;        });         debug_print_info(msg.str());    }     void cmd_print_symbols(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt)    {        if (a.size() < 2)            throw debug_cmd_error_t{ "Must supply a module ID" };         module_id_t vm_id;        try        {            vm_id = std::stoul(a[1]);        }        catch (...)        {            throw debug_cmd_error_t{ "Invalid module ID format" };        }         if (!cxt.dbg.symbols_exist(vm_id))            throw debug_cmd_error_t{ "No symbols loaded for module" };         // Interpret the last non-module ID value as the partial name to match.        const char *name_to_match = nullptr;        if (a.size() > 2)            name_to_match = a.back().c_str();         auto msg = std::stringstream{};        auto module_funcs = cxt.dbg.get_module_functions(vm_id, name_to_match);        msg << "Function name [PC range]\n";        for (auto &f : module_funcs)            msg << "  " << f << "\n";         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);             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())

Lines 1–500 of 1203.