diff --git a/src/xenia/debug/debug_listener.h b/src/xenia/debug/debug_listener.h new file mode 100644 index 000000000..e437fde43 --- /dev/null +++ b/src/xenia/debug/debug_listener.h @@ -0,0 +1,63 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_DEBUG_DEBUG_LISTENER_H_ +#define XENIA_DEBUG_DEBUG_LISTENER_H_ + +#include "xenia/cpu/thread_state.h" +#include "xenia/debug/breakpoint.h" + +namespace xe { +namespace kernel { +class XThread; +} // namespace kernel +} // namespace xe + +namespace xe { +namespace debug { + +// Debug event listener interface. +// Implementations will receive calls from arbitrary threads and must marshal +// them as required. +class DebugListener { + public: + // Handles request for debugger focus (such as when the user requests the + // debugger be brought to the front). + virtual void OnFocus() = 0; + + // Handles the debugger detaching from the target. + // This will be called on shutdown or when the user requests the debug session + // end. + virtual void OnDetached() = 0; + + // Handles execution being interrupted and transitioning to + // ExceutionState::kPaused. + virtual void OnExecutionPaused() = 0; + // Handles execution continuing when transitioning to + // ExecutionState::kRunning or ExecutionState::kStepping. + virtual void OnExecutionContinued() = 0; + // Handles execution ending when transitioning to ExecutionState::kEnded. + virtual void OnExecutionEnded() = 0; + + // Handles step completion events when a requested step operation completes. + // The thread is the one that hit its step target. Note that because multiple + // threads could be stepping simultaneously (such as a run-to-cursor) use the + // thread passed instead of keeping any other state. + virtual void OnStepCompleted(xe::kernel::XThread* thread) = 0; + + // Handles breakpoint events as they are hit per-thread. + // Breakpoints may be hit during stepping. + virtual void OnBreakpointHit(Breakpoint* breakpoint, + xe::kernel::XThread* thread) = 0; +}; + +} // namespace debug +} // namespace xe + +#endif // XENIA_DEBUG_DEBUG_LISTENER_H_ diff --git a/src/xenia/debug/debugger.h b/src/xenia/debug/debugger.h index bf6ff3016..9d660a3f2 100644 --- a/src/xenia/debug/debugger.h +++ b/src/xenia/debug/debugger.h @@ -26,6 +26,8 @@ #include "xenia/cpu/processor.h" #include "xenia/cpu/thread_state.h" #include "xenia/debug/breakpoint.h" +#include "xenia/debug/debug_listener.h" +#include "xenia/debug/thread_execution_info.h" DECLARE_bool(debug); @@ -53,118 +55,6 @@ enum class ExecutionState { kEnded, }; -// Per-XThread structure holding debugger state and a cache of the sampled call -// stack. -// -// In most cases debug consumers should rely only on data in this structure as -// it is never removed (even when a thread is destroyed) and always available -// even when running. -struct ThreadExecutionInfo { - ThreadExecutionInfo(); - ~ThreadExecutionInfo(); - - enum class State { - // Thread is alive and running. - kAlive, - // Thread is in a wait state. - kWaiting, - // Thread has exited but not yet been killed. - kExited, - // Thread has been killed. - kZombie, - }; - - // XThread::thread_id(), unique to the thread for the run of the emulator. - uint32_t thread_id = 0; - // XThread::handle() of the thread. - // This will be invalidated when the thread dies. - uint32_t thread_handle = 0; - // Target XThread, if it has not been destroyed. - // TODO(benvanik): hold a ref here to keep zombie threads around? - kernel::XThread* thread = nullptr; - // Current state of the thread. - State state = State::kAlive; - // Whether the debugger has forcefully suspended this thread. - bool suspended = false; - - // A breakpoint managed by the stepping system, installed as required to - // trigger a break at the next instruction. - std::unique_ptr step_breakpoint; - // A breakpoint managed by the stepping system, installed as required to - // trigger after a step over a disabled breakpoint. - // When this breakpoint is hit the breakpoint referenced in - // restore_original_breakpoint will be reinstalled. - // Breakpoint restore_step_breakpoint; - // If the thread is stepping over a disabled breakpoint this will point to - // that breakpoint so it can be restored. - // Breakpoint* restore_original_breakpoint = nullptr; - - // Last-sampled PPC context. - // This is updated whenever the debugger stops. - xe::cpu::ppc::PPCContext guest_context; - // Last-sampled host x64 context. - // This is updated whenever the debugger stops and must be used instead of any - // value taken from the StackWalker as it properly respects exception stacks. - X64Context host_context; - - // A single frame in a call stack. - struct Frame { - // PC of the current instruction in host code. - uint64_t host_pc = 0; - // Base of the function the current host_pc is located within. - uint64_t host_function_address = 0; - // PC of the current instruction in guest code. - // 0 if not a guest address or not known. - uint32_t guest_pc = 0; - // Base of the function the current guest_pc is located within. - uint32_t guest_function_address = 0; - // Function the current guest_pc is located within. - cpu::Function* guest_function = nullptr; - // Name of the function, if known. - // TODO(benvanik): string table? - char name[256] = {0}; - }; - - // Last-sampled call stack. - // This is updated whenever the debugger stops. - std::vector frames; -}; - -// Debug event listener interface. -// Implementations will receive calls from arbitrary threads and must marshal -// them as required. -class DebugListener { - public: - // Handles request for debugger focus (such as when the user requests the - // debugger be brought to the front). - virtual void OnFocus() = 0; - - // Handles the debugger detaching from the target. - // This will be called on shutdown or when the user requests the debug session - // end. - virtual void OnDetached() = 0; - - // Handles execution being interrupted and transitioning to - // ExceutionState::kPaused. - virtual void OnExecutionPaused() = 0; - // Handles execution continuing when transitioning to - // ExecutionState::kRunning or ExecutionState::kStepping. - virtual void OnExecutionContinued() = 0; - // Handles execution ending when transitioning to ExecutionState::kEnded. - virtual void OnExecutionEnded() = 0; - - // Handles step completion events when a requested step operation completes. - // The thread is the one that hit its step target. Note that because multiple - // threads could be stepping simultaneously (such as a run-to-cursor) use the - // thread passed instead of keeping any other state. - virtual void OnStepCompleted(xe::kernel::XThread* thread) = 0; - - // Handles breakpoint events as they are hit per-thread. - // Breakpoints may be hit during stepping. - virtual void OnBreakpointHit(Breakpoint* breakpoint, - xe::kernel::XThread* thread) = 0; -}; - class Debugger { public: explicit Debugger(Emulator* emulator); diff --git a/src/xenia/debug/thread_execution_info.cc b/src/xenia/debug/thread_execution_info.cc new file mode 100644 index 000000000..4f837907f --- /dev/null +++ b/src/xenia/debug/thread_execution_info.cc @@ -0,0 +1,20 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/debug/thread_execution_info.h" + +namespace xe { +namespace debug { + +ThreadExecutionInfo::ThreadExecutionInfo() = default; + +ThreadExecutionInfo::~ThreadExecutionInfo() = default; + +} // namespace debug +} // namespace xe diff --git a/src/xenia/debug/thread_execution_info.h b/src/xenia/debug/thread_execution_info.h new file mode 100644 index 000000000..317561e63 --- /dev/null +++ b/src/xenia/debug/thread_execution_info.h @@ -0,0 +1,108 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_DEBUG_THREAD_EXECUTION_INFO_H_ +#define XENIA_DEBUG_THREAD_EXECUTION_INFO_H_ + +#include + +#include "xenia/base/x64_context.h" +#include "xenia/cpu/thread_state.h" +#include "xenia/debug/breakpoint.h" + +namespace xe { +namespace kernel { +class XThread; +} // namespace kernel +} // namespace xe + +namespace xe { +namespace debug { + +// Per-XThread structure holding debugger state and a cache of the sampled call +// stack. +// +// In most cases debug consumers should rely only on data in this structure as +// it is never removed (even when a thread is destroyed) and always available +// even when running. +struct ThreadExecutionInfo { + ThreadExecutionInfo(); + ~ThreadExecutionInfo(); + + enum class State { + // Thread is alive and running. + kAlive, + // Thread is in a wait state. + kWaiting, + // Thread has exited but not yet been killed. + kExited, + // Thread has been killed. + kZombie, + }; + + // XThread::thread_id(), unique to the thread for the run of the emulator. + uint32_t thread_id = 0; + // XThread::handle() of the thread. + // This will be invalidated when the thread dies. + uint32_t thread_handle = 0; + // Target XThread, if it has not been destroyed. + // TODO(benvanik): hold a ref here to keep zombie threads around? + kernel::XThread* thread = nullptr; + // Current state of the thread. + State state = State::kAlive; + // Whether the debugger has forcefully suspended this thread. + bool suspended = false; + + // A breakpoint managed by the stepping system, installed as required to + // trigger a break at the next instruction. + std::unique_ptr step_breakpoint; + // A breakpoint managed by the stepping system, installed as required to + // trigger after a step over a disabled breakpoint. + // When this breakpoint is hit the breakpoint referenced in + // restore_original_breakpoint will be reinstalled. + // Breakpoint restore_step_breakpoint; + // If the thread is stepping over a disabled breakpoint this will point to + // that breakpoint so it can be restored. + // Breakpoint* restore_original_breakpoint = nullptr; + + // Last-sampled PPC context. + // This is updated whenever the debugger stops. + xe::cpu::ppc::PPCContext guest_context; + // Last-sampled host x64 context. + // This is updated whenever the debugger stops and must be used instead of any + // value taken from the StackWalker as it properly respects exception stacks. + X64Context host_context; + + // A single frame in a call stack. + struct Frame { + // PC of the current instruction in host code. + uint64_t host_pc = 0; + // Base of the function the current host_pc is located within. + uint64_t host_function_address = 0; + // PC of the current instruction in guest code. + // 0 if not a guest address or not known. + uint32_t guest_pc = 0; + // Base of the function the current guest_pc is located within. + uint32_t guest_function_address = 0; + // Function the current guest_pc is located within. + cpu::Function* guest_function = nullptr; + // Name of the function, if known. + // TODO(benvanik): string table? + char name[256] = {0}; + }; + + // Last-sampled call stack. + // This is updated whenever the debugger stops. + std::vector frames; +}; + +} // namespace debug +} // namespace xe + +#endif // XENIA_DEBUG_THREAD_EXECUTION_INFO_H_