Capturing guest/host context and showing registers in debugger.
This commit is contained in:
@@ -174,14 +174,14 @@ bool DebugClient::ProcessPacket(proto::PacketReader* packet_reader,
|
||||
auto entries = packet_reader->ReadArray<ThreadListEntry>(body->count);
|
||||
listener_->OnThreadsUpdated(std::move(entries));
|
||||
} break;
|
||||
case PacketType::kThreadCallStacksResponse: {
|
||||
auto body = packet_reader->Read<ThreadCallStacksResponse>();
|
||||
case PacketType::kThreadStatesResponse: {
|
||||
auto body = packet_reader->Read<ThreadStatesResponse>();
|
||||
for (size_t i = 0; i < body->count; ++i) {
|
||||
auto entry = packet_reader->Read<ThreadCallStackEntry>();
|
||||
auto entry = packet_reader->Read<ThreadStateEntry>();
|
||||
auto frames =
|
||||
packet_reader->ReadArray<ThreadCallStackFrame>(entry->frame_count);
|
||||
listener_->OnThreadCallStackUpdated(entry->thread_handle,
|
||||
std::move(frames));
|
||||
listener_->OnThreadStateUpdated(entry->thread_handle, entry,
|
||||
std::move(frames));
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
@@ -261,7 +261,7 @@ void DebugClient::BeginUpdateAllState() {
|
||||
packet_writer_.Begin(PacketType::kThreadListRequest);
|
||||
packet_writer_.End();
|
||||
// Request the full call stacks for all threads.
|
||||
packet_writer_.Begin(PacketType::kThreadCallStacksRequest);
|
||||
packet_writer_.Begin(PacketType::kThreadStatesRequest);
|
||||
packet_writer_.End();
|
||||
|
||||
Flush();
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace debug {
|
||||
using proto::ModuleListEntry;
|
||||
using proto::ThreadCallStackFrame;
|
||||
using proto::ThreadListEntry;
|
||||
using proto::ThreadStateEntry;
|
||||
|
||||
enum class ExecutionState {
|
||||
kRunning,
|
||||
@@ -52,8 +53,8 @@ class DebugClientListener {
|
||||
std::vector<const ModuleListEntry*> entries) = 0;
|
||||
virtual void OnThreadsUpdated(
|
||||
std::vector<const ThreadListEntry*> entries) = 0;
|
||||
virtual void OnThreadCallStackUpdated(
|
||||
uint32_t thread_handle,
|
||||
virtual void OnThreadStateUpdated(
|
||||
uint32_t thread_handle, const ThreadStateEntry* entry,
|
||||
std::vector<const ThreadCallStackFrame*> frames) = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -285,9 +285,9 @@ bool DebugServer::ProcessPacket(const proto::Packet* packet) {
|
||||
}
|
||||
packet_writer_.End();
|
||||
} break;
|
||||
case PacketType::kThreadCallStacksRequest: {
|
||||
packet_writer_.Begin(PacketType::kThreadCallStacksResponse);
|
||||
auto body = packet_writer_.Append<ThreadCallStacksResponse>();
|
||||
case PacketType::kThreadStatesRequest: {
|
||||
packet_writer_.Begin(PacketType::kThreadStatesResponse);
|
||||
auto body = packet_writer_.Append<ThreadStatesResponse>();
|
||||
auto stack_walker = emulator->processor()->stack_walker();
|
||||
auto threads =
|
||||
emulator->kernel_state()->object_table()->GetObjectsByType<XThread>(
|
||||
@@ -296,13 +296,29 @@ bool DebugServer::ProcessPacket(const proto::Packet* packet) {
|
||||
uint64_t frame_host_pcs[64];
|
||||
cpu::StackFrame frames[64];
|
||||
for (auto& thread : threads) {
|
||||
auto thread_entry_body = packet_writer_.Append<ThreadStateEntry>();
|
||||
thread_entry_body->thread_handle = thread->handle();
|
||||
|
||||
// Grab PPC context.
|
||||
// Note that this is only up to date if --store_all_context_values is
|
||||
// enabled (or --debug).
|
||||
if (thread->is_guest_thread()) {
|
||||
std::memcpy(&thread_entry_body->guest_context,
|
||||
thread->thread_state()->context(),
|
||||
sizeof(thread_entry_body->guest_context));
|
||||
} else {
|
||||
std::memset(&thread_entry_body->guest_context, 0,
|
||||
sizeof(thread_entry_body->guest_context));
|
||||
}
|
||||
|
||||
// Grab stack trace and X64 context then resolve all symbols.
|
||||
uint64_t hash;
|
||||
size_t count = stack_walker->CaptureStackTrace(
|
||||
thread->GetWaitHandle()->native_handle(), frame_host_pcs, 0, 64,
|
||||
&hash);
|
||||
&thread_entry_body->host_context, &hash);
|
||||
stack_walker->ResolveStack(frame_host_pcs, frames, count);
|
||||
auto thread_entry_body = packet_writer_.Append<ThreadCallStackEntry>();
|
||||
thread_entry_body->thread_handle = thread->handle();
|
||||
|
||||
// Populate stack frames with additional information.
|
||||
thread_entry_body->frame_count = uint32_t(count);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
auto& frame = frames[i];
|
||||
|
||||
@@ -161,7 +161,8 @@ void Debugger::DumpThreadStacks() {
|
||||
uint64_t frame_host_pcs[64];
|
||||
uint64_t hash;
|
||||
size_t count = stack_walker->CaptureStackTrace(
|
||||
thread->GetWaitHandle()->native_handle(), frame_host_pcs, 0, 64, &hash);
|
||||
thread->GetWaitHandle()->native_handle(), frame_host_pcs, 0, 64,
|
||||
nullptr, &hash);
|
||||
cpu::StackFrame frames[64];
|
||||
stack_walker->ResolveStack(frame_host_pcs, frames, count);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "xenia/base/vec128.h"
|
||||
#include "xenia/cpu/frontend/ppc_context.h"
|
||||
#include "xenia/cpu/stack_walker.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace proto {
|
||||
@@ -28,8 +32,8 @@ enum class PacketType : uint16_t {
|
||||
|
||||
kThreadListRequest = 30,
|
||||
kThreadListResponse = 31,
|
||||
kThreadCallStacksRequest = 32,
|
||||
kThreadCallStacksResponse = 33,
|
||||
kThreadStatesRequest = 32,
|
||||
kThreadStatesResponse = 33,
|
||||
};
|
||||
|
||||
using request_id_t = uint16_t;
|
||||
@@ -168,17 +172,19 @@ struct ThreadListEntry {
|
||||
uint32_t tls_address;
|
||||
};
|
||||
|
||||
struct ThreadCallStacksRequest {
|
||||
static const PacketType type = PacketType::kThreadCallStacksRequest;
|
||||
struct ThreadStatesRequest {
|
||||
static const PacketType type = PacketType::kThreadStatesRequest;
|
||||
};
|
||||
struct ThreadCallStacksResponse {
|
||||
static const PacketType type = PacketType::kThreadCallStacksResponse;
|
||||
struct ThreadStatesResponse {
|
||||
static const PacketType type = PacketType::kThreadStatesResponse;
|
||||
|
||||
uint32_t count;
|
||||
// ThreadCallStackEntry[count]
|
||||
// ThreadStateEntry[count]
|
||||
};
|
||||
struct ThreadCallStackEntry {
|
||||
struct ThreadStateEntry {
|
||||
uint32_t thread_handle;
|
||||
cpu::frontend::PPCContext guest_context;
|
||||
cpu::X64Context host_context;
|
||||
uint32_t frame_count;
|
||||
// ThreadCallStackFrame[frame_count]
|
||||
};
|
||||
|
||||
@@ -112,12 +112,13 @@ void System::OnThreadsUpdated(std::vector<const ThreadListEntry*> entries) {
|
||||
on_threads_updated();
|
||||
}
|
||||
|
||||
void System::OnThreadCallStackUpdated(
|
||||
uint32_t thread_handle, std::vector<const ThreadCallStackFrame*> frames) {
|
||||
void System::OnThreadStateUpdated(
|
||||
uint32_t thread_handle, const ThreadStateEntry* entry,
|
||||
std::vector<const ThreadCallStackFrame*> frames) {
|
||||
auto thread = threads_by_handle_[thread_handle];
|
||||
if (thread != nullptr) {
|
||||
thread->UpdateCallStack(std::move(frames));
|
||||
on_thread_call_stack_updated(thread);
|
||||
thread->UpdateState(entry, std::move(frames));
|
||||
on_thread_state_updated(thread);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class System : public DebugClientListener {
|
||||
Delegate<void> on_execution_state_changed;
|
||||
Delegate<void> on_modules_updated;
|
||||
Delegate<void> on_threads_updated;
|
||||
Delegate<Thread*> on_thread_call_stack_updated;
|
||||
Delegate<Thread*> on_thread_state_updated;
|
||||
|
||||
private:
|
||||
void OnExecutionStateChanged(ExecutionState execution_state) override;
|
||||
@@ -54,8 +54,8 @@ class System : public DebugClientListener {
|
||||
std::vector<const proto::ModuleListEntry*> entries) override;
|
||||
void OnThreadsUpdated(
|
||||
std::vector<const proto::ThreadListEntry*> entries) override;
|
||||
void OnThreadCallStackUpdated(
|
||||
uint32_t thread_handle,
|
||||
void OnThreadStateUpdated(
|
||||
uint32_t thread_handle, const ThreadStateEntry* entry,
|
||||
std::vector<const ThreadCallStackFrame*> frames) override;
|
||||
|
||||
xe::ui::Loop* loop_ = nullptr;
|
||||
|
||||
@@ -16,6 +16,12 @@ namespace debug {
|
||||
namespace ui {
|
||||
namespace model {
|
||||
|
||||
Thread::Thread(System* system) : system_(system) {
|
||||
state_ = memory::AlignedAlloc<proto::ThreadStateEntry>(64);
|
||||
}
|
||||
|
||||
Thread::~Thread() { memory::AlignedFree(state_); }
|
||||
|
||||
std::string Thread::to_string() {
|
||||
std::string value = entry_.name;
|
||||
if (is_host_thread()) {
|
||||
@@ -28,8 +34,10 @@ void Thread::Update(const proto::ThreadListEntry* entry) {
|
||||
std::memcpy(&entry_, entry, sizeof(entry_));
|
||||
}
|
||||
|
||||
void Thread::UpdateCallStack(
|
||||
void Thread::UpdateState(
|
||||
const proto::ThreadStateEntry* entry,
|
||||
std::vector<const proto::ThreadCallStackFrame*> frames) {
|
||||
std::memcpy(state_, entry, sizeof(*state_));
|
||||
call_stack_.resize(frames.size());
|
||||
for (size_t i = 0; i < frames.size(); ++i) {
|
||||
std::memcpy(call_stack_.data() + i, frames[i], sizeof(Frame));
|
||||
|
||||
@@ -27,27 +27,37 @@ class Thread {
|
||||
public:
|
||||
using Frame = proto::ThreadCallStackFrame;
|
||||
|
||||
Thread(System* system) : system_(system) {}
|
||||
Thread(System* system);
|
||||
~Thread();
|
||||
|
||||
bool is_dead() const { return is_dead_; }
|
||||
void set_dead(bool is_dead) { is_dead_ = is_dead; }
|
||||
|
||||
const proto::ThreadListEntry* entry() const { return &entry_; }
|
||||
const proto::ThreadStateEntry* state() const { return state_; }
|
||||
|
||||
uint32_t thread_handle() const { return entry_.thread_handle; }
|
||||
uint32_t thread_id() const { return entry_.thread_id; }
|
||||
bool is_host_thread() const { return entry_.is_host_thread; }
|
||||
std::string name() const { return entry_.name; }
|
||||
const proto::ThreadListEntry* entry() const { return &entry_; }
|
||||
|
||||
const cpu::frontend::PPCContext* guest_context() const {
|
||||
return &state_->guest_context;
|
||||
}
|
||||
const cpu::X64Context* host_context() const { return &state_->host_context; }
|
||||
const std::vector<Frame>& call_stack() const { return call_stack_; }
|
||||
|
||||
std::string to_string();
|
||||
|
||||
void Update(const proto::ThreadListEntry* entry);
|
||||
void UpdateCallStack(std::vector<const proto::ThreadCallStackFrame*> frames);
|
||||
void UpdateState(const proto::ThreadStateEntry* entry,
|
||||
std::vector<const proto::ThreadCallStackFrame*> frames);
|
||||
|
||||
private:
|
||||
System* system_ = nullptr;
|
||||
bool is_dead_ = false;
|
||||
proto::ThreadListEntry entry_ = {0};
|
||||
proto::ThreadStateEntry* state_ = nullptr;
|
||||
std::vector<Frame> call_stack_;
|
||||
};
|
||||
|
||||
|
||||
@@ -116,12 +116,11 @@ el::Element* CallStackControl::BuildUI() {
|
||||
void CallStackControl::Setup(DebugClient* client) {
|
||||
client_ = client;
|
||||
|
||||
system()->on_thread_call_stack_updated.AddListener(
|
||||
[this](model::Thread* thread) {
|
||||
if (thread == thread_) {
|
||||
InvalidateCallStack();
|
||||
}
|
||||
});
|
||||
system()->on_thread_state_updated.AddListener([this](model::Thread* thread) {
|
||||
if (thread == thread_) {
|
||||
InvalidateCallStack();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CallStackControl::set_thread(model::Thread* thread) {
|
||||
|
||||
@@ -17,7 +17,12 @@ namespace ui {
|
||||
namespace views {
|
||||
namespace cpu {
|
||||
|
||||
CpuView::CpuView() : View("CPU") {}
|
||||
CpuView::CpuView()
|
||||
: View("CPU"),
|
||||
gr_registers_control_(RegisterSet::kGeneral),
|
||||
fr_registers_control_(RegisterSet::kFloat),
|
||||
vr_registers_control_(RegisterSet::kVector),
|
||||
host_registers_control_(RegisterSet::kHost) {}
|
||||
|
||||
CpuView::~CpuView() = default;
|
||||
|
||||
@@ -72,9 +77,14 @@ el::Element* CpuView::BuildUI() {
|
||||
auto source_registers_node =
|
||||
TabContainerNode()
|
||||
.gravity(Gravity::kAll)
|
||||
.tab(ButtonNode("GPR"), CloneNode(register_list_node))
|
||||
.tab(ButtonNode("FPR"), CloneNode(register_list_node))
|
||||
.tab(ButtonNode("VMX"), CloneNode(register_list_node));
|
||||
.tab(ButtonNode("GR"), LabelNode("<register list control>")
|
||||
.id("gr_registers_placeholder"))
|
||||
.tab(ButtonNode("FR"), LabelNode("<register list control>")
|
||||
.id("fr_registers_placeholder"))
|
||||
.tab(ButtonNode("VR"), LabelNode("<register list control>")
|
||||
.id("vr_registers_placeholder"))
|
||||
.tab(ButtonNode("X64"), LabelNode("<register list control>")
|
||||
.id("host_registers_placeholder"));
|
||||
|
||||
auto source_tools_node =
|
||||
TabContainerNode()
|
||||
@@ -110,7 +120,7 @@ el::Element* CpuView::BuildUI() {
|
||||
.gravity(Gravity::kAll)
|
||||
.axis(Axis::kY)
|
||||
.fixed_pane(FixedPane::kSecond)
|
||||
.value(180)
|
||||
.value(240)
|
||||
.pane(source_code_node)
|
||||
.pane(source_registers_node))
|
||||
.pane(source_tools_node)));
|
||||
@@ -135,10 +145,30 @@ el::Element* CpuView::BuildUI() {
|
||||
root_element_.set_layout_distribution(LayoutDistribution::kAvailable);
|
||||
root_element_.LoadNodeTree(node);
|
||||
|
||||
el::Label* gr_registers_placeholder;
|
||||
el::Label* fr_registers_placeholder;
|
||||
el::Label* vr_registers_placeholder;
|
||||
el::Label* host_registers_placeholder;
|
||||
el::Label* call_stack_placeholder;
|
||||
root_element_.GetElementsById({
|
||||
{TBIDC("gr_registers_placeholder"), &gr_registers_placeholder},
|
||||
{TBIDC("fr_registers_placeholder"), &fr_registers_placeholder},
|
||||
{TBIDC("vr_registers_placeholder"), &vr_registers_placeholder},
|
||||
{TBIDC("host_registers_placeholder"), &host_registers_placeholder},
|
||||
{TBIDC("call_stack_placeholder"), &call_stack_placeholder},
|
||||
});
|
||||
gr_registers_placeholder->parent()->ReplaceChild(
|
||||
gr_registers_placeholder, gr_registers_control_.BuildUI());
|
||||
gr_registers_control_.Setup(client_);
|
||||
fr_registers_placeholder->parent()->ReplaceChild(
|
||||
fr_registers_placeholder, fr_registers_control_.BuildUI());
|
||||
fr_registers_control_.Setup(client_);
|
||||
vr_registers_placeholder->parent()->ReplaceChild(
|
||||
vr_registers_placeholder, vr_registers_control_.BuildUI());
|
||||
vr_registers_control_.Setup(client_);
|
||||
host_registers_placeholder->parent()->ReplaceChild(
|
||||
host_registers_placeholder, host_registers_control_.BuildUI());
|
||||
host_registers_control_.Setup(client_);
|
||||
call_stack_placeholder->parent()->ReplaceChild(call_stack_placeholder,
|
||||
call_stack_control_.BuildUI());
|
||||
call_stack_control_.Setup(client_);
|
||||
@@ -244,6 +274,10 @@ void CpuView::UpdateThreadList() {
|
||||
|
||||
void CpuView::SwitchCurrentThread(model::Thread* thread) {
|
||||
current_thread_ = thread;
|
||||
gr_registers_control_.set_thread(thread);
|
||||
fr_registers_control_.set_thread(thread);
|
||||
vr_registers_control_.set_thread(thread);
|
||||
host_registers_control_.set_thread(thread);
|
||||
call_stack_control_.set_thread(thread);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "xenia/debug/ui/view.h"
|
||||
#include "xenia/debug/ui/views/cpu/call_stack_control.h"
|
||||
#include "xenia/debug/ui/views/cpu/register_list_control.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
@@ -42,6 +43,10 @@ class CpuView : public View {
|
||||
// TODO(benvanik): better state machine.
|
||||
model::Thread* current_thread_ = nullptr;
|
||||
|
||||
RegisterListControl gr_registers_control_;
|
||||
RegisterListControl fr_registers_control_;
|
||||
RegisterListControl vr_registers_control_;
|
||||
RegisterListControl host_registers_control_;
|
||||
CallStackControl call_stack_control_;
|
||||
};
|
||||
|
||||
|
||||
324
src/xenia/debug/ui/views/cpu/register_list_control.cc
Normal file
324
src/xenia/debug/ui/views/cpu/register_list_control.cc
Normal file
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "el/animation_manager.h"
|
||||
#include "xenia/base/string_buffer.h"
|
||||
#include "xenia/base/string_util.h"
|
||||
#include "xenia/cpu/x64_context.h"
|
||||
#include "xenia/debug/ui/views/cpu/register_list_control.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
namespace views {
|
||||
namespace cpu {
|
||||
|
||||
using xe::cpu::frontend::PPCRegister;
|
||||
using xe::cpu::X64Register;
|
||||
|
||||
enum class RegisterType {
|
||||
kControl,
|
||||
kInteger,
|
||||
kFloat,
|
||||
kVector,
|
||||
};
|
||||
|
||||
class RegisterItem : public el::GenericStringItem {
|
||||
public:
|
||||
const char* name() { return name_.c_str(); }
|
||||
|
||||
virtual void set_thread(model::Thread* thread) { thread_ = thread; }
|
||||
|
||||
protected:
|
||||
RegisterItem(RegisterSet set, RegisterType type, int ordinal)
|
||||
: GenericStringItem(""), set_(set), type_(type), ordinal_(ordinal) {
|
||||
tag.set_integer(ordinal);
|
||||
}
|
||||
|
||||
RegisterSet set_;
|
||||
RegisterType type_;
|
||||
int ordinal_;
|
||||
model::Thread* thread_ = nullptr;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
class GuestRegisterItem : public RegisterItem {
|
||||
public:
|
||||
GuestRegisterItem(RegisterSet set, RegisterType type, PPCRegister reg)
|
||||
: RegisterItem(set, type, static_cast<int>(reg)), reg_(reg) {
|
||||
name_ = xe::cpu::frontend::PPCContext::GetRegisterName(reg_);
|
||||
}
|
||||
|
||||
void set_thread(model::Thread* thread) override {
|
||||
RegisterItem::set_thread(thread);
|
||||
if (thread_) {
|
||||
str = thread_->guest_context()->GetStringFromValue(reg_);
|
||||
} else {
|
||||
str = "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
PPCRegister reg_;
|
||||
};
|
||||
|
||||
class HostRegisterItem : public RegisterItem {
|
||||
public:
|
||||
HostRegisterItem(RegisterSet set, RegisterType type, X64Register reg)
|
||||
: RegisterItem(set, type, static_cast<int>(reg)), reg_(reg) {
|
||||
name_ = xe::cpu::X64Context::GetRegisterName(reg_);
|
||||
}
|
||||
|
||||
void set_thread(model::Thread* thread) override {
|
||||
RegisterItem::set_thread(thread);
|
||||
if (thread_) {
|
||||
str = thread_->host_context()->GetStringFromValue(reg_);
|
||||
} else {
|
||||
str = "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
X64Register reg_;
|
||||
};
|
||||
|
||||
class RegisterItemElement : public el::LayoutBox {
|
||||
public:
|
||||
RegisterItemElement(RegisterItem* item, RegisterItemSource* source,
|
||||
el::ListItemObserver* source_viewer, size_t index)
|
||||
: item_(item),
|
||||
source_(source),
|
||||
source_viewer_(source_viewer),
|
||||
index_(index) {
|
||||
set_background_skin(TBIDC("ListItem"));
|
||||
set_axis(el::Axis::kY);
|
||||
set_gravity(el::Gravity::kAll);
|
||||
set_layout_position(el::LayoutPosition::kLeftTop);
|
||||
set_layout_distribution(el::LayoutDistribution::kAvailable);
|
||||
set_layout_distribution_position(el::LayoutDistributionPosition::kLeftTop);
|
||||
set_layout_size(el::LayoutSize::kAvailable);
|
||||
set_paint_overflow_fadeout(false);
|
||||
|
||||
using namespace el::dsl; // NOLINT(build/namespaces)
|
||||
el::AnimationBlocker animation_blocker;
|
||||
|
||||
auto node = LayoutBoxNode()
|
||||
.axis(Axis::kX)
|
||||
.gravity(Gravity::kLeft)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
.child(LabelNode(item_->name())
|
||||
.ignore_input(true)
|
||||
.width(45_px)
|
||||
.gravity(Gravity::kLeft))
|
||||
.child(TextBoxNode(item_->str.c_str())
|
||||
.gravity(Gravity::kLeftRight));
|
||||
content_root()->LoadNodeTree(node);
|
||||
}
|
||||
|
||||
bool OnEvent(const el::Event& ev) override {
|
||||
return el::LayoutBox::OnEvent(ev);
|
||||
}
|
||||
|
||||
private:
|
||||
RegisterItem* item_ = nullptr;
|
||||
RegisterItemSource* source_ = nullptr;
|
||||
el::ListItemObserver* source_viewer_ = nullptr;
|
||||
size_t index_ = 0;
|
||||
};
|
||||
|
||||
class RegisterItemSource : public el::ListItemSourceList<RegisterItem> {
|
||||
public:
|
||||
el::Element* CreateItemElement(size_t index,
|
||||
el::ListItemObserver* viewer) override {
|
||||
return new RegisterItemElement(at(index), this, viewer, index);
|
||||
}
|
||||
};
|
||||
|
||||
void DefineRegisterItem(RegisterItemSource* item_source, RegisterSet set,
|
||||
RegisterType type, PPCRegister reg) {
|
||||
auto item = std::make_unique<GuestRegisterItem>(set, type, reg);
|
||||
item->tag.set_integer(static_cast<int>(reg));
|
||||
item_source->push_back(std::move(item));
|
||||
}
|
||||
void DefineRegisterItem(RegisterItemSource* item_source, RegisterSet set,
|
||||
RegisterType type, X64Register reg) {
|
||||
auto item = std::make_unique<HostRegisterItem>(set, type, reg);
|
||||
item->tag.set_integer(static_cast<int>(reg));
|
||||
item_source->push_back(std::move(item));
|
||||
}
|
||||
|
||||
RegisterListControl::RegisterListControl(RegisterSet set)
|
||||
: set_(set), item_source_(new RegisterItemSource()) {
|
||||
switch (set_) {
|
||||
case RegisterSet::kGeneral: {
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
PPCRegister::kLR);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
PPCRegister::kCTR);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kControl,
|
||||
PPCRegister::kXER);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kControl,
|
||||
PPCRegister::kCR);
|
||||
for (size_t i = 0; i < 32; ++i) {
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
static_cast<PPCRegister>(
|
||||
static_cast<size_t>(PPCRegister::kR0) + i));
|
||||
}
|
||||
} break;
|
||||
case RegisterSet::kFloat: {
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kControl,
|
||||
PPCRegister::kFPSCR);
|
||||
for (size_t i = 0; i < 32; ++i) {
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kFloat,
|
||||
static_cast<PPCRegister>(
|
||||
static_cast<size_t>(PPCRegister::kFR0) + i));
|
||||
}
|
||||
} break;
|
||||
case RegisterSet::kVector: {
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kControl,
|
||||
PPCRegister::kVSCR);
|
||||
for (size_t i = 0; i < 128; ++i) {
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
static_cast<PPCRegister>(
|
||||
static_cast<size_t>(PPCRegister::kVR0) + i));
|
||||
}
|
||||
} break;
|
||||
case RegisterSet::kHost: {
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kRip);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kControl,
|
||||
X64Register::kEflags);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kRax);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kRcx);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kRdx);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kRbx);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kRsp);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kRbp);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kRsi);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kRdi);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kR8);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kR9);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kR10);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kR11);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kR12);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kR13);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kR14);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kInteger,
|
||||
X64Register::kR15);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm0);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm1);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm2);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm3);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm4);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm5);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm6);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm7);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm8);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm9);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm10);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm11);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm12);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm13);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm14);
|
||||
DefineRegisterItem(item_source_.get(), set_, RegisterType::kVector,
|
||||
X64Register::kXmm15);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
RegisterListControl::~RegisterListControl() = default;
|
||||
|
||||
el::Element* RegisterListControl::BuildUI() {
|
||||
using namespace el::dsl; // NOLINT(build/namespaces)
|
||||
el::AnimationBlocker animation_blocker;
|
||||
|
||||
auto node =
|
||||
LayoutBoxNode()
|
||||
.gravity(Gravity::kAll)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
.child(ListBoxNode().id("register_listbox").gravity(Gravity::kAll));
|
||||
|
||||
root_element_.set_gravity(Gravity::kAll);
|
||||
root_element_.set_layout_distribution(LayoutDistribution::kAvailable);
|
||||
root_element_.LoadNodeTree(node);
|
||||
|
||||
auto register_listbox =
|
||||
root_element_.GetElementById<el::ListBox>(TBIDC("register_listbox"));
|
||||
register_listbox->set_source(item_source_.get());
|
||||
register_listbox->scroll_container()->set_scroll_mode(
|
||||
el::ScrollMode::kAutoXAutoY);
|
||||
|
||||
handler_ = std::make_unique<el::EventHandler>(&root_element_);
|
||||
|
||||
return &root_element_;
|
||||
}
|
||||
|
||||
void RegisterListControl::Setup(DebugClient* client) {
|
||||
client_ = client;
|
||||
|
||||
system()->on_thread_state_updated.AddListener([this](model::Thread* thread) {
|
||||
if (thread == thread_) {
|
||||
InvalidateRegisters();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void RegisterListControl::set_thread(model::Thread* thread) {
|
||||
thread_ = thread;
|
||||
InvalidateRegisters();
|
||||
}
|
||||
|
||||
void RegisterListControl::InvalidateRegisters() {
|
||||
for (size_t i = 0; i < item_source_->size(); ++i) {
|
||||
item_source_->at(i)->set_thread(thread_);
|
||||
}
|
||||
|
||||
auto register_listbox =
|
||||
root_element_.GetElementById<el::ListBox>(TBIDC("register_listbox"));
|
||||
register_listbox->InvalidateList();
|
||||
}
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace views
|
||||
} // namespace ui
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
59
src/xenia/debug/ui/views/cpu/register_list_control.h
Normal file
59
src/xenia/debug/ui/views/cpu/register_list_control.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_DEBUG_UI_VIEWS_CPU_REGISTER_LIST_CONTROL_H_
|
||||
#define XENIA_DEBUG_UI_VIEWS_CPU_REGISTER_LIST_CONTROL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "xenia/debug/ui/control.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
namespace views {
|
||||
namespace cpu {
|
||||
|
||||
class RegisterItemSource;
|
||||
|
||||
enum class RegisterSet {
|
||||
kGeneral,
|
||||
kFloat,
|
||||
kVector,
|
||||
kHost,
|
||||
};
|
||||
|
||||
class RegisterListControl : public Control {
|
||||
public:
|
||||
RegisterListControl(RegisterSet set);
|
||||
~RegisterListControl() override;
|
||||
|
||||
el::Element* BuildUI() override;
|
||||
|
||||
void Setup(xe::debug::DebugClient* client) override;
|
||||
|
||||
model::Thread* thread() const { return thread_; }
|
||||
void set_thread(model::Thread* thread);
|
||||
|
||||
private:
|
||||
void InvalidateRegisters();
|
||||
|
||||
RegisterSet set_;
|
||||
model::Thread* thread_ = nullptr;
|
||||
std::unique_ptr<RegisterItemSource> item_source_;
|
||||
};
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace views
|
||||
} // namespace ui
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_DEBUG_UI_VIEWS_CPU_REGISTER_LIST_CONTROL_H_
|
||||
Reference in New Issue
Block a user