win_dbghelp.cpp 8.6 KB

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