diff --git a/src/xenia/cpu/ppc/testing/premake5.lua b/src/xenia/cpu/ppc/testing/premake5.lua index 10195369b..04466e384 100644 --- a/src/xenia/cpu/ppc/testing/premake5.lua +++ b/src/xenia/cpu/ppc/testing/premake5.lua @@ -12,9 +12,6 @@ project("xenia-cpu-ppc-tests") "xenia-core", "xenia-cpu", "xenia-cpu-backend-x64", - - -- TODO(benvanik): remove these dependencies. - "xenia-kernel" }) files({ "ppc_testing_main.cc", diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index 3c6341088..2877a79b7 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -28,12 +28,10 @@ #include "xenia/cpu/ppc/ppc_decode_data.h" #include "xenia/cpu/ppc/ppc_frontend.h" #include "xenia/cpu/stack_walker.h" +#include "xenia/cpu/thread.h" #include "xenia/cpu/thread_state.h" #include "xenia/cpu/xex_module.h" -// HACK(benvanik): XThread has too much stuff :/ -#include "xenia/kernel/xthread.h" - // TODO(benvanik): based on compiler support #include "xenia/cpu/backend/x64/x64_backend.h" @@ -49,6 +47,10 @@ DEFINE_string(trace_function_data_path, "", "File to write trace data to."); DEFINE_bool(break_on_start, false, "Break into the debugger on startup."); namespace xe { +namespace kernel { +class XThread; +} // namespace kernel + namespace cpu { using xe::cpu::ppc::PPCOpcode; @@ -491,8 +493,7 @@ void Processor::OnFunctionDefined(Function* function) { } void Processor::OnThreadCreated(uint32_t thread_handle, - ThreadState* thread_state, - kernel::XThread* thread) { + ThreadState* thread_state, Thread* thread) { auto global_lock = global_critical_region_.Acquire(); auto thread_info = std::make_unique(); thread_info->thread_handle = thread_handle; @@ -683,7 +684,7 @@ bool Processor::OnThreadBreakpointHit(Exception* ex) { debug_listener_->OnExecutionPaused(); } - thread_info->thread->Suspend(nullptr); + thread_info->thread->thread()->Suspend(); // Apply thread context changes. // TODO(benvanik): apply to all threads? @@ -711,7 +712,7 @@ bool Processor::OnUnhandledException(Exception* ex) { } // If this isn't a managed thread, fail - let VS handle it for now. - if (!XThread::IsInThread()) { + if (!Thread::IsInThread()) { return false; } @@ -720,7 +721,7 @@ bool Processor::OnUnhandledException(Exception* ex) { // Suspend all guest threads (but this one). SuspendAllThreads(); - UpdateThreadExecutionStates(XThread::GetCurrentThreadId(), + UpdateThreadExecutionStates(Thread::GetCurrentThreadId(), ex->thread_context()); // Stop and notify the listener. @@ -733,7 +734,7 @@ bool Processor::OnUnhandledException(Exception* ex) { debug_listener_->OnExecutionPaused(); // Suspend self. - XThread::GetCurrentThread()->Suspend(nullptr); + Thread::GetCurrentThread()->thread()->Suspend(); return true; } @@ -757,8 +758,8 @@ bool Processor::SuspendAllThreads() { thread_info->state == ThreadDebugInfo::State::kExited) { // Thread is dead and cannot be suspended - ignore. continue; - } else if (XThread::IsInThread() && - thread_info->thread_id == XThread::GetCurrentThreadId()) { + } else if (Thread::IsInThread() && + thread_info->thread_id == Thread::GetCurrentThreadId()) { // Can't suspend ourselves. continue; } @@ -767,7 +768,7 @@ bool Processor::SuspendAllThreads() { // Thread is a host thread, and we aren't suspending those (for now). continue; } - bool did_suspend = XSUCCEEDED(thread->Suspend(nullptr)); + bool did_suspend = thread->thread()->Suspend(nullptr); assert_true(did_suspend); thread_info->suspended = true; } @@ -786,7 +787,7 @@ bool Processor::ResumeThread(uint32_t thread_id) { thread_info->state == ThreadDebugInfo::State::kZombie); thread_info->suspended = false; auto thread = thread_info->thread; - return XSUCCEEDED(thread->Resume()); + return thread->thread()->Resume(); } bool Processor::ResumeAllThreads() { @@ -800,8 +801,8 @@ bool Processor::ResumeAllThreads() { thread_info->state == ThreadDebugInfo::State::kExited) { // Thread is dead and cannot be resumed - ignore. continue; - } else if (XThread::IsInThread() && - thread_info->thread_id == XThread::GetCurrentThreadId()) { + } else if (Thread::IsInThread() && + thread_info->thread_id == Thread::GetCurrentThreadId()) { // Can't resume ourselves. continue; } @@ -811,7 +812,7 @@ bool Processor::ResumeAllThreads() { continue; } thread_info->suspended = false; - bool did_resume = XSUCCEEDED(thread->Resume()); + bool did_resume = thread->thread()->Resume(); assert_true(did_resume); } return true; @@ -987,7 +988,7 @@ bool Processor::StepToGuestAddress(uint32_t thread_id, uint32_t pc) { if (functions.empty()) { // Function hasn't been generated yet. Generate it. if (!ResolveFunction(pc)) { - XELOGE("XThread::StepToAddress(%.8X) - Function could not be resolved", + XELOGE("Processor::StepToAddress(%.8X) - Function could not be resolved", pc); return false; } @@ -1005,7 +1006,7 @@ bool Processor::StepToGuestAddress(uint32_t thread_id, uint32_t pc) { auto thread_info = QueryThreadDebugInfo(thread_id); uint32_t suspend_count = 1; while (suspend_count) { - thread_info->thread->Resume(&suspend_count); + thread_info->thread->thread()->Resume(&suspend_count); } fence.Wait(); @@ -1075,7 +1076,7 @@ uint32_t Processor::StepIntoGuestBranchTarget(uint32_t thread_id, uint32_t pc) { // HACK uint32_t suspend_count = 1; while (suspend_count) { - thread->Resume(&suspend_count); + thread->thread()->Resume(&suspend_count); } fence.Wait(); @@ -1090,7 +1091,7 @@ uint32_t Processor::StepToGuestSafePoint(uint32_t thread_id, bool ignore_host) { // This cannot be done if we're the calling thread! if (thread_id == ThreadState::GetThreadID()) { assert_always( - "XThread::StepToSafePoint(): target thread is the calling thread!"); + "Processor::StepToSafePoint(): target thread is the calling thread!"); return 0; } auto thread_info = QueryThreadDebugInfo(thread_id); @@ -1213,11 +1214,15 @@ uint32_t Processor::StepToGuestSafePoint(uint32_t thread_id, bool ignore_host) { } else { // We've managed to catch a thread before it called into the guest. // Set a breakpoint on its startup procedure and capture it there. + // TODO(DrChat): Reimplement + assert_always("Unimplemented"); + /* auto creation_params = thread->creation_params(); pc = creation_params->xapi_thread_startup ? creation_params->xapi_thread_startup : creation_params->start_address; StepToGuestAddress(thread_id, pc); + */ } } diff --git a/src/xenia/cpu/processor.h b/src/xenia/cpu/processor.h index 7121d9ea0..4ea6d4429 100644 --- a/src/xenia/cpu/processor.h +++ b/src/xenia/cpu/processor.h @@ -189,7 +189,7 @@ class Processor { public: // TODO(benvanik): hide. void OnThreadCreated(uint32_t handle, ThreadState* thread_state, - kernel::XThread* thread); + Thread* thread); void OnThreadExit(uint32_t thread_id); void OnThreadDestroyed(uint32_t thread_id); void OnThreadEnteringWait(uint32_t thread_id); diff --git a/src/xenia/cpu/thread.cc b/src/xenia/cpu/thread.cc new file mode 100644 index 000000000..97f036b1f --- /dev/null +++ b/src/xenia/cpu/thread.cc @@ -0,0 +1,29 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2017 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/cpu/thread.h" +#include "xenia/cpu/thread_state.h" + +namespace xe { +namespace cpu { + +thread_local Thread* Thread::current_thread_tls_ = nullptr; + +Thread::Thread() {} +Thread::~Thread() {} + +bool Thread::IsInThread() { return current_thread_tls_ != nullptr; } + +Thread* Thread::GetCurrentThread() { return current_thread_tls_; } +uint32_t Thread::GetCurrentThreadId() { + return Thread::GetCurrentThread()->thread_state()->thread_id(); +} + +} // namespace cpu +} // namespace xe \ No newline at end of file diff --git a/src/xenia/cpu/thread.h b/src/xenia/cpu/thread.h new file mode 100644 index 000000000..d1b68bf10 --- /dev/null +++ b/src/xenia/cpu/thread.h @@ -0,0 +1,55 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2017 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_CPU_THREAD_H_ +#define XENIA_CPU_THREAD_H_ + +#include "xenia/base/threading.h" + +#include + +namespace xe { +namespace cpu { + +class ThreadState; + +// Represents a thread that runs guest code. +class Thread { + public: + Thread(); + ~Thread(); + + static bool IsInThread(); + static Thread* GetCurrentThread(); + static uint32_t GetCurrentThreadId(); + ThreadState* thread_state() const { return thread_state_; } + + // True if the thread should be paused by the debugger. + // All threads that can run guest code must be stopped for the debugger to + // work properly. + bool can_debugger_suspend() const { return can_debugger_suspend_; } + void set_can_debugger_suspend(bool value) { can_debugger_suspend_ = value; } + + xe::threading::Thread* thread() { return thread_.get(); } + const std::string& thread_name() const { return thread_name_; } + + protected: + static thread_local Thread* current_thread_tls_; + + ThreadState* thread_state_ = nullptr; + std::unique_ptr thread_ = nullptr; + + bool can_debugger_suspend_ = true; + std::string thread_name_; +}; + +} // namespace cpu +} // namespace xe + +#endif // XENIA_CPU_THREAD_H_ diff --git a/src/xenia/cpu/thread_debug_info.h b/src/xenia/cpu/thread_debug_info.h index c83212ae2..ffce6822c 100644 --- a/src/xenia/cpu/thread_debug_info.h +++ b/src/xenia/cpu/thread_debug_info.h @@ -13,14 +13,9 @@ #include #include "xenia/base/x64_context.h" +#include "xenia/cpu/thread.h" #include "xenia/cpu/thread_state.h" -namespace xe { -namespace kernel { -class XThread; -} // namespace kernel -} // namespace xe - namespace xe { namespace cpu { @@ -53,8 +48,8 @@ struct ThreadDebugInfo { // XThread::handle() of the thread. // This will be invalidated when the thread dies. uint32_t thread_handle = 0; - // Kernel thread object. Only valid when the thread is alive. - kernel::XThread* thread = nullptr; + // Thread object. Only valid when the thread is alive. + Thread* thread = nullptr; // Current state of the thread. State state = State::kAlive; // Whether the debugger has forcefully suspended this thread. diff --git a/src/xenia/debug/ui/debug_window.cc b/src/xenia/debug/ui/debug_window.cc index 4acb352ad..27195d94d 100644 --- a/src/xenia/debug/ui/debug_window.cc +++ b/src/xenia/debug/ui/debug_window.cc @@ -288,7 +288,7 @@ void DebugWindow::DrawToolbar() { current_thread_index = i; } if (thread_info->state != cpu::ThreadDebugInfo::State::kZombie) { - thread_combo.Append(thread_info->thread->name()); + thread_combo.Append(thread_info->thread->thread_name()); } else { thread_combo.Append("(zombie)"); } diff --git a/src/xenia/kernel/xthread.cc b/src/xenia/kernel/xthread.cc index c269c1806..42af839c1 100644 --- a/src/xenia/kernel/xthread.cc +++ b/src/xenia/kernel/xthread.cc @@ -43,7 +43,6 @@ const uint32_t XAPC::kDummyRundownRoutine; using xe::cpu::ppc::PPCOpcode; uint32_t next_xthread_id_ = 0; -thread_local XThread* current_thread_tls_ = nullptr; XThread::XThread(KernelState* kernel_state) : XObject(kernel_state, kTypeThread), guest_thread_(true) {} @@ -102,13 +101,14 @@ XThread::~XThread() { } } +bool XThread::IsInThread() { return Thread::IsInThread(); } + bool XThread::IsInThread(XThread* other) { return current_thread_tls_ == other; } -bool XThread::IsInThread() { return current_thread_tls_ != nullptr; } XThread* XThread::GetCurrentThread() { - XThread* thread = current_thread_tls_; + XThread* thread = reinterpret_cast(current_thread_tls_); if (!thread) { assert_always("Attempting to use kernel stuff from a non-kernel thread"); } @@ -142,12 +142,12 @@ void XThread::set_last_error(uint32_t error_code) { } void XThread::set_name(const std::string& name) { - name_ = xe::format_string("%s (%.8X)", name.c_str(), handle()); + thread_name_ = xe::format_string("%s (%.8X)", name.c_str(), handle()); if (thread_) { // May be getting set before the thread is created. // One the thread is ready it will handle it. - thread_->set_name(name_); + thread_->set_name(thread_name_); } } @@ -393,10 +393,13 @@ X_STATUS XThread::Create() { XELOGE("CreateThread failed"); return X_STATUS_NO_MEMORY; } - thread_->set_affinity_mask(proc_mask); + + if (!FLAGS_ignore_thread_affinities) { + thread_->set_affinity_mask(proc_mask); + } // Set the thread name based on host ID (for easier debugging). - if (name_.empty()) { + if (thread_name_.empty()) { char thread_name[32]; snprintf(thread_name, xe::countof(thread_name), "XThread%.04X", thread_->system_id()); @@ -472,7 +475,7 @@ X_STATUS XThread::Terminate(int exit_code) { void XThread::Execute() { XELOGKERNEL("XThread::Execute thid %d (handle=%.8X, '%s', native=%.8X)", - thread_id_, handle(), name_.c_str(), thread_->system_id()); + thread_id_, handle(), thread_name_.c_str(), thread_->system_id()); // Let the kernel know we are starting. kernel_state()->OnThreadExecute(this); @@ -852,7 +855,7 @@ bool XThread::Save(ByteStream* stream) { } stream->Write('THRD'); - stream->Write(name_); + stream->Write(thread_name_); ThreadSavedState state; state.thread_id = thread_id_; @@ -918,7 +921,7 @@ object_ref XThread::Restore(KernelState* kernel_state, XELOGD("XThread %.8X", thread->handle()); - thread->name_ = stream->Read(); + thread->thread_name_ = stream->Read(); ThreadSavedState state; stream->Read(&state, sizeof(ThreadSavedState)); @@ -1030,7 +1033,7 @@ XHostThread::XHostThread(KernelState* kernel_state, uint32_t stack_size, void XHostThread::Execute() { XELOGKERNEL( "XThread::Execute thid %d (handle=%.8X, '%s', native=%.8X, )", - thread_id_, handle(), name_.c_str(), thread_->system_id()); + thread_id_, handle(), thread_name_.c_str(), thread_->system_id()); // Let the kernel know we are starting. kernel_state()->OnThreadExecute(this); diff --git a/src/xenia/kernel/xthread.h b/src/xenia/kernel/xthread.h index 2ca565eb7..0b38f9689 100644 --- a/src/xenia/kernel/xthread.h +++ b/src/xenia/kernel/xthread.h @@ -15,6 +15,7 @@ #include "xenia/base/mutex.h" #include "xenia/base/threading.h" +#include "xenia/cpu/thread.h" #include "xenia/cpu/thread_state.h" #include "xenia/kernel/util/native_list.h" #include "xenia/kernel/xmutant.h" @@ -102,7 +103,7 @@ struct X_KTHREAD { }; static_assert_size(X_KTHREAD, 0xAB0); -class XThread : public XObject { +class XThread : public XObject, public cpu::Thread { public: static const Type kType = kTypeThread; @@ -136,18 +137,11 @@ class XThread : public XObject { // True if the thread is created by the guest app. bool is_guest_thread() const { return guest_thread_; } bool main_thread() const { return main_thread_; } - // True if the thread should be paused by the debugger. - // All threads that can run guest code must be stopped for the debugger to - // work properly. - bool can_debugger_suspend() const { return can_debugger_suspend_; } - void set_can_debugger_suspend(bool value) { can_debugger_suspend_ = value; } bool is_running() const { return running_; } - cpu::ThreadState* thread_state() const { return thread_state_; } uint32_t thread_id() const { return thread_id_; } uint32_t last_error(); void set_last_error(uint32_t error_code); - const std::string& name() const { return name_; } void set_name(const std::string& name); X_STATUS Create(); @@ -211,7 +205,6 @@ class XThread : public XObject { std::vector> pending_mutant_acquires_; uint32_t thread_id_ = 0; - std::unique_ptr thread_; uint32_t scratch_address_ = 0; uint32_t scratch_size_ = 0; uint32_t tls_static_address_ = 0; @@ -222,14 +215,10 @@ class XThread : public XObject { uint32_t stack_alloc_size_ = 0; // Stack alloc size uint32_t stack_base_ = 0; // High address uint32_t stack_limit_ = 0; // Low address - cpu::ThreadState* thread_state_ = nullptr; bool guest_thread_ = false; bool main_thread_ = false; // Entry-point thread - bool can_debugger_suspend_ = true; bool running_ = false; - std::string name_; - int32_t priority_ = 0; uint32_t affinity_ = 0;