From dcd9f8b6fff866366ecd2ea784db156a1d003e91 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Tue, 24 Dec 2013 17:25:29 -0800 Subject: [PATCH] Module info in json. --- debugger/assets/styles/app.css | 4 + debugger/assets/ui/code/code-tab.js | 16 +- debugger/assets/ui/code/module-info.html | 60 +--- debugger/assets/ui/code/module-info.js | 5 +- src/xenia/cpu/processor.cc | 372 +++++++++++++++++++---- src/xenia/cpu/processor.h | 5 + src/xenia/cpu/xex_module.h | 2 + 7 files changed, 339 insertions(+), 125 deletions(-) diff --git a/debugger/assets/styles/app.css b/debugger/assets/styles/app.css index b907522b9..e64d2b08a 100644 --- a/debugger/assets/styles/app.css +++ b/debugger/assets/styles/app.css @@ -321,6 +321,9 @@ body { display: block; pointer-events: none; } +.debugger-module-info .modal-dialog { + width: 60vw; +} .debugger-module-info.fade .modal-dialog { -webkit-transition: none; -webkit-transform: translate(0,0); @@ -329,6 +332,7 @@ body { pointer-events: auto; } .debugger-module-info .modal-body { + max-width: 60vw; max-height: 80vh; overflow-y: auto; } diff --git a/debugger/assets/ui/code/code-tab.js b/debugger/assets/ui/code/code-tab.js index c69e71826..67e0d2d2f 100644 --- a/debugger/assets/ui/code/code-tab.js +++ b/debugger/assets/ui/code/code-tab.js @@ -66,12 +66,16 @@ module.controller('CodeTabController', function( var modalInstance = $modal.open({ templateUrl: 'assets/ui/code/module-info.html', controller: 'ModuleInfoController', - windowClass: 'debugger-module-info' -// resolve: { -// items: function () { -// return $scope.items; -// } -// } + windowClass: 'debugger-module-info', + resolve: { + moduleName: function() { + return $scope.selectedModule.name; + }, + moduleInfo: function() { + return app.session.dataSource.getModule( + $scope.selectedModule.name); + } + } }); modalInstance.result.then(function() { }, function () { diff --git a/debugger/assets/ui/code/module-info.html b/debugger/assets/ui/code/module-info.html index 84f20d235..061f44473 100644 --- a/debugger/assets/ui/code/module-info.html +++ b/debugger/assets/ui/code/module-info.html @@ -2,66 +2,10 @@ diff --git a/debugger/assets/ui/code/module-info.js b/debugger/assets/ui/code/module-info.js index 09d8db763..53a0da773 100644 --- a/debugger/assets/ui/code/module-info.js +++ b/debugger/assets/ui/code/module-info.js @@ -17,7 +17,10 @@ var module = angular.module('xe.ui.code.moduleInfo', [ module.controller('ModuleInfoController', function( - $rootScope, $scope, $modal, log) { + $rootScope, $scope, $modal, log, moduleName, moduleInfo) { + $scope.moduleName = moduleName; + $scope.moduleInfo = moduleInfo; + $scope.close = function() { $scope.$close(null); }; diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index 56c91c31e..6884080f0 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -236,8 +237,7 @@ json_t* Processor::OnDebugRequest( 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) { + for (auto 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()); @@ -246,7 +246,18 @@ json_t* Processor::OnDebugRequest( } return list; } else if (xestrcmpa(command, "get_module") == 0) { - return json_null(); + 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"); + } + return DumpModule(module, succeeded); } 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)) { @@ -285,63 +296,7 @@ json_t* Processor::OnDebugRequest( 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, true, &fn)) { - succeeded = false; - return json_string("Unable to resolve function"); - } - DebugInfo* debug_info = fn->debug_info(); - if (!debug_info) { - succeeded = false; - return json_string("No debug info present for function"); - } - - json_t* fn_json = json_object(); - const char* name = info->name(); - char name_buffer[32]; - if (!name) { - xesnprintfa(name_buffer, XECOUNT(name_buffer), "sub_%.8X", - info->address()); - name = name_buffer; - } - json_t* name_json = json_string(name); - 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); - - json_t* disasm_json = json_object(); - json_t* disasm_str_json; - disasm_str_json = json_loads(debug_info->source_json(), 0, NULL); - json_object_set_new(disasm_json, "source", disasm_str_json); - disasm_str_json = json_string(debug_info->raw_hir_disasm()); - json_object_set_new(disasm_json, "rawHir", disasm_str_json); - disasm_str_json = json_string(debug_info->hir_disasm()); - json_object_set_new(disasm_json, "hir", disasm_str_json); - disasm_str_json = json_string(debug_info->raw_lir_disasm()); - json_object_set_new(disasm_json, "rawLir", disasm_str_json); - disasm_str_json = json_string(debug_info->lir_disasm()); - json_object_set_new(disasm_json, "lir", disasm_str_json); - disasm_str_json = json_string(debug_info->machine_code_disasm()); - json_object_set_new(disasm_json, "machineCode", disasm_str_json); - json_object_set_new(fn_json, "disasm", disasm_json); - - delete fn; - - return fn_json; + return DumpFunction(address, succeeded); } else if (xestrcmpa(command, "add_breakpoints") == 0) { // breakpoints: [{}] json_t* breakpoints_json = json_object_get(request, "breakpoints"); @@ -442,6 +397,303 @@ json_t* Processor::OnDebugRequest( } } +json_t* json_object_set_string_new( + json_t* object, const char* key, const char* value) { + json_t* value_json = json_string(value); + json_object_set_new(object, key, value_json); + return value_json; +} + +json_t* json_object_set_string_format_new( + json_t* object, const char* key, const char* format, ...) { + char buffer[1024]; + va_list args; + va_start(args, format); + xevsnprintfa(buffer, XECOUNT(buffer), format, args); + va_end(args); + json_t* value_json = json_string(buffer); + json_object_set_new(object, key, value_json); + return value_json; +} + +json_t* json_object_set_integer_new( + json_t* object, const char* key, json_int_t value) { + json_t* value_json = json_integer(value); + json_object_set_new(object, key, value_json); + return value_json; +} + +json_t* Processor::DumpModule(XexModule* module, bool& succeeded) { + auto xex = module->xex(); + auto header = xe_xex2_get_header(xex); + + auto export_resolver = runtime_->export_resolver(); + + json_t* module_json = json_object(); + + json_object_set_integer_new( + module_json, "moduleFlags", header->module_flags); + json_object_set_integer_new( + module_json, "systemFlags", header->system_flags); + json_object_set_integer_new( + module_json, "exeAddress", header->exe_address); + json_object_set_integer_new( + module_json, "exeEntryPoint", header->exe_entry_point); + json_object_set_integer_new( + module_json, "exeStackSize", header->exe_stack_size); + json_object_set_integer_new( + module_json, "exeHeapSize", header->exe_heap_size); + + json_t* exec_info_json = json_object(); + json_object_set_integer_new( + exec_info_json, "mediaId", header->execution_info.media_id); + json_object_set_string_format_new( + exec_info_json, "version", "%d.%d.%d.%d", + header->execution_info.version.major, + header->execution_info.version.minor, + header->execution_info.version.build, + header->execution_info.version.qfe); + json_object_set_string_format_new( + exec_info_json, "baseVersion", "%d.%d.%d.%d", + header->execution_info.base_version.major, + header->execution_info.base_version.minor, + header->execution_info.base_version.build, + header->execution_info.base_version.qfe); + json_object_set_integer_new( + exec_info_json, "titleId", header->execution_info.title_id); + json_object_set_integer_new( + exec_info_json, "platform", header->execution_info.platform); + json_object_set_integer_new( + exec_info_json, "executableTable", header->execution_info.executable_table); + json_object_set_integer_new( + exec_info_json, "discNumber", header->execution_info.disc_number); + json_object_set_integer_new( + exec_info_json, "discCount", header->execution_info.disc_count); + json_object_set_integer_new( + exec_info_json, "savegameId", header->execution_info.savegame_id); + json_object_set_new(module_json, "executionInfo", exec_info_json); + + json_t* loader_info_json = json_object(); + json_object_set_integer_new( + loader_info_json, "imageFlags", header->loader_info.image_flags); + json_object_set_integer_new( + loader_info_json, "gameRegions", header->loader_info.game_regions); + json_object_set_integer_new( + loader_info_json, "mediaFlags", header->loader_info.media_flags); + json_object_set_new(module_json, "loaderInfo", loader_info_json); + + json_t* tls_info_json = json_object(); + json_object_set_integer_new( + tls_info_json, "slotCount", header->tls_info.slot_count); + json_object_set_integer_new( + tls_info_json, "dataSize", header->tls_info.data_size); + json_object_set_integer_new( + tls_info_json, "rawDataAddress", header->tls_info.raw_data_address); + json_object_set_integer_new( + tls_info_json, "rawDataSize", header->tls_info.raw_data_size); + json_object_set_new(module_json, "tlsInfo", tls_info_json); + + json_t* headers_json = json_array(); + for (size_t n = 0; n < header->header_count; n++) { + auto opt_header = &header->headers[n]; + json_t* header_entry_json = json_object(); + json_object_set_integer_new( + header_entry_json, "key", opt_header->key); + json_object_set_integer_new( + header_entry_json, "offset", opt_header->offset); + json_object_set_integer_new( + header_entry_json, "length", opt_header->length); + json_object_set_integer_new( + header_entry_json, "value", opt_header->value); + json_array_append_new(headers_json, header_entry_json); + } + json_object_set_new(module_json, "headers", headers_json); + + // TODO(benvanik): resources. + json_t* resource_info_json = json_object(); + json_object_set_integer_new( + resource_info_json, "address", header->resource_info.address); + json_object_set_integer_new( + resource_info_json, "size", header->resource_info.size); + json_object_set_new(module_json, "resourceInfo", resource_info_json); + + json_t* sections_json = json_array(); + for (size_t n = 0, i = 0; n < header->section_count; n++) { + const xe_xex2_section_t* section = &header->sections[n]; + const char* type = "unknown"; + switch (section->info.type) { + case XEX_SECTION_CODE: + type = "code"; + break; + case XEX_SECTION_DATA: + type = "rwdata"; + break; + case XEX_SECTION_READONLY_DATA: + type = "rodata"; + break; + } + const size_t start_address = + header->exe_address + (i * xe_xex2_section_length); + const size_t end_address = + start_address + (section->info.page_count * xe_xex2_section_length); + json_t* section_entry_json = json_object(); + json_object_set_string_new( + section_entry_json, "type", type); + json_object_set_integer_new( + section_entry_json, "pageCount", section->info.page_count); + json_object_set_integer_new( + section_entry_json, "startAddress", start_address); + json_object_set_integer_new( + section_entry_json, "endAddress", end_address); + json_object_set_integer_new( + section_entry_json, "totalLength", + section->info.page_count * xe_xex2_section_length); + json_array_append_new(sections_json, section_entry_json); + i += section->info.page_count; + } + json_object_set_new(module_json, "sections", sections_json); + + json_t* static_libraries_json = json_array(); + for (size_t n = 0; n < header->static_library_count; n++) { + const xe_xex2_static_library_t *library = &header->static_libraries[n]; + json_t* static_library_entry_json = json_object(); + json_object_set_string_new( + static_library_entry_json, "name", library->name); + json_object_set_string_format_new( + static_library_entry_json, "version", "%d.%d.%d.%d", + library->major, library->minor, library->build, library->qfe); + json_array_append_new(static_libraries_json, static_library_entry_json); + } + json_object_set_new(module_json, "staticLibraries", static_libraries_json); + + json_t* library_imports_json = json_array(); + for (size_t n = 0; n < header->import_library_count; n++) { + const xe_xex2_import_library_t* library = &header->import_libraries[n]; + xe_xex2_import_info_t* import_infos; + size_t import_info_count; + if (xe_xex2_get_import_infos( + xex, library, &import_infos, &import_info_count)) { + continue; + } + + json_t* import_library_json = json_object(); + json_object_set_string_new( + import_library_json, "name", library->name); + json_object_set_string_format_new( + import_library_json, "version", "%d.%d.%d.%d", + library->version.major, library->version.minor, + library->version.build, library->version.qfe); + json_object_set_string_format_new( + import_library_json, "minVersion", "%d.%d.%d.%d", + library->min_version.major, library->min_version.minor, + library->min_version.build, library->min_version.qfe); + + json_t* imports_json = json_array(); + for (size_t m = 0; m < import_info_count; m++) { + auto info = &import_infos[m]; + auto kernel_export = export_resolver->GetExportByOrdinal( + library->name, info->ordinal); + json_t* import_json = json_object(); + if (kernel_export) { + json_object_set_string_new( + import_json, "name", kernel_export->name); + json_object_set_new( + import_json, "implemented", + kernel_export->is_implemented ? json_true() : json_false()); + } else { + json_object_set_new(import_json, "name", json_null()); + json_object_set_new(import_json, "implemented", json_false()); + } + json_object_set_integer_new( + import_json, "ordinal", info->ordinal); + json_object_set_integer_new( + import_json, "valueAddress", info->value_address); + if (kernel_export && kernel_export->type == KernelExport::Variable) { + json_object_set_string_new( + import_json, "type", "function"); + } else if (kernel_export) { + json_object_set_string_new( + import_json, "type", "function"); + json_object_set_integer_new( + import_json, "thunkAddress", info->thunk_address); + } else { + json_object_set_string_new( + import_json, "type", "unknown"); + } + json_array_append_new(imports_json, import_json); + } + json_object_set_new(import_library_json, "imports", imports_json); + + json_array_append_new(library_imports_json, import_library_json); + xe_free(import_infos); + } + json_object_set_new(module_json, "libraryImports", library_imports_json); + + // TODO(benvanik): exports? + + return module_json; +} + +json_t* Processor::DumpFunction(uint64_t address, bool& succeeded) { + 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, true, &fn)) { + succeeded = false; + return json_string("Unable to resolve function"); + } + DebugInfo* debug_info = fn->debug_info(); + if (!debug_info) { + succeeded = false; + return json_string("No debug info present for function"); + } + + json_t* fn_json = json_object(); + const char* name = info->name(); + char name_buffer[32]; + if (!name) { + xesnprintfa(name_buffer, XECOUNT(name_buffer), "sub_%.8X", + info->address()); + name = name_buffer; + } + json_t* name_json = json_string(name); + 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); + + json_t* disasm_json = json_object(); + json_t* disasm_str_json; + disasm_str_json = json_loads(debug_info->source_json(), 0, NULL); + json_object_set_new(disasm_json, "source", disasm_str_json); + disasm_str_json = json_string(debug_info->raw_hir_disasm()); + json_object_set_new(disasm_json, "rawHir", disasm_str_json); + disasm_str_json = json_string(debug_info->hir_disasm()); + json_object_set_new(disasm_json, "hir", disasm_str_json); + disasm_str_json = json_string(debug_info->raw_lir_disasm()); + json_object_set_new(disasm_json, "rawLir", disasm_str_json); + disasm_str_json = json_string(debug_info->lir_disasm()); + json_object_set_new(disasm_json, "lir", disasm_str_json); + disasm_str_json = json_string(debug_info->machine_code_disasm()); + json_object_set_new(disasm_json, "machineCode", disasm_str_json); + json_object_set_new(fn_json, "disasm", disasm_json); + + delete fn; + + succeeded = true; + return fn_json; +} + 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 3232187c9..0b596b74a 100644 --- a/src/xenia/cpu/processor.h +++ b/src/xenia/cpu/processor.h @@ -22,6 +22,7 @@ XEDECLARECLASS1(xe, ExportResolver); XEDECLARECLASS2(xe, cpu, XenonMemory); XEDECLARECLASS2(xe, cpu, XenonRuntime); XEDECLARECLASS2(xe, cpu, XenonThreadState); +XEDECLARECLASS2(xe, cpu, XexModule); namespace xe { @@ -63,6 +64,10 @@ public: uint32_t client_id, const char* command, json_t* request, bool& succeeded); +private: + json_t* DumpModule(XexModule* module, bool& succeeded); + json_t* DumpFunction(uint64_t address, bool& succeeded); + private: Emulator* emulator_; ExportResolver* export_resolver_; diff --git a/src/xenia/cpu/xex_module.h b/src/xenia/cpu/xex_module.h index ae2f5b0db..96c7395af 100644 --- a/src/xenia/cpu/xex_module.h +++ b/src/xenia/cpu/xex_module.h @@ -26,6 +26,8 @@ public: XexModule(XenonRuntime* runtime); virtual ~XexModule(); + xe_xex2_ref xex() const { return xex_; } + int Load(const char* name, const char* path, xe_xex2_ref xex); virtual const char* name() const { return name_; }