ipc/chromium/src/base/debug_util_win.cc

changeset 0
6474c204b198
     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 +}

mercurial