mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge pull request #20181 from hrydgard/symbol-maps
Add symbol map export support to the ImDebugger
This commit is contained in:
commit
845587abb9
12 changed files with 128 additions and 80 deletions
|
@ -86,7 +86,11 @@ std::string NiceTimeFormat(int seconds);
|
|||
// TODO: We actually also have Buffer, with .Printf(), which is almost as convenient. Should maybe improve that instead?
|
||||
class StringWriter {
|
||||
public:
|
||||
explicit StringWriter(char *buffer, size_t bufSize) : start_(buffer), p_(buffer), bufSize_(bufSize) {
|
||||
StringWriter(char *buffer, size_t bufSize) : start_(buffer), p_(buffer), bufSize_(bufSize) {
|
||||
buffer[0] = '\0';
|
||||
}
|
||||
template<size_t sz>
|
||||
explicit StringWriter(char (&buffer)[sz]) : start_(buffer), p_(buffer), bufSize_(sz) {
|
||||
buffer[0] = '\0';
|
||||
}
|
||||
StringWriter(const StringWriter &) = delete;
|
||||
|
|
|
@ -281,6 +281,13 @@ int OpenFD(const Path &path, OpenFlag flags) {
|
|||
return descriptor;
|
||||
}
|
||||
|
||||
void CloseFD(int fd) {
|
||||
#if PPSSPP_PLATFORM(ANDROID)
|
||||
close(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
static bool ResolvePathVista(const std::wstring &path, wchar_t *buf, DWORD bufSize) {
|
||||
typedef DWORD(WINAPI *getFinalPathNameByHandleW_f)(HANDLE hFile, LPWSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags);
|
||||
|
|
|
@ -56,6 +56,9 @@ enum OpenFlag {
|
|||
// of DirectoryFileSystem::Open here eventually for symmetry.
|
||||
int OpenFD(const Path &filename, OpenFlag flags);
|
||||
|
||||
// Cross-platform way to close FDs, corresponsing in platform support with OpenFD above.
|
||||
void CloseFD(int fd);
|
||||
|
||||
// Resolves symlinks and similar.
|
||||
std::string ResolvePath(std::string_view path);
|
||||
|
||||
|
|
|
@ -45,11 +45,9 @@ static bool g_exitOnAssert;
|
|||
static AssertNoCallbackFunc g_assertCancelCallback = 0;
|
||||
static void *g_assertCancelCallbackUserData = 0;
|
||||
|
||||
|
||||
|
||||
void SetAssertDialogParent(void *handle) {
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
g_dialogParent = (HWND)handle;
|
||||
g_dialogParent = (HWND)handle;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -280,6 +280,7 @@ static const ConfigSetting generalSettings[] = {
|
|||
// "default" means let emulator decide, "" means disable.
|
||||
ConfigSetting("ReportingHost", &g_Config.sReportHost, "default", CfgFlag::DEFAULT),
|
||||
ConfigSetting("AutoSaveSymbolMap", &g_Config.bAutoSaveSymbolMap, false, CfgFlag::PER_GAME),
|
||||
ConfigSetting("CompressSymbols", &g_Config.bCompressSymbols, true, CfgFlag::DEFAULT),
|
||||
ConfigSetting("CacheFullIsoInRam", &g_Config.bCacheFullIsoInRam, false, CfgFlag::PER_GAME),
|
||||
ConfigSetting("RemoteISOPort", &g_Config.iRemoteISOPort, 0, CfgFlag::DEFAULT),
|
||||
ConfigSetting("LastRemoteISOServer", &g_Config.sLastRemoteISOServer, "", CfgFlag::DEFAULT),
|
||||
|
@ -1212,11 +1213,6 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
|
|||
// For iOS, issue #19211
|
||||
TryUpdateSavedPath(¤tDirectory);
|
||||
|
||||
// This check is probably not really necessary here anyway, you can always
|
||||
// press Home or Browse if you're in a bad directory.
|
||||
if (!File::Exists(currentDirectory))
|
||||
currentDirectory = defaultCurrentDirectory;
|
||||
|
||||
Section *log = iniFile.GetOrCreateSection(logSectionName);
|
||||
|
||||
bool debugDefaults = false;
|
||||
|
|
|
@ -122,6 +122,7 @@ public:
|
|||
int iIOTimingMethod;
|
||||
int iLockedCPUSpeed;
|
||||
bool bAutoSaveSymbolMap;
|
||||
bool bCompressSymbols;
|
||||
bool bCacheFullIsoInRam;
|
||||
int iRemoteISOPort;
|
||||
std::string sLastRemoteISOServer;
|
||||
|
|
|
@ -42,7 +42,9 @@
|
|||
#include "Common/Log.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/Buffer.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Debugger/SymbolMap.h"
|
||||
|
||||
#ifndef NO_ARMIPS
|
||||
|
@ -189,10 +191,10 @@ bool SymbolMap::LoadSymbolMap(const Path &filename) {
|
|||
continue;
|
||||
|
||||
if (!strcmp(name, ".text") || !strcmp(name, ".init") || strlen(name) <= 1) {
|
||||
|
||||
// Ignored
|
||||
} else {
|
||||
switch (type)
|
||||
{
|
||||
// Seems legit
|
||||
switch (type) {
|
||||
case ST_FUNCTION:
|
||||
AddFunction(name, vaddress, size, moduleIndex);
|
||||
break;
|
||||
|
@ -209,6 +211,7 @@ bool SymbolMap::LoadSymbolMap(const Path &filename) {
|
|||
}
|
||||
}
|
||||
gzclose(f);
|
||||
activeNeedUpdate_ = true;
|
||||
SortSymbols();
|
||||
return started;
|
||||
}
|
||||
|
@ -221,33 +224,49 @@ bool SymbolMap::SaveSymbolMap(const Path &filename) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
// TODO(scoped): Use gzdopen
|
||||
#if defined(_WIN32) && defined(UNICODE)
|
||||
gzFile f = gzopen_w(filename.ToWString().c_str(), "w9");
|
||||
#else
|
||||
gzFile f = gzopen(filename.c_str(), "w9");
|
||||
#endif
|
||||
|
||||
if (f == Z_NULL)
|
||||
return false;
|
||||
|
||||
gzprintf(f, ".text\n");
|
||||
|
||||
Buffer buf;
|
||||
buf.Printf(".text\n");
|
||||
for (auto it = modules.begin(), end = modules.end(); it != end; ++it) {
|
||||
const ModuleEntry &mod = *it;
|
||||
gzprintf(f, ".module %x %08x %08x %s\n", mod.index, mod.start, mod.size, mod.name);
|
||||
buf.Printf(".module %x %08x %08x %s\n", mod.index, mod.start, mod.size, mod.name);
|
||||
}
|
||||
|
||||
for (auto it = functions.begin(), end = functions.end(); it != end; ++it) {
|
||||
const FunctionEntry& e = it->second;
|
||||
gzprintf(f, "%08x %08x %x %i %s\n", e.start, e.size, e.module, ST_FUNCTION, GetLabelNameRel(e.start, e.module));
|
||||
buf.Printf("%08x %08x %x %i %s\n", e.start, e.size, e.module, ST_FUNCTION, GetLabelNameRel(e.start, e.module));
|
||||
}
|
||||
|
||||
for (auto it = data.begin(), end = data.end(); it != end; ++it) {
|
||||
const DataEntry& e = it->second;
|
||||
gzprintf(f, "%08x %08x %x %i %s\n", e.start, e.size, e.module, ST_DATA, GetLabelNameRel(e.start, e.module));
|
||||
buf.Printf("%08x %08x %x %i %s\n", e.start, e.size, e.module, ST_DATA, GetLabelNameRel(e.start, e.module));
|
||||
}
|
||||
|
||||
std::string data;
|
||||
buf.TakeAll(&data);
|
||||
if (g_Config.bCompressSymbols) {
|
||||
// TODO: Wrap this in some nicer way.
|
||||
gzFile f;
|
||||
if (filename.Type() == PathType::CONTENT_URI) {
|
||||
int fd = File::OpenFD(filename, File::OPEN_WRITE);
|
||||
f = gzdopen(fd, "w9");
|
||||
if (f == Z_NULL) {
|
||||
File::CloseFD(fd);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
f = gzopen(filename.c_str(), "w9");
|
||||
if (f == Z_NULL) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
gzwrite(f, data.data(), (unsigned int)data.size());
|
||||
gzclose(f);
|
||||
} else {
|
||||
// Just plain write it.
|
||||
FILE *file = File::OpenCFile(filename, "wb");
|
||||
fwrite(data.data(), 1, data.size(), file);
|
||||
fclose(file);
|
||||
}
|
||||
gzclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -291,43 +310,43 @@ bool SymbolMap::LoadNocashSym(const Path &filename) {
|
|||
}
|
||||
} else { // labels
|
||||
unsigned int size = 1;
|
||||
char* seperator = strchr(value, ',');
|
||||
if (seperator != NULL) {
|
||||
*seperator = 0;
|
||||
sscanf(seperator+1,"%08X",&size);
|
||||
char *separator = strchr(value, ',');
|
||||
if (separator != NULL) {
|
||||
*separator = '\0';
|
||||
sscanf(separator + 1, "%08X", &size);
|
||||
}
|
||||
|
||||
if (size != 1) {
|
||||
AddFunction(value, address,size, 0);
|
||||
AddFunction(value, address, size, 0);
|
||||
} else {
|
||||
AddLabel(value, address, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SymbolMap::SaveNocashSym(const Path &filename) const {
|
||||
bool SymbolMap::SaveNocashSym(const Path &filename) const {
|
||||
std::lock_guard<std::recursive_mutex> guard(lock_);
|
||||
|
||||
// Don't bother writing a blank file.
|
||||
if (!File::Exists(filename) && functions.empty() && data.empty()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE* f = File::OpenCFile(filename, "w");
|
||||
if (f == NULL)
|
||||
return;
|
||||
FILE *f = File::OpenCFile(filename, "w");
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
// only write functions, the rest isn't really interesting
|
||||
for (auto it = functions.begin(), end = functions.end(); it != end; ++it) {
|
||||
const FunctionEntry& e = it->second;
|
||||
fprintf(f, "%08X %s,%04X\n", GetModuleAbsoluteAddr(e.start,e.module),GetLabelNameRel(e.start, e.module), e.size);
|
||||
fprintf(f, "%08X %s,%04X\n", GetModuleAbsoluteAddr(e.start,e.module), GetLabelNameRel(e.start, e.module), e.size);
|
||||
}
|
||||
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
SymbolType SymbolMap::GetSymbolType(u32 address) {
|
||||
|
@ -421,7 +440,7 @@ std::string SymbolMap::GetDescription(unsigned int address) {
|
|||
return descriptionTemp;
|
||||
}
|
||||
|
||||
std::vector<SymbolEntry> SymbolMap::GetAllSymbols(SymbolType symmask) {
|
||||
std::vector<SymbolEntry> SymbolMap::GetAllActiveSymbols(SymbolType symmask) {
|
||||
if (activeNeedUpdate_)
|
||||
UpdateActiveSymbols();
|
||||
|
||||
|
@ -720,8 +739,8 @@ void SymbolMap::AssignFunctionIndices() {
|
|||
}
|
||||
}
|
||||
|
||||
// Copies functions, labels and data to the active set depending on which modules are "active".
|
||||
void SymbolMap::UpdateActiveSymbols() {
|
||||
// return; (slow in debug mode)
|
||||
std::lock_guard<std::recursive_mutex> guard(lock_);
|
||||
|
||||
activeFunctions.clear();
|
||||
|
|
|
@ -72,13 +72,13 @@ public:
|
|||
bool LoadSymbolMap(const Path &filename);
|
||||
bool SaveSymbolMap(const Path &filename) const;
|
||||
bool LoadNocashSym(const Path &filename);
|
||||
void SaveNocashSym(const Path &filename) const;
|
||||
bool SaveNocashSym(const Path &filename) const;
|
||||
|
||||
SymbolType GetSymbolType(u32 address);
|
||||
bool GetSymbolInfo(SymbolInfo *info, u32 address, SymbolType symmask = ST_FUNCTION);
|
||||
u32 GetNextSymbolAddress(u32 address, SymbolType symmask);
|
||||
std::string GetDescription(unsigned int address);
|
||||
std::vector<SymbolEntry> GetAllSymbols(SymbolType symmask);
|
||||
std::vector<SymbolEntry> GetAllActiveSymbols(SymbolType symmask);
|
||||
|
||||
#ifdef _WIN32
|
||||
void FillSymbolListBox(HWND listbox, SymbolType symType);
|
||||
|
|
|
@ -209,7 +209,7 @@ void WebSocketHLEFuncList(DebuggerRequest &req) {
|
|||
if (!g_symbolMap)
|
||||
return req.Fail("CPU not active");
|
||||
|
||||
auto functions = g_symbolMap->GetAllSymbols(ST_FUNCTION);
|
||||
auto functions = g_symbolMap->GetAllActiveSymbols(ST_FUNCTION);
|
||||
|
||||
JsonWriter &json = req.Respond();
|
||||
json.pushArray("functions");
|
||||
|
|
|
@ -455,7 +455,7 @@ void DrawFPS(UIContext *ctx, const Bounds &bounds) {
|
|||
__DisplayGetFPS(&vps, &fps, &actual_fps);
|
||||
|
||||
char temp[256];
|
||||
StringWriter w(temp, sizeof(temp));
|
||||
StringWriter w(temp);
|
||||
|
||||
if ((g_Config.iShowStatusFlags & ((int)ShowStatusFlags::FPS_COUNTER | (int)ShowStatusFlags::SPEED_COUNTER)) == ((int)ShowStatusFlags::FPS_COUNTER | (int)ShowStatusFlags::SPEED_COUNTER)) {
|
||||
// Both at the same time gets a shorter formulation.
|
||||
|
|
|
@ -1660,34 +1660,6 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu
|
|||
ImGui::MenuItem("Don't break on start", nullptr, &g_Config.bAutoRun); // should really invert this bool!
|
||||
ImGui::MenuItem("Fast memory", nullptr, &g_Config.bFastMemory);
|
||||
ImGui::Separator();
|
||||
|
||||
/*
|
||||
// Symbol stuff. Move to separate menu?
|
||||
// Doesn't quite seem to work yet.
|
||||
if (ImGui::MenuItem("Load symbol map...")) {
|
||||
System_BrowseForFile(reqToken_, "Load symbol map", BrowseFileType::SYMBOL_MAP, [&](const char *responseString, int) {
|
||||
Path path(responseString);
|
||||
if (!g_symbolMap->LoadSymbolMap(path)) {
|
||||
ERROR_LOG(Log::Common, "Failed to load symbol map");
|
||||
}
|
||||
disasm_.DirtySymbolMap();
|
||||
});
|
||||
}
|
||||
if (ImGui::MenuItem("Save symbol map...")) {
|
||||
System_BrowseForFileSave(reqToken_, "Save symbol map", "symbols.map", BrowseFileType::SYMBOL_MAP, [](const char *responseString, int) {
|
||||
Path path(responseString);
|
||||
if (!g_symbolMap->SaveSymbolMap(path)) {
|
||||
ERROR_LOG(Log::Common, "Failed to save symbol map");
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
if (ImGui::MenuItem("Reset symbol map")) {
|
||||
g_symbolMap->Clear();
|
||||
disasm_.DirtySymbolMap();
|
||||
// NotifyDebuggerMapLoaded();
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Take screenshot")) {
|
||||
g_TakeScreenshot = true;
|
||||
}
|
||||
|
@ -1715,6 +1687,50 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu
|
|||
ImGui::MenuItem("Breakpoints", nullptr, &cfg_.breakpointsOpen);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Symbols")) {
|
||||
if (ImGui::MenuItem("Load .ppmap...")) {
|
||||
System_BrowseForFile(reqToken_, "Load PPSSPP symbol map", BrowseFileType::SYMBOL_MAP, [&](const char *responseString, int) {
|
||||
Path path(responseString);
|
||||
if (!g_symbolMap->LoadSymbolMap(path)) {
|
||||
ERROR_LOG(Log::Common, "Failed to load symbol map");
|
||||
}
|
||||
disasm_.DirtySymbolMap();
|
||||
});
|
||||
}
|
||||
if (ImGui::MenuItem("Save .ppmap...")) {
|
||||
System_BrowseForFileSave(reqToken_, "Save PPSSPP symbol map", "symbols.ppmap", BrowseFileType::SYMBOL_MAP, [](const char *responseString, int) {
|
||||
Path path(responseString);
|
||||
if (!g_symbolMap->SaveSymbolMap(path)) {
|
||||
ERROR_LOG(Log::Common, "Failed to save symbol map");
|
||||
}
|
||||
});
|
||||
}
|
||||
if (ImGui::MenuItem("Load No$ .sym...")) {
|
||||
System_BrowseForFile(reqToken_, "Load No$ symbol map", BrowseFileType::SYMBOL_MAP, [&](const char *responseString, int) {
|
||||
Path path(responseString);
|
||||
if (!g_symbolMap->LoadNocashSym(path)) {
|
||||
ERROR_LOG(Log::Common, "Failed to load No$ symbol map");
|
||||
}
|
||||
disasm_.DirtySymbolMap();
|
||||
});
|
||||
}
|
||||
if (ImGui::MenuItem("Save No$ .sym...")) {
|
||||
System_BrowseForFileSave(reqToken_, "Save No$ symbol map", "symbols.sym", BrowseFileType::SYMBOL_MAP, [](const char *responseString, int) {
|
||||
Path path(responseString);
|
||||
if (!g_symbolMap->SaveNocashSym(path)) {
|
||||
ERROR_LOG(Log::Common, "Failed to save No$ symbol map");
|
||||
}
|
||||
});
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem("Compress .ppmap files", nullptr, &g_Config.bCompressSymbols);
|
||||
if (ImGui::MenuItem("Reset symbol map")) {
|
||||
g_symbolMap->Clear();
|
||||
disasm_.DirtySymbolMap();
|
||||
// NotifyDebuggerMapLoaded();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Memory")) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
char title[64];
|
||||
|
@ -1765,14 +1781,17 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu
|
|||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Misc")) {
|
||||
if (ImGui::MenuItem("Close Debugger")) {
|
||||
g_Config.bShowImDebugger = false;
|
||||
}
|
||||
ImGui::MenuItem("PPSSPP Internals", nullptr, &cfg_.internalsOpen);
|
||||
ImGui::MenuItem("Dear ImGui Demo", nullptr, &cfg_.demoOpen);
|
||||
ImGui::MenuItem("Dear ImGui Style editor", nullptr, &cfg_.styleEditorOpen);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
// Let's have this at the top level, to help anyone confused.
|
||||
if (ImGui::BeginMenu("Close Debugger")) {
|
||||
g_Config.bShowImDebugger = false;
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
|
@ -2203,7 +2222,7 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImContro
|
|||
|
||||
if (ImGui::BeginChild("left", ImVec2(150.0f, avail.y), ImGuiChildFlags_ResizeX)) {
|
||||
if (symCache_.empty() || symsDirty_) {
|
||||
symCache_ = g_symbolMap->GetAllSymbols(SymbolType::ST_FUNCTION);
|
||||
symCache_ = g_symbolMap->GetAllActiveSymbols(SymbolType::ST_FUNCTION);
|
||||
symsDirty_ = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -970,7 +970,8 @@ void GameBrowser::Refresh() {
|
|||
|
||||
bool GameBrowser::IsCurrentPathPinned() {
|
||||
const auto paths = g_Config.vPinnedPaths;
|
||||
return std::find(paths.begin(), paths.end(), File::ResolvePath(path_.GetPath().ToString())) != paths.end();
|
||||
std::string resolved = File::ResolvePath(path_.GetPath().ToString());
|
||||
return std::find(paths.begin(), paths.end(), resolved) != paths.end();
|
||||
}
|
||||
|
||||
std::vector<Path> GameBrowser::GetPinnedPaths() const {
|
||||
|
|
Loading…
Add table
Reference in a new issue