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?)
This commit is contained in:
T0b1
2024-05-13 21:17:18 +02:00
parent 70871a5671
commit e63179e14f
3 changed files with 255 additions and 24 deletions

View File

@@ -143,9 +143,11 @@ void LLDBBackend::handle_state_change(lldb::StateType state)
{
spdlog::trace("Got initial state change: {}", static_cast<uint32_t>(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<uint32_t>(state));
spdlog::trace("Not handling initial state {}",
static_cast<uint32_t>(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<data::source::ReadAsArray>(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<std::pair<uint16_t, size_t>>
}
}
// TODO: reserve the slot
auto idx = this->_data_res.size();
this->_data_res.resize(idx + 1);
return idx;
@@ -2292,6 +2318,121 @@ std::optional<std::pair<uint16_t, size_t>>
.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<data::source::ReadAsArray>(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<uint64_t>(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<uint8_t>(_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");

View File

@@ -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::monostate, Source, Disassemble, LineEntry, ReadAsCStr>
std::variant<std::monostate, Source, Disassemble, LineEntry, ReadAsCStr, ReadAsArray>
data;
};
} // namespace source

View File

@@ -4,6 +4,7 @@
#include "imgui_internal.h"
#include <fstream>
#include <filesystem>
#include <charconv>
using namespace dbgui;
using namespace dbgui::frontend;
@@ -818,6 +819,9 @@ namespace
return token;
}
std::optional<size_t> parse_array_desc_inner(ParseState& state, size_t addr_src_id);
// <data desc> = <data desc>.<data desc> | <array desc> | <ident> | <reg>
std::optional<size_t> 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 <data desc>, <array desc> and end of <data desc>
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 <array desc>
assert(0);
return {};
// parse <array desc>
// 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 <data desc>
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);
}
// <array desc> = <ident>\[<arrsize spec>|<arrpath>\]
std::optional<size_t> 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