This commit is contained in:
T0b1
2023-06-15 02:25:57 +02:00
parent b393f3dd28
commit 9ab08de243
15 changed files with 1294 additions and 166 deletions

View File

@@ -31,6 +31,7 @@ set(DBGUI_SOURCES
src/main.cpp src/frontend/frontend.cpp src/frontend/window.cpp
src/backend/backend.cpp
src/backend/lldb/lldb_backend.cpp
src/data.cpp
${IMGUI_SOURCES}
${DBGUI_HEADERS})

View File

@@ -58,6 +58,13 @@ void Backend::send_data_result(BackToFront::DataResult &&info)
BackToFront::MsgType::data_result, std::move(info));
this->send_msg(std::move(msg));
}
void Backend::remove_data_node(uint16_t idx)
{
auto msg = std::make_unique<BackToFront::Msg>(
BackToFront::MsgType::remove_data_node,
std::move(BackToFront::RemoveDataNode{.node = idx}));
this->send_msg(std::move(msg));
}
void Backend::send_selected_thread_changed(uint16_t idx)
{
auto msg = std::make_unique<BackToFront::Msg>(

View File

@@ -26,8 +26,8 @@ namespace dbgui::backend
virtual void cont() = 0;
virtual void pause() = 0;
virtual void add_data_node(const data::DataNode &) = 0;
virtual void remove_data_node(uint64_t id) = 0;
virtual void add_data_node(const data::source::Node &) = 0;
virtual void remove_data_node(uint64_t id) = 0;
virtual void add_breakpoint(uint64_t addr, size_t id) = 0;
virtual bool add_breakpoint(const char *file, uint32_t line, size_t id) = 0;
@@ -68,6 +68,7 @@ namespace dbgui::backend
void send_frame_changed(BackToFront::FrameChanged &&);
void send_frame_removed(BackToFront::FrameRemoved &&);
void send_data_result(BackToFront::DataResult &&);
void remove_data_node(uint16_t);
void send_selected_thread_changed(uint16_t idx);
void send_selected_frame_changed(uint16_t idx);

View File

@@ -10,6 +10,10 @@
#include <lldb/API/SBInstruction.h>
#include <lldb/API/SBBreakpoint.h>
#include <lldb/API/SBBreakpointLocation.h>
#include <lldb/API/SBTypeFilter.h>
#include <lldb/API/SBTypeFormat.h>
#include <lldb/API/SBDeclaration.h>
#include <lldb/API/SBTypeEnumMember.h>
#include <filesystem>
#include <array>
#include <cassert>
@@ -150,7 +154,8 @@ void LLDBBackend::handle_state_change(lldb::StateType state)
}
}
this->dump_threads();
//this->dump_threads();
this->dump_variables();
switch (state)
{
@@ -452,6 +457,476 @@ void LLDBBackend::dump_threads()
}
}
std::optional<dbgui::data::type_info::TypeID>
basic_type_id(lldb::BasicType type)
{
using namespace dbgui::data::type_info;
using namespace lldb;
switch (type)
{
case eBasicTypeVoid: return TypeID::basic(Type::_void);
case eBasicTypeChar:
case eBasicTypeSignedChar: return TypeID::basic(Type::i8);
case eBasicTypeUnsignedChar: return TypeID::basic(Type::u8);
case eBasicTypeWChar:
case eBasicTypeSignedWChar:
case eBasicTypeChar16:
case eBasicTypeShort: return TypeID::basic(Type::i16);
case eBasicTypeUnsignedWChar:
case eBasicTypeUnsignedShort: return TypeID::basic(Type::u16);
case eBasicTypeChar32:
case eBasicTypeInt: return TypeID::basic(Type::i32);
case eBasicTypeUnsignedInt: return TypeID::basic(Type::u32);
case eBasicTypeLong:
// TODO: decide based on target platform
return TypeID::basic(Type::i32);
case eBasicTypeUnsignedLong:
// TODO: decide based on target platform
return TypeID::basic(Type::u32);
case eBasicTypeLongLong: return TypeID::basic(Type::i64);
case eBasicTypeUnsignedLongLong: return TypeID::basic(Type::u64);
case eBasicTypeInt128: return TypeID::basic(Type::i128);
case eBasicTypeUnsignedInt128: return TypeID::basic(Type::u128);
case eBasicTypeBool: return TypeID::basic(Type::_bool);
case eBasicTypeFloat: return TypeID::basic(Type::f32);
case eBasicTypeDouble: return TypeID::basic(Type::f64);
case eBasicTypeLongDouble: return TypeID::basic(Type::f128);
case eBasicTypeNullPtr:
// TODO: target platform dependent
return TypeID::basic(Type::u64);
// unhandled
case eBasicTypeHalf:
case eBasicTypeFloatComplex:
case eBasicTypeDoubleComplex:
case eBasicTypeLongDoubleComplex:
case eBasicTypeObjCID:
case eBasicTypeObjCClass:
case eBasicTypeObjCSel:
case eBasicTypeOther:
case eBasicTypeInvalid: return {};
}
assert(0);
exit(1);
}
dbgui::data::type_info::TypeID
parse_type_inner(std::vector<dbgui::data::type_info::TypeInfo> &out_vec,
lldb::SBType &type)
{
using namespace lldb;
using namespace dbgui::data;
if (auto tmp = basic_type_id(type.GetBasicType()); tmp)
{
return *tmp;
}
if (type.IsPointerType() || type.IsReferenceType())
{
auto inner_type = SBType{};
if (type.IsPointerType())
{
inner_type = type.GetPointeeType();
} else
{
inner_type = type.GetDereferencedType();
}
auto inner_id = parse_type_inner(out_vec, inner_type);
if (inner_id.is_basic())
{
return type_info::TypeID{
.type = type_info::Type::ptr, .sub_type = inner_id.type, .idx = 0};
} else if (inner_id.type == type_info::Type::ptr)
{
auto idx = static_cast<uint32_t>(out_vec.size());
// TODO: byte_size
out_vec.push_back(type_info::TypeInfo{
.type = type_info::Type::ptr,
.byte_size = 8,
.name = std::string{inner_type.GetDisplayTypeName()},
.member_types = inner_id});
return type_info::TypeID{.type = type_info::Type::ptr,
.sub_type = type_info::Type::ptr,
.idx = idx};
} else
{
return type_info::TypeID{.type = type_info::Type::ptr,
.sub_type = inner_id.type,
.idx = inner_id.idx};
}
}
// should not be a basic type
if (type.GetBasicType() != eBasicTypeInvalid || type.IsPointerType()
|| type.IsReferenceType())
{
assert(0);
exit(1);
}
auto name = type.GetDisplayTypeName();
auto type_copy = type;
while (type_copy.IsTypedefType())
{
type_copy = type_copy.GetTypedefedType();
}
// TODO: create entry for named basic types?
if (auto tmp = basic_type_id(type_copy.GetBasicType()); tmp)
{
return *tmp;
}
printf("Name: %s\n", name);
printf("TrueName: %s\n", type_copy.GetDisplayTypeName());
printf("Pointer: %u\n", type_copy.IsPointerType());
printf("Ref: %u\n", type_copy.IsReferenceType());
printf("Func: %u\n", type_copy.IsFunctionType());
printf("Poly: %u\n", type_copy.IsPolymorphicClass());
printf("Array: %u\n", type_copy.IsArrayType());
printf("Vec: %u\n", type_copy.IsVectorType());
printf("Typedef: %u\n", type_copy.IsTypedefType());
printf("Anonymous: %u\n", type_copy.IsAnonymousType());
printf("ScopedEnum: %u\n", type_copy.IsScopedEnumerationType());
printf("Aggregate: %u\n", type_copy.IsAggregateType());
printf("EnumIntTypeValid: %u\n",
type_copy.GetEnumerationIntegerType().IsValid());
// note: type can be both array and aggregate
if (type_copy.IsArrayType())
{
out_vec.push_back(type_info::TypeInfo{.type = type_info::Type::array,
.byte_size = type.GetByteSize(),
.name = std::string{name}});
uint32_t self_idx = out_vec.size() - 1;
auto inner_type = type_copy.GetArrayElementType();
auto inner_id = parse_type_inner(out_vec, inner_type);
out_vec[self_idx].member_types = inner_id;
return type_info::TypeID{.type = type_info::Type::array, .idx = self_idx};
} else if (auto base_type = type_copy.GetEnumerationIntegerType();
base_type.IsValid())
{
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,
.name = std::string{name},
.member_types = std::vector<type_info::MemberInfo>{}});
uint32_t self_idx = out_vec.size() - 1;
auto enum_list = type_copy.GetEnumMembers();
uint32_t len = enum_list.GetSize();
for (uint32_t i = 0; i < len; ++i)
{
auto member = enum_list.GetTypeEnumMemberAtIndex(i);
auto member_type = member.GetType();
auto member_id = parse_type_inner(out_vec, member_type);
auto name = member.GetName();
if (!name)
{
name = "<none>";
}
auto val = member.GetValueAsUnsigned();
out_vec[self_idx].member_vec().push_back(type_info::MemberInfo{
.name = name, .type_id = member_id, .enum_val = val});
}
return type_info::TypeID{.type = type_info::Type::_enum, .idx = self_idx};
} else if (type_copy.IsAggregateType())
{
out_vec.push_back(type_info::TypeInfo{
.type = type_info::Type::complex,
.byte_size = type_copy.GetByteSize(),
.name = std::string{name},
.member_types = std::vector<type_info::MemberInfo>{}});
uint32_t self_idx = out_vec.size() - 1;
uint32_t len = type_copy.GetNumberOfFields();
for (uint32_t i = 0; i < len; ++i)
{
auto member = type_copy.GetFieldAtIndex(i);
auto member_type = member.GetType();
auto member_id = parse_type_inner(out_vec, member_type);
auto name = member.GetName();
if (!name)
{
name = "<none>";
}
auto offset = member.GetOffsetInBytes();
auto bitfield_size = member.GetBitfieldSizeInBits();
if (bitfield_size != 0)
{
offset = member.GetOffsetInBits();
}
out_vec[self_idx].member_vec().push_back(
type_info::MemberInfo{.name = name,
.type_id = member_id,
.bitfield_size = bitfield_size,
.offset = offset});
}
return type_info::TypeID{.type = type_info::Type::complex, .idx = self_idx};
} else
{
printf("Unknown type encountered!\n");
assert(0);
exit(1);
}
}
void format_type(lldb::SBType &type, std::string &out)
{
using namespace lldb;
using namespace dbgui::data;
auto vec = std::vector<type_info::TypeInfo>{};
auto type_id = parse_type_inner(vec, type);
out += "Types:\n";
for (size_t i = 0; i < vec.size(); ++i)
{
char buf[32];
std::snprintf(buf, sizeof(buf), "Type %lu:\n", i);
out += buf;
vec[i].format(vec, out);
out += "\n\n";
}
out += "Formatted type: ";
type_id.format(out);
out += "\n";
/*switch (type.GetBasicType())
{
case eBasicTypeVoid: out = "void"; return;
case eBasicTypeChar:
case eBasicTypeSignedChar: out = "i8"; return;
case eBasicTypeUnsignedChar: out = "u8"; return;
case eBasicTypeWChar:
case eBasicTypeSignedWChar:
case eBasicTypeChar16:
case eBasicTypeShort: out = "i16"; return;
case eBasicTypeUnsignedWChar:
case eBasicTypeUnsignedShort: out = "u16"; return;
case eBasicTypeChar32:
case eBasicTypeInt: out = "i32"; return;
case eBasicTypeUnsignedInt: out = "u32"; return;
case eBasicTypeLong:
// TODO: decide based on target platform
out = "i32";
return;
case eBasicTypeUnsignedLong:
// TODO: decide based on target platform
out = "u32";
return;
case eBasicTypeLongLong: out = "i64"; return;
case eBasicTypeUnsignedLongLong: out = "u64"; return;
case eBasicTypeInt128: out = "i128"; return;
case eBasicTypeUnsignedInt128: out = "u128"; return;
case eBasicTypeBool: out = "bool"; return;
case eBasicTypeFloat: out = "f32"; return;
case eBasicTypeDouble: out = "f64"; return;
case eBasicTypeLongDouble: out = "f128"; return;
case eBasicTypeNullPtr:
// TODO: target platform dependent
out = "u64";
return;
// unhandled
case eBasicTypeHalf:
case eBasicTypeFloatComplex:
case eBasicTypeDoubleComplex:
case eBasicTypeLongDoubleComplex:
case eBasicTypeObjCID:
case eBasicTypeObjCClass:
case eBasicTypeObjCSel:
case eBasicTypeOther: out = "<unk>"; return;
case eBasicTypeInvalid:
{
if (type.IsPointerType())
{
auto inner_type = type.GetPointeeType();
std::string tmp = {};
format_type(inner_type, tmp);
out = "ptr<";
out += tmp;
out += ">";
} else if (type.IsReferenceType())
{
auto inner_type = type.GetDereferencedType();
std::string tmp = {};
format_type(inner_type, tmp);
out = "ref<";
out += tmp;
out += ">";
} else
{
auto vec = std::vector<dbgui::data::type_info::TypeInfo>{};
parse_type_inner(vec, type);
dbgui::data::type_info::TypeInfo::format(vec, 0, out);
return;
}
}
default: assert(0); exit(1);
}*/
}
#if 0
namespace llvm
{
int DisableABIBreakingChecks;
}
#endif
void LLDBBackend::dump_variables()
{
using namespace lldb;
auto thread = _process->GetSelectedThread();
auto frame = thread.GetSelectedFrame();
if (!thread.IsValid() || !frame.IsValid())
{
return;
}
printf("Dumping Variables...\n");
if (!_last_frame || !_last_frame->IsEqual(frame))
{
printf("Frame changed!\n");
_last_frame = frame;
}
auto stream = SBStream{};
for (auto &[var, touched] : _locals)
{
touched = false;
stream.Clear();
var.GetExpressionPath(stream);
auto name = var.GetName();
if (!name)
{
name = "<null>";
}
printf("Var %.*s ('%s'): %lu\n", static_cast<int>(stream.GetSize()),
stream.GetData(), name, var.GetID());
stream.Clear();
var.GetDescription(stream);
printf(" -> Desc: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
printf(" -> Valid: %u\n", var.IsValid());
printf(" -> InScope: %u\n", var.IsInScope());
printf(" -> ValueDidChange: %u\n", var.GetValueDidChange());
printf(" -> IsSynthetic: %u\n", var.IsSynthetic());
printf(" -> IsDynamic: %u\n", var.IsDynamic());
printf(" -> Value: %s\n", var.GetValue());
auto loc = var.GetLocation();
if (!loc)
{
loc = "<null>";
}
printf(" -> Location: %s\n", loc);
/*auto static_var = var.GetStaticValue();
printf(" -> Static: '%s' %lu\n", static_var.GetName(), static_var.GetID());
printf(" -> Valid: %u\n", static_var.IsValid());
printf(" -> InScope: %u\n", static_var.IsInScope());
printf(" -> ValueDidChange: %u\n", static_var.GetValueDidChange());
printf(" -> IsSynthetic: %u\n", static_var.IsSynthetic());
printf(" -> IsDynamic: %u\n", static_var.IsDynamic());
printf(" -> Value: %s\n", static_var.GetValue());*/
}
auto fill_locals = _locals.empty();
// static includes globals and thread local values
// can't include them as we have no way of finding out
// whether a static is defined inside our scope or not
auto list = frame.GetVariables(true, true, false, true);
auto len = list.GetSize();
for (uint32_t i = 0; i < len; ++i)
{
auto var = list.GetValueAtIndex(i);
if (!var.IsValid())
{
continue;
}
/*for (auto &[prev_var, touched] : _locals) {
if (prev_var.)
}*/
stream.Clear();
var.GetDescription(stream);
printf("Var %u: %.*s\n", i, static_cast<int>(stream.GetSize()),
stream.GetData());
stream.Clear();
var.GetExpressionPath(stream);
printf("ExprPath: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
if (fill_locals)
{
_locals.push_back(std::make_pair(var, true));
}
auto var_frame = var.GetFrame();
stream.Clear();
var_frame.GetDescription(stream);
printf(" -> VarFrame: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
auto decl = var.GetDeclaration();
stream.Clear();
decl.GetDescription(stream);
printf(" -> Declaration: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
auto type_name = var.GetTypeName() ?: "";
auto type_display_name = var.GetDisplayTypeName() ?: "";
printf(" -> TypeName: '%s', DisplayName: '%s'\n", type_name,
type_display_name);
auto type = var.GetType();
auto type_filter = var.GetTypeFilter();
stream.Clear();
type.GetDescription(stream, eDescriptionLevelFull);
printf(" -> TypeDesc: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
stream.Clear();
type_filter.GetDescription(stream, eDescriptionLevelFull);
printf(" -> TypeFilter: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
stream.Clear();
var.GetTypeFormat().GetDescription(stream, eDescriptionLevelFull);
printf(" -> TypeFormat: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
stream.Clear();
type.GetDescription(stream, eDescriptionLevelVerbose);
printf(" -> Type: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
uint32_t num_fields = type.GetNumberOfFields();
printf(" -> NumberOfFields: %u\n", num_fields);
// TODO: look through typedef to check whether it is a primitive type
for (uint32_t i = 0; i < num_fields; ++i)
{
auto field = type.GetFieldAtIndex(i);
stream.Clear();
field.GetType().GetDescription(stream, eDescriptionLevelFull);
printf(" -> Field %u: '%s': %.*s\n", i, field.GetName(),
static_cast<int>(stream.GetSize()), stream.GetData());
}
std::string fmt{};
format_type(type, fmt);
printf(" -> Custom Type Info: %s\n", fmt.c_str());
}
}
bool LLDBBackend::step_into(bool source_step)
{
std::lock_guard g{_data_lock};
@@ -955,7 +1430,7 @@ void LLDBBackend::check_frame_changes()
}
}
void LLDBBackend::add_data_node(const data::DataNode &node)
void LLDBBackend::add_data_node(const data::source::Node &node)
{
std::lock_guard g{_data_lock};
@@ -972,14 +1447,14 @@ void LLDBBackend::add_data_node(const data::DataNode &node)
// TODO: make this somehow automatic
switch (node.type)
{
using enum data::DataNode::Type;
using enum data::source::Node::Type;
case data_source:
case source:
// nothing to do rn
break;
case disassemble:
{
auto src_id = std::get<data::Disassemble>(node.data).src_id;
auto src_id = std::get<data::source::Disassemble>(node.data).src_id;
if (!_data_dag.nodes.contains(src_id))
{
printf("Invalid add sequence\n");
@@ -990,7 +1465,7 @@ void LLDBBackend::add_data_node(const data::DataNode &node)
}
case line_entry:
{
auto src_id = std::get<data::LineEntry>(node.data).src_id;
auto src_id = std::get<data::source::LineEntry>(node.data).src_id;
if (!_data_dag.nodes.contains(src_id))
{
printf("Invalid add sequence\n");
@@ -1024,12 +1499,30 @@ void LLDBBackend::remove_data_node(size_t id)
}
_data_nodes.erase(it);
auto cache_it =
std::find_if(_cached_data_results.begin(), _cached_data_results.end(),
[id](const auto &el) { return el.id == id; });
if (cache_it != _cached_data_results.end())
std::vector<uint16_t> to_delete{};
to_delete.push_back(id);
size_t old_size = 0;
while (to_delete.size() != old_size)
{
_cached_data_results.erase(cache_it);
auto cur_size = to_delete.size();
for (size_t i = old_size; i < cur_size; ++i)
{
for (size_t j = 0; j < _data_res.size(); ++j)
{
if (_data_res[j] && _data_res[j]->parent_node == to_delete[i])
{
to_delete.push_back(j);
}
}
}
old_size = cur_size;
}
for (auto idx : to_delete)
{
this->remove_data_node(id);
_data_res[idx] = {};
}
_dag_linear_valid = false;
@@ -1055,103 +1548,169 @@ void LLDBBackend::check_data_changes()
exit(1);
}
const auto &result_it =
std::find_if(_cached_data_results.begin(), _cached_data_results.end(),
[id](const auto &el) { return el.id == id; });
auto new_res = this->calc_data_res(*node_it);
auto res_vec = std::vector<data::result::Node>{};
auto src_id_mapping = this->calc_data_res(*node_it, res_vec);
// TODO: for disasm it would be better to check if the bytes have changed
auto should_send = true;
if (result_it != _cached_data_results.end())
if (!res_vec.empty())
{
if (new_res.success == result_it->success
&& new_res.type == result_it->type)
// TODO: queue and send at once to prevent UI lag?
this->send_data_result(
{.nodes = std::move(res_vec), .src_node_id = src_id_mapping});
}
}
}
// TODO: call node src_node?
std::optional<std::pair<uint16_t, size_t>>
LLDBBackend::calc_data_res(const data::source::Node &node,
std::vector<data::result::Node> &data_res)
{
const auto find_node_for_src_id =
[this](size_t src_id) -> std::optional<uint16_t> {
for (uint16_t i = 0; i < this->_data_res.size(); ++i)
{
if (!this->_data_res[i])
{
if (new_res.data.size() == result_it->data.size())
continue;
}
if (this->_data_res[i]->src_id == src_id)
{
return i;
}
}
return {};
};
const auto get_free_res_slot = [this]() -> uint16_t {
for (uint16_t i = 0; i < this->_data_res.size(); ++i)
{
if (!this->_data_res[i])
{
return i;
}
}
auto idx = this->_data_res.size();
this->_data_res.resize(idx + 1);
return idx;
};
const auto check_single_res_changed =
[this, node, &data_res](
data::result::Node &&res) -> std::optional<std::pair<uint16_t, size_t>> {
auto val_changed = true;
if (this->_data_res.size() > res.idx && this->_data_res[res.idx])
{
auto &cached = *this->_data_res[res.idx];
if (cached.src_id == node.id)
{
if (!cached.child_node)
{
if (!std::memcmp(new_res.data.data(), result_it->data.data(),
new_res.data.size()))
if (cached.node.success == res.success
&& cached.node.type_id == res.type_id
&& cached.node.data == res.data)
{
should_send = false;
val_changed = false;
}
}
}
}
if (!should_send)
if (!val_changed)
{
continue;
return {};
}
if (result_it == _cached_data_results.end())
if (this->_data_res.size() <= res.idx)
{
_cached_data_results.push_back(new_res);
} else
{
*result_it = new_res;
this->_data_res.resize(res.idx + 1);
}
this->_data_res[res.idx] = CachedDataRes{.node = res,
.child_node = false,
.parent_node = 0,
.parent_member_id = 0,
.src_id = node.id};
// TODO: queue and send at once to prevent UI lag?
this->send_data_result(
BackToFront::DataResult{.result = std::move(new_res)});
}
}
auto idx = res.idx;
data_res.push_back(std::move(res));
return std::make_pair(idx, node.id);
};
dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
{
switch (node.type)
{
using enum data::DataNode::Type;
using enum data::source::Node::Type;
case data_source:
case source:
{
const auto &src_data = std::get<data::DataSource>(node.data);
const auto &src_data = std::get<data::source::Source>(node.data);
switch (src_data.type)
{
using enum data::DataSource::Type;
using enum data::source::Source::Type;
case reg:
{
const auto &info = std::get<data::DataSource::Reg>(src_data.data);
if (info.set >= _reg_sets.size()
|| info.idx >= _reg_sets[info.set].values.size())
uint16_t cache_idx = 0;
if (auto found_idx = find_node_for_src_id(node.id); found_idx)
{
return data::DataResult{
.id = node.id,
.success = false,
};
cache_idx = *found_idx;
} else
{
cache_idx = get_free_res_slot();
}
// TODO: these indices *could* (very not likely) be incorrect
// TODO: for now, pretend every register is u64
return data::DataResult{
.id = node.id,
.success = true,
.type = data::TypeInfo{.type = data::TypeInfo::Type::u64},
.data = _reg_sets[info.set].values[info.idx]};
auto res =
data::result::Node{.idx = cache_idx,
.type_id = data::type_info::TypeID::none(),
.success = false};
{
const auto &info =
std::get<data::source::Source::Reg>(src_data.data);
if (info.set < _reg_sets.size()
&& info.idx < _reg_sets[info.set].values.size())
{
res.success = true;
res.type_id = data::type_info::TypeID::u64();
// TODO: these indices *could* (very not likely) be incorrect
// TODO: for now, pretend every register is u64
res.data = *reinterpret_cast<const uint64_t *>(
_reg_sets[info.set].values[info.idx].data());
}
}
return check_single_res_changed(std::move(res));
}
case frame_ip:
{
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();
}
auto thread = _process->GetSelectedThread();
auto frame = thread.GetSelectedFrame();
if (!thread.IsValid() || !frame.IsValid())
{
return data::DataResult{
.id = node.id,
return check_single_res_changed(data::result::Node{
.idx = cache_idx,
.type_id = data::type_info::TypeID::none(),
.success = false,
};
});
}
uint64_t pc = frame.GetPC();
std::vector<uint8_t> out{};
out.resize(sizeof(uint64_t));
*reinterpret_cast<uint64_t *>(out.data()) = pc;
return data::DataResult{
.id = node.id,
return check_single_res_changed(data::result::Node{
.idx = cache_idx,
.type_id = data::type_info::TypeID::u64(),
.success = true,
.type = data::TypeInfo{.type = data::TypeInfo::Type::u64},
.data = std::move(out),
};
.data = pc,
});
}
}
break;
@@ -1160,22 +1719,37 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
case disassemble:
{
using namespace lldb;
size_t addr_id = std::get<data::Disassemble>(node.data).src_id;
const auto res_it =
std::find_if(_cached_data_results.begin(), _cached_data_results.end(),
[addr_id](const auto &el) { return el.id == addr_id; });
if (res_it == _cached_data_results.end() || !res_it->success)
uint16_t cache_idx = 0;
if (auto found_idx = find_node_for_src_id(node.id); found_idx)
{
return data::DataResult{.id = node.id, .success = false};
cache_idx = *found_idx;
} else
{
cache_idx = get_free_res_slot();
}
size_t addr_id = std::get<data::source::Disassemble>(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,
});
}
// TODO: for now only accept u64
if (res_it->type.type != data::TypeInfo::Type::u64)
if (_data_res[addr_idx]->node.type_id.type != data::type_info::Type::u64)
{
return data::DataResult{.id = node.id, .success = false};
return check_single_res_changed(data::result::Node{
.idx = cache_idx,
.type_id = data::type_info::TypeID::none(),
.success = false,
});
}
const auto pc = *reinterpret_cast<uint64_t *>(res_it->data.data());
const auto pc = std::get<uint64_t>(_data_res[addr_idx]->node.data);
auto sc = _target.ResolveSymbolContextForAddress(
SBAddress{pc, _target}, eSymbolContextFunction | eSymbolContextSymbol);
@@ -1288,31 +1862,47 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
std::copy(comm.begin(), comm.end(), out.begin() + insert_idx);
}
return data::DataResult{
.id = node.id,
return check_single_res_changed(data::result::Node{
.idx = cache_idx,
.type_id = data::type_info::TypeID::custom(),
.success = true,
.type = data::TypeInfo{.type = data::TypeInfo::Type::custom},
.data = std::move(out)};
.data = std::move(out),
});
}
case line_entry:
{
using namespace lldb;
size_t addr_id = std::get<data::LineEntry>(node.data).src_id;
const auto res_it =
std::find_if(_cached_data_results.begin(), _cached_data_results.end(),
[addr_id](const auto &el) { return el.id == addr_id; });
if (res_it == _cached_data_results.end() || !res_it->success)
uint16_t cache_idx = 0;
if (auto found_idx = find_node_for_src_id(node.id); found_idx)
{
return data::DataResult{.id = node.id, .success = false};
cache_idx = *found_idx;
} else
{
cache_idx = get_free_res_slot();
}
size_t addr_id = std::get<data::source::LineEntry>(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,
});
}
// TODO: for now only accept u64
if (res_it->type.type != data::TypeInfo::Type::u64)
if (_data_res[addr_idx]->node.type_id.type != data::type_info::Type::u64)
{
return data::DataResult{.id = node.id, .success = false};
return check_single_res_changed(data::result::Node{
.idx = cache_idx,
.type_id = data::type_info::TypeID::none(),
.success = false,
});
}
const auto addr = *reinterpret_cast<uint64_t *>(res_it->data.data());
const auto addr = std::get<uint64_t>(_data_res[addr_idx]->node.data);
auto sc = _target.ResolveSymbolContextForAddress(SBAddress{addr, _target},
eSymbolContextLineEntry);
@@ -1334,7 +1924,11 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
stream.GetData());*/
if (!le.IsValid() || !file_spec.IsValid())
{
return data::DataResult{.id = node.id, .success = false};
return check_single_res_changed(data::result::Node{
.idx = cache_idx,
.type_id = data::type_info::TypeID::none(),
.success = false,
});
}
// TODO: for now the instlist is serialized in a custom format
@@ -1359,11 +1953,12 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
*reinterpret_cast<uint32_t *>(out.data() + 4) = path_len;
std::memcpy(out.data() + 8, path_buf, path_len);
return data::DataResult{
.id = node.id,
return check_single_res_changed(data::result::Node{
.idx = cache_idx,
.type_id = data::type_info::TypeID::custom(),
.success = true,
.type = data::TypeInfo{.type = data::TypeInfo::Type::custom},
.data = std::move(out)};
.data = std::move(out),
});
}
}

View File

@@ -7,6 +7,7 @@
#include <string>
#include <thread>
#include <mutex>
#include <utility>
#include "util/dag.h"
@@ -47,6 +48,23 @@ namespace dbgui::backend
lldb::break_id_t lldb_id;
};
struct CachedDataRes
{
struct DisasmInfo
{
uint64_t range_start, range_end;
std::vector<uint8_t> bytes;
};
data::result::Node node;
bool child_node;
uint16_t parent_node;
uint16_t parent_member_id;
size_t src_id; // invalid = size_t::MAX
// std::variant<std::monostate, DisasmInfo> extra_info;
};
// TODO: source_init_file: false
LLDBBackend(std::string filename);
virtual ~LLDBBackend();
@@ -60,7 +78,7 @@ namespace dbgui::backend
void cont() override;
void pause() override;
void add_data_node(const data::DataNode &) override;
void add_data_node(const data::source::Node &) override;
void remove_data_node(uint64_t id) override;
void add_breakpoint(uint64_t addr, size_t id) override;
@@ -79,11 +97,14 @@ namespace dbgui::backend
void prepare_proc_info(BackToFront::InitialProcessInfo &,
std::vector<BackToFront::RegsChanged> &);
void dump_threads();
void dump_variables();
void check_reg_changes();
void check_thread_changes();
void check_frame_changes();
void check_data_changes();
data::DataResult calc_data_res(const data::DataNode &);
std::optional<std::pair<uint16_t, size_t>>
calc_data_res(const data::source::Node &,
std::vector<data::result::Node> &data_res);
std::string _filename;
lldb::SBDebugger _instance;
@@ -102,11 +123,16 @@ namespace dbgui::backend
uint16_t _selected_frame = 0;
uint16_t _selected_thread = 0;
util::DAG _data_dag = {};
std::vector<data::DataNode> _data_nodes = {};
std::vector<size_t> _dag_linear = {};
bool _dag_linear_valid = false;
std::vector<data::DataResult> _cached_data_results = {};
util::DAG _data_dag = {};
std::vector<data::source::Node> _data_nodes = {};
std::vector<size_t> _dag_linear = {};
bool _dag_linear_valid = false;
// util::DAG _data_res_dag = {};
std::vector<std::optional<CachedDataRes>> _data_res = {};
std::vector<std::pair<lldb::SBValue, bool>> _locals = {};
std::optional<lldb::SBFrame> _last_frame = {};
// std::unordered_map<size_t, uint16_t> _src_id_to_data_idx = {};
// std::vector<data::DataResult> _cached_data_results = {};
std::vector<Breakpoint> _breakpoints = {};
};

