Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(39)

Side by Side Diff: base/debug_util_win.cc

Issue 62140: Print backtraces on FATAL log messages in debug mode. (Closed)
Patch Set: Add todo for stackwalk64 Created 11 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/debug_util_unittest.cc ('k') | base/logging.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/debug_util.h"
6
5 #include <windows.h> 7 #include <windows.h>
8 #include <dbghelp.h>
6 9
7 #include "base/basictypes.h" 10 #include "base/basictypes.h"
8 #include "base/debug_util.h" 11 #include "base/lock.h"
9 #include "logging.h" 12 #include "base/logging.h"
13 #include "base/singleton.h"
10 14
11 namespace { 15 namespace {
12 16
13 // Minimalist key reader. 17 // Minimalist key reader.
14 // Note: Does not use the CRT. 18 // Note: Does not use the CRT.
15 bool RegReadString(HKEY root, const wchar_t* subkey, 19 bool RegReadString(HKEY root, const wchar_t* subkey,
16 const wchar_t* value_name, wchar_t* buffer, int* len) { 20 const wchar_t* value_name, wchar_t* buffer, int* len) {
17 HKEY key = NULL; 21 HKEY key = NULL;
18 DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key); 22 DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key);
19 if (ERROR_SUCCESS != res || key == NULL) 23 if (ERROR_SUCCESS != res || key == NULL)
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
55 i += 2; 59 i += 2;
56 } else { 60 } else {
57 if (current_output_len >= output_len) 61 if (current_output_len >= output_len)
58 return false; 62 return false;
59 output[current_output_len] = input[i]; 63 output[current_output_len] = input[i];
60 } 64 }
61 } 65 }
62 return true; 66 return true;
63 } 67 }
64 68
69 // SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family
70 // of functions. The Sym* family of functions may only be invoked by one
71 // thread at a time. SymbolContext code may access a symbol server over the
72 // network while holding the lock for this singleton. In the case of high
73 // latency, this code will adversly affect performance.
74 //
75 // There is also a known issue where this backtrace code can interact
76 // badly with breakpad if breakpad is invoked in a separate thread while
77 // we are using the Sym* functions. This is because breakpad does now
78 // share a lock with this function. See this related bug:
79 //
80 // http://code.google.com/p/google-breakpad/issues/detail?id=311
81 //
82 // This is a very unlikely edge case, and the current solution is to
83 // just ignore it.
84 class SymbolContext {
85 public:
86 static SymbolContext* Get() {
87 // We use a leaky singleton because code may call this during process
88 // termination.
89 return
90 Singleton<SymbolContext, LeakySingletonTraits<SymbolContext> >::get();
91 }
92
93 // Initializes the symbols for the process if it hasn't been done yet.
94 // Subsequent calls will not reinitialize the symbol, but instead return
95 // the error code from the first call.
96 bool Init() {
97 AutoLock lock(lock_);
98 if (!initialized_) {
99 process_ = GetCurrentProcess();
100
101 // Defer symbol load until they're needed, use undecorated names, and
102 // get line numbers.
103 SymSetOptions(SYMOPT_DEFERRED_LOADS |
104 SYMOPT_UNDNAME |
105 SYMOPT_LOAD_LINES);
106 if (SymInitialize(process_, NULL, TRUE)) {
107 init_error_ = ERROR_SUCCESS;
108 } else {
109 init_error_ = GetLastError();
110 }
111 }
112
113 initialized_ = true;
114 return init_error_ == ERROR_SUCCESS;
115 }
116
117 // Returns the error code of a failed initialization. This should only be
118 // called if Init() has been called. We do not LOG(FATAL) here because
119 // this code is called might be triggered by a LOG(FATAL) itself. Instead,
120 // we log an ERROR, and return ERROR_INVALID_DATA.
121 DWORD init_error() {
122 if (!initialized_) {
123 LOG(ERROR) << "Calling GetInitError() before Init() was called. "
124 << "Returning ERROR_INVALID_DATA.";
125 return ERROR_INVALID_DATA;
126 }
127
128 return init_error_;
129 }
130
131 // Returns the process this was initialized for. This should only be
132 // called if Init() has been called. We LOG(ERROR) in this situation.
133 // LOG(FATAL) is not used because this code is might be triggered
134 // by a LOG(FATAL) itself.
135 HANDLE process() {
136 if (!initialized_) {
137 LOG(ERROR) << "Calling process() before Init() was called. "
138 << "Returning NULL.";
139 return NULL;
140 }
141
142 return process_;
143 }
144
145 // For the given trace, attempts to resolve the symbols, and output a trace
146 // to the ostream os. The format for each line of the backtrace is:
147 //
148 // <tab>SymbolName[0xAddress+Offset] (FileName:LineNo)
149 //
150 // This function should only be called if Init() has been called. We do not
151 // LOG(FATAL) here because this code is called might be triggered by a
152 // LOG(FATAL) itself.
153 void OutputTraceToStream(const std::vector<void*>& trace, std::ostream* os) {
154 AutoLock lock(lock_);
155
156 for (size_t i = 0; (i < trace.size()) && os->good(); ++i) {
157 const int kMaxNameLength = 256;
158 DWORD_PTR frame = reinterpret_cast<DWORD_PTR>(trace[i]);
159
160 // Code adapted from MSDN example:
161 // http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
162 ULONG64 buffer[
163 (sizeof(SYMBOL_INFO) +
164 kMaxNameLength * sizeof(wchar_t) +
165 sizeof(ULONG64) - 1) /
166 sizeof(ULONG64)];
167
168 // Initialize symbol information retrieval structures.
169 DWORD64 sym_displacement = 0;
170 PSYMBOL_INFO symbol = reinterpret_cast<PSYMBOL_INFO>(&buffer[0]);
171 symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
172 symbol->MaxNameLen = kMaxNameLength;
173 BOOL has_symbol = SymFromAddr(process(), frame,
174 &sym_displacement, symbol);
175
176 // Attempt to retrieve line number information.
177 DWORD line_displacement = 0;
178 IMAGEHLP_LINE64 line = {};
179 line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
180 BOOL has_line = SymGetLineFromAddr64(process(), frame,
181 &line_displacement, &line);
182
183 // Output the backtrace line.
184 (*os) << "\t";
185 if (has_symbol) {
186 (*os) << symbol->Name << " [0x" << trace[i] << "+"
187 << sym_displacement << "]";
188 } else {
189 // If there is no symbol informtion, add a spacer.
190 (*os) << "(No symbol) [0x" << trace[i] << "]";
191 }
192 if (has_line) {
193 (*os) << " (" << line.FileName << ":" << line.LineNumber << ")";
194 }
195 (*os) << "\n";
196 }
197 }
198
199 SymbolContext()
200 : initialized_(false),
201 process_(NULL),
202 init_error_(ERROR_SUCCESS) {
203 }
204
205 private:
206 Lock lock_;
207 bool initialized_;
208 HANDLE process_;
209 DWORD init_error_;
210
211 DISALLOW_COPY_AND_ASSIGN(SymbolContext);
212 };
213
65 } // namespace 214 } // namespace
66 215
67 // Note: Does not use the CRT. 216 // Note: Does not use the CRT.
68 bool DebugUtil::SpawnDebuggerOnProcess(unsigned process_id) { 217 bool DebugUtil::SpawnDebuggerOnProcess(unsigned process_id) {
69 wchar_t reg_value[1026]; 218 wchar_t reg_value[1026];
70 int len = arraysize(reg_value); 219 int len = arraysize(reg_value);
71 if (RegReadString(HKEY_LOCAL_MACHINE, 220 if (RegReadString(HKEY_LOCAL_MACHINE,
72 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", 221 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug",
73 L"Debugger", reg_value, &len)) { 222 L"Debugger", reg_value, &len)) {
74 wchar_t command_line[1026]; 223 wchar_t command_line[1026];
(...skipping 20 matching lines...) Expand all
95 // static 244 // static
96 bool DebugUtil::BeingDebugged() { 245 bool DebugUtil::BeingDebugged() {
97 return ::IsDebuggerPresent() != 0; 246 return ::IsDebuggerPresent() != 0;
98 } 247 }
99 248
100 // static 249 // static
101 void DebugUtil::BreakDebugger() { 250 void DebugUtil::BreakDebugger() {
102 __debugbreak(); 251 __debugbreak();
103 } 252 }
104 253
105 // TODO(port): not implemented on Windows
106 StackTrace::StackTrace() { 254 StackTrace::StackTrace() {
255 // From http://msdn.microsoft.com/en-us/library/bb204633(VS.85).aspx,
256 // the sum of FramesToSkip and FramesToCapture must be less than 63,
257 // so set it to 62.
258 const int kMaxCallers = 62;
259
260 void* callers[kMaxCallers];
261 // TODO(ajwong): Migrate this to StackWalk64.
262 int count = CaptureStackBackTrace(0, kMaxCallers, callers, NULL);
263 if (count > 0) {
264 trace_.resize(count);
265 memcpy(&trace_[0], callers, sizeof(callers[0]) * count);
266 } else {
267 trace_.resize(0);
268 }
107 } 269 }
108 270
109 void PrintBacktrace() { 271 void StackTrace::PrintBacktrace() {
110 NOTIMPLEMENTED(); 272 OutputToStream(&std::cerr);
111 } 273 }
274
275 void StackTrace::OutputToStream(std::ostream* os) {
276 SymbolContext* context = SymbolContext::Get();
277
278 if (context->Init() != ERROR_SUCCESS) {
279 DWORD error = context->init_error();
280 (*os) << "Error initializing symbols (" << error
281 << "). Dumping unresolved backtrace:\n";
282 for (size_t i = 0; (i < trace_.size()) && os->good(); ++i) {
283 (*os) << "\t" << trace_[i] << "\n";
284 }
285 } else {
286 (*os) << "Backtrace:\n";
287 context->OutputTraceToStream(trace_, os);
288 }
289 }
OLDNEW
« no previous file with comments | « base/debug_util_unittest.cc ('k') | base/logging.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698