From ec2eeb10f712e45974d931388d7bad06ce2a4279 Mon Sep 17 00:00:00 2001 From: T0b1 Date: Fri, 2 Jun 2023 16:54:00 +0200 Subject: [PATCH] threads and frames --- src/backend/backend.cpp | 26 +++ src/backend/debug_backend.h | 7 + src/backend/lldb/lldb_backend.cpp | 256 +++++++++++++++++++++++++++++- src/backend/lldb/lldb_backend.h | 26 ++- src/frontend/frontend.cpp | 134 +++++++++++++++- src/frontend/frontend.h | 11 +- src/frontend/target.h | 18 +++ src/frontend/window.cpp | 129 ++++++++++++++- src/frontend/window.h | 22 ++- src/msg.h | 47 +++++- tmp/main | Bin 23320 -> 23504 bytes tmp/main.c | 6 + 12 files changed, 665 insertions(+), 17 deletions(-) diff --git a/src/backend/backend.cpp b/src/backend/backend.cpp index 14a648b..4ff47e2 100644 --- a/src/backend/backend.cpp +++ b/src/backend/backend.cpp @@ -24,4 +24,30 @@ void Backend::send_reg_change(BackToFront::RegsChanged &&info) auto msg = std::make_unique( BackToFront::MsgType::regs_changed, std::move(info)); this->send_msg(std::move(msg)); +} + +void Backend::send_thread_change(BackToFront::ThreadChange &&info) +{ + auto msg = std::make_unique( + BackToFront::MsgType::thread_changed, std::move(info)); + this->send_msg(std::move(msg)); +} +void Backend::send_thread_removed(BackToFront::ThreadRemoved &&info) +{ + auto msg = std::make_unique( + BackToFront::MsgType::thread_removed, std::move(info)); + this->send_msg(std::move(msg)); +} + +void Backend::send_frame_changed(BackToFront::FrameChanged &&info) +{ + auto msg = std::make_unique( + BackToFront::MsgType::frame_changed, std::move(info)); + this->send_msg(std::move(msg)); +} +void Backend::send_frame_removed(BackToFront::FrameRemoved &&info) +{ + auto msg = std::make_unique( + BackToFront::MsgType::frame_removed, std::move(info)); + this->send_msg(std::move(msg)); } \ No newline at end of file diff --git a/src/backend/debug_backend.h b/src/backend/debug_backend.h index 4df78c3..ffe4ecf 100644 --- a/src/backend/debug_backend.h +++ b/src/backend/debug_backend.h @@ -23,6 +23,9 @@ namespace dbgui::backend virtual bool step_over() = 0; virtual bool step_out() = 0; + virtual void cont() = 0; + virtual void pause() = 0; + auto retrieve_msg_for_frontend() -> std::optional> { @@ -48,6 +51,10 @@ namespace dbgui::backend void send_state_change(TargetState new_state, StateChangeReason reason); void send_proc_info(BackToFront::InitialProcessInfo &&); void send_reg_change(BackToFront::RegsChanged &&); + void send_thread_change(BackToFront::ThreadChange &&); + void send_thread_removed(BackToFront::ThreadRemoved &&); + void send_frame_changed(BackToFront::FrameChanged &&); + void send_frame_removed(BackToFront::FrameRemoved &&); private: std::mutex _back_front_msg_mutex; diff --git a/src/backend/lldb/lldb_backend.cpp b/src/backend/lldb/lldb_backend.cpp index 0560486..6292d48 100644 --- a/src/backend/lldb/lldb_backend.cpp +++ b/src/backend/lldb/lldb_backend.cpp @@ -112,6 +112,9 @@ void LLDBBackend::handle_state_change(lldb::StateType state) } reg_infos.clear(); + this->check_thread_changes(); + this->check_frame_changes(); + if (state == StateType::eStateStopped) { this->send_state_change(TargetState::paused, @@ -124,6 +127,8 @@ void LLDBBackend::handle_state_change(lldb::StateType state) { case eStateStopped: this->check_reg_changes(); + this->check_thread_changes(); + this->check_frame_changes(); this->send_state_change(TargetState::paused, StateChangeReason::unknown); break; case eStateRunning: @@ -259,13 +264,15 @@ void LLDBBackend::dump_threads() case eStopReasonSignal: { auto signal_num = thread.GetStopReasonDataAtIndex(0); - printf("Thread %lu: TID: %lu, Stop Reason: Signal: %lu\n", i, - thread.GetThreadID(), signal_num); + printf("Thread %lu (%s: %s): TID: %lu, Stop Reason: Signal: %lu\n", i, + thread.GetFrameAtIndex(0).GetDisplayFunctionName(), + thread.GetName(), thread.GetThreadID(), signal_num); break; } default: - printf("Thread %lu: TID: %lu, Stop Reason: %lu\n", i, - thread.GetThreadID(), stop_reason); + printf("Thread %lu (%s: %s): TID: %lu, Stop Reason: %lu\n", i, + thread.GetFrameAtIndex(0).GetDisplayFunctionName(), + thread.GetName(), thread.GetThreadID(), stop_reason); } } @@ -335,6 +342,30 @@ bool LLDBBackend::step_out() return false; } +void LLDBBackend::cont() +{ + std::lock_guard g{_data_lock}; + if (!_process) + { + return; + } + + // TODO: error handling + _process->Continue(); +} + +void LLDBBackend::pause() +{ + std::lock_guard g{_data_lock}; + if (!_process) + { + return; + } + + // TODO: error handling + _process->Stop(); +} + void LLDBBackend::check_reg_changes() { auto thread = _process->GetSelectedThread(); @@ -405,6 +436,223 @@ void LLDBBackend::check_reg_changes() } } +void LLDBBackend::check_thread_changes() +{ + using namespace lldb; + const auto len = _process->GetNumThreads(); + + if (len == 0) + { + if (!_threads.empty()) + { + for (size_t i = 0; i < _threads.size(); ++i) + { + this->send_thread_removed(BackToFront::ThreadRemoved{i}); + } + _threads.clear(); + } + return; + } + + // TODO: work with SBThread::GetIndexID instead of relying + // on the index order? + uint16_t cache_idx = 0; + for (uint32_t i = 0; i < len; ++i) + { + auto thread = _process->GetThreadAtIndex(i); + if (!thread.IsValid()) + { + continue; + } + + auto frame = thread.GetFrameAtIndex(0); + if (_threads.size() <= cache_idx) + { + _threads.push_back( + Thread{.id = thread.GetThreadID(), + .ip = frame.GetPC(), + .name = thread.GetName(), + .cur_display_fn = frame.GetDisplayFunctionName()}); + + auto &info = _threads.back(); + switch (thread.GetStopReason()) + { + case eStopReasonBreakpoint: + info.stop_reason = ThreadStopReason::breakpoint; + info.stop_extra = thread.GetStopReasonDataAtIndex(0); + break; + case eStopReasonWatchpoint: + info.stop_reason = ThreadStopReason::watchpoint; + info.stop_extra = thread.GetStopReasonDataAtIndex(0); + break; + case eStopReasonSignal: + info.stop_reason = ThreadStopReason::signal; + info.stop_extra = thread.GetStopReasonDataAtIndex(0); + break; + case eStopReasonException: + info.stop_reason = ThreadStopReason::exception; + break; + default: info.stop_reason = ThreadStopReason::unknown; break; + } + + this->send_thread_change(BackToFront::ThreadChange{ + .id = info.id, + .ip = info.ip, + .stop_extra = info.stop_extra, + .idx = cache_idx, + .stop_reason = info.stop_reason, + .name = info.name, + .cur_display_fn = info.cur_display_fn, + }); + cache_idx++; + continue; + } + + auto &cache_entry = _threads[cache_idx]; + auto change_entry = BackToFront::ThreadChange{}; + change_entry.idx = cache_idx; + auto send_change = false; + + if (thread.GetThreadID() != cache_entry.id) + { + cache_entry.id = thread.GetThreadID(); + send_change = true; + } + if (frame.GetPC() != cache_entry.ip) + { + cache_entry.ip = frame.GetPC(); + send_change = true; + } + auto name = std::string_view{thread.GetName()}; + if (name != cache_entry.name) + { + change_entry.name = name; + send_change = true; + } + auto display_name = std::string_view{frame.GetDisplayFunctionName()}; + if (display_name != cache_entry.cur_display_fn) + { + change_entry.cur_display_fn = display_name; + send_change = true; + } + + auto stop_reason = cache_entry.stop_reason; + auto stop_extra = cache_entry.stop_extra; + switch (thread.GetStopReason()) + { + case eStopReasonBreakpoint: + stop_reason = ThreadStopReason::breakpoint; + stop_extra = thread.GetStopReasonDataAtIndex(0); + break; + case eStopReasonWatchpoint: + stop_reason = ThreadStopReason::watchpoint; + stop_extra = thread.GetStopReasonDataAtIndex(0); + break; + case eStopReasonSignal: + stop_reason = ThreadStopReason::signal; + stop_extra = thread.GetStopReasonDataAtIndex(0); + break; + case eStopReasonException: + stop_reason = ThreadStopReason::exception; + break; + default: stop_reason = ThreadStopReason::unknown; break; + } + if (stop_reason != cache_entry.stop_reason + || stop_extra != cache_entry.stop_extra) + { + send_change = true; + cache_entry.stop_reason = stop_reason; + cache_entry.stop_extra = stop_extra; + } + + if (send_change) + { + change_entry.id = cache_entry.id; + change_entry.ip = cache_entry.ip; + change_entry.stop_reason = cache_entry.stop_reason; + change_entry.stop_extra = cache_entry.stop_extra; + this->send_thread_change(std::move(change_entry)); + } + + cache_idx++; + } + + if (_threads.size() > cache_idx) + { + // here cache_idx == num_threads + size_t rem_count = _threads.size() - cache_idx; + for (size_t i = 0; i < rem_count; ++i) + { + this->send_thread_removed( + BackToFront::ThreadRemoved{_threads.size() - i - 1}); + _threads.pop_back(); + } + } +} + +void LLDBBackend::check_frame_changes() +{ + using namespace lldb; + auto thread = _process->GetSelectedThread(); + if (!thread.IsValid()) + { + // TODO: cleanup + return; + } + + size_t len = thread.GetNumFrames(); + uint16_t cache_idx = 0; + size_t selected_idx = 0; + for (size_t i = 0; i < len; ++i) + { + auto frame = thread.GetFrameAtIndex(i); + if (!frame.IsValid()) + { + continue; + } + + if (thread.GetSelectedFrame().IsEqual(frame)) + { + selected_idx = cache_idx; + } + + auto ip = frame.GetPC(); + auto name = std::string_view{frame.GetDisplayFunctionName()}; + if (_frames.size() <= cache_idx) + { + _frames.push_back(Frame{ + .ip = ip, + .display_name = std::string{name}, + }); + + this->send_frame_changed(BackToFront::FrameChanged{ + .ip = ip, .idx = cache_idx, .display_name = std::string{name}}); + cache_idx++; + continue; + } + + auto &cache_entry = _frames[cache_idx]; + if (cache_entry.ip != ip || cache_entry.display_name != name) + { + this->send_frame_changed(BackToFront::FrameChanged{ + .ip = ip, .idx = cache_idx, .display_name = std::string{name}}); + } + cache_idx++; + } + + if (_frames.size() > cache_idx) + { + // here cache_idx == num_frames + size_t rem_count = _frames.size() - cache_idx; + for (size_t i = 0; i < rem_count; ++i) + { + this->send_frame_removed( + BackToFront::FrameRemoved{_frames.size() - i - 1}); + _frames.pop_back(); + } + } +} + /* Reg output for x64 Got register set General Purpose Registers diff --git a/src/backend/lldb/lldb_backend.h b/src/backend/lldb/lldb_backend.h index e48ea7f..5eb386c 100644 --- a/src/backend/lldb/lldb_backend.h +++ b/src/backend/lldb/lldb_backend.h @@ -14,7 +14,7 @@ namespace dbgui::backend { struct RegSet { - std::string set_name; + std::string set_name; // map from debugger reg idx to frontend reg idx // std::vector idx_map; std::vector names; @@ -22,6 +22,22 @@ namespace dbgui::backend std::vector> values; }; + struct Thread + { + uint64_t id; + uint64_t ip; + uint64_t stop_extra; + ThreadStopReason stop_reason; + std::string name; + std::string cur_display_fn; + }; + + struct Frame + { + uint64_t ip; + std::string display_name; + }; + // TODO: source_init_file: false LLDBBackend(std::string filename); virtual ~LLDBBackend(); @@ -32,6 +48,9 @@ namespace dbgui::backend bool step_over() override; bool step_out() override; + void cont() override; + void pause() override; + private: void run_msg_loop(); void wait_for_debug_events(); @@ -42,6 +61,8 @@ namespace dbgui::backend std::vector &); void dump_threads(); void check_reg_changes(); + void check_thread_changes(); + void check_frame_changes(); std::string _filename; lldb::SBDebugger _instance; @@ -55,5 +76,8 @@ namespace dbgui::backend bool _first_run = true; TargetState _state = TargetState::stopped; std::vector _reg_sets = {}; + std::vector _threads = {}; + std::vector _frames = {}; + uint16_t _selected_frame = 0; }; } // namespace dbgui::backend \ No newline at end of file diff --git a/src/frontend/frontend.cpp b/src/frontend/frontend.cpp index 062f2f8..719d46a 100644 --- a/src/frontend/frontend.cpp +++ b/src/frontend/frontend.cpp @@ -15,6 +15,16 @@ Target::Target(std::string filename) backend = std::make_shared(this->filename.c_str()); } +Frontend::Frontend() +{ + _open_popup_name_buf.resize(512); + const char *str = "/home/klee/projects/dbgui/tmp/main"; + std::memcpy(_open_popup_name_buf.data(), str, strlen(str) + 1); + _windows.push_back(Window::create_regs(window_id++)); + _windows.push_back(Window::create_threads(window_id++)); + _windows.push_back(Window::create_frames(window_id++)); +} + void Frontend::run_frame() { this->handle_msgs(); @@ -38,6 +48,12 @@ void Frontend::run_frame() if (ImGui::Begin("Test", nullptr, win_flags)) { + // TODO: if we just do SetNextWindowDockID in each of the windows + // this seems to crash once the first window is docked to it? + // so some id change is necessary + // so we should try to figure out what the dock id for the "topleft" + // window is or whatever and use that for new windows + // spawning them floating looks bad (and might reuse saved positions) this->dock_id = ImGui::GetID("MyDockSpace"); ImGui::DockSpace(this->dock_id, ImVec2{0, 0}, ImGuiDockNodeFlags_PassthruCentralNode); @@ -45,6 +61,7 @@ void Frontend::run_frame() for (auto &window : _windows) { window.draw(*this); + //this->dock_id = ImGui::GetWindowDockID(); } ImGui::End(); @@ -104,8 +121,19 @@ void Frontend::draw_header() break; case stopped: ImGui::Button("Run"); break; - case paused: ImGui::Button("Continue"); break; - case running: ImGui::Button("Pause"); break; + case paused: + if (ImGui::Button("Continue")) + { + this->target->backend->cont(); + // TODO: mark target already as running to prevent input? + } + break; + case running: + if (ImGui::Button("Pause")) + { + this->target->backend->pause(); + } + break; } } else { @@ -244,6 +272,22 @@ void Frontend::handle_msgs() case regs_changed: this->handle_reg_change(std::get(msg->data)); break; + case thread_changed: + this->handle_thread_change( + std::move(std::get(msg->data))); + break; + case thread_removed: + this->handle_thread_remove( + std::get(msg->data)); + break; + case frame_changed: + this->handle_frame_change( + std::move(std::get(msg->data))); + break; + case frame_removed: + this->handle_frame_remove( + std::get(msg->data)); + break; } } } @@ -290,4 +334,90 @@ void Frontend::handle_reg_change(const BackToFront::RegsChanged &info) // TODO: opt for uint64? std::copy(val.begin(), val.end(), reg.bytes.begin()); } +} + +void Frontend::handle_thread_remove(const BackToFront::ThreadRemoved &info) +{ + auto &threads = this->target->threads; + if (threads.size() >= info.idx) + { + return; + } + + threads[info.idx] = {}; +} + +void Frontend::handle_thread_change(BackToFront::ThreadChange &&info) +{ + auto &threads = this->target->threads; + if (threads.size() <= info.idx) + { + threads.resize(info.idx + 1); + } + + auto &thread = threads[info.idx]; + if (!thread) + { + thread = Target::Thread{ + .id = info.id, + .ip = info.ip, + .stop_extra = info.stop_extra, + .stop_reason = info.stop_reason, + }; + if (info.name) + { + thread->name = std::move(*info.name); + } + if (info.cur_display_fn) + { + thread->cur_display_fn = std::move(*info.cur_display_fn); + } + return; + } + + thread->id = info.id; + thread->ip = info.ip; + thread->stop_reason = info.stop_reason; + thread->stop_extra = info.stop_extra; + if (info.name) + { + thread->name = std::move(*info.name); + } + if (info.cur_display_fn) + { + thread->cur_display_fn = std::move(*info.cur_display_fn); + } +} + +void Frontend::handle_frame_remove(const BackToFront::FrameRemoved &info) +{ + auto &frames = this->target->frames; + if (frames.size() >= info.idx) + { + return; + } + + frames[info.idx] = {}; +} + +void Frontend::handle_frame_change(BackToFront::FrameChanged &&info) +{ + auto &frames = this->target->frames; + if (frames.size() <= info.idx) + { + frames.resize(info.idx + 1); + } + + auto &frame = frames[info.idx]; + if (!frame) + { + frame = Target::Frame{ + .ip = info.ip, + .display_name = info.display_name, + }; + return; + } + + frame->ip = info.ip; + frame->display_name = info.display_name; } \ No newline at end of file diff --git a/src/frontend/frontend.h b/src/frontend/frontend.h index f3e8506..e1fe92f 100644 --- a/src/frontend/frontend.h +++ b/src/frontend/frontend.h @@ -17,12 +17,7 @@ namespace dbgui::frontend struct Frontend { - Frontend() - { - _open_popup_name_buf.resize(512); - const char *str = "/home/klee/projects/dbgui/tmp/main"; - std::memcpy(_open_popup_name_buf.data(), str, strlen(str) + 1); - } + Frontend(); void run_frame(); @@ -39,6 +34,10 @@ namespace dbgui::frontend void handle_state_change(const BackToFront::StateChange &); void handle_proc_info(BackToFront::InitialProcessInfo &&); void handle_reg_change(const BackToFront::RegsChanged &); + void handle_thread_remove(const BackToFront::ThreadRemoved &); + void handle_thread_change(BackToFront::ThreadChange &&); + void handle_frame_remove(const BackToFront::FrameRemoved &); + void handle_frame_change(BackToFront::FrameChanged &&); bool _draw_second = false; diff --git a/src/frontend/target.h b/src/frontend/target.h index 8e5d39b..a291842 100644 --- a/src/frontend/target.h +++ b/src/frontend/target.h @@ -25,6 +25,22 @@ namespace dbgui::frontend std::vector regs; }; + struct Thread + { + uint64_t id; + uint64_t ip; + uint64_t stop_extra; + ThreadStopReason stop_reason; + std::string name; + std::string cur_display_fn; + }; + + struct Frame + { + uint64_t ip; + std::string display_name; + }; + Target(std::string filename); TargetState state = TargetState::stopped; @@ -33,6 +49,8 @@ namespace dbgui::frontend Arch arch; std::vector reg_sets; + std::vector> threads; + std::vector> frames; std::shared_ptr backend = nullptr; }; diff --git a/src/frontend/window.cpp b/src/frontend/window.cpp index 4d2ed93..3381bf5 100644 --- a/src/frontend/window.cpp +++ b/src/frontend/window.cpp @@ -11,6 +11,8 @@ void Window::draw(const Frontend &frontend) using enum WindowType; case regs: std::get(this->data).draw(frontend); break; + case threads: std::get(this->data).draw(frontend); break; + case frames: std::get(this->data).draw(frontend); break; default: printf("Unhandled window draw: %u\n", this->type); exit(1); } } @@ -24,9 +26,27 @@ Window Window::create_regs(size_t id) .data = RegWindow{.id = id_str, .open = true}}; } +Window Window::create_threads(size_t id) +{ + auto id_str = std::string{"Threads##"}; + id_str.append(std::to_string(id)); + + return Window{.type = WindowType::threads, + .data = ThreadWindow{.id = id_str, .open = true}}; +} + +Window Window::create_frames(size_t id) +{ + auto id_str = std::string{"Frames##"}; + id_str.append(std::to_string(id)); + + return Window{.type = WindowType::frames, + .data = FrameWindow{.id = id_str, .open = true}}; +} + void RegWindow::draw(const Frontend &frontend) { - ImGui::SetNextWindowDockID(frontend.dock_id, ImGuiCond_Appearing); + //ImGui::SetNextWindowDockID(frontend.dock_id, ImGuiCond_Appearing); if (!ImGui::Begin(this->id.c_str())) { return; @@ -97,5 +117,112 @@ void RegWindow::draw(const Frontend &frontend) ImGui::EndTable(); } + ImGui::End(); +} + +void ThreadWindow::draw(const Frontend &frontend) +{ + //ImGui::SetNextWindowDockID(frontend.dock_id, ImGuiCond_Appearing); + if (!ImGui::Begin(this->id.c_str())) + { + return; + } + + if (!frontend.target) + { + ImGui::End(); + return; + } + + if (ImGui::BeginTable("table", 3, + ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) + { + ImGui::TableSetupColumn("ID"); + ImGui::TableSetupColumn("Name"); + ImGui::TableSetupColumn("Location"); + ImGui::TableHeadersRow(); + + const auto &threads = frontend.target->threads; + char buf[32]; + for (size_t idx = 0; idx < threads.size(); ++idx) + { + const auto &thread = threads[idx]; + if (!thread) + { + continue; + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + std::snprintf(buf, sizeof(buf), "%lu", thread->id); + ImGui::Text(buf); + ImGui::TableNextColumn(); + + ImGui::Text(thread->name.c_str()); + ImGui::TableNextColumn(); + + if (thread->cur_display_fn != "") + { + ImGui::Text(thread->cur_display_fn.c_str()); + } else + { + std::snprintf(buf, sizeof(buf), "%lX", thread->ip); + ImGui::Text(buf); + } + } + + ImGui::EndTable(); + } + + ImGui::End(); +} + +void FrameWindow::draw(const Frontend &frontend) +{ + //ImGui::SetNextWindowDockID(frontend.dock_id, ImGuiCond_Appearing); + if (!ImGui::Begin(this->id.c_str())) + { + return; + } + + if (!frontend.target) + { + ImGui::End(); + return; + } + + if (ImGui::BeginTable("table", 1, + ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) + { + ImGui::TableSetupColumn("Location"); + ImGui::TableHeadersRow(); + + const auto &frames = frontend.target->frames; + char buf[32]; + for (size_t idx = 0; idx < frames.size(); ++idx) + { + const auto &frame = frames[idx]; + if (!frame) + { + continue; + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + if (frame->display_name != "") + { + ImGui::Text(frame->display_name.c_str()); + } else + { + std::snprintf(buf, sizeof(buf), "%lX", frame->ip); + ImGui::Text(buf); + } + } + + ImGui::EndTable(); + } + ImGui::End(); } \ No newline at end of file diff --git a/src/frontend/window.h b/src/frontend/window.h index 53edba0..8a9e1db 100644 --- a/src/frontend/window.h +++ b/src/frontend/window.h @@ -17,7 +17,7 @@ namespace dbgui::frontend source, memory, variables, - stack, + frames, threads, disassembly, }; @@ -55,13 +55,31 @@ namespace dbgui::frontend bool open; }; + struct ThreadWindow + { + void draw(const Frontend &); + + std::string id; + bool open; + }; + + struct FrameWindow + { + void draw(const Frontend &); + + std::string id; + bool open; + }; + struct Window { WindowType type; - std::variant data; + std::variant data; void draw(const Frontend &); static Window create_regs(size_t window_id); + static Window create_threads(size_t window_id); + static Window create_frames(size_t window_id); }; } // namespace dbgui::frontend \ No newline at end of file diff --git a/src/msg.h b/src/msg.h index 0d59678..9dff0f0 100644 --- a/src/msg.h +++ b/src/msg.h @@ -4,6 +4,7 @@ #include #include #include +#include namespace dbgui { @@ -31,6 +32,15 @@ namespace dbgui // x86 }; + enum class ThreadStopReason : uint8_t + { + unknown, + breakpoint, + watchpoint, + signal, + exception, + }; + namespace FrontToBack { enum class MsgType : uint8_t @@ -52,6 +62,12 @@ namespace dbgui ip_change, initial_proc_info, regs_changed, + // TODO: thread_added? + thread_changed, + thread_removed, + frame_changed, + frame_removed, + // TODO: frame_moved, frame_added, etc? }; struct StateChange @@ -84,11 +100,40 @@ namespace dbgui std::vector>> changes; }; + struct ThreadChange + { + uint64_t id; + uint64_t ip; + uint64_t stop_extra; + uint16_t idx; + ThreadStopReason stop_reason; + std::optional name; + std::optional cur_display_fn; + }; + + struct ThreadRemoved + { + size_t idx; + }; + + struct FrameChanged + { + uint64_t ip; + uint16_t idx; + std::string display_name; + }; + + struct FrameRemoved + { + size_t idx; + }; + struct Msg { MsgType type; std::variant + RegsChanged, ThreadChange, ThreadRemoved, FrameChanged, + FrameRemoved> data; }; } // namespace BackToFront diff --git a/tmp/main b/tmp/main index d740caeb8467e03a2464fda68ae8f8c27e30b85c..b2fdb7ccf5d407c67bd0de19d026ee10b3edf3cb 100755 GIT binary patch delta 2846 zcmcImZ){Ul6u-Caw%56}udHS5ifcQ!)jwO`x`F&L*gO8%j%_i+G1g_#VOV8xnoX!= z8gvsi0ZrIAiP-{1XZ-g?BdJjci9m=t3<)EC!Y>XC$rPe2@?pUAdG34n7BY+9Jjs3M zp7T5Ro_p>+_uM!B6`lT?j(WpPWrUOYB}8pEMB~HADpHI{fRvBL#tPLu6H@cWeu~)p zs44Y=6yA^9V8QCnCp-Ildk5ftTa~pwvH~{PDxy~5m=X)|VPC{f(q(;iLagLI zq5E>|9gN9BG#Ih1Dq_qS#{F0`M{KtW530eSzTyXs5WVUwew_h^JudIymcP#Ex(-3r z|75ib0nbhZuBRf-!0X05e>%%=p1v)P9#zpnVlDsfDmltc5^tQ`Wk10c`b=u{bn3P- z_wXdN8mR#SfAK?HMnoj)x^H^%=a4? z3eoC{;{-pF+1bB!eeZJ%bnl)8lD=yAJ#!j?jMw#{<6zKT#ZSqGO}(b;1IX);Cy@6b z??%3KPS?jF8VvW&{GsdBs398)RKBHk*cMPH(vVbg2-`U7g2;(OeazwAqnxgZ+aR)w_@FHkh;|E< zpDibuyna(8ir{5}6PS!>l)?E(fHuKgB-|u3WH!bO8;h{?3hox1Cj+NalaYBFGkQ`# z1tCqN)H(MtPvf%n0?(LmM<%X}MshTdKLX!(Dki^l%X#wihR%3SjzaX{`==JLj8 zsXZ$~GTv)-kQAJXd;G|SnK&P^oOdh^!eGw8<@QV5xVZ)%O7QWE@sG3P+j8QUviL~C zgX>^0k*LkFj(VQ4=qCjibI#Kxa0afXT3Va~AoV$pej zj&>H}AlD$&?D6}Etl~U;PSc1}g4Y?^nmx-LL>3kk#Y&H*kCGBBA@ExB2I~?s1!tOT zeI6noHfh+zYb0@jv!#@lLbyfqdx^rBgbjWGZB*hVMKIXn@jHpU-_q!?z!jKmsl}3h zuBFtEg*0f+VmLt4)wp#kzFR8LR=)LeYh;=I01cO5$PA*bp7`yMrI&eu^H+&egYhNg zFVwp%Uc%p%JIRg{8xL!NpZ6?9nFFn5c`oBwW$~7#b?cw1kMO7U)6MTGFZA|riwyMd zf{jm{%Xr&+DD8ovEt-po!pKAvO)wTOfwNndx%LZWzd#hYf%fAIv@MIILneR^^-8s5u=CULPr4HHnpC=SDYmO1p7Qk_;^mT z4lrThqj0mM-1iwmcbdr)?S|;qFwH=6Ym6R;iLC)z1IO9}p)r&3>}g)P~?#*UG|+n@y>P!kYH-e*n-a B!P@`; delta 2427 zcmcIlZ%iCT6rb5!uDA4nTi`h86?d+XmdHE=m*J99<6`pvhQ zdHde)_h#PAyq#Tqi=KFsrfZu_pG}xJJt3d&;U@2F5+!Ab)RV25_Dt!#>Ok{q=CiQY zNFA#YbZR|uQbb8je2#;W+}NUF$Byo@`HR#aiBT4XmT?NQSX@lR#YCLcX~7p7&eA2V zfsaf`13atTv+YJRUKEiRDU`Y%uWm}e;=cOR>l2Y@`o1{$)!NiM%EL$Cq-zWgCsepa zt)|VOs{!az8{vRj7bJw@Q-w2$1(G*1$1zcb)Wdl-nmeR-Z$9QT!S)63bL%$A`@tgQ z$BA%-@f1(*hl-niTexnf=RLMfVz;w?s%oB5Vn@zRX=j-u|K&t_Vd6Tu!m!D^oIm}& z2j2FDs0S|lg4HcJ1=(LU7@NKN&B1&=|3OK>f>PZqpFQ+PYP(CDeHg>3j?xB9eCRw(PT|`!E_|34T@X_nFSZx8Z?=Q39D^E zEh%s}w5a|TZv8KGL+t-d?h@d$Mrt7w_jBG{ytS^Jcs7joKJs)@ z4$JXCFhJOqx?Vo!qj*U_g!}LSDibR-|s>C|q>9<0ymGghA&(A8w~v z7#KESC1HlniYP@yMa4!&O~-LRWD^FYht-;Lbl~A~kr#V34A+LYZ}|kB71$&!i^Aq` z=n6tCD+$Ygy!`j4A+^t-*C4a6oi4)?zT@RQY=qV@hOJ1(5qXSl=tNN=l`uko;T!w0 zNR})abv=u^ATtu6D$I>EwRmw*_M +#include int main(int argc, char* argv[]) { + int tmp = 10; + while (tmp != 0) { + sleep(1); + tmp--; + } return 0; }