829 lines · 24.1 KB · C++
RawDownload
//// Dis VM// File: debugger.cpp// Author: arr// #include <iostream>#include <iomanip>#include <sstream>#include <string>#include <algorithm>#include <locale>#include <map>#include <vm_tools.h>#include <utils.h>#include <vm_memory.h>#include <vm_asm.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    }     void debug_print_error(const std::string &str)    {        std::cout            << console_modifiers::red_font            << console_modifiers::bold            << "Error: "            << str            << console_modifiers::reset_all            << "\n";    }     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(const vm_t &v, const vm_registers_t &r)            : initial_register{ r }            , exit_break{ false }            , vm{ v }        {            current_register = &initial_register;        }         bool exit_break;         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)    {        auto msg = std::stringstream{};        walk_stack(r, [&msg](const pointer_t, const vm_pc_t pc, const vm_module_ref_t &module_ref)        {            auto module_name = "<No Name>";            if (module_ref.module->module_name != nullptr)                module_name = module_ref.module->module_name->str();             msg << module_name << "@" << pc << "\n";            return true;        });         return msg.str();    }     std::string registers_to_string(const vm_registers_t &r)    {        auto register_string = std::stringstream{};         register_string            << "Registers:\n"            << "  Thread ID:  " << r.thread.get_thread_id() << "\n"            << "     Module:  " << r.module_ref->module->module_name->str() << "\n"            << "         PC:  " << r.pc << "\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\n"                << "\tsize:  " << alloc->alloc_type->size_in_bytes << "\n";        }         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;    }     std::string to_string_frame_pointers(vm_registers_t &r)    {        auto frame = r.stack.peek_frame();        if (frame == nullptr)            return "<empty>";         auto frame_string = std::stringstream{};        enum_pointer_fields(*frame->frame_type, frame->base(), [&frame_string](pointer_t p)        {            frame_string << std::showbase << std::hex << reinterpret_cast<uintptr_t>(p) << " : " << std::dec << vm_alloc_t::from_allocation(p);        });         return frame_string.str();    }     void cmd_continue(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt)    {        cxt.exit_break = true;    }     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{};        msg << "Threads:\n";        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{};        msg << "Modules:\n";         cxt.vm.enum_loaded_modules([&msg](const loaded_vm_module_t &m)        {            auto module_instance = m.module.lock();            if (module_instance == nullptr)                return;             msg << std::setw(24) <<  module_instance->module_name->str() << std::setw(0) << "    " << m.origin->str() << "\n";        });         debug_print_info(msg.str());    }     void cmd_stacktrace(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;            auto all_threads = cxt.vm.get_scheduler_control().get_all_threads();            if (a[1].compare("*") == 0)            {                thread_id = 0;            }            else            {                try                {                    thread_id = std::stoi(a[1]);                }                catch (...)                {                    debug_print_error("Invalid thread ID format");                    return;                }            }             // Find the thread(s)            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())        {            debug_print_error("Unknown thread ID");            return;        }         auto msg = std::stringstream{};        for (auto t : thread_registers)        {            msg << "Thread " << t.first << ":\n";            msg << stack_to_string(*t.second);            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)        {            debug_print_error("Must supply a target thread ID");            return;        }         uint32_t thread_id;        try        {            thread_id = std::stoi(a[1]);        }        catch (...)        {            debug_print_error("Invalid thread ID format");            return;        }         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;            }        }         debug_print_error("Unknown thread ID");    }     void cmd_disassemble(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt)    {        auto disassemble_length = 1;        if (a.size() > 1)        {            try            {                disassemble_length = std::stoi(a[1]);            }            catch (...)            {                debug_print_error("Invalid disassemble count format");                return;            }        }         const auto &r = *cxt.current_register;        const auto &code_section = r.module_ref->code_section;         auto begin_index = static_cast<std::size_t>(r.pc);        auto end_index = static_cast<std::size_t>(r.pc + 1);        if (disassemble_length < 0)        {            const auto begin_maybe = r.pc - (-disassemble_length) + 1;            begin_index = static_cast<std::size_t>(std::max(begin_maybe, 0));        }        else        {            const auto end_maybe = static_cast<std::size_t>(r.pc + disassemble_length);            end_index = std::min(end_maybe, code_section.size());        }         auto msg = std::stringstream{};        for (auto i = begin_index; i < end_index; ++i)            msg << "  " << std::setw(5) << i << ": " << code_section[i].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)        {            debug_print_error("Invalid number of arguments");            return;        }         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)            {                debug_print_error("Invalid frame pointer ");                return;            }             base_type = frame->frame_type;            base_pointer = reinterpret_cast<word_t *>(frame->base());        }        else

Lines 1–500 of 829.