Compare commits
5 Commits
oldDAGBack
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e63179e14f | ||
|
|
70871a5671 | ||
|
|
e1be42052d | ||
|
|
894ff6a408 | ||
|
|
aa80d690d9 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
build/
|
||||
build*
|
||||
.idea/
|
||||
.vscode/
|
||||
.cache/
|
||||
|
||||
|
||||
@@ -33,10 +33,14 @@ set(DBGUI_HEADERS
|
||||
src/frontend/frontend.h
|
||||
src/frontend/target.h
|
||||
src/frontend/window.h
|
||||
src/frontend/window/watch_window.h
|
||||
src/msg.h)
|
||||
|
||||
set(DBGUI_SOURCES
|
||||
src/main.cpp src/frontend/frontend.cpp src/frontend/window.cpp
|
||||
src/main.cpp src/frontend/frontend.cpp
|
||||
src/frontend/window.cpp
|
||||
src/frontend/window/watch_window.cpp
|
||||
|
||||
src/backend/backend.cpp
|
||||
src/backend/lldb/lldb_backend.cpp
|
||||
src/data.cpp
|
||||
|
||||
@@ -141,6 +141,16 @@ void LLDBBackend::handle_state_change(lldb::StateType state)
|
||||
using namespace lldb;
|
||||
if (_first_run)
|
||||
{
|
||||
spdlog::trace("Got initial state change: {}", static_cast<uint32_t>(state));
|
||||
|
||||
if (state != lldb::StateType::eStateStopped)
|
||||
{
|
||||
// TODO: handle other stopped states, e.g. crashed
|
||||
spdlog::trace("Not handling initial state {}",
|
||||
static_cast<uint32_t>(state));
|
||||
return;
|
||||
}
|
||||
|
||||
_first_run = false;
|
||||
// TODO: do initialization
|
||||
// TODO: we should only do this when the process was stopped, no?
|
||||
@@ -280,26 +290,26 @@ void LLDBBackend::prepare_proc_info(
|
||||
auto frame = _process->GetThreadAtIndex(0).GetFrameAtIndex(0);
|
||||
const auto regs = frame.GetRegisters();
|
||||
/*const auto len = regs.GetSize();
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
auto reg_or_set = regs.GetValueAtIndex(i);
|
||||
if (reg_or_set.GetValueType() == eValueTypeRegister) {
|
||||
printf("Got register %s (%s) with value: %lX\n", reg_or_set.GetName(), reg_or_set.GetTypeName(), reg_or_set.GetValueAsUnsigned(0));
|
||||
info.reg_names.emplace_back(reg_or_set.GetName());
|
||||
continue;
|
||||
}
|
||||
printf("Got register set %s\n", reg_or_set.GetName());
|
||||
const auto is_gp_set = std::string_view{reg_or_set.GetName()} == "General Purpose Registers";
|
||||
for (uint32_t child_idx = 0; child_idx < reg_or_set.GetNumChildren(); ++child_idx) {
|
||||
auto reg = reg_or_set.GetChildAtIndex(child_idx);
|
||||
printf("Got register %s (%s) with value: %lX\n", reg.GetName(), reg.GetTypeName(), reg.GetValueAsUnsigned(0));
|
||||
if (is_gp_set && info.arch == Arch::x86_64 && reg.GetByteSize() < 8) {
|
||||
// skip non-full regs here
|
||||
printf(" -> Skipped\n");
|
||||
continue;
|
||||
}
|
||||
info.reg_names.emplace_back(reg.GetName());
|
||||
}
|
||||
}*/
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
auto reg_or_set = regs.GetValueAtIndex(i);
|
||||
if (reg_or_set.GetValueType() == eValueTypeRegister) {
|
||||
spdlog::trace("Got register {} ({}) with value: {:X}", reg_or_set.GetName(), reg_or_set.GetTypeName(), reg_or_set.GetValueAsUnsigned(0));
|
||||
//info.reg_names.emplace_back(reg_or_set.GetName());
|
||||
continue;
|
||||
}
|
||||
spdlog::trace("Got register set {}\n", reg_or_set.GetName());
|
||||
const auto is_gp_set = std::string_view{reg_or_set.GetName()} == "General Purpose Registers";
|
||||
for (uint32_t child_idx = 0; child_idx < reg_or_set.GetNumChildren(); ++child_idx) {
|
||||
auto reg = reg_or_set.GetChildAtIndex(child_idx);
|
||||
spdlog::trace("Got register {} ({}) with value: {:X}", reg.GetName(), reg.GetTypeName(), reg.GetValueAsUnsigned(0));
|
||||
if (is_gp_set && info.arch == Arch::x86_64 && reg.GetByteSize() < 8) {
|
||||
// skip non-full regs here
|
||||
spdlog::trace(" -> Skipped");
|
||||
continue;
|
||||
}
|
||||
//info.reg_names.emplace_back(reg.GetName());
|
||||
}
|
||||
}*/
|
||||
|
||||
if (info.arch == Arch::x86_64)
|
||||
{
|
||||
@@ -640,8 +650,8 @@ dbgui::data::type_info::TypeID
|
||||
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,
|
||||
.byte_size = type_copy.GetByteSize(),
|
||||
.display_name = std::string{display_name},
|
||||
.internal_name = std::string{internal_name},
|
||||
.member_types = std::vector<type_info::MemberInfo>{}});
|
||||
@@ -1522,6 +1532,29 @@ void LLDBBackend::add_data_node(const data::source::Node &node)
|
||||
_data_dag.add_edge(node.id, src_id);
|
||||
break;
|
||||
}
|
||||
case read_array:
|
||||
{
|
||||
const auto &arr_node = std::get<data::source::ReadAsArray>(node.data);
|
||||
auto src_id = arr_node.addr_src_id;
|
||||
if (!_data_dag.nodes.contains(src_id))
|
||||
{
|
||||
printf("Invalid add sequence\n");
|
||||
exit(1);
|
||||
}
|
||||
_data_dag.add_edge(node.id, src_id);
|
||||
|
||||
if (!arr_node.is_const_size)
|
||||
{
|
||||
auto size_id = arr_node.size_src_id;
|
||||
if (!_data_dag.nodes.contains(size_id))
|
||||
{
|
||||
printf("Invalid add sequence\n");
|
||||
exit(1);
|
||||
}
|
||||
_data_dag.add_edge(node.id, size_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_dag_linear_valid = false;
|
||||
@@ -1673,6 +1706,7 @@ std::optional<std::pair<uint16_t, size_t>>
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: reserve the slot
|
||||
auto idx = this->_data_res.size();
|
||||
this->_data_res.resize(idx + 1);
|
||||
return idx;
|
||||
@@ -2284,6 +2318,121 @@ std::optional<std::pair<uint16_t, size_t>>
|
||||
.success = true,
|
||||
.data = std::move(buf)});
|
||||
}
|
||||
case read_array:
|
||||
{
|
||||
using namespace lldb;
|
||||
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();
|
||||
// reserve the slot
|
||||
_data_res[cache_idx] = CachedDataRes{.src_id = node.id};
|
||||
}
|
||||
|
||||
const auto &arr_node = std::get<data::source::ReadAsArray>(node.data);
|
||||
auto res_node = data::result::Node{
|
||||
.idx = cache_idx,
|
||||
.type_id = data::type_info::TypeID::none(),
|
||||
.success = false,
|
||||
};
|
||||
|
||||
size_t addr_id = arr_node.addr_src_id;
|
||||
uint16_t addr_parent_idx = *find_node_for_src_id(addr_id);
|
||||
if (!_data_res[addr_parent_idx]
|
||||
|| !_data_res[addr_parent_idx]->node.success
|
||||
|| _data_res[addr_parent_idx]->node.children.size() != 1)
|
||||
{
|
||||
return check_single_res_changed(std::move(res_node));
|
||||
}
|
||||
|
||||
uint16_t addr_idx = _data_res[addr_parent_idx]->node.children[0];
|
||||
|
||||
if (!_data_res[addr_idx] || !_data_res[addr_idx]->node.success
|
||||
|| _data_res[addr_idx]->node.type_id.type
|
||||
!= data::type_info::Type::ptr)
|
||||
{
|
||||
return check_single_res_changed(std::move(res_node));
|
||||
}
|
||||
|
||||
if (!arr_node.is_const_size)
|
||||
{
|
||||
spdlog::trace("read_as_array: non-const size not supported");
|
||||
return check_single_res_changed(std::move(res_node));
|
||||
}
|
||||
|
||||
const auto addr = _data_res[addr_idx]->node.get_primitive<uint64_t>(0);
|
||||
spdlog::trace("read_as_array: addr: {:X}", addr);
|
||||
|
||||
const auto pointee_type_idx = _data_res[addr_idx]->node.type_id.idx;
|
||||
if (!_types[pointee_type_idx])
|
||||
{
|
||||
spdlog::trace("Found no pointee type for read_as_array");
|
||||
return check_single_res_changed(std::move(res_node));
|
||||
}
|
||||
|
||||
auto pointee_type = _types[pointee_type_idx]->first;
|
||||
if (pointee_type.IsArrayType())
|
||||
{
|
||||
// TODO: should we do this until there is no array left?
|
||||
pointee_type = pointee_type.GetArrayElementType();
|
||||
}
|
||||
spdlog::trace("read_as_array: pointee_type valid?: {}",
|
||||
pointee_type.IsValid());
|
||||
|
||||
std::string type_buf;
|
||||
format_type(pointee_type, type_buf);
|
||||
spdlog::trace("read_as_array: pointee_type: {}", type_buf);
|
||||
|
||||
auto arr_type = pointee_type.GetArrayType(arr_node.size_const);
|
||||
|
||||
spdlog::trace("read_as_array: arr_type valid?: {}", arr_type.IsValid());
|
||||
type_buf.clear();
|
||||
format_type(arr_type, type_buf);
|
||||
spdlog::trace("read_as_array: arr_type: {}", type_buf);
|
||||
|
||||
char buf[128], buf2[32];
|
||||
/*std::snprintf(buf2, sizeof(buf2), "%zX", node.id);
|
||||
std::snprintf(buf, sizeof(buf), "*((%s *)0x%zX)", arr_type.GetName(),
|
||||
addr);
|
||||
auto arr_val = _target.CreateValueFromExpression(buf2, buf);
|
||||
if (!arr_val.IsValid())
|
||||
{
|
||||
spdlog::trace("read_as_array: arr_val not valid");
|
||||
return check_single_res_changed(std::move(res_node));
|
||||
}*/
|
||||
std::snprintf(buf, sizeof(buf), "(char*)0x%zX", addr);
|
||||
auto tmp_val = _target.CreateValueFromExpression("arr_tmp", buf);
|
||||
if (!tmp_val.IsValid())
|
||||
{
|
||||
spdlog::trace("read_as_array: tmp_val not valid");
|
||||
return check_single_res_changed(std::move(res_node));
|
||||
}
|
||||
|
||||
std::snprintf(buf, sizeof(buf), "%zX", node.id);
|
||||
auto arr_val = tmp_val.CreateValueFromAddress(buf, addr, arr_type);
|
||||
if (!arr_val.IsValid())
|
||||
{
|
||||
spdlog::trace("read_as_array: arr_val not valid");
|
||||
return check_single_res_changed(std::move(res_node));
|
||||
}
|
||||
//arr_val = arr_val.Cast(arr_type);
|
||||
|
||||
auto res_idx = this->build_nodes_for_var(arr_val, data_res, false);
|
||||
if (!res_idx)
|
||||
{
|
||||
return check_single_res_changed(std::move(res_node));
|
||||
}
|
||||
|
||||
spdlog::trace(
|
||||
"Got res for {}: {} ({})", node.id, *res_idx,
|
||||
static_cast<uint8_t>(_data_res[*res_idx]->node.type_id.type));
|
||||
res_node.success = true;
|
||||
res_node.children.push_back(*res_idx);
|
||||
return check_single_res_changed(std::move(res_node));
|
||||
}
|
||||
}
|
||||
|
||||
printf("Unhandled data type\n");
|
||||
@@ -2659,8 +2808,8 @@ dbgui::data::type_info::TypeID
|
||||
auto base_id = this->build_type_from_lldb_type(base_type);
|
||||
_types.emplace_back(std::make_pair(
|
||||
lldb_type, TypeInfo{.type = Type::_enum,
|
||||
.byte_size = lldb_type.GetByteSize(),
|
||||
.enum_base = base_id,
|
||||
.byte_size = lldb_type.GetByteSize(),
|
||||
.display_name = std::string{display_name},
|
||||
.internal_name = std::string{internal_name},
|
||||
.member_types = std::vector<MemberInfo>{}}));
|
||||
|
||||
12
src/data.h
12
src/data.h
@@ -355,6 +355,15 @@ namespace dbgui::data
|
||||
size_t src_id;
|
||||
};
|
||||
|
||||
struct ReadAsArray {
|
||||
size_t addr_src_id;
|
||||
union {
|
||||
size_t size_src_id;
|
||||
uint64_t size_const;
|
||||
};
|
||||
bool is_const_size;
|
||||
};
|
||||
|
||||
struct Node
|
||||
{
|
||||
enum class Type : uint8_t
|
||||
@@ -364,12 +373,13 @@ namespace dbgui::data
|
||||
line_entry,
|
||||
locals,
|
||||
read_cstr,
|
||||
read_array,
|
||||
};
|
||||
|
||||
size_t id;
|
||||
Type type;
|
||||
// when adding something here, remember to update LLDBBackend::add_data_node
|
||||
std::variant<std::monostate, Source, Disassemble, LineEntry, ReadAsCStr>
|
||||
std::variant<std::monostate, Source, Disassemble, LineEntry, ReadAsCStr, ReadAsArray>
|
||||
data;
|
||||
};
|
||||
} // namespace source
|
||||
|
||||
@@ -982,700 +982,6 @@ 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;
|
||||
}
|
||||
|
||||
auto expr_path_vec = std::vector<ExprPathPart>{};
|
||||
|
||||
// ImGuiTableFlags_SizingFixedFit
|
||||
if (ImGui::BeginTable("##Variables", 2,
|
||||
ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable))
|
||||
{
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Value");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::PushID("TTTTTT");
|
||||
if (ImGui::TreeNodeEx("Locals", ImGuiTreeNodeFlags_SpanFullWidth
|
||||
| ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
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];
|
||||
expr_path_vec.clear();
|
||||
this->draw_value(frontend,
|
||||
frontend.target->data_res_nodes[node_idx]->type_id,
|
||||
name, node_idx, 0, expr_path_vec);
|
||||
|
||||
cur_idx += 1;
|
||||
cur_off += 2 + str_len;
|
||||
}
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
for (size_t i = 0; i < extra_slots.size(); ++i)
|
||||
{
|
||||
auto res_idx = frontend.target->data_idx_for_src_id(extra_slots[i].id);
|
||||
auto no_success = true;
|
||||
if (res_idx)
|
||||
{
|
||||
const auto &node = *frontend.target->data_res_nodes[*res_idx];
|
||||
if (node.success && node.children.size() == 1)
|
||||
{
|
||||
no_success = false;
|
||||
const auto &child_node =
|
||||
*frontend.target->data_res_nodes[node.children[0]];
|
||||
expr_path_vec.clear();
|
||||
this->draw_value(frontend, child_node.type_id, &extra_slots[i],
|
||||
node.children[0], 0, expr_path_vec);
|
||||
|
||||
if (extra_slots[i].bak == "")
|
||||
{
|
||||
frontend.target->backend->remove_data_node(extra_slots[i].id);
|
||||
extra_slots.erase(extra_slots.begin() + i);
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (no_success)
|
||||
{
|
||||
auto &slot = extra_slots[i];
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
|
||||
auto is_editing = false;
|
||||
if (slot.is_editing)
|
||||
{
|
||||
is_editing = true;
|
||||
if (slot.edit_was_started)
|
||||
{
|
||||
slot.edit_was_started = false;
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
if (ImGui::InputText("##dddd", slot.buf, sizeof(slot.buf),
|
||||
ImGuiInputTextFlags_AutoSelectAll
|
||||
| ImGuiInputTextFlags_EnterReturnsTrue)
|
||||
&& ImGui::IsItemDeactivatedAfterEdit())
|
||||
{
|
||||
if (slot.buf[0] != '\0')
|
||||
{
|
||||
frontend.target->backend->remove_data_node(slot.id);
|
||||
|
||||
using namespace data::source;
|
||||
frontend.target->backend->add_data_node(
|
||||
Node{.id = slot.id,
|
||||
.type = Node::Type::source,
|
||||
.data = Source{.type = Source::Type::variable,
|
||||
.data = Source::Variable{
|
||||
.expr_path = slot.buf,
|
||||
.address_of_or_pointer = false}}});
|
||||
}
|
||||
|
||||
slot.bak = slot.buf;
|
||||
slot.is_editing = false;
|
||||
}
|
||||
if (ImGui::IsItemDeactivated())
|
||||
{
|
||||
memcpy(slot.buf, slot.bak.data(), slot.bak.size());
|
||||
slot.buf[slot.bak.size()] = '\0';
|
||||
slot.is_editing = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_editing)
|
||||
{
|
||||
char tree_id_buf[128];
|
||||
std::snprintf(tree_id_buf, sizeof(tree_id_buf), "slot%lu", i);
|
||||
// TODO: better id
|
||||
ImGui::TreeNodeEx(tree_id_buf,
|
||||
ImGuiTreeNodeFlags_SpanFullWidth
|
||||
| ImGuiTreeNodeFlags_Leaf
|
||||
| ImGuiTreeNodeFlags_NoTreePushOnOpen,
|
||||
"%s", slot.bak.c_str());
|
||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)
|
||||
&& ImGui::IsItemHovered())
|
||||
{
|
||||
slot.is_editing = true;
|
||||
slot.edit_was_started = true;
|
||||
}
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextDisabled("<not available>");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
|
||||
if (ImGui::InputText("##EXTRASLOT", this->add_slot_buf,
|
||||
sizeof(this->add_slot_buf),
|
||||
ImGuiInputTextFlags_AutoSelectAll
|
||||
| ImGuiInputTextFlags_EnterReturnsTrue)
|
||||
&& ImGui::IsItemDeactivatedAfterEdit())
|
||||
{
|
||||
if (this->add_slot_buf[0] != '\0')
|
||||
{
|
||||
auto id = frontend.target->data_node_id++;
|
||||
const auto *expr_path_buf = this->add_slot_buf;
|
||||
bool address_of_or_pointer = false;
|
||||
if (expr_path_buf[0] == '+')
|
||||
{
|
||||
address_of_or_pointer = true;
|
||||
++expr_path_buf;
|
||||
}
|
||||
|
||||
using namespace data::source;
|
||||
frontend.target->backend->add_data_node(Node{
|
||||
.id = id,
|
||||
.type = Node::Type::source,
|
||||
.data = Source{.type = Source::Type::variable,
|
||||
.data = Source::Variable{.expr_path = expr_path_buf,
|
||||
.address_of_or_pointer =
|
||||
address_of_or_pointer}}});
|
||||
extra_slots.push_back(ExtraSlot{.id = id, .bak = this->add_slot_buf});
|
||||
memcpy(extra_slots.back().buf, this->add_slot_buf,
|
||||
sizeof(this->add_slot_buf));
|
||||
this->add_slot_buf[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void WatchWindow::draw_value(Frontend &frontend,
|
||||
data::type_info::TypeID type_id,
|
||||
std::variant<std::string_view, ExtraSlot *> name,
|
||||
data::result::NodeIdx node_idx, size_t off,
|
||||
std::vector<ExprPathPart> &expr_path)
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
const char *name_begin, *name_end;
|
||||
if (name.index() == 0)
|
||||
{
|
||||
name_begin = std::get<std::string_view>(name).begin();
|
||||
name_end = std::get<std::string_view>(name).end();
|
||||
} else
|
||||
{
|
||||
name_begin = &*std::get<ExtraSlot *>(name)->bak.begin();
|
||||
name_end = &*std::get<ExtraSlot *>(name)->bak.end();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
char tree_id_buf[128];
|
||||
std::snprintf(tree_id_buf, sizeof(tree_id_buf), "%u#off%lu", node_idx, off);
|
||||
auto is_editing = false;
|
||||
if (name.index() == 1)
|
||||
{
|
||||
auto *slot = std::get<ExtraSlot *>(name);
|
||||
if (slot->is_editing)
|
||||
{
|
||||
is_editing = true;
|
||||
ImGui::BeginDisabled();
|
||||
tree_open = ImGui::TreeNodeEx(tree_id_buf, 0, "");
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
if (slot->edit_was_started)
|
||||
{
|
||||
slot->edit_was_started = false;
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
if (ImGui::InputText("##dddd", slot->buf, sizeof(slot->buf),
|
||||
ImGuiInputTextFlags_AutoSelectAll
|
||||
| ImGuiInputTextFlags_EnterReturnsTrue)
|
||||
&& ImGui::IsItemDeactivatedAfterEdit())
|
||||
{
|
||||
if (slot->buf[0] != '\0')
|
||||
{
|
||||
frontend.target->backend->remove_data_node(slot->id);
|
||||
using namespace data::source;
|
||||
frontend.target->backend->add_data_node(
|
||||
Node{.id = slot->id,
|
||||
.type = Node::Type::source,
|
||||
.data =
|
||||
Source{.type = Source::Type::variable,
|
||||
.data = Source::Variable{.expr_path = slot->buf}}});
|
||||
}
|
||||
|
||||
slot->bak = slot->buf;
|
||||
slot->is_editing = false;
|
||||
}
|
||||
if (ImGui::IsItemDeactivated())
|
||||
{
|
||||
slot->is_editing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_editing)
|
||||
{
|
||||
tree_open = ImGui::TreeNodeEx(
|
||||
tree_id_buf,
|
||||
ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanAvailWidth,
|
||||
"%.*s", static_cast<int>(name_end - name_begin), name_begin);
|
||||
if (name.index() == 1
|
||||
&& ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)
|
||||
&& ImGui::IsItemHovered())
|
||||
{
|
||||
auto *slot = std::get<ExtraSlot *>(name);
|
||||
slot->is_editing = true;
|
||||
slot->edit_was_started = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (tree_open)
|
||||
{
|
||||
if (type_id.type == Type::complex)
|
||||
{
|
||||
char tree_id_buf[128];
|
||||
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];
|
||||
if (member.bitfield_size)
|
||||
{
|
||||
std::snprintf(tree_id_buf, sizeof(tree_id_buf), "%u#member%lu",
|
||||
node_idx, i);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::TreeNodeEx(tree_id_buf,
|
||||
ImGuiTreeNodeFlags_SpanFullWidth
|
||||
| ImGuiTreeNodeFlags_Leaf
|
||||
| ImGuiTreeNodeFlags_NoTreePushOnOpen,
|
||||
"%s", member.name.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
auto member_type = member.type_id;
|
||||
while (member_type.type == Type::alias)
|
||||
{
|
||||
member_type = std::get<TypeID>(
|
||||
frontend.target->types[member_type.idx].member_types);
|
||||
}
|
||||
|
||||
// TODO: handle u128/i128
|
||||
assert(member_type.type != Type::i128
|
||||
&& member_type.type != Type::u128);
|
||||
assert(member_type.is_integral()
|
||||
|| member_type.type == Type::_enum);
|
||||
assert(member.bitfield_size <= 8 * 8);
|
||||
auto byte_off = member.offset / 8;
|
||||
auto bit_off = member.offset % 8;
|
||||
auto byte_size = (member.bitfield_size + 7) / 8;
|
||||
uint64_t value = 0;
|
||||
uint8_t arr[8] = {};
|
||||
const auto &data = node.vec_data();
|
||||
std::copy(data.begin() + off + byte_off,
|
||||
data.begin() + off + byte_off + byte_size, arr);
|
||||
value = *reinterpret_cast<uint64_t *>(arr);
|
||||
|
||||
assert(bit_off + member.bitfield_size <= 64);
|
||||
value = value >> bit_off;
|
||||
value =
|
||||
value & (0xFFFFFFFF'FFFFFFFF >> (64 - member.bitfield_size));
|
||||
|
||||
if (member_type.type == Type::_enum)
|
||||
{
|
||||
const auto &members =
|
||||
frontend.target->types[type_id.idx].member_vec();
|
||||
auto printed = false;
|
||||
for (const auto &member : members)
|
||||
{
|
||||
if (value == member.enum_val)
|
||||
{
|
||||
ImGui::Text("%s", member.name.c_str());
|
||||
printed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!printed)
|
||||
{
|
||||
ImGui::Text("<invalid: %lu>", value);
|
||||
}
|
||||
} else
|
||||
{
|
||||
ImGui::Text("%ld", value);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
expr_path.push_back(ExprPathPart{
|
||||
.ident = std::string_view{name_begin, static_cast<size_t>(
|
||||
name_end - name_begin)},
|
||||
.deref = false});
|
||||
this->draw_value(frontend, member.type_id, member.name, node_idx,
|
||||
off + member.offset, expr_path);
|
||||
expr_path.pop_back();
|
||||
}
|
||||
} 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;
|
||||
|
||||
expr_path.push_back(ExprPathPart{
|
||||
.ident = std::string_view{name_begin,
|
||||
static_cast<size_t>(name_end - name_begin)},
|
||||
.array = true});
|
||||
|
||||
char buf[32];
|
||||
size_t member_off = 0;
|
||||
for (size_t i = 0; i < el_count; ++i)
|
||||
{
|
||||
std::snprintf(buf, sizeof(buf), "[%lu]", i);
|
||||
auto expr_path_size = expr_path.size();
|
||||
expr_path.back().array_idx = i;
|
||||
this->draw_value(frontend, member_ty_id, buf, node_idx,
|
||||
off + member_off, expr_path);
|
||||
assert(expr_path.size() == expr_path_size);
|
||||
member_off += member_size;
|
||||
}
|
||||
|
||||
expr_path.pop_back();
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
} else
|
||||
{
|
||||
auto is_editing = false;
|
||||
if (name.index() == 1)
|
||||
{
|
||||
auto *slot = std::get<ExtraSlot *>(name);
|
||||
if (slot->is_editing)
|
||||
{
|
||||
is_editing = true;
|
||||
if (slot->edit_was_started)
|
||||
{
|
||||
slot->edit_was_started = false;
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
if (ImGui::InputText("##dddd", slot->buf, sizeof(slot->buf),
|
||||
ImGuiInputTextFlags_AutoSelectAll
|
||||
| ImGuiInputTextFlags_EnterReturnsTrue)
|
||||
&& ImGui::IsItemDeactivatedAfterEdit())
|
||||
{
|
||||
if (slot->buf[0] != '\0')
|
||||
{
|
||||
frontend.target->backend->remove_data_node(slot->id);
|
||||
using namespace data::source;
|
||||
frontend.target->backend->add_data_node(
|
||||
Node{.id = slot->id,
|
||||
.type = Node::Type::source,
|
||||
.data =
|
||||
Source{.type = Source::Type::variable,
|
||||
.data = Source::Variable{.expr_path = slot->buf}}});
|
||||
}
|
||||
|
||||
slot->bak = slot->buf;
|
||||
slot->is_editing = false;
|
||||
}
|
||||
if (ImGui::IsItemDeactivated())
|
||||
{
|
||||
slot->is_editing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto tree_open = false;
|
||||
if (!is_editing)
|
||||
{
|
||||
char tree_id_buf[128];
|
||||
std::snprintf(tree_id_buf, sizeof(tree_id_buf), "%u#off%lu", node_idx,
|
||||
off);
|
||||
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_SpanFullWidth;
|
||||
if (type_id.type != Type::ptr)
|
||||
{
|
||||
flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
|
||||
}
|
||||
|
||||
// TODO: better id
|
||||
tree_open =
|
||||
ImGui::TreeNodeEx(tree_id_buf, flags, "%.*s",
|
||||
static_cast<int>(name_end - name_begin), name_begin);
|
||||
//ImGui::TextUnformatted(name_begin, name_end);
|
||||
if (name.index() == 1
|
||||
&& ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)
|
||||
&& ImGui::IsItemHovered())
|
||||
{
|
||||
auto *slot = std::get<ExtraSlot *>(name);
|
||||
slot->is_editing = true;
|
||||
slot->edit_was_started = true;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
if (tree_open)
|
||||
{
|
||||
// TODO: make this func be able to not print the "outer" line
|
||||
// and just accept a bool to only draw the containing value
|
||||
std::string path{};
|
||||
if (type_id.sub_type != Type::i8)
|
||||
{
|
||||
// TODO: better check if this is really a string
|
||||
path = "*";
|
||||
}
|
||||
// why cant i just path name_begin, name_end to string_view? :[
|
||||
construct_expr_path(
|
||||
path, expr_path,
|
||||
std::string_view{name_begin,
|
||||
static_cast<size_t>(name_end - name_begin)});
|
||||
|
||||
auto src_id = frontend.target->find_or_create_expr_path(
|
||||
std::move(path), type_id.sub_type == Type::i8);
|
||||
auto res_idx = frontend.target->data_idx_for_src_id(src_id);
|
||||
if (res_idx)
|
||||
{
|
||||
const auto &node = *frontend.target->data_res_nodes[*res_idx];
|
||||
if (type_id.sub_type == Type::i8 && node.success)
|
||||
{
|
||||
// TODO: make span whole row (see https://github.com/ocornut/imgui/issues/3565)
|
||||
assert(node.type_id.type == Type::custom);
|
||||
assert(node.vec_data().back() == '\0');
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::TextWrapped("\"%s\"", node.vec_data().data());
|
||||
} else if (node.success && node.children.size() == 1)
|
||||
{
|
||||
auto data_idx = node.children[0];
|
||||
const auto &data_node =
|
||||
*frontend.target->data_res_nodes[data_idx];
|
||||
expr_path.push_back(ExprPathPart{
|
||||
.ident = std::string_view{name_begin, static_cast<size_t>(
|
||||
name_end - name_begin)},
|
||||
.deref = true});
|
||||
|
||||
this->draw_value(frontend, data_node.type_id,
|
||||
std::string_view{"<pointee>"}, data_idx, 0,
|
||||
expr_path);
|
||||
|
||||
expr_path.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
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 WatchWindow::construct_expr_path(std::string &out,
|
||||
const std::vector<ExprPathPart> &path,
|
||||
std::string_view tail)
|
||||
{
|
||||
for (const auto &entry : path)
|
||||
{
|
||||
// FIXME: BIGGEST HACK in cinema history
|
||||
if (entry.ident == "<pointee>")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
out += entry.ident;
|
||||
if (entry.deref)
|
||||
{
|
||||
out += "->";
|
||||
} else if (!entry.array)
|
||||
{
|
||||
out += '.';
|
||||
}
|
||||
}
|
||||
out += tail;
|
||||
}
|
||||
|
||||
void Window::handle_source_updated(Target &target, size_t id)
|
||||
{
|
||||
switch (this->type)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "msg.h"
|
||||
#include "frontend/target.h"
|
||||
|
||||
#include "window/watch_window.h"
|
||||
|
||||
namespace dbgui::frontend
|
||||
{
|
||||
struct Frontend;
|
||||
@@ -124,46 +126,6 @@ namespace dbgui::frontend
|
||||
std::vector<std::string_view> lines;
|
||||
};
|
||||
|
||||
struct WatchWindow
|
||||
{
|
||||
struct ExtraSlot
|
||||
{
|
||||
size_t id;
|
||||
char buf[256];
|
||||
std::string bak;
|
||||
bool is_editing = false;
|
||||
bool edit_was_started = false;
|
||||
};
|
||||
|
||||
// TODO: cache the expr_path by node_idx + off or smth?
|
||||
struct ExprPathPart
|
||||
{
|
||||
std::string_view ident;
|
||||
bool deref = false;
|
||||
bool array = false;
|
||||
uint64_t array_idx = 0;
|
||||
};
|
||||
|
||||
bool draw(Frontend &);
|
||||
void draw_value(Frontend &, data::type_info::TypeID,
|
||||
std::variant<std::string_view, ExtraSlot *> name,
|
||||
data::result::NodeIdx node_idx, size_t off,
|
||||
std::vector<ExprPathPart> &expr_path);
|
||||
// void handle_source_updated(Target& target, size_t id);
|
||||
void construct_expr_path(std::string &out,
|
||||
const std::vector<ExprPathPart> &,
|
||||
std::string_view tail);
|
||||
|
||||
std::string id;
|
||||
bool open;
|
||||
bool first;
|
||||
|
||||
size_t locals_src_id;
|
||||
|
||||
std::vector<ExtraSlot> extra_slots;
|
||||
char add_slot_buf[256] = {};
|
||||
};
|
||||
|
||||
struct Window
|
||||
{
|
||||
WindowType type;
|
||||
|
||||
1081
src/frontend/window/watch_window.cpp
Normal file
1081
src/frontend/window/watch_window.cpp
Normal file
File diff suppressed because it is too large
Load Diff
75
src/frontend/window/watch_window.h
Normal file
75
src/frontend/window/watch_window.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include "msg.h"
|
||||
|
||||
namespace dbgui::frontend
|
||||
{
|
||||
struct Frontend;
|
||||
|
||||
struct WatchWindow
|
||||
{
|
||||
enum class VarFormatTy : uint8_t
|
||||
{
|
||||
ty_builtin,
|
||||
i8,
|
||||
i16,
|
||||
i32,
|
||||
i64,
|
||||
i128,
|
||||
u8,
|
||||
u16,
|
||||
u32,
|
||||
u64,
|
||||
u128,
|
||||
f32,
|
||||
f128,
|
||||
str,
|
||||
cstr,
|
||||
};
|
||||
|
||||
struct ExtraSlot
|
||||
{
|
||||
size_t id;
|
||||
char buf[256];
|
||||
std::string bak;
|
||||
bool is_editing = false;
|
||||
bool edit_was_started = false;
|
||||
VarFormatTy fmt_ty = VarFormatTy::ty_builtin;
|
||||
uint32_t fmt_matrix_x = 0, fmt_matrix_y = 0;
|
||||
};
|
||||
|
||||
// TODO: cache the expr_path by node_idx + off or smth?
|
||||
struct ExprPathPart
|
||||
{
|
||||
std::string_view ident;
|
||||
bool deref = false;
|
||||
bool array = false;
|
||||
uint64_t array_idx = 0;
|
||||
};
|
||||
|
||||
bool draw(Frontend &);
|
||||
void draw_value(Frontend &, data::type_info::TypeID,
|
||||
std::variant<std::string_view, ExtraSlot *> name,
|
||||
data::result::NodeIdx node_idx, size_t off,
|
||||
std::vector<ExprPathPart> &expr_path);
|
||||
// void handle_source_updated(Target& target, size_t id);
|
||||
void construct_expr_path(std::string &out,
|
||||
const std::vector<ExprPathPart> &,
|
||||
std::string_view tail);
|
||||
|
||||
void create_node_for_extra_slot(Frontend &frontend, size_t slot_idx);
|
||||
|
||||
std::string id;
|
||||
bool open;
|
||||
bool first;
|
||||
|
||||
size_t locals_src_id;
|
||||
|
||||
std::vector<ExtraSlot> extra_slots;
|
||||
char add_slot_buf[256] = {};
|
||||
};
|
||||
} // namespace dbgui::frontend
|
||||
Reference in New Issue
Block a user