Threads displayed.
This commit is contained in:
parent
d368e0cb74
commit
a1da55a006
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()">×</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>
|
|
@ -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);
|
||||
};
|
||||
});
|
|
@ -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>
|
||||
|
|
|
@ -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'
|
||||
]);
|
||||
|
|
|
@ -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]);
|
||||
};
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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_);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
@ -31,6 +31,7 @@ public:
|
|||
|
||||
const char* GetString() const;
|
||||
char* ToString();
|
||||
char* EncodeBase64();
|
||||
|
||||
private:
|
||||
char* buffer_;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue