mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Merge pull request #1652 from wwylele/kernal-tool
Debugger: implement wait tree widget
This commit is contained in:
		
						commit
						09c3e444d4
					
				
					 12 changed files with 646 additions and 7 deletions
				
			
		|  | @ -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 | ||||
|  |  | |||
							
								
								
									
										417
									
								
								src/citra_qt/debugger/wait_tree.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										417
									
								
								src/citra_qt/debugger/wait_tree.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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); | ||||
| } | ||||
							
								
								
									
										186
									
								
								src/citra_qt/debugger/wait_tree.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								src/citra_qt/debugger/wait_tree.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
| }; | ||||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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]; | ||||
| }; | ||||
|  |  | |||
|  | @ -9,12 +9,6 @@ | |||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| enum class ResetType { | ||||
|     OneShot, | ||||
|     Sticky, | ||||
|     Pulse, | ||||
| }; | ||||
| 
 | ||||
| class Event final : public WaitObject { | ||||
| public: | ||||
|     /**
 | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -665,4 +665,8 @@ void ThreadingShutdown() { | |||
|     ready_queue.clear(); | ||||
| } | ||||
| 
 | ||||
| const std::vector<SharedPtr<Thread>>& GetThreadList() { | ||||
|     return thread_list; | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue