summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-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
15 debugger/profiler.cpp 15 debugger/profiler.cpp
16 debugger/ramview.cpp 16 debugger/ramview.cpp
17 debugger/registers.cpp 17 debugger/registers.cpp
18 debugger/wait_tree.cpp
18 util/spinbox.cpp 19 util/spinbox.cpp
19 util/util.cpp 20 util/util.cpp
20 bootmanager.cpp 21 bootmanager.cpp
@@ -48,6 +49,7 @@ set(HEADERS
48 debugger/profiler.h 49 debugger/profiler.h
49 debugger/ramview.h 50 debugger/ramview.h
50 debugger/registers.h 51 debugger/registers.h
52 debugger/wait_tree.h
51 util/spinbox.h 53 util/spinbox.h
52 util/util.h 54 util/util.h
53 bootmanager.h 55 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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "citra_qt/debugger/wait_tree.h"
6#include "citra_qt/util/util.h"
7
8#include "core/hle/kernel/event.h"
9#include "core/hle/kernel/mutex.h"
10#include "core/hle/kernel/semaphore.h"
11#include "core/hle/kernel/session.h"
12#include "core/hle/kernel/thread.h"
13#include "core/hle/kernel/timer.h"
14
15WaitTreeItem::~WaitTreeItem() {}
16
17QColor WaitTreeItem::GetColor() const {
18 return QColor(Qt::GlobalColor::black);
19}
20
21std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeItem::GetChildren() const {
22 return {};
23}
24
25void WaitTreeItem::Expand() {
26 if (IsExpandable() && !expanded) {
27 children = GetChildren();
28 for (std::size_t i = 0; i < children.size(); ++i) {
29 children[i]->parent = this;
30 children[i]->row = i;
31 }
32 expanded = true;
33 }
34}
35
36WaitTreeItem* WaitTreeItem::Parent() const {
37 return parent;
38}
39
40const std::vector<std::unique_ptr<WaitTreeItem>>& WaitTreeItem::Children() const {
41 return children;
42}
43
44bool WaitTreeItem::IsExpandable() const {
45 return false;
46}
47
48std::size_t WaitTreeItem::Row() const {
49 return row;
50}
51
52std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() {
53 const auto& threads = Kernel::GetThreadList();
54 std::vector<std::unique_ptr<WaitTreeThread>> item_list;
55 item_list.reserve(threads.size());
56 for (std::size_t i = 0; i < threads.size(); ++i) {
57 item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i]));
58 item_list.back()->row = i;
59 }
60 return item_list;
61}
62
63WaitTreeText::WaitTreeText(const QString& t) : text(t) {}
64
65QString WaitTreeText::GetText() const {
66 return text;
67}
68
69WaitTreeWaitObject::WaitTreeWaitObject(const Kernel::WaitObject& o) : object(o) {}
70
71bool WaitTreeExpandableItem::IsExpandable() const {
72 return true;
73}
74
75QString WaitTreeWaitObject::GetText() const {
76 return tr("[%1]%2 %3")
77 .arg(object.GetObjectId())
78 .arg(QString::fromStdString(object.GetTypeName()),
79 QString::fromStdString(object.GetName()));
80}
81
82std::unique_ptr<WaitTreeWaitObject> WaitTreeWaitObject::make(const Kernel::WaitObject& object) {
83 switch (object.GetHandleType()) {
84 case Kernel::HandleType::Event:
85 return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::Event&>(object));
86 case Kernel::HandleType::Mutex:
87 return std::make_unique<WaitTreeMutex>(static_cast<const Kernel::Mutex&>(object));
88 case Kernel::HandleType::Semaphore:
89 return std::make_unique<WaitTreeSemaphore>(static_cast<const Kernel::Semaphore&>(object));
90 case Kernel::HandleType::Timer:
91 return std::make_unique<WaitTreeTimer>(static_cast<const Kernel::Timer&>(object));
92 case Kernel::HandleType::Thread:
93 return std::make_unique<WaitTreeThread>(static_cast<const Kernel::Thread&>(object));
94 default:
95 return std::make_unique<WaitTreeWaitObject>(object);
96 }
97}
98
99std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeWaitObject::GetChildren() const {
100 std::vector<std::unique_ptr<WaitTreeItem>> list;
101
102 const auto& threads = object.GetWaitingThreads();
103 if (threads.empty()) {
104 list.push_back(std::make_unique<WaitTreeText>(tr("waited by no thread")));
105 } else {
106 list.push_back(std::make_unique<WaitTreeThreadList>(threads));
107 }
108 return list;
109}
110
111QString WaitTreeWaitObject::GetResetTypeQString(Kernel::ResetType reset_type) {
112 switch (reset_type) {
113 case Kernel::ResetType::OneShot:
114 return tr("one shot");
115 case Kernel::ResetType::Sticky:
116 return tr("sticky");
117 case Kernel::ResetType::Pulse:
118 return tr("pulse");
119 }
120}
121
122WaitTreeObjectList::WaitTreeObjectList(
123 const std::vector<Kernel::SharedPtr<Kernel::WaitObject>>& list, bool w_all)
124 : object_list(list), wait_all(w_all) {}
125
126QString WaitTreeObjectList::GetText() const {
127 if (wait_all)
128 return tr("waiting for all objects");
129 return tr("waiting for one of the following objects");
130}
131
132std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeObjectList::GetChildren() const {
133 std::vector<std::unique_ptr<WaitTreeItem>> list(object_list.size());
134 std::transform(object_list.begin(), object_list.end(), list.begin(),
135 [](const auto& t) { return WaitTreeWaitObject::make(*t); });
136 return list;
137}
138
139WaitTreeThread::WaitTreeThread(const Kernel::Thread& thread) : WaitTreeWaitObject(thread) {}
140
141QString WaitTreeThread::GetText() const {
142 const auto& thread = static_cast<const Kernel::Thread&>(object);
143 QString status;
144 switch (thread.status) {
145 case THREADSTATUS_RUNNING:
146 status = tr("running");
147 break;
148 case THREADSTATUS_READY:
149 status = tr("ready");
150 break;
151 case THREADSTATUS_WAIT_ARB:
152 status = tr("waiting for address 0x%1").arg(thread.wait_address, 8, 16, QLatin1Char('0'));
153 break;
154 case THREADSTATUS_WAIT_SLEEP:
155 status = tr("sleeping");
156 break;
157 case THREADSTATUS_WAIT_SYNCH:
158 status = tr("waiting for objects");
159 break;
160 case THREADSTATUS_DORMANT:
161 status = tr("dormant");
162 break;
163 case THREADSTATUS_DEAD:
164 status = tr("dead");
165 break;
166 }
167 QString pc_info = tr(" PC = 0x%1 LR = 0x%2")
168 .arg(thread.context.pc, 8, 16, QLatin1Char('0'))
169 .arg(thread.context.lr, 8, 16, QLatin1Char('0'));
170 return WaitTreeWaitObject::GetText() + pc_info + " (" + status + ") ";
171}
172
173QColor WaitTreeThread::GetColor() const {
174 const auto& thread = static_cast<const Kernel::Thread&>(object);
175 switch (thread.status) {
176 case THREADSTATUS_RUNNING:
177 return QColor(Qt::GlobalColor::darkGreen);
178 case THREADSTATUS_READY:
179 return QColor(Qt::GlobalColor::darkBlue);
180 case THREADSTATUS_WAIT_ARB:
181 return QColor(Qt::GlobalColor::darkRed);
182 case THREADSTATUS_WAIT_SLEEP:
183 return QColor(Qt::GlobalColor::darkYellow);
184 case THREADSTATUS_WAIT_SYNCH:
185 return QColor(Qt::GlobalColor::red);
186 case THREADSTATUS_DORMANT:
187 return QColor(Qt::GlobalColor::darkCyan);
188 case THREADSTATUS_DEAD:
189 return QColor(Qt::GlobalColor::gray);
190 default:
191 return WaitTreeItem::GetColor();
192 }
193}
194
195std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
196 std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
197
198 const auto& thread = static_cast<const Kernel::Thread&>(object);
199
200 QString processor;
201 switch (thread.processor_id) {
202 case ThreadProcessorId::THREADPROCESSORID_DEFAULT:
203 processor = tr("default");
204 break;
205 case ThreadProcessorId::THREADPROCESSORID_ALL:
206 processor = tr("all");
207 break;
208 case ThreadProcessorId::THREADPROCESSORID_0:
209 processor = tr("AppCore");
210 break;
211 case ThreadProcessorId::THREADPROCESSORID_1:
212 processor = tr("SysCore");
213 break;
214 default:
215 processor = tr("Unknown processor %1").arg(thread.processor_id);
216 break;
217 }
218
219 list.push_back(std::make_unique<WaitTreeText>(tr("processor = %1").arg(processor)));
220 list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadId())));
221 list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)")
222 .arg(thread.current_priority)
223 .arg(thread.nominal_priority)));
224 list.push_back(std::make_unique<WaitTreeText>(
225 tr("last running ticks = %1").arg(thread.last_running_ticks)));
226
227 if (thread.held_mutexes.empty()) {
228 list.push_back(std::make_unique<WaitTreeText>(tr("not holding mutex")));
229 } else {
230 list.push_back(std::make_unique<WaitTreeMutexList>(thread.held_mutexes));
231 }
232 if (thread.status == THREADSTATUS_WAIT_SYNCH) {
233 list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, thread.wait_all));
234 }
235
236 return list;
237}
238
239WaitTreeEvent::WaitTreeEvent(const Kernel::Event& object) : WaitTreeWaitObject(object) {}
240
241std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
242 std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
243
244 list.push_back(std::make_unique<WaitTreeText>(
245 tr("reset type = %1")
246 .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).reset_type))));
247 return list;
248}
249
250WaitTreeMutex::WaitTreeMutex(const Kernel::Mutex& object) : WaitTreeWaitObject(object) {}
251
252std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutex::GetChildren() const {
253 std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
254
255 const auto& mutex = static_cast<const Kernel::Mutex&>(object);
256 if (mutex.lock_count) {
257 list.push_back(
258 std::make_unique<WaitTreeText>(tr("locked %1 times by thread:").arg(mutex.lock_count)));
259 list.push_back(std::make_unique<WaitTreeThread>(*mutex.holding_thread));
260 } else {
261 list.push_back(std::make_unique<WaitTreeText>(tr("free")));
262 }
263 return list;
264}
265
266WaitTreeSemaphore::WaitTreeSemaphore(const Kernel::Semaphore& object)
267 : WaitTreeWaitObject(object) {}
268
269std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeSemaphore::GetChildren() const {
270 std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
271
272 const auto& semaphore = static_cast<const Kernel::Semaphore&>(object);
273 list.push_back(
274 std::make_unique<WaitTreeText>(tr("available count = %1").arg(semaphore.available_count)));
275 list.push_back(std::make_unique<WaitTreeText>(tr("max count = %1").arg(semaphore.max_count)));
276 return list;
277}
278
279WaitTreeTimer::WaitTreeTimer(const Kernel::Timer& object) : WaitTreeWaitObject(object) {}
280
281std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeTimer::GetChildren() const {
282 std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
283
284 const auto& timer = static_cast<const Kernel::Timer&>(object);
285
286 list.push_back(std::make_unique<WaitTreeText>(
287 tr("reset type = %1").arg(GetResetTypeQString(timer.reset_type))));
288 list.push_back(
289 std::make_unique<WaitTreeText>(tr("initial delay = %1").arg(timer.initial_delay)));
290 list.push_back(
291 std::make_unique<WaitTreeText>(tr("interval delay = %1").arg(timer.interval_delay)));
292 return list;
293}
294
295WaitTreeMutexList::WaitTreeMutexList(
296 const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& list)
297 : mutex_list(list) {}
298
299QString WaitTreeMutexList::GetText() const {
300 return tr("holding mutexes");
301}
302
303std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexList::GetChildren() const {
304 std::vector<std::unique_ptr<WaitTreeItem>> list(mutex_list.size());
305 std::transform(mutex_list.begin(), mutex_list.end(), list.begin(),
306 [](const auto& t) { return std::make_unique<WaitTreeMutex>(*t); });
307 return list;
308}
309
310WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::SharedPtr<Kernel::Thread>>& list)
311 : thread_list(list) {}
312
313QString WaitTreeThreadList::GetText() const {
314 return tr("waited by thread");
315}
316
317std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThreadList::GetChildren() const {
318 std::vector<std::unique_ptr<WaitTreeItem>> list(thread_list.size());
319 std::transform(thread_list.begin(), thread_list.end(), list.begin(),
320 [](const auto& t) { return std::make_unique<WaitTreeThread>(*t); });
321 return list;
322}
323
324WaitTreeModel::WaitTreeModel(QObject* parent) : QAbstractItemModel(parent) {}
325
326QModelIndex WaitTreeModel::index(int row, int column, const QModelIndex& parent) const {
327 if (!hasIndex(row, column, parent))
328 return {};
329
330 if (parent.isValid()) {
331 WaitTreeItem* parent_item = static_cast<WaitTreeItem*>(parent.internalPointer());
332 parent_item->Expand();
333 return createIndex(row, column, parent_item->Children()[row].get());
334 }
335
336 return createIndex(row, column, thread_items[row].get());
337}
338
339QModelIndex WaitTreeModel::parent(const QModelIndex& index) const {
340 if (!index.isValid())
341 return {};
342
343 WaitTreeItem* parent_item = static_cast<WaitTreeItem*>(index.internalPointer())->Parent();
344 if (!parent_item) {
345 return QModelIndex();
346 }
347 return createIndex(static_cast<int>(parent_item->Row()), 0, parent_item);
348}
349
350int WaitTreeModel::rowCount(const QModelIndex& parent) const {
351 if (!parent.isValid())
352 return static_cast<int>(thread_items.size());
353
354 WaitTreeItem* parent_item = static_cast<WaitTreeItem*>(parent.internalPointer());
355 parent_item->Expand();
356 return static_cast<int>(parent_item->Children().size());
357}
358
359int WaitTreeModel::columnCount(const QModelIndex&) const {
360 return 1;
361}
362
363QVariant WaitTreeModel::data(const QModelIndex& index, int role) const {
364 if (!index.isValid())
365 return {};
366
367 switch (role) {
368 case Qt::DisplayRole:
369 return static_cast<WaitTreeItem*>(index.internalPointer())->GetText();
370 case Qt::ForegroundRole:
371 return static_cast<WaitTreeItem*>(index.internalPointer())->GetColor();
372 default:
373 return {};
374 }
375}
376
377void WaitTreeModel::ClearItems() {
378 thread_items.clear();
379}
380
381void WaitTreeModel::InitItems() {
382 thread_items = WaitTreeItem::MakeThreadItemList();
383}
384
385WaitTreeWidget::WaitTreeWidget(QWidget* parent) : QDockWidget(tr("Wait Tree"), parent) {
386 setObjectName("WaitTreeWidget");
387 view = new QTreeView(this);
388 view->setHeaderHidden(true);
389 setWidget(view);
390 setEnabled(false);
391}
392
393void WaitTreeWidget::OnDebugModeEntered() {
394 if (!Core::g_app_core)
395 return;
396 model->InitItems();
397 view->setModel(model);
398 setEnabled(true);
399}
400
401void WaitTreeWidget::OnDebugModeLeft() {
402 setEnabled(false);
403 view->setModel(nullptr);
404 model->ClearItems();
405}
406
407void WaitTreeWidget::OnEmulationStarting(EmuThread* emu_thread) {
408 model = new WaitTreeModel(this);
409 view->setModel(model);
410 setEnabled(false);
411}
412
413void WaitTreeWidget::OnEmulationStopping() {
414 view->setModel(nullptr);
415 delete model;
416 setEnabled(false);
417}
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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <boost/container/flat_set.hpp>
6
7#include <QAbstractItemModel>
8#include <QDockWidget>
9#include <QTreeView>
10
11#include "core/core.h"
12#include "core/hle/kernel/kernel.h"
13
14class EmuThread;
15
16namespace Kernel {
17class WaitObject;
18class Event;
19class Mutex;
20class Semaphore;
21class Session;
22class Thread;
23class Timer;
24}
25
26class WaitTreeThread;
27
28class WaitTreeItem : public QObject {
29 Q_OBJECT
30public:
31 virtual bool IsExpandable() const;
32 virtual std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const;
33 virtual QString GetText() const = 0;
34 virtual QColor GetColor() const;
35 virtual ~WaitTreeItem();
36 void Expand();
37 WaitTreeItem* Parent() const;
38 const std::vector<std::unique_ptr<WaitTreeItem>>& Children() const;
39 std::size_t Row() const;
40 static std::vector<std::unique_ptr<WaitTreeThread>> MakeThreadItemList();
41
42private:
43 std::size_t row;
44 bool expanded = false;
45 WaitTreeItem* parent = nullptr;
46 std::vector<std::unique_ptr<WaitTreeItem>> children;
47};
48
49class WaitTreeText : public WaitTreeItem {
50 Q_OBJECT
51public:
52 WaitTreeText(const QString& text);
53 QString GetText() const override;
54
55private:
56 QString text;
57};
58
59class WaitTreeExpandableItem : public WaitTreeItem {
60 Q_OBJECT
61public:
62 bool IsExpandable() const override;
63};
64
65class WaitTreeWaitObject : public WaitTreeExpandableItem {
66 Q_OBJECT
67public:
68 WaitTreeWaitObject(const Kernel::WaitObject& object);
69 static std::unique_ptr<WaitTreeWaitObject> make(const Kernel::WaitObject& object);
70 QString GetText() const override;
71 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
72
73protected:
74 const Kernel::WaitObject& object;
75
76 static QString GetResetTypeQString(Kernel::ResetType reset_type);
77};
78
79class WaitTreeObjectList : public WaitTreeExpandableItem {
80 Q_OBJECT
81public:
82 WaitTreeObjectList(const std::vector<Kernel::SharedPtr<Kernel::WaitObject>>& list,
83 bool wait_all);
84 QString GetText() const override;
85 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
86
87private:
88 const std::vector<Kernel::SharedPtr<Kernel::WaitObject>>& object_list;
89 bool wait_all;
90};
91
92class WaitTreeThread : public WaitTreeWaitObject {
93 Q_OBJECT
94public:
95 WaitTreeThread(const Kernel::Thread& thread);
96 QString GetText() const override;
97 QColor GetColor() const override;
98 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
99};
100
101class WaitTreeEvent : public WaitTreeWaitObject {
102 Q_OBJECT
103public:
104 WaitTreeEvent(const Kernel::Event& object);
105 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
106};
107
108class WaitTreeMutex : public WaitTreeWaitObject {
109 Q_OBJECT
110public:
111 WaitTreeMutex(const Kernel::Mutex& object);
112 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
113};
114
115class WaitTreeSemaphore : public WaitTreeWaitObject {
116 Q_OBJECT
117public:
118 WaitTreeSemaphore(const Kernel::Semaphore& object);
119 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
120};
121
122class WaitTreeTimer : public WaitTreeWaitObject {
123 Q_OBJECT
124public:
125 WaitTreeTimer(const Kernel::Timer& object);
126 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
127};
128
129class WaitTreeMutexList : public WaitTreeExpandableItem {
130 Q_OBJECT
131public:
132 WaitTreeMutexList(const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& list);
133 QString GetText() const override;
134 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
135
136private:
137 const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& mutex_list;
138};
139
140class WaitTreeThreadList : public WaitTreeExpandableItem {
141 Q_OBJECT
142public:
143 WaitTreeThreadList(const std::vector<Kernel::SharedPtr<Kernel::Thread>>& list);
144 QString GetText() const override;
145 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
146
147private:
148 const std::vector<Kernel::SharedPtr<Kernel::Thread>>& thread_list;
149};
150
151class WaitTreeModel : public QAbstractItemModel {
152 Q_OBJECT
153
154public:
155 WaitTreeModel(QObject* parent = nullptr);
156
157 QVariant data(const QModelIndex& index, int role) const override;
158 QModelIndex index(int row, int column, const QModelIndex& parent) const override;
159 QModelIndex parent(const QModelIndex& index) const override;
160 int rowCount(const QModelIndex& parent) const override;
161 int columnCount(const QModelIndex& parent) const override;
162
163 void ClearItems();
164 void InitItems();
165
166private:
167 std::vector<std::unique_ptr<WaitTreeThread>> thread_items;
168};
169
170class WaitTreeWidget : public QDockWidget {
171 Q_OBJECT
172
173public:
174 WaitTreeWidget(QWidget* parent = nullptr);
175
176public slots:
177 void OnDebugModeEntered();
178 void OnDebugModeLeft();
179
180 void OnEmulationStarting(EmuThread* emu_thread);
181 void OnEmulationStopping();
182
183private:
184 QTreeView* view;
185 WaitTreeModel* model;
186};
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 @@
25#include "citra_qt/debugger/profiler.h" 25#include "citra_qt/debugger/profiler.h"
26#include "citra_qt/debugger/ramview.h" 26#include "citra_qt/debugger/ramview.h"
27#include "citra_qt/debugger/registers.h" 27#include "citra_qt/debugger/registers.h"
28#include "citra_qt/debugger/wait_tree.h"
28#include "citra_qt/game_list.h" 29#include "citra_qt/game_list.h"
29#include "citra_qt/hotkeys.h" 30#include "citra_qt/hotkeys.h"
30#include "citra_qt/main.h" 31#include "citra_qt/main.h"
@@ -104,6 +105,10 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
104 connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, 105 connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this,
105 SLOT(OnCreateGraphicsSurfaceViewer())); 106 SLOT(OnCreateGraphicsSurfaceViewer()));
106 107
108 waitTreeWidget = new WaitTreeWidget(this);
109 addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
110 waitTreeWidget->hide();
111
107 QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); 112 QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
108 debug_menu->addAction(graphicsSurfaceViewerAction); 113 debug_menu->addAction(graphicsSurfaceViewerAction);
109 debug_menu->addSeparator(); 114 debug_menu->addSeparator();
@@ -119,6 +124,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
119 debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); 124 debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
120 debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); 125 debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
121 debug_menu->addAction(graphicsTracingWidget->toggleViewAction()); 126 debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
127 debug_menu->addAction(waitTreeWidget->toggleViewAction());
122 128
123 // Set default UI state 129 // Set default UI state
124 // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half 130 // 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) {
184 connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget, 190 connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget,
185 SLOT(OnEmulationStarting(EmuThread*))); 191 SLOT(OnEmulationStarting(EmuThread*)));
186 connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping())); 192 connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping()));
193 connect(this, SIGNAL(EmulationStarting(EmuThread*)), waitTreeWidget,
194 SLOT(OnEmulationStarting(EmuThread*)));
195 connect(this, SIGNAL(EmulationStopping()), waitTreeWidget, SLOT(OnEmulationStopping()));
187 196
188 // Setup hotkeys 197 // Setup hotkeys
189 RegisterHotkey("Main Window", "Load File", QKeySequence::Open); 198 RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
@@ -345,12 +354,16 @@ void GMainWindow::BootGame(const std::string& filename) {
345 SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); 354 SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
346 connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget, 355 connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget,
347 SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); 356 SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
357 connect(emu_thread.get(), SIGNAL(DebugModeEntered()), waitTreeWidget,
358 SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
348 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), 359 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()),
349 Qt::BlockingQueuedConnection); 360 Qt::BlockingQueuedConnection);
350 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), 361 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()),
351 Qt::BlockingQueuedConnection); 362 Qt::BlockingQueuedConnection);
352 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), 363 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()),
353 Qt::BlockingQueuedConnection); 364 Qt::BlockingQueuedConnection);
365 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), waitTreeWidget, SLOT(OnDebugModeLeft()),
366 Qt::BlockingQueuedConnection);
354 367
355 // Update the GUI 368 // Update the GUI
356 registersWidget->OnDebugModeEntered(); 369 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;
21class CallstackWidget; 21class CallstackWidget;
22class GPUCommandStreamWidget; 22class GPUCommandStreamWidget;
23class GPUCommandListWidget; 23class GPUCommandListWidget;
24class WaitTreeWidget;
24 25
25class GMainWindow : public QMainWindow { 26class GMainWindow : public QMainWindow {
26 Q_OBJECT 27 Q_OBJECT
@@ -128,6 +129,7 @@ private:
128 CallstackWidget* callstackWidget; 129 CallstackWidget* callstackWidget;
129 GPUCommandStreamWidget* graphicsWidget; 130 GPUCommandStreamWidget* graphicsWidget;
130 GPUCommandListWidget* graphicsCommandsWidget; 131 GPUCommandListWidget* graphicsCommandsWidget;
132 WaitTreeWidget* waitTreeWidget;
131 133
132 QAction* actions_recent_files[max_recent_files_item]; 134 QAction* actions_recent_files[max_recent_files_item];
133}; 135};
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 @@
9 9
10namespace Kernel { 10namespace Kernel {
11 11
12enum class ResetType {
13 OneShot,
14 Sticky,
15 Pulse,
16};
17
18class Event final : public WaitObject { 12class Event final : public WaitObject {
19public: 13public:
20 /** 14 /**
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() {
40 HLE::Reschedule(__func__); 40 HLE::Reschedule(__func__);
41} 41}
42 42
43const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const {
44 return waiting_threads;
45}
46
43HandleTable::HandleTable() { 47HandleTable::HandleTable() {
44 next_generation = 1; 48 next_generation = 1;
45 Clear(); 49 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 {
53 DEFAULT_STACK_SIZE = 0x4000, 53 DEFAULT_STACK_SIZE = 0x4000,
54}; 54};
55 55
56enum class ResetType {
57 OneShot,
58 Sticky,
59 Pulse,
60};
61
56class Object : NonCopyable { 62class Object : NonCopyable {
57public: 63public:
58 virtual ~Object() {} 64 virtual ~Object() {}
@@ -149,6 +155,9 @@ public:
149 /// Wake up all threads waiting on this object 155 /// Wake up all threads waiting on this object
150 void WakeupAllWaitingThreads(); 156 void WakeupAllWaitingThreads();
151 157
158 /// Get a const reference to the waiting threads list for debug use
159 const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const;
160
152private: 161private:
153 /// Threads waiting for this object to become available 162 /// Threads waiting for this object to become available
154 std::vector<SharedPtr<Thread>> waiting_threads; 163 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() {
665 ready_queue.clear(); 665 ready_queue.clear();
666} 666}
667 667
668const std::vector<SharedPtr<Thread>>& GetThreadList() {
669 return thread_list;
670}
671
668} // namespace 672} // 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();
236 */ 236 */
237void ThreadingShutdown(); 237void ThreadingShutdown();
238 238
239/**
240 * Get a const reference to the thread list for debug use
241 */
242const std::vector<SharedPtr<Thread>>& GetThreadList();
243
239} // namespace 244} // 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 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hle/kernel/event.h"
9#include "core/hle/kernel/kernel.h" 8#include "core/hle/kernel/kernel.h"
10 9
11namespace Kernel { 10namespace 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) {
576 using Kernel::Mutex; 576 using Kernel::Mutex;
577 577
578 SharedPtr<Mutex> mutex = Mutex::Create(initial_locked != 0); 578 SharedPtr<Mutex> mutex = Mutex::Create(initial_locked != 0);
579 mutex->name = Common::StringFromFormat("mutex-%08x", Core::g_app_core->GetReg(14));
579 CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(mutex))); 580 CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(mutex)));
580 581
581 LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X", 582 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
646 using Kernel::Semaphore; 647 using Kernel::Semaphore;
647 648
648 CASCADE_RESULT(SharedPtr<Semaphore> semaphore, Semaphore::Create(initial_count, max_count)); 649 CASCADE_RESULT(SharedPtr<Semaphore> semaphore, Semaphore::Create(initial_count, max_count));
650 semaphore->name = Common::StringFromFormat("semaphore-%08x", Core::g_app_core->GetReg(14));
649 CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(semaphore))); 651 CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(semaphore)));
650 652
651 LOG_TRACE(Kernel_SVC, "called initial_count=%d, max_count=%d, created handle=0x%08X", 653 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) {
702 using Kernel::Event; 704 using Kernel::Event;
703 705
704 SharedPtr<Event> evt = Event::Create(static_cast<Kernel::ResetType>(reset_type)); 706 SharedPtr<Event> evt = Event::Create(static_cast<Kernel::ResetType>(reset_type));
707 evt->name = Common::StringFromFormat("event-%08x", Core::g_app_core->GetReg(14));
705 CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(evt))); 708 CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(evt)));
706 709
707 LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", reset_type, 710 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) {
748 using Kernel::Timer; 751 using Kernel::Timer;
749 752
750 SharedPtr<Timer> timer = Timer::Create(static_cast<Kernel::ResetType>(reset_type)); 753 SharedPtr<Timer> timer = Timer::Create(static_cast<Kernel::ResetType>(reset_type));
754 timer->name = Common::StringFromFormat("timer-%08x", Core::g_app_core->GetReg(14));
751 CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(timer))); 755 CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(timer)));
752 756
753 LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", reset_type, 757 LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", reset_type,