mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	implement wait tree widget
This commit is contained in:
		
							parent
							
								
									c59bdecd23
								
							
						
					
					
						commit
						f69a543110
					
				
					 9 changed files with 636 additions and 0 deletions
				
			
		|  | @ -15,6 +15,7 @@ set(SRCS | ||||||
|             debugger/profiler.cpp |             debugger/profiler.cpp | ||||||
|             debugger/ramview.cpp |             debugger/ramview.cpp | ||||||
|             debugger/registers.cpp |             debugger/registers.cpp | ||||||
|  |             debugger/wait_tree.cpp | ||||||
|             util/spinbox.cpp |             util/spinbox.cpp | ||||||
|             util/util.cpp |             util/util.cpp | ||||||
|             bootmanager.cpp |             bootmanager.cpp | ||||||
|  | @ -48,6 +49,7 @@ set(HEADERS | ||||||
|             debugger/profiler.h |             debugger/profiler.h | ||||||
|             debugger/ramview.h |             debugger/ramview.h | ||||||
|             debugger/registers.h |             debugger/registers.h | ||||||
|  |             debugger/wait_tree.h | ||||||
|             util/spinbox.h |             util/spinbox.h | ||||||
|             util/util.h |             util/util.h | ||||||
|             bootmanager.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/profiler.h" | ||||||
| #include "citra_qt/debugger/ramview.h" | #include "citra_qt/debugger/ramview.h" | ||||||
| #include "citra_qt/debugger/registers.h" | #include "citra_qt/debugger/registers.h" | ||||||
|  | #include "citra_qt/debugger/wait_tree.h" | ||||||
| #include "citra_qt/game_list.h" | #include "citra_qt/game_list.h" | ||||||
| #include "citra_qt/hotkeys.h" | #include "citra_qt/hotkeys.h" | ||||||
| #include "citra_qt/main.h" | #include "citra_qt/main.h" | ||||||
|  | @ -104,6 +105,10 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { | ||||||
|     connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, |     connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, | ||||||
|             SLOT(OnCreateGraphicsSurfaceViewer())); |             SLOT(OnCreateGraphicsSurfaceViewer())); | ||||||
| 
 | 
 | ||||||
|  |     waitTreeWidget = new WaitTreeWidget(this); | ||||||
|  |     addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); | ||||||
|  |     waitTreeWidget->hide(); | ||||||
|  | 
 | ||||||
|     QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); |     QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); | ||||||
|     debug_menu->addAction(graphicsSurfaceViewerAction); |     debug_menu->addAction(graphicsSurfaceViewerAction); | ||||||
|     debug_menu->addSeparator(); |     debug_menu->addSeparator(); | ||||||
|  | @ -119,6 +124,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { | ||||||
|     debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); |     debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | ||||||
|     debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); |     debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); | ||||||
|     debug_menu->addAction(graphicsTracingWidget->toggleViewAction()); |     debug_menu->addAction(graphicsTracingWidget->toggleViewAction()); | ||||||
|  |     debug_menu->addAction(waitTreeWidget->toggleViewAction()); | ||||||
| 
 | 
 | ||||||
|     // Set default UI state
 |     // Set default UI state
 | ||||||
|     // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
 |     // 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, |     connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget, | ||||||
|             SLOT(OnEmulationStarting(EmuThread*))); |             SLOT(OnEmulationStarting(EmuThread*))); | ||||||
|     connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping())); |     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
 |     // Setup hotkeys
 | ||||||
