diff --git a/src/Cafe/OS/RPL/rpl_symbol_storage.cpp b/src/Cafe/OS/RPL/rpl_symbol_storage.cpp index 5d3046e3..21832399 100644 --- a/src/Cafe/OS/RPL/rpl_symbol_storage.cpp +++ b/src/Cafe/OS/RPL/rpl_symbol_storage.cpp @@ -73,6 +73,10 @@ RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolN storedSymbol->libName = libNameStorage; storedSymbol->symbolName = symbolNameStorage; storedSymbol->flags = 0; + storedSymbol->previous = nullptr; + auto it = rplSymbolStorage.map_symbolByAddress.find(address); + if (it != rplSymbolStorage.map_symbolByAddress.end()) + storedSymbol->previous = it->second; rplSymbolStorage.map_symbolByAddress[address] = storedSymbol; return storedSymbol; } @@ -80,7 +84,10 @@ RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolN RPLStoredSymbol* rplSymbolStorage_getByAddress(MPTR address) { std::unique_lock lck(rplSymbolStorage.m_symbolStorageMutex); - return rplSymbolStorage.map_symbolByAddress[address]; + auto it = rplSymbolStorage.map_symbolByAddress.find(address); + if (it == rplSymbolStorage.map_symbolByAddress.end()) + return nullptr; + return it->second; } RPLStoredSymbol* rplSymbolStorage_getByClosestAddress(MPTR address) @@ -89,7 +96,8 @@ RPLStoredSymbol* rplSymbolStorage_getByClosestAddress(MPTR address) std::unique_lock lck(rplSymbolStorage.m_symbolStorageMutex); for(uint32 i=0; i<4096; i++) { - RPLStoredSymbol* symbol = rplSymbolStorage.map_symbolByAddress[address]; + auto it = rplSymbolStorage.map_symbolByAddress.find(address); + RPLStoredSymbol* symbol = (it == rplSymbolStorage.map_symbolByAddress.end()) ? nullptr : it->second; if(symbol) return symbol; address -= 4; @@ -100,8 +108,25 @@ RPLStoredSymbol* rplSymbolStorage_getByClosestAddress(MPTR address) void rplSymbolStorage_remove(RPLStoredSymbol* storedSymbol) { std::unique_lock lck(rplSymbolStorage.m_symbolStorageMutex); - if (rplSymbolStorage.map_symbolByAddress[storedSymbol->address] == storedSymbol) - rplSymbolStorage.map_symbolByAddress[storedSymbol->address] = nullptr; + auto it = rplSymbolStorage.map_symbolByAddress.find(storedSymbol->address); + if (it != rplSymbolStorage.map_symbolByAddress.end()) + { + if (it->second == storedSymbol) + { + if (storedSymbol->previous) + it->second = storedSymbol->previous; + else + rplSymbolStorage.map_symbolByAddress.erase(it); + } + else + { + RPLStoredSymbol* parentSymbol = it->second; + while (parentSymbol && parentSymbol->previous != storedSymbol) + parentSymbol = parentSymbol->previous; + if (parentSymbol) + parentSymbol->previous = storedSymbol->previous; + } + } delete storedSymbol; } @@ -145,7 +170,15 @@ void rplSymbolStorage_unloadAll() { // free symbols for (auto& it : rplSymbolStorage.map_symbolByAddress) - delete it.second; + { + RPLStoredSymbol* symbol = it.second; + while (symbol) + { + RPLStoredSymbol* previous = symbol->previous; + delete symbol; + symbol = previous; + } + } rplSymbolStorage.map_symbolByAddress.clear(); // free libs rplSymbolLib_t* lib = rplSymbolStorage.libs; diff --git a/src/Cafe/OS/RPL/rpl_symbol_storage.h b/src/Cafe/OS/RPL/rpl_symbol_storage.h index d940186a..afd887fc 100644 --- a/src/Cafe/OS/RPL/rpl_symbol_storage.h +++ b/src/Cafe/OS/RPL/rpl_symbol_storage.h @@ -4,6 +4,7 @@ struct RPLStoredSymbol void* libName; void* symbolName; uint32 flags; + RPLStoredSymbol* previous; }; void rplSymbolStorage_init(); @@ -16,4 +17,4 @@ RPLStoredSymbol* rplSymbolStorage_getByClosestAddress(MPTR address); void rplSymbolStorage_createJumpProxySymbol(MPTR jumpAddress, MPTR destAddress); std::unordered_map& rplSymbolStorage_lockSymbolMap(); -void rplSymbolStorage_unlockSymbolMap(); \ No newline at end of file +void rplSymbolStorage_unlockSymbolMap(); diff --git a/src/gui/wxgui/debugger/DebuggerWindow2.cpp b/src/gui/wxgui/debugger/DebuggerWindow2.cpp index e91bc1e0..202009ac 100644 --- a/src/gui/wxgui/debugger/DebuggerWindow2.cpp +++ b/src/gui/wxgui/debugger/DebuggerWindow2.cpp @@ -5,9 +5,12 @@ #include +#include "Common/FileStream.h" #include "config/ActiveSettings.h" +#include "Cafe/HW/MMU/MMU.h" #include "Cafe/OS/RPL/rpl_structs.h" #include "Cafe/OS/RPL/rpl_debug_symbols.h" +#include "Cafe/OS/RPL/rpl_symbol_storage.h" #include "wxgui/debugger/RegisterWindow.h" #include "wxgui/debugger/DumpWindow.h" @@ -20,6 +23,7 @@ #include "wxgui/debugger/BreakpointWindow.h" #include "wxgui/debugger/ModuleWindow.h" #include "util/helpers/helpers.h" +#include "util/helpers/StringHelpers.h" #include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h" #include "Cemu/Logging/CemuLogging.h" @@ -99,6 +103,118 @@ struct DebuggerModuleInfoNotify : public wxClientData DebuggerWindow2* s_debuggerWindow; +static bool TryParseMapAddress(std::string_view token, uint32& value) +{ + trim(token, "\t\n\v\f\r ,:;()[]"); + if (const auto colonIndex = token.find_last_of(':'); colonIndex != std::string_view::npos) + token = token.substr(colonIndex + 1); + trim(token, "\t\n\v\f\r ,:;()[]"); + if (!token.empty() && (token.back() == 'h' || token.back() == 'H')) + token.remove_suffix(1); + if (token.size() > 2 && token[0] == '0' && (token[1] == 'x' || token[1] == 'X')) + token.remove_prefix(2); + if (token.empty()) + return false; + + uint64 parsedValue = 0; + const auto result = std::from_chars(token.data(), token.data() + token.size(), parsedValue, 16); + if (result.ec != std::errc() || result.ptr != (token.data() + token.size()) || parsedValue > std::numeric_limits::max()) + return false; + + value = static_cast(parsedValue); + return true; +} + +static bool TryParseMapSymbolLine(std::string_view rawLine, uint32& address, std::string& symbolName) +{ + auto line = rawLine; + trim(line); + if (line.empty() || line.front() == '#' || line.front() == ';') + return false; + + bool foundAddress = false; + bool foundPreferredAddress = false; + uint32 fallbackAddress = 0; + symbolName.clear(); + while (!line.empty()) + { + trim(line); + if (line.empty()) + break; + + const auto splitIndex = line.find_first_of(" \t"); + auto token = splitIndex == std::string_view::npos ? line : line.substr(0, splitIndex); + line = splitIndex == std::string_view::npos ? std::string_view{} : line.substr(splitIndex + 1); + trim(token, "\t\n\v\f\r ,:;()[]"); + if (token.empty()) + continue; + + if (!foundAddress) + { + if (TryParseMapAddress(token, address)) + { + foundAddress = true; + fallbackAddress = address; + if (address >= MEMORY_CODEAREA_ADDR) + foundPreferredAddress = true; + } + continue; + } + + uint32 parsedAddress = 0; + if (TryParseMapAddress(token, parsedAddress)) + { + if (parsedAddress >= MEMORY_CODEAREA_ADDR) + { + address = parsedAddress; + foundPreferredAddress = true; + } + continue; + } + + std::string lowerToken(token); + std::transform(lowerToken.begin(), lowerToken.end(), lowerToken.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); + if (lowerToken == "load" || lowerToken == "provide" || lowerToken == "assert" || lowerToken == "*fill*" || lowerToken == "=") + continue; + if (lowerToken.find('/') != std::string::npos || lowerToken.find('\\') != std::string::npos) + continue; + if (lowerToken.find(".a(") != std::string::npos || lowerToken.find(".o(") != std::string::npos || lowerToken.find(".obj(") != std::string::npos) + continue; + if (lowerToken.ends_with(".o") || lowerToken.ends_with(".obj") || lowerToken.ends_with(".a") || lowerToken.ends_with(".lib")) + continue; + + if (symbolName.empty()) + symbolName.assign(token); + } + + if (!foundAddress || symbolName.empty()) + return false; + + if (!foundPreferredAddress) + address = fallbackAddress; + + return true; +} + +static std::optional GetRelocatedMapAddress(const DebuggerModuleInfo& moduleInfo, uint32 mapAddress) +{ + if (moduleInfo.textArea.ContainsAddress(mapAddress) || moduleInfo.dataArea.ContainsAddress(mapAddress)) + return mapAddress; + + if (mapAddress < moduleInfo.textArea.size) + return moduleInfo.textArea.base + mapAddress; + if (mapAddress < moduleInfo.dataArea.size) + return moduleInfo.dataArea.base + mapAddress; + + if (mapAddress >= MEMORY_CODEAREA_ADDR && mapAddress < (MEMORY_CODEAREA_ADDR + moduleInfo.textArea.size)) + return moduleInfo.textArea.base + (mapAddress - MEMORY_CODEAREA_ADDR); + + if (mapAddress >= MEMORY_DATA_AREA_ADDR && mapAddress < (MEMORY_DATA_AREA_ADDR + moduleInfo.dataArea.size)) + return moduleInfo.dataArea.base + (mapAddress - MEMORY_DATA_AREA_ADDR); + + return std::nullopt; +} + void DebuggerConfig::Load(XMLConfigParser& parser) { pin_to_main = parser.get("PinToMainWindow", true); @@ -282,26 +398,79 @@ void DebuggerWindow2::LoadModuleStorage(const DebuggerModuleInfo& moduleInfo) bool already_loaded = std::any_of(m_modulesStorage.begin(), m_modulesStorage.end(), [path](const std::unique_ptr& debug) { return debug->GetFilename() == path; }); if (!path.empty() && !already_loaded) { - m_modulesStorage.emplace_back(new XMLDebuggerModuleConfig(path, { moduleInfo, false }))->Load(); + auto& moduleStorage = m_modulesStorage.emplace_back(new XMLDebuggerModuleConfig(path, { moduleInfo, false })); + moduleStorage->Load(); + LoadModuleMap(moduleStorage->data()); } } void DebuggerWindow2::SaveModuleStorage(const DebuggerModuleInfo& moduleInfo, bool deleteModuleStorage) { auto path = GetModuleStoragePath(moduleInfo.moduleName, moduleInfo.patchCRC); - for (auto& moduleStorage : m_modulesStorage) + for (auto it = m_modulesStorage.begin(); it != m_modulesStorage.end(); ++it) { + auto& moduleStorage = *it; if (moduleStorage->data().moduleInfo.moduleName == moduleInfo.moduleName && moduleStorage->data().moduleInfo.patchCRC == moduleInfo.patchCRC) { moduleStorage->data().delete_breakpoints_after_saving = deleteModuleStorage; // make sure breakpoints are deleted when the storage is removed moduleStorage->Save(path); if (deleteModuleStorage) - m_modulesStorage.erase(std::find(m_modulesStorage.begin(), m_modulesStorage.end(), moduleStorage)); - break; + { + UnloadModuleMap(moduleStorage->data()); + m_modulesStorage.erase(it); + } + return; } } } +void DebuggerWindow2::LoadModuleMap(DebuggerModuleStorage& moduleStorage) +{ + UnloadModuleMap(moduleStorage); + + const auto mapPath = GetModuleMapPath(moduleStorage.moduleInfo.moduleName, moduleStorage.moduleInfo.patchCRC); + if (mapPath.empty()) + return; + + std::unique_ptr fileStream(FileStream::openFile(mapPath.c_str())); + if (!fileStream) + return; + + const auto fileSize = fileStream->GetSize(); + if (fileSize == 0 || fileSize > std::numeric_limits::max()) + return; + + const auto readSize = static_cast(fileSize); + std::vector fileData(readSize); + if (fileStream->readData(fileData.data(), readSize) != readSize) + return; + + std::string_view fileView(fileData.data(), fileData.size()); + for (const auto& line : StringHelpers::StringLineIterator(fileView)) + { + uint32 mapAddress = 0; + std::string symbolName; + if (TryParseMapSymbolLine(line, mapAddress, symbolName)) + { + const auto relocatedAddress = GetRelocatedMapAddress(moduleStorage.moduleInfo, mapAddress); + if (relocatedAddress) + { + moduleStorage.loaded_map_symbols.emplace_back(rplSymbolStorage_store(moduleStorage.moduleInfo.moduleName.c_str(), symbolName.c_str(), *relocatedAddress)); + } + } + } +} + +void DebuggerWindow2::UnloadModuleMap(DebuggerModuleStorage& moduleStorage) +{ + for (auto* symbol : moduleStorage.loaded_map_symbols) + { + if (symbol) + rplSymbolStorage_remove(symbol); + } + moduleStorage.loaded_map_symbols.clear(); +} + DebuggerWindow2::DebuggerWindow2(wxFrame& parent, const wxRect& display_size) : wxFrame(&parent, wxID_ANY, _("PPC Debugger"), wxDefaultPosition, wxSize(1280, 300), wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT), m_module_address(0) @@ -537,6 +706,12 @@ std::wstring DebuggerWindow2::GetModuleStoragePath(std::string module_name, uint return ActiveSettings::GetConfigPath("debugger/{}_{:#10x}.xml", module_name, crc_hash).generic_wstring(); } +std::wstring DebuggerWindow2::GetModuleMapPath(std::string module_name, uint32_t crc_hash) const +{ + if (module_name.empty() || crc_hash == 0) return {}; + return ActiveSettings::GetConfigPath("debugger/{}_{:#10x}.map", module_name, crc_hash).generic_wstring(); +} + void DebuggerWindow2::OnBreakpointHit(wxCommandEvent& event) { PPCInterpreter_t* hCPU = debugger_lockDebugSession(); diff --git a/src/gui/wxgui/debugger/DebuggerWindow2.h b/src/gui/wxgui/debugger/DebuggerWindow2.h index 1b4d3c72..02176741 100644 --- a/src/gui/wxgui/debugger/DebuggerWindow2.h +++ b/src/gui/wxgui/debugger/DebuggerWindow2.h @@ -10,12 +10,17 @@ #include #include +#include +#include +#include + class BreakpointWindow; class RegisterWindow; class DumpWindow; class ModuleWindow; class SymbolWindow; class wxStaticText; +struct RPLStoredSymbol; wxDECLARE_EVENT(wxEVT_UPDATE_VIEW, wxCommandEvent); wxDECLARE_EVENT(wxEVT_BREAKPOINT_HIT, wxCommandEvent); @@ -56,7 +61,7 @@ struct DebuggerModuleInfo MPTR base; uint32 size; - bool ContainsAddress(MPTR addr) + bool ContainsAddress(MPTR addr) const { return addr >= base && (addr < (base+size)); } @@ -74,6 +79,7 @@ struct DebuggerModuleStorage { DebuggerModuleInfo moduleInfo; bool delete_breakpoints_after_saving; + std::vector loaded_map_symbols; void Load(XMLConfigParser& parser); void Save(XMLConfigParser& parser); @@ -126,6 +132,9 @@ private: void CreateMenuBar(); void UpdateModuleLabel(uint32 address = 0); + void LoadModuleMap(DebuggerModuleStorage& moduleStorage); + void UnloadModuleMap(DebuggerModuleStorage& moduleStorage); + std::wstring GetModuleMapPath(std::string module_name, uint32_t crc_hash) const; void UpdateViewThreadsafe() override; void NotifyDebugBreakpointHit() override;