967 lines · 29.2 KB · C++
RawDownload
        }         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 disassemble_length = int{ 1 };        if (a.size() > 1)        {            try            {                disassemble_length = std::stoi(a[1]);            }            catch (...)            {                throw debug_cmd_error_t{ "Invalid disassemble count format" };            }        }         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)            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;    }     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 },         { "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)", 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", "d (-?[0-9]+)?", "d, d -4, d 5", cmd_disassemble },        { "x", "Examine memory", "x [mp|fp] [0-9]+ ([0-9]+)?", "x mp 3, x fp 20 6", cmd_examine },        { "?", "Print help", nullptr, nullptr, cmd_help },         // Undocumented commands        { "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            if (c.description == nullptr)                continue;             ss << "  " << c.cmd << "\t" << c.description << "\n";             if (c.syntax != nullptr)                ss << "\t  Syntax: " << c.syntax << "\n";             if (c.example != nullptr)                ss << "\t  Example(s): " << c.example << "\n";        }         debug_print_info(ss.str());    }     // split the string using the supplied delimiter    std::vector<std::string> split(const std::string &str, const char delim = ' ')    {        auto split_string = std::vector<std::string>{};         auto curr = str.begin();        auto end = str.cend();        while (curr != end)        {            auto curr_begin = curr;            while (curr != end && *curr != delim)                ++curr;             if (curr_begin != curr)                split_string.push_back(std::move(std::string{ curr_begin, curr }));             if (curr == end)                break;             ++curr;        }         return split_string;    }     void prompt(debugger &debugger, const vm_registers_t &r)    {        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())                continue;             const auto cmd_tokens = split(cmd);            if (cmd_tokens.empty())                continue;             auto cmd_exec = find_cmd(cmd_tokens[0]);            if (cmd_exec == nullptr)            {                debug_print_error("Unknown command. '?' for help.");                continue;            }             try            {                cmd_exec(cmd_tokens, cxt);                if (cxt.exit_break)                    break;            }            catch (const debug_cmd_error_t &e)            {                debug_print_error(e.what());            }        }    }} std::ostream& operator<<(std::ostream &ss, const debugger_options o){    ss << "Break on enter: " << std::boolalpha << o.break_on_enter << "\n"       << "Break on module load: " << std::boolalpha << o.break_on_module_load << "\n"       << "Break on exception: " << std::boolalpha << o.break_on_exception << "\n";     return ss;} debugger::debugger(const debugger_options &opt)    : _options{ opt }    , _tool_id{ 0 }    , controller{ nullptr }{ } void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id){    _tool_id = tool_id;    controller = &controller_;     _event_cookies.push_back(controller->subscribe_event(vm_event_t::thread_begin, [](vm_event_t, vm_event_context_t &cxt)    {        auto t = cxt.value1.thread;        auto ss = std::stringstream{};        ss << "Thread "            << t->get_thread_id()            << " started in "            << t->get_registers().module_ref->module->module_name->str()            << " @ "            << t->get_registers().pc;         debug_print_info(ss.str());    }));     _event_cookies.push_back(controller->subscribe_event(vm_event_t::thread_end, [](vm_event_t, vm_event_context_t &cxt)    {        auto ss = std::stringstream{};        ss << "Thread "            << cxt.value1.thread->get_thread_id()            << " ended.";         debug_print_info(ss.str());    }));     _event_cookies.push_back(controller->subscribe_event(vm_event_t::exception_raised, [&](vm_event_t, vm_event_context_t &cxt)    {        auto ss = std::stringstream{};        ss << "Exception '"            << cxt.value2.str->str()            << "' thrown.";         debug_print_info(ss.str());         if (_options.break_on_exception)            prompt(*this, *cxt.value1.registers);    }));     _event_cookies.push_back(controller->subscribe_event(vm_event_t::exception_unhandled, [&](vm_event_t, vm_event_context_t &cxt)    {        auto ss = std::stringstream{};        ss << "Unhandled exception '"            << cxt.value2.str->str()            << "'";         debug_print_error(ss.str());        prompt(*this, *cxt.value1.registers);    }));     _event_cookies.push_back(controller->subscribe_event(vm_event_t::module_thread_load, [&](vm_event_t, vm_event_context_t &cxt)    {        auto m = (*cxt.value2.module);        auto ss = std::stringstream{};        ss << "Module "            << m->module_name->str()            << " loaded";         if (util::has_flag(m->header.runtime_flag, runtime_flags_t::builtin))            ss << " (built-in)";         debug_print_info(ss.str());         // If the break on enter flag was set, it will be true        // and the first module load will be the user entry module.        if (_options.break_on_enter)        {            // Reset the break on enter flag and set a breakpoint            // on the entry PC for the module - 'break on enter'            _options.break_on_enter = false;            controller->set_breakpoint(m, m->header.entry_pc);        }         if (_options.break_on_module_load)            prompt(*this, *cxt.value1.registers);    }));     _event_cookies.push_back(controller->subscribe_event(vm_event_t::breakpoint, [&](vm_event_t, vm_event_context_t &cxt)    {        auto ss = std::stringstream{};        ss << "Breakpoint hit";        if (cxt.value2.cookie != 0)            ss << " [" << cxt.value2.cookie << "]";         debug_print_info(ss.str());         auto reg_str = registers_to_string(*cxt.value1.registers);        debug_print_info(reg_str);         prompt(*this, *cxt.value1.registers);    }));} void debugger::on_unload(){    assert(controller != nullptr);     for (auto ec : _event_cookies)        controller->unsubscribe_event(ec);     controller = nullptr;}

Lines 501–967 of 967. Previous lines