From e63179e14fc2f07e2170bac1a241886a8a31a8ea Mon Sep 17 00:00:00 2001 From: T0b1 Date: Mon, 13 May 2024 21:17:18 +0200 Subject: [PATCH] add basic array sizing with constant size, tho the parsing is probably not correct this should just be a normal array index also missing handling to correctly construct the expr path when accessing pointers (maybe just make this node based in the future?) --- src/backend/lldb/lldb_backend.cpp | 149 ++++++++++++++++++++++++++- src/data.h | 12 ++- src/frontend/window/watch_window.cpp | 118 +++++++++++++++++---- 3 files changed, 255 insertions(+), 24 deletions(-) diff --git a/src/backend/lldb/lldb_backend.cpp b/src/backend/lldb/lldb_backend.cpp index f7aef6b..a820fb8 100644 --- a/src/backend/lldb/lldb_backend.cpp +++ b/src/backend/lldb/lldb_backend.cpp @@ -143,9 +143,11 @@ void LLDBBackend::handle_state_change(lldb::StateType state) { spdlog::trace("Got initial state change: {}", static_cast(state)); - if (state != lldb::StateType::eStateStopped) { + if (state != lldb::StateType::eStateStopped) + { // TODO: handle other stopped states, e.g. crashed - spdlog::trace("Not handling initial state {}", static_cast(state)); + spdlog::trace("Not handling initial state {}", + static_cast(state)); return; } @@ -286,8 +288,8 @@ void LLDBBackend::prepare_proc_info( } auto frame = _process->GetThreadAtIndex(0).GetFrameAtIndex(0); - /*const auto regs = frame.GetRegisters(); - const auto len = regs.GetSize(); + const auto regs = frame.GetRegisters(); + /*const auto len = regs.GetSize(); for (size_t i = 0; i < len; ++i) { auto reg_or_set = regs.GetValueAtIndex(i); if (reg_or_set.GetValueType() == eValueTypeRegister) { @@ -1530,6 +1532,29 @@ void LLDBBackend::add_data_node(const data::source::Node &node) _data_dag.add_edge(node.id, src_id); break; } + case read_array: + { + const auto &arr_node = std::get(node.data); + auto src_id = arr_node.addr_src_id; + if (!_data_dag.nodes.contains(src_id)) + { + printf("Invalid add sequence\n"); + exit(1); + } + _data_dag.add_edge(node.id, src_id); + + if (!arr_node.is_const_size) + { + auto size_id = arr_node.size_src_id; + if (!_data_dag.nodes.contains(size_id)) + { + printf("Invalid add sequence\n"); + exit(1); + } + _data_dag.add_edge(node.id, size_id); + } + break; + } } _dag_linear_valid = false; @@ -1681,6 +1706,7 @@ std::optional> } } + // TODO: reserve the slot auto idx = this->_data_res.size(); this->_data_res.resize(idx + 1); return idx; @@ -2292,6 +2318,121 @@ std::optional> .success = true, .data = std::move(buf)}); } + case read_array: + { + 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(); + // reserve the slot + _data_res[cache_idx] = CachedDataRes{.src_id = node.id}; + } + + const auto &arr_node = std::get(node.data); + auto res_node = data::result::Node{ + .idx = cache_idx, + .type_id = data::type_info::TypeID::none(), + .success = false, + }; + + size_t addr_id = arr_node.addr_src_id; + uint16_t addr_parent_idx = *find_node_for_src_id(addr_id); + if (!_data_res[addr_parent_idx] + || !_data_res[addr_parent_idx]->node.success + || _data_res[addr_parent_idx]->node.children.size() != 1) + { + return check_single_res_changed(std::move(res_node)); + } + + uint16_t addr_idx = _data_res[addr_parent_idx]->node.children[0]; + + if (!_data_res[addr_idx] || !_data_res[addr_idx]->node.success + || _data_res[addr_idx]->node.type_id.type + != data::type_info::Type::ptr) + { + return check_single_res_changed(std::move(res_node)); + } + + if (!arr_node.is_const_size) + { + spdlog::trace("read_as_array: non-const size not supported"); + return check_single_res_changed(std::move(res_node)); + } + + const auto addr = _data_res[addr_idx]->node.get_primitive(0); + spdlog::trace("read_as_array: addr: {:X}", addr); + + const auto pointee_type_idx = _data_res[addr_idx]->node.type_id.idx; + if (!_types[pointee_type_idx]) + { + spdlog::trace("Found no pointee type for read_as_array"); + return check_single_res_changed(std::move(res_node)); + } + + auto pointee_type = _types[pointee_type_idx]->first; + if (pointee_type.IsArrayType()) + { + // TODO: should we do this until there is no array left? + pointee_type = pointee_type.GetArrayElementType(); + } + spdlog::trace("read_as_array: pointee_type valid?: {}", + pointee_type.IsValid()); + + std::string type_buf; + format_type(pointee_type, type_buf); + spdlog::trace("read_as_array: pointee_type: {}", type_buf); + + auto arr_type = pointee_type.GetArrayType(arr_node.size_const); + + spdlog::trace("read_as_array: arr_type valid?: {}", arr_type.IsValid()); + type_buf.clear(); + format_type(arr_type, type_buf); + spdlog::trace("read_as_array: arr_type: {}", type_buf); + + char buf[128], buf2[32]; + /*std::snprintf(buf2, sizeof(buf2), "%zX", node.id); + std::snprintf(buf, sizeof(buf), "*((%s *)0x%zX)", arr_type.GetName(), + addr); + auto arr_val = _target.CreateValueFromExpression(buf2, buf); + if (!arr_val.IsValid()) + { + spdlog::trace("read_as_array: arr_val not valid"); + return check_single_res_changed(std::move(res_node)); + }*/ + std::snprintf(buf, sizeof(buf), "(char*)0x%zX", addr); + auto tmp_val = _target.CreateValueFromExpression("arr_tmp", buf); + if (!tmp_val.IsValid()) + { + spdlog::trace("read_as_array: tmp_val not valid"); + return check_single_res_changed(std::move(res_node)); + } + + std::snprintf(buf, sizeof(buf), "%zX", node.id); + auto arr_val = tmp_val.CreateValueFromAddress(buf, addr, arr_type); + if (!arr_val.IsValid()) + { + spdlog::trace("read_as_array: arr_val not valid"); + return check_single_res_changed(std::move(res_node)); + } + //arr_val = arr_val.Cast(arr_type); + + auto res_idx = this->build_nodes_for_var(arr_val, data_res, false); + if (!res_idx) + { + return check_single_res_changed(std::move(res_node)); + } + + spdlog::trace( + "Got res for {}: {} ({})", node.id, *res_idx, + static_cast(_data_res[*res_idx]->node.type_id.type)); + res_node.success = true; + res_node.children.push_back(*res_idx); + return check_single_res_changed(std::move(res_node)); + } } printf("Unhandled data type\n"); diff --git a/src/data.h b/src/data.h index 93c0f73..36c6a4c 100644 --- a/src/data.h +++ b/src/data.h @@ -355,6 +355,15 @@ namespace dbgui::data size_t src_id; }; + struct ReadAsArray { + size_t addr_src_id; + union { + size_t size_src_id; + uint64_t size_const; + }; + bool is_const_size; + }; + struct Node { enum class Type : uint8_t @@ -364,12 +373,13 @@ namespace dbgui::data line_entry, locals, read_cstr, + read_array, }; size_t id; Type type; // when adding something here, remember to update LLDBBackend::add_data_node - std::variant + std::variant data; }; } // namespace source diff --git a/src/frontend/window/watch_window.cpp b/src/frontend/window/watch_window.cpp index b295cb2..dd88875 100644 --- a/src/frontend/window/watch_window.cpp +++ b/src/frontend/window/watch_window.cpp @@ -4,6 +4,7 @@ #include "imgui_internal.h" #include #include +#include using namespace dbgui; using namespace dbgui::frontend; @@ -818,6 +819,9 @@ namespace return token; } + std::optional parse_array_desc_inner(ParseState& state, size_t addr_src_id); + + // = . | | | std::optional parse_data_desc(ParseState &state) { @@ -917,6 +921,27 @@ namespace } state.path_buf.append(ident); + const auto create_var_node = [&](bool address_of) -> size_t { + spdlog::trace("Parsing data_desc: creating variable source (address?: {}) with path {}", + address_of, state.path_buf); + 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::variable, + .data = + data::source::Source::Variable{ + .expr_path = state.path_buf, + .address_of_or_pointer = address_of, + }, + }, + }); + state.path_buf.clear(); + return node_id; + }; + // differentiate between , and end of const auto peek_token_opt = peek_token(state); if (peek_token_opt) @@ -934,30 +959,85 @@ namespace if (peek_token == WatchToken::bracket_open) { - // TODO: parse - assert(0); - return {}; + // parse + // consume token + next_token(state); + const auto src_node = create_var_node(true); + const auto res = parse_array_desc_inner(state, src_node); + if (!res) { + return res; + } + + // we expect a closing bracket + const auto closing_opt = next_token(state); + if (!closing_opt) { + spdlog::trace("Failed to parse data_desc: Expected closing bracket in array_desc but got none"); + return {}; + } + + if (*closing_opt != WatchToken::bracket_close) { + spdlog::trace("Failed to parse data_desc: Expected closing bracket in array_desc but got {}", token_to_str(state, *closing_opt)); + return {}; + } + + return res; } } - spdlog::trace("Parsing data_desc: creating variable source with path {}", - state.path_buf); // something else, end of - 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::variable, - .data = - data::source::Source::Variable{ - .expr_path = state.path_buf, - .address_of_or_pointer = false, - }, - }, - }); + return create_var_node(false); + } + // = \[|\] + std::optional parse_array_desc_inner(ParseState& state, size_t addr_src_id) { + // the bracket has already been consumed by the caller + spdlog::trace("Parsing array_desc_inner"); + + // for now only support constant sized arrays + const auto peek_opt = peek_token(state); + if (!peek_opt) { + spdlog::trace("Failed to parse array_desc_inner: end of token"); + return {}; + } + + const auto peek_token = *peek_opt; + if (!token_is_literal(peek_token)) { + spdlog::trace("Failed to parse array_desc_inner: Expected literal but got {}", token_to_str(state, peek_token)); + return {}; + } + + // consume + next_token(state); + + auto literal = token_literal(state, peek_token); + spdlog::trace("Parsing array_desc_inner: Got literal {}", literal); + + int base = 10; + if (literal.starts_with("0x")) { + base = 16; + literal.remove_prefix(2); + } + uint64_t size = 0; + + const auto parse_res = std::from_chars(literal.begin(), literal.end(), size, base); + if (parse_res.ec != std::errc{} || parse_res.ptr != literal.end()) { + spdlog::trace("Failed parsing array_desc_inner: Literal is not a number"); + return {}; + } + + auto node_id = state.frontend->target->data_node_id++; + spdlog::trace("Parsing array_desc_inner: creating read_as_array with size {} and id {}", + size, node_id); + state.nodes.push_back(data::source::Node{ + .id = node_id, + .type = data::source::Node::Type::read_array, + .data = + data::source::ReadAsArray{ + .addr_src_id = addr_src_id, + .size_const = size, + .is_const_size = true, + }, + }); return node_id; } } // namespace