breakpoints; thread/frame selection that should work; TODO: is slow
This commit is contained in:
@@ -25,6 +25,7 @@ Frontend::Frontend()
|
||||
_windows.push_back(Window::create_frames(window_id++));
|
||||
_windows.push_back(Window::create_disas(window_id++));
|
||||
_windows.push_back(Window::create_bp(window_id++));
|
||||
_windows.push_back(Window::create_source(window_id++));
|
||||
}
|
||||
|
||||
void Frontend::run_frame()
|
||||
@@ -132,7 +133,6 @@ void Frontend::draw_header()
|
||||
{
|
||||
if (ImGui::BeginMenuBar())
|
||||
{
|
||||
const auto orig_cursor_x = ImGui::GetCursorPosX();
|
||||
if (this->target)
|
||||
{
|
||||
switch (this->target->state)
|
||||
@@ -155,7 +155,7 @@ void Frontend::draw_header()
|
||||
}
|
||||
break;
|
||||
case running:
|
||||
if (ImGui::Button("Pause"))
|
||||
if (ImGui::Button("Pause "))
|
||||
{
|
||||
this->target->backend->pause();
|
||||
}
|
||||
@@ -168,9 +168,6 @@ void Frontend::draw_header()
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
// TODO: this depends on font-size, we just want to make sure the the other buttons don't flicker even if the state is changed
|
||||
ImGui::SetCursorPosX(orig_cursor_x + 71.f);
|
||||
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical, 2.f);
|
||||
|
||||
const auto is_paused =
|
||||
@@ -185,14 +182,14 @@ void Frontend::draw_header()
|
||||
|
||||
if (ImGui::Button("Step Over"))
|
||||
{
|
||||
if (this->target->backend->step_over())
|
||||
if (this->target->backend->step_over(!this->target->step_instruction))
|
||||
{
|
||||
// TODO: already disable the UI into running mode
|
||||
}
|
||||
}
|
||||
if (ImGui::Button("Step Into"))
|
||||
{
|
||||
if (this->target->backend->step_into())
|
||||
if (this->target->backend->step_into(!this->target->step_instruction))
|
||||
{
|
||||
// TODO: already disable the UI into running mode
|
||||
}
|
||||
@@ -225,6 +222,28 @@ void Frontend::draw_header()
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical, 2.f);
|
||||
|
||||
if (!this->target)
|
||||
{
|
||||
ImGui::Button("Step Mode: Source");
|
||||
} else
|
||||
{
|
||||
if (this->target->step_instruction)
|
||||
{
|
||||
if (ImGui::Button("Step Mode: Instruction"))
|
||||
{
|
||||
this->target->step_instruction = false;
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (ImGui::Button("Step Mode: Source"))
|
||||
{
|
||||
this->target->step_instruction = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
ImGui::End();
|
||||
@@ -334,6 +353,25 @@ void Frontend::handle_msgs()
|
||||
{
|
||||
window.handle_data_res(result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case selected_frame_changed:
|
||||
{
|
||||
if (this->target)
|
||||
{
|
||||
this->target->selected_frame =
|
||||
std::get<BackToFront::SelectedFrameChanged>(msg->data).idx;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case selected_thread_changed:
|
||||
{
|
||||
if (this->target)
|
||||
{
|
||||
this->target->selected_thread =
|
||||
std::get<BackToFront::SelectedThreadChanged>(msg->data).idx;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -386,7 +424,7 @@ void Frontend::handle_reg_change(const BackToFront::RegsChanged &info)
|
||||
void Frontend::handle_thread_remove(const BackToFront::ThreadRemoved &info)
|
||||
{
|
||||
auto &threads = this->target->threads;
|
||||
if (threads.size() >= info.idx)
|
||||
if (threads.size() <= info.idx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -439,7 +477,7 @@ void Frontend::handle_thread_change(BackToFront::ThreadChange &&info)
|
||||
void Frontend::handle_frame_remove(const BackToFront::FrameRemoved &info)
|
||||
{
|
||||
auto &frames = this->target->frames;
|
||||
if (frames.size() >= info.idx)
|
||||
if (frames.size() <= info.idx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -458,6 +496,8 @@ void Frontend::handle_frame_change(BackToFront::FrameChanged &&info)
|
||||
auto &frame = frames[info.idx];
|
||||
if (!frame)
|
||||
{
|
||||
printf("Adding new frame at %u with name '%s'\n", info.idx,
|
||||
info.display_name.c_str());
|
||||
frame = Target::Frame{
|
||||
.ip = info.ip,
|
||||
.display_name = info.display_name,
|
||||
@@ -465,6 +505,8 @@ void Frontend::handle_frame_change(BackToFront::FrameChanged &&info)
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Updating frame at %u with name '%s'\n", info.idx,
|
||||
info.display_name.c_str());
|
||||
frame->ip = info.ip;
|
||||
frame->display_name = info.display_name;
|
||||
}
|
||||
@@ -41,11 +41,41 @@ namespace dbgui::frontend
|
||||
std::string display_name;
|
||||
};
|
||||
|
||||
// TODO: need a way for the backend to report where the breakpoint was actually placed
|
||||
// (might have been moved) or if it could be placed at all
|
||||
// iow let the backend resolve the address + possible file location for the breakpoint
|
||||
// and then display it
|
||||
// this should be needed anyways when you want to add expression-based breakpoints, e.g. 'main'
|
||||
struct Breakpoint
|
||||
{
|
||||
struct FileLoc
|
||||
{
|
||||
std::string name;
|
||||
uint32_t line;
|
||||
};
|
||||
|
||||
bool removed = false;
|
||||
// TODO: srcloc
|
||||
uint64_t addr;
|
||||
std::variant<uint64_t, FileLoc> data;
|
||||
|
||||
bool at_addr(uint64_t addr) const
|
||||
{
|
||||
return this->data.index() == 0
|
||||
&& std::get<uint64_t>(this->data) == addr;
|
||||
}
|
||||
|
||||
bool at_file_loc(std::string_view file, uint32_t line) const
|
||||
{
|
||||
if (this->data.index() != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto &loc = std::get<FileLoc>(this->data);
|
||||
if (loc.line == line && loc.name == file)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Target(std::string filename);
|
||||
@@ -55,6 +85,9 @@ namespace dbgui::frontend
|
||||
uint64_t id;
|
||||
size_t data_node_id = 0;
|
||||
Arch arch;
|
||||
bool step_instruction = false;
|
||||
uint16_t selected_thread = 0;
|
||||
uint16_t selected_frame = 0;
|
||||
|
||||
std::vector<RegSet> reg_sets;
|
||||
std::vector<std::optional<Thread>> threads;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "frontend.h"
|
||||
#include <algorithm>
|
||||
#include "imgui_internal.h"
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace dbgui;
|
||||
using namespace dbgui::frontend;
|
||||
@@ -23,6 +25,9 @@ bool Window::draw(Frontend &frontend)
|
||||
case breakpoints:
|
||||
return std::get<BreakpointWindow>(this->data).draw(frontend);
|
||||
break;
|
||||
case source:
|
||||
return std::get<SourceWindow>(this->data).draw(frontend);
|
||||
break;
|
||||
default: printf("Unhandled window draw: %u\n", this->type); exit(1);
|
||||
}
|
||||
}
|
||||
@@ -73,6 +78,16 @@ Window Window::create_bp(size_t id)
|
||||
.data = BreakpointWindow{.id = id_str, .open = true}};
|
||||
}
|
||||
|
||||
Window Window::create_source(size_t id)
|
||||
{
|
||||
auto id_str = std::string{"Source##"};
|
||||
id_str.append(std::to_string(id));
|
||||
|
||||
return Window{.type = WindowType::source,
|
||||
.data =
|
||||
SourceWindow{.id = id_str, .open = true, .first = true}};
|
||||
}
|
||||
|
||||
bool RegWindow::draw(const Frontend &frontend)
|
||||
{
|
||||
//ImGui::SetNextWindowDockID(frontend.dock_id, ImGuiCond_Appearing);
|
||||
@@ -202,7 +217,8 @@ bool ThreadWindow::draw(const Frontend &frontend)
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("table", 3,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg))
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg
|
||||
| ImGuiTableFlags_ScrollX))
|
||||
{
|
||||
ImGui::TableSetupColumn("ID");
|
||||
ImGui::TableSetupColumn("Name");
|
||||
@@ -222,6 +238,14 @@ bool ThreadWindow::draw(const Frontend &frontend)
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
|
||||
if (frontend.target->selected_thread == idx)
|
||||
{
|
||||
// TODO: style coloring
|
||||
const auto col =
|
||||
ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Button));
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, col);
|
||||
}
|
||||
|
||||
std::snprintf(buf, sizeof(buf), "%lu", thread->id);
|
||||
ImGui::Text(buf);
|
||||
ImGui::TableNextColumn();
|
||||
@@ -237,6 +261,45 @@ bool ThreadWindow::draw(const Frontend &frontend)
|
||||
std::snprintf(buf, sizeof(buf), "%lX", thread->ip);
|
||||
ImGui::Text(buf);
|
||||
}
|
||||
|
||||
auto min_pos =
|
||||
ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 0).GetTL();
|
||||
auto max_pos =
|
||||
ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 2).GetBR();
|
||||
|
||||
/*if (idx & 1)
|
||||
{
|
||||
ImGui::GetWindowDrawList()->AddRect(min_pos, max_pos,
|
||||
IM_COL32(255, 0, 0, 255));
|
||||
} else
|
||||
{
|
||||
ImGui::GetWindowDrawList()->AddRect(min_pos, max_pos,
|
||||
IM_COL32(0, 0, 255, 255));
|
||||
}*/
|
||||
|
||||
// TODO: better scrollbar handling
|
||||
auto win_pos = ImGui::GetWindowPos();
|
||||
auto min = ImGui::GetWindowContentRegionMin();
|
||||
auto max = ImGui::GetWindowContentRegionMax();
|
||||
min.x += win_pos.x;
|
||||
min.y += win_pos.y;
|
||||
max.x += win_pos.x;
|
||||
max.y += win_pos.y;
|
||||
|
||||
auto win = ImGui::GetCurrentWindow();
|
||||
if (win->ScrollbarY)
|
||||
{
|
||||
max.x -= win->ScrollbarSizes.x;
|
||||
}
|
||||
|
||||
ImGui::PushClipRect(min, max, false);
|
||||
if (frontend.target->selected_thread != idx
|
||||
&& ImGui::IsMouseClicked(ImGuiMouseButton_Left)
|
||||
&& ImGui::IsMouseHoveringRect(min_pos, max_pos, true))
|
||||
{
|
||||
frontend.target->backend->select_thread(idx);
|
||||
}
|
||||
ImGui::PopClipRect();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
@@ -273,7 +336,8 @@ bool FrameWindow::draw(const Frontend &frontend)
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("table", 1,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg))
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg
|
||||
| ImGuiTableFlags_ScrollX))
|
||||
{
|
||||
ImGui::TableSetupColumn("Location");
|
||||
ImGui::TableHeadersRow();
|
||||
@@ -291,6 +355,14 @@ bool FrameWindow::draw(const Frontend &frontend)
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
|
||||
if (frontend.target->selected_frame == idx)
|
||||
{
|
||||
// TODO: style coloring
|
||||
const auto col =
|
||||
ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Button));
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, col);
|
||||
}
|
||||
|
||||
if (frame->display_name != "")
|
||||
{
|
||||
ImGui::Text(frame->display_name.c_str());
|
||||
@@ -299,6 +371,23 @@ bool FrameWindow::draw(const Frontend &frontend)
|
||||
std::snprintf(buf, sizeof(buf), "%lX", frame->ip);
|
||||
ImGui::Text(buf);
|
||||
}
|
||||
|
||||
auto min_pos =
|
||||
ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 0).GetTL();
|
||||
auto max_pos =
|
||||
ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 0).GetBR();
|
||||
max_pos.y += ImGui::GetTextLineHeight();
|
||||
#if 0
|
||||
ImGui::GetWindowDrawList()->AddRect(min_pos, max_pos,
|
||||
IM_COL32(255, 0, 0, 255));
|
||||
#endif
|
||||
if (frontend.target->selected_frame != idx
|
||||
&& ImGui::IsMouseClicked(ImGuiMouseButton_Left)
|
||||
&& ImGui::IsMouseHoveringRect(min_pos, max_pos, true))
|
||||
{
|
||||
printf("Select frame %lu\n", idx);
|
||||
frontend.target->backend->select_frame(idx);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
@@ -348,7 +437,7 @@ bool DisasmWindow::draw(Frontend &frontend)
|
||||
|
||||
if (first)
|
||||
{
|
||||
auto found = false;
|
||||
/*auto found = false;
|
||||
uint16_t set_idx, reg_idx;
|
||||
for (size_t i = 0; i < frontend.target->reg_sets.size(); ++i)
|
||||
{
|
||||
@@ -375,16 +464,16 @@ bool DisasmWindow::draw(Frontend &frontend)
|
||||
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->ip_src_id,
|
||||
.type = data::DataNode::Type::data_source,
|
||||
.data = data::DataSource{
|
||||
.type = data::DataSource::Type::frame_ip,
|
||||
}});
|
||||
|
||||
frontend.target->backend->add_data_node(
|
||||
data::DataNode{.id = this->disas_src_id,
|
||||
@@ -418,7 +507,7 @@ bool DisasmWindow::draw(Frontend &frontend)
|
||||
auto &bps = frontend.target->breakpoints;
|
||||
const auto bp_it =
|
||||
std::find_if(bps.begin(), bps.end(),
|
||||
[inst](const auto &el) { return el.addr == inst.addr; });
|
||||
[inst](const auto &el) { return el.at_addr(inst.addr); });
|
||||
const auto is_bp = bp_it != bps.end() && !bp_it->removed;
|
||||
if (is_bp)
|
||||
{
|
||||
@@ -430,9 +519,16 @@ bool DisasmWindow::draw(Frontend &frontend)
|
||||
auto end_pos = pos;
|
||||
end_pos.x += 10.f;
|
||||
end_pos.y += 10.f;
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(pos, end_pos,
|
||||
IM_COL32(255, 0, 0, 255));
|
||||
|
||||
pos.x += 5.f;
|
||||
pos.y += 5.f;
|
||||
|
||||
ImGui::GetWindowDrawList()->AddCircleFilled(pos, 5.f,
|
||||
IM_COL32(255, 0, 0, 255));
|
||||
}
|
||||
|
||||
// TODO: write custom code to catch mouse clicks in the whole cell, including padding
|
||||
// to make targeting easier
|
||||
if (ImGui::InvisibleButton("Break",
|
||||
ImVec2{10.f, ImGui::GetTextLineHeight()}))
|
||||
{
|
||||
@@ -449,10 +545,10 @@ bool DisasmWindow::draw(Frontend &frontend)
|
||||
if (idx == bps.size())
|
||||
{
|
||||
bps.push_back(
|
||||
Target::Breakpoint{.removed = false, .addr = inst.addr});
|
||||
Target::Breakpoint{.removed = false, .data = inst.addr});
|
||||
} else
|
||||
{
|
||||
bps[idx] = Target::Breakpoint{.removed = false, .addr = inst.addr};
|
||||
bps[idx] = Target::Breakpoint{.removed = false, .data = inst.addr};
|
||||
}
|
||||
frontend.target->backend->add_breakpoint(inst.addr, idx);
|
||||
} else
|
||||
@@ -535,10 +631,11 @@ bool BreakpointWindow::draw(Frontend &frontend)
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("table", 2,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg))
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg
|
||||
| ImGuiTableFlags_SizingFixedFit))
|
||||
{
|
||||
ImGui::TableSetupColumn("ID");
|
||||
ImGui::TableSetupColumn("Address");
|
||||
ImGui::TableSetupColumn("Location");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
auto &bps = frontend.target->breakpoints;
|
||||
@@ -558,7 +655,14 @@ bool BreakpointWindow::draw(Frontend &frontend)
|
||||
ImGui::Text("%zu", idx);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::Text("%lX", bp.addr);
|
||||
if (auto addr = std::get_if<uint64_t>(&bp.data))
|
||||
{
|
||||
ImGui::Text("%lX", *addr);
|
||||
} else
|
||||
{
|
||||
const auto &loc = std::get<Target::Breakpoint::FileLoc>(bp.data);
|
||||
ImGui::Text("%s:%u", loc.name.c_str(), loc.line);
|
||||
}
|
||||
|
||||
auto min_pos =
|
||||
ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 0).GetTL();
|
||||
@@ -593,6 +697,277 @@ bool BreakpointWindow::draw(Frontend &frontend)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SourceWindow::draw(Frontend &frontend)
|
||||
{
|
||||
if (!ImGui::Begin(this->id.c_str()))
|
||||
{
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!frontend.target)
|
||||
{
|
||||
this->first = true;
|
||||
this->file_name = "";
|
||||
this->lines.clear();
|
||||
this->file_data.clear();
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (frontend.target->state == TargetState::stopped
|
||||
|| frontend.target->state == TargetState::startup)
|
||||
{
|
||||
this->file_name = "";
|
||||
this->lines.clear();
|
||||
this->file_data.clear();
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: we need to clean up the initializations/sync of the DAG somehow
|
||||
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->file_name = "";
|
||||
this->lines.clear();
|
||||
this->file_data.clear();
|
||||
return false;
|
||||
}*/
|
||||
|
||||
this->ip_src_id = frontend.target->data_node_id++;
|
||||
this->line_entry_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::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}});
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (!this->file_name.empty() && this->file_data.empty())
|
||||
{
|
||||
// parse file
|
||||
auto file =
|
||||
std::ifstream{this->file_name, std::ios::binary | std::ios::ate};
|
||||
if (file.is_open())
|
||||
{
|
||||
const auto size = file.tellg();
|
||||
if (size != 0)
|
||||
{
|
||||
this->file_data.resize(size);
|
||||
file.seekg(std::ios::beg);
|
||||
file.read(this->file_data.data(), this->file_data.size());
|
||||
|
||||
auto view =
|
||||
std::string_view{this->file_data.data(), this->file_data.size()};
|
||||
assert(this->lines.empty());
|
||||
while (true)
|
||||
{
|
||||
// TODO: better line handling?
|
||||
const auto pos = view.find_first_of('\n');
|
||||
if (pos == std::string_view::npos)
|
||||
{
|
||||
this->lines.push_back(view);
|
||||
break;
|
||||
}
|
||||
|
||||
this->lines.push_back(view.substr(0, pos));
|
||||
view.remove_prefix(pos + 1);
|
||||
}
|
||||
} else
|
||||
{
|
||||
printf("File is empty: %s\n", this->file_name.c_str());
|
||||
// prevent spamming the open call
|
||||
this->file_data.resize(1);
|
||||
}
|
||||
} else
|
||||
{
|
||||
printf("Couldn't open file: %s\n", this->file_name.c_str());
|
||||
// prevent spamming the open call
|
||||
this->file_data.resize(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->file_name.empty())
|
||||
{
|
||||
//auto txt_pos = ImGui::GetWindowPos();
|
||||
auto txt_pos = ImGui::GetWindowContentRegionMin();
|
||||
auto max = ImGui::GetWindowContentRegionMax();
|
||||
txt_pos.x += (max.x - txt_pos.x) * 0.5f;
|
||||
txt_pos.y += (max.y - txt_pos.y) * 0.5f;
|
||||
|
||||
const auto txt = "No source available";
|
||||
const auto txt_size = ImGui::CalcTextSize(txt);
|
||||
txt_pos.x -= txt_size.x * 0.5f;
|
||||
txt_pos.y -= txt_size.y * 0.5f;
|
||||
|
||||
ImGui::SetCursorPos(txt_pos);
|
||||
ImGui::TextDisabled(txt);
|
||||
}
|
||||
|
||||
auto pad = ImGui::GetStyle().CellPadding;
|
||||
pad.x += 10.f;
|
||||
|
||||
// TODO: use ImGuiListClipper to work with larger files
|
||||
if (ImGui::BeginTable("table", 3,
|
||||
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY
|
||||
| ImGuiTableFlags_PadOuterX
|
||||
| ImGuiTableFlags_ScrollX))
|
||||
{
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 10.f);
|
||||
ImGui::TableSetupColumn("LineNo");
|
||||
ImGui::TableSetupColumn("LineContent");
|
||||
//ImGui::TableHeadersRow();
|
||||
|
||||
for (size_t idx = 0; idx < this->lines.size(); ++idx)
|
||||
{
|
||||
//printf("Inst at %lx with '%s'\n", inst.addr, inst.fmt_str.c_str());
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
|
||||
ImGui::PushID(idx);
|
||||
auto &bps = frontend.target->breakpoints;
|
||||
const auto bp_it =
|
||||
std::find_if(bps.begin(), bps.end(), [this, idx](const auto &el) {
|
||||
return el.at_file_loc(this->file_name, idx + 1);
|
||||
});
|
||||
const auto is_bp = bp_it != bps.end() && !bp_it->removed;
|
||||
if (is_bp)
|
||||
{
|
||||
auto win_pos = ImGui::GetWindowPos();
|
||||
auto pos = ImGui::GetCursorPos();
|
||||
pos.x += win_pos.x;
|
||||
pos.y += win_pos.y - ImGui::GetScrollY();
|
||||
pos.y += (ImGui::GetTextLineHeight() - 10.f) / 2.f;
|
||||
auto end_pos = pos;
|
||||
end_pos.x += 10.f;
|
||||
end_pos.y += 10.f;
|
||||
|
||||
pos.x += 5.f;
|
||||
pos.y += 5.f;
|
||||
|
||||
ImGui::GetWindowDrawList()->AddCircleFilled(pos, 5.f,
|
||||
IM_COL32(255, 0, 0, 255));
|
||||
}
|
||||
|
||||
if (this->line == idx + 1)
|
||||
{
|
||||
auto win_pos = ImGui::GetWindowPos();
|
||||
auto pos = ImGui::GetCursorPos();
|
||||
pos.x += win_pos.x;
|
||||
pos.y += win_pos.y - ImGui::GetScrollY();
|
||||
pos.y += (ImGui::GetTextLineHeight() - 10.f) / 2.f;
|
||||
auto p2 = pos;
|
||||
auto p3 = pos;
|
||||
p2.x += 10.f;
|
||||
p2.y += 5.f;
|
||||
p3.y += 10.f;
|
||||
|
||||
ImGui::GetWindowDrawList()->AddTriangleFilled(
|
||||
pos, p2, p3, IM_COL32(227, 197, 103, 255));
|
||||
}
|
||||
|
||||
// TODO: write custom code to catch mouse clicks in the whole cell, including padding
|
||||
// to make targeting easier
|
||||
if (ImGui::InvisibleButton("Break",
|
||||
ImVec2{10.f, ImGui::GetTextLineHeight()}))
|
||||
{
|
||||
if (!is_bp)
|
||||
{
|
||||
size_t bp_idx = 0;
|
||||
for (; bp_idx < bps.size(); ++bp_idx)
|
||||
{
|
||||
if (bps[bp_idx].removed)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (frontend.target->backend->add_breakpoint(this->file_name.c_str(),
|
||||
idx + 1, bp_idx))
|
||||
{
|
||||
auto bp =
|
||||
Target::Breakpoint{.removed = false,
|
||||
.data = Target::Breakpoint::FileLoc{
|
||||
.name = this->file_name,
|
||||
.line = static_cast<uint32_t>(idx + 1)}};
|
||||
if (bp_idx == bps.size())
|
||||
{
|
||||
bps.push_back(bp);
|
||||
} else
|
||||
{
|
||||
bps[bp_idx] = bp;
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
frontend.target->backend->remove_breakpoint(bp_it - bps.begin());
|
||||
bp_it->removed = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (this->line == idx + 1)
|
||||
{
|
||||
// TODO: color config
|
||||
//const auto col =
|
||||
// ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Button));
|
||||
//ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, col);
|
||||
if (this->line_changed)
|
||||
{
|
||||
ImGui::SetScrollHereY();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text("%lu ", idx + 1);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
const auto line = this->lines[idx];
|
||||
ImGui::Text("%.*s", static_cast<int>(line.size()), line.data());
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
this->line_changed = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Window::handle_data_res(const BackToFront::DataResult &result)
|
||||
{
|
||||
switch (this->type)
|
||||
@@ -602,6 +977,9 @@ void Window::handle_data_res(const BackToFront::DataResult &result)
|
||||
case disassembly:
|
||||
std::get<DisasmWindow>(this->data).handle_data_res(result);
|
||||
break;
|
||||
case source:
|
||||
std::get<SourceWindow>(this->data).handle_data_res(result);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@@ -706,4 +1084,78 @@ void DisasmWindow::handle_data_res(
|
||||
|
||||
this->max_mnem_len = max_mnem_len;
|
||||
this->max_op_len = max_op_len;
|
||||
}
|
||||
|
||||
void SourceWindow::handle_data_res(
|
||||
const BackToFront::DataResult &result_wrapper)
|
||||
{
|
||||
const auto &result = result_wrapper.result;
|
||||
if (result.id == this->ip_src_id)
|
||||
{
|
||||
// should not need to care
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.id != this->line_entry_src_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success || result.type.type != data::TypeInfo::Type::custom)
|
||||
{
|
||||
this->lines.clear();
|
||||
this->file_data.clear();
|
||||
this->file_name.clear();
|
||||
this->line = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// parse line entry
|
||||
// struct LineEntry {
|
||||
// uint32_t line_no;
|
||||
// uint32_t name_len;
|
||||
// char file_name[];
|
||||
// };
|
||||
|
||||
if (result.data.size() < 8)
|
||||
{
|
||||
this->lines.clear();
|
||||
this->file_data.clear();
|
||||
this->file_name.clear();
|
||||
this->line = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto line = *reinterpret_cast<const uint32_t *>(result.data.data());
|
||||
const auto name_len =
|
||||
*reinterpret_cast<const uint32_t *>(result.data.data() + 4);
|
||||
if (result.data.size() < 8 + name_len)
|
||||
{
|
||||
this->lines.clear();
|
||||
this->file_data.clear();
|
||||
this->file_name.clear();
|
||||
this->line = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto file_view = std::string_view{
|
||||
reinterpret_cast<const char *>(result.data.data() + 8), name_len};
|
||||
|
||||
printf("New LE: %.*s:%u", static_cast<int>(file_view.size()),
|
||||
file_view.data(), line);
|
||||
|
||||
if (this->file_name != file_view)
|
||||
{
|
||||
// force reload
|
||||
this->lines.clear();
|
||||
this->file_data.clear();
|
||||
this->file_name = file_view;
|
||||
this->line_changed = true;
|
||||
}
|
||||
|
||||
if (this->line != line)
|
||||
{
|
||||
this->line = line;
|
||||
this->line_changed = true;
|
||||
}
|
||||
}
|
||||
@@ -107,11 +107,28 @@ namespace dbgui::frontend
|
||||
bool open;
|
||||
};
|
||||
|
||||
struct SourceWindow
|
||||
{
|
||||
bool draw(Frontend &);
|
||||
void handle_data_res(const BackToFront::DataResult &result);
|
||||
|
||||
std::string id;
|
||||
bool open;
|
||||
bool first;
|
||||
bool line_changed;
|
||||
|
||||
size_t ip_src_id, line_entry_src_id;
|
||||
std::string file_name;
|
||||
uint32_t line;
|
||||
std::vector<char> file_data;
|
||||
std::vector<std::string_view> lines;
|
||||
};
|
||||
|
||||
struct Window
|
||||
{
|
||||
WindowType type;
|
||||
std::variant<std::monostate, RegWindow, ThreadWindow, FrameWindow,
|
||||
DisasmWindow, BreakpointWindow>
|
||||
DisasmWindow, BreakpointWindow, SourceWindow>
|
||||
data;
|
||||
|
||||
// if true, window is closed and should be deleted
|
||||
@@ -122,6 +139,7 @@ namespace dbgui::frontend
|
||||
static Window create_frames(size_t window_id);
|
||||
static Window create_disas(size_t window_id);
|
||||
static Window create_bp(size_t window_id);
|
||||
static Window create_source(size_t window_id);
|
||||
|
||||
void handle_data_res(const BackToFront::DataResult &result);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user