WIP
This commit is contained in:
@@ -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})
|
||||
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
175
src/data.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
230
src/data.h
230
src/data.h
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
19
src/msg.h
19
src/msg.h
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
20
tmp/sec.cpp
20
tmp/sec.cpp
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user