Home | History | Annotate | Download | only in app
      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 
      5 #include "chrome/app/breakpad_win.h"
      6 
      7 #include <shellapi.h>
      8 #include <tchar.h>
      9 #include <userenv.h>
     10 #include <windows.h>
     11 #include <winnt.h>
     12 
     13 #include <algorithm>
     14 #include <vector>
     15 
     16 #include "base/base_switches.h"
     17 #include "base/command_line.h"
     18 #include "base/debug/crash_logging.h"
     19 #include "base/environment.h"
     20 #include "base/memory/scoped_ptr.h"
     21 #include "base/strings/string16.h"
     22 #include "base/strings/string_split.h"
     23 #include "base/strings/string_util.h"
     24 #include "base/strings/stringprintf.h"
     25 #include "base/strings/utf_string_conversions.h"
     26 #include "base/win/metro.h"
     27 #include "base/win/pe_image.h"
     28 #include "base/win/registry.h"
     29 #include "base/win/win_util.h"
     30 #include "breakpad/src/client/windows/handler/exception_handler.h"
     31 #include "chrome/app/breakpad_field_trial_win.h"
     32 #include "chrome/app/hard_error_handler_win.h"
     33 #include "chrome/common/child_process_logging.h"
     34 #include "components/breakpad/breakpad_client.h"
     35 #include "content/public/common/content_switches.h"
     36 #include "content/public/common/result_codes.h"
     37 #include "policy/policy_constants.h"
     38 #include "sandbox/win/src/nt_internals.h"
     39 #include "sandbox/win/src/sidestep/preamble_patcher.h"
     40 
     41 // userenv.dll is required for GetProfileType().
     42 #pragma comment(lib, "userenv.lib")
     43 
     44 #pragma intrinsic(_AddressOfReturnAddress)
     45 #pragma intrinsic(_ReturnAddress)
     46 
     47 namespace breakpad_win {
     48 
     49 // TODO(raymes): Modify the way custom crash info is stored. g_custom_entries
     50 // is way too too fragile. See
     51 // https://code.google.com/p/chromium/issues/detail?id=137062.
     52 std::vector<google_breakpad::CustomInfoEntry>* g_custom_entries = NULL;
     53 size_t g_num_of_experiments_offset = 0;
     54 size_t g_experiment_chunks_offset = 0;
     55 bool g_deferred_crash_uploads = false;
     56 
     57 }   // namespace breakpad_win
     58 
     59 using breakpad_win::g_custom_entries;
     60 using breakpad_win::g_deferred_crash_uploads;
     61 using breakpad_win::g_experiment_chunks_offset;
     62 using breakpad_win::g_num_of_experiments_offset;
     63 
     64 namespace {
     65 
     66 // Minidump with stacks, PEB, TEB, and unloaded module list.
     67 const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>(
     68     MiniDumpWithProcessThreadData |  // Get PEB and TEB.
     69     MiniDumpWithUnloadedModules);  // Get unloaded modules when available.
     70 
     71 // Minidump with all of the above, plus memory referenced from stack.
     72 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
     73     MiniDumpWithProcessThreadData |  // Get PEB and TEB.
     74     MiniDumpWithUnloadedModules |  // Get unloaded modules when available.
     75     MiniDumpWithIndirectlyReferencedMemory);  // Get memory referenced by stack.
     76 
     77 // Large dump with all process memory.
     78 const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
     79     MiniDumpWithFullMemory |  // Full memory from process.
     80     MiniDumpWithProcessThreadData |  // Get PEB and TEB.
     81     MiniDumpWithHandleData |  // Get all handle information.
     82     MiniDumpWithUnloadedModules);  // Get unloaded modules when available.
     83 
     84 const char kPipeNameVar[] = "CHROME_BREAKPAD_PIPE_NAME";
     85 
     86 const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
     87 const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
     88 
     89 // This is the well known SID for the system principal.
     90 const wchar_t kSystemPrincipalSid[] =L"S-1-5-18";
     91 
     92 google_breakpad::ExceptionHandler* g_breakpad = NULL;
     93 google_breakpad::ExceptionHandler* g_dumphandler_no_crash = NULL;
     94 
     95 EXCEPTION_POINTERS g_surrogate_exception_pointers = {0};
     96 EXCEPTION_RECORD g_surrogate_exception_record = {0};
     97 CONTEXT g_surrogate_context = {0};
     98 
     99 typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle,
    100                                                  NTSTATUS ExitStatus);
    101 char* g_real_terminate_process_stub = NULL;
    102 
    103 static size_t g_url_chunks_offset = 0;
    104 static size_t g_num_of_extensions_offset = 0;
    105 static size_t g_extension_ids_offset = 0;
    106 static size_t g_client_id_offset = 0;
    107 static size_t g_gpu_info_offset = 0;
    108 static size_t g_printer_info_offset = 0;
    109 static size_t g_num_of_views_offset = 0;
    110 static size_t g_num_switches_offset = 0;
    111 static size_t g_switches_offset = 0;
    112 static size_t g_dynamic_keys_offset = 0;
    113 typedef std::map<std::string, google_breakpad::CustomInfoEntry*>
    114     DynamicEntriesMap;
    115 DynamicEntriesMap* g_dynamic_entries = NULL;
    116 static size_t g_dynamic_entries_count = 0;
    117 
    118 // Maximum length for plugin path to include in plugin crash reports.
    119 const size_t kMaxPluginPathLength = 256;
    120 
    121 // Dumps the current process memory.
    122 extern "C" void __declspec(dllexport) __cdecl DumpProcess() {
    123   if (g_breakpad)
    124     g_breakpad->WriteMinidump();
    125 }
    126 
    127 // Used for dumping a process state when there is no crash.
    128 extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() {
    129   if (g_dumphandler_no_crash) {
    130     g_dumphandler_no_crash->WriteMinidump();
    131   }
    132 }
    133 
    134 // We need to prevent ICF from folding DumpForHangDebuggingThread() and
    135 // DumpProcessWithoutCrashThread() together, since that makes them
    136 // indistinguishable in crash dumps. We do this by making the function
    137 // bodies unique, and prevent optimization from shuffling things around.
    138 MSVC_DISABLE_OPTIMIZE()
    139 MSVC_PUSH_DISABLE_WARNING(4748)
    140 
    141 DWORD WINAPI DumpProcessWithoutCrashThread(void*) {
    142   DumpProcessWithoutCrash();
    143   return 0;
    144 }
    145 
    146 // The following two functions do exactly the same thing as the two above. But
    147 // we want the signatures to be different so that we can easily track them in
    148 // crash reports.
    149 // TODO(yzshen): Remove when enough information is collected and the hang rate
    150 // of pepper/renderer processes is reduced.
    151 DWORD WINAPI DumpForHangDebuggingThread(void*) {
    152   DumpProcessWithoutCrash();
    153   LOG(INFO) << "dumped for hang debugging";
    154   return 0;
    155 }
    156 
    157 MSVC_POP_WARNING()
    158 MSVC_ENABLE_OPTIMIZE()
    159 
    160 // Injects a thread into a remote process to dump state when there is no crash.
    161 extern "C" HANDLE __declspec(dllexport) __cdecl
    162 InjectDumpProcessWithoutCrash(HANDLE process) {
    163   return CreateRemoteThread(process, NULL, 0, DumpProcessWithoutCrashThread,
    164                             0, 0, NULL);
    165 }
    166 
    167 extern "C" HANDLE __declspec(dllexport) __cdecl
    168 InjectDumpForHangDebugging(HANDLE process) {
    169   return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread,
    170                             0, 0, NULL);
    171 }
    172 
    173 extern "C" void DumpProcessAbnormalSignature() {
    174   if (!g_breakpad)
    175     return;
    176   g_custom_entries->push_back(
    177       google_breakpad::CustomInfoEntry(L"unusual-crash-signature", L""));
    178   g_breakpad->WriteMinidump();
    179 }
    180 
    181 // Reduces the size of the string |str| to a max of 64 chars. Required because
    182 // breakpad's CustomInfoEntry raises an invalid_parameter error if the string
    183 // we want to set is longer.
    184 std::wstring TrimToBreakpadMax(const std::wstring& str) {
    185   std::wstring shorter(str);
    186   return shorter.substr(0,
    187       google_breakpad::CustomInfoEntry::kValueMaxLength - 1);
    188 }
    189 
    190 static void SetIntegerValue(size_t offset, int value) {
    191   if (!g_custom_entries)
    192     return;
    193 
    194   base::wcslcpy((*g_custom_entries)[offset].value,
    195                 base::StringPrintf(L"%d", value).c_str(),
    196                 google_breakpad::CustomInfoEntry::kValueMaxLength);
    197 }
    198 
    199 bool IsBoringCommandLineSwitch(const std::wstring& flag) {
    200   return StartsWith(flag, L"--channel=", true) ||
    201 
    202          // No point to including this since we already have a ptype field.
    203          StartsWith(flag, L"--type=", true) ||
    204 
    205          // Not particularly interesting
    206          StartsWith(flag, L"--flash-broker=", true) ||
    207 
    208          // Just about everything has this, don't bother.
    209          StartsWith(flag, L"/prefetch:", true) ||
    210 
    211          // We handle the plugin path separately since it is usually too big
    212          // to fit in the switches (limited to 63 characters).
    213          StartsWith(flag, L"--plugin-path=", true) ||
    214 
    215          // This is too big so we end up truncating it anyway.
    216          StartsWith(flag, L"--force-fieldtest=", true) ||
    217 
    218          // These surround the flags that were added by about:flags, it lets
    219          // you distinguish which flags were added manually via the command
    220          // line versus those added through about:flags. For the most part
    221          // we don't care how an option was enabled, so we strip these.
    222          // (If you need to know can always look at the PEB).
    223          flag == L"--flag-switches-begin" ||
    224          flag == L"--flag-switches-end";
    225 }
    226 
    227 // Note that this is suffixed with "2" due to a parameter change that was made
    228 // to the predecessor "SetCommandLine()". If the signature changes again, use
    229 // a new name.
    230 extern "C" void __declspec(dllexport) __cdecl SetCommandLine2(
    231     const wchar_t** argv, size_t argc) {
    232   if (!g_custom_entries)
    233     return;
    234 
    235   // Copy up to the kMaxSwitches arguments into the custom entries array. Skip
    236   // past the first argument, as it is just the executable path.
    237   size_t argv_i = 1;
    238   size_t num_added = 0;
    239 
    240   for (; argv_i < argc && num_added < kMaxSwitches; ++argv_i) {
    241     // Don't bother including boring command line switches in crash reports.
    242     if (IsBoringCommandLineSwitch(argv[argv_i]))
    243       continue;
    244 
    245     base::wcslcpy((*g_custom_entries)[g_switches_offset + num_added].value,
    246                   argv[argv_i],
    247                   google_breakpad::CustomInfoEntry::kValueMaxLength);
    248     num_added++;
    249   }
    250 
    251   // Make note of the total number of switches. This is useful in case we have
    252   // truncated at kMaxSwitches, to see how many were unaccounted for.
    253   SetIntegerValue(g_num_switches_offset, static_cast<int>(argc) - 1);
    254 }
    255 
    256 // Appends the plugin path to |g_custom_entries|.
    257 void SetPluginPath(const std::wstring& path) {
    258   DCHECK(g_custom_entries);
    259 
    260   if (path.size() > kMaxPluginPathLength) {
    261     // If the path is too long, truncate from the start rather than the end,
    262     // since we want to be able to recover the DLL name.
    263     SetPluginPath(path.substr(path.size() - kMaxPluginPathLength));
    264     return;
    265   }
    266 
    267   // The chunk size without terminator.
    268   const size_t kChunkSize = static_cast<size_t>(
    269       google_breakpad::CustomInfoEntry::kValueMaxLength - 1);
    270 
    271   int chunk_index = 0;
    272   size_t chunk_start = 0;  // Current position inside |path|
    273 
    274   for (chunk_start = 0; chunk_start < path.size(); chunk_index++) {
    275     size_t chunk_length = std::min(kChunkSize, path.size() - chunk_start);
    276 
    277     g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
    278         base::StringPrintf(L"plugin-path-chunk-%i", chunk_index + 1).c_str(),
    279         path.substr(chunk_start, chunk_length).c_str()));
    280 
    281     chunk_start += chunk_length;
    282   }
    283 }
    284 
    285 // Determine whether configuration management allows loading the crash reporter.
    286 // Since the configuration management infrastructure is not initialized at this
    287 // point, we read the corresponding registry key directly. The return status
    288 // indicates whether policy data was successfully read. If it is true, |result|
    289 // contains the value set by policy.
    290 static bool MetricsReportingControlledByPolicy(bool* result) {
    291   string16 key_name = UTF8ToUTF16(policy::key::kMetricsReportingEnabled);
    292   DWORD value = 0;
    293   base::win::RegKey hklm_policy_key(HKEY_LOCAL_MACHINE,
    294                                     policy::kRegistryChromePolicyKey, KEY_READ);
    295   if (hklm_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) {
    296     *result = value != 0;
    297     return true;
    298   }
    299 
    300   base::win::RegKey hkcu_policy_key(HKEY_CURRENT_USER,
    301                                     policy::kRegistryChromePolicyKey, KEY_READ);
    302   if (hkcu_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) {
    303     *result = value != 0;
    304     return true;
    305   }
    306 
    307   return false;
    308 }
    309 
    310 // Appends the breakpad dump path to |g_custom_entries|.
    311 void SetBreakpadDumpPath() {
    312   DCHECK(g_custom_entries);
    313   base::FilePath crash_dumps_dir_path;
    314   if (breakpad::GetBreakpadClient()->GetAlternativeCrashDumpLocation(
    315           &crash_dumps_dir_path)) {
    316     g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
    317         L"breakpad-dump-location", crash_dumps_dir_path.value().c_str()));
    318   }
    319 }
    320 
    321 // Returns a string containing a list of all modifiers for the loaded profile.
    322 std::wstring GetProfileType() {
    323   std::wstring profile_type;
    324   DWORD profile_bits = 0;
    325   if (::GetProfileType(&profile_bits)) {
    326     static const struct {
    327       DWORD bit;
    328       const wchar_t* name;
    329     } kBitNames[] = {
    330       { PT_MANDATORY, L"mandatory" },
    331       { PT_ROAMING, L"roaming" },
    332       { PT_TEMPORARY, L"temporary" },
    333     };
    334     for (size_t i = 0; i < arraysize(kBitNames); ++i) {
    335       const DWORD this_bit = kBitNames[i].bit;
    336       if ((profile_bits & this_bit) != 0) {
    337         profile_type.append(kBitNames[i].name);
    338         profile_bits &= ~this_bit;
    339         if (profile_bits != 0)
    340           profile_type.append(L", ");
    341       }
    342     }
    343   } else {
    344     DWORD last_error = ::GetLastError();
    345     base::SStringPrintf(&profile_type, L"error %u", last_error);
    346   }
    347   return profile_type;
    348 }
    349 
    350 // Returns the custom info structure based on the dll in parameter and the
    351 // process type.
    352 google_breakpad::CustomClientInfo* GetCustomInfo(const std::wstring& exe_path,
    353                                                  const std::wstring& type) {
    354   base::string16 version, product;
    355   base::string16 special_build;
    356   base::string16 channel_name;
    357   breakpad::GetBreakpadClient()->GetProductNameAndVersion(
    358       base::FilePath(exe_path),
    359       &product,
    360       &version,
    361       &special_build,
    362       &channel_name);
    363 
    364   // We only expect this method to be called once per process.
    365   DCHECK(!g_custom_entries);
    366   g_custom_entries = new std::vector<google_breakpad::CustomInfoEntry>;
    367 
    368   // Common g_custom_entries.
    369   g_custom_entries->push_back(
    370       google_breakpad::CustomInfoEntry(L"ver", UTF16ToWide(version).c_str()));
    371   g_custom_entries->push_back(
    372       google_breakpad::CustomInfoEntry(L"prod", UTF16ToWide(product).c_str()));
    373   g_custom_entries->push_back(
    374       google_breakpad::CustomInfoEntry(L"plat", L"Win32"));
    375   g_custom_entries->push_back(
    376       google_breakpad::CustomInfoEntry(L"ptype", type.c_str()));
    377   g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
    378       L"channel", base::UTF16ToWide(channel_name).c_str()));
    379   g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
    380       L"profile-type", GetProfileType().c_str()));
    381 
    382   if (g_deferred_crash_uploads)
    383     g_custom_entries->push_back(
    384         google_breakpad::CustomInfoEntry(L"deferred-upload", L"true"));
    385 
    386   if (!special_build.empty())
    387     g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
    388         L"special", UTF16ToWide(special_build).c_str()));
    389 
    390   g_num_of_extensions_offset = g_custom_entries->size();
    391   g_custom_entries->push_back(
    392       google_breakpad::CustomInfoEntry(L"num-extensions", L"N/A"));
    393 
    394   g_extension_ids_offset = g_custom_entries->size();
    395   // one-based index for the name suffix.
    396   for (int i = 1; i <= kMaxReportedActiveExtensions; ++i) {
    397     g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
    398         base::StringPrintf(L"extension-%i", i).c_str(), L""));
    399   }
    400 
    401   // Add empty values for the gpu_info. We'll put the actual values when we
    402   // collect them at this location.
    403   g_gpu_info_offset = g_custom_entries->size();
    404   static const wchar_t* const kGpuEntries[] = {
    405     L"gpu-venid",
    406     L"gpu-devid",
    407     L"gpu-driver",
    408     L"gpu-psver",
    409     L"gpu-vsver",
    410   };
    411   for (size_t i = 0; i < arraysize(kGpuEntries); ++i) {
    412     g_custom_entries->push_back(
    413         google_breakpad::CustomInfoEntry(kGpuEntries[i], L""));
    414   }
    415 
    416   // Add empty values for the prn_info-*. We'll put the actual values when we
    417   // collect them at this location.
    418   g_printer_info_offset = g_custom_entries->size();
    419   // one-based index for the name suffix.
    420   for (size_t i = 1; i <= kMaxReportedPrinterRecords; ++i) {
    421     g_custom_entries->push_back(
    422         google_breakpad::CustomInfoEntry(
    423             base::StringPrintf(L"prn-info-%d", i).c_str(), L""));
    424   }
    425 
    426   // Read the id from registry. If reporting has never been enabled
    427   // the result will be empty string. Its OK since when user enables reporting
    428   // we will insert the new value at this location.
    429   std::wstring guid =
    430       base::UTF16ToWide(breakpad::GetBreakpadClient()->GetCrashGUID());
    431   g_client_id_offset = g_custom_entries->size();
    432   g_custom_entries->push_back(
    433       google_breakpad::CustomInfoEntry(L"guid", guid.c_str()));
    434 
    435   // Add empty values for the command line switches. We will fill them with
    436   // actual values as part of SetCommandLine2().
    437   g_num_switches_offset = g_custom_entries->size();
    438   g_custom_entries->push_back(
    439       google_breakpad::CustomInfoEntry(L"num-switches", L""));
    440 
    441   g_switches_offset = g_custom_entries->size();
    442   // one-based index for the name suffix.
    443   for (int i = 1; i <= kMaxSwitches; ++i) {
    444     g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
    445         base::StringPrintf(L"switch-%i", i).c_str(), L""));
    446   }
    447 
    448   // Fill in the command line arguments using CommandLine::ForCurrentProcess().
    449   // The browser process may call SetCommandLine2() again later on with a
    450   // command line that has been augmented with the about:flags experiments.
    451   std::vector<const wchar_t*> switches;
    452   StringVectorToCStringVector(
    453       CommandLine::ForCurrentProcess()->argv(), &switches);
    454   SetCommandLine2(&switches[0], switches.size());
    455 
    456   if (type == L"renderer" || type == L"plugin" || type == L"ppapi" ||
    457       type == L"gpu-process") {
    458     g_num_of_views_offset = g_custom_entries->size();
    459     g_custom_entries->push_back(
    460         google_breakpad::CustomInfoEntry(L"num-views", L""));
    461     // Create entries for the URL. Currently we only allow each chunk to be 64
    462     // characters, which isn't enough for a URL. As a hack we create 8 entries
    463     // and split the URL across the g_custom_entries.
    464     g_url_chunks_offset = g_custom_entries->size();
    465     // one-based index for the name suffix.
    466     for (int i = 1; i <= kMaxUrlChunks; ++i) {
    467       g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
    468           base::StringPrintf(L"url-chunk-%i", i).c_str(), L""));
    469     }
    470 
    471     if (type == L"plugin" || type == L"ppapi") {
    472       std::wstring plugin_path =
    473           CommandLine::ForCurrentProcess()->GetSwitchValueNative("plugin-path");
    474       if (!plugin_path.empty())
    475         SetPluginPath(plugin_path);
    476     }
    477   } else {
    478     g_custom_entries->push_back(
    479         google_breakpad::CustomInfoEntry(L"num-views", L"N/A"));
    480   }
    481 
    482   // Check whether configuration management controls crash reporting.
    483   bool crash_reporting_enabled = true;
    484   bool controlled_by_policy =
    485       MetricsReportingControlledByPolicy(&crash_reporting_enabled);
    486   const CommandLine& command = *CommandLine::ForCurrentProcess();
    487   bool use_crash_service =
    488       !controlled_by_policy &&
    489       (command.HasSwitch(switches::kNoErrorDialogs) ||
    490        breakpad::GetBreakpadClient()->IsRunningUnattended());
    491   if (use_crash_service)
    492     SetBreakpadDumpPath();
    493 
    494   g_num_of_experiments_offset = g_custom_entries->size();
    495   g_custom_entries->push_back(
    496       google_breakpad::CustomInfoEntry(L"num-experiments", L"N/A"));
    497 
    498   g_experiment_chunks_offset = g_custom_entries->size();
    499   // We depend on this in UpdateExperiments...
    500   DCHECK_NE(0UL, g_experiment_chunks_offset);
    501   // And the test code depends on this.
    502   DCHECK_EQ(g_num_of_experiments_offset + 1, g_experiment_chunks_offset);
    503   // one-based index for the name suffix.
    504   for (int i = 1; i <= kMaxReportedVariationChunks; ++i) {
    505     g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
    506         base::StringPrintf(L"experiment-chunk-%i", i).c_str(), L""));
    507   }
    508 
    509   // Create space for dynamic ad-hoc keys. The names and values are set using
    510   // the API defined in base/debug/crash_logging.h.
    511   g_dynamic_entries_count = breakpad::GetBreakpadClient()->RegisterCrashKeys();
    512   g_dynamic_keys_offset = g_custom_entries->size();
    513   for (size_t i = 0; i < g_dynamic_entries_count; ++i) {
    514     // The names will be mutated as they are set. Un-numbered since these are
    515     // merely placeholders. The name cannot be empty because Breakpad's
    516     // HTTPUpload will interpret that as an invalid parameter.
    517     g_custom_entries->push_back(
    518         google_breakpad::CustomInfoEntry(L"unspecified-crash-key", L""));
    519   }
    520   g_dynamic_entries = new DynamicEntriesMap;
    521 
    522   static google_breakpad::CustomClientInfo custom_client_info;
    523   custom_client_info.entries = &g_custom_entries->front();
    524   custom_client_info.count = g_custom_entries->size();
    525 
    526   return &custom_client_info;
    527 }
    528 
    529 // This callback is used when we want to get a dump without crashing the
    530 // process.
    531 bool DumpDoneCallbackWhenNoCrash(const wchar_t*, const wchar_t*, void*,
    532                                  EXCEPTION_POINTERS* ex_info,
    533                                  MDRawAssertionInfo*, bool) {
    534   return true;
    535 }
    536 
    537 // This callback is executed when the browser process has crashed, after
    538 // the crash dump has been created. We need to minimize the amount of work
    539 // done here since we have potentially corrupted process. Our job is to
    540 // spawn another instance of chrome which will show a 'chrome has crashed'
    541 // dialog. This code needs to live in the exe and thus has no access to
    542 // facilities such as the i18n helpers.
    543 bool DumpDoneCallback(const wchar_t*, const wchar_t*, void*,
    544                       EXCEPTION_POINTERS* ex_info,
    545                       MDRawAssertionInfo*, bool) {
    546   // Check if the exception is one of the kind which would not be solved
    547   // by simply restarting chrome. In this case we show a message box with
    548   // and exit silently. Remember that chrome is in a crashed state so we
    549   // can't show our own UI from this process.
    550   if (HardErrorHandler(ex_info))
    551     return true;
    552 
    553   if (!breakpad::GetBreakpadClient()->AboutToRestart())
    554     return true;
    555 
    556   // Now we just start chrome browser with the same command line.
    557   STARTUPINFOW si = {sizeof(si)};
    558   PROCESS_INFORMATION pi;
    559   if (::CreateProcessW(NULL, ::GetCommandLineW(), NULL, NULL, FALSE,
    560                        CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi)) {
    561     ::CloseHandle(pi.hProcess);
    562     ::CloseHandle(pi.hThread);
    563   }
    564   // After this return we will be terminated. The actual return value is
    565   // not used at all.
    566   return true;
    567 }
    568 
    569 // flag to indicate that we are already handling an exception.
    570 volatile LONG handling_exception = 0;
    571 
    572 // This callback is used when there is no crash. Note: Unlike the
    573 // |FilterCallback| below this does not do dupe detection. It is upto the caller
    574 // to implement it.
    575 bool FilterCallbackWhenNoCrash(
    576     void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) {
    577   return true;
    578 }
    579 
    580 // This callback is executed when the Chrome process has crashed and *before*
    581 // the crash dump is created. To prevent duplicate crash reports we
    582 // make every thread calling this method, except the very first one,
    583 // go to sleep.
    584 bool FilterCallback(void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) {
    585   // Capture every thread except the first one in the sleep. We don't
    586   // want multiple threads to concurrently report exceptions.
    587   if (::InterlockedCompareExchange(&handling_exception, 1, 0) == 1) {
    588     ::Sleep(INFINITE);
    589   }
    590   return true;
    591 }
    592 
    593 // Previous unhandled filter. Will be called if not null when we
    594 // intercept a crash.
    595 LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = NULL;
    596 
    597 // Exception filter used when breakpad is not enabled. We just display
    598 // the "Do you want to restart" message and then we call the previous filter.
    599 long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) {
    600   DumpDoneCallback(NULL, NULL, NULL, info, NULL, false);
    601 
    602   if (previous_filter)
    603     return previous_filter(info);
    604 
    605   return EXCEPTION_EXECUTE_HANDLER;
    606 }
    607 
    608 // Exception filter for the service process used when breakpad is not enabled.
    609 // We just display the "Do you want to restart" message and then die
    610 // (without calling the previous filter).
    611 long WINAPI ServiceExceptionFilter(EXCEPTION_POINTERS* info) {
    612   DumpDoneCallback(NULL, NULL, NULL, info, NULL, false);
    613   return EXCEPTION_EXECUTE_HANDLER;
    614 }
    615 
    616 extern "C" void __declspec(dllexport) __cdecl SetActiveURL(
    617     const wchar_t* url_cstring) {
    618   DCHECK(url_cstring);
    619 
    620   if (!g_custom_entries)
    621     return;
    622 
    623   std::wstring url(url_cstring);
    624   size_t chunk_index = 0;
    625   size_t url_size = url.size();
    626 
    627   // Split the url across all the chunks.
    628   for (size_t url_offset = 0;
    629        chunk_index < kMaxUrlChunks && url_offset < url_size; ++chunk_index) {
    630     size_t current_chunk_size = std::min(url_size - url_offset,
    631         static_cast<size_t>(
    632             google_breakpad::CustomInfoEntry::kValueMaxLength - 1));
    633 
    634     wchar_t* entry_value =
    635         (*g_custom_entries)[g_url_chunks_offset + chunk_index].value;
    636     url._Copy_s(entry_value,
    637                 google_breakpad::CustomInfoEntry::kValueMaxLength,
    638                 current_chunk_size, url_offset);
    639     entry_value[current_chunk_size] = L'\0';
    640     url_offset += current_chunk_size;
    641   }
    642 
    643   // And null terminate any unneeded chunks.
    644   for (; chunk_index < kMaxUrlChunks; ++chunk_index)
    645     (*g_custom_entries)[g_url_chunks_offset + chunk_index].value[0] = L'\0';
    646 }
    647 
    648 extern "C" void __declspec(dllexport) __cdecl SetClientId(
    649     const wchar_t* client_id) {
    650   if (client_id == NULL)
    651     return;
    652 
    653   if (!g_custom_entries)
    654     return;
    655 
    656   base::wcslcpy((*g_custom_entries)[g_client_id_offset].value,
    657                 client_id,
    658                 google_breakpad::CustomInfoEntry::kValueMaxLength);
    659 }
    660 
    661 extern "C" void __declspec(dllexport) __cdecl SetNumberOfExtensions(
    662     int number_of_extensions) {
    663   SetIntegerValue(g_num_of_extensions_offset, number_of_extensions);
    664 }
    665 
    666 extern "C" void __declspec(dllexport) __cdecl SetExtensionID(
    667     int index, const wchar_t* id) {
    668   DCHECK(id);
    669   DCHECK(index < kMaxReportedActiveExtensions);
    670 
    671   if (!g_custom_entries)
    672     return;
    673 
    674   base::wcslcpy((*g_custom_entries)[g_extension_ids_offset + index].value,
    675                 id,
    676                 google_breakpad::CustomInfoEntry::kValueMaxLength);
    677 }
    678 
    679 extern "C" void __declspec(dllexport) __cdecl SetGpuInfo(
    680     const wchar_t* vendor_id, const wchar_t* device_id,
    681     const wchar_t* driver_version, const wchar_t* pixel_shader_version,
    682     const wchar_t* vertex_shader_version) {
    683   if (!g_custom_entries)
    684     return;
    685 
    686   const wchar_t* info[] = {
    687     vendor_id,
    688     device_id,
    689     driver_version,
    690     pixel_shader_version,
    691     vertex_shader_version
    692   };
    693 
    694   for (size_t i = 0; i < arraysize(info); ++i) {
    695     base::wcslcpy((*g_custom_entries)[g_gpu_info_offset + i].value,
    696                   info[i],
    697                   google_breakpad::CustomInfoEntry::kValueMaxLength);
    698   }
    699 }
    700 
    701 extern "C" void __declspec(dllexport) __cdecl SetPrinterInfo(
    702     const wchar_t* printer_info) {
    703   if (!g_custom_entries)
    704     return;
    705   std::vector<string16> info;
    706   base::SplitString(printer_info, L';', &info);
    707   DCHECK_LE(info.size(), kMaxReportedPrinterRecords);
    708   info.resize(kMaxReportedPrinterRecords);
    709   for (size_t i = 0; i < info.size(); ++i) {
    710     base::wcslcpy((*g_custom_entries)[g_printer_info_offset + i].value,
    711                 info[i].c_str(),
    712                 google_breakpad::CustomInfoEntry::kValueMaxLength);
    713   }
    714 }
    715 
    716 extern "C" void __declspec(dllexport) __cdecl SetNumberOfViews(
    717     int number_of_views) {
    718   SetIntegerValue(g_num_of_views_offset, number_of_views);
    719 }
    720 
    721 void SetCrashKeyValue(const base::StringPiece& key,
    722                       const base::StringPiece& value) {
    723   // CustomInfoEntry limits the length of key and value. If they exceed
    724   // their maximum length the underlying string handling functions raise
    725   // an exception and prematurely trigger a crash. Truncate here.
    726   base::StringPiece safe_key(key.substr(
    727       0, google_breakpad::CustomInfoEntry::kNameMaxLength  - 1));
    728   base::StringPiece safe_value(value.substr(
    729       0, google_breakpad::CustomInfoEntry::kValueMaxLength - 1));
    730 
    731   // Keep a copy of the safe key as a std::string, we'll reuse it later.
    732   std::string key_string(safe_key.begin(), safe_key.end());
    733 
    734   // If we already have a value for this key, update it; otherwise, insert
    735   // the new value if we have not exhausted the pre-allocated slots for dynamic
    736   // entries.
    737   DynamicEntriesMap::iterator it = g_dynamic_entries->find(key_string);
    738   google_breakpad::CustomInfoEntry* entry = NULL;
    739   if (it == g_dynamic_entries->end()) {
    740     if (g_dynamic_entries->size() >= g_dynamic_entries_count)
    741       return;
    742     entry = &(*g_custom_entries)[g_dynamic_keys_offset++];
    743     g_dynamic_entries->insert(std::make_pair(key_string, entry));
    744   } else {
    745     entry = it->second;
    746   }
    747 
    748   entry->set(UTF8ToWide(safe_key).data(), UTF8ToWide(safe_value).data());
    749 }
    750 
    751 extern "C" void __declspec(dllexport) __cdecl SetCrashKeyValuePair(
    752     const char* key, const char* value) {
    753   SetCrashKeyValue(base::StringPiece(key), base::StringPiece(value));
    754 }
    755 
    756 void ClearCrashKeyValue(const base::StringPiece& key) {
    757   DynamicEntriesMap::iterator it = g_dynamic_entries->find(key.as_string());
    758   if (it == g_dynamic_entries->end())
    759     return;
    760 
    761   it->second->set_value(NULL);
    762 }
    763 
    764 }  // namespace
    765 
    766 namespace testing {
    767 
    768 // Access to namespace protected functions for testing purposes.
    769 void InitCustomInfoEntries() {
    770   GetCustomInfo(L"", L"");
    771 }
    772 
    773 }  // namespace testing
    774 
    775 bool WrapMessageBoxWithSEH(const wchar_t* text, const wchar_t* caption,
    776                            UINT flags, bool* exit_now) {
    777   // We wrap the call to MessageBoxW with a SEH handler because it some
    778   // machines with CursorXP, PeaDict or with FontExplorer installed it crashes
    779   // uncontrollably here. Being this a best effort deal we better go away.
    780   __try {
    781     *exit_now = (IDOK != ::MessageBoxW(NULL, text, caption, flags));
    782   } __except(EXCEPTION_EXECUTE_HANDLER) {
    783     // Its not safe to continue executing, exit silently here.
    784     ::TerminateProcess(
    785         ::GetCurrentProcess(),
    786         breakpad::GetBreakpadClient()->GetResultCodeRespawnFailed());
    787   }
    788 
    789   return true;
    790 }
    791 
    792 // This function is executed by the child process that DumpDoneCallback()
    793 // spawned and basically just shows the 'chrome has crashed' dialog if
    794 // the CHROME_CRASHED environment variable is present.
    795 bool ShowRestartDialogIfCrashed(bool* exit_now) {
    796   // If we are being launched in metro mode don't try to show the dialog.
    797   if (base::win::IsMetroProcess())
    798     return false;
    799 
    800   // Only show this for the browser process. See crbug.com/132119.
    801   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
    802   std::string process_type =
    803       command_line.GetSwitchValueASCII(switches::kProcessType);
    804   if (!process_type.empty())
    805     return false;
    806 
    807   base::string16 message;
    808   base::string16 title;
    809   bool is_rtl_locale;
    810   if (!breakpad::GetBreakpadClient()->ShouldShowRestartDialog(
    811           &title, &message, &is_rtl_locale)) {
    812     return false;
    813   }
    814 
    815   // If the UI layout is right-to-left, we need to pass the appropriate MB_XXX
    816   // flags so that an RTL message box is displayed.
    817   UINT flags = MB_OKCANCEL | MB_ICONWARNING;
    818   if (is_rtl_locale)
    819     flags |= MB_RIGHT | MB_RTLREADING;
    820 
    821   return WrapMessageBoxWithSEH(base::UTF16ToWide(message).c_str(),
    822                                base::UTF16ToWide(title).c_str(),
    823                                flags,
    824                                exit_now);
    825 }
    826 
    827 // Crashes the process after generating a dump for the provided exception. Note
    828 // that the crash reporter should be initialized before calling this function
    829 // for it to do anything.
    830 extern "C" int __declspec(dllexport) CrashForException(
    831     EXCEPTION_POINTERS* info) {
    832   if (g_breakpad) {
    833     g_breakpad->WriteMinidumpForException(info);
    834     // Patched stub exists based on conditions (See InitCrashReporter).
    835     // As a side note this function also gets called from
    836     // WindowProcExceptionFilter.
    837     if (g_real_terminate_process_stub == NULL) {
    838       ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED);
    839     } else {
    840       NtTerminateProcessPtr real_terminate_proc =
    841           reinterpret_cast<NtTerminateProcessPtr>(
    842               static_cast<char*>(g_real_terminate_process_stub));
    843       real_terminate_proc(::GetCurrentProcess(), content::RESULT_CODE_KILLED);
    844     }
    845   }
    846   return EXCEPTION_CONTINUE_SEARCH;
    847 }
    848 
    849 NTSTATUS WINAPI HookNtTerminateProcess(HANDLE ProcessHandle,
    850                                        NTSTATUS ExitStatus) {
    851   if (g_breakpad &&
    852       (ProcessHandle == ::GetCurrentProcess() || ProcessHandle == NULL)) {
    853     NT_TIB* tib = reinterpret_cast<NT_TIB*>(NtCurrentTeb());
    854     void* address_on_stack = _AddressOfReturnAddress();
    855     if (address_on_stack < tib->StackLimit ||
    856         address_on_stack > tib->StackBase) {
    857       g_surrogate_exception_record.ExceptionAddress = _ReturnAddress();
    858       g_surrogate_exception_record.ExceptionCode = DBG_TERMINATE_PROCESS;
    859       g_surrogate_exception_record.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
    860       CrashForException(&g_surrogate_exception_pointers);
    861     }
    862   }
    863 
    864   NtTerminateProcessPtr real_proc =
    865       reinterpret_cast<NtTerminateProcessPtr>(
    866           static_cast<char*>(g_real_terminate_process_stub));
    867   return real_proc(ProcessHandle, ExitStatus);
    868 }
    869 
    870 static void InitTerminateProcessHooks() {
    871   NtTerminateProcessPtr terminate_process_func_address =
    872       reinterpret_cast<NtTerminateProcessPtr>(::GetProcAddress(
    873           ::GetModuleHandle(L"ntdll.dll"), "NtTerminateProcess"));
    874   if (terminate_process_func_address == NULL)
    875     return;
    876 
    877   DWORD old_protect = 0;
    878   if (!::VirtualProtect(terminate_process_func_address, 5,
    879                         PAGE_EXECUTE_READWRITE, &old_protect))
    880     return;
    881 
    882   g_real_terminate_process_stub = reinterpret_cast<char*>(VirtualAllocEx(
    883       ::GetCurrentProcess(), NULL, sidestep::kMaxPreambleStubSize,
    884       MEM_COMMIT, PAGE_EXECUTE_READWRITE));
    885   if (g_real_terminate_process_stub == NULL)
    886     return;
    887 
    888   g_surrogate_exception_pointers.ContextRecord = &g_surrogate_context;
    889   g_surrogate_exception_pointers.ExceptionRecord =
    890       &g_surrogate_exception_record;
    891 
    892   sidestep::SideStepError patch_result =
    893       sidestep::PreamblePatcher::Patch(
    894           terminate_process_func_address, HookNtTerminateProcess,
    895           g_real_terminate_process_stub, sidestep::kMaxPreambleStubSize);
    896   if (patch_result != sidestep::SIDESTEP_SUCCESS) {
    897     CHECK(::VirtualFreeEx(::GetCurrentProcess(), g_real_terminate_process_stub,
    898                     0, MEM_RELEASE));
    899     CHECK(::VirtualProtect(terminate_process_func_address, 5, old_protect,
    900                            &old_protect));
    901     return;
    902   }
    903 
    904   DWORD dummy = 0;
    905   CHECK(::VirtualProtect(terminate_process_func_address,
    906                          5,
    907                          old_protect,
    908                          &dummy));
    909   CHECK(::VirtualProtect(g_real_terminate_process_stub,
    910                          sidestep::kMaxPreambleStubSize,
    911                          old_protect,
    912                          &old_protect));
    913 }
    914 
    915 static void InitPipeNameEnvVar(bool is_per_user_install) {
    916   scoped_ptr<base::Environment> env(base::Environment::Create());
    917   if (env->HasVar(kPipeNameVar)) {
    918     // The Breakpad pipe name is already configured: nothing to do.
    919     return;
    920   }
    921 
    922   // Check whether configuration management controls crash reporting.
    923   bool crash_reporting_enabled = true;
    924   bool controlled_by_policy =
    925       MetricsReportingControlledByPolicy(&crash_reporting_enabled);
    926 
    927   const CommandLine& command = *CommandLine::ForCurrentProcess();
    928   bool use_crash_service =
    929       !controlled_by_policy &&
    930       (command.HasSwitch(switches::kNoErrorDialogs) ||
    931        breakpad::GetBreakpadClient()->IsRunningUnattended());
    932 
    933   std::wstring pipe_name;
    934   if (use_crash_service) {
    935     // Crash reporting is done by crash_service.exe.
    936     pipe_name = kChromePipeName;
    937   } else {
    938     // We want to use the Google Update crash reporting. We need to check if the
    939     // user allows it first (in case the administrator didn't already decide
    940     // via policy).
    941     if (!controlled_by_policy) {
    942       crash_reporting_enabled =
    943           breakpad::GetBreakpadClient()->GetCollectStatsConsent();
    944     }
    945 
    946     if (!crash_reporting_enabled) {
    947       if (!controlled_by_policy &&
    948           breakpad::GetBreakpadClient()->GetDeferredUploadsSupported(
    949               is_per_user_install)) {
    950         g_deferred_crash_uploads = true;
    951       } else {
    952         return;
    953       }
    954     }
    955 
    956     // Build the pipe name. It can be either:
    957     // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18"
    958     // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>"
    959     std::wstring user_sid;
    960     if (is_per_user_install) {
    961       if (!base::win::GetUserSidString(&user_sid)) {
    962         return;
    963       }
    964     } else {
    965       user_sid = kSystemPrincipalSid;
    966     }
    967 
    968     pipe_name = kGoogleUpdatePipeName;
    969     pipe_name += user_sid;
    970   }
    971   env->SetVar(kPipeNameVar, WideToASCII(pipe_name));
    972 }
    973 
    974 void InitCrashReporter() {
    975   const CommandLine& command = *CommandLine::ForCurrentProcess();
    976   if (command.HasSwitch(switches::kDisableBreakpad))
    977     return;
    978 
    979   // Disable the message box for assertions.
    980   _CrtSetReportMode(_CRT_ASSERT, 0);
    981 
    982   std::wstring process_type =
    983     command.GetSwitchValueNative(switches::kProcessType);
    984   if (process_type.empty())
    985     process_type = L"browser";
    986 
    987   wchar_t exe_path[MAX_PATH];
    988   exe_path[0] = 0;
    989   GetModuleFileNameW(NULL, exe_path, MAX_PATH);
    990 
    991   bool is_per_user_install = breakpad::GetBreakpadClient()->GetIsPerUserInstall(
    992       base::FilePath(exe_path));
    993 
    994   base::debug::SetCrashKeyReportingFunctions(
    995       &SetCrashKeyValue, &ClearCrashKeyValue);
    996 
    997   google_breakpad::CustomClientInfo* custom_info =
    998       GetCustomInfo(exe_path, process_type);
    999 
   1000   google_breakpad::ExceptionHandler::MinidumpCallback callback = NULL;
   1001   LPTOP_LEVEL_EXCEPTION_FILTER default_filter = NULL;
   1002   // We install the post-dump callback only for the browser and service
   1003   // processes. It spawns a new browser/service process.
   1004   if (process_type == L"browser") {
   1005     callback = &DumpDoneCallback;
   1006     default_filter = &ChromeExceptionFilter;
   1007   } else if (process_type == L"service") {
   1008     callback = &DumpDoneCallback;
   1009     default_filter = &ServiceExceptionFilter;
   1010   }
   1011 
   1012   if (process_type == L"browser") {
   1013     InitPipeNameEnvVar(is_per_user_install);
   1014   }
   1015 
   1016   scoped_ptr<base::Environment> env(base::Environment::Create());
   1017   std::string pipe_name_ascii;
   1018   if (!env->GetVar(kPipeNameVar, &pipe_name_ascii)) {
   1019     // Breakpad is not enabled.  Configuration is managed or the user
   1020     // did not allow Google Update to send crashes.  We need to use
   1021     // our default crash handler instead, but only for the
   1022     // browser/service processes.
   1023     if (default_filter)
   1024       InitDefaultCrashCallback(default_filter);
   1025     return;
   1026   }
   1027   std::wstring pipe_name = ASCIIToWide(pipe_name_ascii);
   1028 
   1029 #ifdef _WIN64
   1030   // The protocol for connecting to the out-of-process Breakpad crash
   1031   // reporter is different for x86-32 and x86-64: the message sizes
   1032   // are different because the message struct contains a pointer.  As
   1033   // a result, there are two different named pipes to connect to.  The
   1034   // 64-bit one is distinguished with an "-x64" suffix.
   1035   pipe_name += L"-x64";
   1036 #endif
   1037 
   1038   // Get the alternate dump directory. We use the temp path.
   1039   wchar_t temp_dir[MAX_PATH] = {0};
   1040   ::GetTempPathW(MAX_PATH, temp_dir);
   1041 
   1042   MINIDUMP_TYPE dump_type = kSmallDumpType;
   1043   // Capture full memory if explicitly instructed to.
   1044   if (command.HasSwitch(switches::kFullMemoryCrashReport))
   1045     dump_type = kFullDumpType;
   1046   else if (breakpad::GetBreakpadClient()->GetShouldDumpLargerDumps(
   1047                is_per_user_install))
   1048     dump_type = kLargerDumpType;
   1049 
   1050   g_breakpad = new google_breakpad::ExceptionHandler(temp_dir, &FilterCallback,
   1051                    callback, NULL,
   1052                    google_breakpad::ExceptionHandler::HANDLER_ALL,
   1053                    dump_type, pipe_name.c_str(), custom_info);
   1054 
   1055   // Now initialize the non crash dump handler.
   1056   g_dumphandler_no_crash = new google_breakpad::ExceptionHandler(temp_dir,
   1057       &FilterCallbackWhenNoCrash,
   1058       &DumpDoneCallbackWhenNoCrash,
   1059       NULL,
   1060       // Set the handler to none so this handler would not be added to
   1061       // |handler_stack_| in |ExceptionHandler| which is a list of exception
   1062       // handlers.
   1063       google_breakpad::ExceptionHandler::HANDLER_NONE,
   1064       dump_type, pipe_name.c_str(), custom_info);
   1065 
   1066   if (g_breakpad->IsOutOfProcess()) {
   1067     // Tells breakpad to handle breakpoint and single step exceptions.
   1068     // This might break JIT debuggers, but at least it will always
   1069     // generate a crashdump for these exceptions.
   1070     g_breakpad->set_handle_debug_exceptions(true);
   1071 
   1072 #ifndef _WIN64
   1073     std::string headless;
   1074     if (process_type != L"browser" &&
   1075         !breakpad::GetBreakpadClient()->IsRunningUnattended()) {
   1076       // Initialize the hook TerminateProcess to catch unexpected exits.
   1077       InitTerminateProcessHooks();
   1078     }
   1079 #endif
   1080   }
   1081 }
   1082 
   1083 void InitDefaultCrashCallback(LPTOP_LEVEL_EXCEPTION_FILTER filter) {
   1084   previous_filter = SetUnhandledExceptionFilter(filter);
   1085 }
   1086 
   1087 void StringVectorToCStringVector(const std::vector<std::wstring>& wstrings,
   1088                                  std::vector<const wchar_t*>* cstrings) {
   1089   cstrings->clear();
   1090   cstrings->reserve(wstrings.size());
   1091   for (size_t i = 0; i < wstrings.size(); ++i)
   1092     cstrings->push_back(wstrings[i].c_str());
   1093 }
   1094