stack_trace_win.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/debug/stack_trace.h"
  5. #include <windows.h>
  6. #include <dbghelp.h>
  7. #include <stddef.h>
  8. #include <algorithm>
  9. #include <iostream>
  10. #include <iterator>
  11. #include <memory>
  12. #include "base/files/file_path.h"
  13. #include "base/logging.h"
  14. #include "base/memory/singleton.h"
  15. #include "base/strings/string_util.h"
  16. #include "base/strings/stringprintf.h"
  17. #include "base/synchronization/lock.h"
  18. #include "build/build_config.h"
  19. namespace base {
  20. namespace debug {
  21. namespace {
  22. // Previous unhandled filter. Will be called if not NULL when we intercept an
  23. // exception. Only used in unit tests.
  24. LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = NULL;
  25. bool g_initialized_symbols = false;
  26. DWORD g_init_error = ERROR_SUCCESS;
  27. // STATUS_INFO_LENGTH_MISMATCH is declared in <ntstatus.h>, but including that
  28. // header creates a conflict with base/win/windows_types.h, so re-declaring it
  29. // here.
  30. DWORD g_status_info_length_mismatch = 0xC0000004;
  31. // Prints the exception call stack.
  32. // This is the unit tests exception filter.
  33. long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) {
  34. DWORD exc_code = info->ExceptionRecord->ExceptionCode;
  35. std::cerr << "Received fatal exception ";
  36. switch (exc_code) {
  37. case EXCEPTION_ACCESS_VIOLATION:
  38. std::cerr << "EXCEPTION_ACCESS_VIOLATION";
  39. break;
  40. case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
  41. std::cerr << "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
  42. break;
  43. case EXCEPTION_BREAKPOINT:
  44. std::cerr << "EXCEPTION_BREAKPOINT";
  45. break;
  46. case EXCEPTION_DATATYPE_MISALIGNMENT:
  47. std::cerr << "EXCEPTION_DATATYPE_MISALIGNMENT";
  48. break;
  49. case EXCEPTION_FLT_DENORMAL_OPERAND:
  50. std::cerr << "EXCEPTION_FLT_DENORMAL_OPERAND";
  51. break;
  52. case EXCEPTION_FLT_DIVIDE_BY_ZERO:
  53. std::cerr << "EXCEPTION_FLT_DIVIDE_BY_ZERO";
  54. break;
  55. case EXCEPTION_FLT_INEXACT_RESULT:
  56. std::cerr << "EXCEPTION_FLT_INEXACT_RESULT";
  57. break;
  58. case EXCEPTION_FLT_INVALID_OPERATION:
  59. std::cerr << "EXCEPTION_FLT_INVALID_OPERATION";
  60. break;
  61. case EXCEPTION_FLT_OVERFLOW:
  62. std::cerr << "EXCEPTION_FLT_OVERFLOW";
  63. break;
  64. case EXCEPTION_FLT_STACK_CHECK:
  65. std::cerr << "EXCEPTION_FLT_STACK_CHECK";
  66. break;
  67. case EXCEPTION_FLT_UNDERFLOW:
  68. std::cerr << "EXCEPTION_FLT_UNDERFLOW";
  69. break;
  70. case EXCEPTION_ILLEGAL_INSTRUCTION:
  71. std::cerr << "EXCEPTION_ILLEGAL_INSTRUCTION";
  72. break;
  73. case EXCEPTION_IN_PAGE_ERROR:
  74. std::cerr << "EXCEPTION_IN_PAGE_ERROR";
  75. break;
  76. case EXCEPTION_INT_DIVIDE_BY_ZERO:
  77. std::cerr << "EXCEPTION_INT_DIVIDE_BY_ZERO";
  78. break;
  79. case EXCEPTION_INT_OVERFLOW:
  80. std::cerr << "EXCEPTION_INT_OVERFLOW";
  81. break;
  82. case EXCEPTION_INVALID_DISPOSITION:
  83. std::cerr << "EXCEPTION_INVALID_DISPOSITION";
  84. break;
  85. case EXCEPTION_NONCONTINUABLE_EXCEPTION:
  86. std::cerr << "EXCEPTION_NONCONTINUABLE_EXCEPTION";
  87. break;
  88. case EXCEPTION_PRIV_INSTRUCTION:
  89. std::cerr << "EXCEPTION_PRIV_INSTRUCTION";
  90. break;
  91. case EXCEPTION_SINGLE_STEP:
  92. std::cerr << "EXCEPTION_SINGLE_STEP";
  93. break;
  94. case EXCEPTION_STACK_OVERFLOW:
  95. std::cerr << "EXCEPTION_STACK_OVERFLOW";
  96. break;
  97. default:
  98. std::cerr << "0x" << std::hex << exc_code;
  99. break;
  100. }
  101. std::cerr << "\n";
  102. debug::StackTrace(info).Print();
  103. if (g_previous_filter)
  104. return g_previous_filter(info);
  105. return EXCEPTION_CONTINUE_SEARCH;
  106. }
  107. FilePath GetExePath() {
  108. wchar_t system_buffer[MAX_PATH];
  109. GetModuleFileName(NULL, system_buffer, MAX_PATH);
  110. system_buffer[MAX_PATH - 1] = L'\0';
  111. return FilePath(system_buffer);
  112. }
  113. constexpr size_t kSymInitializeRetryCount = 3;
  114. // A wrapper for SymInitialize. SymInitialize seems to occasionally fail
  115. // because of an internal race condition. So wrap it and retry a finite
  116. // number of times.
  117. // See crbug.com/1339753
  118. bool SymInitializeWrapper(HANDLE handle, BOOL invade_process) {
  119. for (size_t i = 0; i < kSymInitializeRetryCount; ++i) {
  120. if (SymInitialize(handle, nullptr, invade_process))
  121. return true;
  122. g_init_error = GetLastError();
  123. if (g_init_error != g_status_info_length_mismatch)
  124. return false;
  125. }
  126. DLOG(ERROR) << "SymInitialize failed repeatedly.";
  127. return false;
  128. }
  129. bool SymInitializeCurrentProc() {
  130. const HANDLE current_process = GetCurrentProcess();
  131. if (SymInitializeWrapper(current_process, TRUE))
  132. return true;
  133. // g_init_error is updated by SymInitializeWrapper.
  134. // No need to do "g_init_error = GetLastError()" here.
  135. if (g_init_error != ERROR_INVALID_PARAMETER)
  136. return false;
  137. // SymInitialize() can fail with ERROR_INVALID_PARAMETER when something has
  138. // already called SymInitialize() in this process. For example, when absl
  139. // support for gtest is enabled, it results in absl calling SymInitialize()
  140. // almost immediately after startup. In such a case, try to reinit to see if
  141. // that succeeds.
  142. SymCleanup(current_process);
  143. if (SymInitializeWrapper(current_process, TRUE))
  144. return true;
  145. return false;
  146. }
  147. bool InitializeSymbols() {
  148. if (g_initialized_symbols) {
  149. // Force a reinitialization. Will ensure any modules loaded after process
  150. // startup also get symbolized.
  151. SymCleanup(GetCurrentProcess());
  152. g_initialized_symbols = false;
  153. }
  154. g_initialized_symbols = true;
  155. // Defer symbol load until they're needed, use undecorated names, and get line
  156. // numbers.
  157. SymSetOptions(SYMOPT_DEFERRED_LOADS |
  158. SYMOPT_UNDNAME |
  159. SYMOPT_LOAD_LINES);
  160. if (!SymInitializeCurrentProc()) {
  161. // When it fails, we should not call debugbreak since it kills the current
  162. // process (prevents future tests from running or kills the browser
  163. // process).
  164. DLOG(ERROR) << "SymInitialize failed: " << g_init_error;
  165. return false;
  166. }
  167. // When transferring the binaries e.g. between bots, path put
  168. // into the executable will get off. To still retrieve symbols correctly,
  169. // add the directory of the executable to symbol search path.
  170. // All following errors are non-fatal.
  171. static constexpr size_t kSymbolsArraySize = 1024;
  172. wchar_t symbols_path[kSymbolsArraySize];
  173. // Note: The below function takes buffer size as number of characters,
  174. // not number of bytes!
  175. if (!SymGetSearchPathW(GetCurrentProcess(), symbols_path,
  176. kSymbolsArraySize)) {
  177. g_init_error = GetLastError();
  178. DLOG(WARNING) << "SymGetSearchPath failed: " << g_init_error;
  179. return false;
  180. }
  181. std::wstring new_path = StringPrintf(L"%ls;%ls", symbols_path,
  182. GetExePath().DirName().value().c_str());
  183. if (!SymSetSearchPathW(GetCurrentProcess(), new_path.c_str())) {
  184. g_init_error = GetLastError();
  185. DLOG(WARNING) << "SymSetSearchPath failed." << g_init_error;
  186. return false;
  187. }
  188. g_init_error = ERROR_SUCCESS;
  189. return true;
  190. }
  191. // SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family
  192. // of functions. The Sym* family of functions may only be invoked by one
  193. // thread at a time. SymbolContext code may access a symbol server over the
  194. // network while holding the lock for this singleton. In the case of high
  195. // latency, this code will adversely affect performance.
  196. //
  197. // There is also a known issue where this backtrace code can interact
  198. // badly with breakpad if breakpad is invoked in a separate thread while
  199. // we are using the Sym* functions. This is because breakpad does now
  200. // share a lock with this function. See this related bug:
  201. //
  202. // https://crbug.com/google-breakpad/311
  203. //
  204. // This is a very unlikely edge case, and the current solution is to
  205. // just ignore it.
  206. class SymbolContext {
  207. public:
  208. static SymbolContext* GetInstance() {
  209. // We use a leaky singleton because code may call this during process
  210. // termination.
  211. return
  212. Singleton<SymbolContext, LeakySingletonTraits<SymbolContext> >::get();
  213. }
  214. SymbolContext(const SymbolContext&) = delete;
  215. SymbolContext& operator=(const SymbolContext&) = delete;
  216. // For the given trace, attempts to resolve the symbols, and output a trace
  217. // to the ostream os. The format for each line of the backtrace is:
  218. //
  219. // <tab>SymbolName[0xAddress+Offset] (FileName:LineNo)
  220. //
  221. // This function should only be called if Init() has been called. We do not
  222. // LOG(FATAL) here because this code is called might be triggered by a
  223. // LOG(FATAL) itself. Also, it should not be calling complex code that is
  224. // extensible like PathService since that can in turn fire CHECKs.
  225. void OutputTraceToStream(const void* const* trace,
  226. size_t count,
  227. std::ostream* os,
  228. const char* prefix_string) {
  229. AutoLock lock(lock_);
  230. for (size_t i = 0; (i < count) && os->good(); ++i) {
  231. const int kMaxNameLength = 256;
  232. DWORD_PTR frame = reinterpret_cast<DWORD_PTR>(trace[i]);
  233. // Code adapted from MSDN example:
  234. // http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
  235. ULONG64 buffer[
  236. (sizeof(SYMBOL_INFO) +
  237. kMaxNameLength * sizeof(wchar_t) +
  238. sizeof(ULONG64) - 1) /
  239. sizeof(ULONG64)];
  240. memset(buffer, 0, sizeof(buffer));
  241. // Initialize symbol information retrieval structures.
  242. DWORD64 sym_displacement = 0;
  243. PSYMBOL_INFO symbol = reinterpret_cast<PSYMBOL_INFO>(&buffer[0]);
  244. symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
  245. symbol->MaxNameLen = kMaxNameLength - 1;
  246. BOOL has_symbol = SymFromAddr(GetCurrentProcess(), frame,
  247. &sym_displacement, symbol);
  248. // Attempt to retrieve line number information.
  249. DWORD line_displacement = 0;
  250. IMAGEHLP_LINE64 line = {};
  251. line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
  252. BOOL has_line = SymGetLineFromAddr64(GetCurrentProcess(), frame,
  253. &line_displacement, &line);
  254. // Output the backtrace line.
  255. if (prefix_string)
  256. (*os) << prefix_string;
  257. (*os) << "\t";
  258. if (has_symbol) {
  259. (*os) << symbol->Name << " [0x" << trace[i] << "+"
  260. << sym_displacement << "]";
  261. } else {
  262. // If there is no symbol information, add a spacer.
  263. (*os) << "(No symbol) [0x" << trace[i] << "]";
  264. }
  265. if (has_line) {
  266. (*os) << " (" << line.FileName << ":" << line.LineNumber << ")";
  267. }
  268. (*os) << "\n";
  269. }
  270. }
  271. private:
  272. friend struct DefaultSingletonTraits<SymbolContext>;
  273. SymbolContext() {
  274. InitializeSymbols();
  275. }
  276. Lock lock_;
  277. };
  278. } // namespace
  279. bool EnableInProcessStackDumping() {
  280. // Add stack dumping support on exception on windows. Similar to OS_POSIX
  281. // signal() handling in process_util_posix.cc.
  282. g_previous_filter = SetUnhandledExceptionFilter(&StackDumpExceptionFilter);
  283. // Need to initialize symbols early in the process or else this fails on
  284. // swarming (since symbols are in different directory than in the exes) and
  285. // also release x64.
  286. return InitializeSymbols();
  287. }
  288. NOINLINE size_t CollectStackTrace(void** trace, size_t count) {
  289. // When walking our own stack, use CaptureStackBackTrace().
  290. return CaptureStackBackTrace(0, count, trace, NULL);
  291. }
  292. StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) {
  293. InitTrace(exception_pointers->ContextRecord);
  294. }
  295. StackTrace::StackTrace(const CONTEXT* context) {
  296. InitTrace(context);
  297. }
  298. void StackTrace::InitTrace(const CONTEXT* context_record) {
  299. // StackWalk64 modifies the register context in place, so we have to copy it
  300. // so that downstream exception handlers get the right context. The incoming
  301. // context may have had more register state (YMM, etc) than we need to unwind
  302. // the stack. Typically StackWalk64 only needs integer and control registers.
  303. CONTEXT context_copy;
  304. memcpy(&context_copy, context_record, sizeof(context_copy));
  305. context_copy.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
  306. // When walking an exception stack, we need to use StackWalk64().
  307. count_ = 0;
  308. // Initialize stack walking.
  309. STACKFRAME64 stack_frame;
  310. memset(&stack_frame, 0, sizeof(stack_frame));
  311. #if defined(ARCH_CPU_X86_64)
  312. DWORD machine_type = IMAGE_FILE_MACHINE_AMD64;
  313. stack_frame.AddrPC.Offset = context_record->Rip;
  314. stack_frame.AddrFrame.Offset = context_record->Rbp;
  315. stack_frame.AddrStack.Offset = context_record->Rsp;
  316. #elif defined(ARCH_CPU_ARM64)
  317. DWORD machine_type = IMAGE_FILE_MACHINE_ARM64;
  318. stack_frame.AddrPC.Offset = context_record->Pc;
  319. stack_frame.AddrFrame.Offset = context_record->Fp;
  320. stack_frame.AddrStack.Offset = context_record->Sp;
  321. #elif defined(ARCH_CPU_X86)
  322. DWORD machine_type = IMAGE_FILE_MACHINE_I386;
  323. stack_frame.AddrPC.Offset = context_record->Eip;
  324. stack_frame.AddrFrame.Offset = context_record->Ebp;
  325. stack_frame.AddrStack.Offset = context_record->Esp;
  326. #else
  327. #error Unsupported Windows Arch
  328. #endif
  329. stack_frame.AddrPC.Mode = AddrModeFlat;
  330. stack_frame.AddrFrame.Mode = AddrModeFlat;
  331. stack_frame.AddrStack.Mode = AddrModeFlat;
  332. while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(),
  333. &stack_frame, &context_copy, NULL,
  334. &SymFunctionTableAccess64, &SymGetModuleBase64, NULL) &&
  335. count_ < std::size(trace_)) {
  336. trace_[count_++] = reinterpret_cast<void*>(stack_frame.AddrPC.Offset);
  337. }
  338. for (size_t i = count_; i < std::size(trace_); ++i)
  339. trace_[i] = NULL;
  340. }
  341. void StackTrace::PrintWithPrefix(const char* prefix_string) const {
  342. OutputToStreamWithPrefix(&std::cerr, prefix_string);
  343. }
  344. void StackTrace::OutputToStreamWithPrefix(std::ostream* os,
  345. const char* prefix_string) const {
  346. SymbolContext* context = SymbolContext::GetInstance();
  347. if (g_init_error != ERROR_SUCCESS) {
  348. (*os) << "Error initializing symbols (" << g_init_error
  349. << "). Dumping unresolved backtrace:\n";
  350. for (size_t i = 0; (i < count_) && os->good(); ++i) {
  351. if (prefix_string)
  352. (*os) << prefix_string;
  353. (*os) << "\t" << trace_[i] << "\n";
  354. }
  355. } else {
  356. (*os) << "Backtrace:\n";
  357. context->OutputTraceToStream(trace_, count_, os, prefix_string);
  358. }
  359. }
  360. } // namespace debug
  361. } // namespace base