1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2013 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include <windows.h> 9cb93a386Sopenharmony_ci#include "tools/win_dbghelp.h" 10cb93a386Sopenharmony_ci 11cb93a386Sopenharmony_ci#include <process.h> 12cb93a386Sopenharmony_ci#include <string.h> 13cb93a386Sopenharmony_ci#include <stdlib.h> 14cb93a386Sopenharmony_ci#include <stdio.h> 15cb93a386Sopenharmony_ci 16cb93a386Sopenharmony_ci// Remove prefix addresses. 18 = 2 * (8 digit hexa + 1 space). 17cb93a386Sopenharmony_ci// e.g. "abcd1234 abcd1234 render_pdf!processInput 18cb93a386Sopenharmony_ci#define CDB_CALLSTACK_PREFIX (18) 19cb93a386Sopenharmony_ci 20cb93a386Sopenharmony_ci// CDB.EXE prints a lot of garbage and there is no argument to pass which 21cb93a386Sopenharmony_ci// would remove all that noise. 22cb93a386Sopenharmony_ci// Using eval feature that evaluates a number in hex and prints it to stdout 23cb93a386Sopenharmony_ci// to mark where the callstack is printed. 24cb93a386Sopenharmony_ci// For example, each thread's callstack will be marked with "12340000" at 25cb93a386Sopenharmony_ci// the beginning and "12340001" at the end. 26cb93a386Sopenharmony_ci// We just made up these numbers; they could be anything, as long as they 27cb93a386Sopenharmony_ci// match up with their decimal equivalents. 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_ci#define MARKER_THREAD_CALLSTACK_START_NUMBER "12340000" 30cb93a386Sopenharmony_ci#define MARKER_THREAD_CALLSTACK_START "Evaluate expression: 305397760 = 12340000" 31cb93a386Sopenharmony_ci 32cb93a386Sopenharmony_ci#define MARKER_THREAD_CALLSTACK_STOP_NUMBER "12340001" 33cb93a386Sopenharmony_ci#define MARKER_THREAD_CALLSTACK_STOP "Evaluate expression: 305397761 = 12340001" 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_ci#define MARKER_EXCEPTION_CALLSTACK_START_NUMBER "12340002" 36cb93a386Sopenharmony_ci#define MARKER_EXCEPTION_CALLSTACK_START "Evaluate expression: 305397762 = 12340002" 37cb93a386Sopenharmony_ci 38cb93a386Sopenharmony_ci#define MARKER_EXCEPTION_CALLSTACK_STOP_NUMBER "12340003" 39cb93a386Sopenharmony_ci#define MARKER_EXCEPTION_CALLSTACK_STOP "Evaluate expression: 305397763 = 12340003" 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_ci// k - print stack 42cb93a386Sopenharmony_ci// ? val - evaluate expression. Used to mark the log file. 43cb93a386Sopenharmony_ci// .ecxr - load exception, if exception was thrown. 44cb93a386Sopenharmony_ci// k - print the resolved stack by .ecxr 45cb93a386Sopenharmony_ci// q - quit cdb.exe 46cb93a386Sopenharmony_ci#define CDB_PRINT_CALLSTACK_CURRENT_THREAD "? " MARKER_THREAD_CALLSTACK_START_NUMBER "; k; ? " MARKER_THREAD_CALLSTACK_STOP_NUMBER "; .ecxr; ? " MARKER_EXCEPTION_CALLSTACK_START_NUMBER "; k; ? " MARKER_EXCEPTION_CALLSTACK_STOP_NUMBER "; q" 47cb93a386Sopenharmony_ci 48cb93a386Sopenharmony_cistatic void strncpyOrSetBlank(char* dest, const char* src, size_t len) { 49cb93a386Sopenharmony_ci const char* srcOrEmptyString = (nullptr == src) ? "" : src; 50cb93a386Sopenharmony_ci strncpy(dest, srcOrEmptyString, len); 51cb93a386Sopenharmony_ci} 52cb93a386Sopenharmony_ci 53cb93a386Sopenharmony_cichar debug_app_name[MAX_PATH] = ""; 54cb93a386Sopenharmony_civoid setAppName(const char* app_name) { 55cb93a386Sopenharmony_ci strncpyOrSetBlank(debug_app_name, app_name, sizeof(debug_app_name)); 56cb93a386Sopenharmony_ci} 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_ciconst char* getAppName() { 59cb93a386Sopenharmony_ci return debug_app_name; 60cb93a386Sopenharmony_ci} 61cb93a386Sopenharmony_ci 62cb93a386Sopenharmony_cichar debug_binaries_path[MAX_PATH] = ""; 63cb93a386Sopenharmony_civoid setBinariesPath(const char* binaries_path) { 64cb93a386Sopenharmony_ci strncpyOrSetBlank(debug_binaries_path, binaries_path, 65cb93a386Sopenharmony_ci sizeof(debug_binaries_path)); 66cb93a386Sopenharmony_ci} 67cb93a386Sopenharmony_ci 68cb93a386Sopenharmony_ciconst char* getBinariesPath() { 69cb93a386Sopenharmony_ci return debug_binaries_path; 70cb93a386Sopenharmony_ci} 71cb93a386Sopenharmony_ci 72cb93a386Sopenharmony_cichar debug_app_version[100] = ""; 73cb93a386Sopenharmony_civoid setAppVersion(const char* version) { 74cb93a386Sopenharmony_ci strncpyOrSetBlank(debug_app_version, version, sizeof(debug_app_version)); 75cb93a386Sopenharmony_ci} 76cb93a386Sopenharmony_ci 77cb93a386Sopenharmony_ciconst char* getAppVersion() { 78cb93a386Sopenharmony_ci return debug_app_version; 79cb93a386Sopenharmony_ci} 80cb93a386Sopenharmony_ci 81cb93a386Sopenharmony_cichar debug_cdb_path[MAX_PATH] = ""; 82cb93a386Sopenharmony_civoid setCdbPath(const char* path) { 83cb93a386Sopenharmony_ci strncpyOrSetBlank(debug_cdb_path, path, sizeof(debug_cdb_path)); 84cb93a386Sopenharmony_ci} 85cb93a386Sopenharmony_ci 86cb93a386Sopenharmony_ciconst char* getCdbPath() { 87cb93a386Sopenharmony_ci return debug_cdb_path; 88cb93a386Sopenharmony_ci} 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_ci/** Print all the lines of a CDB k command whicha are callstacks. 91cb93a386Sopenharmony_ci * Callstack lines are marked by start and stop markers and they are prefixed 92cb93a386Sopenharmony_ci * byt 2 hex adresses, which will not be reported. 93cb93a386Sopenharmony_ci */ 94cb93a386Sopenharmony_cistatic void printCallstack(const char* filename, 95cb93a386Sopenharmony_ci const char* start, 96cb93a386Sopenharmony_ci const char* stop) { 97cb93a386Sopenharmony_ci FILE* file = fopen(filename, "rt"); 98cb93a386Sopenharmony_ci char line[1000]; 99cb93a386Sopenharmony_ci bool started = false; 100cb93a386Sopenharmony_ci // Not the most performant code, but this will be used only to collect 101cb93a386Sopenharmony_ci // the callstack from a log files, only when the application had failed. 102cb93a386Sopenharmony_ci while (fgets(line, sizeof(line), file)) { 103cb93a386Sopenharmony_ci if (!started && strncmp(start, line, strlen(start)) == 0) { 104cb93a386Sopenharmony_ci started = true; 105cb93a386Sopenharmony_ci } else if (started && strncmp(stop, line, strlen(stop)) == 0) { 106cb93a386Sopenharmony_ci break; 107cb93a386Sopenharmony_ci } else if (started) { 108cb93a386Sopenharmony_ci // Filter messages. Calstack lines contain "exe/dll!function" 109cb93a386Sopenharmony_ci if (strchr(line, '!') != nullptr && strlen(line) > CDB_CALLSTACK_PREFIX) { 110cb93a386Sopenharmony_ci printf("%s", line + CDB_CALLSTACK_PREFIX); // fgets includes \n already. 111cb93a386Sopenharmony_ci } 112cb93a386Sopenharmony_ci } 113cb93a386Sopenharmony_ci } 114cb93a386Sopenharmony_ci fclose(file); 115cb93a386Sopenharmony_ci} 116cb93a386Sopenharmony_ci 117cb93a386Sopenharmony_ci#define BUILD_UNIQUE_FILENAME(var, ext, szPath, szAppName, szVersion, stLocalTime) \ 118cb93a386Sopenharmony_ci sprintf(szFileName, "%s%s\\%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld" ext, \ 119cb93a386Sopenharmony_ci szPath, szAppName, szVersion, \ 120cb93a386Sopenharmony_ci stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay, \ 121cb93a386Sopenharmony_ci stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond, \ 122cb93a386Sopenharmony_ci GetCurrentProcessId(), GetCurrentThreadId()); 123cb93a386Sopenharmony_ci 124cb93a386Sopenharmony_ci// Exception execution handler. Exception is recognized. Transfer control to 125cb93a386Sopenharmony_ci// the exception handler by executing the __except compound statement, 126cb93a386Sopenharmony_ci// then continue execution after the __except block. 127cb93a386Sopenharmony_ciint GenerateDumpAndPrintCallstack(EXCEPTION_POINTERS* pExceptionPointers) { 128cb93a386Sopenharmony_ci BOOL bMiniDumpSuccessful; 129cb93a386Sopenharmony_ci char szPath[MAX_PATH]; 130cb93a386Sopenharmony_ci char szFileName[MAX_PATH]; 131cb93a386Sopenharmony_ci char szFileNameOutput[MAX_PATH]; 132cb93a386Sopenharmony_ci const char* szAppName = getAppName(); 133cb93a386Sopenharmony_ci const char* szVersion = getAppVersion(); 134cb93a386Sopenharmony_ci DWORD dwBufferSize = MAX_PATH; 135cb93a386Sopenharmony_ci HANDLE hDumpFile; 136cb93a386Sopenharmony_ci SYSTEMTIME stLocalTime; 137cb93a386Sopenharmony_ci MINIDUMP_EXCEPTION_INFORMATION ExpParam; 138cb93a386Sopenharmony_ci 139cb93a386Sopenharmony_ci GetLocalTime( &stLocalTime ); 140cb93a386Sopenharmony_ci GetTempPath( dwBufferSize, szPath ); 141cb93a386Sopenharmony_ci 142cb93a386Sopenharmony_ci sprintf( szFileName, "%s%s", szPath, szAppName ); 143cb93a386Sopenharmony_ci CreateDirectory( szFileName, nullptr ); 144cb93a386Sopenharmony_ci 145cb93a386Sopenharmony_ci BUILD_UNIQUE_FILENAME(szFileName, ".dmp", szPath, szAppName, szVersion, stLocalTime); 146cb93a386Sopenharmony_ci BUILD_UNIQUE_FILENAME(szFileNameOutput, ".out", szPath, szAppName, szVersion, stLocalTime); 147cb93a386Sopenharmony_ci 148cb93a386Sopenharmony_ci hDumpFile = CreateFile(szFileName, 149cb93a386Sopenharmony_ci GENERIC_READ|GENERIC_WRITE, 150cb93a386Sopenharmony_ci FILE_SHARE_WRITE|FILE_SHARE_READ, 151cb93a386Sopenharmony_ci 0, 152cb93a386Sopenharmony_ci CREATE_ALWAYS, 153cb93a386Sopenharmony_ci 0, 154cb93a386Sopenharmony_ci 0); 155cb93a386Sopenharmony_ci 156cb93a386Sopenharmony_ci ExpParam.ThreadId = GetCurrentThreadId(); 157cb93a386Sopenharmony_ci ExpParam.ExceptionPointers = pExceptionPointers; 158cb93a386Sopenharmony_ci ExpParam.ClientPointers = TRUE; 159cb93a386Sopenharmony_ci 160cb93a386Sopenharmony_ci bMiniDumpSuccessful = MiniDumpWriteDump(GetCurrentProcess(), 161cb93a386Sopenharmony_ci GetCurrentProcessId(), 162cb93a386Sopenharmony_ci hDumpFile, 163cb93a386Sopenharmony_ci MiniDumpWithDataSegs, 164cb93a386Sopenharmony_ci &ExpParam, 165cb93a386Sopenharmony_ci nullptr, 166cb93a386Sopenharmony_ci nullptr); 167cb93a386Sopenharmony_ci 168cb93a386Sopenharmony_ci printf("MiniDump file: %s\n", szFileName); 169cb93a386Sopenharmony_ci printf("App exe and pdb: %s\n", getBinariesPath()); 170cb93a386Sopenharmony_ci 171cb93a386Sopenharmony_ci const char* cdbExePath = getCdbPath(); 172cb93a386Sopenharmony_ci if (cdbExePath && *cdbExePath != '\0') { 173cb93a386Sopenharmony_ci printf("Cdb exe: %s\n", cdbExePath); 174cb93a386Sopenharmony_ci 175cb93a386Sopenharmony_ci char command[MAX_PATH * 4]; 176cb93a386Sopenharmony_ci sprintf(command, "%s -y \"%s\" -i \"%s\" -z \"%s\" -c \"%s\" -kqm >\"%s\"", 177cb93a386Sopenharmony_ci cdbExePath, 178cb93a386Sopenharmony_ci getBinariesPath(), 179cb93a386Sopenharmony_ci getBinariesPath(), 180cb93a386Sopenharmony_ci szFileName, 181cb93a386Sopenharmony_ci CDB_PRINT_CALLSTACK_CURRENT_THREAD, 182cb93a386Sopenharmony_ci szFileNameOutput); 183cb93a386Sopenharmony_ci system(command); 184cb93a386Sopenharmony_ci 185cb93a386Sopenharmony_ci printf("\nThread Callstack:\n"); 186cb93a386Sopenharmony_ci printCallstack(szFileNameOutput, 187cb93a386Sopenharmony_ci MARKER_THREAD_CALLSTACK_START, 188cb93a386Sopenharmony_ci MARKER_THREAD_CALLSTACK_STOP); 189cb93a386Sopenharmony_ci 190cb93a386Sopenharmony_ci printf("\nException Callstack:\n"); 191cb93a386Sopenharmony_ci printCallstack(szFileNameOutput, 192cb93a386Sopenharmony_ci MARKER_EXCEPTION_CALLSTACK_START, 193cb93a386Sopenharmony_ci MARKER_EXCEPTION_CALLSTACK_STOP); 194cb93a386Sopenharmony_ci } else { 195cb93a386Sopenharmony_ci printf("Warning: CDB path not set up.\n"); 196cb93a386Sopenharmony_ci } 197cb93a386Sopenharmony_ci 198cb93a386Sopenharmony_ci return EXCEPTION_EXECUTE_HANDLER; 199cb93a386Sopenharmony_ci} 200cb93a386Sopenharmony_ci 201cb93a386Sopenharmony_ci/** Sets the debugging variables. Input parameter is app location. 202cb93a386Sopenharmony_ci * e.g out\Debug\render_pdfs.exe 203cb93a386Sopenharmony_ci * This function expects the .pdb file to be in the same directory. 204cb93a386Sopenharmony_ci */ 205cb93a386Sopenharmony_civoid setUpDebuggingFromArgs(const char* vargs0) { 206cb93a386Sopenharmony_ci size_t i = strlen(vargs0); 207cb93a386Sopenharmony_ci 208cb93a386Sopenharmony_ci if (i >= 4 && _stricmp(vargs0 - 4, ".exe") == 0) { 209cb93a386Sopenharmony_ci // Ignore .exe 210cb93a386Sopenharmony_ci i -= 4; 211cb93a386Sopenharmony_ci } 212cb93a386Sopenharmony_ci 213cb93a386Sopenharmony_ci size_t pos_period = i; 214cb93a386Sopenharmony_ci 215cb93a386Sopenharmony_ci // Find last \ in path - this is Windows! 216cb93a386Sopenharmony_ci while (i >= 0 && vargs0[i] != '\\') { 217cb93a386Sopenharmony_ci i--; 218cb93a386Sopenharmony_ci } 219cb93a386Sopenharmony_ci 220cb93a386Sopenharmony_ci size_t pos_last_slash = i; 221cb93a386Sopenharmony_ci 222cb93a386Sopenharmony_ci char app_name[MAX_PATH]; 223cb93a386Sopenharmony_ci strncpy(app_name, vargs0 + pos_last_slash + 1, pos_period - pos_last_slash - 1); 224cb93a386Sopenharmony_ci app_name[pos_period - pos_last_slash] = '\0'; 225cb93a386Sopenharmony_ci setAppName(app_name); 226cb93a386Sopenharmony_ci 227cb93a386Sopenharmony_ci char binaries_path[MAX_PATH]; 228cb93a386Sopenharmony_ci strncpy(binaries_path, vargs0, pos_last_slash); 229cb93a386Sopenharmony_ci binaries_path[pos_last_slash] = '\0'; 230cb93a386Sopenharmony_ci setBinariesPath(binaries_path); 231cb93a386Sopenharmony_ci 232cb93a386Sopenharmony_ci setAppVersion("1.0"); // Placeholder for now, but use revision instead if we use 233cb93a386Sopenharmony_ci // the minidump for anything else other than 234cb93a386Sopenharmony_ci // collecting the callstack. 235cb93a386Sopenharmony_ci 236cb93a386Sopenharmony_ci // cdb.exe is the app used to load the minidump which prints the callstack. 237cb93a386Sopenharmony_ci char cdbExePath[MAX_PATH]; 238cb93a386Sopenharmony_ci#ifdef _WIN64 239cb93a386Sopenharmony_ci sprintf(cdbExePath, "%s\\x64\\cdb.exe", SK_CDB_PATH); 240cb93a386Sopenharmony_ci#else 241cb93a386Sopenharmony_ci sprintf(cdbExePath, "%s\\cdb.exe", SK_CDB_PATH); 242cb93a386Sopenharmony_ci#endif 243cb93a386Sopenharmony_ci setCdbPath(cdbExePath); 244cb93a386Sopenharmony_ci} 245