blob: c813fedba92ed9e76e3bc812f38b63820bdc7c5e [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2012 The Chromium Authors
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
[email protected]1ffe08c12008-08-13 11:15:114
[email protected]58580352010-10-26 04:07:505#include "base/debug/stack_trace.h"
[email protected]96fd0032009-04-24 00:13:086
[email protected]1ffe08c12008-08-13 11:15:117#include <windows.h>
Peter Birk Pakkenberg9e3302d2022-06-30 09:40:158
[email protected]96fd0032009-04-24 00:13:089#include <dbghelp.h>
aviebe805c2015-12-24 08:20:2810#include <stddef.h>
[email protected]1ffe08c12008-08-13 11:15:1111
wez73f8d7e2017-01-29 06:18:1312#include <algorithm>
[email protected]f5393332009-06-03 15:01:2913#include <iostream>
Daniel Cheng84cf5182022-03-05 01:00:0514#include <iterator>
anantaf2651872016-06-16 22:21:0215#include <memory>
[email protected]f5393332009-06-03 15:01:2916
Tom Sepez978847c2025-03-22 03:43:5917#include "base/compiler_specific.h"
anantaf2651872016-06-16 22:21:0218#include "base/files/file_path.h"
[email protected]96fd0032009-04-24 00:13:0819#include "base/logging.h"
[email protected]3b63f8f42011-03-28 01:54:1520#include "base/memory/singleton.h"
Peter Kastingbfb5e7d2023-09-21 23:59:3521#include "base/strings/strcat_win.h"
jdoerrie5c4dc4e2019-02-01 18:02:3322#include "base/strings/string_util.h"
Alex Gough02b452c2025-11-26 18:44:4723#include "base/strings/stringprintf.h"
[email protected]20305ec2011-01-21 04:55:5224#include "base/synchronization/lock.h"
Tom Tan15d3ed32018-11-14 18:20:5025#include "build/build_config.h"
[email protected]1ffe08c12008-08-13 11:15:1126
[email protected]58580352010-10-26 04:07:5027namespace base {
28namespace debug {
29
[email protected]1ffe08c12008-08-13 11:15:1130namespace {
31
[email protected]11b93faa2012-11-01 21:58:3032// Previous unhandled filter. Will be called if not NULL when we intercept an
33// exception. Only used in unit tests.
34LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = NULL;
35
jam79dc59a2015-08-17 03:38:1636bool g_initialized_symbols = false;
37DWORD g_init_error = ERROR_SUCCESS;
Peter Birk Pakkenberg9e3302d2022-06-30 09:40:1538// STATUS_INFO_LENGTH_MISMATCH is declared in <ntstatus.h>, but including that
39// header creates a conflict with base/win/windows_types.h, so re-declaring it
40// here.
41DWORD g_status_info_length_mismatch = 0xC0000004;
jam79dc59a2015-08-17 03:38:1642
Alex Goughd007d622025-09-11 17:55:4943// Are symbolized in-process stack dumps enabled?
44bool g_in_process_stack_dumps_enabled = false;
45
[email protected]11b93faa2012-11-01 21:58:3046// Prints the exception call stack.
47// This is the unit tests exception filter.
48long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) {
Peter Collingbourne76a6655c2017-10-12 18:01:0349 DWORD exc_code = info->ExceptionRecord->ExceptionCode;
50 std::cerr << "Received fatal exception ";
51 switch (exc_code) {
52 case EXCEPTION_ACCESS_VIOLATION:
53 std::cerr << "EXCEPTION_ACCESS_VIOLATION";
54 break;
55 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
56 std::cerr << "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
57 break;
58 case EXCEPTION_BREAKPOINT:
59 std::cerr << "EXCEPTION_BREAKPOINT";
60 break;
61 case EXCEPTION_DATATYPE_MISALIGNMENT:
62 std::cerr << "EXCEPTION_DATATYPE_MISALIGNMENT";
63 break;
64 case EXCEPTION_FLT_DENORMAL_OPERAND:
65 std::cerr << "EXCEPTION_FLT_DENORMAL_OPERAND";
66 break;
67 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
68 std::cerr << "EXCEPTION_FLT_DIVIDE_BY_ZERO";
69 break;
70 case EXCEPTION_FLT_INEXACT_RESULT:
71 std::cerr << "EXCEPTION_FLT_INEXACT_RESULT";
72 break;
73 case EXCEPTION_FLT_INVALID_OPERATION:
74 std::cerr << "EXCEPTION_FLT_INVALID_OPERATION";
75 break;
76 case EXCEPTION_FLT_OVERFLOW:
77 std::cerr << "EXCEPTION_FLT_OVERFLOW";
78 break;
79 case EXCEPTION_FLT_STACK_CHECK:
80 std::cerr << "EXCEPTION_FLT_STACK_CHECK";
81 break;
82 case EXCEPTION_FLT_UNDERFLOW:
83 std::cerr << "EXCEPTION_FLT_UNDERFLOW";
84 break;
85 case EXCEPTION_ILLEGAL_INSTRUCTION:
86 std::cerr << "EXCEPTION_ILLEGAL_INSTRUCTION";
87 break;
88 case EXCEPTION_IN_PAGE_ERROR:
89 std::cerr << "EXCEPTION_IN_PAGE_ERROR";
90 break;
91 case EXCEPTION_INT_DIVIDE_BY_ZERO:
92 std::cerr << "EXCEPTION_INT_DIVIDE_BY_ZERO";
93 break;
94 case EXCEPTION_INT_OVERFLOW:
95 std::cerr << "EXCEPTION_INT_OVERFLOW";
96 break;
97 case EXCEPTION_INVALID_DISPOSITION:
98 std::cerr << "EXCEPTION_INVALID_DISPOSITION";
99 break;
100 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
101 std::cerr << "EXCEPTION_NONCONTINUABLE_EXCEPTION";
102 break;
103 case EXCEPTION_PRIV_INSTRUCTION:
104 std::cerr << "EXCEPTION_PRIV_INSTRUCTION";
105 break;
106 case EXCEPTION_SINGLE_STEP:
107 std::cerr << "EXCEPTION_SINGLE_STEP";
108 break;
109 case EXCEPTION_STACK_OVERFLOW:
110 std::cerr << "EXCEPTION_STACK_OVERFLOW";
111 break;
112 default:
Alex Gough02b452c2025-11-26 18:44:47113 std::cerr << base::StringPrintf("0x%08x", exc_code);
Peter Collingbourne76a6655c2017-10-12 18:01:03114 break;
115 }
116 std::cerr << "\n";
117
[email protected]5ddbf1c2013-08-29 01:59:38118 debug::StackTrace(info).Print();
Peter Kasting134ef9af2024-12-28 02:30:09119 if (g_previous_filter) {
[email protected]11b93faa2012-11-01 21:58:30120 return g_previous_filter(info);
Peter Kasting134ef9af2024-12-28 02:30:09121 }
[email protected]11b93faa2012-11-01 21:58:30122 return EXCEPTION_CONTINUE_SEARCH;
123}
124
[email protected]d285aba2014-07-03 21:09:20125FilePath GetExePath() {
danakjd53babfe2024-10-09 14:04:16126 std::array<wchar_t, MAX_PATH> system_buffer;
127 GetModuleFileName(NULL, system_buffer.data(), system_buffer.size());
128 system_buffer.back() = L'\0';
129 return FilePath(system_buffer.data());
[email protected]d285aba2014-07-03 21:09:20130}
131
Peter Birk Pakkenberg9e3302d2022-06-30 09:40:15132constexpr size_t kSymInitializeRetryCount = 3;
133
134// A wrapper for SymInitialize. SymInitialize seems to occasionally fail
135// because of an internal race condition. So wrap it and retry a finite
136// number of times.
137// See crbug.com/1339753
138bool SymInitializeWrapper(HANDLE handle, BOOL invade_process) {
139 for (size_t i = 0; i < kSymInitializeRetryCount; ++i) {
Peter Kasting134ef9af2024-12-28 02:30:09140 if (SymInitialize(handle, nullptr, invade_process)) {
Peter Birk Pakkenberg9e3302d2022-06-30 09:40:15141 return true;
Peter Kasting134ef9af2024-12-28 02:30:09142 }
Peter Birk Pakkenberg9e3302d2022-06-30 09:40:15143
144 g_init_error = GetLastError();
Peter Kasting134ef9af2024-12-28 02:30:09145 if (g_init_error != g_status_info_length_mismatch) {
Peter Birk Pakkenberg9e3302d2022-06-30 09:40:15146 return false;
Peter Kasting134ef9af2024-12-28 02:30:09147 }
Peter Birk Pakkenberg9e3302d2022-06-30 09:40:15148 }
149 DLOG(ERROR) << "SymInitialize failed repeatedly.";
150 return false;
151}
152
Peter Kastingb5edda772020-07-31 22:35:47153bool SymInitializeCurrentProc() {
154 const HANDLE current_process = GetCurrentProcess();
Peter Kasting134ef9af2024-12-28 02:30:09155 if (SymInitializeWrapper(current_process, TRUE)) {
Peter Kastingb5edda772020-07-31 22:35:47156 return true;
Peter Kasting134ef9af2024-12-28 02:30:09157 }
Peter Kastingb5edda772020-07-31 22:35:47158
Peter Birk Pakkenberg9e3302d2022-06-30 09:40:15159 // g_init_error is updated by SymInitializeWrapper.
160 // No need to do "g_init_error = GetLastError()" here.
Peter Kasting134ef9af2024-12-28 02:30:09161 if (g_init_error != ERROR_INVALID_PARAMETER) {
Peter Kastingb5edda772020-07-31 22:35:47162 return false;
Peter Kasting134ef9af2024-12-28 02:30:09163 }
Peter Kastingb5edda772020-07-31 22:35:47164
165 // SymInitialize() can fail with ERROR_INVALID_PARAMETER when something has
166 // already called SymInitialize() in this process. For example, when absl
167 // support for gtest is enabled, it results in absl calling SymInitialize()
168 // almost immediately after startup. In such a case, try to reinit to see if
169 // that succeeds.
170 SymCleanup(current_process);
Peter Kasting134ef9af2024-12-28 02:30:09171 if (SymInitializeWrapper(current_process, TRUE)) {
Peter Kastingb5edda772020-07-31 22:35:47172 return true;
Peter Kasting134ef9af2024-12-28 02:30:09173 }
Peter Kastingb5edda772020-07-31 22:35:47174
Peter Kastingb5edda772020-07-31 22:35:47175 return false;
176}
177
jam79dc59a2015-08-17 03:38:16178bool InitializeSymbols() {
Jamie Madilla1b600e52019-04-12 16:32:35179 if (g_initialized_symbols) {
180 // Force a reinitialization. Will ensure any modules loaded after process
181 // startup also get symbolized.
182 SymCleanup(GetCurrentProcess());
183 g_initialized_symbols = false;
184 }
jam79dc59a2015-08-17 03:38:16185 g_initialized_symbols = true;
186 // Defer symbol load until they're needed, use undecorated names, and get line
187 // numbers.
Peter Kasting134ef9af2024-12-28 02:30:09188 SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
Peter Kastingb5edda772020-07-31 22:35:47189 if (!SymInitializeCurrentProc()) {
jam79dc59a2015-08-17 03:38:16190 // When it fails, we should not call debugbreak since it kills the current
191 // process (prevents future tests from running or kills the browser
192 // process).
193 DLOG(ERROR) << "SymInitialize failed: " << g_init_error;
194 return false;
195 }
196
197 // When transferring the binaries e.g. between bots, path put
198 // into the executable will get off. To still retrieve symbols correctly,
199 // add the directory of the executable to symbol search path.
200 // All following errors are non-fatal.
jdoerrie5c4dc4e2019-02-01 18:02:33201 static constexpr size_t kSymbolsArraySize = 1024;
Jan Wilken Dörrieb630aca72019-12-04 10:59:11202 wchar_t symbols_path[kSymbolsArraySize];
jam79dc59a2015-08-17 03:38:16203
204 // Note: The below function takes buffer size as number of characters,
205 // not number of bytes!
Jan Wilken Dörrieb630aca72019-12-04 10:59:11206 if (!SymGetSearchPathW(GetCurrentProcess(), symbols_path,
jam79dc59a2015-08-17 03:38:16207 kSymbolsArraySize)) {
jam79dc59a2015-08-17 03:38:16208 g_init_error = GetLastError();
brucedawson4af09d1d2015-09-17 20:21:44209 DLOG(WARNING) << "SymGetSearchPath failed: " << g_init_error;
jam79dc59a2015-08-17 03:38:16210 return false;
211 }
212
Peter Kastingbfb5e7d2023-09-21 23:59:35213 std::wstring new_path =
214 StrCat({symbols_path, L";", GetExePath().DirName().value()});
Jan Wilken Dörrieb630aca72019-12-04 10:59:11215 if (!SymSetSearchPathW(GetCurrentProcess(), new_path.c_str())) {
jam79dc59a2015-08-17 03:38:16216 g_init_error = GetLastError();
217 DLOG(WARNING) << "SymSetSearchPath failed." << g_init_error;
218 return false;
219 }
220
221 g_init_error = ERROR_SUCCESS;
222 return true;
223}
224
[email protected]96fd0032009-04-24 00:13:08225// SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family
226// of functions. The Sym* family of functions may only be invoked by one
227// thread at a time. SymbolContext code may access a symbol server over the
228// network while holding the lock for this singleton. In the case of high
[email protected]e645c7a2012-07-25 21:43:44229// latency, this code will adversely affect performance.
[email protected]96fd0032009-04-24 00:13:08230//
231// There is also a known issue where this backtrace code can interact
232// badly with breakpad if breakpad is invoked in a separate thread while
233// we are using the Sym* functions. This is because breakpad does now
234// share a lock with this function. See this related bug:
235//
Mark Mentovaiebb9ddd62017-09-25 17:24:41236// https://crbug.com/google-breakpad/311
[email protected]96fd0032009-04-24 00:13:08237//
238// This is a very unlikely edge case, and the current solution is to
239// just ignore it.
240class SymbolContext {
241 public:
[email protected]8e8bb6d2010-12-13 08:18:55242 static SymbolContext* GetInstance() {
[email protected]96fd0032009-04-24 00:13:08243 // We use a leaky singleton because code may call this during process
244 // termination.
Peter Kasting134ef9af2024-12-28 02:30:09245 return Singleton<SymbolContext, LeakySingletonTraits<SymbolContext>>::get();
[email protected]96fd0032009-04-24 00:13:08246 }
247
Peter Boström75cd3c02021-09-28 15:23:18248 SymbolContext(const SymbolContext&) = delete;
249 SymbolContext& operator=(const SymbolContext&) = delete;
250
[email protected]96fd0032009-04-24 00:13:08251 // For the given trace, attempts to resolve the symbols, and output a trace
252 // to the ostream os. The format for each line of the backtrace is:
253 //
254 // <tab>SymbolName[0xAddress+Offset] (FileName:LineNo)
[email protected]f5393332009-06-03 15:01:29255 //
[email protected]96fd0032009-04-24 00:13:08256 // This function should only be called if Init() has been called. We do not
257 // LOG(FATAL) here because this code is called might be triggered by a
[email protected]d285aba2014-07-03 21:09:20258 // LOG(FATAL) itself. Also, it should not be calling complex code that is
259 // extensible like PathService since that can in turn fire CHECKs.
Jan Keitel4161bcb2024-08-01 12:44:24260 void OutputTraceToStream(base::span<const void* const> traces,
Mason Freedb9ef2b62018-09-10 17:17:27261 std::ostream* os,
Greg Thompson8b1b295b2024-04-10 07:44:14262 cstring_view prefix_string) {
jdoerrie5c4dc4e2019-02-01 18:02:33263 AutoLock lock(lock_);
[email protected]96fd0032009-04-24 00:13:08264
Jan Keitel4161bcb2024-08-01 12:44:24265 for (size_t i = 0; (i < traces.size()) && os->good(); ++i) {
[email protected]96fd0032009-04-24 00:13:08266 const int kMaxNameLength = 256;
Jan Keitel4161bcb2024-08-01 12:44:24267 DWORD_PTR frame = reinterpret_cast<DWORD_PTR>(traces[i]);
[email protected]96fd0032009-04-24 00:13:08268
269 // Code adapted from MSDN example:
270 // http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
Peter Kasting134ef9af2024-12-28 02:30:09271 ULONG64 buffer[(sizeof(SYMBOL_INFO) + kMaxNameLength * sizeof(wchar_t) +
272 sizeof(ULONG64) - 1) /
273 sizeof(ULONG64)];
Tom Sepez978847c2025-03-22 03:43:59274 UNSAFE_TODO(memset(buffer, 0, sizeof(buffer)));
[email protected]96fd0032009-04-24 00:13:08275
276 // Initialize symbol information retrieval structures.
277 DWORD64 sym_displacement = 0;
278 PSYMBOL_INFO symbol = reinterpret_cast<PSYMBOL_INFO>(&buffer[0]);
279 symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
[email protected]b3f97bc72009-10-27 15:54:05280 symbol->MaxNameLen = kMaxNameLength - 1;
Peter Kasting134ef9af2024-12-28 02:30:09281 BOOL has_symbol =
282 SymFromAddr(GetCurrentProcess(), frame, &sym_displacement, symbol);
[email protected]96fd0032009-04-24 00:13:08283
284 // Attempt to retrieve line number information.
285 DWORD line_displacement = 0;
286 IMAGEHLP_LINE64 line = {};
287 line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
[email protected]de1b764f2009-08-24 15:36:41288 BOOL has_line = SymGetLineFromAddr64(GetCurrentProcess(), frame,
[email protected]96fd0032009-04-24 00:13:08289 &line_displacement, &line);
290
291 // Output the backtrace line.
Greg Thompson8b1b295b2024-04-10 07:44:14292 (*os) << prefix_string << "\t";
[email protected]96fd0032009-04-24 00:13:08293 if (has_symbol) {
Alex Gough02b452c2025-11-26 18:44:47294 (*os) << symbol->Name << " "
295 << base::StringPrintf("[%p+%x]", traces[i], sym_displacement);
[email protected]96fd0032009-04-24 00:13:08296 } else {
[email protected]e645c7a2012-07-25 21:43:44297 // If there is no symbol information, add a spacer.
Alex Gough02b452c2025-11-26 18:44:47298 (*os) << "(No symbol) " << base::StringPrintf("[%p]", traces[i]);
[email protected]96fd0032009-04-24 00:13:08299 }
300 if (has_line) {
Alex Gough02b452c2025-11-26 18:44:47301 (*os) << " (" << line.FileName << ":"
302 << base::StringPrintf("%lu", line.LineNumber) << ")";
[email protected]96fd0032009-04-24 00:13:08303 }
304 (*os) << "\n";
305 }
306 }
307
[email protected]de1b764f2009-08-24 15:36:41308 private:
309 friend struct DefaultSingletonTraits<SymbolContext>;
310
Peter Kasting134ef9af2024-12-28 02:30:09311 SymbolContext() { InitializeSymbols(); }
[email protected]96fd0032009-04-24 00:13:08312
jdoerrie5c4dc4e2019-02-01 18:02:33313 Lock lock_;
[email protected]96fd0032009-04-24 00:13:08314};
315
Alex Gough0bbe87d2025-09-12 21:12:11316// Raw output when symbols are not available.
317void OutputAddressesWithPrefix(std::ostream* os,
318 cstring_view prefix_string,
319 span<const void* const> addresses) {
320 for (const void* const addr : addresses) {
Alex Gough02b452c2025-11-26 18:44:47321 (*os) << prefix_string << "\t" << base::StringPrintf("%p", addr) << "\n";
Alex Gough0bbe87d2025-09-12 21:12:11322 if (!os->good()) {
323 break;
324 }
325 }
326}
327
[email protected]1ffe08c12008-08-13 11:15:11328} // namespace
329
Anna Tsvirchkovae4a10372023-06-28 08:52:41330bool EnableInProcessStackDumping() {
[email protected]11b93faa2012-11-01 21:58:30331 // Add stack dumping support on exception on windows. Similar to OS_POSIX
332 // signal() handling in process_util_posix.cc.
333 g_previous_filter = SetUnhandledExceptionFilter(&StackDumpExceptionFilter);
jam79dc59a2015-08-17 03:38:16334
335 // Need to initialize symbols early in the process or else this fails on
336 // swarming (since symbols are in different directory than in the exes) and
337 // also release x64.
Alex Goughd007d622025-09-11 17:55:49338 g_in_process_stack_dumps_enabled = InitializeSymbols();
339 return g_in_process_stack_dumps_enabled;
340}
341
342bool InProcessStackDumpingEnabled() {
343 return g_in_process_stack_dumps_enabled;
[email protected]11b93faa2012-11-01 21:58:30344}
345
Alex Gough0bbe87d2025-09-12 21:12:11346bool DisableInProcessStackDumpingForTesting() {
347 g_previous_filter = SetUnhandledExceptionFilter(g_previous_filter);
348 g_in_process_stack_dumps_enabled = false;
349 return true;
350}
351
danakje75c6c812024-07-26 20:37:47352NOINLINE size_t CollectStackTrace(span<const void*> trace) {
[email protected]0fe31f22009-09-08 23:53:53353 // When walking our own stack, use CaptureStackBackTrace().
danakje75c6c812024-07-26 20:37:47354 return CaptureStackBackTrace(0, trace.size(),
355 const_cast<void**>(trace.data()), NULL);
[email protected]0fe31f22009-09-08 23:53:53356}
[email protected]96fd0032009-04-24 00:13:08357
jam79dc59a2015-08-17 03:38:16358StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) {
359 InitTrace(exception_pointers->ContextRecord);
wittman7808da72015-02-03 17:46:51360}
361
rnk0565cdd62015-10-06 16:48:30362StackTrace::StackTrace(const CONTEXT* context) {
jam79dc59a2015-08-17 03:38:16363 InitTrace(context);
wittman7808da72015-02-03 17:46:51364}
365
rnk0565cdd62015-10-06 16:48:30366void StackTrace::InitTrace(const CONTEXT* context_record) {
Greg Thompson75724f92024-03-21 08:31:27367 if (ShouldSuppressOutput()) {
368 CHECK_EQ(count_, 0U);
Peter Kasting025a94252025-01-29 21:28:37369 std::ranges::fill(trace_, nullptr);
Greg Thompson75724f92024-03-21 08:31:27370 return;
371 }
372
rnk0565cdd62015-10-06 16:48:30373 // StackWalk64 modifies the register context in place, so we have to copy it
374 // so that downstream exception handlers get the right context. The incoming
375 // context may have had more register state (YMM, etc) than we need to unwind
376 // the stack. Typically StackWalk64 only needs integer and control registers.
377 CONTEXT context_copy;
Tom Sepez978847c2025-03-22 03:43:59378 UNSAFE_TODO(memcpy(&context_copy, context_record, sizeof(context_copy)));
rnk0565cdd62015-10-06 16:48:30379 context_copy.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
380
wittman7808da72015-02-03 17:46:51381 // When walking an exception stack, we need to use StackWalk64().
382 count_ = 0;
[email protected]0fe31f22009-09-08 23:53:53383 // Initialize stack walking.
Arthur Sonzognid9cbbfb02025-08-29 16:24:15384 STACKFRAME64 stack_frame = {};
Tom Tan15d3ed32018-11-14 18:20:50385#if defined(ARCH_CPU_X86_64)
Peter Kastingd892d342022-05-27 21:56:36386 DWORD machine_type = IMAGE_FILE_MACHINE_AMD64;
wittman7808da72015-02-03 17:46:51387 stack_frame.AddrPC.Offset = context_record->Rip;
388 stack_frame.AddrFrame.Offset = context_record->Rbp;
389 stack_frame.AddrStack.Offset = context_record->Rsp;
Tom Tan15d3ed32018-11-14 18:20:50390#elif defined(ARCH_CPU_ARM64)
Peter Kastingd892d342022-05-27 21:56:36391 DWORD machine_type = IMAGE_FILE_MACHINE_ARM64;
Tom Tan15d3ed32018-11-14 18:20:50392 stack_frame.AddrPC.Offset = context_record->Pc;
393 stack_frame.AddrFrame.Offset = context_record->Fp;
394 stack_frame.AddrStack.Offset = context_record->Sp;
395#elif defined(ARCH_CPU_X86)
Peter Kastingd892d342022-05-27 21:56:36396 DWORD machine_type = IMAGE_FILE_MACHINE_I386;
wittman7808da72015-02-03 17:46:51397 stack_frame.AddrPC.Offset = context_record->Eip;
398 stack_frame.AddrFrame.Offset = context_record->Ebp;
399 stack_frame.AddrStack.Offset = context_record->Esp;
Tom Tan15d3ed32018-11-14 18:20:50400#else
401#error Unsupported Windows Arch
[email protected]0fe31f22009-09-08 23:53:53402#endif
403 stack_frame.AddrPC.Mode = AddrModeFlat;
404 stack_frame.AddrFrame.Mode = AddrModeFlat;
405 stack_frame.AddrStack.Mode = AddrModeFlat;
Avi Drissman72b4cba2019-01-10 21:54:23406 while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(),
407 &stack_frame, &context_copy, NULL,
408 &SymFunctionTableAccess64, &SymGetModuleBase64, NULL) &&
Daniel Cheng84cf5182022-03-05 01:00:05409 count_ < std::size(trace_)) {
[email protected]0fe31f22009-09-08 23:53:53410 trace_[count_++] = reinterpret_cast<void*>(stack_frame.AddrPC.Offset);
[email protected]96fd0032009-04-24 00:13:08411 }
[email protected]e645c7a2012-07-25 21:43:44412
Peter Kasting025a94252025-01-29 21:28:37413 std::ranges::fill(span(trace_).last(trace_.size() - count_), nullptr);
[email protected]5485cdc2009-01-16 21:17:30414}
415
Greg Thompsonc82cbc92024-04-10 07:36:06416// static
Greg Thompson8b1b295b2024-04-10 07:44:14417void StackTrace::PrintMessageWithPrefix(cstring_view prefix_string,
Greg Thompsonc82cbc92024-04-10 07:36:06418 cstring_view message) {
Greg Thompson8b1b295b2024-04-10 07:44:14419 std::cerr << prefix_string << message;
[email protected]96fd0032009-04-24 00:13:08420}
421
Greg Thompson8b1b295b2024-04-10 07:44:14422void StackTrace::PrintWithPrefixImpl(cstring_view prefix_string) const {
Greg Thompsonc82cbc92024-04-10 07:36:06423 OutputToStreamWithPrefixImpl(&std::cerr, prefix_string);
424}
425
Greg Thompson8b1b295b2024-04-10 07:44:14426void StackTrace::OutputToStreamWithPrefixImpl(
427 std::ostream* os,
428 cstring_view prefix_string) const {
Alex Gough0bbe87d2025-09-12 21:12:11429 if (!InProcessStackDumpingEnabled()) {
430 (*os) << "Symbols not available. Dumping unresolved backtrace:\n";
431 OutputAddressesWithPrefix(os, prefix_string, addresses());
[email protected]96fd0032009-04-24 00:13:08432 } else {
Alex Gough0bbe87d2025-09-12 21:12:11433 SymbolContext* context = SymbolContext::GetInstance();
434 if (g_init_error != ERROR_SUCCESS) {
435 (*os) << "Error initializing symbols (" << g_init_error
436 << "). Dumping unresolved backtrace:\n";
437 OutputAddressesWithPrefix(os, prefix_string, addresses());
438 } else {
439 context->OutputTraceToStream(addresses(), os, prefix_string);
440 }
[email protected]96fd0032009-04-24 00:13:08441 }
[email protected]5485cdc2009-01-16 21:17:30442}
[email protected]58580352010-10-26 04:07:50443
444} // namespace debug
445} // namespace base