Compare commits

...

3 Commits

Author SHA1 Message Date
T0b1
e63179e14f 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?)
2024-05-13 21:17:18 +02:00
T0b1
70871a5671 adjust gitignore 2024-05-13 18:29:28 +02:00
T0b1
e1be42052d fix bug with initial state change 2024-05-13 18:28:14 +02:00
4 changed files with 283 additions and 43 deletions

3
.gitignore vendored
View File

@@ -1,4 +1,5 @@
build/
build*
.idea/
.vscode/
.cache/

View File

@@ -141,6 +141,16 @@ void LLDBBackend::handle_state_change(lldb::StateType state)
using namespace lldb;
if (_first_run)
{
spdlog::trace("Got initial state change: {}", static_cast<uint32_t>(state));
if (state != lldb::StateType::eStateStopped)
{
// TODO: handle other stopped states, e.g. crashed
spdlog::trace("Not handling initial state {}",
static_cast<uint32_t>(state));
return;
}
_first_run = false;
// TODO: do initialization
// TODO: we should only do this when the process was stopped, no?
@@ -280,26 +290,26 @@ void LLDBBackend::prepare_proc_info(
auto frame = _process->GetThreadAtIndex(0).GetFrameAtIndex(0);
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) {
printf("Got register %s (%s) with value: %lX\n", reg_or_set.GetName(), reg_or_set.GetTypeName(), reg_or_set.GetValueAsUnsigned(0));
info.reg_names.emplace_back(reg_or_set.GetName());
continue;
}
printf("Got register set %s\n", reg_or_set.GetName());
const auto is_gp_set = std::string_view{reg_or_set.GetName()} == "General Purpose Registers";
for (uint32_t child_idx = 0; child_idx < reg_or_set.GetNumChildren(); ++child_idx) {
auto reg = reg_or_set.GetChildAtIndex(child_idx);
printf("Got register %s (%s) with value: %lX\n", reg.GetName(), reg.GetTypeName(), reg.GetValueAsUnsigned(0));
if (is_gp_set && info.arch == Arch::x86_64 && reg.GetByteSize() < 8) {
// skip non-full regs here
printf(" -> Skipped\n");
continue;
}
info.reg_names.emplace_back(reg.GetName());
}
}*/
for (size_t i = 0; i < len; ++i) {
auto reg_or_set = regs.GetValueAtIndex(i);
if (reg_or_set.GetValueType() == eValueTypeRegister) {
spdlog::trace("Got register {} ({}) with value: {:X}", reg_or_set.GetName(), reg_or_set.GetTypeName(), reg_or_set.GetValueAsUnsigned(0));
//info.reg_names.emplace_back(reg_or_set.GetName());
continue;
}
spdlog::trace("Got register set {}\n", reg_or_set.GetName());
const auto is_gp_set = std::string_view{reg_or_set.GetName()} == "General Purpose Registers";
for (uint32_t child_idx = 0; child_idx < reg_or_set.GetNumChildren(); ++child_idx) {
auto reg = reg_or_set.GetChildAtIndex(child_idx);
spdlog::trace("Got register {} ({}) with value: {:X}", reg.GetName(), reg.GetTypeName(), reg.GetValueAsUnsigned(0));
if (is_gp_set && info.arch == Arch::x86_64 && reg.GetByteSize() < 8) {
// skip non-full regs here
spdlog::trace(" -> Skipped");
continue;
}
//info.reg_names.emplace_back(reg.GetName());
}
}*/
if (info.arch == Arch::x86_64)
{
@@ -640,8 +650,8 @@ dbgui::data::type_info::TypeID
auto base_id = parse_type_inner(out_vec, base_type);
out_vec.push_back(type_info::TypeInfo{
.type = type_info::Type::_enum,
.byte_size = type_copy.GetByteSize(),
.enum_base = base_id,
.byte_size = type_copy.GetByteSize(),
.display_name = std::string{display_name},
.internal_name = std::string{internal_name},
.member_types = std::vector<type_info::MemberInfo>{}});
@@ -1522,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;
@@ -1673,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;
@@ -2284,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");
@@ -2659,8 +2808,8 @@ dbgui::data::type_info::TypeID
auto base_id = this->build_type_from_lldb_type(base_type);
_types.emplace_back(std::make_pair(
lldb_type, TypeInfo{.type = Type::_enum,
.byte_size = lldb_type.GetByteSize(),
.enum_base = base_id,
.byte_size = lldb_type.GetByteSize(),
.display_name = std::string{display_name},
.internal_name = std::string{internal_name},
.member_types = std::vector<MemberInfo>{}}));

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