175
src/data.cpp Normal file
View File

@@ -0,0 +1,175 @@
#include "data.h"
#include <cassert>
using namespace dbgui::data;
void type_info::TypeInfo::format(const std::vector<TypeInfo> &types,
std::string &out)
{
switch (this->type)
{
using enum Type;
case none: out += "<none>"; break;
case _void: out += "void"; break;
case custom: out += "<custom>"; break;
case _bool: out += "bool"; break;
case u8: out += "u8"; break;
case u16: out += "u16"; break;
case u32: out += "u32"; break;
case u64: out += "u64"; break;
case u128: out += "u128"; break;
case i8: out += "i8"; break;
case i16: out += "i16"; break;
case i32: out += "i32"; break;
case i64: out += "i64"; break;
case i128: out += "i128"; break;
case f32: out += "f32"; break;
case f64: out += "f64"; break;
case f128: out += "f128"; break;
case array:
{
out += "array";
break;
}
case ptr:
{
out += "ptr";
break;
}
case complex:
{
out += "complex";
break;
}
case _union:
{
out += "union";
break;
}
case _enum:
{
out += "enum";
break;
}
}
out += " ";
out += this->name;
char buf[32];
std::snprintf(buf, sizeof(buf), " $%lu", this->byte_size);
out += buf;
if (this->type == Type::array)
{
out += " ";
std::get<TypeID>(this->member_types).format(out);
// TODO: calc member size?
} else if (this->type == Type::complex || this->type == Type::_union)
{
out += " {";
for (const auto &member : this->member_vec())
{
out += "\n";
out += member.name;
if (member.bitfield_size)
{
std::snprintf(buf, sizeof(buf), " +%lu:%u ", member.offset,
member.bitfield_size);
} else
{
std::snprintf(buf, sizeof(buf), " +%lu ", member.offset);
}
out += buf;
member.type_id.format(out);
out += ";";
}
out += "\n}";
} else if (this->type == Type::ptr)
{
std::get<TypeID>(this->member_types).format(out);
} else if (this->type == Type::_enum)
{
out += " ";
this->enum_base.format(out);
out += " {";
for (const auto &member : this->member_vec())
{
out += "\n ";
out += member.name;
std::snprintf(buf, sizeof(buf), " = %lu", member.enum_val);
out += buf;
out += ";";
}
out += "\n}";
}
out += ";";
}
void type_info::TypeID::format(std::string &out) const
{
switch (this->type)
{
using enum Type;
case none: out += "<none>"; return;
case _void: out += "void"; return;
case custom: out += "<custom>"; return;
case _bool: out += "bool"; return;
case u8: out += "u8"; return;
case u16: out += "u16"; return;
case u32: out += "u32"; return;
case u64: out += "u64"; return;
case u128: out += "u128"; return;
case i8: out += "i8"; return;
case i16: out += "i16"; return;
case i32: out += "i32"; return;
case i64: out += "i64"; return;
case i128: out += "i128"; return;
case f32: out += "f32"; return;
case f64: out += "f64"; return;
case f128: out += "f128"; return;
case array:
{
char buf[32];
std::snprintf(buf, sizeof(buf), "array #%u", this->idx);
out += buf;
return;
}
case ptr:
{
out += "ptr ";
if (sub_type == ptr)
{
char buf[32];
std::snprintf(buf, sizeof(buf), "#%u", this->idx);
out += buf;
} else
{
auto tmp_id = TypeID{.type = sub_type, .idx = idx};
tmp_id.format(out);
}
return;
}
case complex:
{
char buf[32];
std::snprintf(buf, sizeof(buf), "complex #%u", this->idx);
out += buf;
return;
}
case _union:
{
char buf[32];
std::snprintf(buf, sizeof(buf), "union #%u", this->idx);
out += buf;
return;
}
case _enum:
{
char buf[32];
std::snprintf(buf, sizeof(buf), "enum #%u", this->idx);
out += buf;
return;
}
}
}

