diff --git a/debugger/assets/styles/app.css b/debugger/assets/styles/app.css index 006be6480..f3b80a4df 100644 --- a/debugger/assets/styles/app.css +++ b/debugger/assets/styles/app.css @@ -156,21 +156,36 @@ body { right: 0; bottom: 0; padding: 5px; - overflow: scroll; + overflow-x: auto; + overflow-y: scroll; +} +.debugger-fnlist-list > table > tbody > tr > td { + padding: 0; + line-height: 1.2em; + font-family: monospace; + border: 0; } .debugger-fnlist-footer { order: 3; flex: 0 0 auto; padding: 5px; } -.debugger-fnview { +.debugger-fnview-outer { order: 2; flex: 1 1 auto; - display: flex; - flex-flow: column nowrap; + position: relative; border-left: 2px solid #ddd; border-right: 2px solid #ddd; } +.debugger-fnview { + position: absolute; + left: 0; + top: 0; + bottom: 0; + right: 0; + display: flex; + flex-flow: column nowrap; +} .debugger-fnview-header { order: 1; flex: 0 0 auto; diff --git a/debugger/assets/ui/code/code-tab.html b/debugger/assets/ui/code/code-tab.html index d5929bbc4..ea43d5694 100644 --- a/debugger/assets/ui/code/code-tab.html +++ b/debugger/assets/ui/code/code-tab.html @@ -1,75 +1,35 @@
debug header/toolbar/etc -
- fn
fn
fn + + + + +
{{fn.name}}
-
-
-
- function name - (0x80000000-0x80000000) -
-
- -
-
-
-
- -
-
- graph! -
-
- -
+
diff --git a/debugger/assets/ui/code/code-tab.js b/debugger/assets/ui/code/code-tab.js index 55fb8e14b..1dcbd7735 100644 --- a/debugger/assets/ui/code/code-tab.js +++ b/debugger/assets/ui/code/code-tab.js @@ -17,16 +17,42 @@ var module = angular.module('xe.ui.code', [ module.controller('CodeTabController', function( $rootScope, $scope, app, log) { + $scope.moduleList = []; + $scope.selectedModule = null; + $scope.functionList = []; $rootScope.$on('refresh', function() { var dataSource = app.session.dataSource; dataSource.getModuleList().then(function(list) { - console.log(list); + $scope.moduleList = list; + if (!$scope.selectedModule) { + if (list.length) { + $scope.selectModule(list[0]); + } + } else { + $scope.selectModule($scope.selectedModule); + } }, function(e) { - log('Unable to fetch module list'); + log.error('Unable to fetch module list'); }); console.log('refresh'); }); + + $scope.selectModule = function(module) { + var moduleChange = module != $scope.selectedModule; + $scope.selectedModule = module; + + if (moduleChange) { + $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'); + }); + }; }); diff --git a/debugger/assets/ui/code/function-view.html b/debugger/assets/ui/code/function-view.html index 980b207bb..884fcf884 100644 --- a/debugger/assets/ui/code/function-view.html +++ b/debugger/assets/ui/code/function-view.html @@ -1,2 +1,43 @@ -TODO: function -
+
+
+
+ + (0x{{fn.startAddress | hex32}}-0x{{fn.endAddress | hex32}}) +
+
+ +
+
+
+
+
+ +
+
+ graph! +
+
+ +
diff --git a/debugger/assets/ui/code/function-view.js b/debugger/assets/ui/code/function-view.js new file mode 100644 index 000000000..c9d46163d --- /dev/null +++ b/debugger/assets/ui/code/function-view.js @@ -0,0 +1,33 @@ +/** + ****************************************************************************** + * 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.functionView', [ + 'xe.log', + 'xe.session' +]); + + +module.controller('FunctionViewController', function( + $rootScope, $scope, app, log) { + $scope.codeType = 'ppc'; + + function refresh() { + var dataSource = app.session.dataSource; + + dataSource.getFunction($scope.functionAddress).then(function(fn) { + $scope.fn = fn; + }, function(e) { + log.error('Unable to fetch function'); + }); + }; + $rootScope.$on('refresh', refresh); + $scope.$watch('functionAddress', refresh); +}); diff --git a/debugger/index.html b/debugger/index.html index ff21331d1..41b7f67c6 100644 --- a/debugger/index.html +++ b/debugger/index.html @@ -26,12 +26,14 @@ + + diff --git a/debugger/src/app.js b/debugger/src/app.js index f345f7867..d53c03cd0 100644 --- a/debugger/src/app.js +++ b/debugger/src/app.js @@ -14,10 +14,12 @@ var module = angular.module('app', [ 'ui.router', 'xe.datasources', 'xe.directives', + 'xe.filters', 'xe.log', 'xe.router', 'xe.session', 'xe.ui.code', + 'xe.ui.code.functionView', 'xe.ui.console', 'xe.ui.navbar' ]); diff --git a/debugger/src/datasources.js b/debugger/src/datasources.js index e7bc13a55..ac7b14475 100644 --- a/debugger/src/datasources.js +++ b/debugger/src/datasources.js @@ -63,17 +63,17 @@ module.service('DataSource', function($q) { }); }; - DataSource.prototype.getModule = function(moduleId) { + DataSource.prototype.getModule = function(moduleName) { return this.issue({ command: 'cpu.get_module', - moduleId: moduleId + module: moduleName }); }; - DataSource.prototype.getFunctionList = function(moduleId) { + DataSource.prototype.getFunctionList = function(moduleName) { return this.issue({ command: 'cpu.get_function_list', - moduleId: moduleId + module: moduleName }); }; diff --git a/debugger/src/directives.js b/debugger/src/directives.js index 8dca19af3..8790acf7c 100644 --- a/debugger/src/directives.js +++ b/debugger/src/directives.js @@ -24,3 +24,17 @@ module.directive('uiEnter', function() { }); }; }); + +module.directive('uiEscape', function() { + return function($scope, element, attrs) { + element.bind("keydown keypress", function(e) { + if(e.which === 27) { + $scope.$apply(function(){ + $scope.$eval(attrs.uiEscape); + }); + e.preventDefault(); + } + }); + }; +}); + diff --git a/debugger/src/filters.js b/debugger/src/filters.js new file mode 100644 index 000000000..0ebf9eb78 --- /dev/null +++ b/debugger/src/filters.js @@ -0,0 +1,23 @@ +/** + ****************************************************************************** + * 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.filters', []); + + +module.filter("hex32", function() { + return function(number) { + if (number !== null && number !== undefined) { + var str = "" + number.toString(16).toUpperCase(); + while (str.length < 8) str = "0" + str; + return str; + } + }; +}); diff --git a/debugger/src/router.js b/debugger/src/router.js index 07e261ae7..c47405504 100644 --- a/debugger/src/router.js +++ b/debugger/src/router.js @@ -26,7 +26,7 @@ module.config(function($stateProvider, $urlRouterProvider) { templateUrl: 'assets/ui/session.html', resolve: { app: 'app', - session: function($stateParams, $urlRouter, $state, $q, $timeout, + session: function($stateParams, $state, $q, Session, app) { // If we are given a session we assume the user is trying to connect to // it. Attempt that now. If we fail we redirect to home, otherwise we @@ -100,7 +100,11 @@ module.config(function($stateProvider, $urlRouterProvider) { $stateProvider.state('session.code.function', { url: '/:module/:function', templateUrl: 'assets/ui/code/function-view.html', - controller: function($stateParams) { + controller: function($scope, $stateParams) { + $scope.moduleName = $stateParams.module; + $scope.functionAddress = parseInt($stateParams.function, 16); + $scope.$emit('xxx'); + $scope.$broadcast('yyy'); }, onEnter: function() {}, onExit: function() {} diff --git a/src/alloy/runtime/module.cc b/src/alloy/runtime/module.cc index 385d5ce16..6d7101dd5 100644 --- a/src/alloy/runtime/module.cc +++ b/src/alloy/runtime/module.cc @@ -152,3 +152,15 @@ SymbolInfo::Status Module::DefineFunction(FunctionInfo* symbol_info) { SymbolInfo::Status Module::DefineVariable(VariableInfo* symbol_info) { return DefineSymbol((SymbolInfo*)symbol_info); } + +void Module::ForEachFunction(std::function callback) { + LockMutex(lock_); + for (SymbolMap::iterator it = map_.begin(); + it != map_.end(); ++it) { + if (it->second->type() == SymbolInfo::TYPE_FUNCTION) { + FunctionInfo* info = (FunctionInfo*)it->second; + callback(info); + } + } + UnlockMutex(lock_); +} diff --git a/src/alloy/runtime/module.h b/src/alloy/runtime/module.h index 42b8a5f16..c294a66b1 100644 --- a/src/alloy/runtime/module.h +++ b/src/alloy/runtime/module.h @@ -10,6 +10,8 @@ #ifndef ALLOY_RUNTIME_MODULE_H_ #define ALLOY_RUNTIME_MODULE_H_ +#include + #include #include #include @@ -41,6 +43,8 @@ public: SymbolInfo::Status DefineFunction(FunctionInfo* symbol_info); SymbolInfo::Status DefineVariable(VariableInfo* symbol_info); + void ForEachFunction(std::function callback); + private: SymbolInfo::Status DeclareSymbol( SymbolInfo::Type type, uint64_t address, SymbolInfo** out_symbol_info); diff --git a/src/alloy/runtime/runtime.cc b/src/alloy/runtime/runtime.cc index 2ee5771ce..de3627311 100644 --- a/src/alloy/runtime/runtime.cc +++ b/src/alloy/runtime/runtime.cc @@ -116,6 +116,29 @@ int Runtime::AddModule(Module* module) { return 0; } +Module* Runtime::GetModule(const char* name) { + Module* result = NULL; + LockMutex(modules_lock_); + for (ModuleList::iterator it = modules_.begin(); + it != modules_.end(); ++it) { + Module* module = *it; + if (xestrcmpa(module->name(), name) == 0) { + result = module; + break; + } + } + UnlockMutex(modules_lock_); + return result; +} + +Runtime::ModuleList Runtime::GetModules() { + ModuleList clone; + LockMutex(modules_lock_); + clone = modules_; + UnlockMutex(modules_lock_); + return clone; +} + int Runtime::ResolveFunction(uint64_t address, Function** out_function) { *out_function = NULL; Entry* entry; diff --git a/src/alloy/runtime/runtime.h b/src/alloy/runtime/runtime.h index 59ab27790..df6b96782 100644 --- a/src/alloy/runtime/runtime.h +++ b/src/alloy/runtime/runtime.h @@ -26,6 +26,9 @@ namespace runtime { class Runtime { +public: + typedef std::vector ModuleList; + public: Runtime(Memory* memory); virtual ~Runtime(); @@ -40,6 +43,8 @@ public: int Initialize(frontend::Frontend* frontend, backend::Backend* backend = 0); int AddModule(Module* module); + Module* GetModule(const char* name); + ModuleList GetModules(); int LookupFunctionInfo(uint64_t address, FunctionInfo** out_symbol_info); int ResolveFunction(uint64_t address, Function** out_function); @@ -60,7 +65,6 @@ protected: EntryTable entry_table_; Mutex* modules_lock_; - typedef std::vector ModuleList; ModuleList modules_; RegisterAccessCallbacks* access_callbacks_; diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index e1a09624d..8c40d14ae 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -14,6 +14,7 @@ #include #include #include +#include using namespace alloy; @@ -169,5 +170,90 @@ uint64_t Processor::ExecuteInterrupt( json_t* Processor::OnDebugRequest( const char* command, json_t* request, bool& succeeded) { succeeded = true; - return json_null(); + if (xestrcmpa(command, "get_module_list") == 0) { + json_t* list = json_array(); + Runtime::ModuleList modules = runtime_->GetModules(); + for (Runtime::ModuleList::iterator it = modules.begin(); + it != modules.end(); ++it) { + XexModule* module = (XexModule*)(*it); + json_t* module_json = json_object(); + json_t* module_name_json = json_string(module->name()); + json_object_set_new(module_json, "name", module_name_json); + json_array_append_new(list, module_json); + } + return list; + /*} else if (xestrcmpa(command, "get_module") == 0) { + return json_null();*/ + } else if (xestrcmpa(command, "get_function_list") == 0) { + json_t* module_name_json = json_object_get(request, "module"); + if (!module_name_json || !json_is_string(module_name_json)) { + succeeded = false; + return json_string("Module name not specified"); + } + const char* module_name = json_string_value(module_name_json); + XexModule* module = (XexModule*)runtime_->GetModule(module_name); + if (!module) { + succeeded = false; + return json_string("Module not found"); + } + json_t* list = json_array(); + module->ForEachFunction([&](FunctionInfo* info) { + json_t* fn_json = json_object(); + // TODO(benvanik): get name + char name_buffer[32]; + xesnprintfa(name_buffer, XECOUNT(name_buffer), "sub_%.8X", + info->address()); + json_t* name_json = json_string(name_buffer); + json_object_set_new(fn_json, "name", name_json); + json_t* address_json = json_integer(info->address()); + json_object_set_new(fn_json, "address", address_json); + json_t* link_status_json = json_integer(info->status()); + json_object_set_new(fn_json, "linkStatus", link_status_json); + json_array_append_new(list, fn_json); + }); + return list; + } else if (xestrcmpa(command, "get_function") == 0) { + json_t* address_json = json_object_get(request, "address"); + if (!address_json || !json_is_number(address_json)) { + succeeded = false; + return json_string("Function address not specified"); + } + uint64_t address = (uint64_t)json_number_value(address_json); + + FunctionInfo* info; + if (runtime_->LookupFunctionInfo(address, &info)) { + succeeded = false; + return json_string("Function not found"); + } + + // Demand a new function with all debug info retained. + // If we ever wanted absolute x64 addresses/etc we could + // use the x64 from the function in the symbol table. + Function* fn; + if (runtime_->frontend()->DefineFunction(info, &fn)) { + succeeded = false; + return json_string("Unable to resolve function"); + } + + json_t* fn_json = json_object(); + // TODO(benvanik): get name + char name_buffer[32]; + xesnprintfa(name_buffer, XECOUNT(name_buffer), "sub_%.8X", + info->address()); + json_t* name_json = json_string(name_buffer); + json_object_set_new(fn_json, "name", name_json); + json_t* start_address_json = json_integer(info->address()); + json_object_set_new(fn_json, "startAddress", start_address_json); + json_t* end_address_json = json_integer(info->end_address()); + json_object_set_new(fn_json, "endAddress", end_address_json); + json_t* link_status_json = json_integer(info->status()); + json_object_set_new(fn_json, "linkStatus", link_status_json); + + delete fn; + + return fn_json; + } else { + succeeded = false; + return json_string("Unknown command"); + } }