disassembly and new font

This commit is contained in:
T0b1
2023-06-03 21:58:56 +02:00
parent 4f0f320ac4
commit 343a30289c
15 changed files with 3631 additions and 32 deletions

BIN
deps/FiraCode-Regular.ttf vendored Normal file

Binary file not shown.

View File

@@ -50,4 +50,10 @@ void Backend::send_frame_removed(BackToFront::FrameRemoved &&info)
auto msg = std::make_unique<BackToFront::Msg>(
BackToFront::MsgType::frame_removed, std::move(info));
this->send_msg(std::move(msg));
}
void Backend::send_data_result(BackToFront::DataResult &&info)
{
auto msg = std::make_unique<BackToFront::Msg>(
BackToFront::MsgType::data_result, std::move(info));
this->send_msg(std::move(msg));
}

View File

@@ -26,6 +26,9 @@ 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;
auto retrieve_msg_for_frontend()
-> std::optional<std::unique_ptr<BackToFront::Msg>>
{
@@ -55,6 +58,7 @@ namespace dbgui::backend
void send_thread_removed(BackToFront::ThreadRemoved &&);
void send_frame_changed(BackToFront::FrameChanged &&);
void send_frame_removed(BackToFront::FrameRemoved &&);
void send_data_result(BackToFront::DataResult &&);
private:
std::mutex _back_front_msg_mutex;

View File

@@ -6,6 +6,8 @@
#include <lldb/API/SBFrame.h>
#include <lldb/API/SBValueList.h>
#include <lldb/API/SBValue.h>
#include <lldb/API/SBInstructionList.h>
#include <lldb/API/SBInstruction.h>
#include <filesystem>
#include <array>
#include <cassert>
@@ -114,9 +116,11 @@ void LLDBBackend::handle_state_change(lldb::StateType state)
this->check_thread_changes();
this->check_frame_changes();
this->check_data_changes();
if (state == StateType::eStateStopped)
{
_state = TargetState::paused;
this->send_state_change(TargetState::paused,
StateChangeReason::initial_entry);
return;
@@ -131,10 +135,13 @@ void LLDBBackend::handle_state_change(lldb::StateType state)
this->check_reg_changes();
this->check_thread_changes();
this->check_frame_changes();
this->check_data_changes();
_state = TargetState::paused;
this->send_state_change(TargetState::paused, StateChangeReason::unknown);
break;
case eStateRunning:
case eStateStepping:
_state = TargetState::running;
this->send_state_change(TargetState::running, StateChangeReason::unknown);
return;
default: printf("Unknown StateType %u encountered\n", state); exit(1);
@@ -304,9 +311,13 @@ void LLDBBackend::dump_threads()
stream.GetData());
}
auto pc = frame.GetPC();
stream.Clear();
auto sc =
frame.GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol);
//auto sc =
// frame.GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol);
auto sc = _target.ResolveSymbolContextForAddress(
SBAddress{pc, _target}, eSymbolContextFunction | eSymbolContextSymbol);
uint64_t start, end;
if (sc.GetFunction().IsValid())
@@ -322,7 +333,7 @@ void LLDBBackend::dump_threads()
end = sym.GetEndAddress().GetLoadAddress(_target);
} else
{
start = frame.GetPC();
start = pc;
end = start + 0x100;
}
@@ -338,6 +349,18 @@ void LLDBBackend::dump_threads()
printf("InstList: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
auto inst = inst_list.GetInstructionAtIndex(0);
const auto *comment = inst.GetComment(_target);
if (comment && *comment != '\0')
{
printf("Selfprint: %s%s ; %s\n", inst.GetMnemonic(_target),
inst.GetOperands(_target), comment);
} else
{
printf("Selfprint: %s%s\n", inst.GetMnemonic(_target),
inst.GetOperands(_target));
}
//printf("Disasm: %s\n", frame.Disassemble());
} else
{
@@ -360,7 +383,10 @@ bool LLDBBackend::step_into()
}
// TODO: figure out what the runmodes mean
thread.StepInto();
// TODO: this is source step
// thread.StepInto();
thread.StepInstruction(false);
return false;
}
@@ -378,7 +404,10 @@ bool LLDBBackend::step_over()
{
return false;
}
thread.StepOver();
//thread.StepOver();
thread.StepInstruction(true);
return false;
}
@@ -712,6 +741,288 @@ void LLDBBackend::check_frame_changes()
}
}
void LLDBBackend::add_data_node(const data::DataNode &node)
{
std::lock_guard g{_data_lock};
if (_data_dag.nodes.contains(node.id))
{
printf("Double ID in DAG\n");
exit(1);
}
_data_nodes.push_back(node);
_data_dag.add_node(node.id);
// add child nodes
switch (node.type)
{
using enum data::DataNode::Type;
case data_source:
// nothing to do rn
break;
case disassemble:
{
auto src_id = std::get<data::Disassemble>(node.data).src_id;
if (!_data_dag.nodes.contains(src_id))
{
printf("Invalid add sequence\n");
exit(1);
}
_data_dag.add_edge(node.id, src_id);
break;
}
}
_dag_linear_valid = false;
// TODO: just insert a dummy event?
if (_state == TargetState::paused)
{
this->check_data_changes();
}
}
void LLDBBackend::remove_data_node(size_t id)
{
std::lock_guard g{_data_lock};
_data_dag.remove_node(id);
auto it = std::find_if(_data_nodes.begin(), _data_nodes.end(),
[id](const auto &el) { return el.id == id; });
if (it == _data_nodes.end())
{
printf("Could not find node for DAG element\n");
exit(1);
}
_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())
{
_cached_data_results.erase(cache_it);
}
_dag_linear_valid = false;
}
void LLDBBackend::check_data_changes()
{
if (!_dag_linear_valid)
{
_dag_linear.clear();
_data_dag.linearize(_dag_linear);
_dag_linear_valid = true;
}
for (auto id : _dag_linear)
{
const auto &node_it =
std::find_if(_data_nodes.begin(), _data_nodes.end(),
[id](const auto &el) { return el.id == id; });
if (node_it == _data_nodes.end())
{
printf("Could not find node for DAG element\n");
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);
// 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 (new_res.success == result_it->success
&& new_res.type == result_it->type)
{
if (new_res.data.size() == result_it->data.size())
{
if (!std::memcmp(new_res.data.data(), result_it->data.data(),
new_res.data.size()))
{
should_send = false;
}
}
}
}
if (!should_send)
{
continue;
}
if (result_it == _cached_data_results.end())
{
_cached_data_results.push_back(new_res);
} else
{
*result_it = new_res;
}
// TODO: queue and send at once to prevent UI lag?
this->send_data_result(
BackToFront::DataResult{.result = std::move(new_res)});
}
}
dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
{
switch (node.type)
{
using enum data::DataNode::Type;
case data_source:
{
const auto &src_data = std::get<data::DataSource>(node.data);
switch (src_data.type)
{
using enum data::DataSource::Type;
case reg:
{
const auto &info = std::get<data::DataSource::Reg>(src_data.data);
if (info.set >= _reg_sets.size()
|| info.idx >= _reg_sets[info.set].values.size())
{
return data::DataResult{
.id = node.id,
.success = false,
};
}
// 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]};
}
}
break;
}
case disassemble:
{
using namespace lldb;
size_t addr_id = std::get<data::Disassemble>(node.data).src_id;
const auto res_it =
std::find_if(_cached_data_results.begin(), _cached_data_results.end(),
[addr_id](const auto &el) { return el.id == addr_id; });
if (res_it == _cached_data_results.end() || !res_it->success)
{
return data::DataResult{.id = node.id, .success = false};
}
// TODO: for now only accept u64
if (res_it->type.type != data::TypeInfo::Type::u64)
{
return data::DataResult{.id = node.id, .success = false};
}
const auto pc = *reinterpret_cast<uint64_t *>(res_it->data.data());
auto sc = _target.ResolveSymbolContextForAddress(
SBAddress{pc, _target}, eSymbolContextFunction | eSymbolContextSymbol);
uint64_t start, end;
if (sc.GetFunction().IsValid())
{
auto fn = sc.GetFunction();
start = fn.GetStartAddress().GetLoadAddress(_target);
end = fn.GetEndAddress().GetLoadAddress(_target);
} else if (sc.GetSymbol().IsValid()
&& sc.GetSymbol().GetStartAddress().IsValid())
{
auto sym = sc.GetSymbol();
start = sym.GetStartAddress().GetLoadAddress(_target);
end = sym.GetEndAddress().GetLoadAddress(_target);
} else
{
start = pc;
end = start + 0x100;
}
auto buf = std::vector<uint8_t>{};
buf.resize(end - start);
auto err = SBError{};
_target.ReadMemory(SBAddress{start, _target}, buf.data(), buf.size(),
err);
auto inst_list = _target.GetInstructionsWithFlavor(
start, "intel", buf.data(), buf.size());
// TODO: for now the instlist is serialized in a custom format
// use a type structure for it later?
// it's basically an array of
// struct Inst {
// uint64_t addr;
// uint8_t mnem_len;
// uint8_t op_len;
// uint8_t comment_len;
// uint8_t inst_len;
// char mnem[];
// char op[];
// char comment[];
// };
std::vector<uint8_t> out{};
for (size_t i = 0; i < inst_list.GetSize(); ++i)
{
auto inst = inst_list.GetInstructionAtIndex(i);
const auto mnem = std::string_view{inst.GetMnemonic(_target)};
const auto op = std::string_view{inst.GetOperands(_target)};
const auto comm = std::string_view{inst.GetComment(_target)};
auto addr = inst.GetAddress().GetLoadAddress(_target);
const auto len = inst.GetByteSize();
SBStream stream{};
inst.GetDescription(stream);
printf("Got inst: %.*s\n", stream.GetSize(), stream.GetData());
if (mnem.size() > 255 || op.size() > 255 || comm.size() > 255)
{
printf("Instruction length limits exceeded:\n");
auto stream = SBStream{};
inst.GetDescription(stream);
printf("%.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
exit(1);
}
size_t insert_idx = out.size();
out.resize(out.size() + 8 + 4 + mnem.size() + op.size() + comm.size());
*reinterpret_cast<uint64_t *>(&out[insert_idx]) = addr;
*reinterpret_cast<uint8_t *>(&out[insert_idx + 8]) = mnem.size();
*reinterpret_cast<uint8_t *>(&out[insert_idx + 9]) = op.size();
*reinterpret_cast<uint8_t *>(&out[insert_idx + 10]) = comm.size();
*reinterpret_cast<uint8_t *>(&out[insert_idx + 11]) = len;
insert_idx += 12;
std::copy(mnem.begin(), mnem.end(), out.begin() + insert_idx);
insert_idx += mnem.size();
std::copy(op.begin(), op.end(), out.begin() + insert_idx);
insert_idx += op.size();
std::copy(comm.begin(), comm.end(), out.begin() + insert_idx);
}
return data::DataResult{
.id = node.id,
.success = true,
.type = data::TypeInfo{.type = data::TypeInfo::Type::custom},
.data = std::move(out)};
}
}
printf("Unhandled data type\n");
exit(1);
}
/*
Reg output for x64
Got register set General Purpose Registers

View File

@@ -8,6 +8,8 @@
#include <thread>
#include <mutex>
#include "util/dag.h"
namespace dbgui::backend
{
struct LLDBBackend : Backend
@@ -51,6 +53,9 @@ namespace dbgui::backend
void cont() override;
void pause() override;
void add_data_node(const data::DataNode &) override;
void remove_data_node(uint64_t id) override;
private:
void run_msg_loop();
void wait_for_debug_events();
@@ -63,6 +68,8 @@ namespace dbgui::backend
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::string _filename;
lldb::SBDebugger _instance;
@@ -79,5 +86,11 @@ namespace dbgui::backend
std::vector<Thread> _threads = {};
std::vector<Frame> _frames = {};
uint16_t _selected_frame = 0;
util::DAG _data_dag = {};
std::vector<data::DataNode> _data_nodes = {};
std::vector<size_t> _dag_linear = {};
bool _dag_linear_valid = false;
std::vector<data::DataResult> _cached_data_results = {};
};
} // namespace dbgui::backend

View File

@@ -7,6 +7,36 @@
namespace dbgui::data
{
struct TypeInfo
{
enum class Type
{
custom,
u8,
u16,
u32,
u64,
i8,
i16,
i32,
i64,
f32,
f64,
// ptr,
// arr,
// complex,
};
Type type;
// std::variant<std::monostate, PtrInfo, ComplexInfo> add;
bool operator==(const TypeInfo &rhs) const
{
return this->type == rhs.type;
}
};
struct DataSource
{
enum class Type : uint8_t
@@ -28,15 +58,34 @@ namespace dbgui::data
std::variant<std::monostate, Reg> data;
};
struct DataNode
struct Disassemble
{
size_t id;
// Node that provides the address to disassemble
// type must be integer
size_t src_id;
};
struct DataNode
{
enum class Type : uint8_t
{
data_source,
disassemble,
};
size_t id;
Type type;
std::variant<DataSource, Disassemble> data;
};
// TODO: this should allow only updating part of arrays
// and moving in it
struct DataResult
{
// TODO: needs indicator that data was failed to be retrieved
size_t id;
bool success;
TypeInfo type;
std::vector<uint8_t> data;
};

1693
src/fira_code.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,7 @@ Frontend::Frontend()
_windows.push_back(Window::create_regs(window_id++));
_windows.push_back(Window::create_threads(window_id++));
_windows.push_back(Window::create_frames(window_id++));
_windows.push_back(Window::create_disas(window_id++));
}
void Frontend::run_frame()
@@ -68,14 +69,21 @@ void Frontend::run_frame()
ImGui::DockSpace(this->dock_id, ImVec2{0, 0},
ImGuiDockNodeFlags_PassthruCentralNode);
for (auto &window : _windows)
size_t window_idx = 0;
while (window_idx < _windows.size())
{
window.draw(*this);
auto &window = _windows[window_idx];
if (window.draw(*this))
{
_windows.erase(_windows.begin() + window_idx);
} else
{
window_idx++;
}
//this->dock_id = ImGui::GetWindowDockID();
}
ImGui::End();
}
ImGui::End();
this->draw_status();
}
@@ -225,8 +233,8 @@ void Frontend::draw_status()
ImGui::Text("Happy status bar");
ImGui::EndMenuBar();
}
ImGui::End();
}
ImGui::End();
}
void Frontend::draw_open_popup()
@@ -305,6 +313,15 @@ void Frontend::handle_msgs()
this->handle_frame_remove(
std::get<BackToFront::FrameRemoved>(msg->data));
break;
case data_result:
{
const auto &result = std::get<BackToFront::DataResult>(msg->data);
printf("Result ID: %lu\n", result.result.id);
for (auto &window : _windows)
{
window.handle_data_res(result);
}
}
}
}
}

View File

@@ -46,6 +46,7 @@ namespace dbgui::frontend
TargetState state = TargetState::stopped;
std::string filename;
uint64_t id;
size_t data_node_id = 0;
Arch arch;
std::vector<RegSet> reg_sets;

View File

@@ -4,15 +4,20 @@
using namespace dbgui;
using namespace dbgui::frontend;
void Window::draw(const Frontend &frontend)
bool Window::draw(Frontend &frontend)
{
switch (this->type)
{
using enum WindowType;
case regs: std::get<RegWindow>(this->data).draw(frontend); break;
case threads: std::get<ThreadWindow>(this->data).draw(frontend); break;
case frames: std::get<FrameWindow>(this->data).draw(frontend); break;
case regs: return std::get<RegWindow>(this->data).draw(frontend); break;
case threads:
return std::get<ThreadWindow>(this->data).draw(frontend);
break;
case frames: return std::get<FrameWindow>(this->data).draw(frontend); break;
case disassembly:
return std::get<DisasmWindow>(this->data).draw(frontend);
break;
default: printf("Unhandled window draw: %u\n", this->type); exit(1);
}
}
@@ -44,18 +49,29 @@ Window Window::create_frames(size_t id)
.data = FrameWindow{.id = id_str, .open = true}};
}
void RegWindow::draw(const Frontend &frontend)
Window Window::create_disas(size_t id)
{
auto id_str = std::string{"Disassembly##"};
id_str.append(std::to_string(id));
return Window{.type = WindowType::disassembly,
.data =
DisasmWindow{.id = id_str, .open = true, .first = true}};
}
bool RegWindow::draw(const Frontend &frontend)
{
//ImGui::SetNextWindowDockID(frontend.dock_id, ImGuiCond_Appearing);
if (!ImGui::Begin(this->id.c_str()))
{
return;
ImGui::End();
return false;
}
if (!frontend.target)
{
ImGui::End();
return;
return false;
}
if (ImGui::BeginTable("table", 1,
@@ -136,20 +152,22 @@ void RegWindow::draw(const Frontend &frontend)
}
ImGui::End();
return false;
}
void ThreadWindow::draw(const Frontend &frontend)
bool ThreadWindow::draw(const Frontend &frontend)
{
//ImGui::SetNextWindowDockID(frontend.dock_id, ImGuiCond_Appearing);
if (!ImGui::Begin(this->id.c_str()))
{
return;
ImGui::End();
return false;
}
if (!frontend.target)
{
ImGui::End();
return;
return false;
}
if (ImGui::BeginTable("table", 3,
@@ -194,20 +212,22 @@ void ThreadWindow::draw(const Frontend &frontend)
}
ImGui::End();
return false;
}
void FrameWindow::draw(const Frontend &frontend)
bool FrameWindow::draw(const Frontend &frontend)
{
//ImGui::SetNextWindowDockID(frontend.dock_id, ImGuiCond_Appearing);
if (!ImGui::Begin(this->id.c_str()))
{
return;
ImGui::End();
return false;
}
if (!frontend.target)
{
ImGui::End();
return;
return false;
}
if (ImGui::BeginTable("table", 1,
@@ -243,4 +263,265 @@ void FrameWindow::draw(const Frontend &frontend)
}
ImGui::End();
return false;
}
bool DisasmWindow::draw(Frontend &frontend)
{
if (!ImGui::Begin(this->id.c_str()))
{
ImGui::End();
return false;
}
if (!frontend.target)
{
this->insts.clear();
this->first = true;
this->ip_unsuccessful = true;
this->disas_unsuccessful = true;
ImGui::End();
return false;
}
if (frontend.target->state == TargetState::stopped
|| frontend.target->state == TargetState::startup)
{
this->ip_unsuccessful = true;
this->disas_unsuccessful = true;
ImGui::End();
return false;
}
if (first)
{
auto found = false;
uint16_t set_idx, reg_idx;
for (size_t i = 0; i < frontend.target->reg_sets.size(); ++i)
{
const auto &set = frontend.target->reg_sets[i];
for (size_t j = 0; j < set.regs.size(); ++j)
{
if (frontend.target->arch == Arch::x86_64 && set.regs[j].name == "rip")
{
found = true;
set_idx = i;
reg_idx = j;
break;
}
}
if (found)
{
break;
}
}
if (!found)
{
ImGui::End();
this->ip_unsuccessful = true;
this->disas_unsuccessful = true;
return false;
}
this->ip_src_id = frontend.target->data_node_id++;
this->disas_src_id = frontend.target->data_node_id++;
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::reg,
.data = data::DataSource::Reg{.set = set_idx, .idx = reg_idx}}});
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}});
first = false;
}
auto pad = ImGui::GetStyle().CellPadding;
pad.x += 10.f;
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, pad);
if (ImGui::BeginTable("table", 2,
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY
| ImGuiTableFlags_RowBg | ImGuiTableFlags_PadOuterX))
{
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("Address");
ImGui::TableSetupColumn("Instruction");
ImGui::TableHeadersRow();
for (size_t idx = 0; idx < insts.size(); ++idx)
{
const auto &inst = insts[idx];
//printf("Inst at %lx with '%s'\n", inst.addr, inst.fmt_str.c_str());
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
if (!this->ip_unsuccessful && inst.addr == ip)
{
// TODO: color config
const auto col =
ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Button));
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, col);
if (this->ip_changed)
{
ImGui::SetScrollHereY();
}
}
// TODO: make some unified handler for int vars
ImGui::PushID(idx);
ImGui::Text("%lX", inst.addr);
if (ImGui::IsItemHovered()
&& ImGui::IsMouseClicked(ImGuiMouseButton_Right))
{
ImGui::OpenPopup("Context");
}
if (ImGui::BeginPopup("Context"))
{
if (ImGui::Selectable("Copy"))
{
char buf[32];
std::snprintf(buf, sizeof(buf), "%lX", inst.addr);
ImGui::SetClipboardText(buf);
}
ImGui::EndPopup();
}
ImGui::PopID();
ImGui::TableNextColumn();
const int mnem_space = max_mnem_len - inst.mnem_len + 1;
const int op_space = max_op_len - inst.op_len + 1;
ImGui::Text(inst.fmt_str.c_str(), mnem_space, ' ', op_space, ' ');
}
ImGui::EndTable();
}
ImGui::PopStyleVar();
ImGui::End();
ip_changed = false;
return false;
}
void Window::handle_data_res(const BackToFront::DataResult &result)
{
switch (this->type)
{
using enum WindowType;
case disassembly:
std::get<DisasmWindow>(this->data).handle_data_res(result);
break;
default: break;
}
}
void DisasmWindow::handle_data_res(
const BackToFront::DataResult &result_wrapper)
{
const auto &result = result_wrapper.result;
if (result.id == this->ip_src_id)
{
if (!result.success || result.type.type != data::TypeInfo::Type::u64)
{
this->ip_unsuccessful = true;
return;
}
this->ip_unsuccessful = false;
this->ip_changed = true;
this->ip = *reinterpret_cast<const uint64_t *>(result.data.data());
printf("IP changed to %lX\n", this->ip);
return;
}
if (result.id != this->disas_src_id)
{
return;
}
if (!result.success || result.type.type != data::TypeInfo::Type::custom)
{
this->disas_unsuccessful = true;
this->insts.clear();
return;
}
this->disas_unsuccessful = false;
this->insts.clear();
// parse insts
// struct Inst {
// uint64_t addr;
// uint8_t mnem_len;
// uint8_t op_len;
// uint8_t comment_len;
// uint8_t inst_len;
// char mnem[];
// char op[];
// char comment[];
// };
char buf[256];
size_t idx = 0;
uint8_t max_mnem_len = 0;
uint8_t max_op_len = 0;
while (idx < result.data.size())
{
if (result.data.size() - idx < 12)
{
break;
}
uint64_t addr = *reinterpret_cast<const uint64_t *>(&result.data[idx]);
uint8_t mnem_len =
*reinterpret_cast<const uint8_t *>(&result.data[idx + 8]);
uint8_t op_len = *reinterpret_cast<const uint8_t *>(&result.data[idx + 9]);
uint8_t comment_len =
*reinterpret_cast<const uint8_t *>(&result.data[idx + 10]);
// dc about inst_len rn
idx += 12;
if (result.data.size() - idx < mnem_len + op_len + comment_len)
{
break;
}
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]);
} else
{
std::snprintf(buf, sizeof(buf), "%.*s%%*c%.*s%%.*c", mnem_len,
&result.data[idx], op_len, &result.data[idx + mnem_len]);
}
idx += mnem_len + op_len + comment_len;
if (mnem_len > max_mnem_len)
{
max_mnem_len = mnem_len;
}
if (op_len > max_op_len)
{
max_op_len = op_len;
}
insts.push_back(Instruction{.fmt_str = buf,
.addr = addr,
.mnem_len = mnem_len,
.op_len = op_len,
.comm_len = comment_len});
}
this->max_mnem_len = max_mnem_len;
this->max_op_len = max_op_len;
}

View File

@@ -49,7 +49,7 @@ namespace dbgui::frontend
std::vector<Reg> regs;*/
void draw(const Frontend &);
bool draw(const Frontend &);
std::string id;
bool open;
@@ -57,7 +57,7 @@ namespace dbgui::frontend
struct ThreadWindow
{
void draw(const Frontend &);
bool draw(const Frontend &);
std::string id;
bool open;
@@ -65,21 +65,54 @@ namespace dbgui::frontend
struct FrameWindow
{
void draw(const Frontend &);
bool draw(const Frontend &);
std::string id;
bool open;
};
struct DisasmWindow
{
struct Instruction
{
// "<mnem>%*c<op>%*c[ ; <comment>]"
std::string fmt_str;
uint64_t addr;
uint8_t mnem_len, op_len, comm_len;
};
bool draw(Frontend &);
void handle_data_res(const BackToFront::DataResult &result);
std::string id;
bool open;
bool first;
bool ip_unsuccessful;
bool disas_unsuccessful;
bool ip_changed;
size_t ip_src_id, disas_src_id;
uint64_t ip;
uint8_t max_mnem_len, max_op_len;
std::vector<Instruction> insts;
};
struct Window
{
WindowType type;
std::variant<std::monostate, RegWindow, ThreadWindow, FrameWindow> data;
std::variant<std::monostate, RegWindow, ThreadWindow, FrameWindow,
DisasmWindow>
data;
void draw(const Frontend &);
// if true, window is closed and should be deleted
bool draw(Frontend &);
static Window create_regs(size_t window_id);
static Window create_threads(size_t window_id);
static Window create_frames(size_t window_id);
static Window create_disas(size_t window_id);
void handle_data_res(const BackToFront::DataResult &result);
};
} // namespace dbgui::frontend

View File

@@ -6,6 +6,7 @@
#define IMGUI_INCLUDE_IMGUI_USER_H
#include "imgui.h"
#include "roboto_medium.h"
#include "fira_code.h"
#include "backends/imgui_impl_glfw.h"
#include "backends/imgui_impl_opengl3.h"
#include <stdio.h>
@@ -89,8 +90,12 @@ int main(int, char **)
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
//IM_ASSERT(font != nullptr);
//io.Fonts->AddFontFromMemoryCompressedBase85TTF(
// roboto_medium_compressed_data_base85, 16.0f);
io.Fonts->AddFontFromMemoryCompressedBase85TTF(
roboto_medium_compressed_data_base85, 16.0f);
fira_code_compressed_data_base85, 16.0f);
// Our state
bool show_demo_window = true;

