None
272
interface/callstack/CallstackView.hpp
Normal file
@@ -0,0 +1,272 @@
|
||||
|
||||
#include <gtkmm.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
class CallstackItem
|
||||
{
|
||||
public:
|
||||
CallstackItem() = default;
|
||||
CallstackItem(Glib::ustring callPoint, Glib::ustring stackBase, Glib::ustring symbol, Glib::ustring module, Glib::ustring depth);
|
||||
CallstackItem(Glib::ustring callPoint, const std::vector<CallstackItem>& children);
|
||||
CallstackItem(const CallstackItem& src) = default;
|
||||
CallstackItem(CallstackItem&& src) noexcept = default;
|
||||
CallstackItem& operator=(const CallstackItem& src) = default;
|
||||
CallstackItem& operator=(CallstackItem&& src) noexcept = default;
|
||||
~CallstackItem() = default;
|
||||
|
||||
Glib::ustring callPoint;
|
||||
Glib::ustring stackBase;
|
||||
Glib::ustring symbol;
|
||||
Glib::ustring module;
|
||||
Glib::ustring depth;
|
||||
std::vector<CallstackItem> m_children;
|
||||
}; // CallstackItem
|
||||
|
||||
CallstackItem::CallstackItem(Glib::ustring callPoint, Glib::ustring stackBase, Glib::ustring symbol, Glib::ustring module, Glib::ustring depth)
|
||||
: callPoint(std::move(callPoint)), stackBase(std::move(stackBase)), symbol(std::move(symbol)), module(std::move(module)), depth(std::move(depth))
|
||||
{ }
|
||||
|
||||
CallstackItem::CallstackItem(Glib::ustring callPoint,
|
||||
const std::vector<CallstackItem>& children)
|
||||
: callPoint(std::move(callPoint)), m_children(children)
|
||||
{ }
|
||||
}
|
||||
|
||||
class CallstackView : public Gtk::ColumnView
|
||||
{
|
||||
protected:
|
||||
class ModelColumns : public Glib::Object
|
||||
{
|
||||
public:
|
||||
std::vector<CallstackItem> m_children;
|
||||
Glib::ustring callPoint;
|
||||
Glib::ustring stackBase;
|
||||
Glib::ustring symbol;
|
||||
Glib::ustring module;
|
||||
Glib::ustring depth;
|
||||
|
||||
static Glib::RefPtr<ModelColumns> create(const CallstackItem& item)
|
||||
{
|
||||
|
||||
return Glib::make_refptr_for_instance<ModelColumns>(new ModelColumns(item));
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit ModelColumns(const CallstackItem& item)
|
||||
: m_children(item.m_children), callPoint(item.callPoint),
|
||||
stackBase(item.stackBase), symbol(item.symbol),
|
||||
module(item.module), depth(item.depth)
|
||||
{ }
|
||||
}; // ModelColumns
|
||||
|
||||
protected:
|
||||
static Glib::RefPtr<Gio::ListModel> create_model(
|
||||
const Glib::RefPtr<Glib::ObjectBase>& item)
|
||||
{
|
||||
auto col = std::dynamic_pointer_cast<ModelColumns>(item);
|
||||
if (col && col->m_children.empty())
|
||||
return {};
|
||||
|
||||
auto result = Gio::ListStore<ModelColumns>::create();
|
||||
const std::vector<CallstackItem>& children = col ? col->m_children : std::vector<CallstackItem>();
|
||||
for (const auto& child : children)
|
||||
result->append(ModelColumns::create(child));
|
||||
return result;
|
||||
}
|
||||
|
||||
Glib::RefPtr<Gtk::TreeListModel> m_TreeListModel;
|
||||
Glib::RefPtr<Gtk::MultiSelection> m_TreeSelection;
|
||||
Glib::RefPtr<Gio::ListStore<ModelColumns>> _store;
|
||||
|
||||
public:
|
||||
void append_group(const std::string &name, const std::vector<CallstackItem> &g)
|
||||
{
|
||||
CallstackItem k(name, g);
|
||||
if(_store) _store->append(ModelColumns::create(k));
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
_store->remove_all();
|
||||
}
|
||||
|
||||
CallstackView (BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &refBuilder) : Gtk::ColumnView(cobject)
|
||||
{
|
||||
auto root = create_model({});
|
||||
|
||||
m_TreeListModel = Gtk::TreeListModel::create(root,
|
||||
sigc::ptr_fun(&CallstackView::create_model), false, true);
|
||||
m_TreeSelection = Gtk::MultiSelection::create(m_TreeListModel);
|
||||
set_model(m_TreeSelection);
|
||||
{
|
||||
auto factory = Gtk::SignalListItemFactory::create();
|
||||
factory->signal_setup().connect(
|
||||
sigc::mem_fun(*this, &CallstackView::on_setup_keylabel));
|
||||
factory->signal_bind().connect(
|
||||
sigc::mem_fun(*this, &CallstackView::on_bind_callPoint));
|
||||
auto column = Gtk::ColumnViewColumn::create("Call Point", factory);
|
||||
column->set_fixed_width(200);
|
||||
append_column(column);
|
||||
|
||||
factory = Gtk::SignalListItemFactory::create();
|
||||
factory->signal_setup().connect(
|
||||
sigc::mem_fun(*this, &CallstackView::on_setup_label));
|
||||
factory->signal_bind().connect(
|
||||
sigc::mem_fun(*this, &CallstackView::on_bind_stackBase));
|
||||
|
||||
column = Gtk::ColumnViewColumn::create("Stack Base", factory);
|
||||
column->set_expand();
|
||||
append_column(column);
|
||||
|
||||
factory = Gtk::SignalListItemFactory::create();
|
||||
factory->signal_setup().connect(
|
||||
sigc::mem_fun(*this, &CallstackView::on_setup_label));
|
||||
factory->signal_bind().connect(
|
||||
sigc::mem_fun(*this, &CallstackView::on_bind_symbol));
|
||||
|
||||
column = Gtk::ColumnViewColumn::create("Symbol", factory);
|
||||
column->set_expand();
|
||||
append_column(column);
|
||||
|
||||
factory = Gtk::SignalListItemFactory::create();
|
||||
factory->signal_setup().connect(
|
||||
sigc::mem_fun(*this, &CallstackView::on_setup_label));
|
||||
factory->signal_bind().connect(
|
||||
sigc::mem_fun(*this, &CallstackView::on_bind_module));
|
||||
|
||||
column = Gtk::ColumnViewColumn::create("Module", factory);
|
||||
column->set_expand();
|
||||
append_column(column);
|
||||
|
||||
factory = Gtk::SignalListItemFactory::create();
|
||||
factory->signal_setup().connect(
|
||||
sigc::mem_fun(*this, &CallstackView::on_setup_label));
|
||||
factory->signal_bind().connect(
|
||||
sigc::mem_fun(*this, &CallstackView::on_bind_depth));
|
||||
|
||||
column = Gtk::ColumnViewColumn::create("Depth", factory);
|
||||
column->set_expand();
|
||||
append_column(column);
|
||||
}
|
||||
|
||||
_store = std::dynamic_pointer_cast<Gio::ListStore<ModelColumns>>(root);
|
||||
}
|
||||
|
||||
void on_setup_keylabel(
|
||||
const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
// Each ListItem contains a TreeExpander, which contains a Label.
|
||||
// The Label shows the ModelColumns::m_holiday_name. That's done in on_bind_holiday().
|
||||
auto expander = Gtk::make_managed<Gtk::TreeExpander>();
|
||||
auto label = Gtk::make_managed<Gtk::Label>();
|
||||
label->set_halign(Gtk::Align::START);
|
||||
expander->set_child(*label);
|
||||
list_item->set_child(*expander);
|
||||
}
|
||||
|
||||
void on_setup_label(
|
||||
const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
// Each ListItem contains a TreeExpander, which contains a Label.
|
||||
// The Label shows the ModelColumns::m_holiday_name. That's done in on_bind_holiday().
|
||||
auto label = Gtk::make_managed<Gtk::Label>();
|
||||
label->set_halign(Gtk::Align::START);
|
||||
list_item->set_child(*label);
|
||||
}
|
||||
|
||||
void on_bind_callPoint(
|
||||
const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto row = std::dynamic_pointer_cast<Gtk::TreeListRow>(list_item->get_item());
|
||||
if (!row)
|
||||
return;
|
||||
auto col = std::dynamic_pointer_cast<ModelColumns>(row->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
auto expander = dynamic_cast<Gtk::TreeExpander*>(list_item->get_child());
|
||||
if (!expander)
|
||||
return;
|
||||
expander->set_list_row(row);
|
||||
auto label = dynamic_cast<Gtk::Label*>(expander->get_child());
|
||||
if (!label)
|
||||
return;
|
||||
label->set_text(col->callPoint);
|
||||
}
|
||||
|
||||
void on_bind_stackBase(
|
||||
const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto row = std::dynamic_pointer_cast<Gtk::TreeListRow>(list_item->get_item());
|
||||
if (!row)
|
||||
return;
|
||||
auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
|
||||
if (!label)
|
||||
return;
|
||||
label->set_visible(!row->is_expandable());
|
||||
if (row->is_expandable())
|
||||
return;
|
||||
|
||||
auto col = std::dynamic_pointer_cast<ModelColumns>(row->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
label->set_text(col->stackBase);
|
||||
}
|
||||
|
||||
void on_bind_symbol(
|
||||
const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto row = std::dynamic_pointer_cast<Gtk::TreeListRow>(list_item->get_item());
|
||||
if (!row)
|
||||
return;
|
||||
auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
|
||||
if (!label)
|
||||
return;
|
||||
label->set_visible(!row->is_expandable());
|
||||
if (row->is_expandable())
|
||||
return;
|
||||
|
||||
auto col = std::dynamic_pointer_cast<ModelColumns>(row->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
label->set_text(col->symbol);
|
||||
}
|
||||
|
||||
void on_bind_module(
|
||||
const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto row = std::dynamic_pointer_cast<Gtk::TreeListRow>(list_item->get_item());
|
||||
if (!row)
|
||||
return;
|
||||
auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
|
||||
if (!label)
|
||||
return;
|
||||
label->set_visible(!row->is_expandable());
|
||||
if (row->is_expandable())
|
||||
return;
|
||||
|
||||
auto col = std::dynamic_pointer_cast<ModelColumns>(row->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
label->set_text(col->module);
|
||||
}
|
||||
|
||||
void on_bind_depth(
|
||||
const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto row = std::dynamic_pointer_cast<Gtk::TreeListRow>(list_item->get_item());
|
||||
if (!row)
|
||||
return;
|
||||
auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
|
||||
if (!label)
|
||||
return;
|
||||
label->set_visible(!row->is_expandable());
|
||||
if (row->is_expandable())
|
||||
return;
|
||||
|
||||
auto col = std::dynamic_pointer_cast<ModelColumns>(row->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
label->set_text(col->depth);
|
||||
}
|
||||
};
|
||||
358
interface/cpu/CPUView.hpp
Normal file
@@ -0,0 +1,358 @@
|
||||
//
|
||||
// Created by acite on 5/1/24.
|
||||
//
|
||||
|
||||
#include <gtkmm.h>
|
||||
#include <capstone/capstone.h>
|
||||
|
||||
#include "../../core/Core.hpp"
|
||||
|
||||
namespace {
|
||||
class CPUModelColumns : public Glib::Object {
|
||||
public:
|
||||
Glib::PropertyProxy<bool> propertyBroke() { return _broke.get_proxy(); }
|
||||
Glib::PropertyProxy<std::vector<std::string>> propertyClasses() { return _classes.get_proxy(); }
|
||||
|
||||
Glib::ustring Address;
|
||||
Glib::ustring Data;
|
||||
Glib::ustring Disassembly;
|
||||
Glib::RefPtr<Glib::Binding> BindingToCheckbutton = nullptr;
|
||||
|
||||
Glib::RefPtr<Glib::Binding> BindingToAddress = nullptr;
|
||||
Glib::RefPtr<Glib::Binding> BindingToData = nullptr;
|
||||
Glib::RefPtr<Glib::Binding> BindingToDisassembly = nullptr;
|
||||
std::shared_ptr<Core> core = nullptr;
|
||||
|
||||
|
||||
static Glib::RefPtr<CPUModelColumns> create(bool broke, const Glib::ustring &address,
|
||||
const Glib::ustring &data, const Glib::ustring &disassembly, std::shared_ptr<Core> _core){
|
||||
return Glib::make_refptr_for_instance<CPUModelColumns>(
|
||||
new CPUModelColumns(broke, address, data, disassembly, std::move(_core)));
|
||||
}
|
||||
|
||||
protected:
|
||||
CPUModelColumns(bool broke, Glib::ustring address,
|
||||
Glib::ustring data, Glib::ustring disassembly, std::shared_ptr<Core> _core)
|
||||
: Glib::ObjectBase(typeid(CPUModelColumns)),
|
||||
Address(std::move(address)), Data(std::move(data)), Disassembly(std::move(disassembly)),
|
||||
_broke(*this, "broke", broke),
|
||||
_classes(*this, "highlighted", {}), core(std::move(_core)){
|
||||
propertyBroke().signal_changed().connect(
|
||||
sigc::mem_fun(*this, &CPUModelColumns::on_fixed_changed));
|
||||
}
|
||||
|
||||
Glib::Property<bool> _broke;
|
||||
Glib::Property<std::vector<std::string>> _classes;
|
||||
|
||||
void on_fixed_changed() {
|
||||
if(propertyBroke().get_value())
|
||||
{
|
||||
core->queueMessage(MSG_BREAK, (void*)std::stoull(Address, nullptr, 16));
|
||||
}
|
||||
else
|
||||
{
|
||||
core->queueMessage(MSG_DELBREAK, (void*)std::stoull(Address, nullptr, 16));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class CPUView : public Gtk::ColumnView
|
||||
{
|
||||
public:
|
||||
uint64_t rip = 0;
|
||||
int _lastSelectedRow = -1;
|
||||
std::shared_ptr<Core> core = nullptr;
|
||||
|
||||
protected:
|
||||
Glib::RefPtr<Gio::ListStore<CPUModelColumns>> _codeViewStore;
|
||||
Glib::RefPtr<Gtk::SingleSelection> _model;
|
||||
|
||||
[[nodiscard]]static std::vector<cs_insn> disassembly(uint8_t* data, int cc, size_t size, int address)
|
||||
{
|
||||
std::vector<cs_insn> r;
|
||||
|
||||
csh handle;
|
||||
cs_insn *insn;
|
||||
size_t count;
|
||||
|
||||
if (cs_open(CS_ARCH_X86, CS_MODE_64, &handle) != CS_ERR_OK)
|
||||
return {};
|
||||
cs_option(handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
|
||||
|
||||
for(int i=0, sz = 0;i<cc && sz < size;i++)
|
||||
{
|
||||
count = cs_disasm(handle, (uint8_t*)data, 32, address, 1, &insn);
|
||||
if(count == 1)
|
||||
{
|
||||
cs_insn k{};
|
||||
memcpy(&k, &insn[0], sizeof(cs_insn));
|
||||
r.emplace_back(k);
|
||||
address += k.size;
|
||||
data += k.size;
|
||||
sz += k.size;
|
||||
}else break;
|
||||
}
|
||||
|
||||
cs_free(insn, count);
|
||||
cs_close(&handle);
|
||||
|
||||
return std::move(r);
|
||||
}
|
||||
|
||||
public:
|
||||
CPUView(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &refBuilder) : Gtk::ColumnView(cobject)
|
||||
{
|
||||
_codeViewStore = Gio::ListStore<CPUModelColumns>::create();
|
||||
_model = Gtk::SingleSelection::create(_codeViewStore);
|
||||
_model->set_autoselect(false);
|
||||
_model->set_can_unselect(true);
|
||||
set_model(_model);
|
||||
set_reorderable(false);
|
||||
set_hexpand();
|
||||
set_vexpand();
|
||||
add_cols();
|
||||
|
||||
signal_activate().connect([this](guint i){
|
||||
auto row = _codeViewStore->get_item(i);
|
||||
bool old = row->propertyBroke().get_value();
|
||||
row->propertyBroke().set_value(!old);
|
||||
});
|
||||
}
|
||||
|
||||
void setData(const std::vector<cs_insn>& insn)
|
||||
{
|
||||
_lastSelectedRow = -1;
|
||||
clear();
|
||||
for (const auto &k : insn) {
|
||||
char cmdAddr[32], insByte[32];
|
||||
std::string bytes, cmdStr;
|
||||
for(int j = 0;j<k.size;j++)
|
||||
{
|
||||
sprintf(insByte, "%2x", k.bytes[j]);
|
||||
bytes += insByte;
|
||||
}
|
||||
sprintf(cmdAddr, "%16lx", k.address);
|
||||
cmdStr = k.mnemonic;
|
||||
cmdStr += " ";
|
||||
cmdStr += k.op_str;
|
||||
|
||||
add_row(cmdAddr, bytes, cmdStr, core->ContainsBreakPoint(k.address));
|
||||
}
|
||||
}
|
||||
|
||||
void add_row(const std::string& addr,
|
||||
const std::string& data,
|
||||
const std::string& disassembly,
|
||||
bool isBroke
|
||||
){
|
||||
_codeViewStore->append(CPUModelColumns::create(isBroke, addr, data, disassembly, core));
|
||||
}
|
||||
|
||||
|
||||
void clear()
|
||||
{
|
||||
_codeViewStore->remove_all();
|
||||
}
|
||||
|
||||
uint64_t GetSelected()
|
||||
{
|
||||
auto i = _model->get_selected();
|
||||
if(i == GTK_INVALID_LIST_POSITION) return 0;
|
||||
|
||||
auto k = _codeViewStore->get_item(i);
|
||||
return std::stoull(k->Address, nullptr, 16);
|
||||
}
|
||||
|
||||
void Select()
|
||||
{
|
||||
for (int i=0;i<_codeViewStore->get_n_items();i++) {
|
||||
auto row = _codeViewStore->get_item(i);
|
||||
std::string str = row->Address;
|
||||
if (std::stoull(str, nullptr, 16) == rip) {
|
||||
if(_lastSelectedRow >= 0)
|
||||
_codeViewStore->get_item(_lastSelectedRow)->propertyClasses().set_value({});
|
||||
_lastSelectedRow = i;
|
||||
row->propertyClasses().set_value({"highlight"});
|
||||
|
||||
_model->select_item(i, true);
|
||||
gtk_column_view_scroll_to(this->gobj(), i, nullptr, GTK_LIST_SCROLL_NONE, nullptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
queue_draw();
|
||||
}
|
||||
|
||||
void add_cols()
|
||||
{
|
||||
/* column for fixed toggles */
|
||||
auto factory = Gtk::SignalListItemFactory::create();
|
||||
factory->signal_setup().connect(
|
||||
sigc::mem_fun(*this, &CPUView::onSetupCheckbutton));
|
||||
factory->signal_bind().connect(
|
||||
sigc::mem_fun(*this, &CPUView::onBindBroke));
|
||||
factory->signal_unbind().connect(
|
||||
sigc::mem_fun(*this, &CPUView::onUnbindBroke));
|
||||
auto column = Gtk::ColumnViewColumn::create("Broke", factory);
|
||||
append_column(column);
|
||||
|
||||
factory = Gtk::SignalListItemFactory::create();
|
||||
factory->signal_setup().connect(sigc::bind(sigc::mem_fun(*this,
|
||||
&CPUView::onSetupLabel), Gtk::Align::START));
|
||||
factory->signal_bind().connect(
|
||||
sigc::mem_fun(*this, &CPUView::onBindAddress));
|
||||
factory->signal_unbind().connect(
|
||||
sigc::mem_fun(*this, &CPUView::onUnbindAddress));
|
||||
column = Gtk::ColumnViewColumn::create("Address", factory);
|
||||
column->set_fixed_width(150);
|
||||
append_column(column);
|
||||
|
||||
factory = Gtk::SignalListItemFactory::create();
|
||||
factory->signal_setup().connect(sigc::bind(sigc::mem_fun(*this,
|
||||
&CPUView::onSetupLabel), Gtk::Align::START));
|
||||
factory->signal_bind().connect(
|
||||
sigc::mem_fun(*this, &CPUView::onBindData));
|
||||
factory->signal_unbind().connect(
|
||||
sigc::mem_fun(*this, &CPUView::onUnbindData));
|
||||
|
||||
column = Gtk::ColumnViewColumn::create("Data", factory);
|
||||
append_column(column);
|
||||
|
||||
factory = Gtk::SignalListItemFactory::create();
|
||||
factory->signal_setup().connect(sigc::bind(sigc::mem_fun(*this,
|
||||
&CPUView::onSetupLabel), Gtk::Align::START));
|
||||
factory->signal_bind().connect(
|
||||
sigc::mem_fun(*this, &CPUView::onBindDisassembly));
|
||||
factory->signal_unbind().connect(
|
||||
sigc::mem_fun(*this, &CPUView::onUnbindDisassembly));
|
||||
|
||||
column = Gtk::ColumnViewColumn::create("Disassembly", factory);
|
||||
column->set_expand();
|
||||
append_column(column);
|
||||
}
|
||||
|
||||
void onSetupCheckbutton(const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto checkbutton = Gtk::make_managed<Gtk::CheckButton>();
|
||||
checkbutton->set_halign(Gtk::Align::CENTER);
|
||||
checkbutton->set_valign(Gtk::Align::CENTER);
|
||||
list_item->set_child(*checkbutton);
|
||||
}
|
||||
|
||||
void onSetupLabel(
|
||||
const Glib::RefPtr<Gtk::ListItem>& list_item, Gtk::Align halign)
|
||||
{
|
||||
auto label = Gtk::make_managed<Gtk::Label>("", halign);
|
||||
list_item->set_child(*label);
|
||||
}
|
||||
|
||||
void onBindBroke(const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto col = std::dynamic_pointer_cast<CPUModelColumns>(list_item->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
auto checkbutton = dynamic_cast<Gtk::CheckButton*>(list_item->get_child());
|
||||
if (!checkbutton)
|
||||
return;
|
||||
checkbutton->set_active(col->propertyBroke());
|
||||
|
||||
if (col->BindingToCheckbutton)
|
||||
col->BindingToCheckbutton->unbind();
|
||||
col->BindingToCheckbutton = Glib::Binding::bind_property(
|
||||
checkbutton->property_active(), col->propertyBroke(),
|
||||
Glib::Binding::Flags::BIDIRECTIONAL);
|
||||
}
|
||||
|
||||
void onUnbindBroke(const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto col = std::dynamic_pointer_cast<CPUModelColumns>(list_item->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
if (col->BindingToCheckbutton)
|
||||
col->BindingToCheckbutton->unbind();
|
||||
col->BindingToCheckbutton.reset();
|
||||
}
|
||||
|
||||
void onBindAddress(const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto col = std::dynamic_pointer_cast<CPUModelColumns>(list_item->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
|
||||
if (!label)
|
||||
return;
|
||||
std::string tx = label->get_text();
|
||||
label->set_text(col->Address);
|
||||
|
||||
if (col->BindingToAddress)
|
||||
col->BindingToAddress->unbind();
|
||||
col->BindingToAddress = Glib::Binding::bind_property(
|
||||
label->property_css_classes(), col->propertyClasses(),
|
||||
Glib::Binding::Flags::BIDIRECTIONAL);
|
||||
}
|
||||
|
||||
void onUnbindAddress(const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto col = std::dynamic_pointer_cast<CPUModelColumns>(list_item->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
if (col->BindingToAddress)
|
||||
col->BindingToAddress->unbind();
|
||||
col->BindingToAddress.reset();
|
||||
}
|
||||
|
||||
void onBindData(const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto col = std::dynamic_pointer_cast<CPUModelColumns>(list_item->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
|
||||
if (!label)
|
||||
return;
|
||||
label->set_text(col->Data + " ");
|
||||
|
||||
if (col->BindingToData)
|
||||
col->BindingToData->unbind();
|
||||
col->BindingToData = Glib::Binding::bind_property(
|
||||
label->property_css_classes(), col->propertyClasses(),
|
||||
Glib::Binding::Flags::BIDIRECTIONAL);
|
||||
}
|
||||
|
||||
void onUnbindData(const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto col = std::dynamic_pointer_cast<CPUModelColumns>(list_item->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
if (col->BindingToData)
|
||||
col->BindingToData->unbind();
|
||||
col->BindingToData.reset();
|
||||
}
|
||||
|
||||
void onBindDisassembly(const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto col = std::dynamic_pointer_cast<CPUModelColumns>(list_item->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
|
||||
if (!label)
|
||||
return;
|
||||
label->set_text(col->Disassembly);
|
||||
|
||||
if (col->BindingToDisassembly)
|
||||
col->BindingToDisassembly->unbind();
|
||||
col->BindingToDisassembly = Glib::Binding::bind_property(
|
||||
label->property_css_classes(), col->propertyClasses(),
|
||||
Glib::Binding::Flags::BIDIRECTIONAL);
|
||||
}
|
||||
|
||||
void onUnbindDisassembly(const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto col = std::dynamic_pointer_cast<CPUModelColumns>(list_item->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
if (col->BindingToDisassembly)
|
||||
col->BindingToDisassembly->unbind();
|
||||
col->BindingToDisassembly.reset();
|
||||
}
|
||||
|
||||
};
|
||||
32
interface/cpu/RegView.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
#include <gtkmm.h>
|
||||
|
||||
class RegView : public Gtk::TreeView
|
||||
{
|
||||
private:
|
||||
Glib::RefPtr<Gtk::ListStore> _regViewStore;
|
||||
|
||||
public:
|
||||
RegView(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &refBuilder) : Gtk::TreeView(cobject),
|
||||
_regViewStore(std::dynamic_pointer_cast<Gtk::ListStore>(get_model()))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void add_data(const std::string& reg, uint64_t value)
|
||||
{
|
||||
auto mm = _regViewStore->append();
|
||||
Gtk::TreeModel::Row row = *mm;
|
||||
|
||||
char regv[64];
|
||||
sprintf(regv, "%lX", value);
|
||||
|
||||
row.set_value(0, reg);
|
||||
row.set_value(1, std::string(regv));
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
_regViewStore->clear();
|
||||
}
|
||||
};
|
||||
79
interface/cpu/SymbolMenu.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
|
||||
#include <gtkmm.h>
|
||||
|
||||
class SymbolMenu;
|
||||
|
||||
class SelfContainedButton : public Gtk::Button
|
||||
{
|
||||
typedef void(*Action)(const std::string &, SymbolMenu* menu, const Symbol& sym);
|
||||
|
||||
public:
|
||||
SelfContainedButton(SymbolMenu* menu, const Symbol& sym) : Gtk::Button(), _menu(menu), _sym(sym)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SetAction(Action ac)
|
||||
{
|
||||
_action = ac;
|
||||
signal_clicked().connect(sigc::mem_fun(*this, &SelfContainedButton::clicked));
|
||||
}
|
||||
|
||||
private:
|
||||
void clicked()
|
||||
{
|
||||
_action(get_label(), _menu, _sym);
|
||||
}
|
||||
|
||||
Action _action = nullptr;
|
||||
SymbolMenu* _menu = nullptr;
|
||||
Symbol _sym;
|
||||
};
|
||||
|
||||
class SymbolMenu : public Gtk::MenuButton
|
||||
{
|
||||
private:
|
||||
void sendMessage(char id, void* param) const
|
||||
{
|
||||
DebugCommand msg{id, param};
|
||||
if(port != -1)
|
||||
{
|
||||
write(port, &msg, sizeof(DebugCommand));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Glib::RefPtr<Gtk::Box> popoverBox = nullptr;
|
||||
std::vector<SelfContainedButton*> buttonList;
|
||||
Glib::RefPtr<Gtk::Label> childLabel = nullptr;
|
||||
int port = -1;
|
||||
|
||||
public:
|
||||
SymbolMenu(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &refBuilder) : Gtk::MenuButton(cobject)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UpdateList(const std::vector<Symbol> &list)
|
||||
{
|
||||
if(popoverBox)
|
||||
{
|
||||
for(const auto &k : buttonList)
|
||||
{
|
||||
popoverBox->remove(*k);
|
||||
}
|
||||
buttonList.clear();
|
||||
|
||||
for(const auto &k : list)
|
||||
{
|
||||
auto b = Gtk::make_managed<SelfContainedButton>(this, k);
|
||||
b->set_label(k.name);
|
||||
b->SetAction([](const std::string& label, SymbolMenu* menu, const Symbol& sym){
|
||||
menu->sendMessage(MSG_SWITCHSYMBOL, (void*)&sym);
|
||||
});
|
||||
buttonList.push_back(b);
|
||||
popoverBox->append(*b);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
225
interface/editor/EditorView.hpp
Normal file
@@ -0,0 +1,225 @@
|
||||
|
||||
#include <gtkmm.h>
|
||||
#include <format>
|
||||
#include <unordered_map>
|
||||
|
||||
std::unordered_map<uint32_t, int> allowedChar
|
||||
{
|
||||
{'0', 1},
|
||||
{'1', 1},
|
||||
{'2', 1},
|
||||
{'3', 1},
|
||||
{'4', 1},
|
||||
{'5', 1},
|
||||
{'6', 1},
|
||||
{'7', 1},
|
||||
{'8', 1},
|
||||
{'9', 1},
|
||||
{'0', 1},
|
||||
{'a', 1},
|
||||
{'b', 1},
|
||||
{'c', 1},
|
||||
{'d', 1},
|
||||
{'e', 1},
|
||||
{'f', 1},
|
||||
{'A', 1},
|
||||
{'B', 1},
|
||||
{'C', 1},
|
||||
{'D', 1},
|
||||
{'E', 1},
|
||||
{'F', 1},
|
||||
};
|
||||
|
||||
class NoNewlineTextView : public Gtk::TextView {
|
||||
public:
|
||||
NoNewlineTextView() {
|
||||
|
||||
auto buffer = get_buffer();
|
||||
buffer->signal_insert().connect(sigc::mem_fun(*this, &NoNewlineTextView::on_insert_text), false);
|
||||
}
|
||||
|
||||
protected:
|
||||
void on_insert_text(const Gtk::TextBuffer::iterator& pos, const Glib::ustring& text, int x) {
|
||||
for(const auto &k : text)
|
||||
{
|
||||
if(!allowedChar[k] || get_buffer()->get_text().size() + text.size() > 2)
|
||||
{
|
||||
get_buffer()->signal_insert().emission_stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void on_focus_out()
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
class EditorModelColumns : public Glib::Object {
|
||||
public:
|
||||
Glib::ustring baseAddress;
|
||||
|
||||
Glib::RefPtr<Glib::Binding> bindingToValues[16];
|
||||
Glib::PropertyProxy<Glib::ustring> propertyValues(int i) { return _values[i].get_proxy(); }
|
||||
|
||||
static Glib::RefPtr<EditorModelColumns> create(const Glib::ustring &baseAddress, uint8_t values[16]){
|
||||
return Glib::make_refptr_for_instance<EditorModelColumns>(new EditorModelColumns(baseAddress, values));
|
||||
}
|
||||
|
||||
protected:
|
||||
Glib::Property<Glib::ustring> _values[16];
|
||||
|
||||
EditorModelColumns(Glib::ustring baseAddress, uint8_t values[16]) : Glib::ObjectBase(typeid(EditorModelColumns)), baseAddress(std::move(baseAddress)),_values{
|
||||
Glib::Property<Glib::ustring> (*this, "i1", std::format("{:02x}", values[0])),
|
||||
Glib::Property<Glib::ustring> (*this, "i2", std::format("{:02x}", values[1])),
|
||||
Glib::Property<Glib::ustring> (*this, "i3", std::format("{:02x}", values[2])),
|
||||
Glib::Property<Glib::ustring> (*this, "i4", std::format("{:02x}", values[3])),
|
||||
Glib::Property<Glib::ustring> (*this, "i5", std::format("{:02x}", values[4])),
|
||||
Glib::Property<Glib::ustring> (*this, "i6", std::format("{:02x}", values[5])),
|
||||
Glib::Property<Glib::ustring> (*this, "i7", std::format("{:02x}", values[6])),
|
||||
Glib::Property<Glib::ustring> (*this, "i8", std::format("{:02x}", values[7])),
|
||||
Glib::Property<Glib::ustring> (*this, "i9", std::format("{:02x}", values[8])),
|
||||
Glib::Property<Glib::ustring> (*this, "i10", std::format("{:02x}", values[9])),
|
||||
Glib::Property<Glib::ustring> (*this, "i11", std::format("{:02x}", values[10])),
|
||||
Glib::Property<Glib::ustring> (*this, "i12", std::format("{:02x}", values[11])),
|
||||
Glib::Property<Glib::ustring> (*this, "i13", std::format("{:02x}", values[12])),
|
||||
Glib::Property<Glib::ustring> (*this, "i14", std::format("{:02x}", values[13])),
|
||||
Glib::Property<Glib::ustring> (*this, "i15", std::format("{:02x}", values[14])),
|
||||
Glib::Property<Glib::ustring> (*this, "i16", std::format("{:02x}", values[15]))} { }
|
||||
};
|
||||
}
|
||||
|
||||
class EditorView : public Gtk::ColumnView {
|
||||
private:
|
||||
Glib::RefPtr<Gio::ListStore<EditorModelColumns>> _editorModel;
|
||||
Glib::RefPtr<Gtk::SingleSelection> _model;
|
||||
|
||||
public:
|
||||
EditorView(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &refBuilder) : Gtk::ColumnView(cobject)
|
||||
{
|
||||
_editorModel = Gio::ListStore<EditorModelColumns>::create();
|
||||
_model = Gtk::SingleSelection::create(_editorModel);
|
||||
_model->set_autoselect(false);
|
||||
_model->set_can_unselect(true);
|
||||
set_model(_model);
|
||||
set_reorderable(false);
|
||||
set_hexpand();
|
||||
set_vexpand();
|
||||
|
||||
add_cols();
|
||||
add_row("7fff7e");
|
||||
add_row("7fff7f");
|
||||
add_row("7fff71");
|
||||
add_row("7fff72");
|
||||
}
|
||||
|
||||
void add_cols()
|
||||
{
|
||||
auto factory = Gtk::SignalListItemFactory::create();
|
||||
factory->signal_setup().connect(sigc::bind(sigc::mem_fun(*this,
|
||||
&EditorView::onSetupLabel), Gtk::Align::START));
|
||||
factory->signal_bind().connect(
|
||||
sigc::mem_fun(*this, &EditorView::onBindBase));
|
||||
auto column = Gtk::ColumnViewColumn::create("Base", factory);
|
||||
column->set_fixed_width(150);
|
||||
append_column(column);
|
||||
|
||||
for(int i=0;i<16;i++)
|
||||
{
|
||||
factory = Gtk::SignalListItemFactory::create();
|
||||
factory->signal_setup().connect(sigc::bind(sigc::mem_fun(*this,
|
||||
&EditorView::onSetupEditableLabel), i));
|
||||
factory->signal_bind().connect(
|
||||
sigc::bind(sigc::mem_fun(*this, &EditorView::onBindValue), i));
|
||||
factory->signal_unbind().connect(
|
||||
sigc::bind(sigc::mem_fun(*this, &EditorView::onUnbindValue), i));
|
||||
|
||||
column = Gtk::ColumnViewColumn::create(std::format("{:02x}", i) , factory);
|
||||
column->set_expand();
|
||||
append_column(column);
|
||||
}
|
||||
}
|
||||
|
||||
void add_row(const std::string& base
|
||||
){
|
||||
uint8_t db[16] = {0};
|
||||
_editorModel->append(EditorModelColumns::create(base, db));
|
||||
}
|
||||
|
||||
void onSetupLabel(
|
||||
const Glib::RefPtr<Gtk::ListItem>& list_item, Gtk::Align halign)
|
||||
{
|
||||
auto label = Gtk::make_managed<Gtk::Label>("", halign);
|
||||
list_item->set_child(*label);
|
||||
}
|
||||
|
||||
void onBindBase(const Glib::RefPtr<Gtk::ListItem>& list_item)
|
||||
{
|
||||
auto col = std::dynamic_pointer_cast<EditorModelColumns>(list_item->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
|
||||
if (!label)
|
||||
return;
|
||||
std::string tx = label->get_text();
|
||||
label->set_text(col->baseAddress);
|
||||
}
|
||||
|
||||
void onSetupEditableLabel(
|
||||
const Glib::RefPtr<Gtk::ListItem>& list_item, int i)
|
||||
{
|
||||
auto label = Gtk::make_managed<NoNewlineTextView>();
|
||||
label->set_editable();
|
||||
label->set_accepts_tab(false);
|
||||
|
||||
// label->signal_insert_text().connect([](const Glib::ustring& str, int* n){
|
||||
//
|
||||
// });
|
||||
label->get_buffer()->property_text().signal_changed().connect(sigc::bind(sigc::mem_fun(
|
||||
*this, &EditorView::on_edited), list_item, i));
|
||||
list_item->set_child(*label);
|
||||
}
|
||||
|
||||
void onBindValue(
|
||||
const Glib::RefPtr<Gtk::ListItem>& list_item, int i)
|
||||
{
|
||||
auto col = std::dynamic_pointer_cast<EditorModelColumns>(list_item->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
auto label = dynamic_cast<NoNewlineTextView*>(list_item->get_child());
|
||||
if (!label)
|
||||
return;
|
||||
|
||||
label->get_buffer()->set_text(col->propertyValues(i).get_value());
|
||||
|
||||
if (col->bindingToValues[i])
|
||||
col->bindingToValues[i]->unbind();
|
||||
|
||||
col->bindingToValues[i] = Glib::Binding::bind_property(
|
||||
label->get_buffer()->property_text(), col->propertyValues(i),
|
||||
Glib::Binding::Flags::BIDIRECTIONAL);
|
||||
}
|
||||
|
||||
void onUnbindValue(
|
||||
const Glib::RefPtr<Gtk::ListItem>& list_item, int i)
|
||||
{
|
||||
auto col = std::dynamic_pointer_cast<EditorModelColumns>(list_item->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
if (col->bindingToValues[i])
|
||||
col->bindingToValues[i]->unbind();
|
||||
col->bindingToValues[i].reset();
|
||||
}
|
||||
|
||||
void on_edited(const Glib::RefPtr<Gtk::ListItem>& list_item, int i)
|
||||
{
|
||||
auto col = std::dynamic_pointer_cast<EditorModelColumns>(list_item->get_item());
|
||||
if (!col)
|
||||
return;
|
||||
auto label = dynamic_cast<NoNewlineTextView*>(list_item->get_child());
|
||||
if (!label)
|
||||
return;
|
||||
}
|
||||
};
|
||||
490
interface/home.hpp
Normal file
@@ -0,0 +1,490 @@
|
||||
|
||||
#include <gtkmm.h>
|
||||
#include <iostream>
|
||||
#include <glibmm.h>
|
||||
#include <memory>
|
||||
|
||||
#include "interface/log/LogView.hpp"
|
||||
#include "interface/cpu/CPUView.hpp"
|
||||
|
||||
#include "../core/Core.hpp"
|
||||
#include "interface/cpu/RegView.hpp"
|
||||
#include "interface/cpu/SymbolMenu.hpp"
|
||||
#include "interface/memory/MemoryView.hpp"
|
||||
#include "interface/callstack/CallstackView.hpp"
|
||||
#include "interface/editor/EditorView.hpp"
|
||||
|
||||
class HomeWindow : public Gtk::Window
|
||||
{
|
||||
public:
|
||||
void sendMessage(char id, void* param) const
|
||||
{
|
||||
DebugCommand msg{id, param};
|
||||
if(sock_pair[0] != -1)
|
||||
{
|
||||
write(sock_pair[0], &msg, sizeof(DebugCommand));
|
||||
}
|
||||
}
|
||||
protected:
|
||||
int sock_pair[2]{};
|
||||
|
||||
Glib::RefPtr<Gtk::Builder> _refBuilder;
|
||||
Glib::RefPtr<Gtk::Application> _app = nullptr;
|
||||
|
||||
std::shared_ptr<Core> _core = nullptr;
|
||||
|
||||
pthread_t _th = 0;
|
||||
|
||||
std::string _lastPath = "";
|
||||
std::string _lastArgv = "";
|
||||
|
||||
char buffer[32] = {0};
|
||||
int ep = -1;
|
||||
epoll_event ev1 {};
|
||||
|
||||
std::vector<cs_insn>* pInsn = nullptr;
|
||||
|
||||
public: // Elements
|
||||
Glib::RefPtr<CPUView> codeView = nullptr;
|
||||
Glib::RefPtr<LogView> logView = nullptr;
|
||||
Glib::RefPtr<Gtk::Button> bAttach = nullptr;
|
||||
Glib::RefPtr<Gtk::Dialog> dialogAttach = nullptr;
|
||||
Glib::RefPtr<Gtk::Button> dCancel = nullptr;
|
||||
Glib::RefPtr<Gtk::Button> dAttach = nullptr;
|
||||
Glib::RefPtr<Gtk::Button> bStop = nullptr;
|
||||
Glib::RefPtr<Gtk::Button> bCont = nullptr;
|
||||
Glib::RefPtr<Gtk::Button> bSuspend = nullptr;
|
||||
Glib::RefPtr<Gtk::Box> debugBox = nullptr;
|
||||
Glib::RefPtr<Gtk::TextView> tExecutable = nullptr;
|
||||
Glib::RefPtr<Gtk::TextView> tArg = nullptr;
|
||||
Glib::RefPtr<Gtk::ScrolledWindow> logScroll = nullptr;
|
||||
Glib::RefPtr<Gtk::HeaderBar> HeaderBar = nullptr;
|
||||
Glib::RefPtr<Gtk::Button> bRestart = nullptr;
|
||||
Glib::RefPtr<RegView> regView = nullptr;
|
||||
Glib::RefPtr<Gtk::Label> windowTitle = nullptr;
|
||||
Glib::RefPtr<Gtk::Button> bStepinto = nullptr;
|
||||
Glib::RefPtr<MemoryView> memory = nullptr;
|
||||
Glib::RefPtr<Gtk::Popover> popover1 = nullptr;
|
||||
Glib::RefPtr<Gtk::Box> popover1Box = nullptr;
|
||||
Glib::RefPtr<Gtk::Label> currentSymbol = nullptr;
|
||||
Glib::RefPtr<Gtk::Button> bStepover = nullptr;
|
||||
Glib::RefPtr<SymbolMenu> mSymbols = nullptr;
|
||||
Glib::RefPtr<Gtk::Button> bTryParse = nullptr;
|
||||
Glib::RefPtr<CPUView> breakpoints = nullptr;
|
||||
Glib::RefPtr<Gtk::Stack> stack = nullptr;
|
||||
Glib::RefPtr<CallstackView> callstack = nullptr;
|
||||
Glib::RefPtr<Gtk::Button> bGoto = nullptr;
|
||||
Glib::RefPtr<EditorView> editor = nullptr;
|
||||
Glib::RefPtr<Gtk::Button> bMemPush = nullptr;
|
||||
Glib::RefPtr<Gtk::Button> bMemPull = nullptr;
|
||||
Glib::RefPtr<Gtk::Button> bMemJump = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
HomeWindow(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &refBuilder, const Glib::RefPtr<Gtk::Application>& app) :
|
||||
Gtk::Window(cobject), _refBuilder(refBuilder), _app(app),
|
||||
codeView(_refBuilder->get_widget_derived<CPUView>(_refBuilder, "code")),
|
||||
regView(_refBuilder->get_widget_derived<RegView>(_refBuilder, "reg")),
|
||||
logScroll(refBuilder->get_widget<Gtk::ScrolledWindow>("logScroll")),
|
||||
bAttach(refBuilder->get_widget<Gtk::Button>("bAttach")),
|
||||
dialogAttach(refBuilder->get_widget<Gtk::Dialog>("dialogAttach")),
|
||||
dCancel(refBuilder->get_widget<Gtk::Button>("dCancel")),
|
||||
dAttach(refBuilder->get_widget<Gtk::Button>("dAttach")),
|
||||
bStop(refBuilder->get_widget<Gtk::Button>("bStop")),
|
||||
debugBox(refBuilder->get_widget<Gtk::Box>("debugBox")),
|
||||
tExecutable(refBuilder->get_widget<Gtk::TextView>("tExecutable")),
|
||||
tArg(refBuilder->get_widget<Gtk::TextView>("tArg")),
|
||||
logView(_refBuilder->get_widget_derived<LogView>(_refBuilder, "log")),
|
||||
bCont(refBuilder->get_widget<Gtk::Button>("bCont")),
|
||||
bSuspend(refBuilder->get_widget<Gtk::Button>("bSuspend")),
|
||||
HeaderBar(refBuilder->get_widget<Gtk::HeaderBar>("HeaderBar")),
|
||||
bRestart(refBuilder->get_widget<Gtk::Button>("bRestart")),
|
||||
windowTitle(refBuilder->get_widget<Gtk::Label>("windowTitle")),
|
||||
bStepinto(refBuilder->get_widget<Gtk::Button>("bStepinto")),
|
||||
memory(_refBuilder->get_widget_derived<MemoryView>(_refBuilder, "memory")),
|
||||
popover1(refBuilder->get_widget<Gtk::Popover>("popover1")),
|
||||
popover1Box(refBuilder->get_widget<Gtk::Box>("popover1Box")),
|
||||
currentSymbol(refBuilder->get_widget<Gtk::Label>("currentSymbol")),
|
||||
bStepover(refBuilder->get_widget<Gtk::Button>("bStepover")),
|
||||
mSymbols(refBuilder->get_widget_derived<SymbolMenu>(_refBuilder, "mSymbols")),
|
||||
bTryParse(refBuilder->get_widget<Gtk::Button>("bTryParse")),
|
||||
breakpoints(_refBuilder->get_widget_derived<CPUView>(_refBuilder, "breakpoints")),
|
||||
stack(refBuilder->get_widget<Gtk::Stack>("stack")),
|
||||
callstack(_refBuilder->get_widget_derived<CallstackView>(_refBuilder, "callstack")),
|
||||
bGoto(refBuilder->get_widget<Gtk::Button>("bGoto")),
|
||||
editor(_refBuilder->get_widget_derived<EditorView>(_refBuilder, "editor")),
|
||||
bMemPush(refBuilder->get_widget<Gtk::Button>("bMemPush")),
|
||||
bMemPull(refBuilder->get_widget<Gtk::Button>("bMemPull")),
|
||||
bMemJump(refBuilder->get_widget<Gtk::Button>("bMemJump"))
|
||||
{
|
||||
socketpair(AF_UNIX, SOCK_STREAM, 0, sock_pair);
|
||||
ep = epoll_create1(0);
|
||||
|
||||
ev1 = epoll_event{.events = EPOLLIN};
|
||||
ev1.data.fd = sock_pair[0];
|
||||
epoll_ctl(ep, EPOLL_CTL_ADD, sock_pair[0], &ev1);
|
||||
bTryParse->hide();
|
||||
this->add_tick_callback([this](const Glib::RefPtr<Gdk::FrameClock>& c){
|
||||
int n = epoll_wait(ep, &ev1, 1, 0);
|
||||
if(n > 0)
|
||||
{
|
||||
auto sz = read(sock_pair[0], buffer, sizeof(DebugCommand));
|
||||
if(sz != sizeof(DebugCommand)) return true;
|
||||
|
||||
auto msg = (DebugCommand*)buffer;
|
||||
switch (msg->id) {
|
||||
case MSG_PAUSE:
|
||||
UpdateUI(1, BTN_CONT | BTN_STOP | BTN_STI);
|
||||
break;
|
||||
case MSG_STOP:
|
||||
_core = nullptr;
|
||||
UpdateUI(0, BTN_ATTACH);
|
||||
break;
|
||||
case MSG_CONTINUE:
|
||||
UpdateUI(2, BTN_INT | BTN_STOP);
|
||||
break;
|
||||
case MSG_STARTUP:
|
||||
UpdateUI(0, BTN_INT | BTN_STOP);
|
||||
break;
|
||||
case MSG_REG:{
|
||||
auto reg = reinterpret_cast<user_regs_struct*>(msg->param);
|
||||
UpdateUI(0, 0, reg); // The first two parameters do not take effect when reg isn't empty
|
||||
codeView->rip = reg->rip;
|
||||
break;
|
||||
}
|
||||
case MSG_QUIT:
|
||||
return false;
|
||||
case MSG_LOG:
|
||||
logView->add_log_data((char*)msg->param);
|
||||
delete[] (char*)msg->param;
|
||||
break;
|
||||
case MSG_TITLE:
|
||||
windowTitle->set_text((char*)msg->param);
|
||||
break;
|
||||
case MSG_MEMORY:{
|
||||
auto pList = reinterpret_cast<std::vector<MapEntire>*>(msg->param);
|
||||
memory->set_data(*pList);
|
||||
break;
|
||||
}
|
||||
case MSG_ASM:{
|
||||
delete pInsn;
|
||||
pInsn = reinterpret_cast<std::vector<cs_insn>*>(msg->param);
|
||||
codeView->setData(*pInsn);
|
||||
break;
|
||||
}
|
||||
case MSG_SELECT:
|
||||
codeView->Select();
|
||||
break;
|
||||
case MSG_CURRENTSYMBOL:
|
||||
currentSymbol->set_text(_core->currentSymbol.name);
|
||||
break;
|
||||
case MSG_RESTART:
|
||||
_core = std::make_shared<Core>(_lastPath, _lastArgv);
|
||||
_core->pipe = sock_pair[1];
|
||||
_core->remotePipe = sock_pair[0];
|
||||
codeView->core = _core;
|
||||
breakpoints->core = _core;
|
||||
|
||||
bAttach->set_sensitive(false);
|
||||
logView->clear();
|
||||
break;
|
||||
case MSG_SYMBOLS:
|
||||
mSymbols->UpdateList(_core->symbols);
|
||||
break;
|
||||
case MSG_NONSYM:
|
||||
bTryParse->show();
|
||||
break;
|
||||
case MSG_INSYM:
|
||||
bTryParse->hide();
|
||||
break;
|
||||
case MSG_GETBREAKS:
|
||||
{
|
||||
breakpoints->clear();
|
||||
auto insn = reinterpret_cast<cs_insn*>(msg->param);
|
||||
auto count = _core->GetBreakpoints().size();
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
char cmdAddr[32], insByte[32];
|
||||
std::string bytes, cmdStr;
|
||||
for(int j = 0;j<insn[i].size;j++)
|
||||
{
|
||||
sprintf(insByte, "%2x", insn[i].bytes[j]);
|
||||
bytes += insByte;
|
||||
}
|
||||
sprintf(cmdAddr, "%16lx", insn[i].address);
|
||||
cmdStr = insn[i].mnemonic;
|
||||
cmdStr += " ";
|
||||
cmdStr += insn[i].op_str;
|
||||
|
||||
breakpoints->add_row(cmdAddr, bytes, cmdStr, _core->ContainsBreakPoint(insn[i].address));
|
||||
}
|
||||
delete[] insn;
|
||||
break;
|
||||
}
|
||||
case MSG_CALLSTACK:
|
||||
{
|
||||
auto *p = (std::stack<CallstackStruct>*)msg->param;
|
||||
|
||||
callstack->clear();
|
||||
while(!p->empty())
|
||||
{
|
||||
std::string name = "Thread: ";
|
||||
std::vector<CallstackItem> g;
|
||||
name += std::to_string(p->top().depth);
|
||||
p->pop();
|
||||
|
||||
int depth = INT_MAX;
|
||||
do
|
||||
{
|
||||
char strBuf[512];
|
||||
depth = p->top().depth;
|
||||
CallstackItem i{};
|
||||
i.depth = std::to_string(depth);
|
||||
|
||||
sprintf(strBuf, "%lX", p->top().callPoint);
|
||||
i.callPoint = strBuf;
|
||||
|
||||
sprintf(strBuf, "%lX", p->top().stackBase);
|
||||
i.stackBase = strBuf;
|
||||
|
||||
i.symbol = p->top().symbol.name;
|
||||
i.module = p->top().module;
|
||||
g.emplace_back(i);
|
||||
p->pop();
|
||||
}while(depth);
|
||||
|
||||
callstack->append_group(name, g);
|
||||
}
|
||||
delete p;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
mSymbols->popoverBox = popover1Box;
|
||||
mSymbols->childLabel = currentSymbol;
|
||||
mSymbols->port = sock_pair[0];
|
||||
|
||||
_app->signal_activate().connect([this](){
|
||||
_app->add_window(*this);
|
||||
this->show();
|
||||
});
|
||||
|
||||
codeView->add_css_class("code_view");
|
||||
|
||||
dialogAttach->set_hide_on_close(true);
|
||||
|
||||
bAttach->signal_clicked().connect([this](){
|
||||
dialogAttach->set_transient_for(*this);
|
||||
dialogAttach->show();
|
||||
});
|
||||
|
||||
dCancel->signal_clicked().connect([this](){
|
||||
dialogAttach->close();
|
||||
});
|
||||
|
||||
dAttach->signal_clicked().connect([this](){
|
||||
auto s = tExecutable->get_buffer()->get_text();
|
||||
std::size_t pos = s.rfind('/');
|
||||
std::string filename = (pos != std::string::npos) ? s.substr(pos + 1) : s;
|
||||
|
||||
dialogAttach->close();
|
||||
_lastPath = s;
|
||||
_lastArgv = filename;
|
||||
|
||||
_core = std::make_shared<Core>(s, filename);
|
||||
_core->pipe = sock_pair[1];
|
||||
_core->remotePipe = sock_pair[0];
|
||||
codeView->core = _core;
|
||||
breakpoints->core = _core;
|
||||
|
||||
bAttach->set_sensitive(false);
|
||||
logView->clear();
|
||||
});
|
||||
|
||||
bStop->signal_clicked().connect([this](){
|
||||
if(_core)
|
||||
{
|
||||
codeView->clear();
|
||||
sendMessage(MSG_STOP, nullptr);
|
||||
_core = nullptr;
|
||||
codeView->core = nullptr;
|
||||
breakpoints->core = nullptr;
|
||||
}
|
||||
});
|
||||
|
||||
bSuspend->signal_clicked().connect([this](){
|
||||
sendMessage(MSG_PAUSE, nullptr);
|
||||
});
|
||||
|
||||
bCont->signal_clicked().connect([this](){
|
||||
sendMessage(MSG_CONTINUE, nullptr);
|
||||
});
|
||||
|
||||
bStepinto->signal_clicked().connect([this](){
|
||||
sendMessage(MSG_STEPINTO, nullptr);
|
||||
});
|
||||
|
||||
bRestart->signal_clicked().connect([this](){
|
||||
if(!_lastPath.empty())
|
||||
{
|
||||
if(!_core)
|
||||
{
|
||||
_core = std::make_shared<Core>(_lastPath, _lastArgv);
|
||||
_core->pipe = sock_pair[1];
|
||||
_core->remotePipe = sock_pair[0];
|
||||
codeView->core = _core;
|
||||
breakpoints->core = _core;
|
||||
|
||||
bAttach->set_sensitive(false);
|
||||
logView->clear();
|
||||
return;
|
||||
}
|
||||
sendMessage(MSG_RESTART, nullptr);
|
||||
_core = nullptr;
|
||||
codeView->core = nullptr;
|
||||
breakpoints->core = nullptr;
|
||||
|
||||
printf("%s\n", "Pending to Restart");
|
||||
}
|
||||
});
|
||||
|
||||
bStepover->signal_clicked().connect([this](){
|
||||
sendMessage(MSG_STEPOVER, nullptr);
|
||||
});
|
||||
bGoto->signal_clicked().connect([this](){
|
||||
auto i = codeView->GetSelected();
|
||||
_core->queueMessage(MSG_BREAKONCE, (void*)i);
|
||||
sendMessage(MSG_CONTINUE, nullptr);
|
||||
});
|
||||
|
||||
bTryParse->signal_clicked().connect([this](){
|
||||
sendMessage(MSG_PARSE, nullptr);
|
||||
});
|
||||
|
||||
memory->signal_row_activated().connect([this](const Gtk::TreeModel::Path& p, Gtk::TreeViewColumn* c){
|
||||
auto iter = memory->get_model()->get_iter(p);
|
||||
if(iter)
|
||||
{
|
||||
Glib::ustring addr;
|
||||
(*iter).get_value(0, addr);
|
||||
printf("%s\n", addr.c_str());
|
||||
|
||||
MapEntire me;
|
||||
_core->GetMapEntire(std::stoull(addr, nullptr, 16), me);
|
||||
|
||||
if(me.access & EXECUTE)
|
||||
{
|
||||
auto buf = new char[512];
|
||||
strcpy(buf, me.path);
|
||||
sendMessage(MSG_SWITCHMODULE, buf);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
stack->property_visible_child().signal_changed().connect([this](){
|
||||
auto name = stack->get_visible_child_name();
|
||||
if(name == "Breakpoints" && _core != nullptr)
|
||||
{
|
||||
if(_core)
|
||||
sendMessage(MSG_GETBREAKS, nullptr);
|
||||
}else if(name == "CPU" && _core != nullptr)
|
||||
{
|
||||
if(pInsn)
|
||||
codeView->setData(*pInsn);
|
||||
codeView->Select();
|
||||
}else if(name == "Call Stack" && _core != nullptr)
|
||||
{
|
||||
if(_core)
|
||||
sendMessage(MSG_CALLSTACK, nullptr);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
UpdateUI(0, BTN_ATTACH);
|
||||
}
|
||||
|
||||
static Glib::RefPtr<HomeWindow> create()
|
||||
{
|
||||
auto app = Gtk::Application::create("acite.aldbg.app");
|
||||
auto refBuilder = Gtk::Builder::create();
|
||||
refBuilder->add_from_file("../aldbg.ui");
|
||||
auto home = Gtk::Builder::get_widget_derived<HomeWindow>(refBuilder, "MainWindow", app);
|
||||
|
||||
if (!home) {
|
||||
std::cerr << "No \"MainWindow\" object in main-window.ui" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto css_provider = Gtk::CssProvider::create();
|
||||
css_provider->load_from_path("../interface/style.css");
|
||||
|
||||
// 将 CSS 提供程序应用到屏幕
|
||||
Gtk::StyleContext::add_provider_for_display(Gdk::Display::get_default(), css_provider, GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||
|
||||
return Glib::make_refptr_for_instance<HomeWindow>(home);
|
||||
}
|
||||
|
||||
enum ButtonMask
|
||||
{
|
||||
BTN_ATTACH = 0x01,
|
||||
BTN_STOP = 0x02,
|
||||
BTN_CONT = 0x04,
|
||||
BTN_INT = 0x08,
|
||||
BTN_STI = 0x10
|
||||
};
|
||||
|
||||
void UpdateUI(int status, uint64_t buttons, user_regs_struct* reg = nullptr)
|
||||
{
|
||||
if(reg)
|
||||
{
|
||||
regView->clear();
|
||||
regView->add_data("rax", reg->rax);
|
||||
regView->add_data("rbx", reg->rbx);
|
||||
regView->add_data("rcx", reg->rcx);
|
||||
regView->add_data("rdx", reg->rdx);
|
||||
regView->add_data("rbp", reg->rbp);
|
||||
regView->add_data("rsp", reg->rsp);
|
||||
regView->add_data("pc", reg->rip);
|
||||
return;
|
||||
}
|
||||
switch(status)
|
||||
{
|
||||
case 0:
|
||||
HeaderBar->remove_css_class("cont");
|
||||
HeaderBar->remove_css_class("trap");
|
||||
break;
|
||||
case 1:
|
||||
HeaderBar->remove_css_class("cont");
|
||||
HeaderBar->add_css_class("trap");
|
||||
break;
|
||||
case 2:
|
||||
HeaderBar->add_css_class("cont");
|
||||
HeaderBar->remove_css_class("trap");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
bSuspend->set_sensitive(buttons & BTN_INT);
|
||||
bCont->set_sensitive(buttons & BTN_CONT);
|
||||
bAttach->set_sensitive(buttons & BTN_ATTACH);
|
||||
bStop->set_sensitive(buttons & BTN_STOP);
|
||||
bStepinto->set_sensitive(buttons & BTN_STI);
|
||||
bStepover->set_sensitive(buttons & BTN_STI);
|
||||
mSymbols->set_sensitive(buttons & BTN_STI);
|
||||
bGoto->set_sensitive(buttons & BTN_STI);
|
||||
|
||||
bMemJump->set_sensitive(buttons & BTN_STI);
|
||||
bMemPull->set_sensitive(buttons & BTN_STI);
|
||||
bMemPush->set_sensitive(buttons & BTN_STI);
|
||||
}
|
||||
|
||||
int loop()
|
||||
{
|
||||
return _app->run();
|
||||
}
|
||||
};
|
||||
BIN
interface/icon/arrow-restart.png
Normal file
|
After Width: | Height: | Size: 803 B |
BIN
interface/icon/arrow-run-cursor.png
Normal file
|
After Width: | Height: | Size: 665 B |
BIN
interface/icon/arrow-run.png
Normal file
|
After Width: | Height: | Size: 525 B |
BIN
interface/icon/arrow-skip.png
Normal file
|
After Width: | Height: | Size: 611 B |
BIN
interface/icon/arrow-small-down.png
Normal file
|
After Width: | Height: | Size: 494 B |
BIN
interface/icon/arrow-small-up.png
Normal file
|
After Width: | Height: | Size: 503 B |
BIN
interface/icon/arrow-step-into.png
Normal file
|
After Width: | Height: | Size: 587 B |
BIN
interface/icon/arrow-step-over.png
Normal file
|
After Width: | Height: | Size: 750 B |
BIN
interface/icon/arrow-step-rtr.png
Normal file
|
After Width: | Height: | Size: 577 B |
BIN
interface/icon/arrow-threads.png
Normal file
|
After Width: | Height: | Size: 710 B |
BIN
interface/icon/close-all-tabs.png
Normal file
|
After Width: | Height: | Size: 544 B |
BIN
interface/icon/control-pause.png
Normal file
|
After Width: | Height: | Size: 378 B |
BIN
interface/icon/control-stop.png
Normal file
|
After Width: | Height: | Size: 365 B |
46
interface/log/LogView.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// Created by acite on 5/1/24.
|
||||
//
|
||||
|
||||
#include <ctime>
|
||||
#include <cstring>
|
||||
#include <gtkmm.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
class LogView : public Gtk::TreeView
|
||||
{
|
||||
protected:
|
||||
Glib::RefPtr<Gtk::ListStore> _logStore = nullptr;
|
||||
|
||||
public:
|
||||
LogView(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &refBuilder) : Gtk::TreeView(cobject),
|
||||
_logStore(std::dynamic_pointer_cast<Gtk::ListStore>(get_model()))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void add_log_data(const std::string& data)
|
||||
{
|
||||
auto mm = _logStore->append();
|
||||
Gtk::TreeModel::Row row = *mm;
|
||||
|
||||
time_t currentTime;
|
||||
time(¤tTime);
|
||||
tm* localTime = localtime(¤tTime);
|
||||
|
||||
char time_str[64];
|
||||
sprintf(time_str, "[%d:%d:%d]", localTime->tm_hour, localTime->tm_min, localTime->tm_sec);
|
||||
std::string cppstrtime = time_str;
|
||||
|
||||
row.set_value(0, cppstrtime);
|
||||
row.set_value(1, data);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
_logStore->clear();
|
||||
}
|
||||
};
|
||||
57
interface/memory/MemoryView.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
#include <gtkmm.h>
|
||||
#include "../../core/MemoryMap.hpp"
|
||||
|
||||
class MemoryView : public Gtk::TreeView
|
||||
{
|
||||
private:
|
||||
Glib::RefPtr<Gtk::ListStore> _memoryViewStore;
|
||||
|
||||
public:
|
||||
void GetSelection(uint64_t& address)
|
||||
{
|
||||
Gtk::TreeModel::Row row = *get_selection()->get_selected();
|
||||
row.get_value(0, address);
|
||||
}
|
||||
|
||||
std::vector<MapEntire> _map;
|
||||
|
||||
MemoryView(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &refBuilder) : Gtk::TreeView(cobject),
|
||||
_memoryViewStore(std::dynamic_pointer_cast<Gtk::ListStore>(get_model()))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void set_data(std::vector<MapEntire>& list)
|
||||
{
|
||||
_map = list;
|
||||
|
||||
_memoryViewStore->clear();
|
||||
|
||||
for(auto e : list)
|
||||
{
|
||||
auto mm = _memoryViewStore->append();
|
||||
Gtk::TreeModel::Row row = *mm;
|
||||
|
||||
char startAddr[32], endAddr[32], offset[32];
|
||||
std::string permission;
|
||||
|
||||
permission += (e.access & READ ? "r" : "-");
|
||||
permission += (e.access & WRITE ? "w" : "-");
|
||||
permission += (e.access & EXECUTE ? "x" : "-");
|
||||
permission += (e.access & PRIVATE ? "p" : "-");
|
||||
permission += (e.access & SHARED ? "s" : "-");
|
||||
|
||||
sprintf(startAddr, "%16lx", e.startAddress);
|
||||
sprintf(endAddr, "%16lx", e.endAddress);
|
||||
sprintf(offset, "%8x", e.offset);
|
||||
|
||||
|
||||
row.set_value(0, std::string(startAddr));
|
||||
row.set_value(1, std::string(endAddr));
|
||||
row.set_value(2, permission);
|
||||
row.set_value(3, std::string(offset));
|
||||
row.set_value(4, std::string(e.path));
|
||||
}
|
||||
}
|
||||
};
|
||||
20
interface/style.css
Normal file
@@ -0,0 +1,20 @@
|
||||
/* 设置窗口的背景颜色 */
|
||||
|
||||
/* 设置标签的字体样式 */
|
||||
label {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
headerbar.cont {
|
||||
background-color: #66CC00;
|
||||
}
|
||||
|
||||
headerbar.trap {
|
||||
background-color: #CC6600;
|
||||
}
|
||||
|
||||
label.highlight {
|
||||
color: #CC6600;
|
||||
font-weight: bold;
|
||||
}
|
||||