Threads displayed.

This commit is contained in:
Ben Vanik 2013-12-25 17:31:53 -08:00
parent d368e0cb74
commit a1da55a006
19 changed files with 468 additions and 131 deletions

View File

@ -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;

View File

@ -1,18 +1,18 @@
<div class="debugger-main" ng-controller="CodeTabController">
<div class="debugger-header">
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-success" ng-click="app.session.continueExecution()" ng-disabled="!app.session.dataSource || !app.session.paused">
<button type="button" class="btn btn-success" ng-click="app.session.continueExecution()" ng-disabled="!app.session.paused">
<span class="glyphicon glyphicon-play"></span>
</button>
<button type="button" class="btn btn-danger" ng-click="app.session.breakExecution()" ng-disabled="!app.session.dataSource || app.session.paused">
<button type="button" class="btn btn-danger" ng-click="app.session.breakExecution()" ng-disabled="app.session.paused">
<span class="glyphicon glyphicon-pause"></span>
</button>
</div>
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-default" ng-click="showLocation()" ng-disabled="!app.session.dataSource || !app.session.paused">
<button type="button" class="btn btn-default" ng-click="showLocation()" ng-disabled="!app.session.paused">
<span class="glyphicon glyphicon glyphicon-arrow-right"></span>
</button>
<button type="button" class="btn btn-default" ng-click="app.session.stepNext()" ng-disabled="!app.session.dataSource || !app.session.paused">
<button type="button" class="btn btn-default" ng-click="app.session.stepNext()" ng-disabled="!app.session.paused">
<span class="glyphicon glyphicon-step-forward"></span>
</button>
</div>
@ -21,7 +21,7 @@
<div class="debugger-fnlist">
<div class="debugger-fnlist-header">
<div class="debugger-fnlist-header-left btn-group btn-group-xs full-width">
<button type="button" class="btn btn-default dropdown-toggle full-width" data-toggle="dropdown">
<button type="button" class="btn btn-default dropdown-toggle left-align full-width" data-toggle="dropdown">
{{selectedModule.name}} <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
@ -29,7 +29,7 @@
</ul>
</div>
<div class="debugger-fnlist-header-right btn-group btn-group-xs">
<button type="button" class="btn btn-default" ng-click="showModuleInfo(selectedModule)">
<button type="button" class="btn btn-default" ng-click="showModuleInfo()">
Info
</button>
</div>
@ -52,15 +52,21 @@
<div class="debugger-fnview-outer" ui-view></div>
<div class="debugger-tools">
<div class="debugger-tools-threads">
<div class="btn-group btn-group-xs full-width">
<button type="button" class="btn btn-default dropdown-toggle full-width" data-toggle="dropdown">
thread 0 <span class="caret"></span>
<div class="debugger-tools-threads-header-left btn-group btn-group-xs full-width">
<button type="button" class="btn btn-default left-align dropdown-toggle full-width" data-toggle="dropdown">
Thread {{app.session.activeThread.id}}: {{app.session.activeThread.name}} <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a href="#">thread 1</a></li>
<li><a href="#">thread 2</a></li>
<li ng-repeat="thread in app.session.state.threadList | orderBy:'id'">
<a href="" ng-click="app.session.activeThread = thread;">Thread {{thread.id}}: {{thread.name}}</a>
</li>
</ul>
</div>
<div class="debugger-tools-threads-header-right btn-group btn-group-xs">
<button type="button" class="btn btn-default" ng-click="showThreadInfo()">
Info
</button>
</div>
</div>
<div class="debugger-tools-callstack">
callstack

View File

@ -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;
}
}
});
};

View File

@ -1,7 +1,7 @@
<div class="debugger-fnview" ng-controller="FunctionViewController">
<div class="debugger-fnview-header">
<div class="debugger-fnview-header-left">
<span class="debugger-fnview-header-name" ng-bind="fn.name"></span>
<span class="debugger-fnview-header-name" ng-bind="fn.name"></span><br/>
<span class="debugger-fnview-header-address">(0x{{fn.startAddress | hex32}}-0x{{fn.endAddress | hex32}})</span>
</div>
<div class="debugger-fnview-header-right">
@ -14,6 +14,7 @@
<button type="button" class="btn btn-default" ng-model="codeType" btn-radio="'lir'">LIR</button>
<button type="button" class="btn btn-default" ng-model="codeType" btn-radio="'machineCode'">MC</button>
</div>
<!--
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-default">1</button>
<button type="button" class="btn btn-default">2</button>
@ -28,6 +29,7 @@
</ul>
</div>
</div>
-->
</div>
</div>
</div>

View File

@ -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);

View File

@ -0,0 +1,35 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" ng-click="close()">&times;</button>
<h4 class="modal-title">Thread {{ thread.id }}: {{ thread.name }}</h4>
</div>
<div class="modal-body">
<table class="table debugger-module-info-outer-table">
<tbody>
<tr>
<td>Thread</td>
<td>
<table class="table table-hover table-condensed debugger-module-info-inner-table">
<tbody>
<tr>
<td>Stack Address</td>
<td><a xe-memref="{{ thread.stackAddress | hex32 }}" ng-click="$close()">{{ thread.stackAddress | hex32 }}</a></td>
</tr>
<tr>
<td>Stack Size</td>
<td>{{ thread.stackSize }}b</td>
</tr>
<tr>
<td>State Address</td>
<td><a xe-memref="{{ thread.threadStateAddress | hex32 }}" ng-click="$close()">{{ thread.threadStateAddress | hex32 }}</a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>

View File

@ -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);
};
});

View File

@ -35,6 +35,7 @@
<script src="assets/ui/code/code-tab.js"></script>
<script src="assets/ui/code/function-view.js"></script>
<script src="assets/ui/code/module-info.js"></script>
<script src="assets/ui/code/thread-info.js"></script>
<script src="debugger.js"></script>
</body>

View File

@ -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'
]);

View File

@ -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]);
};

View File

@ -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() {

View File

@ -76,6 +76,15 @@ int Debugger::ResumeAllThreads(bool force) {
return result;
}
void Debugger::ForEachThread(std::function<void(ThreadState*)> 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_);

View File

@ -86,6 +86,8 @@ public:
int ResumeThread(uint32_t thread_id);
int ResumeAllThreads(bool force = false);
void ForEachThread(std::function<void (ThreadState*)> callback);
int AddBreakpoint(Breakpoint* breakpoint);
int RemoveBreakpoint(Breakpoint* breakpoint);
void FindBreakpoints(

View File

@ -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) {

View File

@ -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_;
};

View File

@ -31,6 +31,7 @@ public:
const char* GetString() const;
char* ToString();
char* EncodeBase64();
private:
char* buffer_;

View File

@ -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);

View File

@ -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_;

View File

@ -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.