|     RegisterHotkey("Main Window", "Load File", QKeySequence::Open); |     RegisterHotkey("Main Window", "Load File", QKeySequence::Open); | ||||||
|  | @ -344,12 +353,16 @@ void GMainWindow::BootGame(const std::string& filename) { | ||||||
|             SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); |             SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); | ||||||
|     connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget, |     connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget, | ||||||
|             SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); |             SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); | ||||||
|  |     connect(emu_thread.get(), SIGNAL(DebugModeEntered()), waitTreeWidget, | ||||||
|  |             SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); | ||||||
|     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), |     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), | ||||||
|             Qt::BlockingQueuedConnection); |             Qt::BlockingQueuedConnection); | ||||||
|     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), |     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), | ||||||
|             Qt::BlockingQueuedConnection); |             Qt::BlockingQueuedConnection); | ||||||
|     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), |     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), | ||||||
|             Qt::BlockingQueuedConnection); |             Qt::BlockingQueuedConnection); | ||||||
|  |     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), waitTreeWidget, SLOT(OnDebugModeLeft()), | ||||||
|  |             Qt::BlockingQueuedConnection); | ||||||
| 
 | 
 | ||||||
|     // Update the GUI
 |     // Update the GUI
 | ||||||
|     registersWidget->OnDebugModeEntered(); |     registersWidget->OnDebugModeEntered(); | ||||||
|  |  | ||||||
|  | @ -21,6 +21,7 @@ class RegistersWidget; | ||||||
| class CallstackWidget; | class CallstackWidget; | ||||||
| class GPUCommandStreamWidget; | class GPUCommandStreamWidget; | ||||||
| class GPUCommandListWidget; | class GPUCommandListWidget; | ||||||
|  | class WaitTreeWidget; | ||||||
| 
 | 
 | ||||||
| class GMainWindow : public QMainWindow { | class GMainWindow : public QMainWindow { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
|  | @ -128,6 +129,7 @@ private: | ||||||
|     CallstackWidget* callstackWidget; |     CallstackWidget* callstackWidget; | ||||||
|     GPUCommandStreamWidget* graphicsWidget; |     GPUCommandStreamWidget* graphicsWidget; | ||||||
|     GPUCommandListWidget* graphicsCommandsWidget; |     GPUCommandListWidget* graphicsCommandsWidget; | ||||||
|  |     WaitTreeWidget* waitTreeWidget; | ||||||
| 
 | 
 | ||||||
|     QAction* actions_recent_files[max_recent_files_item]; |     QAction* actions_recent_files[max_recent_files_item]; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -40,6 +40,10 @@ void WaitObject::WakeupAllWaitingThreads() { | ||||||
|     HLE::Reschedule(__func__); |     HLE::Reschedule(__func__); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const { | ||||||
|  |     return waiting_threads; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| HandleTable::HandleTable() { | HandleTable::HandleTable() { | ||||||
|     next_generation = 1; |     next_generation = 1; | ||||||
|     Clear(); |     Clear(); | ||||||
|  |  | ||||||
|  | @ -149,6 +149,9 @@ public: | ||||||
|     /// Wake up all threads waiting on this object
 |     /// Wake up all threads waiting on this object
 | ||||||
|     void WakeupAllWaitingThreads(); |     void WakeupAllWaitingThreads(); | ||||||
| 
 | 
 | ||||||
|  |     /// Get a const reference to the waiting threads list for debug use
 | ||||||
|  |     const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const; | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     /// Threads waiting for this object to become available
 |     /// Threads waiting for this object to become available
 | ||||||
|     std::vector<SharedPtr<Thread>> waiting_threads; |     std::vector<SharedPtr<Thread>> waiting_threads; | ||||||
|  |  | ||||||
|  | @ -665,4 +665,8 @@ void ThreadingShutdown() { | ||||||
|     ready_queue.clear(); |     ready_queue.clear(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | const std::vector<SharedPtr<Thread>>& GetThreadList() { | ||||||
|  |     return thread_list; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace
 | } // namespace
 | ||||||
|  |  | ||||||
|  | @ -236,4 +236,9 @@ void ThreadingInit(); | ||||||
|  */ |  */ | ||||||
| void ThreadingShutdown(); | void ThreadingShutdown(); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get a const reference to the thread list for debug use | ||||||
|  |  */ | ||||||
|  | const std::vector<SharedPtr<Thread>>& GetThreadList(); | ||||||
|  | 
 | ||||||
| } // namespace
 | } // namespace
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue