663 lines · 18.9 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 exec operation    using dbg_cmd_exec_t = bool(*)(const std::vector<std::string> &, const vm_t &, const vm_registers_t &);     // 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)    {        ss << "string\n"           << "\tlength:  " << s->get_length() << "\n"           << "\tvalue:   " << s->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 *a)    {        ss << "alloc\n"           << "\tsize:  " << a->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;             auto alloc = vm_alloc_t::from_allocation(p);            if (alloc->alloc_type == intrinsic_type_desc::type<vm_string_t>())                frame_string << vm_alloc_t::from_allocation<vm_string_t>(p);            else if (alloc->alloc_type == intrinsic_type_desc::type<vm_array_t>())                frame_string << vm_alloc_t::from_allocation<vm_array_t>(p);            else if (alloc->alloc_type == intrinsic_type_desc::type<vm_list_t>())                frame_string << vm_alloc_t::from_allocation<vm_list_t>(p);            else if (alloc->alloc_type == intrinsic_type_desc::type<vm_channel_t>())                frame_string << vm_alloc_t::from_allocation<vm_channel_t>(p);            else                frame_string << alloc;        });         return frame_string.str();    }     bool cmd_continue(const std::vector<std::string> &, const vm_t &, const vm_registers_t &)    {        return false;    }     bool cmd_print_registers(const std::vector<std::string> &, const vm_t &, const vm_registers_t &r)    {        auto s = registers_to_string(r);        debug_print_info(s);         return true;    }     bool cmd_print_threads(const std::vector<std::string> &, const vm_t &vm, const vm_registers_t &)    {        auto msg = std::stringstream{};        msg << "Threads:\n";        for (auto t : vm.get_scheduler_control().get_all_threads())            msg << "  " << std::setw(5) << t->get_thread_id() << "  -  " << t->get_state() << "\n";         debug_print_info(msg.str());         return true;    }     bool cmd_print_modules(const std::vector<std::string> &, const vm_t &vm, const vm_registers_t &)    {        auto msg = std::stringstream{};        msg << "Modules:\n";        for (auto m : vm.get_loaded_modules())            msg << "  " << m->module_name->str() << "\n";         debug_print_info(msg.str());         return true;    }     bool cmd_stacktrace(const std::vector<std::string> &a, const vm_t &vm, const vm_registers_t &r)    {        auto thread_registers = std::map<uint32_t, const vm_registers_t*>{};        if (a.size() == 1)        {            thread_registers[r.thread.get_thread_id()] = &r;        }        else        {            uint32_t thread_id;            auto all_threads = 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 true;                }            }             // 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 true;        }         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());         return true;    }     // Forward declaration    void prompt(const vm_t &, const vm_registers_t &);     bool cmd_switchthread(const std::vector<std::string> &a, const vm_t &vm, const vm_registers_t &r)    {        if (a.size() == 1)        {            debug_print_error("Must supply a target thread ID");            return true;        }         uint32_t thread_id;        try        {            thread_id = std::stoi(a[1]);        }        catch (...)        {            debug_print_error("Invalid thread ID format");            return true;        }         auto all_threads = 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)            {                prompt(vm, t->get_registers());                 // If we are returning from a thread switch we are not continuing, so return false to indicate as such.                return false;            }        }         debug_print_error("Unknown thread ID");        return true;    }     bool cmd_disassemble(const std::vector<std::string> &a, const vm_t &, const vm_registers_t &r)    {        auto disassemble_length = 1;        if (a.size() > 1)        {            try            {                disassemble_length = std::stoi(a[1]);            }            catch (...)            {                debug_print_error("Invalid disassemble count format");                return true;            }        }         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());         return true;    }     // Forward declaration    void print_help();     bool cmd_help(const std::vector<std::string> &, const vm_t &, const vm_registers_t &)    {        print_help();        return true;    }     // Easter egg    bool cmd_tlk_tilde_star(const std::vector<std::string> &, const vm_t &, const vm_registers_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;         return true;    }     const dbg_cmd_t cmds[] =    {        { "c", "Continue", nullptr, nullptr, cmd_continue },        { "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 },        { "sw", "Stack walk/back trace", "bt ([0-9]+|\\*)?", "bt, bt 34, bt *", cmd_stacktrace },        { "st", "Switch to thread", "st [0-9]+", "st 42", cmd_switchthread },        { "d", "Disassemble the next/previous N instructions", "d (-?[0-9]+)?", "d, d -4, d 5", cmd_disassemble },        { "?", "Print help", nullptr, nullptr, cmd_help },        { "tlk~*", nullptr, nullptr, nullptr, cmd_tlk_tilde_star }    };     dbg_cmd_exec_t find_cmd(const std::string &cmd_maybe)    {        for (auto i = std::size_t{}; i < sizeof(cmds) / sizeof(cmds[0]); ++i)        {            if (cmd_maybe.compare(cmds[i].cmd) == 0)                return cmds[i].exec;        }         // [TODO] Didn't find the cmd, try ignoring case?        return nullptr;    }     void print_help()    {        auto ss = std::stringstream{};        ss << "Commands:\n";        for (auto i = std::size_t{}; i < sizeof(cmds) / sizeof(cmds[0]); ++i)        {            auto &c = cmds[i];             // Ignore if undocumented

Lines 1–500 of 663.