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/main.cpp src/frontend/frontend.cpp src/frontend/window.cpp
|
||||||
src/backend/backend.cpp
|
src/backend/backend.cpp
|
||||||
src/backend/lldb/lldb_backend.cpp
|
src/backend/lldb/lldb_backend.cpp
|
||||||
|
src/data.cpp
|
||||||
${IMGUI_SOURCES}
|
${IMGUI_SOURCES}
|
||||||
${DBGUI_HEADERS})
|
${DBGUI_HEADERS})
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,13 @@ void Backend::send_data_result(BackToFront::DataResult &&info)
|
|||||||
BackToFront::MsgType::data_result, std::move(info));
|
BackToFront::MsgType::data_result, std::move(info));
|
||||||
this->send_msg(std::move(msg));
|
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)
|
void Backend::send_selected_thread_changed(uint16_t idx)
|
||||||
{
|
{
|
||||||
auto msg = std::make_unique<BackToFront::Msg>(
|
auto msg = std::make_unique<BackToFront::Msg>(
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace dbgui::backend
|
|||||||
virtual void cont() = 0;
|
virtual void cont() = 0;
|
||||||
virtual void pause() = 0;
|
virtual void pause() = 0;
|
||||||
|
|
||||||
virtual void add_data_node(const data::DataNode &) = 0;
|
virtual void add_data_node(const data::source::Node &) = 0;
|
||||||
virtual void remove_data_node(uint64_t id) = 0;
|
virtual void remove_data_node(uint64_t id) = 0;
|
||||||
|
|
||||||
virtual void add_breakpoint(uint64_t addr, size_t id) = 0;
|
virtual void add_breakpoint(uint64_t addr, size_t id) = 0;
|
||||||
@@ -68,6 +68,7 @@ namespace dbgui::backend
|
|||||||
void send_frame_changed(BackToFront::FrameChanged &&);
|
void send_frame_changed(BackToFront::FrameChanged &&);
|
||||||
void send_frame_removed(BackToFront::FrameRemoved &&);
|
void send_frame_removed(BackToFront::FrameRemoved &&);
|
||||||
void send_data_result(BackToFront::DataResult &&);
|
void send_data_result(BackToFront::DataResult &&);
|
||||||
|
void remove_data_node(uint16_t);
|
||||||
void send_selected_thread_changed(uint16_t idx);
|
void send_selected_thread_changed(uint16_t idx);
|
||||||
void send_selected_frame_changed(uint16_t idx);
|
void send_selected_frame_changed(uint16_t idx);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
#include <lldb/API/SBInstruction.h>
|
#include <lldb/API/SBInstruction.h>
|
||||||
#include <lldb/API/SBBreakpoint.h>
|
#include <lldb/API/SBBreakpoint.h>
|
||||||
#include <lldb/API/SBBreakpointLocation.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 <filesystem>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cassert>
|
#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)
|
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)
|
bool LLDBBackend::step_into(bool source_step)
|
||||||
{
|
{
|
||||||
std::lock_guard g{_data_lock};
|
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};
|
std::lock_guard g{_data_lock};
|
||||||
|
|
||||||
@@ -972,14 +1447,14 @@ void LLDBBackend::add_data_node(const data::DataNode &node)
|
|||||||
// TODO: make this somehow automatic
|
// TODO: make this somehow automatic
|
||||||
switch (node.type)
|
switch (node.type)
|
||||||
{
|
{
|
||||||
using enum data::DataNode::Type;
|
using enum data::source::Node::Type;
|
||||||
|
|
||||||
case data_source:
|
case source:
|
||||||
// nothing to do rn
|
// nothing to do rn
|
||||||
break;
|
break;
|
||||||
case disassemble:
|
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))
|
if (!_data_dag.nodes.contains(src_id))
|
||||||
{
|
{
|
||||||
printf("Invalid add sequence\n");
|
printf("Invalid add sequence\n");
|
||||||
@@ -990,7 +1465,7 @@ void LLDBBackend::add_data_node(const data::DataNode &node)
|
|||||||
}
|
}
|
||||||
case line_entry:
|
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))
|
if (!_data_dag.nodes.contains(src_id))
|
||||||
{
|
{
|
||||||
printf("Invalid add sequence\n");
|
printf("Invalid add sequence\n");
|
||||||
@@ -1024,12 +1499,30 @@ void LLDBBackend::remove_data_node(size_t id)
|
|||||||
}
|
}
|
||||||
_data_nodes.erase(it);
|
_data_nodes.erase(it);
|
||||||
|
|
||||||
auto cache_it =
|
std::vector<uint16_t> to_delete{};
|
||||||
std::find_if(_cached_data_results.begin(), _cached_data_results.end(),
|
to_delete.push_back(id);
|
||||||
[id](const auto &el) { return el.id == id; });
|
size_t old_size = 0;
|
||||||
if (cache_it != _cached_data_results.end())
|
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;
|
_dag_linear_valid = false;
|
||||||
@@ -1055,103 +1548,169 @@ void LLDBBackend::check_data_changes()
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &result_it =
|
auto res_vec = std::vector<data::result::Node>{};
|
||||||
std::find_if(_cached_data_results.begin(), _cached_data_results.end(),
|
auto src_id_mapping = this->calc_data_res(*node_it, res_vec);
|
||||||
[id](const auto &el) { return el.id == id; });
|
|
||||||
auto new_res = this->calc_data_res(*node_it);
|
|
||||||
|
|
||||||
// TODO: for disasm it would be better to check if the bytes have changed
|
if (!res_vec.empty())
|
||||||
auto should_send = true;
|
|
||||||
if (result_it != _cached_data_results.end())
|
|
||||||
{
|
{
|
||||||
if (new_res.success == result_it->success
|
// TODO: queue and send at once to prevent UI lag?
|
||||||
&& new_res.type == result_it->type)
|
this->send_data_result(
|
||||||
{
|
{.nodes = std::move(res_vec), .src_node_id = src_id_mapping});
|
||||||
if (new_res.data.size() == result_it->data.size())
|
|
||||||
{
|
|
||||||
if (!std::memcmp(new_res.data.data(), result_it->data.data(),
|
|
||||||
new_res.data.size()))
|
|
||||||
{
|
|
||||||
should_send = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!should_send)
|
// 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])
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result_it == _cached_data_results.end())
|
if (this->_data_res[i]->src_id == src_id)
|
||||||
{
|
{
|
||||||
_cached_data_results.push_back(new_res);
|
return i;
|
||||||
} else
|
}
|
||||||
{
|
|
||||||
*result_it = new_res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: queue and send at once to prevent UI lag?
|
return {};
|
||||||
this->send_data_result(
|
};
|
||||||
BackToFront::DataResult{.result = std::move(new_res)});
|
|
||||||
}
|
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 (cached.node.success == res.success
|
||||||
|
&& cached.node.type_id == res.type_id
|
||||||
|
&& cached.node.data == res.data)
|
||||||
|
{
|
||||||
|
val_changed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!val_changed)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->_data_res.size() <= res.idx)
|
||||||
|
{
|
||||||
|
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};
|
||||||
|
|
||||||
|
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)
|
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)
|
switch (src_data.type)
|
||||||
{
|
{
|
||||||
using enum data::DataSource::Type;
|
using enum data::source::Source::Type;
|
||||||
|
|
||||||
case reg:
|
case reg:
|
||||||
{
|
{
|
||||||
const auto &info = std::get<data::DataSource::Reg>(src_data.data);
|
uint16_t cache_idx = 0;
|
||||||
if (info.set >= _reg_sets.size()
|
if (auto found_idx = find_node_for_src_id(node.id); found_idx)
|
||||||
|| info.idx >= _reg_sets[info.set].values.size())
|
|
||||||
{
|
{
|
||||||
return data::DataResult{
|
cache_idx = *found_idx;
|
||||||
.id = node.id,
|
} else
|
||||||
.success = false,
|
{
|
||||||
};
|
cache_idx = get_free_res_slot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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: these indices *could* (very not likely) be incorrect
|
||||||
// TODO: for now, pretend every register is u64
|
// TODO: for now, pretend every register is u64
|
||||||
return data::DataResult{
|
res.data = *reinterpret_cast<const uint64_t *>(
|
||||||
.id = node.id,
|
_reg_sets[info.set].values[info.idx].data());
|
||||||
.success = true,
|
}
|
||||||
.type = data::TypeInfo{.type = data::TypeInfo::Type::u64},
|
}
|
||||||
.data = _reg_sets[info.set].values[info.idx]};
|
|
||||||
|
return check_single_res_changed(std::move(res));
|
||||||
}
|
}
|
||||||
case frame_ip:
|
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 thread = _process->GetSelectedThread();
|
||||||
auto frame = thread.GetSelectedFrame();
|
auto frame = thread.GetSelectedFrame();
|
||||||
if (!thread.IsValid() || !frame.IsValid())
|
if (!thread.IsValid() || !frame.IsValid())
|
||||||
{
|
{
|
||||||
return data::DataResult{
|
return check_single_res_changed(data::result::Node{
|
||||||
.id = node.id,
|
.idx = cache_idx,
|
||||||
|
.type_id = data::type_info::TypeID::none(),
|
||||||
.success = false,
|
.success = false,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t pc = frame.GetPC();
|
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{
|
return check_single_res_changed(data::result::Node{
|
||||||
.id = node.id,
|
.idx = cache_idx,
|
||||||
|
.type_id = data::type_info::TypeID::u64(),
|
||||||
.success = true,
|
.success = true,
|
||||||
.type = data::TypeInfo{.type = data::TypeInfo::Type::u64},
|
.data = pc,
|
||||||
.data = std::move(out),
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1160,22 +1719,37 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
|
|||||||
case disassemble:
|
case disassemble:
|
||||||
{
|
{
|
||||||
using namespace lldb;
|
using namespace lldb;
|
||||||
size_t addr_id = std::get<data::Disassemble>(node.data).src_id;
|
uint16_t cache_idx = 0;
|
||||||
const auto res_it =
|
if (auto found_idx = find_node_for_src_id(node.id); found_idx)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
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
|
// 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(
|
auto sc = _target.ResolveSymbolContextForAddress(
|
||||||
SBAddress{pc, _target}, eSymbolContextFunction | eSymbolContextSymbol);
|
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);
|
std::copy(comm.begin(), comm.end(), out.begin() + insert_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data::DataResult{
|
return check_single_res_changed(data::result::Node{
|
||||||
.id = node.id,
|
.idx = cache_idx,
|
||||||
|
.type_id = data::type_info::TypeID::custom(),
|
||||||
.success = true,
|
.success = true,
|
||||||
.type = data::TypeInfo{.type = data::TypeInfo::Type::custom},
|
.data = std::move(out),
|
||||||
.data = std::move(out)};
|
});
|
||||||
}
|
}
|
||||||
case line_entry:
|
case line_entry:
|
||||||
{
|
{
|
||||||
using namespace lldb;
|
using namespace lldb;
|
||||||
size_t addr_id = std::get<data::LineEntry>(node.data).src_id;
|
uint16_t cache_idx = 0;
|
||||||
const auto res_it =
|
if (auto found_idx = find_node_for_src_id(node.id); found_idx)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
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
|
// 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},
|
auto sc = _target.ResolveSymbolContextForAddress(SBAddress{addr, _target},
|
||||||
eSymbolContextLineEntry);
|
eSymbolContextLineEntry);
|
||||||
@@ -1334,7 +1924,11 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
|
|||||||
stream.GetData());*/
|
stream.GetData());*/
|
||||||
if (!le.IsValid() || !file_spec.IsValid())
|
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
|
// 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;
|
*reinterpret_cast<uint32_t *>(out.data() + 4) = path_len;
|
||||||
std::memcpy(out.data() + 8, path_buf, path_len);
|
std::memcpy(out.data() + 8, path_buf, path_len);
|
||||||
|
|
||||||
return data::DataResult{
|
return check_single_res_changed(data::result::Node{
|
||||||
.id = node.id,
|
.idx = cache_idx,
|
||||||
|
.type_id = data::type_info::TypeID::custom(),
|
||||||
.success = true,
|
.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 <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "util/dag.h"
|
#include "util/dag.h"
|
||||||
|
|
||||||
@@ -47,6 +48,23 @@ namespace dbgui::backend
|
|||||||
lldb::break_id_t lldb_id;
|
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
|
// TODO: source_init_file: false
|
||||||
LLDBBackend(std::string filename);
|
LLDBBackend(std::string filename);
|
||||||
virtual ~LLDBBackend();
|
virtual ~LLDBBackend();
|
||||||
@@ -60,7 +78,7 @@ namespace dbgui::backend
|
|||||||
void cont() override;
|
void cont() override;
|
||||||
void pause() 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 remove_data_node(uint64_t id) override;
|
||||||
|
|
||||||
void add_breakpoint(uint64_t addr, size_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 &,
|
void prepare_proc_info(BackToFront::InitialProcessInfo &,
|
||||||
std::vector<BackToFront::RegsChanged> &);
|
std::vector<BackToFront::RegsChanged> &);
|
||||||
void dump_threads();
|
void dump_threads();
|
||||||
|
void dump_variables();
|
||||||
void check_reg_changes();
|
void check_reg_changes();
|
||||||
void check_thread_changes();
|
void check_thread_changes();
|
||||||
void check_frame_changes();
|
void check_frame_changes();
|
||||||
void check_data_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;
|
std::string _filename;
|
||||||
lldb::SBDebugger _instance;
|
lldb::SBDebugger _instance;
|
||||||
@@ -103,10 +124,15 @@ namespace dbgui::backend
|
|||||||
uint16_t _selected_thread = 0;
|
uint16_t _selected_thread = 0;
|
||||||
|
|
||||||
util::DAG _data_dag = {};
|
util::DAG _data_dag = {};
|
||||||
std::vector<data::DataNode> _data_nodes = {};
|
std::vector<data::source::Node> _data_nodes = {};
|
||||||
std::vector<size_t> _dag_linear = {};
|
std::vector<size_t> _dag_linear = {};
|
||||||
bool _dag_linear_valid = false;
|
bool _dag_linear_valid = false;
|
||||||
std::vector<data::DataResult> _cached_data_results = {};
|
// 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 = {};
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
228
src/data.h
228
src/data.h
@@ -4,10 +4,11 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace dbgui::data
|
namespace dbgui::data
|
||||||
{
|
{
|
||||||
struct TypeInfo
|
/*struct TypeInfo
|
||||||
{
|
{
|
||||||
enum class Type
|
enum class Type
|
||||||
{
|
{
|
||||||
@@ -97,6 +98,231 @@ namespace dbgui::data
|
|||||||
bool success;
|
bool success;
|
||||||
TypeInfo type;
|
TypeInfo type;
|
||||||
std::vector<uint8_t> data;
|
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
|
} // namespace dbgui::data
|
||||||
@@ -348,10 +348,37 @@ void Frontend::handle_msgs()
|
|||||||
case data_result:
|
case data_result:
|
||||||
{
|
{
|
||||||
const auto &result = std::get<BackToFront::DataResult>(msg->data);
|
const auto &result = std::get<BackToFront::DataResult>(msg->data);
|
||||||
printf("Result ID: %lu\n", result.result.id);
|
for (size_t i = 0; i < result.nodes.size(); ++i)
|
||||||
|
{
|
||||||
|
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)
|
for (auto &window : _windows)
|
||||||
{
|
{
|
||||||
window.handle_data_res(result);
|
window.handle_source_updated(*this->target, id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,34 @@ namespace dbgui::frontend
|
|||||||
|
|
||||||
Target(std::string filename);
|
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;
|
TargetState state = TargetState::stopped;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
@@ -93,6 +121,8 @@ namespace dbgui::frontend
|
|||||||
std::vector<std::optional<Thread>> threads;
|
std::vector<std::optional<Thread>> threads;
|
||||||
std::vector<std::optional<Frame>> frames;
|
std::vector<std::optional<Frame>> frames;
|
||||||
std::vector<Breakpoint> breakpoints;
|
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;
|
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->ip_src_id = frontend.target->data_node_id++;
|
||||||
this->disas_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(
|
frontend.target->backend->add_data_node(
|
||||||
data::DataNode{.id = this->ip_src_id,
|
Node{.id = this->ip_src_id,
|
||||||
.type = data::DataNode::Type::data_source,
|
.type = Node::Type::source,
|
||||||
.data = data::DataSource{
|
.data = Source{
|
||||||
.type = data::DataSource::Type::frame_ip,
|
.type = Source::Type::frame_ip,
|
||||||
}});
|
}});
|
||||||
|
|
||||||
frontend.target->backend->add_data_node(
|
frontend.target->backend->add_data_node(
|
||||||
data::DataNode{.id = this->disas_src_id,
|
Node{.id = this->disas_src_id,
|
||||||
.type = data::DataNode::Type::disassemble,
|
.type = Node::Type::disassemble,
|
||||||
.data = data::Disassemble{.src_id = this->ip_src_id}});
|
.data = Disassemble{.src_id = this->ip_src_id}});
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
@@ -760,17 +761,18 @@ bool SourceWindow::draw(Frontend &frontend)
|
|||||||
|
|
||||||
this->ip_src_id = frontend.target->data_node_id++;
|
this->ip_src_id = frontend.target->data_node_id++;
|
||||||
this->line_entry_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(
|
frontend.target->backend->add_data_node(
|
||||||
data::DataNode{.id = this->ip_src_id,
|
Node{.id = this->ip_src_id,
|
||||||
.type = data::DataNode::Type::data_source,
|
.type = Node::Type::source,
|
||||||
.data = data::DataSource{
|
.data = Source{
|
||||||
.type = data::DataSource::Type::frame_ip,
|
.type = Source::Type::frame_ip,
|
||||||
}});
|
}});
|
||||||
|
|
||||||
frontend.target->backend->add_data_node(
|
frontend.target->backend->add_data_node(
|
||||||
data::DataNode{.id = this->line_entry_src_id,
|
Node{.id = this->line_entry_src_id,
|
||||||
.type = data::DataNode::Type::line_entry,
|
.type = Node::Type::line_entry,
|
||||||
.data = data::LineEntry{.src_id = this->ip_src_id}});
|
.data = LineEntry{.src_id = this->ip_src_id}});
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
@@ -968,29 +970,29 @@ bool SourceWindow::draw(Frontend &frontend)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::handle_data_res(const BackToFront::DataResult &result)
|
void Window::handle_source_updated(Target &target, size_t id)
|
||||||
{
|
{
|
||||||
switch (this->type)
|
switch (this->type)
|
||||||
{
|
{
|
||||||
using enum WindowType;
|
using enum WindowType;
|
||||||
|
|
||||||
case disassembly:
|
case disassembly:
|
||||||
std::get<DisasmWindow>(this->data).handle_data_res(result);
|
std::get<DisasmWindow>(this->data).handle_source_updated(target, id);
|
||||||
break;
|
break;
|
||||||
case source:
|
case source:
|
||||||
std::get<SourceWindow>(this->data).handle_data_res(result);
|
std::get<SourceWindow>(this->data).handle_source_updated(target, id);
|
||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisasmWindow::handle_data_res(
|
void DisasmWindow::handle_source_updated(Target &target, size_t id)
|
||||||
const BackToFront::DataResult &result_wrapper)
|
|
||||||
{
|
{
|
||||||
const auto &result = result_wrapper.result;
|
if (id == this->ip_src_id)
|
||||||
if (result.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;
|
this->ip_unsuccessful = true;
|
||||||
return;
|
return;
|
||||||
@@ -998,17 +1000,19 @@ void DisasmWindow::handle_data_res(
|
|||||||
|
|
||||||
this->ip_unsuccessful = false;
|
this->ip_unsuccessful = false;
|
||||||
this->ip_changed = true;
|
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);
|
printf("IP changed to %lX\n", this->ip);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.id != this->disas_src_id)
|
if (id != this->disas_src_id)
|
||||||
{
|
{
|
||||||
return;
|
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->disas_unsuccessful = true;
|
||||||
this->insts.clear();
|
this->insts.clear();
|
||||||
@@ -1035,21 +1039,20 @@ void DisasmWindow::handle_data_res(
|
|||||||
|
|
||||||
uint8_t max_mnem_len = 0;
|
uint8_t max_mnem_len = 0;
|
||||||
uint8_t max_op_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;
|
break;
|
||||||
}
|
}
|
||||||
uint64_t addr = *reinterpret_cast<const uint64_t *>(&result.data[idx]);
|
uint64_t addr = *reinterpret_cast<const uint64_t *>(&data[idx]);
|
||||||
uint8_t mnem_len =
|
uint8_t mnem_len = *reinterpret_cast<const uint8_t *>(&data[idx + 8]);
|
||||||
*reinterpret_cast<const uint8_t *>(&result.data[idx + 8]);
|
uint8_t op_len = *reinterpret_cast<const uint8_t *>(&data[idx + 9]);
|
||||||
uint8_t op_len = *reinterpret_cast<const uint8_t *>(&result.data[idx + 9]);
|
uint8_t comment_len = *reinterpret_cast<const uint8_t *>(&data[idx + 10]);
|
||||||
uint8_t comment_len =
|
|
||||||
*reinterpret_cast<const uint8_t *>(&result.data[idx + 10]);
|
|
||||||
// dc about inst_len rn
|
// dc about inst_len rn
|
||||||
idx += 12;
|
idx += 12;
|
||||||
if (result.data.size() - idx < mnem_len + op_len + comment_len)
|
if (data.size() - idx < mnem_len + op_len + comment_len)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1057,12 +1060,12 @@ void DisasmWindow::handle_data_res(
|
|||||||
if (comment_len)
|
if (comment_len)
|
||||||
{
|
{
|
||||||
std::snprintf(buf, sizeof(buf), "%.*s%%*c%.*s%%.*c ; %.*s", mnem_len,
|
std::snprintf(buf, sizeof(buf), "%.*s%%*c%.*s%%.*c ; %.*s", mnem_len,
|
||||||
&result.data[idx], op_len, &result.data[idx + mnem_len],
|
&data[idx], op_len, &data[idx + mnem_len], comment_len,
|
||||||
comment_len, &result.data[idx + mnem_len + op_len]);
|
&data[idx + mnem_len + op_len]);
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
std::snprintf(buf, sizeof(buf), "%.*s%%*c%.*s%%.*c", mnem_len,
|
std::snprintf(buf, sizeof(buf), "%.*s%%*c%.*s%%.*c", mnem_len, &data[idx],
|
||||||
&result.data[idx], op_len, &result.data[idx + mnem_len]);
|
op_len, &data[idx + mnem_len]);
|
||||||
}
|
}
|
||||||
idx += mnem_len + op_len + comment_len;
|
idx += mnem_len + op_len + comment_len;
|
||||||
|
|
||||||
@@ -1086,22 +1089,22 @@ void DisasmWindow::handle_data_res(
|
|||||||
this->max_op_len = max_op_len;
|
this->max_op_len = max_op_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceWindow::handle_data_res(
|
void SourceWindow::handle_source_updated(Target &target, size_t id)
|
||||||
const BackToFront::DataResult &result_wrapper)
|
|
||||||
{
|
{
|
||||||
const auto &result = result_wrapper.result;
|
if (id == this->ip_src_id)
|
||||||
if (result.id == this->ip_src_id)
|
|
||||||
{
|
{
|
||||||
// should not need to care
|
// should not need to care
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.id != this->line_entry_src_id)
|
if (id != this->line_entry_src_id)
|
||||||
{
|
{
|
||||||
return;
|
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->lines.clear();
|
||||||
this->file_data.clear();
|
this->file_data.clear();
|
||||||
@@ -1117,7 +1120,8 @@ void SourceWindow::handle_data_res(
|
|||||||
// char file_name[];
|
// char file_name[];
|
||||||
// };
|
// };
|
||||||
|
|
||||||
if (result.data.size() < 8)
|
const auto &data = result->vec_data();
|
||||||
|
if (data.size() < 8)
|
||||||
{
|
{
|
||||||
this->lines.clear();
|
this->lines.clear();
|
||||||
this->file_data.clear();
|
this->file_data.clear();
|
||||||
@@ -1126,10 +1130,9 @@ void SourceWindow::handle_data_res(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto line = *reinterpret_cast<const uint32_t *>(result.data.data());
|
const auto line = *reinterpret_cast<const uint32_t *>(data.data());
|
||||||
const auto name_len =
|
const auto name_len = *reinterpret_cast<const uint32_t *>(data.data() + 4);
|
||||||
*reinterpret_cast<const uint32_t *>(result.data.data() + 4);
|
if (data.size() < 8 + name_len)
|
||||||
if (result.data.size() < 8 + name_len)
|
|
||||||
{
|
{
|
||||||
this->lines.clear();
|
this->lines.clear();
|
||||||
this->file_data.clear();
|
this->file_data.clear();
|
||||||
@@ -1138,8 +1141,8 @@ void SourceWindow::handle_data_res(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto file_view = std::string_view{
|
const auto file_view =
|
||||||
reinterpret_cast<const char *>(result.data.data() + 8), name_len};
|
std::string_view{reinterpret_cast<const char *>(data.data() + 8), name_len};
|
||||||
|
|
||||||
printf("New LE: %.*s:%u", static_cast<int>(file_view.size()),
|
printf("New LE: %.*s:%u", static_cast<int>(file_view.size()),
|
||||||
file_view.data(), line);
|
file_view.data(), line);
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ namespace dbgui::frontend
|
|||||||
|
|
||||||
bool draw(Frontend &);
|
bool draw(Frontend &);
|
||||||
|
|
||||||
void handle_data_res(const BackToFront::DataResult &result);
|
void handle_source_updated(Target& target, size_t id);
|
||||||
|
|
||||||
std::string id;
|
std::string id;
|
||||||
bool open;
|
bool open;
|
||||||
@@ -110,7 +110,7 @@ namespace dbgui::frontend
|
|||||||
struct SourceWindow
|
struct SourceWindow
|
||||||
{
|
{
|
||||||
bool draw(Frontend &);
|
bool draw(Frontend &);
|
||||||
void handle_data_res(const BackToFront::DataResult &result);
|
void handle_source_updated(Target& target, size_t id);
|
||||||
|
|
||||||
std::string id;
|
std::string id;
|
||||||
bool open;
|
bool open;
|
||||||
@@ -141,6 +141,6 @@ namespace dbgui::frontend
|
|||||||
static Window create_bp(size_t window_id);
|
static Window create_bp(size_t window_id);
|
||||||
static Window create_source(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
|
} // namespace dbgui::frontend
|
||||||
19
src/msg.h
19
src/msg.h
@@ -71,6 +71,7 @@ namespace dbgui
|
|||||||
frame_removed,
|
frame_removed,
|
||||||
// TODO: frame_moved, frame_added, etc?
|
// TODO: frame_moved, frame_added, etc?
|
||||||
data_result,
|
data_result,
|
||||||
|
remove_data_node,
|
||||||
selected_frame_changed,
|
selected_frame_changed,
|
||||||
selected_thread_changed,
|
selected_thread_changed,
|
||||||
};
|
};
|
||||||
@@ -136,7 +137,15 @@ namespace dbgui
|
|||||||
|
|
||||||
struct DataResult
|
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
|
struct SelectedFrameChanged
|
||||||
@@ -149,13 +158,19 @@ namespace dbgui
|
|||||||
uint16_t idx;
|
uint16_t idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TypeInfo
|
||||||
|
{
|
||||||
|
data::type_info::TypeInfo type_info;
|
||||||
|
uint32_t idx;
|
||||||
|
};
|
||||||
|
|
||||||
struct Msg
|
struct Msg
|
||||||
{
|
{
|
||||||
MsgType type;
|
MsgType type;
|
||||||
std::variant<std::monostate, StateChange, IPChange, InitialProcessInfo,
|
std::variant<std::monostate, StateChange, IPChange, InitialProcessInfo,
|
||||||
RegsChanged, ThreadChange, ThreadRemoved, FrameChanged,
|
RegsChanged, ThreadChange, ThreadRemoved, FrameChanged,
|
||||||
FrameRemoved, DataResult, SelectedFrameChanged,
|
FrameRemoved, DataResult, SelectedFrameChanged,
|
||||||
SelectedThreadChanged>
|
SelectedThreadChanged, RemoveDataNode>
|
||||||
data;
|
data;
|
||||||
};
|
};
|
||||||
} // namespace BackToFront
|
} // namespace BackToFront
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void helper_fn();
|
void helper_fn();
|
||||||
|
void helper_fn2();
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
MyType tmp = MyType{10};
|
MyType tmp = MyType{10};
|
||||||
while (tmp.data != 0) {
|
while (tmp.data != 0) {
|
||||||
helper_fn();
|
helper_fn();
|
||||||
|
helper_fn2();
|
||||||
tmp.data--;
|
tmp.data--;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
20
tmp/sec.cpp
20
tmp/sec.cpp
@@ -1,13 +1,33 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
|
enum MyEnum : uint16_t {
|
||||||
|
ENUM_VAL1 = 10,
|
||||||
|
ENUM_VAL2 = 5,
|
||||||
|
ENUM_VAL3 = 0xFFF,
|
||||||
|
};
|
||||||
|
|
||||||
struct MyType {
|
struct MyType {
|
||||||
int data;
|
int data;
|
||||||
|
uint32_t test_bits : 20;
|
||||||
|
uint32_t test_bits2: 12;
|
||||||
|
MyEnum enum_val;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void helper_fn() {
|
void helper_fn() {
|
||||||
test::MyType tmp = test::MyType{1};
|
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);
|
sleep(tmp.data);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user