diff --git a/src/backend/lldb/lldb_backend.cpp b/src/backend/lldb/lldb_backend.cpp index fd71240..f9d2be0 100644 --- a/src/backend/lldb/lldb_backend.cpp +++ b/src/backend/lldb/lldb_backend.cpp @@ -2146,6 +2146,100 @@ std::optional> return std::make_pair(cache_idx, node.id); } } + case read_cstr: + { + using namespace lldb; + uint16_t cache_idx = 0; + if (auto found_idx = find_node_for_src_id(node.id); found_idx) + { + cache_idx = *found_idx; + } else + { + cache_idx = get_free_res_slot(); + } + + size_t addr_id = std::get(node.data).src_id; + uint16_t addr_idx = *find_node_for_src_id(addr_id); + if (!_data_res[addr_idx] || !_data_res[addr_idx]->node.success) + { + return check_single_res_changed(data::result::Node{ + .idx = cache_idx, + .type_id = data::type_info::TypeID::none(), + .success = false, + }); + } + + if (_data_res[addr_idx]->node.type_id.type != data::type_info::Type::ptr) + { + return check_single_res_changed(data::result::Node{ + .idx = cache_idx, + .type_id = data::type_info::TypeID::none(), + .success = false, + }); + } + + auto addr = std::get(_data_res[addr_idx]->node.data); + std::vector buf{}; + + char tmp_buf[256]; + auto success = true; + while (true) + { + auto lldb_addr = _target.ResolveLoadAddress(addr); + auto err = SBError{}; + err.Clear(); + auto bytes_read = + _target.ReadMemory(lldb_addr, tmp_buf, sizeof(tmp_buf), err); + if (bytes_read == 0) + { + success = false; + break; + } + + auto null_pos = std::find(tmp_buf, tmp_buf + bytes_read, '\0'); + if (null_pos != tmp_buf + bytes_read) + { + size_t len = null_pos - tmp_buf; + auto old_size = buf.size(); + buf.resize(old_size + len + 1); + std::copy(tmp_buf, null_pos + 1, buf.data() + old_size); + break; + } + + if (bytes_read != sizeof(tmp_buf)) + { + if (err.Fail()) + { + printf("ReadAsCStr failed: %s\n", err.GetCString()); + success = false; + break; + } + } + + auto old_size = buf.size(); + buf.resize(old_size + bytes_read); + std::copy(tmp_buf, tmp_buf + bytes_read, buf.data() + old_size); + + addr += bytes_read; + } + + if (!success) + { + return check_single_res_changed(data::result::Node{ + .idx = cache_idx, + .type_id = data::type_info::TypeID::none(), + .success = false, + }); + } + + // TODO: embed the array size into the TypeID and then make this + // an i8 array type? + return check_single_res_changed( + data::result::Node{.idx = cache_idx, + .type_id = data::type_info::TypeID::custom(), + .success = true, + .data = std::move(buf)}); + } } printf("Unhandled data type\n"); @@ -3045,7 +3139,7 @@ dbgui::data::result::NodeIdx LLDBBackend::build_nodes_for_var_uncached( // TODO: always get child info for first level // TODO: depending on what is previewed? - res_node.success = true; + res_node.success = err.Success(); to_send.push_back(res_node); _data_res[res_idx] = CachedDataRes{.node = std::move(res_node), @@ -3132,7 +3226,7 @@ void LLDBBackend::build_nodes_for_var_cached( // TODO: always get child info for first level // TODO: depending on what is previewed? - res_node.success = true; + res_node.success = err.Success(); if (!_data_res[cache_idx]->node.success || _data_res[cache_idx]->node.data != res_node.data) { diff --git a/src/data.h b/src/data.h index 35bcbef..48fff7d 100644 --- a/src/data.h +++ b/src/data.h @@ -321,7 +321,8 @@ namespace dbgui::data uint16_t idx; }; - struct Variable { + struct Variable + { std::string expr_path; }; @@ -343,6 +344,14 @@ namespace dbgui::data size_t src_id; }; + // need this as length is unbounded + // note: does not support wchar strings right now + struct ReadAsCStr + { + // node that provides the address to read at + size_t src_id; + }; + struct Node { enum class Type : uint8_t @@ -351,12 +360,14 @@ namespace dbgui::data disassemble, line_entry, locals, + read_cstr, }; size_t id; Type type; // when adding something here, remember to update LLDBBackend::add_data_node - std::variant data; + std::variant + data; }; } // namespace source diff --git a/src/frontend/frontend.cpp b/src/frontend/frontend.cpp index b8a9ace..f7a1486 100644 --- a/src/frontend/frontend.cpp +++ b/src/frontend/frontend.cpp @@ -62,6 +62,14 @@ void Frontend::run_frame() if (ImGui::Begin("Test", nullptr, win_flags)) { + if (this->target) + { + for (auto &entry : this->target->expr_cache) + { + entry.used_this_frame = false; + } + } + // 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 @@ -85,6 +93,22 @@ void Frontend::run_frame() } //this->dock_id = ImGui::GetWindowDockID(); } + + if (this->target) + { + for (size_t i = 0; i < this->target->expr_cache.size();) + { + auto &entry = this->target->expr_cache[i]; + if (!entry.used_this_frame) + { + this->target->backend->remove_data_node(entry.id); + this->target->expr_cache.erase(this->target->expr_cache.begin() + i); + } else + { + ++i; + } + } + } } ImGui::End(); diff --git a/src/frontend/target.h b/src/frontend/target.h index f81b3b9..782adac 100644 --- a/src/frontend/target.h +++ b/src/frontend/target.h @@ -78,6 +78,13 @@ namespace dbgui::frontend } }; + struct ExprCacheEntry + { + std::string expr_path; + size_t id; + bool used_this_frame = false; + }; + Target(std::string filename); std::optional data_idx_for_src_id(size_t id) @@ -108,6 +115,29 @@ namespace dbgui::frontend return &*data_res_nodes[*idx]; } + size_t find_or_create_expr_path(std::string &&path) + { + for (auto &entry : expr_cache) + { + if (entry.expr_path == path) + { + entry.used_this_frame = true; + return entry.id; + } + } + + using namespace data::source; + auto id = this->id++; + this->backend->add_data_node( + Node{.id = id, + .type = Node::Type::source, + .data = Source{.type = Source::Type::variable, + .data = Source::Variable{.expr_path = path}}}); + expr_cache.push_back(ExprCacheEntry{ + .expr_path = std::move(path), .id = id, .used_this_frame = true}); + return id; + } + TargetState state = TargetState::stopped; std::string filename; uint64_t id; @@ -124,6 +154,7 @@ namespace dbgui::frontend std::vector> src_id_to_data_idx; std::vector> data_res_nodes; std::vector types; + std::vector expr_cache; std::shared_ptr backend = nullptr; }; diff --git a/src/frontend/window.cpp b/src/frontend/window.cpp index 30273d0..7adfee0 100644 --- a/src/frontend/window.cpp +++ b/src/frontend/window.cpp @@ -1014,6 +1014,8 @@ bool WatchWindow::draw(Frontend &frontend) first = false; } + auto expr_path_vec = std::vector{}; + // ImGuiTableFlags_SizingFixedFit if (ImGui::BeginTable("##Variables", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable)) @@ -1053,9 +1055,10 @@ bool WatchWindow::draw(Frontend &frontend) 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); + name, node_idx, 0, expr_path_vec); cur_idx += 1; cur_off += 2 + str_len; @@ -1078,8 +1081,9 @@ bool WatchWindow::draw(Frontend &frontend) 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); + node.children[0], 0, expr_path_vec); if (extra_slots[i].bak == "") { @@ -1192,7 +1196,8 @@ bool WatchWindow::draw(Frontend &frontend) void WatchWindow::draw_value(Frontend &frontend, data::type_info::TypeID type_id, std::variant name, - data::result::NodeIdx node_idx, size_t off) + data::result::NodeIdx node_idx, size_t off, + std::vector &expr_path) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -1367,8 +1372,12 @@ void WatchWindow::draw_value(Frontend &frontend, continue; } + expr_path.push_back(ExprPathPart{ + .ident = std::string_view{name_begin, name_end - name_begin}, + .deref = false}); this->draw_value(frontend, member.type_id, member.name, node_idx, - off + member.offset); + off + member.offset, expr_path); + expr_path.pop_back(); } } else { @@ -1379,15 +1388,21 @@ void WatchWindow::draw_value(Frontend &frontend, 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, 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); this->draw_value(frontend, member_ty_id, buf, node_idx, - off + member_off); + off + member_off, expr_path); member_off += member_size; } + + expr_path.pop_back(); } ImGui::TreePop(); @@ -1435,17 +1450,22 @@ void WatchWindow::draw_value(Frontend &frontend, } } + 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 - ImGui::TreeNodeEx( - tree_id_buf, - ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf - | ImGuiTreeNodeFlags_NoTreePushOnOpen, - "%.*s", static_cast(name_end - name_begin), name_begin); + 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) @@ -1518,6 +1538,43 @@ void WatchWindow::draw_value(Frontend &frontend, { // 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{"*"}; + // 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)); + 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 (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: @@ -1563,6 +1620,30 @@ void WatchWindow::draw_value(Frontend &frontend, } } +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 2f0b6d7..699ece4 100644 --- a/src/frontend/window.h +++ b/src/frontend/window.h @@ -135,11 +135,23 @@ namespace dbgui::frontend 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; + }; + bool draw(Frontend &); void draw_value(Frontend &, data::type_info::TypeID, std::variant name, - data::result::NodeIdx node_idx, size_t off); + 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; diff --git a/tmp/main b/tmp/main index 5b76443..b19ca1a 100755 Binary files a/tmp/main and b/tmp/main differ diff --git a/tmp/sec.cpp b/tmp/sec.cpp index a8a6011..d592d95 100644 --- a/tmp/sec.cpp +++ b/tmp/sec.cpp @@ -16,6 +16,7 @@ namespace test { uint32_t test_bits : 20; uint32_t test_bits2: 12; MyEnum enum_val; + std::string* ptr = nullptr; }; } @@ -26,7 +27,7 @@ void helper_fn() { unsigned int y = 30; sleep(tmp.data); { - test::MyType tmp = test::MyType{2}; + test::MyType tmp = test::MyType{.data = 2, .ptr = &test}; sleep(tmp.data); } } diff --git a/todos.txt b/todos.txt index 66a80a4..02fa295 100644 --- a/todos.txt +++ b/todos.txt @@ -3,3 +3,4 @@ - memory + cpu statistics like VS - visualization for branches (draw arror if branch is taken (or in both cases?)) - allow (partial) path override for symbol source paths +- check if classes with base classes work