Cleaning up debugger threading and adding hacky callstacks to UI.

This commit is contained in:
Ben Vanik
2015-08-05 07:50:37 -07:00
parent 0a8d6eec91
commit 48d6e6becf
17 changed files with 283 additions and 110 deletions

View File

@@ -60,7 +60,6 @@ std::unique_ptr<Application> Application::Create() {
bool Application::Initialize() {
// Bind the object model to the client so it'll maintain state.
system_ = std::make_unique<model::System>(loop(), &client_);
client_.set_listener(system_.get());
// TODO(benvanik): flags and such.
if (!client_.Connect("localhost", 9002)) {

View File

@@ -17,13 +17,7 @@ namespace ui {
namespace model {
void Module::Update(const proto::ModuleListEntry* entry) {
if (!entry_.module_handle) {
std::memcpy(&entry_, entry, sizeof(entry_));
} else {
std::memcpy(&temp_entry_, entry, sizeof(temp_entry_));
system_->loop()->Post(
[this]() { std::memcpy(&entry_, &temp_entry_, sizeof(temp_entry_)); });
}
std::memcpy(&entry_, entry, sizeof(entry_));
}
} // namespace model

View File

@@ -40,7 +40,6 @@ class Module {
System* system_ = nullptr;
bool is_dead_ = false;
proto::ModuleListEntry entry_ = {0};
proto::ModuleListEntry temp_entry_ = {0};
};
} // namespace model

View File

@@ -19,15 +19,16 @@ namespace model {
using namespace xe::debug::proto;
System::System(xe::ui::Loop* loop, DebugClient* client)
: loop_(loop), client_(client) {}
ExecutionState System::execution_state() {
std::lock_guard<std::recursive_mutex> lock(mutex_);
return client_->execution_state();
: loop_(loop), client_(client) {
client_->set_listener(this);
client_->set_loop(loop);
}
System::~System() { client_->set_listener(nullptr); }
ExecutionState System::execution_state() { return client_->execution_state(); }
std::vector<Module*> System::modules() {
std::lock_guard<std::recursive_mutex> lock(mutex_);
std::vector<Module*> result;
for (auto& module : modules_) {
result.push_back(module.get());
@@ -36,7 +37,6 @@ std::vector<Module*> System::modules() {
}
std::vector<Thread*> System::threads() {
std::lock_guard<std::recursive_mutex> lock(mutex_);
std::vector<Thread*> result;
for (auto& thread : threads_) {
result.push_back(thread.get());
@@ -45,86 +45,80 @@ std::vector<Thread*> System::threads() {
}
Module* System::GetModuleByHandle(uint32_t module_handle) {
std::lock_guard<std::recursive_mutex> lock(mutex_);
auto it = modules_by_handle_.find(module_handle);
return it != modules_by_handle_.end() ? it->second : nullptr;
}
Thread* System::GetThreadByHandle(uint32_t thread_handle) {
std::lock_guard<std::recursive_mutex> lock(mutex_);
auto it = threads_by_handle_.find(thread_handle);
return it != threads_by_handle_.end() ? it->second : nullptr;
}
void System::OnExecutionStateChanged(ExecutionState execution_state) {
loop_->Post([this]() {
std::lock_guard<std::recursive_mutex> lock(mutex_);
on_execution_state_changed();
});
on_execution_state_changed();
}
void System::OnModulesUpdated(std::vector<const ModuleListEntry*> entries) {
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
std::unordered_set<uint32_t> extra_modules;
for (size_t i = 0; i < modules_.size(); ++i) {
extra_modules.emplace(modules_[i]->module_handle());
}
for (auto entry : entries) {
auto existing_module = modules_by_handle_.find(entry->module_handle);
if (existing_module == modules_by_handle_.end()) {
auto module = std::make_unique<Module>(this);
module->Update(entry);
modules_by_handle_.emplace(entry->module_handle, module.get());
modules_.emplace_back(std::move(module));
} else {
existing_module->second->Update(entry);
extra_modules.erase(existing_module->first);
}
}
for (auto module_handle : extra_modules) {
auto module = modules_by_handle_.find(module_handle);
if (module != modules_by_handle_.end()) {
module->second->set_dead(true);
}
std::unordered_set<uint32_t> extra_modules;
for (size_t i = 0; i < modules_.size(); ++i) {
extra_modules.emplace(modules_[i]->module_handle());
}
for (auto entry : entries) {
auto existing_module = modules_by_handle_.find(entry->module_handle);
if (existing_module == modules_by_handle_.end()) {
auto module = std::make_unique<Module>(this);
module->Update(entry);
modules_by_handle_.emplace(entry->module_handle, module.get());
modules_.emplace_back(std::move(module));
} else {
existing_module->second->Update(entry);
extra_modules.erase(existing_module->first);
}
}
loop_->Post([this]() {
std::lock_guard<std::recursive_mutex> lock(mutex_);
on_modules_updated();
});
for (auto module_handle : extra_modules) {
auto module = modules_by_handle_.find(module_handle);
if (module != modules_by_handle_.end()) {
module->second->set_dead(true);
}
}
on_modules_updated();
}
void System::OnThreadsUpdated(std::vector<const ThreadListEntry*> entries) {
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
std::unordered_set<uint32_t> extra_threads;
for (size_t i = 0; i < threads_.size(); ++i) {
extra_threads.emplace(threads_[i]->thread_handle());
}
for (auto entry : entries) {
auto existing_thread = threads_by_handle_.find(entry->thread_handle);
if (existing_thread == threads_by_handle_.end()) {
auto thread = std::make_unique<Thread>(this);
thread->Update(entry);
threads_by_handle_.emplace(entry->thread_handle, thread.get());
threads_.emplace_back(std::move(thread));
} else {
existing_thread->second->Update(entry);
extra_threads.erase(existing_thread->first);
}
}
for (auto thread_handle : extra_threads) {
auto thread = threads_by_handle_.find(thread_handle);
if (thread != threads_by_handle_.end()) {
thread->second->set_dead(true);
}
std::unordered_set<uint32_t> extra_threads;
for (size_t i = 0; i < threads_.size(); ++i) {
extra_threads.emplace(threads_[i]->thread_handle());
}
for (auto entry : entries) {
auto existing_thread = threads_by_handle_.find(entry->thread_handle);
if (existing_thread == threads_by_handle_.end()) {
auto thread = std::make_unique<Thread>(this);
thread->Update(entry);
threads_by_handle_.emplace(entry->thread_handle, thread.get());
threads_.emplace_back(std::move(thread));
} else {
existing_thread->second->Update(entry);
extra_threads.erase(existing_thread->first);
}
}
loop_->Post([this]() {
std::lock_guard<std::recursive_mutex> lock(mutex_);
on_threads_updated();
});
for (auto thread_handle : extra_threads) {
auto thread = threads_by_handle_.find(thread_handle);
if (thread != threads_by_handle_.end()) {
thread->second->set_dead(true);
}
}
on_threads_updated();
}
void System::OnThreadCallStackUpdated(
uint32_t thread_handle, 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);
}
}
} // namespace model

View File

@@ -12,7 +12,6 @@
#include <cstdint>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <vector>
@@ -28,9 +27,10 @@ namespace debug {
namespace ui {
namespace model {
class System : public ClientListener {
class System : public DebugClientListener {
public:
System(xe::ui::Loop* loop, DebugClient* client);
~System() override;
xe::ui::Loop* loop() const { return loop_; }
DebugClient* client() const { return client_; }
@@ -46,6 +46,7 @@ class System : public ClientListener {
Delegate<void> on_execution_state_changed;
Delegate<void> on_modules_updated;
Delegate<void> on_threads_updated;
Delegate<Thread*> on_thread_call_stack_updated;
private:
void OnExecutionStateChanged(ExecutionState execution_state) override;
@@ -53,11 +54,13 @@ class System : public ClientListener {
std::vector<const proto::ModuleListEntry*> entries) override;
void OnThreadsUpdated(
std::vector<const proto::ThreadListEntry*> entries) override;
void OnThreadCallStackUpdated(
uint32_t thread_handle,
std::vector<const ThreadCallStackFrame*> frames) override;
xe::ui::Loop* loop_ = nullptr;
DebugClient* client_ = nullptr;
std::recursive_mutex mutex_;
std::vector<std::unique_ptr<Module>> modules_;
std::unordered_map<uint32_t, Module*> modules_by_handle_;
std::vector<std::unique_ptr<Thread>> threads_;

View File

@@ -25,12 +25,14 @@ std::string Thread::to_string() {
}
void Thread::Update(const proto::ThreadListEntry* entry) {
if (!entry_.thread_handle) {
std::memcpy(&entry_, entry, sizeof(entry_));
} else {
std::memcpy(&temp_entry_, entry, sizeof(temp_entry_));
system_->loop()->Post(
[this]() { std::memcpy(&entry_, &temp_entry_, sizeof(temp_entry_)); });
std::memcpy(&entry_, entry, sizeof(entry_));
}
void Thread::UpdateCallStack(
std::vector<const proto::ThreadCallStackFrame*> frames) {
call_stack_.resize(frames.size());
for (size_t i = 0; i < frames.size(); ++i) {
std::memcpy(call_stack_.data() + i, frames[i], sizeof(Frame));
}
}

View File

@@ -12,6 +12,7 @@
#include <cstdint>
#include <string>
#include <vector>
#include "xenia/debug/proto/xdp_protocol.h"
@@ -24,6 +25,8 @@ class System;
class Thread {
public:
using Frame = proto::ThreadCallStackFrame;
Thread(System* system) : system_(system) {}
bool is_dead() const { return is_dead_; }
@@ -34,16 +37,18 @@ class Thread {
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 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);
private:
System* system_ = nullptr;
bool is_dead_ = false;
proto::ThreadListEntry entry_ = {0};
proto::ThreadListEntry temp_entry_ = {0};
std::vector<Frame> call_stack_;
};
} // namespace model

View File

@@ -8,6 +8,7 @@
*/
#include "el/animation_manager.h"
#include "xenia/base/string_buffer.h"
#include "xenia/debug/ui/views/cpu/cpu_view.h"
namespace xe {
@@ -57,6 +58,7 @@ el::Element* CpuView::BuildUI() {
.axis(Axis::kX)
.child(ButtonNode("A")))
.child(TextBoxNode("source!")
.id("source_textbox")
.gravity(Gravity::kAll)
.is_multiline(true)
.is_read_only(true));
@@ -141,6 +143,20 @@ el::Element* CpuView::BuildUI() {
UpdateFunctionList();
return true;
});
handler_->Listen(
el::EventType::kChanged, TBIDC("thread_dropdown"),
[this](const el::Event& ev) {
auto thread_dropdown = root_element_.GetElementById<el::DropDownButton>(
TBIDC("thread_dropdown"));
auto thread_handle = uint32_t(thread_dropdown->selected_item_id());
if (thread_handle) {
current_thread_ = system()->GetThreadByHandle(thread_handle);
} else {
current_thread_ = nullptr;
}
UpdateThreadCallStack(current_thread_);
return true;
});
return &root_element_;
}
@@ -152,6 +168,12 @@ void CpuView::Setup(DebugClient* client) {
[this]() { UpdateElementState(); });
system()->on_modules_updated.AddListener([this]() { UpdateModuleList(); });
system()->on_threads_updated.AddListener([this]() { UpdateThreadList(); });
system()->on_thread_call_stack_updated.AddListener(
[this](model::Thread* thread) {
if (thread == current_thread_) {
UpdateThreadCallStack(thread);
}
});
}
void CpuView::UpdateElementState() {
@@ -219,6 +241,30 @@ void CpuView::UpdateThreadList() {
}
}
void CpuView::UpdateThreadCallStack(model::Thread* thread) {
auto textbox =
root_element_.GetElementById<el::TextBox>(TBIDC("source_textbox"));
if (!thread) {
textbox->set_text("no thread");
return;
}
auto& call_stack = thread->call_stack();
StringBuffer str;
for (size_t i = 0; i < call_stack.size(); ++i) {
auto& frame = call_stack[i];
size_t ordinal = call_stack.size() - i - 1;
if (frame.guest_pc) {
str.AppendFormat(" %.2lld %.16llX %.8X %s", ordinal, frame.host_pc,
frame.guest_pc, frame.name);
} else {
str.AppendFormat(" %.2lld %.16llX %s", ordinal, frame.host_pc,
frame.name);
}
str.Append('\n');
}
textbox->set_text(str.to_string());
}
} // namespace cpu
} // namespace views
} // namespace ui

View File

@@ -35,6 +35,10 @@ class CpuView : public View {
void UpdateModuleList();
void UpdateFunctionList();
void UpdateThreadList();
void UpdateThreadCallStack(model::Thread* thread);
// TODO(benvanik): better state machine.
model::Thread* current_thread_ = nullptr;
};
} // namespace cpu