From 9ab08de2436abeb87194d815778d83ce825032d6 Mon Sep 17 00:00:00 2001 From: T0b1 Date: Thu, 15 Jun 2023 02:25:57 +0200 Subject: [PATCH] WIP --- CMakeLists.txt | 1 + src/backend/backend.cpp | 7 + src/backend/debug_backend.h | 5 +- src/backend/lldb/lldb_backend.cpp | 783 ++++++++++++++++++++++++++---- src/backend/lldb/lldb_backend.h | 40 +- src/data.cpp | 175 +++++++ src/data.h | 230 ++++++++- src/frontend/frontend.cpp | 33 +- src/frontend/target.h | 30 ++ src/frontend/window.cpp | 109 +++-- src/frontend/window.h | 6 +- src/msg.h | 19 +- tmp/main | Bin 26664 -> 82400 bytes tmp/main.cpp | 2 + tmp/sec.cpp | 20 + 15 files changed, 1294 insertions(+), 166 deletions(-) create mode 100644 src/data.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ceb68d2..ce61185 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ set(DBGUI_SOURCES src/main.cpp src/frontend/frontend.cpp src/frontend/window.cpp src/backend/backend.cpp src/backend/lldb/lldb_backend.cpp + src/data.cpp ${IMGUI_SOURCES} ${DBGUI_HEADERS}) diff --git a/src/backend/backend.cpp b/src/backend/backend.cpp index 5df92db..2333cc8 100644 --- a/src/backend/backend.cpp +++ b/src/backend/backend.cpp @@ -58,6 +58,13 @@ void Backend::send_data_result(BackToFront::DataResult &&info) BackToFront::MsgType::data_result, std::move(info)); this->send_msg(std::move(msg)); } +void Backend::remove_data_node(uint16_t idx) +{ + auto msg = std::make_unique( + BackToFront::MsgType::remove_data_node, + std::move(BackToFront::RemoveDataNode{.node = idx})); + this->send_msg(std::move(msg)); +} void Backend::send_selected_thread_changed(uint16_t idx) { auto msg = std::make_unique( diff --git a/src/backend/debug_backend.h b/src/backend/debug_backend.h index 506790a..15c08aa 100644 --- a/src/backend/debug_backend.h +++ b/src/backend/debug_backend.h @@ -26,8 +26,8 @@ namespace dbgui::backend virtual void cont() = 0; virtual void pause() = 0; - virtual void add_data_node(const data::DataNode &) = 0; - virtual void remove_data_node(uint64_t id) = 0; + virtual void add_data_node(const data::source::Node &) = 0; + virtual void remove_data_node(uint64_t id) = 0; virtual void add_breakpoint(uint64_t addr, size_t id) = 0; virtual bool add_breakpoint(const char *file, uint32_t line, size_t id) = 0; @@ -68,6 +68,7 @@ namespace dbgui::backend void send_frame_changed(BackToFront::FrameChanged &&); void send_frame_removed(BackToFront::FrameRemoved &&); void send_data_result(BackToFront::DataResult &&); + void remove_data_node(uint16_t); void send_selected_thread_changed(uint16_t idx); void send_selected_frame_changed(uint16_t idx); diff --git a/src/backend/lldb/lldb_backend.cpp b/src/backend/lldb/lldb_backend.cpp index 2557970..b984f3c 100644 --- a/src/backend/lldb/lldb_backend.cpp +++ b/src/backend/lldb/lldb_backend.cpp @@ -10,6 +10,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -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 + 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 &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(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{}}); + 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 = ""; + } + 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{}}); + 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 = ""; + } + 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{}; + 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 = ""; 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{}; + 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 = ""; + } + printf("Var %.*s ('%s'): %lu\n", static_cast(stream.GetSize()), + stream.GetData(), name, var.GetID()); + stream.Clear(); + var.GetDescription(stream); + printf(" -> Desc: %.*s\n", static_cast(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 = ""; + } + 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(stream.GetSize()), + stream.GetData()); + + stream.Clear(); + var.GetExpressionPath(stream); + printf("ExprPath: %.*s\n", static_cast(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(stream.GetSize()), + stream.GetData()); + + auto decl = var.GetDeclaration(); + stream.Clear(); + decl.GetDescription(stream); + printf(" -> Declaration: %.*s\n", static_cast(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(stream.GetSize()), + stream.GetData()); + + stream.Clear(); + type_filter.GetDescription(stream, eDescriptionLevelFull); + printf(" -> TypeFilter: %.*s\n", static_cast(stream.GetSize()), + stream.GetData()); + + stream.Clear(); + var.GetTypeFormat().GetDescription(stream, eDescriptionLevelFull); + printf(" -> TypeFormat: %.*s\n", static_cast(stream.GetSize()), + stream.GetData()); + + stream.Clear(); + type.GetDescription(stream, eDescriptionLevelVerbose); + printf(" -> Type: %.*s\n", static_cast(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(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(node.data).src_id; + auto src_id = std::get(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(node.data).src_id; + auto src_id = std::get(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 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{}; + 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> + LLDBBackend::calc_data_res(const data::source::Node &node, + std::vector &data_res) +{ + const auto find_node_for_src_id = + [this](size_t src_id) -> std::optional { + 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> { + 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(node.data); + const auto &src_data = std::get(node.data); switch (src_data.type) { - using enum data::DataSource::Type; + using enum data::source::Source::Type; case reg: { - const auto &info = std::get(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(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( + _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 out{}; - out.resize(sizeof(uint64_t)); - *reinterpret_cast(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(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(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(res_it->data.data()); + const auto pc = std::get(_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(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(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(res_it->data.data()); + const auto addr = std::get(_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(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), + }); } } diff --git a/src/backend/lldb/lldb_backend.h b/src/backend/lldb/lldb_backend.h index 81dc67e..a64450e 100644 --- a/src/backend/lldb/lldb_backend.h +++ b/src/backend/lldb/lldb_backend.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "util/dag.h" @@ -47,6 +48,23 @@ namespace dbgui::backend lldb::break_id_t lldb_id; }; + struct CachedDataRes + { + struct DisasmInfo + { + uint64_t range_start, range_end; + std::vector 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 extra_info; + }; + // TODO: source_init_file: false LLDBBackend(std::string filename); virtual ~LLDBBackend(); @@ -60,7 +78,7 @@ namespace dbgui::backend void cont() override; void pause() override; - void add_data_node(const data::DataNode &) override; + void add_data_node(const data::source::Node &) override; void remove_data_node(uint64_t id) override; void add_breakpoint(uint64_t addr, size_t id) override; @@ -79,11 +97,14 @@ namespace dbgui::backend void prepare_proc_info(BackToFront::InitialProcessInfo &, std::vector &); void dump_threads(); + void dump_variables(); void check_reg_changes(); void check_thread_changes(); void check_frame_changes(); void check_data_changes(); - data::DataResult calc_data_res(const data::DataNode &); + std::optional> + calc_data_res(const data::source::Node &, + std::vector &data_res); std::string _filename; lldb::SBDebugger _instance; @@ -102,11 +123,16 @@ namespace dbgui::backend uint16_t _selected_frame = 0; uint16_t _selected_thread = 0; - util::DAG _data_dag = {}; - std::vector _data_nodes = {}; - std::vector _dag_linear = {}; - bool _dag_linear_valid = false; - std::vector _cached_data_results = {}; + util::DAG _data_dag = {}; + std::vector _data_nodes = {}; + std::vector _dag_linear = {}; + bool _dag_linear_valid = false; + // util::DAG _data_res_dag = {}; + std::vector> _data_res = {}; + std::vector> _locals = {}; + std::optional _last_frame = {}; + // std::unordered_map _src_id_to_data_idx = {}; + // std::vector _cached_data_results = {}; std::vector _breakpoints = {}; }; diff --git a/src/data.cpp b/src/data.cpp new file mode 100644 index 0000000..28e4705 --- /dev/null +++ b/src/data.cpp @@ -0,0 +1,175 @@ +#include "data.h" +#include + +using namespace dbgui::data; + +void type_info::TypeInfo::format(const std::vector &types, + std::string &out) +{ + switch (this->type) + { + using enum Type; + case none: out += ""; break; + case _void: out += "void"; break; + case custom: out += ""; 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(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(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 += ""; return; + case _void: out += "void"; return; + case custom: out += ""; 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; + } + } +} \ No newline at end of file diff --git a/src/data.h b/src/data.h index b12c010..53c9579 100644 --- a/src/data.h +++ b/src/data.h @@ -4,10 +4,11 @@ #include #include #include +#include namespace dbgui::data { - struct TypeInfo + /*struct TypeInfo { enum class Type { @@ -97,6 +98,231 @@ namespace dbgui::data bool success; TypeInfo type; std::vector 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, TypeID> + member_types; + + auto &member_vec() + { + return std::get>(this->member_types); + } + + void format(const std::vector &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 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 data; + }; + } // namespace source + + // result stuff + namespace result + { + struct Node + { + uint16_t idx; + // uint16_t gen; + type_info::TypeID type_id; + bool success; + std::variant, uint64_t, float, double> data; + + const std::vector &vec_data() const + { + return std::get>(this->data); + } + }; + } // namespace result } // namespace dbgui::data \ No newline at end of file diff --git a/src/frontend/frontend.cpp b/src/frontend/frontend.cpp index 727e64a..07b27aa 100644 --- a/src/frontend/frontend.cpp +++ b/src/frontend/frontend.cpp @@ -348,10 +348,37 @@ void Frontend::handle_msgs() case data_result: { const auto &result = std::get(msg->data); - printf("Result ID: %lu\n", result.result.id); - for (auto &window : _windows) + for (size_t i = 0; i < result.nodes.size(); ++i) { - window.handle_data_res(result); + uint16_t idx = result.nodes[i].idx; + if (this->target->data_res_nodes.size() <= idx) + { + this->target->data_res_nodes.resize(idx + 1); + } + this->target->data_res_nodes[idx] = std::move(result.nodes[i]); + } + if (result.src_node_id) + { + auto id = result.src_node_id->second; + auto found = false; + for (auto &entry : this->target->src_id_to_data_idx) + { + if (entry.first == id) + { + entry.second = result.src_node_id->first; + found = true; + break; + } + } + if (!found) + { + this->target->src_id_to_data_idx.push_back( + std::make_pair(id, result.src_node_id->first)); + } + for (auto &window : _windows) + { + window.handle_source_updated(*this->target, id); + } } break; } diff --git a/src/frontend/target.h b/src/frontend/target.h index 37a14a1..5b3cbdb 100644 --- a/src/frontend/target.h +++ b/src/frontend/target.h @@ -80,6 +80,34 @@ namespace dbgui::frontend Target(std::string filename); + std::optional data_idx_for_src_id(size_t id) + { + for (auto &[entry_id, idx] : this->src_id_to_data_idx) + { + if (entry_id == id) + { + return idx; + } + } + return {}; + } + + data::result::Node *data_node_for_src_id(size_t id) + { + auto idx = this->data_idx_for_src_id(id); + if (!idx) + { + return nullptr; + } + + if (*idx >= data_res_nodes.size() || !data_res_nodes[*idx]) + { + return nullptr; + } + + return &*data_res_nodes[*idx]; + } + TargetState state = TargetState::stopped; std::string filename; uint64_t id; @@ -93,6 +121,8 @@ namespace dbgui::frontend std::vector> threads; std::vector> frames; std::vector breakpoints; + std::vector> src_id_to_data_idx; + std::vector> data_res_nodes; std::shared_ptr backend = nullptr; }; diff --git a/src/frontend/window.cpp b/src/frontend/window.cpp index 6af8ce7..8fa3125 100644 --- a/src/frontend/window.cpp +++ b/src/frontend/window.cpp @@ -468,17 +468,18 @@ bool DisasmWindow::draw(Frontend &frontend) this->ip_src_id = frontend.target->data_node_id++; this->disas_src_id = frontend.target->data_node_id++; + using namespace data::source; frontend.target->backend->add_data_node( - data::DataNode{.id = this->ip_src_id, - .type = data::DataNode::Type::data_source, - .data = data::DataSource{ - .type = data::DataSource::Type::frame_ip, - }}); + Node{.id = this->ip_src_id, + .type = Node::Type::source, + .data = Source{ + .type = Source::Type::frame_ip, + }}); frontend.target->backend->add_data_node( - data::DataNode{.id = this->disas_src_id, - .type = data::DataNode::Type::disassemble, - .data = data::Disassemble{.src_id = this->ip_src_id}}); + Node{.id = this->disas_src_id, + .type = Node::Type::disassemble, + .data = Disassemble{.src_id = this->ip_src_id}}); first = false; } @@ -760,17 +761,18 @@ bool SourceWindow::draw(Frontend &frontend) this->ip_src_id = frontend.target->data_node_id++; this->line_entry_src_id = frontend.target->data_node_id++; + using namespace data::source; frontend.target->backend->add_data_node( - data::DataNode{.id = this->ip_src_id, - .type = data::DataNode::Type::data_source, - .data = data::DataSource{ - .type = data::DataSource::Type::frame_ip, - }}); + Node{.id = this->ip_src_id, + .type = Node::Type::source, + .data = Source{ + .type = Source::Type::frame_ip, + }}); frontend.target->backend->add_data_node( - data::DataNode{.id = this->line_entry_src_id, - .type = data::DataNode::Type::line_entry, - .data = data::LineEntry{.src_id = this->ip_src_id}}); + Node{.id = this->line_entry_src_id, + .type = Node::Type::line_entry, + .data = LineEntry{.src_id = this->ip_src_id}}); first = false; } @@ -968,29 +970,29 @@ bool SourceWindow::draw(Frontend &frontend) return false; } -void Window::handle_data_res(const BackToFront::DataResult &result) +void Window::handle_source_updated(Target &target, size_t id) { switch (this->type) { using enum WindowType; case disassembly: - std::get(this->data).handle_data_res(result); + std::get(this->data).handle_source_updated(target, id); break; case source: - std::get(this->data).handle_data_res(result); + std::get(this->data).handle_source_updated(target, id); break; default: break; } } -void DisasmWindow::handle_data_res( - const BackToFront::DataResult &result_wrapper) +void DisasmWindow::handle_source_updated(Target &target, size_t id) { - const auto &result = result_wrapper.result; - if (result.id == this->ip_src_id) + if (id == this->ip_src_id) { - if (!result.success || result.type.type != data::TypeInfo::Type::u64) + auto result = target.data_node_for_src_id(id); + if (!result || !result->success + || result->type_id.type != data::type_info::Type::u64) { this->ip_unsuccessful = true; return; @@ -998,17 +1000,19 @@ void DisasmWindow::handle_data_res( this->ip_unsuccessful = false; this->ip_changed = true; - this->ip = *reinterpret_cast(result.data.data()); + this->ip = std::get(result->data); printf("IP changed to %lX\n", this->ip); return; } - if (result.id != this->disas_src_id) + if (id != this->disas_src_id) { return; } - if (!result.success || result.type.type != data::TypeInfo::Type::custom) + auto result = target.data_node_for_src_id(id); + if (!result || !result->success + || result->type_id.type != data::type_info::Type::custom) { this->disas_unsuccessful = true; this->insts.clear(); @@ -1035,21 +1039,20 @@ void DisasmWindow::handle_data_res( uint8_t max_mnem_len = 0; uint8_t max_op_len = 0; - while (idx < result.data.size()) + const auto &data = result->vec_data(); + while (idx < data.size()) { - if (result.data.size() - idx < 12) + if (data.size() - idx < 12) { break; } - uint64_t addr = *reinterpret_cast(&result.data[idx]); - uint8_t mnem_len = - *reinterpret_cast(&result.data[idx + 8]); - uint8_t op_len = *reinterpret_cast(&result.data[idx + 9]); - uint8_t comment_len = - *reinterpret_cast(&result.data[idx + 10]); + uint64_t addr = *reinterpret_cast(&data[idx]); + uint8_t mnem_len = *reinterpret_cast(&data[idx + 8]); + uint8_t op_len = *reinterpret_cast(&data[idx + 9]); + uint8_t comment_len = *reinterpret_cast(&data[idx + 10]); // dc about inst_len rn idx += 12; - if (result.data.size() - idx < mnem_len + op_len + comment_len) + if (data.size() - idx < mnem_len + op_len + comment_len) { break; } @@ -1057,12 +1060,12 @@ void DisasmWindow::handle_data_res( if (comment_len) { std::snprintf(buf, sizeof(buf), "%.*s%%*c%.*s%%.*c ; %.*s", mnem_len, - &result.data[idx], op_len, &result.data[idx + mnem_len], - comment_len, &result.data[idx + mnem_len + op_len]); + &data[idx], op_len, &data[idx + mnem_len], comment_len, + &data[idx + mnem_len + op_len]); } else { - std::snprintf(buf, sizeof(buf), "%.*s%%*c%.*s%%.*c", mnem_len, - &result.data[idx], op_len, &result.data[idx + mnem_len]); + std::snprintf(buf, sizeof(buf), "%.*s%%*c%.*s%%.*c", mnem_len, &data[idx], + op_len, &data[idx + mnem_len]); } idx += mnem_len + op_len + comment_len; @@ -1086,22 +1089,22 @@ void DisasmWindow::handle_data_res( this->max_op_len = max_op_len; } -void SourceWindow::handle_data_res( - const BackToFront::DataResult &result_wrapper) +void SourceWindow::handle_source_updated(Target &target, size_t id) { - const auto &result = result_wrapper.result; - if (result.id == this->ip_src_id) + if (id == this->ip_src_id) { // should not need to care return; } - if (result.id != this->line_entry_src_id) + if (id != this->line_entry_src_id) { return; } - if (!result.success || result.type.type != data::TypeInfo::Type::custom) + auto result = target.data_node_for_src_id(id); + if (!result || !result->success + || result->type_id.type != data::type_info::Type::custom) { this->lines.clear(); this->file_data.clear(); @@ -1117,7 +1120,8 @@ void SourceWindow::handle_data_res( // char file_name[]; // }; - if (result.data.size() < 8) + const auto &data = result->vec_data(); + if (data.size() < 8) { this->lines.clear(); this->file_data.clear(); @@ -1126,10 +1130,9 @@ void SourceWindow::handle_data_res( return; } - const auto line = *reinterpret_cast(result.data.data()); - const auto name_len = - *reinterpret_cast(result.data.data() + 4); - if (result.data.size() < 8 + name_len) + const auto line = *reinterpret_cast(data.data()); + const auto name_len = *reinterpret_cast(data.data() + 4); + if (data.size() < 8 + name_len) { this->lines.clear(); this->file_data.clear(); @@ -1138,8 +1141,8 @@ void SourceWindow::handle_data_res( return; } - const auto file_view = std::string_view{ - reinterpret_cast(result.data.data() + 8), name_len}; + const auto file_view = + std::string_view{reinterpret_cast(data.data() + 8), name_len}; printf("New LE: %.*s:%u", static_cast(file_view.size()), file_view.data(), line); diff --git a/src/frontend/window.h b/src/frontend/window.h index 8004bda..da39aa1 100644 --- a/src/frontend/window.h +++ b/src/frontend/window.h @@ -84,7 +84,7 @@ namespace dbgui::frontend bool draw(Frontend &); - void handle_data_res(const BackToFront::DataResult &result); + void handle_source_updated(Target& target, size_t id); std::string id; bool open; @@ -110,7 +110,7 @@ namespace dbgui::frontend struct SourceWindow { bool draw(Frontend &); - void handle_data_res(const BackToFront::DataResult &result); + void handle_source_updated(Target& target, size_t id); std::string id; bool open; @@ -141,6 +141,6 @@ namespace dbgui::frontend static Window create_bp(size_t window_id); static Window create_source(size_t window_id); - void handle_data_res(const BackToFront::DataResult &result); + void handle_source_updated(Target& target, size_t id); }; } // namespace dbgui::frontend \ No newline at end of file diff --git a/src/msg.h b/src/msg.h index 00f08c8..98e1c7f 100644 --- a/src/msg.h +++ b/src/msg.h @@ -71,6 +71,7 @@ namespace dbgui frame_removed, // TODO: frame_moved, frame_added, etc? data_result, + remove_data_node, selected_frame_changed, selected_thread_changed, }; @@ -136,7 +137,15 @@ namespace dbgui struct DataResult { - data::DataResult result; + std::vector nodes; + + // if present, node at idx maps to specified src_id + std::optional> src_node_id; + }; + + struct RemoveDataNode + { + uint16_t node; }; struct SelectedFrameChanged @@ -149,13 +158,19 @@ namespace dbgui uint16_t idx; }; + struct TypeInfo + { + data::type_info::TypeInfo type_info; + uint32_t idx; + }; + struct Msg { MsgType type; std::variant + SelectedThreadChanged, RemoveDataNode> data; }; } // namespace BackToFront diff --git a/tmp/main b/tmp/main index d87e84741ca9737acba15f8f7555fca2b643724d..17ee69e6b0807553df8bd80ec7fd64af0ed28cee 100755 GIT binary patch literal 82400 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 literal 26664 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 dDL9~i2Uy1KGL740Ir}7*W2ijtGH{XNzX4 #include +#include namespace test { + enum MyEnum : uint16_t { + ENUM_VAL1 = 10, + ENUM_VAL2 = 5, + ENUM_VAL3 = 0xFFF, + }; + struct MyType { int data; + uint32_t test_bits : 20; + uint32_t test_bits2: 12; + MyEnum enum_val; }; } void helper_fn() { test::MyType tmp = test::MyType{1}; + std::string test = "Hello World"; + sleep(tmp.data); + { + test::MyType tmp = test::MyType{2}; + sleep(tmp.data); + } +} + +void helper_fn2() { + test::MyType tmp = test::MyType{3}; sleep(tmp.data); }