Write memory usage to crashdump on Windows.

This commit is contained in:
John Preston 2018-11-12 11:50:40 +04:00
parent af5f85a288
commit 4ab0e693c1
5 changed files with 35 additions and 439 deletions

View file

@ -174,8 +174,7 @@ void SignalHandler(int signum) {
ProcessAnnotations[i.first] = wrapped;
}
const Annotations c_ProcessAnnotations(ProcessAnnotations);
for (const auto &i : c_ProcessAnnotations) {
for (const auto &i : ProcessAnnotations) {
dump() << i.first.c_str() << ": " << i.second.c_str() << "\n";
}
psWriteDump();
@ -260,9 +259,7 @@ void SignalHandler(int signum) {
backtrace_symbols_fd(addresses, size, ReportFileNo);
#else // Q_OS_MAC || Q_OS_LINUX32 || Q_OS_LINUX64
dump() << "\nBacktrace:\n";
psWriteStackTrace();
dump() << "\nBacktrace omitted.\n";
#endif // else for Q_OS_MAC || Q_OS_LINUX32 || Q_OS_LINUX64
dump() << "\n";

View file

@ -746,442 +746,28 @@ void psUpdateOverlayed(TWidget *widget) {
if (!wv) widget->setAttribute(Qt::WA_WState_Visible, false);
}
// Stack walk code is inspired by http://www.codeproject.com/Articles/11132/Walking-the-callstack
static const int StackEntryMaxNameLength = MAX_SYM_NAME + 1;
typedef BOOL(FAR STDAPICALLTYPE *t_SymCleanup)(
_In_ HANDLE hProcess
);
t_SymCleanup symCleanup = 0;
typedef PVOID (FAR STDAPICALLTYPE *t_SymFunctionTableAccess64)(
_In_ HANDLE hProcess,
_In_ DWORD64 AddrBase
);
t_SymFunctionTableAccess64 symFunctionTableAccess64 = 0;
typedef BOOL (FAR STDAPICALLTYPE *t_SymGetLineFromAddr64)(
_In_ HANDLE hProcess,
_In_ DWORD64 dwAddr,
_Out_ PDWORD pdwDisplacement,
_Out_ PIMAGEHLP_LINEW64 Line
);
t_SymGetLineFromAddr64 symGetLineFromAddr64 = 0;
typedef DWORD64 (FAR STDAPICALLTYPE *t_SymGetModuleBase64)(
_In_ HANDLE hProcess,
_In_ DWORD64 qwAddr
);
t_SymGetModuleBase64 symGetModuleBase64 = 0;
typedef BOOL (FAR STDAPICALLTYPE *t_SymGetModuleInfo64)(
_In_ HANDLE hProcess,
_In_ DWORD64 qwAddr,
_Out_ PIMAGEHLP_MODULEW64 ModuleInfo
);
t_SymGetModuleInfo64 symGetModuleInfo64 = 0;
typedef DWORD (FAR STDAPICALLTYPE *t_SymGetOptions)(
VOID
);
t_SymGetOptions symGetOptions = 0;
typedef DWORD (FAR STDAPICALLTYPE *t_SymSetOptions)(
_In_ DWORD SymOptions
);
t_SymSetOptions symSetOptions = 0;
typedef BOOL (FAR STDAPICALLTYPE *t_SymGetSymFromAddr64)(
IN HANDLE hProcess,
IN DWORD64 dwAddr,
OUT PDWORD64 pdwDisplacement,
OUT PIMAGEHLP_SYMBOL64 Symbol
);
t_SymGetSymFromAddr64 symGetSymFromAddr64 = 0;
typedef BOOL (FAR STDAPICALLTYPE *t_SymInitialize)(
_In_ HANDLE hProcess,
_In_opt_ PCWSTR UserSearchPath,
_In_ BOOL fInvadeProcess
);
t_SymInitialize symInitialize = 0;
typedef DWORD64 (FAR STDAPICALLTYPE *t_SymLoadModule64)(
_In_ HANDLE hProcess,
_In_opt_ HANDLE hFile,
_In_opt_ PCSTR ImageName,
_In_opt_ PCSTR ModuleName,
_In_ DWORD64 BaseOfDll,
_In_ DWORD SizeOfDll
);
t_SymLoadModule64 symLoadModule64;
typedef BOOL (FAR STDAPICALLTYPE *t_StackWalk64)(
_In_ DWORD MachineType,
_In_ HANDLE hProcess,
_In_ HANDLE hThread,
_Inout_ LPSTACKFRAME64 StackFrame,
_Inout_ PVOID ContextRecord,
_In_opt_ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
_In_opt_ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
_In_opt_ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
_In_opt_ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
);
t_StackWalk64 stackWalk64 = 0;
typedef DWORD (FAR STDAPICALLTYPE *t_UnDecorateSymbolName)(
PCSTR DecoratedName,
PSTR UnDecoratedName,
DWORD UndecoratedLength,
DWORD Flags
);
t_UnDecorateSymbolName unDecorateSymbolName = 0;
typedef BOOL(FAR STDAPICALLTYPE *t_SymGetSearchPath)(
_In_ HANDLE hProcess,
_Out_writes_(SearchPathLength) PWSTR SearchPath,
_In_ DWORD SearchPathLength
);
t_SymGetSearchPath symGetSearchPath = 0;
BOOL __stdcall ReadProcessMemoryRoutine64(
_In_ HANDLE hProcess,
_In_ DWORD64 qwBaseAddress,
_Out_writes_bytes_(nSize) PVOID lpBuffer,
_In_ DWORD nSize,
_Out_ LPDWORD lpNumberOfBytesRead
) {
SIZE_T st;
BOOL bRet = ReadProcessMemory(hProcess, (LPVOID)qwBaseAddress, lpBuffer, nSize, &st);
*lpNumberOfBytesRead = (DWORD)st;
return bRet;
}
// **************************************** ToolHelp32 ************************
#define MAX_MODULE_NAME32 255
#define TH32CS_SNAPMODULE 0x00000008
#pragma pack( push, 8 )
typedef struct tagMODULEENTRY32
{
DWORD dwSize;
DWORD th32ModuleID; // This module
DWORD th32ProcessID; // owning process
DWORD GlblcntUsage; // Global usage count on the module
DWORD ProccntUsage; // Module usage count in th32ProcessID's context
BYTE * modBaseAddr; // Base address of module in th32ProcessID's context
DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr
HMODULE hModule; // The hModule of this module in th32ProcessID's context
char szModule[MAX_MODULE_NAME32 + 1];
char szExePath[MAX_PATH];
} MODULEENTRY32;
typedef MODULEENTRY32 *PMODULEENTRY32;
typedef MODULEENTRY32 *LPMODULEENTRY32;
#pragma pack( pop )
typedef HANDLE (FAR STDAPICALLTYPE *t_CreateToolhelp32Snapshot)(DWORD dwFlags, DWORD th32ProcessID);
t_CreateToolhelp32Snapshot createToolhelp32Snapshot = 0;
typedef BOOL (FAR STDAPICALLTYPE *t_Module32First)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
t_Module32First module32First = 0;
typedef BOOL (FAR STDAPICALLTYPE *t_Module32Next)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
t_Module32Next module32Next = 0;
bool LoadDbgHelp(bool extended = false) {
if (stackWalk64 && (!extended || symInitialize)) return true;
HMODULE hDll = 0;
WCHAR szTemp[4096];
if (GetModuleFileName(NULL, szTemp, 4096) > 0) {
wcscat_s(szTemp, L".local");
if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES) {
// ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows"
if (GetEnvironmentVariable(L"ProgramFiles", szTemp, 4096) > 0) {
wcscat_s(szTemp, L"\\Debugging Tools for Windows\\dbghelp.dll");
// now check if the file exists:
if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) {
hDll = LoadLibrary(szTemp);
}
}
// Still not found? Then try to load the 64-Bit version:
if (!hDll && (GetEnvironmentVariable(L"ProgramFiles", szTemp, 4096) > 0)) {
wcscat_s(szTemp, L"\\Debugging Tools for Windows 64-Bit\\dbghelp.dll");
if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) {
hDll = LoadLibrary(szTemp);
}
}
}
}
if (!hDll) {
hDll = LoadLibrary(L"DBGHELP.DLL");
}
if (!hDll) return false;
stackWalk64 = (t_StackWalk64)GetProcAddress(hDll, "StackWalk64");
symFunctionTableAccess64 = (t_SymFunctionTableAccess64)GetProcAddress(hDll, "SymFunctionTableAccess64");
symGetModuleBase64 = (t_SymGetModuleBase64)GetProcAddress(hDll, "SymGetModuleBase64");
if (!stackWalk64 ||
!symFunctionTableAccess64 ||
!symGetModuleBase64) {
stackWalk64 = 0;
return false;
}
if (extended) {
HANDLE hProcess = GetCurrentProcess();
DWORD dwProcessId = GetCurrentProcessId();
symGetLineFromAddr64 = (t_SymGetLineFromAddr64)GetProcAddress(hDll, "SymGetLineFromAddrW64");
symGetModuleInfo64 = (t_SymGetModuleInfo64)GetProcAddress(hDll, "SymGetModuleInfoW64");
symGetSymFromAddr64 = (t_SymGetSymFromAddr64)GetProcAddress(hDll, "SymGetSymFromAddr64");
unDecorateSymbolName = (t_UnDecorateSymbolName)GetProcAddress(hDll, "UnDecorateSymbolName");
symInitialize = (t_SymInitialize)GetProcAddress(hDll, "SymInitializeW");
symCleanup = (t_SymCleanup)GetProcAddress(hDll, "SymCleanup");
symGetSearchPath = (t_SymGetSearchPath)GetProcAddress(hDll, "SymGetSearchPathW");
symGetOptions = (t_SymGetOptions)GetProcAddress(hDll, "SymGetOptions");
symSetOptions = (t_SymSetOptions)GetProcAddress(hDll, "SymSetOptions");
symLoadModule64 = (t_SymLoadModule64)GetProcAddress(hDll, "SymLoadModule64");
if (!symGetModuleInfo64 ||
!symGetLineFromAddr64 ||
!symGetSymFromAddr64 ||
!unDecorateSymbolName ||
!symInitialize ||
!symCleanup ||
!symGetOptions ||
!symSetOptions ||
!symLoadModule64) {
symInitialize = 0;
return false;
}
const size_t nSymPathLen = 10 * MAX_PATH;
WCHAR szSymPath[nSymPathLen] = { 0 };
wcscat_s(szSymPath, nSymPathLen, L".;..;");
WCHAR szTemp[MAX_PATH + 1] = { 0 };
if (GetCurrentDirectory(MAX_PATH, szTemp) > 0) {
wcscat_s(szSymPath, nSymPathLen, szTemp);
wcscat_s(szSymPath, nSymPathLen, L";");
}
if (GetModuleFileName(NULL, szTemp, MAX_PATH) > 0) {
for (WCHAR *p = (szTemp + wcslen(szTemp) - 1); p >= szTemp; --p) {
if ((*p == '\\') || (*p == '/') || (*p == ':')) {
*p = 0;
break;
}
}
if (wcslen(szTemp) > 0) {
wcscat_s(szSymPath, nSymPathLen, szTemp);
wcscat_s(szSymPath, nSymPathLen, L";");
}
}
if (GetEnvironmentVariable(L"_NT_SYMBOL_PATH", szTemp, MAX_PATH) > 0) {
wcscat_s(szSymPath, nSymPathLen, szTemp);
wcscat_s(szSymPath, nSymPathLen, L";");
}
if (GetEnvironmentVariable(L"_NT_ALTERNATE_SYMBOL_PATH", szTemp, MAX_PATH) > 0) {
wcscat_s(szSymPath, nSymPathLen, szTemp);
wcscat_s(szSymPath, nSymPathLen, L";");
}
if (GetEnvironmentVariable(L"SYSTEMROOT", szTemp, MAX_PATH) > 0) {
wcscat_s(szSymPath, nSymPathLen, szTemp);
wcscat_s(szSymPath, nSymPathLen, L";");
// also add the "system32"-directory:
wcscat_s(szTemp, MAX_PATH, L"\\system32");
wcscat_s(szSymPath, nSymPathLen, szTemp);
wcscat_s(szSymPath, nSymPathLen, L";");
}
if (GetEnvironmentVariable(L"SYSTEMDRIVE", szTemp, MAX_PATH) > 0) {
wcscat_s(szSymPath, nSymPathLen, L"SRV*");
wcscat_s(szSymPath, nSymPathLen, szTemp);
wcscat_s(szSymPath, nSymPathLen, L"\\websymbols*http://msdl.microsoft.com/download/symbols;");
} else {
wcscat_s(szSymPath, nSymPathLen, L"SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;");
}
if (symInitialize(hProcess, szSymPath, FALSE) == FALSE) {
symInitialize = 0;
return false;
}
DWORD symOptions = symGetOptions();
symOptions |= SYMOPT_LOAD_LINES;
symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
symOptions = symSetOptions(symOptions);
const WCHAR *dllname[] = { L"kernel32.dll", L"tlhelp32.dll" };
HINSTANCE hToolhelp = NULL;
HANDLE hSnap;
MODULEENTRY32 me;
me.dwSize = sizeof(me);
BOOL keepGoing;
size_t i;
for (i = 0; i < (sizeof(dllname) / sizeof(dllname[0])); i++) {
hToolhelp = LoadLibrary(dllname[i]);
if (!hToolhelp) continue;
createToolhelp32Snapshot = (t_CreateToolhelp32Snapshot)GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
module32First = (t_Module32First)GetProcAddress(hToolhelp, "Module32First");
module32Next = (t_Module32Next)GetProcAddress(hToolhelp, "Module32Next");
if (createToolhelp32Snapshot && module32First && module32Next) {
break; // found the functions!
}
FreeLibrary(hToolhelp);
hToolhelp = NULL;
}
if (hToolhelp == NULL) {
return false;
}
hSnap = createToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
if (hSnap == (HANDLE)-1)
return FALSE;
keepGoing = !!module32First(hSnap, &me);
int cnt = 0;
while (keepGoing) {
symLoadModule64(hProcess, 0, me.szExePath, me.szModule, (DWORD64)me.modBaseAddr, me.modBaseSize);
++cnt;
keepGoing = !!module32Next(hSnap, &me);
}
CloseHandle(hSnap);
FreeLibrary(hToolhelp);
return (cnt > 0);
}
return true;
}
struct StackEntry {
DWORD64 offset; // if 0, we have no valid entry
CHAR name[StackEntryMaxNameLength];
CHAR undName[StackEntryMaxNameLength];
CHAR undFullName[StackEntryMaxNameLength];
DWORD64 offsetFromSmybol;
DWORD offsetFromLine;
DWORD lineNumber;
WCHAR lineFileName[StackEntryMaxNameLength];
DWORD symType;
LPCSTR symTypeString;
WCHAR moduleName[StackEntryMaxNameLength];
DWORD64 baseOfImage;
WCHAR loadedImageName[StackEntryMaxNameLength];
};
enum StackEntryType {
StackEntryFirst,
StackEntryNext,
StackEntryLast,
};
char GetModuleInfoData[2 * sizeof(IMAGEHLP_MODULEW64)];
BOOL _getModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULEW64 *pModuleInfo) {
pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULEW64);
memcpy(GetModuleInfoData, pModuleInfo, sizeof(IMAGEHLP_MODULEW64));
if (symGetModuleInfo64(hProcess, baseAddr, (IMAGEHLP_MODULEW64*)GetModuleInfoData) != FALSE) {
// only copy as much memory as is reserved...
memcpy(pModuleInfo, GetModuleInfoData, sizeof(IMAGEHLP_MODULEW64));
pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULEW64);
return TRUE;
}
return FALSE;
}
void psWriteDump() {
}
void psWriteStackTrace() {
#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
if (!LoadDbgHelp()) {
CrashReports::dump() << "ERROR: Could not load dbghelp.dll!\n";
return;
PROCESS_MEMORY_COUNTERS data = { 0 };
if (Dlls::GetProcessMemoryInfo
&& Dlls::GetProcessMemoryInfo(
GetCurrentProcess(),
&data,
sizeof(data))) {
CrashReports::dump()
<< "Memory-usage: "
<< data.PeakWorkingSetSize
<< " (peak), "
<< data.WorkingSetSize
<< " (current)\n";
CrashReports::dump()
<< "Pagefile-usage: "
<< data.PeakPagefileUsage
<< " (peak), "
<< data.PagefileUsage
<< " (current)\n";
}
HANDLE hThread = GetCurrentThread(), hProcess = GetCurrentProcess();
const CONTEXT *context = NULL;
LPVOID pUserData = NULL;
CONTEXT c;
int frameNum;
memset(&c, 0, sizeof(CONTEXT));
c.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&c);
// init STACKFRAME for first call
STACKFRAME64 s; // in/out stackframe
memset(&s, 0, sizeof(s));
DWORD imageType;
#ifdef _M_IX86
// normally, call ImageNtHeader() and use machine info from PE header
imageType = IMAGE_FILE_MACHINE_I386;
s.AddrPC.Offset = c.Eip;
s.AddrPC.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.Ebp;
s.AddrFrame.Mode = AddrModeFlat;
s.AddrStack.Offset = c.Esp;
s.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
imageType = IMAGE_FILE_MACHINE_AMD64;
s.AddrPC.Offset = c.Rip;
s.AddrPC.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.Rsp;
s.AddrFrame.Mode = AddrModeFlat;
s.AddrStack.Offset = c.Rsp;
s.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
imageType = IMAGE_FILE_MACHINE_IA64;
s.AddrPC.Offset = c.StIIP;
s.AddrPC.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.IntSp;
s.AddrFrame.Mode = AddrModeFlat;
s.AddrBStore.Offset = c.RsBSP;
s.AddrBStore.Mode = AddrModeFlat;
s.AddrStack.Offset = c.IntSp;
s.AddrStack.Mode = AddrModeFlat;
#else
#error "Platform not supported!"
#endif
for (frameNum = 0; frameNum < 1024; ++frameNum) {
// get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
// if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
// assume that either you are done, or that the stack is so hosed that the next
// deeper frame could not be found.
// CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
if (!stackWalk64(imageType, hProcess, hThread, &s, &c, ReadProcessMemoryRoutine64, symFunctionTableAccess64, symGetModuleBase64, NULL)) {
CrashReports::dump() << "ERROR: Call to StackWalk64() failed!\n";
return;
}
if (s.AddrPC.Offset == s.AddrReturn.Offset) {
CrashReports::dump() << s.AddrPC.Offset << "\n";
CrashReports::dump() << "ERROR: StackWalk64() endless callstack!";
return;
}
if (s.AddrPC.Offset != 0) { // we seem to have a valid PC
CrashReports::dump() << s.AddrPC.Offset << "\n";
}
if (s.AddrReturn.Offset == 0) {
break;
}
}
#endif // !TDESKTOP_DISABLE_CRASH_REPORTS
#endif // TDESKTOP_DISABLE_CRASH_REPORTS
}
bool psLaunchMaps(const LocationCoords &coords) {

View file

@ -54,7 +54,6 @@ inline void psCheckLocalSocket(const QString &) {
}
void psWriteDump();
void psWriteStackTrace();
void psDeleteDir(const QString &dir);

View file

@ -50,6 +50,7 @@ f_RmRegisterResources RmRegisterResources;
f_RmGetList RmGetList;
f_RmShutdown RmShutdown;
f_RmEndSession RmEndSession;
f_GetProcessMemoryInfo GetProcessMemoryInfo;
HINSTANCE LibUxTheme;
HINSTANCE LibShell32;
@ -58,6 +59,7 @@ HINSTANCE LibPropSys;
HINSTANCE LibComBase;
HINSTANCE LibDwmApi;
HINSTANCE LibRstrtMgr;
HINSTANCE LibPsApi;
void start() {
init();
@ -101,6 +103,9 @@ void start() {
load(LibRstrtMgr, "RmShutdown", RmShutdown);
load(LibRstrtMgr, "RmEndSession", RmEndSession);
}
LibPsApi = LoadLibrary(L"PSAPI.DLL");
load(LibPsApi, "GetProcessMemoryInfo", GetProcessMemoryInfo);
}
} // namespace Dlls

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <roapi.h>
#include <dwmapi.h>
#include <RestartManager.h>
#include <psapi.h>
namespace Platform {
namespace Dlls {
@ -166,5 +167,13 @@ using f_RmEndSession = DWORD(FAR STDAPICALLTYPE*)(
_In_ DWORD dwSessionHandle);
extern f_RmEndSession RmEndSession;
// PSAPI.DLL
using f_GetProcessMemoryInfo = BOOL(FAR STDAPICALLTYPE*)(
HANDLE Process,
PPROCESS_MEMORY_COUNTERS ppsmemCounters,
DWORD cb);
extern f_GetProcessMemoryInfo GetProcessMemoryInfo;
} // namespace Dlls
} // namespace Platform