mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-07-09 17:14:47 -06:00
debugger: Add support for loading symbols from .map file
Also adds support for storing multiple symbols on one address by using a linked list.
This commit is contained in:
parent
8e3e961b8e
commit
1d0819109c
@ -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<std::mutex> 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<std::mutex> 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<std::mutex> 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;
|
||||
|
||||
@ -4,6 +4,7 @@ struct RPLStoredSymbol
|
||||
void* libName;
|
||||
void* symbolName;
|
||||
uint32 flags;
|
||||
RPLStoredSymbol* previous;
|
||||
};
|
||||
|
||||
void rplSymbolStorage_init();
|
||||
|
||||
@ -5,9 +5,12 @@
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#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<uint32>::max())
|
||||
return false;
|
||||
|
||||
value = static_cast<uint32>(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<char>(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<MPTR> 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<XMLDebuggerModuleConfig>& 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(FileStream::openFile(mapPath.c_str()));
|
||||
if (!fileStream)
|
||||
return;
|
||||
|
||||
const auto fileSize = fileStream->GetSize();
|
||||
if (fileSize == 0 || fileSize > std::numeric_limits<uint32>::max())
|
||||
return;
|
||||
|
||||
const auto readSize = static_cast<uint32>(fileSize);
|
||||
std::vector<char> 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();
|
||||
|
||||
@ -10,12 +10,17 @@
|
||||
#include <wx/frame.h>
|
||||
#include <wx/mstream.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<RPLStoredSymbol*> 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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user