From aa80d690d982e35126f1e44b38981ae848317d88 Mon Sep 17 00:00:00 2001 From: T0b1 Date: Sun, 12 May 2024 03:23:51 +0200 Subject: [PATCH] start work on new watch syntax --- CMakeLists.txt | 6 +- src/frontend/window.cpp | 694 --------------------- src/frontend/window.h | 42 +- src/frontend/window/watch_window.cpp | 896 +++++++++++++++++++++++++++ src/frontend/window/watch_window.h | 75 +++ 5 files changed, 978 insertions(+), 735 deletions(-) create mode 100644 src/frontend/window/watch_window.cpp create mode 100644 src/frontend/window/watch_window.h diff --git a/CMakeLists.txt b/CMakeLists.txt index aeca09d..e5cd046 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,10 +33,14 @@ set(DBGUI_HEADERS src/frontend/frontend.h src/frontend/target.h src/frontend/window.h + src/frontend/window/watch_window.h src/msg.h) set(DBGUI_SOURCES - src/main.cpp src/frontend/frontend.cpp src/frontend/window.cpp + src/main.cpp src/frontend/frontend.cpp + src/frontend/window.cpp + src/frontend/window/watch_window.cpp + src/backend/backend.cpp src/backend/lldb/lldb_backend.cpp src/data.cpp diff --git a/src/frontend/window.cpp b/src/frontend/window.cpp index ac6f137..a2e7191 100644 --- a/src/frontend/window.cpp +++ b/src/frontend/window.cpp @@ -982,700 +982,6 @@ bool SourceWindow::draw(Frontend &frontend) return false; } -bool WatchWindow::draw(Frontend &frontend) -{ - if (!ImGui::Begin(this->id.c_str())) - { - ImGui::End(); - return false; - } - - if (!frontend.target) - { - this->first = true; - ImGui::End(); - return false; - } - - if (frontend.target->state == TargetState::stopped - || frontend.target->state == TargetState::startup) - { - ImGui::End(); - return false; - } - - // TODO: we need to clean up the initializations/sync of the DAG somehow - if (first) - { - this->locals_src_id = frontend.target->data_node_id++; - using namespace data::source; - frontend.target->backend->add_data_node(Node{.id = this->locals_src_id, - .type = Node::Type::locals, - .data = std::monostate{}}); - - first = false; - } - - auto expr_path_vec = std::vector{}; - - // ImGuiTableFlags_SizingFixedFit - if (ImGui::BeginTable("##Variables", 2, - ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable)) - { - ImGui::TableSetupColumn("Name"); - ImGui::TableSetupColumn("Value"); - ImGui::TableHeadersRow(); - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::PushID("TTTTTT"); - if (ImGui::TreeNodeEx("Locals", ImGuiTreeNodeFlags_SpanFullWidth - | ImGuiTreeNodeFlags_DefaultOpen)) - { - auto locals_idx = - frontend.target->data_idx_for_src_id(this->locals_src_id); - if (locals_idx) - { - const auto &local_node = *frontend.target->data_res_nodes[*locals_idx]; - const auto &locals_data = local_node.vec_data(); - size_t cur_off = 0; - size_t cur_idx = 0; - while (cur_off < locals_data.size()) - { - if (cur_off + 2 > locals_data.size()) - { - break; - } - auto str_len = - *reinterpret_cast(locals_data.data() + cur_off); - if (cur_off + 2 + str_len > locals_data.size()) - { - break; - } - - auto name = std::string_view{ - reinterpret_cast(locals_data.data() + cur_off + 2), - str_len}; - auto node_idx = local_node.children[cur_idx]; - expr_path_vec.clear(); - this->draw_value(frontend, - frontend.target->data_res_nodes[node_idx]->type_id, - name, node_idx, 0, expr_path_vec); - - cur_idx += 1; - cur_off += 2 + str_len; - } - } - ImGui::TreePop(); - } - - ImGui::PopID(); - - for (size_t i = 0; i < extra_slots.size(); ++i) - { - auto res_idx = frontend.target->data_idx_for_src_id(extra_slots[i].id); - auto no_success = true; - if (res_idx) - { - const auto &node = *frontend.target->data_res_nodes[*res_idx]; - if (node.success && node.children.size() == 1) - { - no_success = false; - const auto &child_node = - *frontend.target->data_res_nodes[node.children[0]]; - expr_path_vec.clear(); - this->draw_value(frontend, child_node.type_id, &extra_slots[i], - node.children[0], 0, expr_path_vec); - - if (extra_slots[i].bak == "") - { - frontend.target->backend->remove_data_node(extra_slots[i].id); - extra_slots.erase(extra_slots.begin() + i); - --i; - continue; - } - } - } - if (no_success) - { - auto &slot = extra_slots[i]; - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - - auto is_editing = false; - if (slot.is_editing) - { - is_editing = true; - if (slot.edit_was_started) - { - slot.edit_was_started = false; - ImGui::SetKeyboardFocusHere(); - } - if (ImGui::InputText("##dddd", slot.buf, sizeof(slot.buf), - ImGuiInputTextFlags_AutoSelectAll - | ImGuiInputTextFlags_EnterReturnsTrue) - && ImGui::IsItemDeactivatedAfterEdit()) - { - if (slot.buf[0] != '\0') - { - frontend.target->backend->remove_data_node(slot.id); - - using namespace data::source; - frontend.target->backend->add_data_node( - Node{.id = slot.id, - .type = Node::Type::source, - .data = Source{.type = Source::Type::variable, - .data = Source::Variable{ - .expr_path = slot.buf, - .address_of_or_pointer = false}}}); - } - - slot.bak = slot.buf; - slot.is_editing = false; - } - if (ImGui::IsItemDeactivated()) - { - memcpy(slot.buf, slot.bak.data(), slot.bak.size()); - slot.buf[slot.bak.size()] = '\0'; - slot.is_editing = false; - } - } - - if (!is_editing) - { - char tree_id_buf[128]; - std::snprintf(tree_id_buf, sizeof(tree_id_buf), "slot%lu", i); - // TODO: better id - ImGui::TreeNodeEx(tree_id_buf, - ImGuiTreeNodeFlags_SpanFullWidth - | ImGuiTreeNodeFlags_Leaf - | ImGuiTreeNodeFlags_NoTreePushOnOpen, - "%s", slot.bak.c_str()); - if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) - && ImGui::IsItemHovered()) - { - slot.is_editing = true; - slot.edit_was_started = true; - } - } - ImGui::TableNextColumn(); - ImGui::TextDisabled(""); - } - } - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - - if (ImGui::InputText("##EXTRASLOT", this->add_slot_buf, - sizeof(this->add_slot_buf), - ImGuiInputTextFlags_AutoSelectAll - | ImGuiInputTextFlags_EnterReturnsTrue) - && ImGui::IsItemDeactivatedAfterEdit()) - { - if (this->add_slot_buf[0] != '\0') - { - auto id = frontend.target->data_node_id++; - const auto *expr_path_buf = this->add_slot_buf; - bool address_of_or_pointer = false; - if (expr_path_buf[0] == '+') - { - address_of_or_pointer = true; - ++expr_path_buf; - } - - using namespace data::source; - frontend.target->backend->add_data_node(Node{ - .id = id, - .type = Node::Type::source, - .data = Source{.type = Source::Type::variable, - .data = Source::Variable{.expr_path = expr_path_buf, - .address_of_or_pointer = - address_of_or_pointer}}}); - extra_slots.push_back(ExtraSlot{.id = id, .bak = this->add_slot_buf}); - memcpy(extra_slots.back().buf, this->add_slot_buf, - sizeof(this->add_slot_buf)); - this->add_slot_buf[0] = '\0'; - } - } - - ImGui::EndTable(); - } - - ImGui::End(); - - return false; -} - -void WatchWindow::draw_value(Frontend &frontend, - data::type_info::TypeID type_id, - std::variant name, - data::result::NodeIdx node_idx, size_t off, - std::vector &expr_path) -{ - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - const char *name_begin, *name_end; - if (name.index() == 0) - { - name_begin = std::get(name).begin(); - name_end = std::get(name).end(); - } else - { - name_begin = &*std::get(name)->bak.begin(); - name_end = &*std::get(name)->bak.end(); - } - - const auto &node_opt = frontend.target->data_res_nodes[node_idx]; - if (!node_opt || !node_opt->success) - { - ImGui::TextUnformatted(name_begin, name_end); - ImGui::TableNextColumn(); - ImGui::TextDisabled(""); - return; - } - - const auto &node = *node_opt; - using namespace data::type_info; - while (type_id.type == Type::alias) - { - type_id = - std::get(frontend.target->types[type_id.idx].member_types); - } - - auto tree_open = false; - auto pop_id = false; - if (type_id.type == Type::complex || type_id.type == Type::array) - { - ImGui::PushID(name_begin, name_end); - pop_id = true; - - char tree_id_buf[128]; - std::snprintf(tree_id_buf, sizeof(tree_id_buf), "%u#off%lu", node_idx, off); - auto is_editing = false; - if (name.index() == 1) - { - auto *slot = std::get(name); - if (slot->is_editing) - { - is_editing = true; - ImGui::BeginDisabled(); - tree_open = ImGui::TreeNodeEx(tree_id_buf, 0, ""); - ImGui::EndDisabled(); - ImGui::SameLine(); - if (slot->edit_was_started) - { - slot->edit_was_started = false; - ImGui::SetKeyboardFocusHere(); - } - if (ImGui::InputText("##dddd", slot->buf, sizeof(slot->buf), - ImGuiInputTextFlags_AutoSelectAll - | ImGuiInputTextFlags_EnterReturnsTrue) - && ImGui::IsItemDeactivatedAfterEdit()) - { - if (slot->buf[0] != '\0') - { - frontend.target->backend->remove_data_node(slot->id); - using namespace data::source; - frontend.target->backend->add_data_node( - Node{.id = slot->id, - .type = Node::Type::source, - .data = - Source{.type = Source::Type::variable, - .data = Source::Variable{.expr_path = slot->buf}}}); - } - - slot->bak = slot->buf; - slot->is_editing = false; - } - if (ImGui::IsItemDeactivated()) - { - slot->is_editing = false; - } - } - } - - if (!is_editing) - { - tree_open = ImGui::TreeNodeEx( - tree_id_buf, - ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanAvailWidth, - "%.*s", static_cast(name_end - name_begin), name_begin); - if (name.index() == 1 - && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) - && ImGui::IsItemHovered()) - { - auto *slot = std::get(name); - slot->is_editing = true; - slot->edit_was_started = true; - } - } - - if (tree_open) - { - if (type_id.type == Type::complex) - { - char tree_id_buf[128]; - const auto &members = frontend.target->types[type_id.idx].member_vec(); - for (size_t i = 0; i < members.size(); ++i) - { - const auto &member = members[i]; - if (member.bitfield_size) - { - std::snprintf(tree_id_buf, sizeof(tree_id_buf), "%u#member%lu", - node_idx, i); - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::TreeNodeEx(tree_id_buf, - ImGuiTreeNodeFlags_SpanFullWidth - | ImGuiTreeNodeFlags_Leaf - | ImGuiTreeNodeFlags_NoTreePushOnOpen, - "%s", member.name.c_str()); - ImGui::TableNextColumn(); - - auto member_type = member.type_id; - while (member_type.type == Type::alias) - { - member_type = std::get( - frontend.target->types[member_type.idx].member_types); - } - - // TODO: handle u128/i128 - assert(member_type.type != Type::i128 - && member_type.type != Type::u128); - assert(member_type.is_integral() - || member_type.type == Type::_enum); - assert(member.bitfield_size <= 8 * 8); - auto byte_off = member.offset / 8; - auto bit_off = member.offset % 8; - auto byte_size = (member.bitfield_size + 7) / 8; - uint64_t value = 0; - uint8_t arr[8] = {}; - const auto &data = node.vec_data(); - std::copy(data.begin() + off + byte_off, - data.begin() + off + byte_off + byte_size, arr); - value = *reinterpret_cast(arr); - - assert(bit_off + member.bitfield_size <= 64); - value = value >> bit_off; - value = - value & (0xFFFFFFFF'FFFFFFFF >> (64 - member.bitfield_size)); - - if (member_type.type == Type::_enum) - { - const auto &members = - frontend.target->types[type_id.idx].member_vec(); - auto printed = false; - for (const auto &member : members) - { - if (value == member.enum_val) - { - ImGui::Text("%s", member.name.c_str()); - printed = true; - break; - } - } - if (!printed) - { - ImGui::Text("", value); - } - } else - { - ImGui::Text("%ld", value); - } - - continue; - } - - expr_path.push_back(ExprPathPart{ - .ident = std::string_view{name_begin, static_cast( - name_end - name_begin)}, - .deref = false}); - this->draw_value(frontend, member.type_id, member.name, node_idx, - off + member.offset, expr_path); - expr_path.pop_back(); - } - } else - { - // array - auto member_ty_id = - std::get(frontend.target->types[type_id.idx].member_types); - auto member_size = member_ty_id.byte_size(frontend.target->types); - size_t el_count = - frontend.target->types[type_id.idx].byte_size / member_size; - - expr_path.push_back(ExprPathPart{ - .ident = std::string_view{name_begin, - static_cast(name_end - name_begin)}, - .array = true}); - - char buf[32]; - size_t member_off = 0; - for (size_t i = 0; i < el_count; ++i) - { - std::snprintf(buf, sizeof(buf), "[%lu]", i); - auto expr_path_size = expr_path.size(); - expr_path.back().array_idx = i; - this->draw_value(frontend, member_ty_id, buf, node_idx, - off + member_off, expr_path); - assert(expr_path.size() == expr_path_size); - member_off += member_size; - } - - expr_path.pop_back(); - } - - ImGui::TreePop(); - } - - ImGui::PopID(); - } else - { - auto is_editing = false; - if (name.index() == 1) - { - auto *slot = std::get(name); - if (slot->is_editing) - { - is_editing = true; - if (slot->edit_was_started) - { - slot->edit_was_started = false; - ImGui::SetKeyboardFocusHere(); - } - if (ImGui::InputText("##dddd", slot->buf, sizeof(slot->buf), - ImGuiInputTextFlags_AutoSelectAll - | ImGuiInputTextFlags_EnterReturnsTrue) - && ImGui::IsItemDeactivatedAfterEdit()) - { - if (slot->buf[0] != '\0') - { - frontend.target->backend->remove_data_node(slot->id); - using namespace data::source; - frontend.target->backend->add_data_node( - Node{.id = slot->id, - .type = Node::Type::source, - .data = - Source{.type = Source::Type::variable, - .data = Source::Variable{.expr_path = slot->buf}}}); - } - - slot->bak = slot->buf; - slot->is_editing = false; - } - if (ImGui::IsItemDeactivated()) - { - slot->is_editing = false; - } - } - } - - auto tree_open = false; - if (!is_editing) - { - char tree_id_buf[128]; - std::snprintf(tree_id_buf, sizeof(tree_id_buf), "%u#off%lu", node_idx, - off); - ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_SpanFullWidth; - if (type_id.type != Type::ptr) - { - flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; - } - - // TODO: better id - tree_open = - ImGui::TreeNodeEx(tree_id_buf, flags, "%.*s", - static_cast(name_end - name_begin), name_begin); - //ImGui::TextUnformatted(name_begin, name_end); - if (name.index() == 1 - && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) - && ImGui::IsItemHovered()) - { - auto *slot = std::get(name); - slot->is_editing = true; - slot->edit_was_started = true; - } - } - - ImGui::TableSetColumnIndex(1); - switch (type_id.type) - { - using enum Type; - case i8: - { - auto val = node.get_primitive(off); - if (std::isprint(val)) - { - ImGui::Text("%c", val); - } else - { - ImGui::Text("%d", val); - } - break; - } - - case u8: ImGui::Text("%lu", node.get_primitive(off)); break; - case u16: ImGui::Text("%lu", node.get_primitive(off)); break; - case u32: ImGui::Text("%lu", node.get_primitive(off)); break; - case u64: ImGui::Text("%lu", node.get_primitive(off)); break; - case i16: ImGui::Text("%ld", node.get_primitive(off)); break; - case i32: ImGui::Text("%ld", node.get_primitive(off)); break; - case i64: ImGui::Text("%ld", node.get_primitive(off)); break; - case _bool: - { - auto val = node.get_primitive(off); - if (val) - { - ImGui::TextUnformatted("true"); - } else - { - ImGui::TextUnformatted("false"); - } - break; - } - // TODO - case f128: - case i128: - case u128: - { - const auto &data = node.vec_data(); - const auto lo = node.get_primitive(off); - const auto hi = node.get_primitive(off + 8); - ImGui::Text("%16lX-%16lX", hi, lo); - break; - } - case f32: - { - ImGui::Text("%f", node.get_primitive(off)); - break; - } - case f64: - { - ImGui::Text("%f", node.get_primitive(off)); - break; - } - case ptr: - { - // TODO: ptr size - ImGui::Text("%lX", node.get_primitive(off)); - if (tree_open) - { - // TODO: make this func be able to not print the "outer" line - // and just accept a bool to only draw the containing value - std::string path{}; - if (type_id.sub_type != Type::i8) - { - // TODO: better check if this is really a string - path = "*"; - } - // why cant i just path name_begin, name_end to string_view? :[ - construct_expr_path( - path, expr_path, - std::string_view{name_begin, - static_cast(name_end - name_begin)}); - - auto src_id = frontend.target->find_or_create_expr_path( - std::move(path), type_id.sub_type == Type::i8); - auto res_idx = frontend.target->data_idx_for_src_id(src_id); - if (res_idx) - { - const auto &node = *frontend.target->data_res_nodes[*res_idx]; - if (type_id.sub_type == Type::i8 && node.success) - { - // TODO: make span whole row (see https://github.com/ocornut/imgui/issues/3565) - assert(node.type_id.type == Type::custom); - assert(node.vec_data().back() == '\0'); - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(1); - ImGui::TextWrapped("\"%s\"", node.vec_data().data()); - } else if (node.success && node.children.size() == 1) - { - auto data_idx = node.children[0]; - const auto &data_node = - *frontend.target->data_res_nodes[data_idx]; - expr_path.push_back(ExprPathPart{ - .ident = std::string_view{name_begin, static_cast( - name_end - name_begin)}, - .deref = true}); - - this->draw_value(frontend, data_node.type_id, - std::string_view{""}, data_idx, 0, - expr_path); - - expr_path.pop_back(); - } - } - - ImGui::TreePop(); - } - break; - } - case _enum: - { - // TODO: robustness - uint64_t val; - const auto byte_size = frontend.target->types[type_id.idx].byte_size; - if (byte_size == 8) - { - val = node.get_primitive(off); - } else if (byte_size == 4) - { - val = node.get_primitive(off); - } else if (byte_size == 2) - { - val = node.get_primitive(off); - } else if (byte_size == 1) - { - val = node.get_primitive(off); - } else - { - assert(0); - val = 0; - } - const auto &members = frontend.target->types[type_id.idx].member_vec(); - auto printed = false; - for (const auto &member : members) - { - if (val == member.enum_val) - { - ImGui::Text("%s", member.name.c_str()); - printed = true; - break; - } - } - if (!printed) - { - ImGui::Text("", val); - } - break; - } - } - } -} - -void WatchWindow::construct_expr_path(std::string &out, - const std::vector &path, - std::string_view tail) -{ - for (const auto &entry : path) - { - // FIXME: BIGGEST HACK in cinema history - if (entry.ident == "") - { - continue; - } - - out += entry.ident; - if (entry.deref) - { - out += "->"; - } else if (!entry.array) - { - out += '.'; - } - } - out += tail; -} - void Window::handle_source_updated(Target &target, size_t id) { switch (this->type) diff --git a/src/frontend/window.h b/src/frontend/window.h index f604bc6..980ab59 100644 --- a/src/frontend/window.h +++ b/src/frontend/window.h @@ -7,6 +7,8 @@ #include "msg.h" #include "frontend/target.h" +#include "window/watch_window.h" + namespace dbgui::frontend { struct Frontend; @@ -124,46 +126,6 @@ namespace dbgui::frontend std::vector lines; }; - struct WatchWindow - { - struct ExtraSlot - { - size_t id; - char buf[256]; - std::string bak; - bool is_editing = false; - bool edit_was_started = false; - }; - - // TODO: cache the expr_path by node_idx + off or smth? - struct ExprPathPart - { - std::string_view ident; - bool deref = false; - bool array = false; - uint64_t array_idx = 0; - }; - - bool draw(Frontend &); - void draw_value(Frontend &, data::type_info::TypeID, - std::variant name, - data::result::NodeIdx node_idx, size_t off, - std::vector &expr_path); - // void handle_source_updated(Target& target, size_t id); - void construct_expr_path(std::string &out, - const std::vector &, - std::string_view tail); - - std::string id; - bool open; - bool first; - - size_t locals_src_id; - - std::vector extra_slots; - char add_slot_buf[256] = {}; - }; - struct Window { WindowType type; diff --git a/src/frontend/window/watch_window.cpp b/src/frontend/window/watch_window.cpp new file mode 100644 index 0000000..125b322 --- /dev/null +++ b/src/frontend/window/watch_window.cpp @@ -0,0 +1,896 @@ +#include "watch_window.h" +#include "frontend/frontend.h" +#include +#include "imgui_internal.h" +#include +#include + +using namespace dbgui; +using namespace dbgui::frontend; + +bool WatchWindow::draw(Frontend &frontend) +{ + if (!ImGui::Begin(this->id.c_str())) + { + ImGui::End(); + return false; + } + + if (!frontend.target) + { + this->first = true; + ImGui::End(); + return false; + } + + if (frontend.target->state == TargetState::stopped + || frontend.target->state == TargetState::startup) + { + ImGui::End(); + return false; + } + + // TODO: we need to clean up the initializations/sync of the DAG somehow + if (first) + { + this->locals_src_id = frontend.target->data_node_id++; + using namespace data::source; + frontend.target->backend->add_data_node(Node{.id = this->locals_src_id, + .type = Node::Type::locals, + .data = std::monostate{}}); + + first = false; + } + + auto expr_path_vec = std::vector{}; + + // ImGuiTableFlags_SizingFixedFit + if (ImGui::BeginTable("##Variables", 2, + ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable)) + { + ImGui::TableSetupColumn("Name"); + ImGui::TableSetupColumn("Value"); + ImGui::TableHeadersRow(); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::PushID("TTTTTT"); + if (ImGui::TreeNodeEx("Locals", ImGuiTreeNodeFlags_SpanFullWidth + | ImGuiTreeNodeFlags_DefaultOpen)) + { + auto locals_idx = + frontend.target->data_idx_for_src_id(this->locals_src_id); + if (locals_idx) + { + const auto &local_node = *frontend.target->data_res_nodes[*locals_idx]; + const auto &locals_data = local_node.vec_data(); + size_t cur_off = 0; + size_t cur_idx = 0; + while (cur_off < locals_data.size()) + { + if (cur_off + 2 > locals_data.size()) + { + break; + } + auto str_len = + *reinterpret_cast(locals_data.data() + cur_off); + if (cur_off + 2 + str_len > locals_data.size()) + { + break; + } + + auto name = std::string_view{ + reinterpret_cast(locals_data.data() + cur_off + 2), + str_len}; + auto node_idx = local_node.children[cur_idx]; + expr_path_vec.clear(); + this->draw_value(frontend, + frontend.target->data_res_nodes[node_idx]->type_id, + name, node_idx, 0, expr_path_vec); + + cur_idx += 1; + cur_off += 2 + str_len; + } + } + ImGui::TreePop(); + } + + ImGui::PopID(); + + for (size_t i = 0; i < extra_slots.size(); ++i) + { + auto res_idx = frontend.target->data_idx_for_src_id(extra_slots[i].id); + auto no_success = true; + if (res_idx) + { + const auto &node = *frontend.target->data_res_nodes[*res_idx]; + if (node.success && node.children.size() == 1) + { + no_success = false; + const auto &child_node = + *frontend.target->data_res_nodes[node.children[0]]; + expr_path_vec.clear(); + this->draw_value(frontend, child_node.type_id, &extra_slots[i], + node.children[0], 0, expr_path_vec); + + if (extra_slots[i].bak == "") + { + frontend.target->backend->remove_data_node(extra_slots[i].id); + extra_slots.erase(extra_slots.begin() + i); + --i; + continue; + } + } + } + if (no_success) + { + auto &slot = extra_slots[i]; + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + auto is_editing = false; + if (slot.is_editing) + { + is_editing = true; + if (slot.edit_was_started) + { + slot.edit_was_started = false; + ImGui::SetKeyboardFocusHere(); + } + if (ImGui::InputText("##dddd", slot.buf, sizeof(slot.buf), + ImGuiInputTextFlags_AutoSelectAll + | ImGuiInputTextFlags_EnterReturnsTrue) + && ImGui::IsItemDeactivatedAfterEdit()) + { + if (slot.buf[0] != '\0') + { + frontend.target->backend->remove_data_node(slot.id); + + create_node_for_extra_slot(frontend, i); + } + + slot.bak = slot.buf; + slot.is_editing = false; + } + if (ImGui::IsItemDeactivated()) + { + memcpy(slot.buf, slot.bak.data(), slot.bak.size()); + slot.buf[slot.bak.size()] = '\0'; + slot.is_editing = false; + } + } + + if (!is_editing) + { + char tree_id_buf[128]; + std::snprintf(tree_id_buf, sizeof(tree_id_buf), "slot%lu", i); + // TODO: better id + ImGui::TreeNodeEx(tree_id_buf, + ImGuiTreeNodeFlags_SpanFullWidth + | ImGuiTreeNodeFlags_Leaf + | ImGuiTreeNodeFlags_NoTreePushOnOpen, + "%s", slot.bak.c_str()); + if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) + && ImGui::IsItemHovered()) + { + slot.is_editing = true; + slot.edit_was_started = true; + } + } + ImGui::TableNextColumn(); + ImGui::TextDisabled(""); + } + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + if (ImGui::InputText("##EXTRASLOT", this->add_slot_buf, + sizeof(this->add_slot_buf), + ImGuiInputTextFlags_AutoSelectAll + | ImGuiInputTextFlags_EnterReturnsTrue) + && ImGui::IsItemDeactivatedAfterEdit()) + { + if (this->add_slot_buf[0] != '\0') + { + auto id = frontend.target->data_node_id++; + + extra_slots.push_back(ExtraSlot{.id = id, .bak = this->add_slot_buf}); + memcpy(extra_slots.back().buf, this->add_slot_buf, + sizeof(this->add_slot_buf)); + this->add_slot_buf[0] = '\0'; + + create_node_for_extra_slot(frontend, extra_slots.size() - 1); + } + } + + ImGui::EndTable(); + } + + ImGui::End(); + + return false; +} + +void WatchWindow::draw_value(Frontend &frontend, + data::type_info::TypeID type_id, + std::variant name, + data::result::NodeIdx node_idx, size_t off, + std::vector &expr_path) +{ + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + const char *name_begin, *name_end; + if (name.index() == 0) + { + name_begin = std::get(name).begin(); + name_end = std::get(name).end(); + } else + { + name_begin = &*std::get(name)->bak.begin(); + name_end = &*std::get(name)->bak.end(); + } + + const auto &node_opt = frontend.target->data_res_nodes[node_idx]; + if (!node_opt || !node_opt->success) + { + ImGui::TextUnformatted(name_begin, name_end); + ImGui::TableNextColumn(); + ImGui::TextDisabled(""); + return; + } + + const auto &node = *node_opt; + using namespace data::type_info; + while (type_id.type == Type::alias) + { + type_id = + std::get(frontend.target->types[type_id.idx].member_types); + } + + auto tree_open = false; + auto pop_id = false; + if (type_id.type == Type::complex || type_id.type == Type::array) + { + ImGui::PushID(name_begin, name_end); + pop_id = true; + + char tree_id_buf[128]; + std::snprintf(tree_id_buf, sizeof(tree_id_buf), "%u#off%lu", node_idx, off); + auto is_editing = false; + if (name.index() == 1) + { + auto *slot = std::get(name); + if (slot->is_editing) + { + is_editing = true; + ImGui::BeginDisabled(); + tree_open = ImGui::TreeNodeEx(tree_id_buf, 0, ""); + ImGui::EndDisabled(); + ImGui::SameLine(); + if (slot->edit_was_started) + { + slot->edit_was_started = false; + ImGui::SetKeyboardFocusHere(); + } + if (ImGui::InputText("##dddd", slot->buf, sizeof(slot->buf), + ImGuiInputTextFlags_AutoSelectAll + | ImGuiInputTextFlags_EnterReturnsTrue) + && ImGui::IsItemDeactivatedAfterEdit()) + { + if (slot->buf[0] != '\0') + { + frontend.target->backend->remove_data_node(slot->id); + + const auto slot_idx = std::distance(extra_slots.data(), slot); + create_node_for_extra_slot(frontend, slot_idx); + } + + slot->bak = slot->buf; + slot->is_editing = false; + } + if (ImGui::IsItemDeactivated()) + { + slot->is_editing = false; + } + } + } + + if (!is_editing) + { + tree_open = ImGui::TreeNodeEx( + tree_id_buf, + ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanAvailWidth, + "%.*s", static_cast(name_end - name_begin), name_begin); + if (name.index() == 1 + && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) + && ImGui::IsItemHovered()) + { + auto *slot = std::get(name); + slot->is_editing = true; + slot->edit_was_started = true; + } + } + + if (tree_open) + { + if (type_id.type == Type::complex) + { + char tree_id_buf[128]; + const auto &members = frontend.target->types[type_id.idx].member_vec(); + for (size_t i = 0; i < members.size(); ++i) + { + const auto &member = members[i]; + if (member.bitfield_size) + { + std::snprintf(tree_id_buf, sizeof(tree_id_buf), "%u#member%lu", + node_idx, i); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TreeNodeEx(tree_id_buf, + ImGuiTreeNodeFlags_SpanFullWidth + | ImGuiTreeNodeFlags_Leaf + | ImGuiTreeNodeFlags_NoTreePushOnOpen, + "%s", member.name.c_str()); + ImGui::TableNextColumn(); + + auto member_type = member.type_id; + while (member_type.type == Type::alias) + { + member_type = std::get( + frontend.target->types[member_type.idx].member_types); + } + + // TODO: handle u128/i128 + assert(member_type.type != Type::i128 + && member_type.type != Type::u128); + assert(member_type.is_integral() + || member_type.type == Type::_enum); + assert(member.bitfield_size <= 8 * 8); + auto byte_off = member.offset / 8; + auto bit_off = member.offset % 8; + auto byte_size = (member.bitfield_size + 7) / 8; + uint64_t value = 0; + uint8_t arr[8] = {}; + const auto &data = node.vec_data(); + std::copy(data.begin() + off + byte_off, + data.begin() + off + byte_off + byte_size, arr); + value = *reinterpret_cast(arr); + + assert(bit_off + member.bitfield_size <= 64); + value = value >> bit_off; + value = + value & (0xFFFFFFFF'FFFFFFFF >> (64 - member.bitfield_size)); + + if (member_type.type == Type::_enum) + { + const auto &members = + frontend.target->types[type_id.idx].member_vec(); + auto printed = false; + for (const auto &member : members) + { + if (value == member.enum_val) + { + ImGui::Text("%s", member.name.c_str()); + printed = true; + break; + } + } + if (!printed) + { + ImGui::Text("", value); + } + } else + { + ImGui::Text("%ld", value); + } + + continue; + } + + expr_path.push_back(ExprPathPart{ + .ident = std::string_view{name_begin, static_cast( + name_end - name_begin)}, + .deref = false}); + this->draw_value(frontend, member.type_id, member.name, node_idx, + off + member.offset, expr_path); + expr_path.pop_back(); + } + } else + { + // array + auto member_ty_id = + std::get(frontend.target->types[type_id.idx].member_types); + auto member_size = member_ty_id.byte_size(frontend.target->types); + size_t el_count = + frontend.target->types[type_id.idx].byte_size / member_size; + + expr_path.push_back(ExprPathPart{ + .ident = std::string_view{name_begin, + static_cast(name_end - name_begin)}, + .array = true}); + + char buf[32]; + size_t member_off = 0; + for (size_t i = 0; i < el_count; ++i) + { + std::snprintf(buf, sizeof(buf), "[%lu]", i); + auto expr_path_size = expr_path.size(); + expr_path.back().array_idx = i; + this->draw_value(frontend, member_ty_id, buf, node_idx, + off + member_off, expr_path); + assert(expr_path.size() == expr_path_size); + member_off += member_size; + } + + expr_path.pop_back(); + } + + ImGui::TreePop(); + } + + ImGui::PopID(); + } else + { + auto is_editing = false; + if (name.index() == 1) + { + auto *slot = std::get(name); + if (slot->is_editing) + { + is_editing = true; + if (slot->edit_was_started) + { + slot->edit_was_started = false; + ImGui::SetKeyboardFocusHere(); + } + if (ImGui::InputText("##dddd", slot->buf, sizeof(slot->buf), + ImGuiInputTextFlags_AutoSelectAll + | ImGuiInputTextFlags_EnterReturnsTrue) + && ImGui::IsItemDeactivatedAfterEdit()) + { + if (slot->buf[0] != '\0') + { + frontend.target->backend->remove_data_node(slot->id); + const auto slot_idx = std::distance(extra_slots.data(), slot); + create_node_for_extra_slot(frontend, slot_idx); + } + + slot->bak = slot->buf; + slot->is_editing = false; + } + if (ImGui::IsItemDeactivated()) + { + slot->is_editing = false; + } + } + } + + auto tree_open = false; + if (!is_editing) + { + char tree_id_buf[128]; + std::snprintf(tree_id_buf, sizeof(tree_id_buf), "%u#off%lu", node_idx, + off); + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_SpanFullWidth; + if (type_id.type != Type::ptr) + { + flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + } + + // TODO: better id + tree_open = + ImGui::TreeNodeEx(tree_id_buf, flags, "%.*s", + static_cast(name_end - name_begin), name_begin); + //ImGui::TextUnformatted(name_begin, name_end); + if (name.index() == 1 + && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) + && ImGui::IsItemHovered()) + { + auto *slot = std::get(name); + slot->is_editing = true; + slot->edit_was_started = true; + } + } + + ImGui::TableSetColumnIndex(1); + switch (type_id.type) + { + using enum Type; + case i8: + { + auto val = node.get_primitive(off); + if (std::isprint(val)) + { + ImGui::Text("%c", val); + } else + { + ImGui::Text("%d", val); + } + break; + } + + case u8: ImGui::Text("%lu", node.get_primitive(off)); break; + case u16: ImGui::Text("%lu", node.get_primitive(off)); break; + case u32: ImGui::Text("%lu", node.get_primitive(off)); break; + case u64: ImGui::Text("%lu", node.get_primitive(off)); break; + case i16: ImGui::Text("%ld", node.get_primitive(off)); break; + case i32: ImGui::Text("%ld", node.get_primitive(off)); break; + case i64: ImGui::Text("%ld", node.get_primitive(off)); break; + case _bool: + { + auto val = node.get_primitive(off); + if (val) + { + ImGui::TextUnformatted("true"); + } else + { + ImGui::TextUnformatted("false"); + } + break; + } + // TODO + case f128: + case i128: + case u128: + { + const auto &data = node.vec_data(); + const auto lo = node.get_primitive(off); + const auto hi = node.get_primitive(off + 8); + ImGui::Text("%16lX-%16lX", hi, lo); + break; + } + case f32: + { + ImGui::Text("%f", node.get_primitive(off)); + break; + } + case f64: + { + ImGui::Text("%f", node.get_primitive(off)); + break; + } + case ptr: + { + // TODO: ptr size + ImGui::Text("%lX", node.get_primitive(off)); + if (tree_open) + { + // TODO: make this func be able to not print the "outer" line + // and just accept a bool to only draw the containing value + std::string path{}; + if (type_id.sub_type != Type::i8) + { + // TODO: better check if this is really a string + path = "*"; + } + // why cant i just path name_begin, name_end to string_view? :[ + construct_expr_path( + path, expr_path, + std::string_view{name_begin, + static_cast(name_end - name_begin)}); + + auto src_id = frontend.target->find_or_create_expr_path( + std::move(path), type_id.sub_type == Type::i8); + auto res_idx = frontend.target->data_idx_for_src_id(src_id); + if (res_idx) + { + const auto &node = *frontend.target->data_res_nodes[*res_idx]; + if (type_id.sub_type == Type::i8 && node.success) + { + // TODO: make span whole row (see https://github.com/ocornut/imgui/issues/3565) + assert(node.type_id.type == Type::custom); + assert(node.vec_data().back() == '\0'); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(1); + ImGui::TextWrapped("\"%s\"", node.vec_data().data()); + } else if (node.success && node.children.size() == 1) + { + auto data_idx = node.children[0]; + const auto &data_node = + *frontend.target->data_res_nodes[data_idx]; + expr_path.push_back(ExprPathPart{ + .ident = std::string_view{name_begin, static_cast( + name_end - name_begin)}, + .deref = true}); + + this->draw_value(frontend, data_node.type_id, + std::string_view{""}, data_idx, 0, + expr_path); + + expr_path.pop_back(); + } + } + + ImGui::TreePop(); + } + break; + } + case _enum: + { + // TODO: robustness + uint64_t val; + const auto byte_size = frontend.target->types[type_id.idx].byte_size; + if (byte_size == 8) + { + val = node.get_primitive(off); + } else if (byte_size == 4) + { + val = node.get_primitive(off); + } else if (byte_size == 2) + { + val = node.get_primitive(off); + } else if (byte_size == 1) + { + val = node.get_primitive(off); + } else + { + assert(0); + val = 0; + } + const auto &members = frontend.target->types[type_id.idx].member_vec(); + auto printed = false; + for (const auto &member : members) + { + if (val == member.enum_val) + { + ImGui::Text("%s", member.name.c_str()); + printed = true; + break; + } + } + if (!printed) + { + ImGui::Text("", val); + } + break; + } + } + } +} + +void WatchWindow::construct_expr_path(std::string &out, + const std::vector &path, + std::string_view tail) +{ + for (const auto &entry : path) + { + // FIXME: BIGGEST HACK in cinema history + if (entry.ident == "") + { + continue; + } + + out += entry.ident; + if (entry.deref) + { + out += "->"; + } else if (!entry.array) + { + out += '.'; + } + } + out += tail; +} + +namespace +{ + enum class WatchToken : uint32_t + { + comma, + dot, + bracket_open, + bracket_close, + circumflex, + dollar, + hashtag, + percent, + + literal_bit = 0x10000000, + }; + + struct ParseState + { + Frontend *frontend; + std::string_view buf; + std::vector literal_buf; + std::string path_buf{}; + std::vector nodes{}; + }; + + bool token_is_literal(const WatchToken token) + { + return (static_cast(token) + & static_cast(WatchToken::literal_bit)) + == static_cast(WatchToken::literal_bit); + } + + std::string_view token_literal(const ParseState &state, + const WatchToken token) + { + assert(token_is_literal(token)); + const auto idx = static_cast(token) + & ~static_cast(WatchToken::literal_bit); + return {state.literal_buf.data() + idx}; + } + + std::string_view token_to_str(const ParseState &state, WatchToken token) + { + switch (token) + { + case WatchToken::comma: return ","; + case WatchToken::dot: return "."; + case WatchToken::bracket_open: return "["; + case WatchToken::bracket_close: return "]"; + case WatchToken::circumflex: return "^"; + case WatchToken::dollar: return "$"; + case WatchToken::hashtag: return "#"; + case WatchToken::percent: return "%"; + + default: + { + if (token_is_literal(token)) + { + const auto idx = static_cast(token) + & ~static_cast(WatchToken::literal_bit); + return {state.literal_buf.data() + idx}; + } + return ""; + } + } + } + + std::optional next_token(ParseState &state) + { + if (state.buf.empty()) + { + spdlog::trace("Token buffer empty"); + return {}; + } + + // TODO: utf8? + auto c = state.buf[0]; + assert(c < 128); + state.buf.remove_prefix(1); + + switch (c) + { + case ',': return WatchToken::comma; + case '.': return WatchToken::dot; + case '[': return WatchToken::bracket_open; + case ']': return WatchToken::bracket_close; + case '^': return WatchToken::circumflex; + case '$': return WatchToken::dollar; + case '#': return WatchToken::hashtag; + case '%': return WatchToken::percent; + + default: + { + if (!std::isalnum(c)) + { + // TODO: error handling + spdlog::trace("Encountered non-alphanumeric token"); + return {}; + } + + const auto literal_end = + std::find_if(state.buf.begin(), state.buf.end(), + [](auto c) { return !std::isalnum(c); }); + + const auto idx = state.literal_buf.size(); + assert(idx < (1ull << 31)); + state.literal_buf.push_back(c); + state.literal_buf.insert(state.literal_buf.end(), state.buf.begin(), + literal_end); + state.literal_buf.push_back('\0'); + state.buf.remove_prefix(literal_end - state.buf.begin() + 1); + + return WatchToken{static_cast(WatchToken::literal_bit) + | static_cast(idx)}; + } + } + } + + std::optional parse_data_desc(ParseState &state) + { + const auto token_opt = next_token(state); + if (!token_opt) + { + spdlog::trace("Failed to parse data_desc: out of token"); + return {}; + } + const auto token = *token_opt; + spdlog::trace("Parsing data_desc: Token {}", token_to_str(state, token)); + + // check for register + if (token == WatchToken::dollar) + { + spdlog::trace("Parsing register"); + const auto token_opt = next_token(state); + if (!token_opt) + { + spdlog::trace("Failed to parse register: out of token"); + return {}; + } + + const auto token = *token_opt; + if (!token_is_literal(token)) + { + spdlog::trace("Failed to parse register: encountered non-literal {}", + token_to_str(state, token)); + return {}; + } + + const auto reg_name = token_literal(state, token); + assert(state.frontend->target); + const auto ®_sets = state.frontend->target->reg_sets; + + uint16_t set_idx = 0, reg_idx = 0; + auto found = false; + for (; set_idx < reg_sets.size(); ++set_idx) + { + const auto &set = reg_sets[set_idx]; + for (reg_idx = 0; reg_idx < set.regs.size(); ++reg_idx) + { + if (set.regs[reg_idx].name == reg_name) + { + found = true; + break; + } + } + + if (found) + { + break; + } + } + + if (!found) + { + spdlog::trace("Failed to parse register: invalid register name '{}'", + reg_name); + return {}; + } + + auto node_id = state.frontend->target->data_node_id++; + state.nodes.push_back(data::source::Node{ + .id = node_id, + .type = data::source::Node::Type::source, + .data = + data::source::Source{ + .type = data::source::Source::Type::reg, + .data = + data::source::Source::Reg{ + .set = set_idx, + .idx = reg_idx, + }, + }, + }); + + spdlog::trace("Created register node with id {}", node_id); + return node_id; + } + } +} // namespace + +void WatchWindow::create_node_for_extra_slot(Frontend &frontend, + const size_t slot_idx) +{ + const auto &slot = extra_slots[slot_idx]; + + auto parse_state = ParseState{ + .frontend = &frontend, + .buf = slot.buf, + .literal_buf = {}, + }; + + assert(0); + + using namespace data::source; + frontend.target->backend->add_data_node(Node{ + .id = slot.id, + .type = Node::Type::source, + .data = Source{.type = Source::Type::variable, + .data = Source::Variable{.expr_path = slot.buf, + .address_of_or_pointer = false}}}); +} diff --git a/src/frontend/window/watch_window.h b/src/frontend/window/watch_window.h new file mode 100644 index 0000000..042e075 --- /dev/null +++ b/src/frontend/window/watch_window.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include +#include "msg.h" + +namespace dbgui::frontend +{ + struct Frontend; + + struct WatchWindow + { + enum class VarFormatTy : uint8_t + { + ty_builtin, + i8, + i16, + i32, + i64, + i128, + u8, + u16, + u32, + u64, + u128, + f32, + f128, + str, + cstr, + }; + + struct ExtraSlot + { + size_t id; + char buf[256]; + std::string bak; + bool is_editing = false; + bool edit_was_started = false; + VarFormatTy fmt_ty = VarFormatTy::ty_builtin; + uint32_t fmt_matrix_x = 0, fmt_matrix_y = 0; + }; + + // TODO: cache the expr_path by node_idx + off or smth? + struct ExprPathPart + { + std::string_view ident; + bool deref = false; + bool array = false; + uint64_t array_idx = 0; + }; + + bool draw(Frontend &); + void draw_value(Frontend &, data::type_info::TypeID, + std::variant name, + data::result::NodeIdx node_idx, size_t off, + std::vector &expr_path); + // void handle_source_updated(Target& target, size_t id); + void construct_expr_path(std::string &out, + const std::vector &, + std::string_view tail); + + void create_node_for_extra_slot(Frontend &frontend, size_t slot_idx); + + std::string id; + bool open; + bool first; + + size_t locals_src_id; + + std::vector extra_slots; + char add_slot_buf[256] = {}; + }; +} // namespace dbgui::frontend \ No newline at end of file