1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-07 19:18:19 +02:00
Beef/IDEHelper/DbgSymSrv.cpp

583 lines
No EOL
13 KiB
C++

#include "DbgSymSrv.h"
#include "DbgSymSrv.h"
#include "BeefySysLib/MemStream.h"
#include "BeefySysLib/util/BeefPerf.h"
#include "BeefySysLib/util/CabUtil.h"
#include "NetManager.h"
#include "Compiler/BfUtil.h"
#include "DebugManager.h"
#include <shlobj.h>
#include "COFF.h"
#include "BeefySysLib/util/AllocDebug.h"
// #define BF_DBG_64
// #include "DebugCommon.h"
// #include "COFF.h"
// #undef BF_DBG_64
USING_NS_BF_DBG;
DbgSymRequest::DbgSymRequest()
{
mFailed = false;
mSearchingSymSrv = false;
mCancelling = false;
mInProcess = false;
mLastUpdateTick = 0;
mDbgSymSrv = NULL;
mWantAge = 0;
mMayBeOld = false;
mIsPreCache = false;
}
DbgSymRequest::~DbgSymRequest()
{
}
String DbgSymRequest::GetGuidString()
{
String str;
// Seems like weird ordering, but the guid is really supposed to be (uint32, uint16, uint16, uint8[8])
str += StrFormat("%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
mWantGuid[3], mWantGuid[2], mWantGuid[1], mWantGuid[0],
mWantGuid[5], mWantGuid[4],
mWantGuid[7], mWantGuid[6],
mWantGuid[8], mWantGuid[9], mWantGuid[10], mWantGuid[11], mWantGuid[12], mWantGuid[13], mWantGuid[14], mWantGuid[15]);
return str;
}
String DbgSymRequest::GetPDBStoreDir()
{
String checkPath = "/";
checkPath += GetFileName(mPDBRequested);
checkPath += "/";
checkPath += GetGuidString();
checkPath += StrFormat("%x/", mWantAge);
return checkPath;
}
void DbgSymRequest::Fail(const StringImpl& error)
{
mFailed = true;
if (mError.IsEmpty())
mError = error;
}
bool DbgSymRequest::CheckPDBData(const StringImpl& pdbPath, uint8 outGuid[16], int32& outAge)
{
BP_ZONE("DbgSymRequest::CheckPDBData");
if (!FileExists(pdbPath))
return false;
mDbgSymSrv->mDebugger->OutputRawMessage(StrFormat("symsrv Checking '%s'", pdbPath.c_str()));
COFF coff(NULL);
coff.mParseKind = COFF::ParseKind_Header;
bool result = coff.TryLoadPDB(pdbPath, mWantGuid, mWantAge);
memcpy(outGuid, coff.mPDBGuid, 16);
outAge = coff.mDebugAge;
if (!result)
{
if (outAge != -1)
{
mDbgSymSrv->mDebugger->OutputMessage(StrFormat("ERROR: %s cannot be used due to age mismatch\n", pdbPath.c_str()));
}
else
{
bool hasGuid = false;
for (int i = 0; i < 16; i++)
if (outGuid[i] != 0)
hasGuid = true;
if (hasGuid)
{
mDbgSymSrv->mDebugger->OutputMessage(StrFormat("ERROR: %s cannot be used due to GUID mismatch\n", pdbPath.c_str()));
}
else
{
mDbgSymSrv->mDebugger->OutputMessage(StrFormat("ERROR: %s failed to load\n", pdbPath.c_str()));
}
}
}
return result;
}
bool DbgSymRequest::Get(const StringImpl& url, const StringImpl& destPath, NetResult** chainNetResult, bool ignoreSuccess)
{
if (mIsPreCache)
{
auto netResult = mDbgSymSrv->mDebugger->mDebugManager->mNetManager->QueueGet(url, destPath, true);
if (chainNetResult != NULL)
{
if ((*chainNetResult != NULL) && (netResult != NULL))
mDbgSymSrv->mDebugger->mDebugManager->mNetManager->SetCancelOnSuccess(*chainNetResult, netResult);
if (!ignoreSuccess)
*chainNetResult = netResult;
}
return false;
}
return mDbgSymSrv->mDebugger->mDebugManager->mNetManager->Get(url, destPath);
}
void DbgSymRequest::SearchLocal()
{
if ((gDebugManager->mSymSrvOptions.mFlags & BfSymSrvFlag_Disable) != 0)
{
mFailed = true;
return;
}
uint8 outGuid[16];
int32 outAge;
if (mPDBRequested.IndexOf('\\') != -1) // Do we have an absolute path at all? System dlls won't.
{
// Check actual path
if (CheckPDBData(mPDBRequested, outGuid, outAge))
{
mFinalPDBPath = mPDBRequested;
return;
}
// Check in same dir as module
String checkPath = ::GetFileDir(mModulePath);
checkPath += "\\";
checkPath += GetFileName(mPDBRequested);
if (!FileNameEquals(checkPath, mPDBRequested))
{
if (CheckPDBData(checkPath, outGuid, outAge))
{
mFinalPDBPath = checkPath;
return;
}
}
}
else
{
String checkPath = ::GetFileDir(mModulePath);
checkPath += "\\";
checkPath += mPDBRequested;
if (CheckPDBData(checkPath, outGuid, outAge))
{
mFinalPDBPath = checkPath;
return;
}
}
mMayBeOld = true;
}
void DbgSymRequest::SearchCache()
{
if ((gDebugManager->mSymSrvOptions.mFlags & BfSymSrvFlag_Disable) != 0)
{
mFailed = true;
return;
}
if (mOptions.mCacheDir.IsEmpty())
{
mFailed = true;
return;
}
uint8 outGuid[16];
int32 outAge;
String cacheDir = mOptions.mCacheDir;
cacheDir += GetPDBStoreDir();
String cacheFilePath = cacheDir + "/" + GetFileName(mPDBRequested);
if (CheckPDBData(cacheFilePath, outGuid, outAge))
{
mFinalPDBPath = cacheFilePath;
return;
}
}
void DbgSymRequest::SearchSymSrv()
{
if ((gDebugManager->mSymSrvOptions.mFlags & BfSymSrvFlag_Disable) != 0)
{
mFailed = true;
return;
}
if (mOptions.mCacheDir.IsEmpty())
{
mFailed = true;
return;
}
/*if (mPDBRequested.Contains("IDEHelper"))
Sleep(3000);*/
SetAndRestoreValue<bool> prevSearchingSymSrv(mSearchingSymSrv, true);
NetResult* chainNetResult = NULL;
uint8 outGuid[16];
int32 outAge;
String cacheDir = mOptions.mCacheDir;
cacheDir += GetPDBStoreDir();
//TODO: Remove!
// for (int i = 0; i < 8; i++)
// {
// mDbgSymSrv->mDebugger->OutputMessage(StrFormat("Sleep %d\n", i));
// if (mCancelling)
// break;
// Sleep(1000);
// }
String cacheFilePath = cacheDir + "/" + GetFileName(mPDBRequested);
for (auto& symServAddr : mOptions.mSymbolServers)
{
if (mCancelling)
break;
int colonPos = symServAddr.IndexOf(':');
if (colonPos > 1)
{
// HTTP
bool done = false;
//TODO: Check for 'index2.txt' for two-tiered structure
String checkPath = symServAddr;
checkPath += GetPDBStoreDir();
checkPath += GetFileName(mPDBRequested);
if (Get(checkPath, cacheFilePath, &chainNetResult))
done = true;
if ((!done) && (!mCancelling))
{
// Compressed version
checkPath[checkPath.length() - 1] = '_';
String compCacheFilePath = cacheFilePath;
compCacheFilePath[compCacheFilePath.length() - 1] = '_';
if (Get(checkPath, compCacheFilePath, &chainNetResult))
{
CabFile cabFile;
cabFile.Load(compCacheFilePath);
mDbgSymSrv->mDebugger->OutputRawMessage(StrFormat("symsrv Decompressing '%s'", compCacheFilePath.c_str()));
if (!cabFile.DecompressAll(cacheDir))
{
Fail(cabFile.mError);
return;
}
done = true;
}
}
if ((!done) && (!mCancelling))
{
String checkPath = symServAddr;
checkPath += GetPDBStoreDir();
checkPath += "/file.dir";
String fileDirFilePath = symServAddr;
fileDirFilePath += GetPDBStoreDir();
fileDirFilePath += "/file.dir";
if (Get(checkPath, fileDirFilePath, &chainNetResult, true))
{
// What do I do with this?
}
}
}
else if (!mIsPreCache)
{
// Uncompressed version
String checkPath = symServAddr;
checkPath += GetPDBStoreDir();
checkPath += GetFileName(mPDBRequested);
if (CheckPDBData(checkPath, outGuid, outAge))
{
mFinalPDBPath = checkPath;
return;
}
// Compressed version
checkPath[checkPath.length() - 1] = '_';
if (FileExists(checkPath))
{
CabFile cabFile;
cabFile.Load(checkPath);
mDbgSymSrv->mDebugger->OutputRawMessage(StrFormat("symsrv Decompressing '%s'", checkPath.c_str()));
if (!cabFile.DecompressAll(cacheDir))
{
Fail(cabFile.mError);
return;
}
}
}
if (mIsPreCache)
continue;
// Re-check cache
if (CheckPDBData(cacheFilePath, outGuid, outAge))
{
mFinalPDBPath = cacheFilePath;
return;
}
}
mFailed = true;
}
bool DbgSymRequest::CheckPEFile(const StringImpl& filePath, uint32 fileTime, int size)
{
FileStream fs;
if (!fs.Open(filePath, "rb"))
return false;
PEHeader hdr;
fs.ReadT(hdr);
PE_NTHeaders64 ntHdr64;
fs.SetPos(hdr.e_lfanew);
fs.Read(&ntHdr64, sizeof(PE_NTHeaders64));
if (ntHdr64.mFileHeader.mMachine == PE_MACHINE_X64)
{
if ((ntHdr64.mFileHeader.mTimeDateStamp != fileTime) || (ntHdr64.mOptionalHeader.mSizeOfImage != size))
return false;
}
else
{
PE_NTHeaders32 ntHdr32;
fs.SetPos(hdr.e_lfanew);
fs.Read(&ntHdr32, sizeof(PE_NTHeaders32));
if ((ntHdr32.mFileHeader.mTimeDateStamp != fileTime) || (ntHdr32.mOptionalHeader.mSizeOfImage != size))
return false;
}
return true;
}
String DbgSymRequest::SearchForImage(const String& filePath, uint32 fileTime, int size)
{
if ((gDebugManager->mSymSrvOptions.mFlags & BfSymSrvFlag_Disable) != 0)
return "";
if (FileExists(filePath))
{
if (CheckPEFile(filePath, fileTime, size))
return filePath;
}
/*if (filePath.Contains("IDEHelper"))
Sleep(3000);*/
if (mOptions.mCacheDir.IsEmpty())
{
mFailed = true;
return "";
}
NetResult* chainNetResult = NULL;
String fileName = GetFileName(filePath);
String imageStoreDir = "/";
imageStoreDir += fileName;
imageStoreDir += "/";
imageStoreDir += StrFormat("%08X%x/", fileTime, size);
SetAndRestoreValue<bool> prevSearchingSymSrv(mSearchingSymSrv, true);
String cacheDir = mOptions.mCacheDir;
cacheDir += imageStoreDir;
/*for (int i = 0; i < 8; i++)
{
mDbgSymSrv->mDebugger->OutputMessage(StrFormat("Image Sleep %d\n", i));
if (mCancelling)
break;
Sleep(1000);
}*/
String cacheFilePath = cacheDir + "/" + GetFileName(filePath);
// Check cache
if (CheckPEFile(cacheFilePath, fileTime, size))
{
return cacheFilePath;
}
for (auto& symServAddr : mOptions.mSymbolServers)
{
if (mCancelling)
break;
int colonPos = symServAddr.IndexOf(':');
if (colonPos > 1)
{
// HTTP
bool done = false;
//TODO: Check for 'index2.txt' for two-tiered structure
String checkPath = symServAddr;
checkPath += imageStoreDir;
checkPath += fileName;
if (Get(checkPath, cacheFilePath, &chainNetResult))
done = true;
if ((!done) && (!mCancelling))
{
// Compressed version
checkPath[checkPath.length() - 1] = '_';
String compCacheFilePath = cacheFilePath;
compCacheFilePath[compCacheFilePath.length() - 1] = '_';
if (Get(checkPath, compCacheFilePath, &chainNetResult))
{
mDbgSymSrv->mDebugger->mDebugManager->mOutMessages.push_back(StrFormat("symsrv Decompressing '%s'", checkPath.c_str()));
CabFile cabFile;
cabFile.Load(compCacheFilePath);
if (!cabFile.DecompressAll(cacheDir))
{
Fail(cabFile.mError);
return "";
}
done = true;
}
}
if ((!done) && (!mCancelling))
{
String checkPath = symServAddr;
checkPath += imageStoreDir;
checkPath += "/file.dir";
String fileDirFilePath = symServAddr;
fileDirFilePath += imageStoreDir;
fileDirFilePath += "/file.dir";
if (Get(checkPath, fileDirFilePath, &chainNetResult, true))
{
// What do I do with this?
}
}
}
else if (!mIsPreCache)
{
// Uncompressed version
String checkPath = symServAddr;
checkPath += imageStoreDir;
checkPath += fileName;
if (CheckPEFile(checkPath, fileTime, size))
{
return checkPath;
}
// Compressed version
checkPath[checkPath.length() - 1] = '_';
if (FileExists(checkPath))
{
CabFile cabFile;
cabFile.Load(checkPath);
if (!cabFile.DecompressAll(cacheDir))
{
Fail(cabFile.mError);
return "";
}
}
}
if (mIsPreCache)
continue;
// Re-check cache
if (CheckPEFile(cacheFilePath, fileTime, size))
{
return cacheFilePath;
}
}
if (!mIsPreCache)
{
mDbgSymSrv->mDebugger->mHadImageFindError = true;
mDbgSymSrv->mDebugger->OutputMessage(StrFormat("ERROR: Unable to locate image '%s' (%08X%x). If this file is located on a symbol server, configure the symbol server location in File\\Preferences\\Settings under Debugger\\Symbol File Locations.\n",
GetFileName(filePath).c_str(), fileTime, size));
}
mFailed = true;
return "";
}
void DbgSymRequest::Cancel()
{
mCancelling = true;
mDbgSymSrv->mDebugger->mDebugManager->mNetManager->CancelCurrent();
}
bool DbgSymRequest::IsDone()
{
AutoCrit autoCrit(mCritSect);
if (!mFinalPDBPath.IsEmpty())
return true;
if (mFailed)
return true;
return false;
}
DbgSymSrv::DbgSymSrv(Debugger* debugger)
{
mDebugger = debugger;
}
void DbgSymSrv::PreCacheImage(const String& filePath, uint32 fileTime, int size)
{
auto request = CreateRequest();
request->mIsPreCache = true;
request->SearchForImage(filePath, fileTime, size);
delete request;
}
DbgSymRequest* DbgSymSrv::CreateRequest(const StringImpl& modulePath, const StringImpl& pdbRequested, uint8 wantGuid[16], int32 wantAge)
{
AutoCrit autoCrit(mDebugger->mDebugManager->mCritSect);
DbgSymRequest* symRequest = new DbgSymRequest();
symRequest->mOptions = mDebugger->mDebugManager->mSymSrvOptions;
symRequest->mDbgSymSrv = this;
symRequest->mModulePath = modulePath;
symRequest->mPDBRequested = pdbRequested;
memcpy(symRequest->mWantGuid, wantGuid, 16);
symRequest->mWantAge = wantAge;
return symRequest;
}
DbgSymRequest* DbgSymSrv::CreateRequest()
{
AutoCrit autoCrit(mDebugger->mDebugManager->mCritSect);
DbgSymRequest* symRequest = new DbgSymRequest();
symRequest->mOptions = mDebugger->mDebugManager->mSymSrvOptions;
symRequest->mDbgSymSrv = this;
return symRequest;
}
void DbgSymSrv::ReleaseRequest(DbgSymRequest* dbgSymRequest)
{
delete dbgSymRequest;
}
void DbgSymSrv::Update()
{
}