View File

@@ -4,10 +4,11 @@
#include <cstdint>
#include <variant>
#include <vector>
#include <string>
namespace dbgui::data
{
struct TypeInfo
/*struct TypeInfo
{
enum class Type
{
@@ -97,6 +98,231 @@ namespace dbgui::data
bool success;
TypeInfo type;
std::vector<uint8_t> data;
};
};*/
namespace type_info
{
struct TypeInfo;
// TODO: vector types
enum class Type : uint32_t
{
none,
_void,
custom,
_bool,
u8,
u16,
u32,
u64,
u128,
i8,
i16,
i32,
i64,
i128,
f32,
f64,
f128,
array,
ptr,
complex,
_union,
_enum,
// if this grows to more than 32 elements, change the bitfield size in TypeID
};
struct TypeID
{
// TODO: just remove the bitfield?
Type type : 5;
// if type == ptr, then this is valid
Type sub_type : 5;
uint32_t idx : 22;
static TypeID none()
{
return TypeID{.type = Type::none, .sub_type = Type::none, .idx = 0};
}
static TypeID u64()
{
return TypeID{.type = Type::u64, .sub_type = Type::none, .idx = 0};
}
static TypeID custom()
{
return TypeID{.type = Type::custom, .sub_type = Type::none, .idx = 0};
}
static TypeID basic(Type type)
{
return TypeID{.type = type, .sub_type = Type::none, .idx = 0};
}
static TypeID ref(Type type, uint32_t idx)
{
return TypeID{.type = type, .sub_type = Type::none, .idx = idx};
}
void format(std::string &out) const;
bool is_basic() const
{
return type != Type::none && type != Type::custom && type != Type::array
&& type != Type::complex && type != Type::ptr
&& type != Type::_union && type != Type::_enum;
}
bool operator==(const TypeID &rhs) const
{
if (this->type != rhs.type)
{
return false;
}
if (this->type == Type::array || this->type == Type::complex
|| this->type == Type::custom || this->type == Type::_enum)
{
if (this->idx != rhs.idx)
{
return false;
}
}
if (this->type == Type::ptr)
{
if (this->sub_type != rhs.sub_type)
{
return false;
}
if (this->sub_type == Type::array || this->sub_type == Type::complex
|| this->sub_type == Type::custom)
{
if (this->idx != rhs.idx)
{
return false;
}
}
}
return true;
}
};
static_assert(sizeof(TypeID) == 4);
struct MemberInfo
{
std::string name;
TypeID type_id;
uint32_t bitfield_size;
// for bitfields, this is the bit offset
uint64_t offset;
uint64_t enum_val; // no support for enum with complex values
};
// TODO: need to add all the scope information somehow?
// or let the backend resolve the type names that are needed for casting
// and then just tell the frontend the type id here?
// TODO: this currently inlines the size of an array
// so for each different array size one TypeInfo would be generated
// do we want to inline this into the MemberInfo?
// TODO: how to handle ADTs a la Rust enums?
struct TypeInfo
{
Type type;
uint64_t byte_size;
TypeID enum_base;
std::string name;
// sike, lldb doesnt give this to us
// std::string def_file_path;
// uint32_t def_file_line;
// if type==ptr, then TypeID.type == ptr and TypeID.sub_type is what is pointed to
std::variant<std::monostate, std::vector<MemberInfo>, TypeID>
member_types;
auto &member_vec()
{
return std::get<std::vector<MemberInfo>>(this->member_types);
}
void format(const std::vector<TypeInfo> &types, std::string &out);
};
} // namespace type_info
// source DAG nodes
namespace source
{
struct Source
{
enum class Type : uint8_t
{
reg,
frame_ip, // binds the selected frame
locals, // binds to selected frame
// TODO: special IP/SP source? so that scope selection can apply to that?
// variable,
// const,
};
struct Reg
{
// TODO: identify through names?
uint16_t set;
uint16_t idx;
};
Type type;
std::variant<std::monostate, Reg> data;
};
struct Disassemble
{
// Node that provides the address to disassemble
// type must be u64
size_t src_id;
};
struct LineEntry
{
// Node that provides address to resolve
// must be u64
size_t src_id;
};
struct Node
{
enum class Type : uint8_t
{
source,
disassemble,
line_entry,
};
size_t id;
Type type;
// when adding something here, remember to update LLDBBackend::add_data_node
std::variant<Source, Disassemble, LineEntry> data;
};
} // namespace source
// result stuff
namespace result
{
struct Node
{
uint16_t idx;
// uint16_t gen;
type_info::TypeID type_id;
bool success;
std::variant<std::vector<uint8_t>, uint64_t, float, double> data;
const std::vector<uint8_t> &vec_data() const
{
return std::get<std::vector<uint8_t>>(this->data);
}
};
} // namespace result
} // namespace dbgui::data

