debugger: Add support for loading symbols from .map file (#1916)

Map has to have the same name as the debugger/*.xml files, but with a .map extension
This commit is contained in:
Crementif 2026-05-19 00:21:14 +02:00 committed by GitHub
parent ad73c1e054
commit 7ff99a5e13
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 289 additions and 113 deletions

View File

@ -420,7 +420,7 @@ void PatchEntryInstruction::undoPatch()
uint8* patchAddr = (uint8*)memory_base + addr; uint8* patchAddr = (uint8*)memory_base + addr;
memcpy(patchAddr, m_dataBackup, m_length); memcpy(patchAddr, m_dataBackup, m_length);
PPCRecompiler_invalidateRange(addr, addr + m_length); PPCRecompiler_invalidateRange(addr, addr + m_length);
rplSymbolStorage_removeRange(addr, m_length); rplSymbolStorage_removeRange(addr, m_length, RPL_STORED_SYMBOL_PATCH);
DebugSymbolStorage::ClearRange(addr, m_length); DebugSymbolStorage::ClearRange(addr, m_length);
} }
@ -434,7 +434,7 @@ bool registerU32Variable(PatchContext_t& ctx, std::string& name, uint32 value, P
} }
ctx.map_values[name] = value; ctx.map_values[name] = value;
// keep track of address symbols for the debugger // keep track of address symbols for the debugger
rplSymbolStorage_store(ctx.graphicPack->GetName().data(), name.data(), value); rplSymbolStorage_store(ctx.graphicPack->GetName().data(), name.data(), value, RPL_STORED_SYMBOL_PATCH);
return true; return true;
} }

View File

@ -536,6 +536,10 @@ bool RPLLoader_LoadSections(sint32 aProcId, RPLModule* rplLoaderContext)
rplLoaderContext->regionSize_loaderInfo = regionLoaderinfoSize; rplLoaderContext->regionSize_loaderInfo = regionLoaderinfoSize;
rplLoaderContext->regionSize_text = regionTextSize; rplLoaderContext->regionSize_text = regionTextSize;
// set original base addresses
rplLoaderContext->regionOrigAddr_text = regionMappingTable.region[RPL_MAPPING_REGION_TEXT].baseAddress;
rplLoaderContext->regionOrigAddr_data = regionMappingTable.region[RPL_MAPPING_REGION_DATA].baseAddress;
// load data sections // load data sections
for (sint32 i = 0; i < (sint32)rplLoaderContext->rplHeader.sectionTableEntryCount; i++) for (sint32 i = 0; i < (sint32)rplLoaderContext->rplHeader.sectionTableEntryCount; i++)
{ {

View File

@ -171,7 +171,9 @@ struct RPLModule
uint32 tlsStartAddress; uint32 tlsStartAddress;
uint32 tlsEndAddress; uint32 tlsEndAddress;
uint32 regionSize_text; uint32 regionSize_text;
uint32 regionOrigAddr_text;
uint32 regionSize_data; uint32 regionSize_data;
uint32 regionOrigAddr_data;
uint32 regionSize_loaderInfo; uint32 regionSize_loaderInfo;
uint32 patchCRC; // Cemuhook style module crc for patches.txt uint32 patchCRC; // Cemuhook style module crc for patches.txt

View File

@ -63,7 +63,7 @@ char* rplSymbolStorage_storeLibname(const char* libName)
return libEntry->libName; return libEntry->libName;
} }
RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolName, MPTR address) RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolName, MPTR address, uint32 type)
{ {
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex); std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
char* libNameStorage = rplSymbolStorage_storeLibname(libName); char* libNameStorage = rplSymbolStorage_storeLibname(libName);
@ -72,7 +72,11 @@ RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolN
storedSymbol->address = address; storedSymbol->address = address;
storedSymbol->libName = libNameStorage; storedSymbol->libName = libNameStorage;
storedSymbol->symbolName = symbolNameStorage; storedSymbol->symbolName = symbolNameStorage;
storedSymbol->flags = 0; storedSymbol->flags = type;
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; rplSymbolStorage.map_symbolByAddress[address] = storedSymbol;
return storedSymbol; return storedSymbol;
} }
@ -80,7 +84,10 @@ RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolN
RPLStoredSymbol* rplSymbolStorage_getByAddress(MPTR address) RPLStoredSymbol* rplSymbolStorage_getByAddress(MPTR address)
{ {
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex); 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) RPLStoredSymbol* rplSymbolStorage_getByClosestAddress(MPTR address)
@ -89,7 +96,8 @@ RPLStoredSymbol* rplSymbolStorage_getByClosestAddress(MPTR address)
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex); std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
for(uint32 i=0; i<4096; i++) 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) if(symbol)
return symbol; return symbol;
address -= 4; address -= 4;
@ -100,18 +108,63 @@ RPLStoredSymbol* rplSymbolStorage_getByClosestAddress(MPTR address)
void rplSymbolStorage_remove(RPLStoredSymbol* storedSymbol) void rplSymbolStorage_remove(RPLStoredSymbol* storedSymbol)
{ {
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex); std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
if (rplSymbolStorage.map_symbolByAddress[storedSymbol->address] == storedSymbol) auto it = rplSymbolStorage.map_symbolByAddress.find(storedSymbol->address);
rplSymbolStorage.map_symbolByAddress[storedSymbol->address] = nullptr; 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; delete storedSymbol;
} }
void rplSymbolStorage_removeRange(MPTR address, sint32 length)
void rplSymbolStorage_removeRange(MPTR address, sint32 length, uint32 type)
{ {
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
while (length > 0) while (length > 0)
{ {
RPLStoredSymbol* symbol = rplSymbolStorage_getByAddress(address); auto it = rplSymbolStorage.map_symbolByAddress.find(address);
if (symbol) if (it != rplSymbolStorage.map_symbolByAddress.end())
rplSymbolStorage_remove(symbol); {
RPLStoredSymbol* current = it->second;
RPLStoredSymbol* newHead = current;
RPLStoredSymbol* previousKept = nullptr;
while (current)
{
RPLStoredSymbol* next = current->previous;
if ((current->flags & type) == type)
{
if (previousKept)
previousKept->previous = next;
else
newHead = next;
delete current;
}
else
{
previousKept = current;
}
current = next;
}
if (newHead)
it->second = newHead;
else
rplSymbolStorage.map_symbolByAddress.erase(it);
}
address += 4; address += 4;
length -= 4; length -= 4;
} }
@ -145,7 +198,15 @@ void rplSymbolStorage_unloadAll()
{ {
// free symbols // free symbols
for (auto& it : rplSymbolStorage.map_symbolByAddress) 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(); rplSymbolStorage.map_symbolByAddress.clear();
// free libs // free libs
rplSymbolLib_t* lib = rplSymbolStorage.libs; rplSymbolLib_t* lib = rplSymbolStorage.libs;

View File

@ -1,19 +1,27 @@
enum RPLStoredSymbolType : uint32
{
RPL_STORED_SYMBOL_NONE = 0,
RPL_STORED_SYMBOL_MAP = 1 << 0,
RPL_STORED_SYMBOL_PATCH = 1 << 1,
};
struct RPLStoredSymbol struct RPLStoredSymbol
{ {
MPTR address; MPTR address;
void* libName; void* libName;
void* symbolName; void* symbolName;
uint32 flags; uint32 flags;
RPLStoredSymbol* previous;
}; };
void rplSymbolStorage_init(); void rplSymbolStorage_init();
void rplSymbolStorage_unloadAll(); void rplSymbolStorage_unloadAll();
RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolName, MPTR address); RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolName, MPTR address, uint32 type = RPL_STORED_SYMBOL_NONE);
void rplSymbolStorage_remove(RPLStoredSymbol* storedSymbol); void rplSymbolStorage_remove(RPLStoredSymbol* storedSymbol);
void rplSymbolStorage_removeRange(MPTR address, sint32 length); void rplSymbolStorage_removeRange(MPTR address, sint32 length, uint32 type = RPL_STORED_SYMBOL_NONE);
RPLStoredSymbol* rplSymbolStorage_getByAddress(MPTR address); RPLStoredSymbol* rplSymbolStorage_getByAddress(MPTR address);
RPLStoredSymbol* rplSymbolStorage_getByClosestAddress(MPTR address); RPLStoredSymbol* rplSymbolStorage_getByClosestAddress(MPTR address);
void rplSymbolStorage_createJumpProxySymbol(MPTR jumpAddress, MPTR destAddress); void rplSymbolStorage_createJumpProxySymbol(MPTR jumpAddress, MPTR destAddress);
std::unordered_map<uint32, RPLStoredSymbol*>& rplSymbolStorage_lockSymbolMap(); std::unordered_map<uint32, RPLStoredSymbol*>& rplSymbolStorage_lockSymbolMap();
void rplSymbolStorage_unlockSymbolMap(); void rplSymbolStorage_unlockSymbolMap();

View File

@ -3,11 +3,12 @@
#include "wxHelper.h" #include "wxHelper.h"
#include <filesystem> #include "Common/FileStream.h"
#include "config/ActiveSettings.h" #include "config/ActiveSettings.h"
#include "Cafe/HW/MMU/MMU.h"
#include "Cafe/OS/RPL/rpl_structs.h" #include "Cafe/OS/RPL/rpl_structs.h"
#include "Cafe/OS/RPL/rpl_debug_symbols.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/RegisterWindow.h"
#include "wxgui/debugger/DumpWindow.h" #include "wxgui/debugger/DumpWindow.h"
@ -20,6 +21,7 @@
#include "wxgui/debugger/BreakpointWindow.h" #include "wxgui/debugger/BreakpointWindow.h"
#include "wxgui/debugger/ModuleWindow.h" #include "wxgui/debugger/ModuleWindow.h"
#include "util/helpers/helpers.h" #include "util/helpers/helpers.h"
#include "util/helpers/StringHelpers.h"
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h" #include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
#include "Cemu/Logging/CemuLogging.h" #include "Cemu/Logging/CemuLogging.h"
@ -84,10 +86,11 @@ DebuggerModuleInfo::DebuggerModuleInfo(RPLModule* module)
moduleName = module->moduleName2; moduleName = module->moduleName2;
patchCRC = module->patchCRC; patchCRC = module->patchCRC;
textArea.base = module->regionMappingBase_text.GetMPTR(); textArea.base = module->regionMappingBase_text.GetMPTR();
textArea.origBase = module->regionOrigAddr_text;
textArea.size = module->regionSize_text; textArea.size = module->regionSize_text;
dataArea.base = module->regionMappingBase_data; dataArea.base = module->regionMappingBase_data;
dataArea.origBase = module->regionOrigAddr_data;
dataArea.size = module->regionSize_data; dataArea.size = module->regionSize_data;
} }
struct DebuggerModuleInfoNotify : public wxClientData struct DebuggerModuleInfoNotify : public wxClientData
@ -99,6 +102,65 @@ struct DebuggerModuleInfoNotify : public wxClientData
DebuggerWindow2* s_debuggerWindow; 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())
return false;
const auto addressEnd = line.find_first_of(" \t");
if (addressEnd == std::string_view::npos)
return false;
if (!TryParseMapAddress(line.substr(0, addressEnd), address))
return false;
line = line.substr(addressEnd + 1);
trim(line);
if (line.empty())
return false;
const auto symbolEnd = line.find_first_of(" \t");
auto symbolToken = symbolEnd == std::string_view::npos ? line : line.substr(0, symbolEnd);
trim(symbolToken);
if (symbolToken.empty())
return false;
symbolName.assign(symbolToken);
return true;
}
static std::optional<MPTR> GetRelocatedMapAddress(const DebuggerModuleInfo& moduleInfo, uint32 mapAddress)
{
if (moduleInfo.textArea.ContainsOriginalAddress(mapAddress))
return moduleInfo.textArea.base + (mapAddress - moduleInfo.textArea.origBase);
if (moduleInfo.dataArea.ContainsOriginalAddress(mapAddress))
return moduleInfo.dataArea.base + (mapAddress - moduleInfo.dataArea.origBase);
return std::nullopt;
}
void DebuggerConfig::Load(XMLConfigParser& parser) void DebuggerConfig::Load(XMLConfigParser& parser)
{ {
pin_to_main = parser.get("PinToMainWindow", true); pin_to_main = parser.get("PinToMainWindow", true);
@ -282,26 +344,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; }); 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) 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) void DebuggerWindow2::SaveModuleStorage(const DebuggerModuleInfo& moduleInfo, bool deleteModuleStorage)
{ {
auto path = GetModuleStoragePath(moduleInfo.moduleName, moduleInfo.patchCRC); 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) 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->data().delete_breakpoints_after_saving = deleteModuleStorage; // make sure breakpoints are deleted when the storage is removed
moduleStorage->Save(path); moduleStorage->Save(path);
if (deleteModuleStorage) 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, RPL_STORED_SYMBOL_MAP));
}
}
}
}
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) 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), : 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) m_module_address(0)
@ -537,6 +652,12 @@ std::wstring DebuggerWindow2::GetModuleStoragePath(std::string module_name, uint
return ActiveSettings::GetConfigPath("debugger/{}_{:#10x}.xml", module_name, crc_hash).generic_wstring(); 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) void DebuggerWindow2::OnBreakpointHit(wxCommandEvent& event)
{ {
PPCInterpreter_t* hCPU = debugger_lockDebugSession(); PPCInterpreter_t* hCPU = debugger_lockDebugSession();

View File

@ -16,6 +16,7 @@ class DumpWindow;
class ModuleWindow; class ModuleWindow;
class SymbolWindow; class SymbolWindow;
class wxStaticText; class wxStaticText;
struct RPLStoredSymbol;
wxDECLARE_EVENT(wxEVT_UPDATE_VIEW, wxCommandEvent); wxDECLARE_EVENT(wxEVT_UPDATE_VIEW, wxCommandEvent);
wxDECLARE_EVENT(wxEVT_BREAKPOINT_HIT, wxCommandEvent); wxDECLARE_EVENT(wxEVT_BREAKPOINT_HIT, wxCommandEvent);
@ -54,12 +55,18 @@ struct DebuggerModuleInfo
struct ModuleArea struct ModuleArea
{ {
MPTR base; MPTR base;
MPTR origBase;
uint32 size; uint32 size;
bool ContainsAddress(MPTR addr) bool ContainsAddress(MPTR addr) const
{ {
return addr >= base && (addr < (base+size)); return addr >= base && (addr < (base+size));
} }
bool ContainsOriginalAddress(MPTR addr) const
{
return addr >= origBase && (addr < (origBase + size));
}
}; };
std::string moduleName; std::string moduleName;
@ -74,6 +81,7 @@ struct DebuggerModuleStorage
{ {
DebuggerModuleInfo moduleInfo; DebuggerModuleInfo moduleInfo;
bool delete_breakpoints_after_saving; bool delete_breakpoints_after_saving;
std::vector<RPLStoredSymbol*> loaded_map_symbols;
void Load(XMLConfigParser& parser); void Load(XMLConfigParser& parser);
void Save(XMLConfigParser& parser); void Save(XMLConfigParser& parser);
@ -126,6 +134,9 @@ private:
void CreateMenuBar(); void CreateMenuBar();
void UpdateModuleLabel(uint32 address = 0); 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 UpdateViewThreadsafe() override;
void NotifyDebugBreakpointHit() override; void NotifyDebugBreakpointHit() override;

View File

@ -14,6 +14,8 @@
#include "Cemu/ExpressionParser/ExpressionParser.h" #include "Cemu/ExpressionParser/ExpressionParser.h"
#include "Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h" #include "Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h"
#include <wx/clipbrd.h>
wxDEFINE_EVENT(wxEVT_DISASMCTRL_NOTIFY_GOTO_ADDRESS, wxCommandEvent); wxDEFINE_EVENT(wxEVT_DISASMCTRL_NOTIFY_GOTO_ADDRESS, wxCommandEvent);
#define MAX_SYMBOL_LEN (120) #define MAX_SYMBOL_LEN (120)
@ -699,20 +701,11 @@ void DisasmCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
void DisasmCtrl::CopyToClipboard(std::string text) void DisasmCtrl::CopyToClipboard(std::string text)
{ {
#if BOOST_OS_WINDOWS if (wxClipboard::Get()->Open())
if (OpenClipboard(nullptr))
{ {
EmptyClipboard(); wxClipboard::Get()->SetData(new wxTextDataObject(text));
const HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, text.size() + 1); wxClipboard::Get()->Close();
if (hGlobal)
{
memcpy(GlobalLock(hGlobal), text.c_str(), text.size() + 1);
GlobalUnlock(hGlobal);
SetClipboardData(CF_TEXT, hGlobal);
}
CloseClipboard();
} }
#endif
} }
static uint32 GetUnrelocatedAddress(MPTR address) static uint32 GetUnrelocatedAddress(MPTR address)

View File

@ -2,6 +2,7 @@
#include "Cafe/OS/RPL/rpl_symbol_storage.h" #include "Cafe/OS/RPL/rpl_symbol_storage.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include <wx/listctrl.h> #include <wx/listctrl.h>
#include <wx/clipbrd.h>
enum ItemColumns enum ItemColumns
{ {
@ -38,13 +39,12 @@ SymbolListCtrl::SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxP
m_list_filter.Clear(); m_list_filter.Clear();
OnGameLoaded(); OnGameLoaded();
SetItemCount(m_data.size());
} }
void SymbolListCtrl::OnGameLoaded() void SymbolListCtrl::OnGameLoaded()
{ {
m_data.clear(); m_data.clear();
m_visible_items.clear();
const auto symbol_map = rplSymbolStorage_lockSymbolMap(); const auto symbol_map = rplSymbolStorage_lockSymbolMap();
for (auto const& [address, symbol_info] : symbol_map) for (auto const& [address, symbol_info] : symbol_map)
{ {
@ -56,48 +56,30 @@ void SymbolListCtrl::OnGameLoaded()
wxString searchNameWX = libNameWX + symbolNameWX; wxString searchNameWX = libNameWX + symbolNameWX;
searchNameWX.MakeLower(); searchNameWX.MakeLower();
auto new_entry = m_data.try_emplace( m_data.try_emplace(
symbol_info->address, address,
symbolNameWX, symbolNameWX,
libNameWX, libNameWX,
searchNameWX, searchNameWX
false
); );
if (m_list_filter.IsEmpty())
new_entry.first->second.visible = true;
else if (new_entry.first->second.searchName.Contains(m_list_filter))
new_entry.first->second.visible = true;
} }
rplSymbolStorage_unlockSymbolMap(); rplSymbolStorage_unlockSymbolMap();
SetItemCount(m_data.size()); RebuildVisibleItems();
if (m_data.size() > 0)
RefreshItems(GetTopItem(), std::min<long>(m_data.size() - 1, GetTopItem() + GetCountPerPage() + 1));
} }
wxString SymbolListCtrl::OnGetItemText(long item, long column) const wxString SymbolListCtrl::OnGetItemText(long item, long column) const
{ {
if (item >= GetItemCount()) if (item < 0 || item >= static_cast<long>(m_visible_items.size()))
return wxEmptyString; return wxEmptyString;
long visible_idx = 0; const auto& [address, symbol] = *m_visible_items[item];
for (const auto& [address, symbol] : m_data) if (column == ColumnName)
{ return symbol.name;
if (!symbol.visible) if (column == ColumnAddress)
continue; return wxString::Format("%08x", address);
if (column == ColumnModule)
if (item == visible_idx) return symbol.libName;
{
if (column == ColumnName)
return wxString(symbol.name);
if (column == ColumnAddress)
return wxString::Format("%08x", address);
if (column == ColumnModule)
return wxString(symbol.libName);
}
visible_idx++;
}
return wxEmptyString; return wxEmptyString;
} }
@ -106,10 +88,9 @@ wxString SymbolListCtrl::OnGetItemText(long item, long column) const
void SymbolListCtrl::OnLeftDClick(wxListEvent& event) void SymbolListCtrl::OnLeftDClick(wxListEvent& event)
{ {
long selected = GetFirstSelected(); long selected = GetFirstSelected();
if (selected == wxNOT_FOUND) if (selected == wxNOT_FOUND || selected >= static_cast<long>(m_visible_items.size()))
return; return;
const auto text = GetItemText(selected, ColumnAddress); const auto address = m_visible_items[selected]->first;
const auto address = std::stoul(text.ToStdString(), nullptr, 16);
if (address == 0) if (address == 0)
return; return;
debugger_jumpToAddressInDisasm(address); debugger_jumpToAddressInDisasm(address);
@ -118,46 +99,40 @@ void SymbolListCtrl::OnLeftDClick(wxListEvent& event)
void SymbolListCtrl::OnRightClick(wxListEvent& event) void SymbolListCtrl::OnRightClick(wxListEvent& event)
{ {
long selected = GetFirstSelected(); long selected = GetFirstSelected();
if (selected == wxNOT_FOUND) if (selected == wxNOT_FOUND || selected >= static_cast<long>(m_visible_items.size()))
return; return;
auto text = GetItemText(selected, ColumnAddress); if (wxClipboard::Get()->Open())
text = "0x" + text;
#if BOOST_OS_WINDOWS
if (OpenClipboard(nullptr))
{ {
EmptyClipboard(); wxClipboard::Get()->SetData(new wxTextDataObject(wxString::Format("0x%08x", m_visible_items[selected]->first)));
const HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, text.size()+1); wxClipboard::Get()->Close();
if (hGlobal)
{
memcpy(GlobalLock(hGlobal), text.c_str(), text.size() + 1);
GlobalUnlock(hGlobal);
SetClipboardData(CF_TEXT, hGlobal);
GlobalFree(hGlobal);
}
CloseClipboard();
} }
#endif }
void SymbolListCtrl::RebuildVisibleItems()
{
m_visible_items.clear();
m_visible_items.reserve(m_data.size());
const bool has_filter = !m_list_filter.IsEmpty();
for (auto it = m_data.cbegin(); it != m_data.cend(); ++it)
{
if (has_filter && !it->second.searchName.Contains(m_list_filter))
continue;
m_visible_items.emplace_back(it);
}
SetItemCount(static_cast<long>(m_visible_items.size()));
if (!m_visible_items.empty())
{
const auto top_item = std::min<long>(GetTopItem(), static_cast<long>(m_visible_items.size() - 1));
RefreshItems(top_item, std::min<long>(static_cast<long>(m_visible_items.size() - 1), top_item + GetCountPerPage() + 1));
}
else
Refresh();
} }
void SymbolListCtrl::ChangeListFilter(wxString filter) void SymbolListCtrl::ChangeListFilter(wxString filter)
{ {
m_list_filter = filter.MakeLower(); m_list_filter = filter.MakeLower();
RebuildVisibleItems();
size_t visible_entries = m_data.size();
for (auto& [address, symbol] : m_data)
{
if (m_list_filter.IsEmpty())
symbol.visible = true;
else if (symbol.searchName.Contains(m_list_filter))
symbol.visible = true;
else
{
visible_entries--;
symbol.visible = false;
}
}
SetItemCount(visible_entries);
if (visible_entries > 0)
RefreshItems(GetTopItem(), std::min<long>(visible_entries - 1, GetTopItem() + GetCountPerPage() + 1));
} }

View File

@ -10,22 +10,23 @@ public:
void ChangeListFilter(wxString filter); void ChangeListFilter(wxString filter);
private: private:
struct SymbolItem { struct SymbolItem {
SymbolItem() = default; SymbolItem() = default;
SymbolItem(wxString name, wxString libName, wxString searchName, bool visible) : name(name), libName(libName), searchName(searchName), visible(visible) {} SymbolItem(const wxString& name, const wxString& libName, const wxString& searchName) : name(name), libName(libName), searchName(searchName) {}
wxString name; wxString name;
wxString libName; wxString libName;
wxString searchName; wxString searchName;
bool visible;
}; };
using SymbolMap = std::map<MPTR, SymbolItem>;
std::map<MPTR, SymbolItem> m_data; SymbolMap m_data;
wxString m_list_filter; wxString m_list_filter;
std::vector<SymbolMap::const_iterator> m_visible_items;
wxString OnGetItemText(long item, long column) const; wxString OnGetItemText(long item, long column) const override;
void RebuildVisibleItems();
void OnLeftDClick(wxListEvent& event); void OnLeftDClick(wxListEvent& event);
void OnRightClick(wxListEvent& event); void OnRightClick(wxListEvent& event);
}; };