From 98efc7ddfa0fa88b6a8b8b976cb1443b1d24dd72 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Mon, 23 Dec 2013 19:46:35 -0800 Subject: [PATCH] Suspend/resume working. --- debugger/assets/ui/code/function-view.js | 2 +- debugger/src/datasources.js | 5 +-- debugger/src/session.js | 44 ++++++++++++++---------- src/alloy/backend/ivm/ivm_function.cc | 8 +++++ src/alloy/frontend/ppc/ppc_context.h | 2 +- src/alloy/runtime/debugger.cc | 4 +-- src/alloy/runtime/debugger.h | 2 +- src/alloy/runtime/thread_state.h | 4 ++- src/xenia/cpu/processor.cc | 19 ++++++++++ src/xenia/cpu/xenon_thread_state.cc | 27 ++++++++++++++- src/xenia/cpu/xenon_thread_state.h | 6 +++- 11 files changed, 95 insertions(+), 28 deletions(-) diff --git a/debugger/assets/ui/code/function-view.js b/debugger/assets/ui/code/function-view.js index 1b848b388..5d1160170 100644 --- a/debugger/assets/ui/code/function-view.js +++ b/debugger/assets/ui/code/function-view.js @@ -247,7 +247,7 @@ module.controller('FunctionViewController', function( instance, line, gutterClass, e) { if (e.which == 1) { if (gutterClass == 'debugger-fnview-gutter-icon' || - gutterClsas == 'debugger-fnview-gutter-addr') { + gutterClass == 'debugger-fnview-gutter-addr') { var sourceLine = $scope.sourceLines[line]; if (!sourceLine || sourceLine[0] != 'i') { return; diff --git a/debugger/src/datasources.js b/debugger/src/datasources.js index 79296ee90..ccb1c7f62 100644 --- a/debugger/src/datasources.js +++ b/debugger/src/datasources.js @@ -145,9 +145,10 @@ module.service('DataSource', function($q) { }); }; - DataSource.prototype.stepNext = function() { + DataSource.prototype.stepNext = function(threadId) { return this.issue({ - command: 'cpu.step' + command: 'cpu.step', + threadId: threadId }); }; diff --git a/debugger/src/session.js b/debugger/src/session.js index 3a73c56b2..bf60d6261 100644 --- a/debugger/src/session.js +++ b/debugger/src/session.js @@ -224,26 +224,31 @@ module.service('Session', function( }; Session.prototype.onBreakpointHit = function(breakpointId, threadId) { - var breakpoint = this.breakpointsById[breakpointId]; - var thread = null; // TODO - if (!breakpoint) { - log.error('Breakpoint hit but not found'); - return; - } - // Now paused! this.paused = true; - $state.go('session.code.function', { - 'function': breakpoint.fnAddress.toString(16).toUpperCase(), - 'a': breakpoint.address.toString(16).toUpperCase() - }, { - notify: false, - reloadOnSearch: false - }); + if (breakpointId) { + var breakpoint = this.breakpointsById[breakpointId]; + var thread = null; // TODO + if (!breakpoint) { + log.error('Breakpoint hit but not found'); + return; + } - // - log.info('breakpoint!!'); + log.info('Breakpoint hit at 0x' + + breakpoint.address.toString(16).toUpperCase() + '.'); + + $state.go('session.code.function', { + 'function': breakpoint.fnAddress.toString(16).toUpperCase(), + 'a': breakpoint.address.toString(16).toUpperCase() + }, { + notify: false, + reloadOnSearch: false + }); + } else { + // Just a general pause. + log.info('Execution paused.'); + } }; Session.prototype.continueExecution = function() { @@ -252,6 +257,7 @@ module.service('Session', function( } this.paused = false; this.dataSource.continueExecution().then(function() { + log.info('Execution resumed.'); }, function(e) { log.error('Unable to continue: ' + e); }); @@ -261,18 +267,20 @@ module.service('Session', function( if (!this.dataSource) { return; } + this.paused = true; this.dataSource.breakExecution().then(function() { + log.info('Execution paused.'); }, function(e) { log.error('Unable to break: ' + e); }); }; - Session.prototype.stepNext = function() { + Session.prototype.stepNext = function(threadId) { if (!this.dataSource) { return; } this.paused = false; - this.dataSource.breakExecution().then(function() { + this.dataSource.stepNext(threadId).then(function() { }, function(e) { log.error('Unable to step: ' + e); }); diff --git a/src/alloy/backend/ivm/ivm_function.cc b/src/alloy/backend/ivm/ivm_function.cc index 1aaffbe40..e616dada4 100644 --- a/src/alloy/backend/ivm/ivm_function.cc +++ b/src/alloy/backend/ivm/ivm_function.cc @@ -119,11 +119,19 @@ int IVMFunction::CallImpl(ThreadState* thread_state, uint64_t return_address) { ics.return_address = return_address; ics.call_return_address = 0; + volatile int* suspend_flag_address = thread_state->suspend_flag_address(); + // TODO(benvanik): DID_CARRY -- need HIR to set a OPCODE_FLAG_SET_CARRY // or something so the fns can set an ics flag. uint32_t ia = 0; while (true) { + // Check suspend. We could do this only on certain instructions, if we + // wanted to speed things up. + if (*suspend_flag_address) { + thread_state->EnterSuspend(); + } + IntCode* i = &intcodes_[ia]; if (i->debug_flags) { diff --git a/src/alloy/frontend/ppc/ppc_context.h b/src/alloy/frontend/ppc/ppc_context.h index a3562421c..6e6f2e8c8 100644 --- a/src/alloy/frontend/ppc/ppc_context.h +++ b/src/alloy/frontend/ppc/ppc_context.h @@ -190,7 +190,7 @@ typedef struct XECACHEALIGN64 PPCContext_s { uint8_t* membase; runtime::Runtime* runtime; runtime::ThreadState* thread_state; - uint32_t suspend_flag; + volatile int suspend_flag; void SetRegFromString(const char* name, const char* value); bool CompareRegWithString(const char* name, const char* value, diff --git a/src/alloy/runtime/debugger.cc b/src/alloy/runtime/debugger.cc index cd2979dd4..6c537c131 100644 --- a/src/alloy/runtime/debugger.cc +++ b/src/alloy/runtime/debugger.cc @@ -63,12 +63,12 @@ int Debugger::ResumeThread(uint32_t thread_id) { return result; } -int Debugger::ResumeAllThreads() { +int Debugger::ResumeAllThreads(bool force) { int result = 0; LockMutex(threads_lock_); for (auto it = threads_.begin(); it != threads_.end(); ++it) { ThreadState* thread_state = it->second; - if (thread_state->Resume()) { + if (thread_state->Resume(force)) { result = 1; } } diff --git a/src/alloy/runtime/debugger.h b/src/alloy/runtime/debugger.h index 20386b466..16e7d1129 100644 --- a/src/alloy/runtime/debugger.h +++ b/src/alloy/runtime/debugger.h @@ -84,7 +84,7 @@ public: int SuspendAllThreads(uint32_t timeout_ms = UINT_MAX); int ResumeThread(uint32_t thread_id); - int ResumeAllThreads(); + int ResumeAllThreads(bool force = false); int AddBreakpoint(Breakpoint* breakpoint); int RemoveBreakpoint(Breakpoint* breakpoint); diff --git a/src/alloy/runtime/thread_state.h b/src/alloy/runtime/thread_state.h index 1d211979c..2725747f1 100644 --- a/src/alloy/runtime/thread_state.h +++ b/src/alloy/runtime/thread_state.h @@ -32,8 +32,10 @@ public: void* backend_data() const { return backend_data_; } void* raw_context() const { return raw_context_; } + virtual volatile int* suspend_flag_address() const = 0; virtual int Suspend(uint32_t timeout_ms = UINT_MAX) = 0; - virtual int Resume() = 0; + virtual int Resume(bool force = false) = 0; + virtual void EnterSuspend() = 0; static void Bind(ThreadState* thread_state); static ThreadState* Get(); diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index 83da74b04..993e89f28 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -218,6 +218,10 @@ void Processor::OnDebugClientDisconnected(uint32_t client_id) { debug_client_states_.erase(client_id); xe_mutex_unlock(debug_client_states_lock_); delete client_state; + + // Whenever we support multiple clients we will need to respect pause + // settings. For now, resume until running. + runtime_->debugger()->ResumeAllThreads(true); } json_t* Processor::OnDebugRequest( @@ -417,6 +421,21 @@ json_t* Processor::OnDebugRequest( return json_string("Unable to remove breakpoints"); } return json_null(); + } else if (xestrcmpa(command, "continue") == 0) { + if (runtime_->debugger()->ResumeAllThreads()) { + succeeded = false; + return json_string("Unable to resume threads"); + } + return json_null(); + } else if (xestrcmpa(command, "break") == 0) { + if (runtime_->debugger()->SuspendAllThreads()) { + succeeded = false; + return json_string("Unable to suspend threads"); + } + return json_null(); + } else if (xestrcmpa(command, "step") == 0) { + // threadId + return json_null(); } else { succeeded = false; return json_string("Unknown command"); diff --git a/src/xenia/cpu/xenon_thread_state.cc b/src/xenia/cpu/xenon_thread_state.cc index 84f221942..925388e7b 100644 --- a/src/xenia/cpu/xenon_thread_state.cc +++ b/src/xenia/cpu/xenon_thread_state.cc @@ -28,6 +28,8 @@ XenonThreadState::XenonThreadState( stack_address_ = memory_->HeapAlloc( 0, stack_size, MEMORY_FLAG_ZERO); + debug_break_ = CreateEvent(NULL, FALSE, FALSE, NULL); + // Allocate with 64b alignment. context_ = (PPCContext*)xe_malloc_aligned(sizeof(PPCContext)); XEASSERT(((uint64_t)context_ & 0xF) == 0); @@ -53,6 +55,8 @@ XenonThreadState::XenonThreadState( XenonThreadState::~XenonThreadState() { runtime_->debugger()->OnThreadDestroyed(this); + CloseHandle(debug_break_); + alloy::tracing::WriteEvent(EventType::ThreadDeinit({ })); @@ -60,10 +64,31 @@ XenonThreadState::~XenonThreadState() { memory_->HeapFree(stack_address_, stack_size_); } +volatile int* XenonThreadState::suspend_flag_address() const { + return &context_->suspend_flag; +} + int XenonThreadState::Suspend(uint32_t timeout_ms) { + // Set suspend flag. + // One of the checks should call in to OnSuspend() at some point. + xe_atomic_inc_32(&context_->suspend_flag); return 0; } -int XenonThreadState::Resume() { +int XenonThreadState::Resume(bool force) { + if (context_->suspend_flag) { + if (force) { + context_->suspend_flag = 0; + SetEvent(debug_break_); + } else { + if (!xe_atomic_dec_32(&context_->suspend_flag)) { + SetEvent(debug_break_); + } + } + } return 0; } + +void XenonThreadState::EnterSuspend() { + WaitForSingleObject(debug_break_, INFINITE); +} diff --git a/src/xenia/cpu/xenon_thread_state.h b/src/xenia/cpu/xenon_thread_state.h index bdecbd8df..05eb278b2 100644 --- a/src/xenia/cpu/xenon_thread_state.h +++ b/src/xenia/cpu/xenon_thread_state.h @@ -31,8 +31,10 @@ public: PPCContext* context() const { return context_; } + virtual volatile int* suspend_flag_address() const; virtual int Suspend(uint32_t timeout_ms = UINT_MAX); - virtual int Resume(); + virtual int Resume(bool force = false); + virtual void EnterSuspend(); private: size_t stack_size_; @@ -44,6 +46,8 @@ private: // NOTE: must be 64b aligned for SSE ops. PPCContext* context_; + + HANDLE debug_break_; };