primitive locals
This commit is contained in:
@@ -58,7 +58,7 @@ 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)
|
||||
void Backend::send_remove_data_node(uint16_t idx)
|
||||
{
|
||||
auto msg = std::make_unique<BackToFront::Msg>(
|
||||
BackToFront::MsgType::remove_data_node,
|
||||
@@ -78,4 +78,10 @@ void Backend::send_selected_frame_changed(uint16_t idx)
|
||||
BackToFront::MsgType::selected_frame_changed,
|
||||
BackToFront::SelectedFrameChanged{.idx = idx});
|
||||
this->send_msg(std::move(msg));
|
||||
}
|
||||
void Backend::send_type_info(BackToFront::TypeInfo &&info)
|
||||
{
|
||||
auto msg = std::make_unique<BackToFront::Msg>(BackToFront::MsgType::type_info,
|
||||
std::move(info));
|
||||
this->send_msg(std::move(msg));
|
||||
}
|
||||
@@ -68,9 +68,10 @@ 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_remove_data_node(uint16_t);
|
||||
void send_selected_thread_changed(uint16_t idx);
|
||||
void send_selected_frame_changed(uint16_t idx);
|
||||
void send_type_info(BackToFront::TypeInfo &&);
|
||||
|
||||
private:
|
||||
std::mutex _back_front_msg_mutex;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -57,14 +57,38 @@ namespace dbgui::backend
|
||||
};
|
||||
|
||||
data::result::Node node;
|
||||
bool child_node;
|
||||
uint16_t parent_node;
|
||||
uint16_t parent_member_id;
|
||||
// bool child_node;
|
||||
bool no_delete_on_src_delete = false;
|
||||
|
||||
size_t src_id; // invalid = size_t::MAX
|
||||
// std::variant<std::monostate, DisasmInfo> extra_info;
|
||||
};
|
||||
|
||||
struct Local
|
||||
{
|
||||
lldb::SBValue lldb_val;
|
||||
std::optional<uint16_t> cached_node;
|
||||
};
|
||||
|
||||
struct VarCacheEntry
|
||||
{
|
||||
// for globals/statics compare location + expr path (bc unions)
|
||||
// for others compare location + expr path + symctx (without line entry)
|
||||
// TODO: this will repeatedly retrieve cyclic values
|
||||
// probably bad so maybe detect this some other way mby without expr paths?
|
||||
// but then we need to cache types somehow...
|
||||
|
||||
lldb::SBValue lldb_val;
|
||||
lldb::SBSymbolContext sym_ctx;
|
||||
std::string expr_path;
|
||||
// NOTE: when used is set to true, this means that the var was
|
||||
// diffed this step already and will no longer be so careful with changing this
|
||||
bool used;
|
||||
bool
|
||||
global_or_static; // need to only compare compilation unit and module
|
||||
std::optional<data::result::NodeIdx> cached_node;
|
||||
};
|
||||
|
||||
// TODO: source_init_file: false
|
||||
LLDBBackend(std::string filename);
|
||||
virtual ~LLDBBackend();
|
||||
@@ -102,7 +126,29 @@ namespace dbgui::backend
|
||||
void check_thread_changes();
|
||||
void check_frame_changes();
|
||||
void check_data_changes();
|
||||
std::optional<std::pair<uint16_t, size_t>>
|
||||
|
||||
// TODO: func to clear locals if they are no longer requested
|
||||
// node_id, did_change
|
||||
// std::pair<uint16_t, bool> build_locals();
|
||||
data::type_info::TypeID build_type_from_lldb_type(lldb::SBType &);
|
||||
bool is_type_equal(data::type_info::TypeID, lldb::SBType &);
|
||||
|
||||
std::optional<data::result::NodeIdx>
|
||||
build_nodes_for_var(lldb::SBValue &val,
|
||||
std::vector<data::result::Node> &to_send);
|
||||
data::result::NodeIdx
|
||||
build_nodes_for_var_uncached(lldb::SBValue &,
|
||||
std::vector<data::result::Node> &to_send);
|
||||
void build_nodes_for_var_cached(lldb::SBValue &,
|
||||
data::result::NodeIdx cache_idx,
|
||||
std::vector<data::result::Node> &to_send);
|
||||
|
||||
void clear_unused_vars_from_cache();
|
||||
|
||||
void delete_node_and_childs(uint16_t);
|
||||
data::result::NodeIdx get_free_data_node();
|
||||
|
||||
std::optional<std::pair<data::result::NodeIdx, size_t>>
|
||||
calc_data_res(const data::source::Node &,
|
||||
std::vector<data::result::Node> &data_res);
|
||||
|
||||
@@ -129,8 +175,17 @@ namespace dbgui::backend
|
||||
bool _dag_linear_valid = false;
|
||||
// util::DAG _data_res_dag = {};
|
||||
std::vector<std::optional<CachedDataRes>> _data_res = {};
|
||||
std::vector<std::pair<lldb::SBValue, bool>> _locals = {};
|
||||
std::optional<lldb::SBFrame> _last_frame = {};
|
||||
|
||||
std::vector<Local> _locals = {};
|
||||
std::optional<data::result::NodeIdx> _locals_node = {};
|
||||
|
||||
std::optional<lldb::SBFrame> _last_frame = {};
|
||||
|
||||
std::vector<
|
||||
std::optional<std::pair<lldb::SBType, data::type_info::TypeInfo>>>
|
||||
_types = {};
|
||||
|
||||
std::vector<VarCacheEntry> _var_cache{};
|
||||
// std::unordered_map<size_t, uint16_t> _src_id_to_data_idx = {};
|
||||
// std::vector<data::DataResult> _cached_data_results = {};
|
||||
|
||||
|
||||
81
src/data.cpp
81
src/data.cpp
@@ -41,30 +41,30 @@ void type_info::TypeInfo::format(const std::vector<TypeInfo> &types,
|
||||
out += "complex";
|
||||
break;
|
||||
}
|
||||
case _union:
|
||||
{
|
||||
out += "union";
|
||||
break;
|
||||
}
|
||||
case _enum:
|
||||
{
|
||||
out += "enum";
|
||||
break;
|
||||
}
|
||||
case alias:
|
||||
{
|
||||
out += "alias";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out += " ";
|
||||
out += this->name;
|
||||
out += this->display_name;
|
||||
char buf[32];
|
||||
std::snprintf(buf, sizeof(buf), " $%lu", this->byte_size);
|
||||
out += buf;
|
||||
|
||||
if (this->type == Type::array)
|
||||
if (this->type == Type::array || this->type == Type::alias)
|
||||
{
|
||||
out += " ";
|
||||
std::get<TypeID>(this->member_types).format(out);
|
||||
// TODO: calc member size?
|
||||
} else if (this->type == Type::complex || this->type == Type::_union)
|
||||
} else if (this->type == Type::complex)
|
||||
{
|
||||
out += " {";
|
||||
for (const auto &member : this->member_vec())
|
||||
@@ -135,6 +135,13 @@ void type_info::TypeID::format(std::string &out) const
|
||||
out += buf;
|
||||
return;
|
||||
}
|
||||
case alias:
|
||||
{
|
||||
char buf[32];
|
||||
std::snprintf(buf, sizeof(buf), "alias #%u", this->idx);
|
||||
out += buf;
|
||||
return;
|
||||
}
|
||||
case ptr:
|
||||
{
|
||||
out += "ptr ";
|
||||
@@ -157,13 +164,6 @@ void type_info::TypeID::format(std::string &out) const
|
||||
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];
|
||||
@@ -172,4 +172,55 @@ void type_info::TypeID::format(std::string &out) const
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t type_info::TypeID::byte_size(
|
||||
const std::vector<type_info::TypeInfo> &types) const
|
||||
{
|
||||
switch (this->type)
|
||||
{
|
||||
using enum Type;
|
||||
case none:
|
||||
case _void: return 0;
|
||||
case custom: assert(0); return 0;
|
||||
case _bool: return 1;
|
||||
case u8: return 1;
|
||||
case u16: return 2;
|
||||
case u32: return 4;
|
||||
case u64: return 8;
|
||||
case u128: return 16;
|
||||
case i8: return 1;
|
||||
case i16: return 2;
|
||||
case i32: return 4;
|
||||
case i64: return 8;
|
||||
case i128: return 16;
|
||||
case f32: return 4;
|
||||
case f64: return 8;
|
||||
case f128: return 16;
|
||||
case array:
|
||||
{
|
||||
return types[this->idx].byte_size;
|
||||
}
|
||||
case alias:
|
||||
{
|
||||
return std::get<type_info::TypeID>(types[this->idx].member_types)
|
||||
.byte_size(types);
|
||||
}
|
||||
case ptr:
|
||||
{
|
||||
// TODO: platform dependent
|
||||
return 8;
|
||||
}
|
||||
case complex:
|
||||
{
|
||||
return types[this->idx].byte_size;
|
||||
}
|
||||
case _enum:
|
||||
{
|
||||
return types[this->idx].byte_size;
|
||||
}
|
||||
}
|
||||
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
88
src/data.h
88
src/data.h
@@ -5,6 +5,7 @@
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
|
||||
namespace dbgui::data
|
||||
{
|
||||
@@ -127,8 +128,8 @@ namespace dbgui::data
|
||||
array,
|
||||
ptr,
|
||||
complex,
|
||||
_union,
|
||||
_enum,
|
||||
alias,
|
||||
// if this grows to more than 32 elements, change the bitfield size in TypeID
|
||||
};
|
||||
|
||||
@@ -136,8 +137,11 @@ namespace dbgui::data
|
||||
{
|
||||
// TODO: just remove the bitfield?
|
||||
Type type : 5;
|
||||
// if type == ptr, then this is valid
|
||||
// if type == ptr || type == alias, then this is valid
|
||||
Type sub_type : 5;
|
||||
// for alias, this is always valid
|
||||
// TODO: make this always point at self even in the ptr case
|
||||
// and use the embedded hint purely as a size hint?
|
||||
uint32_t idx : 22;
|
||||
|
||||
static TypeID none()
|
||||
@@ -167,11 +171,14 @@ namespace dbgui::data
|
||||
|
||||
void format(std::string &out) const;
|
||||
|
||||
// TODO: move this to target as its target dependent?
|
||||
size_t byte_size(const std::vector<TypeInfo> &) 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;
|
||||
&& type != Type::_enum && type != Type::alias;
|
||||
}
|
||||
|
||||
bool operator==(const TypeID &rhs) const
|
||||
@@ -198,13 +205,20 @@ namespace dbgui::data
|
||||
}
|
||||
|
||||
if (this->sub_type == Type::array || this->sub_type == Type::complex
|
||||
|| this->sub_type == Type::custom)
|
||||
|| this->sub_type == Type::custom || this->sub_type == Type::ptr
|
||||
|| this->sub_type == Type::alias)
|
||||
{
|
||||
if (this->idx != rhs.idx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (this->type == Type::alias)
|
||||
{
|
||||
if (this->sub_type != rhs.sub_type || this->idx != rhs.idx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -234,7 +248,8 @@ namespace dbgui::data
|
||||
Type type;
|
||||
uint64_t byte_size;
|
||||
TypeID enum_base;
|
||||
std::string name;
|
||||
std::string display_name;
|
||||
std::string internal_name;
|
||||
// sike, lldb doesnt give this to us
|
||||
// std::string def_file_path;
|
||||
// uint32_t def_file_line;
|
||||
@@ -247,6 +262,31 @@ namespace dbgui::data
|
||||
return std::get<std::vector<MemberInfo>>(this->member_types);
|
||||
}
|
||||
|
||||
TypeID type_id(uint32_t self_idx) const
|
||||
{
|
||||
if (type == Type::ptr)
|
||||
{
|
||||
auto inner_type = std::get<TypeID>(this->member_types);
|
||||
if (inner_type.type == Type::ptr)
|
||||
return TypeID{
|
||||
.type = Type::ptr, .sub_type = inner_type.type, .idx = self_idx};
|
||||
else
|
||||
{
|
||||
return TypeID{.type = Type::ptr,
|
||||
.sub_type = inner_type.type,
|
||||
.idx = inner_type.idx};
|
||||
}
|
||||
} else if (type == Type::alias)
|
||||
{
|
||||
auto inner_type = std::get<TypeID>(this->member_types);
|
||||
return TypeID{
|
||||
.type = type, .sub_type = inner_type.type, .idx = self_idx};
|
||||
} else
|
||||
{
|
||||
return TypeID{.type = type, .sub_type = Type::none, .idx = self_idx};
|
||||
}
|
||||
}
|
||||
|
||||
void format(const std::vector<TypeInfo> &types, std::string &out);
|
||||
};
|
||||
} // namespace type_info
|
||||
@@ -298,30 +338,64 @@ namespace dbgui::data
|
||||
source,
|
||||
disassemble,
|
||||
line_entry,
|
||||
locals,
|
||||
};
|
||||
|
||||
size_t id;
|
||||
Type type;
|
||||
// when adding something here, remember to update LLDBBackend::add_data_node
|
||||
std::variant<Source, Disassemble, LineEntry> data;
|
||||
std::variant<std::monostate, Source, Disassemble, LineEntry> data;
|
||||
};
|
||||
} // namespace source
|
||||
|
||||
// result stuff
|
||||
namespace result
|
||||
{
|
||||
using NodeIdx = uint16_t;
|
||||
|
||||
struct Node
|
||||
{
|
||||
uint16_t idx;
|
||||
NodeIdx idx;
|
||||
// uint16_t gen;
|
||||
type_info::TypeID type_id;
|
||||
bool success;
|
||||
std::variant<std::vector<uint8_t>, uint64_t, float, double> data;
|
||||
std::vector<NodeIdx> children;
|
||||
|
||||
const std::vector<uint8_t> &vec_data() const
|
||||
{
|
||||
return std::get<std::vector<uint8_t>>(this->data);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> &vec_data()
|
||||
{
|
||||
return std::get<std::vector<uint8_t>>(this->data);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T get_primitive(size_t off) const
|
||||
{
|
||||
if (data.index() == 0)
|
||||
{
|
||||
const auto &vec = this->vec_data();
|
||||
assert(vec.size() >= off + sizeof(T));
|
||||
return *reinterpret_cast<const T *>(vec.data() + off);
|
||||
} else
|
||||
{
|
||||
assert(off == 0);
|
||||
if constexpr (std::is_same_v<T, float>)
|
||||
{
|
||||
assert(data.index() == 2);
|
||||
return std::get<float>(data);
|
||||
} else if constexpr (std::is_same_v<T, double>)
|
||||
{
|
||||
assert(data.index() == 3);
|
||||
return std::get<double>(data);
|
||||
}
|
||||
assert(data.index() == 1);
|
||||
return static_cast<T>(std::get<uint64_t>(data));
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace result
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ Frontend::Frontend()
|
||||
_windows.push_back(Window::create_disas(window_id++));
|
||||
_windows.push_back(Window::create_bp(window_id++));
|
||||
_windows.push_back(Window::create_source(window_id++));
|
||||
_windows.push_back(Window::create_watch(window_id++));
|
||||
}
|
||||
|
||||
void Frontend::run_frame()
|
||||
@@ -400,6 +401,15 @@ void Frontend::handle_msgs()
|
||||
}
|
||||
break;
|
||||
}
|
||||
case type_info:
|
||||
{
|
||||
auto &info = std::get<BackToFront::TypeInfo>(msg->data);
|
||||
if (this->target->types.size() <= info.idx)
|
||||
{
|
||||
this->target->types.resize(info.idx + 1);
|
||||
}
|
||||
this->target->types[info.idx] = std::move(info.type_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +123,7 @@ namespace dbgui::frontend
|
||||
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::vector<data::type_info::TypeInfo> types;
|
||||
|
||||
std::shared_ptr<backend::Backend> backend = nullptr;
|
||||
};
|
||||
|
||||
@@ -28,6 +28,7 @@ bool Window::draw(Frontend &frontend)
|
||||
case source:
|
||||
return std::get<SourceWindow>(this->data).draw(frontend);
|
||||
break;
|
||||
case watch: return std::get<WatchWindow>(this->data).draw(frontend);
|
||||
default: printf("Unhandled window draw: %u\n", this->type); exit(1);
|
||||
}
|
||||
}
|
||||
@@ -88,6 +89,15 @@ Window Window::create_source(size_t id)
|
||||
SourceWindow{.id = id_str, .open = true, .first = true}};
|
||||
}
|
||||
|
||||
Window Window::create_watch(size_t id)
|
||||
{
|
||||
auto id_str = std::string{"Watch##"};
|
||||
id_str.append(std::to_string(id));
|
||||
|
||||
return Window{.type = WindowType::watch,
|
||||
.data = WatchWindow{.id = id_str, .open = true, .first = true}};
|
||||
}
|
||||
|
||||
bool RegWindow::draw(const Frontend &frontend)
|
||||
{
|
||||
//ImGui::SetNextWindowDockID(frontend.dock_id, ImGuiCond_Appearing);
|
||||
@@ -970,6 +980,278 @@ bool SourceWindow::draw(Frontend &frontend)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WatchWindow::draw(Frontend &frontend)
|
||||
{
|
||||
if (!ImGui::Begin(this->id.c_str()))
|
||||
{
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!frontend.target)
|
||||
{
|
||||
this->first = true;
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (frontend.target->state == TargetState::stopped
|
||||
|| frontend.target->state == TargetState::startup)
|
||||
{
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: we need to clean up the initializations/sync of the DAG somehow
|
||||
if (first)
|
||||
{
|
||||
this->locals_src_id = frontend.target->data_node_id++;
|
||||
using namespace data::source;
|
||||
frontend.target->backend->add_data_node(Node{.id = this->locals_src_id,
|
||||
.type = Node::Type::locals,
|
||||
.data = std::monostate{}});
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
// ImGuiTableFlags_SizingFixedFit
|
||||
if (ImGui::BeginTable("##Variables", 2))
|
||||
{
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Value");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
auto locals_idx = frontend.target->data_idx_for_src_id(this->locals_src_id);
|
||||
if (locals_idx)
|
||||
{
|
||||
const auto &local_node = *frontend.target->data_res_nodes[*locals_idx];
|
||||
const auto &locals_data = local_node.vec_data();
|
||||
size_t cur_off = 0;
|
||||
size_t cur_idx = 0;
|
||||
while (cur_off < locals_data.size())
|
||||
{
|
||||
if (cur_off + 2 > locals_data.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
auto str_len =
|
||||
*reinterpret_cast<const uint16_t *>(locals_data.data() + cur_off);
|
||||
if (cur_off + 2 + str_len > locals_data.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto name = std::string_view{
|
||||
reinterpret_cast<const char *>(locals_data.data() + cur_off + 2),
|
||||
str_len};
|
||||
auto node_idx = local_node.children[cur_idx];
|
||||
this->draw_value(frontend,
|
||||
frontend.target->data_res_nodes[node_idx]->type_id,
|
||||
name, node_idx, 0);
|
||||
|
||||
cur_idx += 1;
|
||||
cur_off += 2 + str_len;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void WatchWindow::draw_value(Frontend &frontend,
|
||||
data::type_info::TypeID type_id,
|
||||
std::string_view name,
|
||||
data::result::NodeIdx node_idx, size_t off)
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
const auto &node_opt = frontend.target->data_res_nodes[node_idx];
|
||||
if (!node_opt || !node_opt->success)
|
||||
{
|
||||
ImGui::TextUnformatted(name.begin(), name.end());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextDisabled("<unavailable>");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &node = *node_opt;
|
||||
using namespace data::type_info;
|
||||
while (type_id.type == Type::alias)
|
||||
{
|
||||
type_id =
|
||||
std::get<TypeID>(frontend.target->types[type_id.idx].member_types);
|
||||
}
|
||||
|
||||
auto tree_open = false;
|
||||
auto pop_id = false;
|
||||
if (type_id.type == Type::complex || type_id.type == Type::array)
|
||||
{
|
||||
ImGui::PushID(name.begin(), name.end());
|
||||
pop_id = true;
|
||||
|
||||
tree_open =
|
||||
ImGui::TreeNodeEx((void *)node_idx, ImGuiTreeNodeFlags_SpanFullWidth,
|
||||
"%.*s", static_cast<int>(name.size()), name.data());
|
||||
|
||||
if (tree_open)
|
||||
{
|
||||
if (type_id.type == Type::complex)
|
||||
{
|
||||
const auto &members = frontend.target->types[type_id.idx].member_vec();
|
||||
for (size_t i = 0; i < members.size(); ++i)
|
||||
{
|
||||
const auto &member = members[i];
|
||||
// Skip bitfield
|
||||
if (member.bitfield_size)
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("%s", member.name.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextDisabled("<bitfield not supported>");
|
||||
continue;
|
||||
}
|
||||
|
||||
this->draw_value(frontend, member.type_id, member.name, node_idx,
|
||||
off + member.offset);
|
||||
}
|
||||
} else
|
||||
{
|
||||
// array
|
||||
auto member_ty_id =
|
||||
std::get<TypeID>(frontend.target->types[type_id.idx].member_types);
|
||||
auto member_size = member_ty_id.byte_size(frontend.target->types);
|
||||
size_t el_count =
|
||||
frontend.target->types[type_id.idx].byte_size / member_size;
|
||||
|
||||
char buf[32];
|
||||
size_t member_off = 0;
|
||||
for (size_t i = 0; i < el_count; ++i)
|
||||
{
|
||||
std::snprintf(buf, sizeof(buf), "[%lu]", i);
|
||||
this->draw_value(frontend, member_ty_id, buf, node_idx,
|
||||
off + member_off);
|
||||
member_off += member_size;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
} else
|
||||
{
|
||||
ImGui::TextUnformatted(name.begin(), name.end());
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
switch (type_id.type)
|
||||
{
|
||||
using enum Type;
|
||||
case i8:
|
||||
{
|
||||
auto val = node.get_primitive<int8_t>(off);
|
||||
if (std::isprint(val))
|
||||
{
|
||||
ImGui::Text("%c", val);
|
||||
} else
|
||||
{
|
||||
ImGui::Text("%d", val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case u8: ImGui::Text("%lu", node.get_primitive<uint8_t>(off)); break;
|
||||
case u16: ImGui::Text("%lu", node.get_primitive<uint16_t>(off)); break;
|
||||
case u32: ImGui::Text("%lu", node.get_primitive<uint32_t>(off)); break;
|
||||
case u64: ImGui::Text("%lu", node.get_primitive<uint64_t>(off)); break;
|
||||
case i16: ImGui::Text("%ld", node.get_primitive<int16_t>(off)); break;
|
||||
case i32: ImGui::Text("%ld", node.get_primitive<int32_t>(off)); break;
|
||||
case i64: ImGui::Text("%ld", node.get_primitive<int64_t>(off)); break;
|
||||
case _bool:
|
||||
{
|
||||
auto val = node.get_primitive<uint8_t>(off);
|
||||
if (val)
|
||||
{
|
||||
ImGui::TextUnformatted("true");
|
||||
} else
|
||||
{
|
||||
ImGui::TextUnformatted("false");
|
||||
}
|
||||
break;
|
||||
}
|
||||
// TODO
|
||||
case f128:
|
||||
case i128:
|
||||
case u128:
|
||||
{
|
||||
const auto &data = node.vec_data();
|
||||
const auto lo = node.get_primitive<uint64_t>(off);
|
||||
const auto hi = node.get_primitive<uint64_t>(off + 8);
|
||||
ImGui::Text("%16lX-%16lX", hi, lo);
|
||||
break;
|
||||
}
|
||||
case f32:
|
||||
{
|
||||
ImGui::Text("%f", node.get_primitive<float>(off));
|
||||
break;
|
||||
}
|
||||
case f64:
|
||||
{
|
||||
ImGui::Text("%f", node.get_primitive<double>(off));
|
||||
break;
|
||||
}
|
||||
case ptr:
|
||||
{
|
||||
// TODO: ptr size
|
||||
ImGui::Text("%lX", node.get_primitive<uint64_t>(off));
|
||||
break;
|
||||
}
|
||||
case _enum:
|
||||
{
|
||||
// TODO: robustness
|
||||
uint64_t val;
|
||||
const auto byte_size = frontend.target->types[type_id.idx].byte_size;
|
||||
if (byte_size == 8)
|
||||
{
|
||||
val = node.get_primitive<uint64_t>(off);
|
||||
} else if (byte_size == 4)
|
||||
{
|
||||
val = node.get_primitive<uint32_t>(off);
|
||||
} else if (byte_size == 2)
|
||||
{
|
||||
val = node.get_primitive<uint16_t>(off);
|
||||
} else if (byte_size == 1)
|
||||
{
|
||||
val = node.get_primitive<uint8_t>(off);
|
||||
} else
|
||||
{
|
||||
assert(0);
|
||||
val = 0;
|
||||
}
|
||||
const auto &members = frontend.target->types[type_id.idx].member_vec();
|
||||
auto printed = false;
|
||||
for (const auto &member : members)
|
||||
{
|
||||
if (val == member.enum_val)
|
||||
{
|
||||
ImGui::Text("%s", member.name.c_str());
|
||||
printed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!printed)
|
||||
{
|
||||
ImGui::Text("<invalid: %lu>", val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Window::handle_source_updated(Target &target, size_t id)
|
||||
{
|
||||
switch (this->type)
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace dbgui::frontend
|
||||
regs,
|
||||
source,
|
||||
memory,
|
||||
variables,
|
||||
watch,
|
||||
frames,
|
||||
threads,
|
||||
disassembly,
|
||||
@@ -84,7 +84,7 @@ namespace dbgui::frontend
|
||||
|
||||
bool draw(Frontend &);
|
||||
|
||||
void handle_source_updated(Target& target, size_t id);
|
||||
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_source_updated(Target& target, size_t id);
|
||||
void handle_source_updated(Target &target, size_t id);
|
||||
|
||||
std::string id;
|
||||
bool open;
|
||||
@@ -124,11 +124,25 @@ namespace dbgui::frontend
|
||||
std::vector<std::string_view> lines;
|
||||
};
|
||||
|
||||
struct WatchWindow
|
||||
{
|
||||
bool draw(Frontend &);
|
||||
void draw_value(Frontend &, data::type_info::TypeID, std::string_view name,
|
||||
data::result::NodeIdx node_idx, size_t off);
|
||||
// void handle_source_updated(Target& target, size_t id);
|
||||
|
||||
std::string id;
|
||||
bool open;
|
||||
bool first;
|
||||
|
||||
size_t locals_src_id;
|
||||
};
|
||||
|
||||
struct Window
|
||||
{
|
||||
WindowType type;
|
||||
std::variant<std::monostate, RegWindow, ThreadWindow, FrameWindow,
|
||||
DisasmWindow, BreakpointWindow, SourceWindow>
|
||||
DisasmWindow, BreakpointWindow, SourceWindow, WatchWindow>
|
||||
data;
|
||||
|
||||
// if true, window is closed and should be deleted
|
||||
@@ -140,7 +154,8 @@ namespace dbgui::frontend
|
||||
static Window create_disas(size_t window_id);
|
||||
static Window create_bp(size_t window_id);
|
||||
static Window create_source(size_t window_id);
|
||||
static Window create_watch(size_t window_id);
|
||||
|
||||
void handle_source_updated(Target& target, size_t id);
|
||||
void handle_source_updated(Target &target, size_t id);
|
||||
};
|
||||
} // namespace dbgui::frontend
|
||||
10
src/msg.h
10
src/msg.h
@@ -74,6 +74,8 @@ namespace dbgui
|
||||
remove_data_node,
|
||||
selected_frame_changed,
|
||||
selected_thread_changed,
|
||||
remove_src_id_mapping,
|
||||
type_info,
|
||||
};
|
||||
|
||||
struct StateChange
|
||||
@@ -148,6 +150,11 @@ namespace dbgui
|
||||
uint16_t node;
|
||||
};
|
||||
|
||||
struct RemoveSrcIDMapping
|
||||
{
|
||||
size_t src_id;
|
||||
};
|
||||
|
||||
struct SelectedFrameChanged
|
||||
{
|
||||
uint16_t idx;
|
||||
@@ -170,7 +177,8 @@ namespace dbgui
|
||||
std::variant<std::monostate, StateChange, IPChange, InitialProcessInfo,
|
||||
RegsChanged, ThreadChange, ThreadRemoved, FrameChanged,
|
||||
FrameRemoved, DataResult, SelectedFrameChanged,
|
||||
SelectedThreadChanged, RemoveDataNode>
|
||||
SelectedThreadChanged, RemoveDataNode, RemoveSrcIDMapping,
|
||||
TypeInfo>
|
||||
data;
|
||||
};
|
||||
} // namespace BackToFront
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
|
||||
uint32_t my_glob = 42;
|
||||
|
||||
namespace test {
|
||||
enum MyEnum : uint16_t {
|
||||
ENUM_VAL1 = 10,
|
||||
@@ -20,6 +22,8 @@ namespace test {
|
||||
void helper_fn() {
|
||||
test::MyType tmp = test::MyType{1};
|
||||
std::string test = "Hello World";
|
||||
uint32_t x = 25;
|
||||
unsigned int y = 30;
|
||||
sleep(tmp.data);
|
||||
{
|
||||
test::MyType tmp = test::MyType{2};
|
||||
|
||||
Reference in New Issue
Block a user