1412 lines · 44.2 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 <set>#include <utils.h>#include <vm_memory.h>#include <vm_asm.h>#include <exceptions.h>#include "exec.h" using disvm::vm_t;using disvm::loaded_vm_module_t;using disvm::opcode_t; using disvm::runtime::type_descriptor_t;using disvm::runtime::intrinsic_type_desc;using disvm::runtime::word_t;using disvm::runtime::pointer_t;using disvm::runtime::module_id_t;using disvm::runtime::runtime_flags_t;using disvm::runtime::cookie_t;using disvm::runtime::vm_pc_t;using disvm::runtime::vm_registers_t;using disvm::runtime::vm_module_t;using disvm::runtime::vm_module_ref_t;using disvm::runtime::vm_alloc_t;using disvm::runtime::vm_string_t;using disvm::runtime::vm_array_t;using disvm::runtime::vm_list_t;using disvm::runtime::vm_channel_t;using disvm::runtime::vm_thread_state_t;using disvm::runtime::vm_trap_flags_t;using disvm::runtime::vm_term_request;using disvm::runtime::vm_tool_controller_t;using disvm::runtime::vm_event_t;using disvm::runtime::vm_event_context_t; using disvm::symbol::function_name_format_t;using disvm::symbol::advance_pc_t;using disvm::symbol::symbol_data_t; 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    }     bool vm_debug_enabled = false;     template<typename T>    struct vm_dbg_type final    {        T t;    };     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    {        static std::string last_successful_cmd_option;         dbg_cmd_cxt_t(debugger &d, vm_registers_t &r, bool transient_context = false)            : dbg{ d }            , exit_break{ false }            , current_register{ &r }            , initial_register{ r }            , transient_context{ transient_context }            , vm{ d.controller->get_vm_instance() }        {            last_successful_cmd = dbg.get_option(last_successful_cmd_option);        }         ~dbg_cmd_cxt_t()        {            if (!transient_context)                dbg.set_option(last_successful_cmd_option, std::move(last_successful_cmd));        }         const bool transient_context;        bool exit_break;        std::string last_successful_cmd;         debugger &dbg;        const vm_t &vm;        const vm_registers_t *current_register;        vm_registers_t &initial_register;    };     std::string dbg_cmd_cxt_t::last_successful_cmd_option = "last_successful_cmd";     // 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, 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;            }             msg << "@" << pc << "\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     Module:  " << r.module_ref->module->module_name->str()            << "\n         PC:  " << r.pc;         if (!r.module_ref->is_builtin_module())        {            assert(static_cast<std::size_t>(r.pc) < r.module_ref->code_section.size());            const auto &op = r.module_ref->code_section[r.pc].op;            register_string                << "\n        Src:  " << op.source                << "\n        Mid:  " << op.middle                << "\n        Dst:  " << op.destination;        }         register_string << "\n";        return register_string.str();    }     std::ostream& operator<<(std::ostream &ss, const std::shared_ptr<const type_descriptor_t> &t)    {        ss << "size: " << t->size_in_bytes; #ifndef NDEBUG        ss << " n: " << t->debug_type_name;#endif         return ss;    }     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 << escaped_string.str() << "    length: " << s->get_length() << " - string";         return ss;    }     std::ostream& operator<<(std::ostream &ss, const vm_array_t *a)    {        ss << "length: " << a->get_length()            << " - array of " << a->get_element_type();         return ss;    }     std::ostream& operator<<(std::ostream &ss, const vm_list_t *l)    {        ss << "length: " << l->get_length()            << " - list of " << l->get_element_type();         return ss;    }     std::ostream& operator<<(std::ostream &ss, const vm_channel_t *c)    {        ss << "buffer size: " << c->get_buffer_size()            << " - channel of" << c->get_data_type();         return ss;    }     std::ostream& operator<<(std::ostream &ss, const vm_dbg_type<const vm_alloc_t *> &dbg_alloc)    {        if (!vm_debug_enabled)            return ss;         return ss            << " [[ref: " << dbg_alloc.t->get_ref_count()            << " addr: " << reinterpret_cast<const void *>(dbg_alloc.t)            << " gc_res: " << reinterpret_cast<const void *>(dbg_alloc.t->gc_reserved)            << "]]";    }     std::ostream& safe_alloc_print(std::ostream &ss, const vm_alloc_t *alloc, int recurse_depth)    {        if (alloc == nullptr)            return ss << "<nil>";         vm_dbg_type<const vm_alloc_t *> dbg_alloc{ alloc };        if (alloc->alloc_type == intrinsic_type_desc::type<vm_string_t>())        {            ss << vm_alloc_t::from_allocation<vm_string_t>(alloc->get_allocation()) << dbg_alloc;        }        else if (alloc->alloc_type == intrinsic_type_desc::type<vm_array_t>())        {            ss << vm_alloc_t::from_allocation<vm_array_t>(alloc->get_allocation()) << dbg_alloc;        }        else if (alloc->alloc_type == intrinsic_type_desc::type<vm_list_t>())        {            ss << vm_alloc_t::from_allocation<vm_list_t>(alloc->get_allocation()) << dbg_alloc;        }        else if (alloc->alloc_type == intrinsic_type_desc::type<vm_channel_t>())        {            ss << vm_alloc_t::from_allocation<vm_channel_t>(alloc->get_allocation()) << dbg_alloc;        }        else        {            ss << alloc->alloc_type << " - alloc" << dbg_alloc;             const auto base_address = reinterpret_cast<std::uintptr_t>(alloc->get_allocation());            enum_pointer_fields(*alloc->alloc_type, alloc->get_allocation(), [&ss, recurse_depth, base_address](pointer_t *p)            {                ss << "\n";                std::fill_n(std::ostream_iterator<std::ostream::char_type>{ ss }, 4 * recurse_depth, ' ');                const auto p_address = reinterpret_cast<std::uintptr_t>(p);                ss << "[" << std::size_t{ p_address - base_address } << "]  ";                 if (recurse_depth < 2)                    safe_alloc_print(ss, vm_alloc_t::from_allocation(*p), recurse_depth + 1);                else                    ss << "...";            });        }         return ss;    }     std::ostream& operator<<(std::ostream &ss, const vm_alloc_t *alloc)    {        return safe_alloc_print(ss, alloc, 0);    }     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_step(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt)    {        cxt.dbg.controller->set_thread_trap_flag(*cxt.current_register, vm_trap_flags_t::instruction);        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_registers().current_thread_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 << " [PC range]      Function";        for (auto &f : module_funcs)            msg << "\n  " << f;         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" }; 

Lines 1–500 of 1412.