View File

@@ -348,10 +348,37 @@ void Frontend::handle_msgs()
case data_result:
{
const auto &result = std::get<BackToFront::DataResult>(msg->data);
printf("Result ID: %lu\n", result.result.id);
for (auto &window : _windows)
for (size_t i = 0; i < result.nodes.size(); ++i)
{
window.handle_data_res(result);
uint16_t idx = result.nodes[i].idx;
if (this->target->data_res_nodes.size() <= idx)
{
this->target->data_res_nodes.resize(idx + 1);
}
this->target->data_res_nodes[idx] = std::move(result.nodes[i]);
}
if (result.src_node_id)
{
auto id = result.src_node_id->second;
auto found = false;
for (auto &entry : this->target->src_id_to_data_idx)
{
if (entry.first == id)
{
entry.second = result.src_node_id->first;
found = true;
break;
}
}
if (!found)
{
this->target->src_id_to_data_idx.push_back(
std::make_pair(id, result.src_node_id->first));
}
for (auto &window : _windows)
{
window.handle_source_updated(*this->target, id);
}
}
break;
}

View File

@@ -80,6 +80,34 @@ namespace dbgui::frontend
Target(std::string filename);
std::optional<uint16_t> data_idx_for_src_id(size_t id)
{
for (auto &[entry_id, idx] : this->src_id_to_data_idx)
{
if (entry_id == id)
{
return idx;
}
}
return {};
}
data::result::Node *data_node_for_src_id(size_t id)
{
auto idx = this->data_idx_for_src_id(id);
if (!idx)
{
return nullptr;
}
if (*idx >= data_res_nodes.size() || !data_res_nodes[*idx])
{
return nullptr;
}
return &*data_res_nodes[*idx];
}
TargetState state = TargetState::stopped;
std::string filename;
uint64_t id;
@@ -93,6 +121,8 @@ namespace dbgui::frontend
std::vector<std::optional<Thread>> threads;
std::vector<std::optional<Frame>> frames;
std::vector<Breakpoint> breakpoints;
std::vector<std::pair<size_t, uint16_t>> src_id_to_data_idx;
std::vector<std::optional<data::result::Node>> data_res_nodes;
std::shared_ptr<backend::Backend> backend = nullptr;
};

