diff --git a/debugger/assets/styles/app.css b/debugger/assets/styles/app.css index f32f71643..1a599c6fa 100644 --- a/debugger/assets/styles/app.css +++ b/debugger/assets/styles/app.css @@ -10,6 +10,9 @@ .full-width { width: 100%; } +.left-align { + text-align: left; +} body { margin: 0; @@ -194,6 +197,7 @@ body { position: relative; border-left: 2px solid #ddd; border-right: 2px solid #ddd; + min-width: 720px; } .debugger-fnview { position: absolute; @@ -215,6 +219,7 @@ body { order: 1; flex: 1 1 auto; padding: 5px; + line-height: 1; } .debugger-fnview-header-name { font-size: 21px; @@ -227,6 +232,7 @@ body { order: 2; flex: 0 0 auto; padding: 5px; + margin-top: 3px; } .debugger-fnview-body { order: 2; @@ -300,20 +306,37 @@ body { flex: 0 0 auto; display: flex; flex-flow: column nowrap; - min-width: 200px; + width: 40%; } .debugger-tools-threads { order: 1; flex: 0 0 auto; padding: 5px; } +.debugger-tools-threads { + order: 1; + flex: 0 0 auto; + padding: 5px; + display: flex; + flex-flow: row nowrap; +} +.debugger-tools-threads-header-left { + order: 1; + flex: 1 1 auto; + padding-right: 5px; +} +.debugger-tools-threads-header-right { + order: 2; + flex: 0 0 auto; +} .debugger-tools-callstack { order: 2; - flex: 1 1 auto; + flex: 0 0 auto; padding: 5px; border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; position: relative; + height: 100px; } .debugger-tools-registers { order: 3; diff --git a/debugger/assets/ui/code/code-tab.html b/debugger/assets/ui/code/code-tab.html index a5b4bd2c5..79a7e1bef 100644 --- a/debugger/assets/ui/code/code-tab.html +++ b/debugger/assets/ui/code/code-tab.html @@ -1,18 +1,18 @@
- -
- -
@@ -21,7 +21,7 @@
-
-
@@ -52,15 +52,21 @@
-
-
+
+ +
callstack diff --git a/debugger/assets/ui/code/code-tab.js b/debugger/assets/ui/code/code-tab.js index 67e0d2d2f..6390e7317 100644 --- a/debugger/assets/ui/code/code-tab.js +++ b/debugger/assets/ui/code/code-tab.js @@ -23,24 +23,19 @@ module.controller('CodeTabController', function( $scope.functionList = []; function refresh() { - if (!app.session || !app.session.dataSource) { + if (!app.session) { $scope.moduleList = []; return; } - var dataSource = app.session.dataSource; - dataSource.getModuleList().then(function(list) { - $scope.moduleList = list; - if (!$scope.selectedModule) { - if (list.length) { - $scope.selectModule(list[0]); - } - } else { - $scope.selectModule($scope.selectedModule); + $scope.moduleList = app.session.state.getModuleList(); + if (!$scope.selectedModule) { + if ($scope.moduleList.length) { + $scope.selectModule($scope.moduleList[0]); } - }, function(e) { - log.error('Unable to fetch module list'); - }); + } else { + $scope.selectModule($scope.selectedModule); + } console.log('refresh'); }; @@ -54,15 +49,10 @@ module.controller('CodeTabController', function( $scope.functionList = []; } - var dataSource = app.session.dataSource; - dataSource.getFunctionList(module.name).then(function(list) { - $scope.functionList = list; - }, function(e) { - log.error('Unable to fetch function list'); - }); + $scope.functionList = app.session.state.getFunctionList(module.name); }; - $scope.showModuleInfo = function(module) { + $scope.showModuleInfo = function() { var modalInstance = $modal.open({ templateUrl: 'assets/ui/code/module-info.html', controller: 'ModuleInfoController', @@ -72,13 +62,23 @@ module.controller('CodeTabController', function( return $scope.selectedModule.name; }, moduleInfo: function() { - return app.session.dataSource.getModule( + return app.session.state.getModule( $scope.selectedModule.name); } } }); - modalInstance.result.then(function() { - }, function () { + }; + + $scope.showThreadInfo = function() { + var modalInstance = $modal.open({ + templateUrl: 'assets/ui/code/thread-info.html', + controller: 'ThreadInfoController', + windowClass: 'debugger-module-info', + resolve: { + thread: function() { + return app.session.activeThread; + } + } }); }; diff --git a/debugger/assets/ui/code/function-view.html b/debugger/assets/ui/code/function-view.html index 39bd3d1a0..7a530e296 100644 --- a/debugger/assets/ui/code/function-view.html +++ b/debugger/assets/ui/code/function-view.html @@ -1,7 +1,7 @@
- +
(0x{{fn.startAddress | hex32}}-0x{{fn.endAddress | hex32}})
@@ -14,6 +14,7 @@
+
diff --git a/debugger/assets/ui/code/function-view.js b/debugger/assets/ui/code/function-view.js index 246c4dddd..6f42127b3 100644 --- a/debugger/assets/ui/code/function-view.js +++ b/debugger/assets/ui/code/function-view.js @@ -21,17 +21,15 @@ module.controller('FunctionViewController', function( $scope.highlightInfo = null; function refresh() { - if (!app.session || !app.session.dataSource) { + if (!app.session) { $scope.fn = null; return; } - var dataSource = app.session.dataSource; - - dataSource.getFunction($scope.functionAddress).then(function(fn) { + app.session.state.fetchFunction($scope.functionAddress).then(function(fn) { $scope.fn = fn; updateCode(); }, function(e) { - log.error('Unable to fetch function'); + log.error('Unable to fetch function.'); }); }; $rootScope.$on('refresh', refresh); diff --git a/debugger/assets/ui/code/thread-info.html b/debugger/assets/ui/code/thread-info.html new file mode 100644 index 000000000..9d1c925af --- /dev/null +++ b/debugger/assets/ui/code/thread-info.html @@ -0,0 +1,35 @@ + diff --git a/debugger/assets/ui/code/thread-info.js b/debugger/assets/ui/code/thread-info.js new file mode 100644 index 000000000..bd48acfb6 --- /dev/null +++ b/debugger/assets/ui/code/thread-info.js @@ -0,0 +1,51 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +'use strict'; + +var module = angular.module('xe.ui.code.threadInfo', [ + 'ui.bootstrap', + 'xe.log', + 'xe.session' +]); + + +module.controller('ThreadInfoController', function( + $rootScope, $scope, $modal, log, thread) { + $scope.thread = thread; + + $scope.headerSort = { + column: 'key', + reverse: false + }; + $scope.sectionSort = { + column: 'startAddress', + reverse: false + }; + $scope.staticLibrarySort = { + column: 'name', + reverse: false + }; + $scope.importSort = { + column: 'ordinal', + reverse: false + }; + $scope.changeSort = function(sort, column) { + if (sort.column == column) { + sort.reverse = !sort.reverse; + } else { + sort.column = column; + sort.reverse = false; + } + }; + + $scope.close = function() { + $scope.$close(null); + }; +}); diff --git a/debugger/index.html b/debugger/index.html index b407cdd40..1756d129b 100644 --- a/debugger/index.html +++ b/debugger/index.html @@ -35,6 +35,7 @@ + diff --git a/debugger/src/app.js b/debugger/src/app.js index fe043c904..9cd2f4d45 100644 --- a/debugger/src/app.js +++ b/debugger/src/app.js @@ -21,6 +21,7 @@ var module = angular.module('app', [ 'xe.ui.code', 'xe.ui.code.functionView', 'xe.ui.code.moduleInfo', + 'xe.ui.code.threadInfo', 'xe.ui.console', 'xe.ui.navbar' ]); diff --git a/debugger/src/datasources.js b/debugger/src/datasources.js index f88230fcd..62e3e66bc 100644 --- a/debugger/src/datasources.js +++ b/debugger/src/datasources.js @@ -61,12 +61,6 @@ module.service('DataSource', function($q) { this.delegate = delegate; this.online = false; this.status = 'disconnected'; - - this.cache_ = { - modules: {}, - moduleFunctionLists: {}, - functions: {} - }; }; inherits(DataSource, EventEmitter); DataSource.prototype.open = function() {}; @@ -86,69 +80,35 @@ module.service('DataSource', function($q) { }; DataSource.prototype.getModule = function(moduleName) { - var d = $q.defer(); - var cached = this.cache_.modules[moduleName]; - if (cached) { - d.resolve(cached); - return d.promise; - } - this.issue({ + return this.issue({ command: 'cpu.get_module', module: moduleName - }).then((function(result) { - this.cache_.modules[moduleName] = result; - d.resolve(result); - }).bind(this), (function(e) { - d.reject(e); - }).bind(this)); - return d.promise; + }); }; - DataSource.prototype.getFunctionList = function(moduleName) { - var d = $q.defer(); - var cached = this.cache_.moduleFunctionLists[moduleName]; - this.issue({ + DataSource.prototype.getFunctionList = function(moduleName, opt_since) { + return this.issue({ command: 'cpu.get_function_list', module: moduleName, - since: cached ? cached.version : 0 - }).then((function(result) { - if (cached) { - cached.version = result.version; - for (var n = 0; n < result.list.length; n++) { - cached.list.push(result.list[n]); - } - } else { - cached = this.cache_.moduleFunctionLists[moduleName] = { - version: result.version, - list: result.list - }; - } - d.resolve(cached.list); - }).bind(this), (function(e) { - d.reject(e); - }).bind(this)); - return d.promise; + since: opt_since || 0 + }); }; DataSource.prototype.getFunction = function(address) { - var d = $q.defer(); - var cached = this.cache_.functions[address]; - if (cached) { - d.resolve(cached); - return d.promise; - } - this.issue({ + return this.issue({ command: 'cpu.get_function', address: address - }).then((function(result) { - this.cache_.functions[address] = result; - d.resolve(result); - }).bind(this), (function(e) { - d.reject(e); - }).bind(this)); - return d.promise; + }); }; + DataSource.prototype.getThreadStates = function() { + return this.issue({ + command: 'cpu.get_thread_states' + }); + }; + + // set registers/etc? + DataSource.prototype.addBreakpoint = function(breakpoint) { return this.addBreakpoints([breakpoint]); }; diff --git a/debugger/src/session.js b/debugger/src/session.js index 07c08b112..7676035ef 100644 --- a/debugger/src/session.js +++ b/debugger/src/session.js @@ -15,6 +15,154 @@ var module = angular.module('xe.session', []); module.service('Session', function( $rootScope, $q, $http, $state, log, Breakpoint, FileDataSource, RemoteDataSource) { + var State = function(session) { + this.session = session; + this.clear(); + }; + State.prototype.clear = function() { + this.cache_ = { + moduleList: [], + modules: {}, + moduleFunctionLists: {}, + functions: {}, + threadStates: {}, + threadList: [] + }; + }; + State.prototype.sync = function() { + var cache = this.cache_; + var dataSource = this.session.dataSource; + if (!dataSource) { + var d = $q.defer(); + d.resolve(); + return d.promise; + } + var ps = []; + + // Update all modules/functions. + var modulesUpdated = $q.defer(); + ps.push(modulesUpdated.promise); + dataSource.getModuleList().then((function(list) { + cache.moduleList = list; + + // Update module information. + var moduleFetches = []; + list.forEach(function(module) { + if (cache.modules[module.name]) { + return; + } + var moduleFetch = $q.defer(); + moduleFetches.push(moduleFetch.promise); + dataSource.getModule(module.name). + then(function(moduleInfo) { + cache.modules[module.name] = moduleInfo; + moduleFetch.resolve(); + }, function(e) { + moduleFetch.reject(e); + }); + }); + + // Update function lists for each module. + list.forEach(function(module) { + var cached = cache.moduleFunctionLists[module.name]; + var functionListFetch = $q.defer(); + moduleFetches.push(functionListFetch); + dataSource.getFunctionList(module.name, cached ? cached.version : 0). + then(function(result) { + if (cached) { + cached.version = result.version; + for (var n = 0; n < result.list.length; n++) { + cached.list.push(result.list[n]); + } + } else { + cached = cache.moduleFunctionLists[module.name] = { + version: result.version, + list: result.list + }; + } + functionListFetch.resolve(); + }, function(e) { + functionListFetch.reject(e); + }); + }); + + $q.all(moduleFetches).then(function() { + modulesUpdated.resolve(); + }, function(e) { + modulesUpdated.reject(); + }); + }).bind(this), function(e) { + modulesUpdated.reject(e); + }); + + // Update threads/thread states. + var threadsUpdated = $q.defer(); + ps.push(threadsUpdated.promise); + dataSource.getThreadStates().then((function(states) { + cache.threadStates = states; + cache.threadList = []; + for (var threadId in states) { + cache.threadList.push(states[threadId]); + } + threadsUpdated.resolve(); + }).bind(this), function(e) { + threadsUpdated.reject(e); + }); + + var d = $q.defer(); + $q.all(ps).then((function() { + d.resolve(); + }).bind(this), (function(e) { + d.reject(e); + }).bind(this)); + return d.promise; + }; + State.prototype.getModuleList = function() { + return this.cache_.moduleList; + }; + State.prototype.getModule = function(moduleName) { + return this.cache_.modules[moduleName] || null; + }; + State.prototype.getFunctionList = function(moduleName) { + var cached = this.cache_.moduleFunctionLists[moduleName]; + return cached ? cached.list : []; + }; + State.prototype.getFunction = function(address) { + return this.cache_.functions[address] || null; + }; + State.prototype.fetchFunction = function(address) { + var cache = this.cache_; + var d = $q.defer(); + var cached = cache.functions[address]; + if (cached) { + d.resolve(cached); + return d.promise; + } + var dataSource = this.session.dataSource; + if (!dataSource) { + d.reject(new Error('Not online.')); + return d.promise; + } + dataSource.getFunction(address).then(function(result) { + cache.functions[address] = result; + d.resolve(result); + }, function(e) { + d.reject(e); + }); + return d.promise; + } + Object.defineProperty(State.prototype, 'threadList', { + get: function() { + return this.cache_.threadList || []; + } + }); + State.prototype.getThreadStates = function() { + return this.cache_.threadStates || {}; + }; + State.prototype.getThreadState = function(threadId) { + return this.cache_.threadStates[threadId] || null; + }; + var Session = function(id, opt_dataSource) { this.id = id; @@ -22,6 +170,9 @@ module.service('Session', function( this.breakpointsById = {}; this.dataSource = opt_dataSource || null; + this.state = new State(this); + + this.activeThread = null; this.paused = false; @@ -144,6 +295,8 @@ module.service('Session', function( d.resolve(); return d.promise; } + this.state.clear(); + this.activeThread = null; this.dataSource = dataSource; this.dataSource.on('online', function() { @@ -165,28 +318,31 @@ module.service('Session', function( } ps.push(this.dataSource.addBreakpoints(breakpointList)); - // Fetch main module info. - // We need this for entry point info/etc. - var moduleInfoDeferred = $q.defer(); - this.dataSource.getModuleList().then(function(moduleList) { + // Perform a full sync. + var syncDeferred = $q.defer(); + ps.push(syncDeferred.promise); + this.state.sync().then((function() { + // Put a breakpoint at the entry point. + // TODO(benvanik): make an option? + var moduleList = this.state.getModuleList(); if (!moduleList.length) { - // Uh. - log.error('No modules loaded on startup!'); - moduleInfoDeferred.reject(new Error('No modules found')); + log.error('No modules found!'); + syncDeferred.reject(new Error('No modules found.')); return; } - moduleList.forEach(function(module) { - dataSource.getModule(module.name).then(function(moduleInfo) { - // Put a breakpoint at the entry point. - var entryPoint = moduleInfo.exeEntryPoint; - self.addTempBreakpoint(entryPoint, entryPoint); - moduleInfoDeferred.resolve(); - }); - }); - }, function(e) { - moduleInfoDeferred.reject(e); - }); - ps.push(moduleInfoDeferred.promise); + var moduleInfo = this.state.getModule(moduleList[0].name); + if (!moduleInfo) { + log.error('Main module not found!'); + syncDeferred.reject(new Error('Main module not found.')); + return; + } + var entryPoint = moduleInfo.exeEntryPoint; + self.addTempBreakpoint(entryPoint, entryPoint); + + syncDeferred.resolve(); + }).bind(this), (function(e) { + syncDeferred.reject(e); + }).bind(this)); $q.all(ps).then((function() { this.dataSource.makeReady().then(function() { @@ -258,16 +414,25 @@ module.service('Session', function( // Now paused! this.paused = true; - $rootScope.$emit('refresh'); + this.state.sync().then((function() { + // Switch active thread. + var thread = this.state.getThreadState(threadId); + this.activeThread = thread; - if (breakpointId) { - var breakpoint = this.breakpointsById[breakpointId]; - var thread = null; // TODO - if (!breakpoint) { - log.error('Breakpoint hit but not found'); + if (!breakpointId) { + // Just a general pause. + log.info('Execution paused.'); return; } + var breakpoint = this.breakpointsById[breakpointId]; + if (!breakpoint) { + log.error('Breakpoint hit but not found.'); + return; + } + + // TODO(benvanik): stash current breakpoint/thread/etc. + log.info('Breakpoint hit at 0x' + breakpoint.address.toString(16).toUpperCase() + '.'); @@ -279,10 +444,9 @@ module.service('Session', function( notify: true, reloadOnSearch: false }); - } else { - // Just a general pause. - log.info('Execution paused.'); - } + }).bind(this), (function(e) { + log.error('Unable to synchronize state,'); + }).bind(this)); }; Session.prototype.continueExecution = function() { diff --git a/src/alloy/runtime/debugger.cc b/src/alloy/runtime/debugger.cc index 6c537c131..3dbb7b906 100644 --- a/src/alloy/runtime/debugger.cc +++ b/src/alloy/runtime/debugger.cc @@ -76,6 +76,15 @@ int Debugger::ResumeAllThreads(bool force) { return result; } +void Debugger::ForEachThread(std::function callback) { + LockMutex(threads_lock_); + for (auto it = threads_.begin(); it != threads_.end(); ++it) { + ThreadState* thread_state = it->second; + callback(thread_state); + } + UnlockMutex(threads_lock_); +} + int Debugger::AddBreakpoint(Breakpoint* breakpoint) { // Add to breakpoints map. LockMutex(breakpoints_lock_); diff --git a/src/alloy/runtime/debugger.h b/src/alloy/runtime/debugger.h index 16e7d1129..ce87efd30 100644 --- a/src/alloy/runtime/debugger.h +++ b/src/alloy/runtime/debugger.h @@ -86,6 +86,8 @@ public: int ResumeThread(uint32_t thread_id); int ResumeAllThreads(bool force = false); + void ForEachThread(std::function callback); + int AddBreakpoint(Breakpoint* breakpoint); int RemoveBreakpoint(Breakpoint* breakpoint); void FindBreakpoints( diff --git a/src/alloy/runtime/thread_state.cc b/src/alloy/runtime/thread_state.cc index e662284da..32edf177e 100644 --- a/src/alloy/runtime/thread_state.cc +++ b/src/alloy/runtime/thread_state.cc @@ -22,7 +22,7 @@ __declspec(thread) ThreadState* thread_state_ = NULL; ThreadState::ThreadState(Runtime* runtime, uint32_t thread_id) : runtime_(runtime), memory_(runtime->memory()), - thread_id_(thread_id), + thread_id_(thread_id), name_(0), backend_data_(0), raw_context_(0) { if (thread_id_ == UINT_MAX) { // System thread. Assign the system thread ID with a high bit @@ -40,6 +40,19 @@ ThreadState::~ThreadState() { if (thread_state_ == this) { thread_state_ = NULL; } + if (name_) { + xe_free(name_); + } +} + +void ThreadState::set_name(const char* value) { + if (value == name_) { + return; + } + if (name_) { + xe_free(name_); + } + name_ = xestrdupa(value); } void ThreadState::Bind(ThreadState* thread_state) { diff --git a/src/alloy/runtime/thread_state.h b/src/alloy/runtime/thread_state.h index 2725747f1..26cd4566b 100644 --- a/src/alloy/runtime/thread_state.h +++ b/src/alloy/runtime/thread_state.h @@ -29,6 +29,8 @@ public: Runtime* runtime() const { return runtime_; } Memory* memory() const { return memory_; } uint32_t thread_id() const { return thread_id_; } + const char* name() const { return name_; } + void set_name(const char* value); void* backend_data() const { return backend_data_; } void* raw_context() const { return raw_context_; } @@ -45,6 +47,7 @@ protected: Runtime* runtime_; Memory* memory_; uint32_t thread_id_; + char* name_; void* backend_data_; void* raw_context_; }; diff --git a/src/alloy/string_buffer.h b/src/alloy/string_buffer.h index 7aa5a3b47..8b2b05d95 100644 --- a/src/alloy/string_buffer.h +++ b/src/alloy/string_buffer.h @@ -31,6 +31,7 @@ public: const char* GetString() const; char* ToString(); + char* EncodeBase64(); private: char* buffer_; diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index 05ee02b43..4b24f88e6 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -330,6 +330,17 @@ json_t* Processor::OnDebugRequest( } uint64_t address = (uint64_t)json_number_value(address_json); return DumpFunction(address, succeeded); + } else if (xestrcmpa(command, "get_thread_states") == 0) { + json_t* result = json_object(); + runtime_->debugger()->ForEachThread([&](ThreadState* thread_state) { + json_t* state_json = DumpThreadState((XenonThreadState*)thread_state); + char threadIdString[32]; + xesnprintfa( + threadIdString, XECOUNT(threadIdString), + "%d", thread_state->thread_id()); + json_object_set_new(result, threadIdString, state_json); + }); + return result; } else if (xestrcmpa(command, "add_breakpoints") == 0) { // breakpoints: [{}] json_t* breakpoints_json = json_object_get(request, "breakpoints"); @@ -699,6 +710,61 @@ json_t* Processor::DumpFunction(uint64_t address, bool& succeeded) { return fn_json; } +json_t* Processor::DumpThreadState(XenonThreadState* thread_state) { + json_t* result = json_object(); + + json_object_set_integer_new(result, "id", thread_state->thread_id()); + json_object_set_string_new(result, "name", thread_state->name()); + json_object_set_integer_new( + result, "stackAddress", thread_state->stack_address()); + json_object_set_integer_new( + result, "stackSize", thread_state->stack_size()); + json_object_set_integer_new( + result, "threadStateAddress", thread_state->thread_state_address()); + + json_t* context_json = json_object(); + auto context = thread_state->context(); + + json_object_set_new( + context_json, "lr", json_integer(context->lr)); + json_object_set_new( + context_json, "ctr", json_integer(context->ctr)); + + // xer + // cr* + // fpscr + + json_t* r_json = json_array(); + for (size_t n = 0; n < 32; n++) { + json_array_append_new(r_json, json_integer(context->r[n])); + } + json_object_set_new(context_json, "r", r_json); + + json_t* f_json = json_array(); + for (size_t n = 0; n < 32; n++) { + json_array_append_new(f_json, json_real(context->f[n])); + } + json_object_set_new(context_json, "f", f_json); + + json_t* v_json = json_array(); + for (size_t n = 0; n < 128; n++) { + auto& v = context->v[n]; + json_t* vec4_json = json_array(); + json_array_append_new(vec4_json, json_integer(v.ix)); + json_array_append_new(vec4_json, json_integer(v.iy)); + json_array_append_new(vec4_json, json_integer(v.iz)); + json_array_append_new(vec4_json, json_integer(v.iw)); + json_array_append_new(v_json, vec4_json); + } + json_object_set_new(context_json, "v", v_json); + + json_object_set_new(result, "context", context_json); + + // TODO(benvanik): callstack + + return result; +} + Processor::DebugClientState::DebugClientState(XenonRuntime* runtime) : runtime_(runtime) { breakpoints_lock_ = xe_mutex_alloc(10000); diff --git a/src/xenia/cpu/processor.h b/src/xenia/cpu/processor.h index 0b596b74a..d08912c88 100644 --- a/src/xenia/cpu/processor.h +++ b/src/xenia/cpu/processor.h @@ -67,6 +67,7 @@ public: private: json_t* DumpModule(XexModule* module, bool& succeeded); json_t* DumpFunction(uint64_t address, bool& succeeded); + json_t* DumpThreadState(XenonThreadState* thread_state); private: Emulator* emulator_; diff --git a/src/xenia/cpu/xenon_thread_state.h b/src/xenia/cpu/xenon_thread_state.h index 05eb278b2..58ed08308 100644 --- a/src/xenia/cpu/xenon_thread_state.h +++ b/src/xenia/cpu/xenon_thread_state.h @@ -29,6 +29,9 @@ public: size_t stack_size, uint64_t thread_state_address); virtual ~XenonThreadState(); + uint64_t stack_address() const { return stack_address_; } + size_t stack_size() const { return stack_size_; } + uint64_t thread_state_address() const { return thread_state_address_; } PPCContext* context() const { return context_; } virtual volatile int* suspend_flag_address() const; @@ -37,11 +40,9 @@ public: virtual void EnterSuspend(); private: - size_t stack_size_; - uint64_t thread_state_address; - uint32_t thread_id_; uint64_t stack_address_; + size_t stack_size_; uint64_t thread_state_address_; // NOTE: must be 64b aligned for SSE ops.