WIP
This commit is contained in:
@@ -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),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user