View File

@@ -468,17 +468,18 @@ bool DisasmWindow::draw(Frontend &frontend)
this->ip_src_id = frontend.target->data_node_id++;
this->disas_src_id = frontend.target->data_node_id++;
using namespace data::source;
frontend.target->backend->add_data_node(
data::DataNode{.id = this->ip_src_id,
.type = data::DataNode::Type::data_source,
.data = data::DataSource{
.type = data::DataSource::Type::frame_ip,
}});
Node{.id = this->ip_src_id,
.type = Node::Type::source,
.data = Source{
.type = Source::Type::frame_ip,
}});
frontend.target->backend->add_data_node(
data::DataNode{.id = this->disas_src_id,
.type = data::DataNode::Type::disassemble,
.data = data::Disassemble{.src_id = this->ip_src_id}});
Node{.id = this->disas_src_id,
.type = Node::Type::disassemble,
.data = Disassemble{.src_id = this->ip_src_id}});
first = false;
}
@@ -760,17 +761,18 @@ bool SourceWindow::draw(Frontend &frontend)
this->ip_src_id = frontend.target->data_node_id++;
this->line_entry_src_id = frontend.target->data_node_id++;
using namespace data::source;
frontend.target->backend->add_data_node(
data::DataNode{.id = this->ip_src_id,
.type = data::DataNode::Type::data_source,
.data = data::DataSource{
.type = data::DataSource::Type::frame_ip,
}});
Node{.id = this->ip_src_id,
.type = Node::Type::source,
.data = Source{
.type = Source::Type::frame_ip,
}});
frontend.target->backend->add_data_node(
data::DataNode{.id = this->line_entry_src_id,
.type = data::DataNode::Type::line_entry,
.data = data::LineEntry{.src_id = this->ip_src_id}});
Node{.id = this->line_entry_src_id,
.type = Node::Type::line_entry,
.data = LineEntry{.src_id = this->ip_src_id}});
first = false;
}
@@ -968,29 +970,29 @@ bool SourceWindow::draw(Frontend &frontend)
return false;
}
void Window::handle_data_res(const BackToFront::DataResult &result)
void Window::handle_source_updated(Target &target, size_t id)
{
switch (this->type)
{
using enum WindowType;
case disassembly:
std::get<DisasmWindow>(this->data).handle_data_res(result);
std::get<DisasmWindow>(this->data).handle_source_updated(target, id);
break;
case source:
std::get<SourceWindow>(this->data).handle_data_res(result);
std::get<SourceWindow>(this->data).handle_source_updated(target, id);
break;
default: break;
}
}
void DisasmWindow::handle_data_res(
const BackToFront::DataResult &result_wrapper)
void DisasmWindow::handle_source_updated(Target &target, size_t id)
{
const auto &result = result_wrapper.result;
if (result.id == this->ip_src_id)
if (id == this->ip_src_id)
{
if (!result.success || result.type.type != data::TypeInfo::Type::u64)
auto result = target.data_node_for_src_id(id);
if (!result || !result->success
|| result->type_id.type != data::type_info::Type::u64)
{
this->ip_unsuccessful = true;
return;
@@ -998,17 +1000,19 @@ void DisasmWindow::handle_data_res(
this->ip_unsuccessful = false;
this->ip_changed = true;
this->ip = *reinterpret_cast<const uint64_t *>(result.data.data());
this->ip = std::get<uint64_t>(result->data);
printf("IP changed to %lX\n", this->ip);
return;
}
if (result.id != this->disas_src_id)
if (id != this->disas_src_id)
{
return;
}
if (!result.success || result.type.type != data::TypeInfo::Type::custom)
auto result = target.data_node_for_src_id(id);
if (!result || !result->success
|| result->type_id.type != data::type_info::Type::custom)
{
this->disas_unsuccessful = true;
this->insts.clear();
@@ -1035,21 +1039,20 @@ void DisasmWindow::handle_data_res(
uint8_t max_mnem_len = 0;
uint8_t max_op_len = 0;
while (idx < result.data.size())
const auto &data = result->vec_data();
while (idx < data.size())
{
if (result.data.size() - idx < 12)
if (data.size() - idx < 12)
{
break;
}
uint64_t addr = *reinterpret_cast<const uint64_t *>(&result.data[idx]);
uint8_t mnem_len =
*reinterpret_cast<const uint8_t *>(&result.data[idx + 8]);
uint8_t op_len = *reinterpret_cast<const uint8_t *>(&result.data[idx + 9]);
uint8_t comment_len =
*reinterpret_cast<const uint8_t *>(&result.data[idx + 10]);
uint64_t addr = *reinterpret_cast<const uint64_t *>(&data[idx]);
uint8_t mnem_len = *reinterpret_cast<const uint8_t *>(&data[idx + 8]);
uint8_t op_len = *reinterpret_cast<const uint8_t *>(&data[idx + 9]);
uint8_t comment_len = *reinterpret_cast<const uint8_t *>(&data[idx + 10]);
// dc about inst_len rn
idx += 12;
if (result.data.size() - idx < mnem_len + op_len + comment_len)
if (data.size() - idx < mnem_len + op_len + comment_len)
{
break;
}
@@ -1057,12 +1060,12 @@ void DisasmWindow::handle_data_res(
if (comment_len)
{
std::snprintf(buf, sizeof(buf), "%.*s%%*c%.*s%%.*c ; %.*s", mnem_len,
&result.data[idx], op_len, &result.data[idx + mnem_len],
comment_len, &result.data[idx + mnem_len + op_len]);
&data[idx], op_len, &data[idx + mnem_len], comment_len,
&data[idx + mnem_len + op_len]);
} else
{
std::snprintf(buf, sizeof(buf), "%.*s%%*c%.*s%%.*c", mnem_len,
&result.data[idx], op_len, &result.data[idx + mnem_len]);
std::snprintf(buf, sizeof(buf), "%.*s%%*c%.*s%%.*c", mnem_len, &data[idx],
op_len, &data[idx + mnem_len]);
}
idx += mnem_len + op_len + comment_len;
@@ -1086,22 +1089,22 @@ void DisasmWindow::handle_data_res(
this->max_op_len = max_op_len;
}
void SourceWindow::handle_data_res(
const BackToFront::DataResult &result_wrapper)
void SourceWindow::handle_source_updated(Target &target, size_t id)
{
const auto &result = result_wrapper.result;
if (result.id == this->ip_src_id)
if (id == this->ip_src_id)
{
// should not need to care
return;
}
if (result.id != this->line_entry_src_id)
if (id != this->line_entry_src_id)
{
return;
}
if (!result.success || result.type.type != data::TypeInfo::Type::custom)
auto result = target.data_node_for_src_id(id);
if (!result || !result->success
|| result->type_id.type != data::type_info::Type::custom)
{
this->lines.clear();
this->file_data.clear();
@@ -1117,7 +1120,8 @@ void SourceWindow::handle_data_res(
// char file_name[];
// };
if (result.data.size() < 8)
const auto &data = result->vec_data();
if (data.size() < 8)
{
this->lines.clear();
this->file_data.clear();
@@ -1126,10 +1130,9 @@ void SourceWindow::handle_data_res(
return;
}
const auto line = *reinterpret_cast<const uint32_t *>(result.data.data());
const auto name_len =
*reinterpret_cast<const uint32_t *>(result.data.data() + 4);
if (result.data.size() < 8 + name_len)
const auto line = *reinterpret_cast<const uint32_t *>(data.data());
const auto name_len = *reinterpret_cast<const uint32_t *>(data.data() + 4);
if (data.size() < 8 + name_len)
{
this->lines.clear();
this->file_data.clear();
@@ -1138,8 +1141,8 @@ void SourceWindow::handle_data_res(
return;
}
const auto file_view = std::string_view{
reinterpret_cast<const char *>(result.data.data() + 8), name_len};
const auto file_view =
std::string_view{reinterpret_cast<const char *>(data.data() + 8), name_len};
printf("New LE: %.*s:%u", static_cast<int>(file_view.size()),
file_view.data(), line);

View File

@@ -84,7 +84,7 @@ namespace dbgui::frontend
bool draw(Frontend &);
void handle_data_res(const BackToFront::DataResult &result);
void handle_source_updated(Target& target, size_t id);
std::string id;
bool open;
@@ -110,7 +110,7 @@ namespace dbgui::frontend
struct SourceWindow
{
bool draw(Frontend &);
void handle_data_res(const BackToFront::DataResult &result);
void handle_source_updated(Target& target, size_t id);
std::string id;
bool open;
@@ -141,6 +141,6 @@ namespace dbgui::frontend
static Window create_bp(size_t window_id);
static Window create_source(size_t window_id);
void handle_data_res(const BackToFront::DataResult &result);
void handle_source_updated(Target& target, size_t id);
};
} // namespace dbgui::frontend

View File

@@ -71,6 +71,7 @@ namespace dbgui
frame_removed,
// TODO: frame_moved, frame_added, etc?
data_result,
remove_data_node,
selected_frame_changed,
selected_thread_changed,
};
@@ -136,7 +137,15 @@ namespace dbgui
struct DataResult
{
data::DataResult result;
std::vector<data::result::Node> nodes;
// if present, node at idx maps to specified src_id
std::optional<std::pair<uint16_t, size_t>> src_node_id;
};
struct RemoveDataNode
{
uint16_t node;
};
struct SelectedFrameChanged
@@ -149,13 +158,19 @@ namespace dbgui
uint16_t idx;
};
struct TypeInfo
{
data::type_info::TypeInfo type_info;
uint32_t idx;
};
struct Msg
{
MsgType type;
std::variant<std::monostate, StateChange, IPChange, InitialProcessInfo,
RegsChanged, ThreadChange, ThreadRemoved, FrameChanged,
FrameRemoved, DataResult, SelectedFrameChanged,
SelectedThreadChanged>
SelectedThreadChanged, RemoveDataNode>
data;
};
} // namespace BackToFront

BIN
tmp/main

Binary file not shown.

View File

@@ -8,11 +8,13 @@ namespace {
}
void helper_fn();
void helper_fn2();
int main(int argc, char* argv[]) {
MyType tmp = MyType{10};
while (tmp.data != 0) {
helper_fn();
helper_fn2();
tmp.data--;
}
return 0;

View File

@@ -1,13 +1,33 @@
#include <cstdint>
#include <unistd.h>
#include <string>
namespace test {
enum MyEnum : uint16_t {
ENUM_VAL1 = 10,
ENUM_VAL2 = 5,
ENUM_VAL3 = 0xFFF,
};
struct MyType {
int data;
uint32_t test_bits : 20;
uint32_t test_bits2: 12;
MyEnum enum_val;
};
}
void helper_fn() {
test::MyType tmp = test::MyType{1};
std::string test = "Hello World";
sleep(tmp.data);
{
test::MyType tmp = test::MyType{2};
sleep(tmp.data);
}
}
void helper_fn2() {
test::MyType tmp = test::MyType{3};
sleep(tmp.data);
}