| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| // Copyright 2020 Dolphin Emulator Project | ||
| // Licensed under GPLv2+ | ||
| // Refer to the license.txt file included. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <array> | ||
| #include <string> | ||
| #include <type_traits> | ||
|
|
||
| #include "Common/CommonTypes.h" | ||
| #include "Common/Debug/Threads.h" | ||
|
|
||
| namespace Common::Debug | ||
| { | ||
| template <class C> | ||
| struct OSQueue | ||
| { | ||
| u32 head; | ||
| u32 tail; | ||
| }; | ||
| template <class C> | ||
| struct OSLink | ||
| { | ||
| u32 next; | ||
| u32 prev; | ||
| }; | ||
|
|
||
| struct OSMutex; | ||
| struct OSThread; | ||
|
|
||
| using OSThreadQueue = OSQueue<OSThread>; | ||
| using OSThreadLink = OSLink<OSThread>; | ||
|
|
||
| using OSMutexQueue = OSQueue<OSMutex>; | ||
| using OSMutexLink = OSLink<OSMutex>; | ||
|
|
||
| struct OSContext | ||
| { | ||
| enum class State : u16 | ||
| { | ||
| HasFPU = 1, | ||
| HasException = 2, | ||
| }; | ||
| std::array<u32, 32> gpr; | ||
| u32 cr; | ||
| u32 lr; | ||
| u32 ctr; | ||
| u32 xer; | ||
| std::array<double, 32> fpr; | ||
| u64 fpscr; | ||
| u32 srr0; | ||
| u32 srr1; | ||
| u16 dummy; | ||
| State state; | ||
| std::array<u32, 8> gqr; | ||
| u32 psf_padding; | ||
| std::array<double, 32> psf; | ||
|
|
||
| void Read(u32 addr); | ||
| }; | ||
|
|
||
| static_assert(std::is_trivially_copyable_v<OSContext>); | ||
| static_assert(std::is_standard_layout_v<OSContext>); | ||
| static_assert(offsetof(OSContext, cr) == 0x80); | ||
| static_assert(offsetof(OSContext, fpscr) == 0x190); | ||
| static_assert(offsetof(OSContext, gqr) == 0x1a4); | ||
| static_assert(offsetof(OSContext, psf) == 0x1c8); | ||
|
|
||
| struct OSThread | ||
| { | ||
| OSContext context; | ||
|
|
||
| u16 state; // Thread state (ready, running, waiting, moribund) | ||
| u16 is_detached; // Is thread detached | ||
| s32 suspend; // Suspended if greater than zero | ||
| s32 effective_priority; // Effective priority | ||
| s32 base_priority; // Base priority | ||
| u32 exit_code_addr; // Exit value address | ||
|
|
||
| u32 queue_addr; // Address of the queue the thread is on | ||
| OSThreadLink queue_link; // Used to traverse the thread queue | ||
| // OSSleepThread uses it to insert the current thread at the end of the thread queue | ||
|
|
||
| OSThreadQueue join_queue; // Threads waiting to be joined | ||
|
|
||
| u32 mutex_addr; // Mutex waiting | ||
| OSMutexQueue mutex_queue; // Mutex owned | ||
|
|
||
| OSThreadLink thread_link; // Link containing all active threads | ||
|
|
||
| // The STACK_MAGIC is written at stack_end | ||
| u32 stack_addr; | ||
| u32 stack_end; | ||
|
|
||
| s32 error; // errno value | ||
| std::array<u32, 2> specific; // Pointers to data (can be used to store thread names) | ||
|
|
||
| static constexpr u32 STACK_MAGIC = 0xDEADBABE; | ||
| void Read(u32 addr); | ||
| bool IsValid() const; | ||
| }; | ||
|
|
||
| static_assert(std::is_trivially_copyable_v<OSThread>); | ||
| static_assert(std::is_standard_layout_v<OSThread>); | ||
| static_assert(offsetof(OSThread, state) == 0x2c8); | ||
| static_assert(offsetof(OSThread, mutex_addr) == 0x2f0); | ||
| static_assert(offsetof(OSThread, stack_addr) == 0x304); | ||
| static_assert(offsetof(OSThread, specific) == 0x310); | ||
|
|
||
| struct OSMutex | ||
| { | ||
| OSThreadQueue thread_queue; // Threads waiting to own the mutex | ||
| u32 owner_addr; // Thread owning the mutex | ||
| s32 lock_count; // Mutex lock count | ||
| OSMutexLink link; // Used to traverse the thread's mutex queue | ||
| // OSLockMutex uses it to insert the acquired mutex at the end of the queue | ||
|
|
||
| void Read(u32 addr); | ||
| }; | ||
|
|
||
| static_assert(std::is_trivially_copyable_v<OSMutex>); | ||
| static_assert(std::is_standard_layout_v<OSMutex>); | ||
| static_assert(offsetof(OSMutex, owner_addr) == 0x8); | ||
| static_assert(offsetof(OSMutex, link) == 0x10); | ||
|
|
||
| class OSThreadView : public Common::Debug::ThreadView | ||
| { | ||
| public: | ||
| explicit OSThreadView(u32 addr); | ||
| ~OSThreadView() = default; | ||
|
|
||
| const OSThread& Data() const; | ||
|
|
||
| PartialContext GetContext() const override; | ||
| u32 GetAddress() const override; | ||
| u16 GetState() const override; | ||
| bool IsSuspended() const override; | ||
| bool IsDetached() const override; | ||
| s32 GetBasePriority() const override; | ||
| s32 GetEffectivePriority() const override; | ||
| u32 GetStackStart() const override; | ||
| u32 GetStackEnd() const override; | ||
| std::size_t GetStackSize() const override; | ||
| s32 GetErrno() const override; | ||
| std::string GetSpecific() const override; | ||
| bool IsValid() const override; | ||
|
|
||
| private: | ||
| u32 m_address = 0; | ||
| OSThread m_thread; | ||
| }; | ||
|
|
||
| } // namespace Common::Debug |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| // Copyright 2020 Dolphin Emulator Project | ||
| // Licensed under GPLv2+ | ||
| // Refer to the license.txt file included. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <array> | ||
| #include <memory> | ||
| #include <optional> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| #include "Common/CommonTypes.h" | ||
|
|
||
| namespace Common::Debug | ||
| { | ||
| struct PartialContext | ||
| { | ||
| std::optional<std::array<u32, 32>> gpr; | ||
| std::optional<u32> cr; | ||
| std::optional<u32> lr; | ||
| std::optional<u32> ctr; | ||
| std::optional<u32> xer; | ||
| std::optional<std::array<double, 32>> fpr; | ||
| std::optional<u64> fpscr; | ||
| std::optional<u32> srr0; | ||
| std::optional<u32> srr1; | ||
| std::optional<u16> dummy; | ||
| std::optional<u16> state; | ||
| std::optional<std::array<u32, 8>> gqr; | ||
| std::optional<std::array<double, 32>> psf; | ||
| }; | ||
|
|
||
| class ThreadView | ||
| { | ||
| public: | ||
| virtual ~ThreadView() = default; | ||
|
|
||
| enum class API | ||
| { | ||
| OSThread, // Nintendo SDK thread | ||
| LWPThread, // devkitPro libogc thread | ||
| }; | ||
|
|
||
| virtual PartialContext GetContext() const = 0; | ||
| virtual u32 GetAddress() const = 0; | ||
| virtual u16 GetState() const = 0; | ||
| virtual bool IsSuspended() const = 0; | ||
| virtual bool IsDetached() const = 0; | ||
| virtual s32 GetBasePriority() const = 0; | ||
| virtual s32 GetEffectivePriority() const = 0; | ||
| virtual u32 GetStackStart() const = 0; | ||
| virtual u32 GetStackEnd() const = 0; | ||
| virtual std::size_t GetStackSize() const = 0; | ||
| virtual s32 GetErrno() const = 0; | ||
| // Implementation specific, used to store arbitrary data | ||
| virtual std::string GetSpecific() const = 0; | ||
| virtual bool IsValid() const = 0; | ||
| }; | ||
|
|
||
| using Threads = std::vector<std::unique_ptr<ThreadView>>; | ||
|
|
||
| } // namespace Common::Debug |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| // Copyright 2020 Dolphin Emulator Project | ||
| // Licensed under GPLv2+ | ||
| // Refer to the license.txt file included. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <QDockWidget> | ||
|
|
||
| #include "Common/CommonTypes.h" | ||
| #include "Common/Debug/Threads.h" | ||
|
|
||
| class QCloseEvent; | ||
| class QGroupBox; | ||
| class QLineEdit; | ||
| class QShowEvent; | ||
| class QTableWidget; | ||
|
|
||
| class ThreadWidget : public QDockWidget | ||
| { | ||
| Q_OBJECT | ||
| public: | ||
| explicit ThreadWidget(QWidget* parent = nullptr); | ||
| ~ThreadWidget(); | ||
|
|
||
| signals: | ||
| void RequestBreakpoint(u32 addr); | ||
| void RequestMemoryBreakpoint(u32 addr); | ||
| void RequestWatch(QString name, u32 addr); | ||
| void RequestViewInCode(u32 addr); | ||
| void RequestViewInMemory(u32 addr); | ||
|
|
||
| protected: | ||
| void closeEvent(QCloseEvent*) override; | ||
| void showEvent(QShowEvent* event) override; | ||
|
|
||
| private: | ||
| void CreateWidgets(); | ||
| void ConnectWidgets(); | ||
|
|
||
| QLineEdit* CreateLineEdit() const; | ||
| QGroupBox* CreateContextGroup(); | ||
| QGroupBox* CreateActiveThreadQueueGroup(); | ||
| QGroupBox* CreateThreadGroup(); | ||
| QGroupBox* CreateThreadContextGroup(); | ||
| QGroupBox* CreateThreadCallstackGroup(); | ||
|
|
||
| void ShowContextMenu(QTableWidget* table); | ||
|
|
||
| void Update(); | ||
| void UpdateThreadContext(const Common::Debug::PartialContext& context); | ||
| void UpdateThreadCallstack(const Common::Debug::PartialContext& context); | ||
| void OnSelectionChanged(int row); | ||
|
|
||
| QGroupBox* m_state; | ||
| QLineEdit* m_current_context; | ||
| QLineEdit* m_current_thread; | ||
| QLineEdit* m_default_thread; | ||
| QLineEdit* m_queue_head; | ||
| QLineEdit* m_queue_tail; | ||
| QTableWidget* m_thread_table; | ||
| QTableWidget* m_context_table; | ||
| QTableWidget* m_callstack_table; | ||
| Common::Debug::Threads m_threads; | ||
| }; |