1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/chromium/src/base/debug_util_win.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,290 @@ 1.4 +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include "base/debug_util.h" 1.9 + 1.10 +#include <windows.h> 1.11 +#include <dbghelp.h> 1.12 +#include <iostream> 1.13 + 1.14 +#include "base/basictypes.h" 1.15 +#include "base/lock.h" 1.16 +#include "base/logging.h" 1.17 +#include "base/singleton.h" 1.18 + 1.19 +namespace { 1.20 + 1.21 +// Minimalist key reader. 1.22 +// Note: Does not use the CRT. 1.23 +bool RegReadString(HKEY root, const wchar_t* subkey, 1.24 + const wchar_t* value_name, wchar_t* buffer, int* len) { 1.25 + HKEY key = NULL; 1.26 + DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key); 1.27 + if (ERROR_SUCCESS != res || key == NULL) 1.28 + return false; 1.29 + 1.30 + DWORD type = 0; 1.31 + DWORD buffer_size = *len * sizeof(wchar_t); 1.32 + // We don't support REG_EXPAND_SZ. 1.33 + res = RegQueryValueEx(key, value_name, NULL, &type, 1.34 + reinterpret_cast<BYTE*>(buffer), &buffer_size); 1.35 + if (ERROR_SUCCESS == res && buffer_size != 0 && type == REG_SZ) { 1.36 + // Make sure the buffer is NULL terminated. 1.37 + buffer[*len - 1] = 0; 1.38 + *len = lstrlen(buffer); 1.39 + RegCloseKey(key); 1.40 + return true; 1.41 + } 1.42 + RegCloseKey(key); 1.43 + return false; 1.44 +} 1.45 + 1.46 +// Replaces each "%ld" in input per a value. Not efficient but it works. 1.47 +// Note: Does not use the CRT. 1.48 +bool StringReplace(const wchar_t* input, int value, wchar_t* output, 1.49 + int output_len) { 1.50 + memset(output, 0, output_len*sizeof(wchar_t)); 1.51 + int input_len = lstrlen(input); 1.52 + 1.53 + for (int i = 0; i < input_len; ++i) { 1.54 + int current_output_len = lstrlen(output); 1.55 + 1.56 + if (input[i] == L'%' && input[i + 1] == L'l' && input[i + 2] == L'd') { 1.57 + // Make sure we have enough place left. 1.58 + if ((current_output_len + 12) >= output_len) 1.59 + return false; 1.60 + 1.61 + // Cheap _itow(). 1.62 + wsprintf(output+current_output_len, L"%d", value); 1.63 + i += 2; 1.64 + } else { 1.65 + if (current_output_len >= output_len) 1.66 + return false; 1.67 + output[current_output_len] = input[i]; 1.68 + } 1.69 + } 1.70 + return true; 1.71 +} 1.72 + 1.73 +// SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family 1.74 +// of functions. The Sym* family of functions may only be invoked by one 1.75 +// thread at a time. SymbolContext code may access a symbol server over the 1.76 +// network while holding the lock for this singleton. In the case of high 1.77 +// latency, this code will adversly affect performance. 1.78 +// 1.79 +// There is also a known issue where this backtrace code can interact 1.80 +// badly with breakpad if breakpad is invoked in a separate thread while 1.81 +// we are using the Sym* functions. This is because breakpad does now 1.82 +// share a lock with this function. See this related bug: 1.83 +// 1.84 +// http://code.google.com/p/google-breakpad/issues/detail?id=311 1.85 +// 1.86 +// This is a very unlikely edge case, and the current solution is to 1.87 +// just ignore it. 1.88 +class SymbolContext { 1.89 + public: 1.90 + static SymbolContext* Get() { 1.91 + // We use a leaky singleton because code may call this during process 1.92 + // termination. 1.93 + return 1.94 + Singleton<SymbolContext, LeakySingletonTraits<SymbolContext> >::get(); 1.95 + } 1.96 + 1.97 + // Initializes the symbols for the process if it hasn't been done yet. 1.98 + // Subsequent calls will not reinitialize the symbol, but instead return 1.99 + // the error code from the first call. 1.100 + bool Init() { 1.101 + AutoLock lock(lock_); 1.102 + if (!initialized_) { 1.103 + process_ = GetCurrentProcess(); 1.104 + 1.105 + // Defer symbol load until they're needed, use undecorated names, and 1.106 + // get line numbers. 1.107 + SymSetOptions(SYMOPT_DEFERRED_LOADS | 1.108 + SYMOPT_UNDNAME | 1.109 + SYMOPT_LOAD_LINES); 1.110 + if (SymInitialize(process_, NULL, TRUE)) { 1.111 + init_error_ = ERROR_SUCCESS; 1.112 + } else { 1.113 + init_error_ = GetLastError(); 1.114 + } 1.115 + } 1.116 + 1.117 + initialized_ = true; 1.118 + return init_error_ == ERROR_SUCCESS; 1.119 + } 1.120 + 1.121 + // Returns the error code of a failed initialization. This should only be 1.122 + // called if Init() has been called. We do not CHROMIUM_LOG(FATAL) here because 1.123 + // this code is called might be triggered by a CHROMIUM_LOG(FATAL) itself. Instead, 1.124 + // we log an ERROR, and return ERROR_INVALID_DATA. 1.125 + DWORD init_error() { 1.126 + if (!initialized_) { 1.127 + CHROMIUM_LOG(ERROR) << "Calling GetInitError() before Init() was called. " 1.128 + << "Returning ERROR_INVALID_DATA."; 1.129 + return ERROR_INVALID_DATA; 1.130 + } 1.131 + 1.132 + return init_error_; 1.133 + } 1.134 + 1.135 + // Returns the process this was initialized for. This should only be 1.136 + // called if Init() has been called. We CHROMIUM_LOG(ERROR) in this situation. 1.137 + // CHROMIUM_LOG(FATAL) is not used because this code is might be triggered 1.138 + // by a CHROMIUM_LOG(FATAL) itself. 1.139 + HANDLE process() { 1.140 + if (!initialized_) { 1.141 + CHROMIUM_LOG(ERROR) << "Calling process() before Init() was called. " 1.142 + << "Returning NULL."; 1.143 + return NULL; 1.144 + } 1.145 + 1.146 + return process_; 1.147 + } 1.148 + 1.149 + // For the given trace, attempts to resolve the symbols, and output a trace 1.150 + // to the ostream os. The format for each line of the backtrace is: 1.151 + // 1.152 + // <tab>SymbolName[0xAddress+Offset] (FileName:LineNo) 1.153 + // 1.154 + // This function should only be called if Init() has been called. We do not 1.155 + // CHROMIUM_LOG(FATAL) here because this code is called might be triggered by a 1.156 + // CHROMIUM_LOG(FATAL) itself. 1.157 + void OutputTraceToStream(const std::vector<void*>& trace, std::ostream* os) { 1.158 + AutoLock lock(lock_); 1.159 + 1.160 + for (size_t i = 0; (i < trace.size()) && os->good(); ++i) { 1.161 + const int kMaxNameLength = 256; 1.162 + DWORD_PTR frame = reinterpret_cast<DWORD_PTR>(trace[i]); 1.163 + 1.164 + // Code adapted from MSDN example: 1.165 + // http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx 1.166 + ULONG64 buffer[ 1.167 + (sizeof(SYMBOL_INFO) + 1.168 + kMaxNameLength * sizeof(wchar_t) + 1.169 + sizeof(ULONG64) - 1) / 1.170 + sizeof(ULONG64)]; 1.171 + 1.172 + // Initialize symbol information retrieval structures. 1.173 + DWORD64 sym_displacement = 0; 1.174 + PSYMBOL_INFO symbol = reinterpret_cast<PSYMBOL_INFO>(&buffer[0]); 1.175 + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); 1.176 + symbol->MaxNameLen = kMaxNameLength; 1.177 + BOOL has_symbol = SymFromAddr(process(), frame, 1.178 + &sym_displacement, symbol); 1.179 + 1.180 + // Attempt to retrieve line number information. 1.181 + DWORD line_displacement = 0; 1.182 + IMAGEHLP_LINE64 line = {}; 1.183 + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); 1.184 + BOOL has_line = SymGetLineFromAddr64(process(), frame, 1.185 + &line_displacement, &line); 1.186 + 1.187 + // Output the backtrace line. 1.188 + (*os) << "\t"; 1.189 + if (has_symbol) { 1.190 + (*os) << symbol->Name << " [0x" << trace[i] << "+" 1.191 + << sym_displacement << "]"; 1.192 + } else { 1.193 + // If there is no symbol informtion, add a spacer. 1.194 + (*os) << "(No symbol) [0x" << trace[i] << "]"; 1.195 + } 1.196 + if (has_line) { 1.197 + (*os) << " (" << line.FileName << ":" << line.LineNumber << ")"; 1.198 + } 1.199 + (*os) << "\n"; 1.200 + } 1.201 + } 1.202 + 1.203 + SymbolContext() 1.204 + : initialized_(false), 1.205 + process_(NULL), 1.206 + init_error_(ERROR_SUCCESS) { 1.207 + } 1.208 + 1.209 + private: 1.210 + Lock lock_; 1.211 + bool initialized_; 1.212 + HANDLE process_; 1.213 + DWORD init_error_; 1.214 + 1.215 + DISALLOW_COPY_AND_ASSIGN(SymbolContext); 1.216 +}; 1.217 + 1.218 +} // namespace 1.219 + 1.220 +// Note: Does not use the CRT. 1.221 +bool DebugUtil::SpawnDebuggerOnProcess(unsigned process_id) { 1.222 + wchar_t reg_value[1026]; 1.223 + int len = arraysize(reg_value); 1.224 + if (RegReadString(HKEY_LOCAL_MACHINE, 1.225 + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", 1.226 + L"Debugger", reg_value, &len)) { 1.227 + wchar_t command_line[1026]; 1.228 + if (StringReplace(reg_value, process_id, command_line, 1.229 + arraysize(command_line))) { 1.230 + // We don't mind if the debugger is present because it will simply fail 1.231 + // to attach to this process. 1.232 + STARTUPINFO startup_info = {0}; 1.233 + startup_info.cb = sizeof(startup_info); 1.234 + PROCESS_INFORMATION process_info = {0}; 1.235 + 1.236 + if (CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL, 1.237 + &startup_info, &process_info)) { 1.238 + CloseHandle(process_info.hThread); 1.239 + WaitForInputIdle(process_info.hProcess, 10000); 1.240 + CloseHandle(process_info.hProcess); 1.241 + return true; 1.242 + } 1.243 + } 1.244 + } 1.245 + return false; 1.246 +} 1.247 + 1.248 +// static 1.249 +bool DebugUtil::BeingDebugged() { 1.250 + return ::IsDebuggerPresent() != 0; 1.251 +} 1.252 + 1.253 +// static 1.254 +void DebugUtil::BreakDebugger() { 1.255 + __debugbreak(); 1.256 +} 1.257 + 1.258 +StackTrace::StackTrace() { 1.259 + // From http://msdn.microsoft.com/en-us/library/bb204633(VS.85).aspx, 1.260 + // the sum of FramesToSkip and FramesToCapture must be less than 63, 1.261 + // so set it to 62. 1.262 + const int kMaxCallers = 62; 1.263 + 1.264 + void* callers[kMaxCallers]; 1.265 + // TODO(ajwong): Migrate this to StackWalk64. 1.266 + int count = CaptureStackBackTrace(0, kMaxCallers, callers, NULL); 1.267 + if (count > 0) { 1.268 + trace_.resize(count); 1.269 + memcpy(&trace_[0], callers, sizeof(callers[0]) * count); 1.270 + } else { 1.271 + trace_.resize(0); 1.272 + } 1.273 +} 1.274 + 1.275 +void StackTrace::PrintBacktrace() { 1.276 + OutputToStream(&std::cerr); 1.277 +} 1.278 + 1.279 +void StackTrace::OutputToStream(std::ostream* os) { 1.280 + SymbolContext* context = SymbolContext::Get(); 1.281 + 1.282 + if (context->Init() != ERROR_SUCCESS) { 1.283 + DWORD error = context->init_error(); 1.284 + (*os) << "Error initializing symbols (" << error 1.285 + << "). Dumping unresolved backtrace:\n"; 1.286 + for (size_t i = 0; (i < trace_.size()) && os->good(); ++i) { 1.287 + (*os) << "\t" << trace_[i] << "\n"; 1.288 + } 1.289 + } else { 1.290 + (*os) << "Backtrace:\n"; 1.291 + context->OutputTraceToStream(trace_, os); 1.292 + } 1.293 +}