summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbunnei <bunneidev@gmail.com>2016-10-05 05:01:56 +0200
committerGitHub <noreply@github.com>2016-10-05 05:01:56 +0200
commit09c3e444d42856ca0978153dcabcb5c3884877b9 (patch)
tree89732a2264868d7dddc8f4c8355c0afac62b108c
parentMerge pull request #2106 from wwylele/delete-recursive (diff)
parentmove ResetType to kernel.h (diff)
downloadyuzu-09c3e444d42856ca0978153dcabcb5c3884877b9.tar
yuzu-09c3e444d42856ca0978153dcabcb5c3884877b9.tar.gz
yuzu-09c3e444d42856ca0978153dcabcb5c3884877b9.tar.bz2
yuzu-09c3e444d42856ca0978153dcabcb5c3884877b9.tar.lz
yuzu-09c3e444d42856ca0978153dcabcb5c3884877b9.tar.xz
yuzu-09c3e444d42856ca0978153dcabcb5c3884877b9.tar.zst
yuzu-09c3e444d42856ca0978153dcabcb5c3884877b9.zip
-rw-r--r--src/citra_qt/CMakeLists.txt2
-rw-r--r--src/citra_qt/debugger/wait_tree.cpp417
-rw-r--r--src/citra_qt/debugger/wait_tree.h186
-rw-r--r--src/citra_qt/main.cpp13
-rw-r--r--src/citra_qt/main.h2
-rw-r--r--src/core/hle/kernel/event.h6
-rw-r--r--src/core/hle/kernel/kernel.cpp4
-rw-r--r--src/core/hle/kernel/kernel.h9
-rw-r--r--src/core/hle/kernel/thread.cpp4
-rw-r--r--src/core/hle/kernel/thread.h5
-rw-r--r--src/core/hle/kernel/timer.h1
-rw-r--r--src/core/hle/svc.cpp4
12 files changed, 646 insertions, 7 deletions
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index e97d33da4..b3c01ddd8 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -15,6 +15,7 @@ set(SRCS
debugger/profiler.cpp
debugger/ramview.cpp
debugger/registers.cpp
+ debugger/wait_tree.cpp
util/spinbox.cpp
util/util.cpp
bootmanager.cpp
@@ -48,6 +49,7 @@ set(HEADERS
debugger/profiler.h
debugger/ramview.h
debugger/registers.h
+ debugger/wait_tree.h
util/spinbox.h
util/util.h
bootmanager.h
diff --git a/src/citra_qt/debugger/wait_tree.cpp b/src/citra_qt/debugger/wait_tree.cpp
new file mode 100644
index 000000000..be5a51e52
--- /dev/null
+++ b/src/citra_qt/debugger/wait_tree.cpp
@@ -0,0 +1,417 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "citra_qt/debugger/wait_tree.h"
+#include "citra_qt/util/util.h"
+
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/semaphore.h"
+#include "core/hle/kernel/session.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/timer.h"
+
+WaitTreeItem::~WaitTreeItem() {}
+
+QColor WaitTreeItem::GetColor() const {
+ return QColor(Qt::GlobalColor::black);
+}
+
+std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeItem::GetChildren() const {
+ return {};
+}
+
+void WaitTreeItem::Expand() {
+ if (IsExpandable() && !expanded) {
+ children = GetChildren();
+ for (std::size_t i = 0; i < children.size(); ++i) {
+ children[i]->parent = this;
+ children[i]->row = i;
+ }
+ expanded = true;
+ }
+}
+
+WaitTreeItem* WaitTreeItem::Parent() const {
+ return parent;
+}
+
+const std::vector<std::unique_ptr<WaitTreeItem>>& WaitTreeItem::Children() const {
+ return children;
+}
+
+bool WaitTreeItem::IsExpandable() const {
+ return false;
+}
+
+std::size_t WaitTreeItem::Row() const {
+ return row;
+}
+
+std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() {
+ const auto& threads = Kernel::GetThreadList();
+ std::vector<std::unique_ptr<WaitTreeThread>> item_list;
+ item_list.reserve(threads.size());
+ for (std::size_t i = 0; i < threads.size(); ++i) {
+ item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i]));
+ item_list.back()->row = i;
+ }
+ return item_list;
+}
+
+WaitTreeText::WaitTreeText(const QString& t) : text(t) {}
+
+QString WaitTreeText::GetText() const {
+ return text;
+}
+
+WaitTreeWaitObject::WaitTreeWaitObject(const Kernel::WaitObject& o) : object(o) {}
+
+bool WaitTreeExpandableItem::IsExpandable() const {
+ return true;
+}
+
+QString WaitTreeWaitObject::GetText() const {
+ return tr("[%1]%2 %3")
+ .arg(object.GetObjectId())
+ .arg(QString::fromStdString(object.GetTypeName()),
+ QString::fromStdString(object.GetName()));
+}
+
+std::unique_ptr<WaitTreeWaitObject> WaitTreeWaitObject::make(const Kernel::WaitObject& object) {
+ switch (object.GetHandleType()) {
+ case Kernel::HandleType::Event:
+ return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::Event&>(object));
+ case Kernel::HandleType::Mutex:
+ return std::make_unique<WaitTreeMutex>(static_cast<const Kernel::Mutex&>(object));
+ case Kernel::HandleType::Semaphore:
+ return std::make_unique<WaitTreeSemaphore>(static_cast<const Kernel::Semaphore&>(object));
+ case Kernel::HandleType::Timer:
+ return std::make_unique<WaitTreeTimer>(static_cast<const Kernel::Timer&>(object));
+ case Kernel::HandleType::Thread:
+ return std::make_unique<WaitTreeThread>(static_cast<const Kernel::Thread&>(object));
+ default:
+ return std::make_unique<WaitTreeWaitObject>(object);
+ }
+}
+
+std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeWaitObject::GetChildren() const {
+ std::vector<std::unique_ptr<WaitTreeItem>> list;
+
+ const auto& threads = object.GetWaitingThreads();
+ if (threads.empty()) {
+ list.push_back(std::make_unique<WaitTreeText>(tr("waited by no thread")));
+ } else {
+ list.push_back(std::make_unique<WaitTreeThreadList>(threads));
+ }
+ return list;
+}
+
+QString WaitTreeWaitObject::GetResetTypeQString(Kernel::ResetType reset_type) {
+ switch (reset_type) {
+ case Kernel::ResetType::OneShot:
+ return tr("one shot");
+ case Kernel::ResetType::Sticky:
+ return tr("sticky");
+ case Kernel::ResetType::Pulse:
+ return tr("pulse");
+ }
+}
+
+WaitTreeObjectList::WaitTreeObjectList(
+ const std::vector<Kernel::SharedPtr<Kernel::WaitObject>>& list, bool w_all)
+ : object_list(list), wait_all(w_all) {}
+
+QString WaitTreeObjectList::GetText() const {
+ if (wait_all)
+ return tr("waiting for all objects");
+ return tr("waiting for one of the following objects");
+}
+
+std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeObjectList::GetChildren() const {
+ std::vector<std::unique_ptr<WaitTreeItem>> list(object_list.size());
+ std::transform(object_list.begin(), object_list.end(), list.begin(),
+ [](const auto& t) { return WaitTreeWaitObject::make(*t); });
+ return list;
+}
+
+WaitTreeThread::WaitTreeThread(const Kernel::Thread& thread) : WaitTreeWaitObject(thread) {}
+
+QString WaitTreeThread::GetText() const {
+ const auto& thread = static_cast<const Kernel::Thread&>(object);
+ QString status;
+ switch (thread.status) {
+ case THREADSTATUS_RUNNING:
+ status = tr("running");
+ break;
+ case THREADSTATUS_READY:
+ status = tr("ready");
+ break;
+ case THREADSTATUS_WAIT_ARB:
+ status = tr("waiting for address 0x%1").arg(thread.wait_address, 8, 16, QLatin1Char('0'));
+ break;
+ case THREADSTATUS_WAIT_SLEEP:
+ status = tr("sleeping");
+ break;
+ case THREADSTATUS_WAIT_SYNCH:
+ status = tr("waiting for objects");
+ break;
+ case THREADSTATUS_DORMANT:
+ status = tr("dormant");
+ break;
+ case THREADSTATUS_DEAD:
+ status = tr("dead");
+ break;
+ }
+ QString pc_info = tr(" PC = 0x%1 LR = 0x%2")
+ .arg(thread.context.pc, 8, 16, QLatin1Char('0'))
+ .arg(thread.context.lr, 8, 16, QLatin1Char('0'));
+ return WaitTreeWaitObject::GetText() + pc_info + " (" + status + ") ";
+}
+
+QColor WaitTreeThread::GetColor() const {
+ const auto& thread = static_cast<const Kernel::Thread&>(object);
+ switch (thread.status) {
+ case THREADSTATUS_RUNNING:
+ return QColor(Qt::GlobalColor::darkGreen);
+ case THREADSTATUS_READY:
+ return QColor(Qt::GlobalColor::darkBlue);
+ case THREADSTATUS_WAIT_ARB:
+ return QColor(Qt::GlobalColor::darkRed);
+ case THREADSTATUS_WAIT_SLEEP:
+ return QColor(Qt::GlobalColor::darkYellow);
+ case THREADSTATUS_WAIT_SYNCH:
+ return QColor(Qt::GlobalColor::red);
+ case THREADSTATUS_DORMANT:
+ return QColor(Qt::GlobalColor::darkCyan);
+ case THREADSTATUS_DEAD:
+ return QColor(Qt::GlobalColor::gray);
+ default:
+ return WaitTreeItem::GetColor();
+ }
+}
+
+std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
+ std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
+
+ const auto& thread = static_cast<const Kernel::Thread&>(object);
+
+ QString processor;
+ switch (thread.processor_id) {
+ case ThreadProcessorId::THREADPROCESSORID_DEFAULT:
+ processor = tr("default");
+ break;
+ case ThreadProcessorId::THREADPROCESSORID_ALL:
+ processor = tr("all");
+ break;
+ case ThreadProcessorId::THREADPROCESSORID_0:
+ processor = tr("AppCore");
+ break;
+ case ThreadProcessorId::THREADPROCESSORID_1:
+ processor = tr("SysCore");
+ break;
+ default:
+ processor = tr("Unknown processor %1").arg(thread.processor_id);
+ break;
+ }
+
+ list.push_back(std::make_unique<WaitTreeText>(tr("processor = %1").arg(processor)));
+ list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadId())));
+ list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)")
+ .arg(thread.current_priority)
+ .arg(thread.nominal_priority)));
+ list.push_back(std::make_unique<WaitTreeText>(
+ tr("last running ticks = %1").arg(thread.last_running_ticks)));
+
+ if (thread.held_mutexes.empty()) {
+ list.push_back(std::make_unique<WaitTreeText>(tr("not holding mutex")));
+ } else {
+ list.push_back(std::make_unique<WaitTreeMutexList>(thread.held_mutexes));
+ }
+ if (thread.status == THREADSTATUS_WAIT_SYNCH) {
+ list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, thread.wait_all));
+ }
+
+ return list;
+}
+
+WaitTreeEvent::WaitTreeEvent(const Kernel::Event& object) : WaitTreeWaitObject(object) {}
+
+std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
+ std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
+
+ list.push_back(std::make_unique<WaitTreeText>(
+ tr("reset type = %1")
+ .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).reset_type))));
+ return list;
+}
+
+WaitTreeMutex::WaitTreeMutex(const Kernel::Mutex& object) : WaitTreeWaitObject(object) {}
+
+std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutex::GetChildren() const {
+ std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
+
+ const auto& mutex = static_cast<const Kernel::Mutex&>(object);
+ if (mutex.lock_count) {
+ list.push_back(
+ std::make_unique<WaitTreeText>(tr("locked %1 times by thread:").arg(mutex.lock_count)));
+ list.push_back(std::make_unique<WaitTreeThread>(*mutex.holding_thread));
+ } else {
+ list.push_back(std::make_unique<WaitTreeText>(tr("free")));
+ }
+ return list;
+}
+
+WaitTreeSemaphore::WaitTreeSemaphore(const Kernel::Semaphore& object)
+ : WaitTreeWaitObject(object) {}
+
+std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeSemaphore::GetChildren() const {
+ std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
+
+ const auto& semaphore = static_cast<const Kernel::Semaphore&>(object);
+ list.push_back(
+ std::make_unique<WaitTreeText>(tr("available count = %1").arg(semaphore.available_count)));
+ list.push_back(std::make_unique<WaitTreeText>(tr("max count = %1").arg(semaphore.max_count)));
+ return list;
+}
+
+WaitTreeTimer::WaitTreeTimer(const Kernel::Timer& object) : WaitTreeWaitObject(object) {}
+
+std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeTimer::GetChildren() const {
+ std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
+
+ const auto& timer = static_cast<const Kernel::Timer&>(object);
+
+ list.push_back(std::make_unique<WaitTreeText>(
+ tr("reset type = %1").arg(GetResetTypeQString(timer.reset_type))));
+ list.push_back(
+ std::make_unique<WaitTreeText>(tr("initial delay = %1").arg(timer.initial_delay)));
+ list.push_back(
+ std::make_unique<WaitTreeText>(tr("interval delay = %1").arg(timer.interval_delay)));
+ return list;
+}
+
+WaitTreeMutexList::WaitTreeMutexList(
+ const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& list)
+ : mutex_list(list) {}
+
+QString WaitTreeMutexList::GetText() const {
+ return tr("holding mutexes");
+}
+
+std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexList::GetChildren() const {
+ std::vector<std::unique_ptr<WaitTreeItem>> list(mutex_list.size());
+ std::transform(mutex_list.begin(), mutex_list.end(), list.begin(),
+ [](const auto& t) { return std::make_unique<WaitTreeMutex>(*t); });
+ return list;
+}
+
+WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::SharedPtr<Kernel::Thread>>& list)
+ : thread_list(list) {}
+
+QString WaitTreeThreadList::GetText() const {
+ return tr("waited by thread");
+}
+
+std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThreadList::GetChildren() const {
+ std::vector<std::unique_ptr<WaitTreeItem>> list(thread_list.size());
+ std::transform(thread_list.begin(), thread_list.end(), list.begin(),
+ [](const auto& t) { return std::make_unique<WaitTreeThread>(*t); });
+ return list;
+}
+
+WaitTreeModel::WaitTreeModel(QObject* parent) : QAbstractItemModel(parent) {}
+
+QModelIndex WaitTreeModel::index(int row, int column, const QModelIndex& parent) const {
+ if (!hasIndex(row, column, parent))
+ return {};
+
+ if (parent.isValid()) {
+ WaitTreeItem* parent_item = static_cast<WaitTreeItem*>(parent.internalPointer());
+ parent_item->Expand();
+ return createIndex(row, column, parent_item->Children()[row].get());
+ }
+
+ return createIndex(row, column, thread_items[row].get());
+}
+
+QModelIndex WaitTreeModel::parent(const QModelIndex& index) const {
+ if (!index.isValid())
+ return {};
+
+ WaitTreeItem* parent_item = static_cast<WaitTreeItem*>(index.internalPointer())->Parent();
+ if (!parent_item) {
+ return QModelIndex();
+ }
+ return createIndex(static_cast<int>(parent_item->Row()), 0, parent_item);
+}
+
+int WaitTreeModel::rowCount(const QModelIndex& parent) const {
+ if (!parent.isValid())
+ return static_cast<int>(thread_items.size());
+
+ WaitTreeItem* parent_item = static_cast<WaitTreeItem*>(parent.internalPointer());
+ parent_item->Expand();
+ return static_cast<int>(parent_item->Children().size());
+}
+
+int WaitTreeModel::columnCount(const QModelIndex&) const {
+ return 1;
+}
+
+QVariant WaitTreeModel::data(const QModelIndex& index, int role) const {
+ if (!index.isValid())
+ return {};
+
+ switch (role) {
+ case Qt::DisplayRole:
+ return static_cast<WaitTreeItem*>(index.internalPointer())->GetText();
+ case Qt::ForegroundRole:
+ return static_cast<WaitTreeItem*>(index.internalPointer())->GetColor();
+ default:
+ return {};
+ }
+}
+
+void WaitTreeModel::ClearItems() {
+ thread_items.clear();
+}
+
+void WaitTreeModel::InitItems() {
+ thread_items = WaitTreeItem::MakeThreadItemList();
+}
+
+WaitTreeWidget::WaitTreeWidget(QWidget* parent) : QDockWidget(tr("Wait Tree"), parent) {
+ setObjectName("WaitTreeWidget");
+ view = new QTreeView(this);
+ view->setHeaderHidden(true);
+ setWidget(view);
+ setEnabled(false);
+}
+
+void WaitTreeWidget::OnDebugModeEntered() {
+ if (!Core::g_app_core)
+ return;
+ model->InitItems();
+ view->setModel(model);
+ setEnabled(true);
+}
+
+void WaitTreeWidget::OnDebugModeLeft() {
+ setEnabled(false);
+ view->setModel(nullptr);
+ model->ClearItems();
+}
+
+void WaitTreeWidget::OnEmulationStarting(EmuThread* emu_thread) {
+ model = new WaitTreeModel(this);
+ view->setModel(model);
+ setEnabled(false);
+}
+
+void WaitTreeWidget::OnEmulationStopping() {
+ view->setModel(nullptr);
+ delete model;
+ setEnabled(false);
+}
diff --git a/src/citra_qt/debugger/wait_tree.h b/src/citra_qt/debugger/wait_tree.h
new file mode 100644
index 000000000..5d1d964d1
--- /dev/null
+++ b/src/citra_qt/debugger/wait_tree.h
@@ -0,0 +1,186 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <boost/container/flat_set.hpp>
+
+#include <QAbstractItemModel>
+#include <QDockWidget>
+#include <QTreeView>
+
+#include "core/core.h"
+#include "core/hle/kernel/kernel.h"
+
+class EmuThread;
+
+namespace Kernel {
+class WaitObject;
+class Event;
+class Mutex;
+class Semaphore;
+class Session;
+class Thread;
+class Timer;
+}
+
+class WaitTreeThread;
+
+class WaitTreeItem : public QObject {
+ Q_OBJECT
+public:
+ virtual bool IsExpandable() const;
+ virtual std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const;
+ virtual QString GetText() const = 0;
+ virtual QColor GetColor() const;
+ virtual ~WaitTreeItem();
+ void Expand();
+ WaitTreeItem* Parent() const;
+ const std::vector<std::unique_ptr<WaitTreeItem>>& Children() const;
+ std::size_t Row() const;
+ static std::vector<std::unique_ptr<WaitTreeThread>> MakeThreadItemList();
+
+private:
+ std::size_t row;
+ bool expanded = false;
+ WaitTreeItem* parent = nullptr;
+ std::vector<std::unique_ptr<WaitTreeItem>> children;
+};
+
+class WaitTreeText : public WaitTreeItem {
+ Q_OBJECT
+public:
+ WaitTreeText(const QString& text);
+ QString GetText() const override;
+
+private:
+ QString text;
+};
+
+class WaitTreeExpandableItem : public WaitTreeItem {
+ Q_OBJECT
+public:
+ bool IsExpandable() const override;
+};
+
+class WaitTreeWaitObject : public WaitTreeExpandableItem {
+ Q_OBJECT
+public:
+ WaitTreeWaitObject(const Kernel::WaitObject& object);
+ static std::unique_ptr<WaitTreeWaitObject> make(const Kernel::WaitObject& object);
+ QString GetText() const override;
+ std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
+
+protected:
+ const Kernel::WaitObject& object;
+
+ static QString GetResetTypeQString(Kernel::ResetType reset_type);
+};
+
+class WaitTreeObjectList : public WaitTreeExpandableItem {
+ Q_OBJECT
+public:
+ WaitTreeObjectList(const std::vector<Kernel::SharedPtr<Kernel::WaitObject>>& list,
+ bool wait_all);
+ QString GetText() const override;
+ std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
+
+private:
+ const std::vector<Kernel::SharedPtr<Kernel::WaitObject>>& object_list;
+ bool wait_all;
+};
+
+class WaitTreeThread : public WaitTreeWaitObject {
+ Q_OBJECT
+public:
+ WaitTreeThread(const Kernel::Thread& thread);
+ QString GetText() const override;
+ QColor GetColor() const override;
+ std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
+};
+
+class WaitTreeEvent : public WaitTreeWaitObject {
+ Q_OBJECT
+public:
+ WaitTreeEvent(const Kernel::Event& object);
+ std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
+};
+
+class WaitTreeMutex : public WaitTreeWaitObject {
+ Q_OBJECT
+public:
+ WaitTreeMutex(const Kernel::Mutex& object);
+ std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
+};
+
+class WaitTreeSemaphore : public WaitTreeWaitObject {
+ Q_OBJECT
+public:
+ WaitTreeSemaphore(const Kernel::Semaphore& object);
+ std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
+};
+
+class WaitTreeTimer : public WaitTreeWaitObject {
+ Q_OBJECT
+public:
+ WaitTreeTimer(const Kernel::Timer& object);
+ std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
+};
+
+class WaitTreeMutexList : public WaitTreeExpandableItem {
+ Q_OBJECT
+public:
+ WaitTreeMutexList(const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& list);
+ QString GetText() const override;
+ std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
+
+private:
+ const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& mutex_list;
+};
+
+class WaitTreeThreadList : public WaitTreeExpandableItem {
+ Q_OBJECT
+public:
+ WaitTreeThreadList(const std::vector<Kernel::SharedPtr<Kernel::Thread>>& list);
+ QString GetText() const override;
+ std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
+
+private:
+ const std::vector<Kernel::SharedPtr<Kernel::Thread>>& thread_list;
+};
+
+class WaitTreeModel : public QAbstractItemModel {
+ Q_OBJECT
+
+public:
+ WaitTreeModel(QObject* parent = nullptr);
+
+ QVariant data(const QModelIndex& index, int role) const override;
+ QModelIndex index(int row, int column, const QModelIndex& parent) const override;
+ QModelIndex parent(const QModelIndex& index) const override;
+ int rowCount(const QModelIndex& parent) const override;
+ int columnCount(const QModelIndex& parent) const override;
+
+ void ClearItems();
+ void InitItems();
+
+private:
+ std::vector<std::unique_ptr<WaitTreeThread>> thread_items;
+};
+
+class WaitTreeWidget : public QDockWidget {
+ Q_OBJECT
+
+public:
+ WaitTreeWidget(QWidget* parent = nullptr);
+
+public slots:
+ void OnDebugModeEntered();
+ void OnDebugModeLeft();
+
+ void OnEmulationStarting(EmuThread* emu_thread);
+ void OnEmulationStopping();
+
+private:
+ QTreeView* view;
+ WaitTreeModel* model;
+};
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 0c7bedfcf..8322e2305 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -25,6 +25,7 @@
#include "citra_qt/debugger/profiler.h"
#include "citra_qt/debugger/ramview.h"
#include "citra_qt/debugger/registers.h"
+#include "citra_qt/debugger/wait_tree.h"
#include "citra_qt/game_list.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/main.h"
@@ -104,6 +105,10 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this,
SLOT(OnCreateGraphicsSurfaceViewer()));
+ waitTreeWidget = new WaitTreeWidget(this);
+ addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
+ waitTreeWidget->hide();
+
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
debug_menu->addAction(graphicsSurfaceViewerAction);
debug_menu->addSeparator();
@@ -119,6 +124,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
+ debug_menu->addAction(waitTreeWidget->toggleViewAction());
// Set default UI state
// geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
@@ -184,6 +190,9 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping()));
+ connect(this, SIGNAL(EmulationStarting(EmuThread*)), waitTreeWidget,
+ SLOT(OnEmulationStarting(EmuThread*)));
+ connect(this, SIGNAL(EmulationStopping()), waitTreeWidget, SLOT(OnEmulationStopping()));
// Setup hotkeys
RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
@@ -345,12 +354,16 @@ void GMainWindow::BootGame(const std::string& filename) {
SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget,
SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
+ connect(emu_thread.get(), SIGNAL(DebugModeEntered()), waitTreeWidget,
+ SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()),
Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()),
Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()),
Qt::BlockingQueuedConnection);
+ connect(emu_thread.get(), SIGNAL(DebugModeLeft()), waitTreeWidget, SLOT(OnDebugModeLeft()),
+ Qt::BlockingQueuedConnection);
// Update the GUI
registersWidget->OnDebugModeEntered();
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index c4349513f..2cf308d80 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -21,6 +21,7 @@ class RegistersWidget;
class CallstackWidget;
class GPUCommandStreamWidget;
class GPUCommandListWidget;
+class WaitTreeWidget;
class GMainWindow : public QMainWindow {
Q_OBJECT
@@ -128,6 +129,7 @@ private:
CallstackWidget* callstackWidget;
GPUCommandStreamWidget* graphicsWidget;
GPUCommandListWidget* graphicsCommandsWidget;
+ WaitTreeWidget* waitTreeWidget;
QAction* actions_recent_files[max_recent_files_item];
};
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index 6fe74065d..8dcd23edb 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -9,12 +9,6 @@
namespace Kernel {
-enum class ResetType {
- OneShot,
- Sticky,
- Pulse,
-};
-
class Event final : public WaitObject {
public:
/**
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 9a2c8ce05..9e1795927 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -40,6 +40,10 @@ void WaitObject::WakeupAllWaitingThreads() {
HLE::Reschedule(__func__);
}
+const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const {
+ return waiting_threads;
+}
+
HandleTable::HandleTable() {
next_generation = 1;
Clear();
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 0e95f7ff0..6b8dbecff 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -53,6 +53,12 @@ enum {
DEFAULT_STACK_SIZE = 0x4000,
};
+enum class ResetType {
+ OneShot,
+ Sticky,
+ Pulse,
+};
+
class Object : NonCopyable {
public:
virtual ~Object() {}
@@ -149,6 +155,9 @@ public:
/// Wake up all threads waiting on this object
void WakeupAllWaitingThreads();
+ /// Get a const reference to the waiting threads list for debug use
+ const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const;
+
private:
/// Threads waiting for this object to become available
std::vector<SharedPtr<Thread>> waiting_threads;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 4486a812c..c4eeeee56 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -665,4 +665,8 @@ void ThreadingShutdown() {
ready_queue.clear();
}
+const std::vector<SharedPtr<Thread>>& GetThreadList() {
+ return thread_list;
+}
+
} // namespace
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index f63131716..e0ffcea8a 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -236,4 +236,9 @@ void ThreadingInit();
*/
void ThreadingShutdown();
+/**
+ * Get a const reference to the thread list for debug use
+ */
+const std::vector<SharedPtr<Thread>>& GetThreadList();
+
} // namespace
diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h
index 59a77aad3..18ea0236b 100644
--- a/src/core/hle/kernel/timer.h
+++ b/src/core/hle/kernel/timer.h
@@ -5,7 +5,6 @@
#pragma once
#include "common/common_types.h"
-#include "core/hle/kernel/event.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 02b397eba..c6b80dc50 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -576,6 +576,7 @@ static ResultCode CreateMutex(Handle* out_handle, u32 initial_locked) {
using Kernel::Mutex;
SharedPtr<Mutex> mutex = Mutex::Create(initial_locked != 0);
+ mutex->name = Common::StringFromFormat("mutex-%08x", Core::g_app_core->GetReg(14));
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(mutex)));
LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X",
@@ -646,6 +647,7 @@ static ResultCode CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max
using Kernel::Semaphore;
CASCADE_RESULT(SharedPtr<Semaphore> semaphore, Semaphore::Create(initial_count, max_count));
+ semaphore->name = Common::StringFromFormat("semaphore-%08x", Core::g_app_core->GetReg(14));
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(semaphore)));
LOG_TRACE(Kernel_SVC, "called initial_count=%d, max_count=%d, created handle=0x%08X",
@@ -702,6 +704,7 @@ static ResultCode CreateEvent(Handle* out_handle, u32 reset_type) {
using Kernel::Event;
SharedPtr<Event> evt = Event::Create(static_cast<Kernel::ResetType>(reset_type));
+ evt->name = Common::StringFromFormat("event-%08x", Core::g_app_core->GetReg(14));
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(evt)));
LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", reset_type,
@@ -748,6 +751,7 @@ static ResultCode CreateTimer(Handle* out_handle, u32 reset_type) {
using Kernel::Timer;
SharedPtr<Timer> timer = Timer::Create(static_cast<Kernel::ResetType>(reset_type));
+ timer->name = Common::StringFromFormat("timer-%08x", Core::g_app_core->GetReg(14));
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(timer)));
LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", reset_type,