View File

@@ -6,6 +6,8 @@
#include <vector>
#include <optional>
#include "data.h"
namespace dbgui
{
enum class TargetState : uint8_t
@@ -68,6 +70,7 @@ namespace dbgui
frame_changed,
frame_removed,
// TODO: frame_moved, frame_added, etc?
data_result,
};
struct StateChange
@@ -128,12 +131,17 @@ namespace dbgui
size_t idx;
};
struct DataResult
{
data::DataResult result;
};
struct Msg
{
MsgType type;
std::variant<std::monostate, StateChange, IPChange, InitialProcessInfo,
RegsChanged, ThreadChange, ThreadRemoved, FrameChanged,
FrameRemoved>
FrameRemoved, DataResult>
data;
};
} // namespace BackToFront

1038
src/roboto_medium.h Normal file

File diff suppressed because it is too large Load Diff

140
src/util/dag.h Normal file
View File

@@ -0,0 +1,140 @@
#pragma once
#include <unordered_set>
#include <cstddef>
#include <unordered_map>
#include <vector>
#include <queue>
namespace dbgui::util
{
// a bit overcomplicated but w/e
struct DAG
{
// all nodes without outgoing edges
std::unordered_set<size_t> nodes;
std::unordered_set<size_t> root_nodes;
std::unordered_map<size_t, std::vector<size_t>> out_edges;
std::unordered_map<size_t, std::vector<size_t>> in_edges;
void add_node(size_t id)
{
nodes.insert(id);
root_nodes.insert(id);
}
bool add_edge(size_t from, size_t to)
{
if (from == to)
{
return false;
}
if (!nodes.contains(from) || !nodes.contains(to))
{
return false;
}
out_edges[from].emplace_back(to);
in_edges[to].emplace_back(from);
root_nodes.erase(from);
return true;
}
bool remove_edge(size_t from, size_t to)
{
if (!nodes.contains(from) || !nodes.contains(to))
{
return false;
}
auto &out_edges = this->out_edges[from];
for (auto it = out_edges.begin(); it != out_edges.end(); ++it)
{
if (*it == to)
{
out_edges.erase(it);
break;
}
}
auto &in_edges = this->in_edges[to];
for (auto it = in_edges.begin(); it != in_edges.end(); ++it)
{
if (*it == from)
{
in_edges.erase(it);
break;
}
}
if (out_edges.empty())
{
root_nodes.emplace(from);
}
return true;
}
void remove_node(size_t id)
{
if (!nodes.contains(id))
{
return;
}
auto &in_edges = this->in_edges[id];
for (auto el : in_edges)
{
auto &out_edges = this->out_edges[el];
std::erase(out_edges, id);
if (out_edges.empty())
{
root_nodes.insert(el);
}
}
auto &out_edges = this->out_edges[id];
for (auto el : out_edges)
{
std::erase(this->in_edges[el], id);
}
this->in_edges.erase(id);
this->out_edges.erase(id);
this->nodes.erase(id);
this->root_nodes.erase(id);
}
void linearize(std::vector<size_t> &out)
{
// TODO: this will loop indefinitely if there is a cycle
// so maybe have at least a panic handler for that
out.clear();
// currently not very efficient
auto self_copy = *this;
while (!self_copy.nodes.empty())
{
size_t insert_idx = out.size();
for (auto id : self_copy.root_nodes)
{
out.push_back(insert_idx);
}
while (insert_idx < out.size())
{
self_copy.remove_node(out[insert_idx]);
insert_idx++;
}
}
}
};
} // namespace dbgui::util