Home | History | Annotate | Download | only in app
      1 // Copyright 2013 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/chrome_breakpad_client.h"
      6 
      7 #include "base/atomicops.h"
      8 #include "base/command_line.h"
      9 #include "base/environment.h"
     10 #include "base/files/file_path.h"
     11 #include "base/logging.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/path_service.h"
     14 #include "base/strings/safe_sprintf.h"
     15 #include "base/strings/string_split.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "chrome/common/chrome_constants.h"
     18 #include "chrome/common/chrome_paths.h"
     19 #include "chrome/common/chrome_result_codes.h"
     20 #include "chrome/common/chrome_switches.h"
     21 #include "chrome/common/crash_keys.h"
     22 #include "chrome/common/env_vars.h"
     23 #include "chrome/installer/util/google_update_settings.h"
     24 
     25 #if defined(OS_WIN)
     26 #include <windows.h>
     27 
     28 #include "base/file_version_info.h"
     29 #include "base/win/registry.h"
     30 #include "chrome/installer/util/google_chrome_sxs_distribution.h"
     31 #include "chrome/installer/util/install_util.h"
     32 #include "policy/policy_constants.h"
     33 #endif
     34 
     35 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS)
     36 #include "chrome/browser/crash_upload_list.h"
     37 #include "chrome/common/chrome_version_info_posix.h"
     38 #endif
     39 
     40 #if defined(OS_POSIX)
     41 #include "chrome/common/dump_without_crashing.h"
     42 #endif
     43 
     44 #if defined(OS_ANDROID)
     45 #include "chrome/common/descriptors_android.h"
     46 #endif
     47 
     48 #if defined(OS_CHROMEOS)
     49 #include "chrome/common/chrome_version_info.h"
     50 #include "chromeos/chromeos_switches.h"
     51 #endif
     52 
     53 namespace chrome {
     54 
     55 namespace {
     56 
     57 #if defined(OS_WIN)
     58 // This is the minimum version of google update that is required for deferred
     59 // crash uploads to work.
     60 const char kMinUpdateVersion[] = "1.3.21.115";
     61 
     62 // The value name prefix will be of the form {chrome-version}-{pid}-{timestamp}
     63 // (i.e., "#####.#####.#####.#####-########-########") which easily fits into a
     64 // 63 character buffer.
     65 const char kBrowserCrashDumpPrefixTemplate[] = "%s-%08x-%08x";
     66 const size_t kBrowserCrashDumpPrefixLength = 63;
     67 char g_browser_crash_dump_prefix[kBrowserCrashDumpPrefixLength + 1] = {};
     68 
     69 // These registry key to which we'll write a value for each crash dump attempt.
     70 HKEY g_browser_crash_dump_regkey = NULL;
     71 
     72 // A atomic counter to make each crash dump value name unique.
     73 base::subtle::Atomic32 g_browser_crash_dump_count = 0;
     74 #endif
     75 
     76 }  // namespace
     77 
     78 ChromeBreakpadClient::ChromeBreakpadClient() {}
     79 
     80 ChromeBreakpadClient::~ChromeBreakpadClient() {}
     81 
     82 void ChromeBreakpadClient::SetClientID(const std::string& client_id) {
     83   crash_keys::SetClientID(client_id);
     84 }
     85 
     86 #if defined(OS_WIN)
     87 bool ChromeBreakpadClient::GetAlternativeCrashDumpLocation(
     88     base::FilePath* crash_dir) {
     89   // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
     90   // location to write breakpad crash dumps can be set.
     91   scoped_ptr<base::Environment> env(base::Environment::Create());
     92   std::string alternate_crash_dump_location;
     93   if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) {
     94     *crash_dir = base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location);
     95     return true;
     96   }
     97 
     98   return false;
     99 }
    100 
    101 void ChromeBreakpadClient::GetProductNameAndVersion(
    102     const base::FilePath& exe_path,
    103     base::string16* product_name,
    104     base::string16* version,
    105     base::string16* special_build,
    106     base::string16* channel_name) {
    107   DCHECK(product_name);
    108   DCHECK(version);
    109   DCHECK(special_build);
    110   DCHECK(channel_name);
    111 
    112   scoped_ptr<FileVersionInfo> version_info(
    113       FileVersionInfo::CreateFileVersionInfo(exe_path));
    114 
    115   if (version_info.get()) {
    116     // Get the information from the file.
    117     *version = version_info->product_version();
    118     if (!version_info->is_official_build())
    119       version->append(base::ASCIIToUTF16("-devel"));
    120 
    121     const CommandLine& command = *CommandLine::ForCurrentProcess();
    122     if (command.HasSwitch(switches::kChromeFrame)) {
    123       *product_name = base::ASCIIToUTF16("ChromeFrame");
    124     } else {
    125       *product_name = version_info->product_short_name();
    126     }
    127 
    128     *special_build = version_info->special_build();
    129   } else {
    130     // No version info found. Make up the values.
    131     *product_name = base::ASCIIToUTF16("Chrome");
    132     *version = base::ASCIIToUTF16("0.0.0.0-devel");
    133   }
    134 
    135   std::wstring channel_string;
    136   GoogleUpdateSettings::GetChromeChannelAndModifiers(
    137       !GetIsPerUserInstall(exe_path), &channel_string);
    138   *channel_name = base::WideToUTF16(channel_string);
    139 }
    140 
    141 bool ChromeBreakpadClient::ShouldShowRestartDialog(base::string16* title,
    142                                                    base::string16* message,
    143                                                    bool* is_rtl_locale) {
    144   scoped_ptr<base::Environment> env(base::Environment::Create());
    145   if (!env->HasVar(env_vars::kShowRestart) ||
    146       !env->HasVar(env_vars::kRestartInfo) ||
    147       env->HasVar(env_vars::kMetroConnected)) {
    148     return false;
    149   }
    150 
    151   std::string restart_info;
    152   env->GetVar(env_vars::kRestartInfo, &restart_info);
    153 
    154   // The CHROME_RESTART var contains the dialog strings separated by '|'.
    155   // See ChromeBrowserMainPartsWin::PrepareRestartOnCrashEnviroment()
    156   // for details.
    157   std::vector<std::string> dlg_strings;
    158   base::SplitString(restart_info, '|', &dlg_strings);
    159 
    160   if (dlg_strings.size() < 3)
    161     return false;
    162 
    163   *title = base::UTF8ToUTF16(dlg_strings[0]);
    164   *message = base::UTF8ToUTF16(dlg_strings[1]);
    165   *is_rtl_locale = dlg_strings[2] == env_vars::kRtlLocale;
    166   return true;
    167 }
    168 
    169 bool ChromeBreakpadClient::AboutToRestart() {
    170   scoped_ptr<base::Environment> env(base::Environment::Create());
    171   if (!env->HasVar(env_vars::kRestartInfo))
    172     return false;
    173 
    174   env->SetVar(env_vars::kShowRestart, "1");
    175   return true;
    176 }
    177 
    178 bool ChromeBreakpadClient::GetDeferredUploadsSupported(
    179     bool is_per_user_install) {
    180   Version update_version = GoogleUpdateSettings::GetGoogleUpdateVersion(
    181       !is_per_user_install);
    182   if (!update_version.IsValid() ||
    183       update_version.IsOlderThan(std::string(kMinUpdateVersion)))
    184     return false;
    185 
    186   return true;
    187 }
    188 
    189 bool ChromeBreakpadClient::GetIsPerUserInstall(const base::FilePath& exe_path) {
    190   return InstallUtil::IsPerUserInstall(exe_path.value().c_str());
    191 }
    192 
    193 bool ChromeBreakpadClient::GetShouldDumpLargerDumps(bool is_per_user_install) {
    194   base::string16 channel_name(base::WideToUTF16(
    195       GoogleUpdateSettings::GetChromeChannel(!is_per_user_install)));
    196 
    197   // Capture more detail in crash dumps for beta and dev channel builds.
    198   if (channel_name == base::ASCIIToUTF16("dev") ||
    199       channel_name == base::ASCIIToUTF16("beta") ||
    200       channel_name == GoogleChromeSxSDistribution::ChannelName())
    201     return true;
    202   return false;
    203 }
    204 
    205 int ChromeBreakpadClient::GetResultCodeRespawnFailed() {
    206   return chrome::RESULT_CODE_RESPAWN_FAILED;
    207 }
    208 
    209 void ChromeBreakpadClient::InitBrowserCrashDumpsRegKey() {
    210   DCHECK(g_browser_crash_dump_regkey == NULL);
    211 
    212   base::win::RegKey regkey;
    213   if (regkey.Create(HKEY_CURRENT_USER,
    214                     chrome::kBrowserCrashDumpAttemptsRegistryPath,
    215                     KEY_ALL_ACCESS) != ERROR_SUCCESS) {
    216     return;
    217   }
    218 
    219   // We use the current process id and the current tick count as a (hopefully)
    220   // unique combination for the crash dump value. There's a small chance that
    221   // across a reboot we might have a crash dump signal written, and the next
    222   // browser process might have the same process id and tick count, but crash
    223   // before consuming the signal (overwriting the signal with an identical one).
    224   // For now, we're willing to live with that risk.
    225   int length = base::strings::SafeSPrintf(g_browser_crash_dump_prefix,
    226                                           kBrowserCrashDumpPrefixTemplate,
    227                                           chrome::kChromeVersion,
    228                                           ::GetCurrentProcessId(),
    229                                           ::GetTickCount());
    230   if (length <= 0) {
    231     NOTREACHED();
    232     g_browser_crash_dump_prefix[0] = '\0';
    233     return;
    234   }
    235 
    236   // Hold the registry key in a global for update on crash dump.
    237   g_browser_crash_dump_regkey = regkey.Take();
    238 }
    239 
    240 void ChromeBreakpadClient::RecordCrashDumpAttempt(bool is_real_crash) {
    241   // If we're not a browser (or the registry is unavailable to us for some
    242   // reason) then there's nothing to do.
    243   if (g_browser_crash_dump_regkey == NULL)
    244     return;
    245 
    246   // Generate the final value name we'll use (appends the crash number to the
    247   // base value name).
    248   const size_t kMaxValueSize = 2 * kBrowserCrashDumpPrefixLength;
    249   char value_name[kMaxValueSize + 1] = {};
    250   int length = base::strings::SafeSPrintf(
    251       value_name,
    252       "%s-%x",
    253       g_browser_crash_dump_prefix,
    254       base::subtle::NoBarrier_AtomicIncrement(&g_browser_crash_dump_count, 1));
    255 
    256   if (length > 0) {
    257     DWORD value_dword = is_real_crash ? 1 : 0;
    258     ::RegSetValueExA(g_browser_crash_dump_regkey, value_name, 0, REG_DWORD,
    259                      reinterpret_cast<BYTE*>(&value_dword),
    260                      sizeof(value_dword));
    261   }
    262 }
    263 
    264 bool ChromeBreakpadClient::ReportingIsEnforcedByPolicy(bool* breakpad_enabled) {
    265 // Determine whether configuration management allows loading the crash reporter.
    266 // Since the configuration management infrastructure is not initialized at this
    267 // point, we read the corresponding registry key directly. The return status
    268 // indicates whether policy data was successfully read. If it is true,
    269 // |breakpad_enabled| contains the value set by policy.
    270   string16 key_name = UTF8ToUTF16(policy::key::kMetricsReportingEnabled);
    271   DWORD value = 0;
    272   base::win::RegKey hklm_policy_key(HKEY_LOCAL_MACHINE,
    273                                     policy::kRegistryChromePolicyKey, KEY_READ);
    274   if (hklm_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) {
    275     *breakpad_enabled = value != 0;
    276     return true;
    277   }
    278 
    279   base::win::RegKey hkcu_policy_key(HKEY_CURRENT_USER,
    280                                     policy::kRegistryChromePolicyKey, KEY_READ);
    281   if (hkcu_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) {
    282     *breakpad_enabled = value != 0;
    283     return true;
    284   }
    285 
    286   return false;
    287 }
    288 #endif
    289 
    290 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS)
    291 void ChromeBreakpadClient::GetProductNameAndVersion(std::string* product_name,
    292                                                     std::string* version) {
    293   DCHECK(product_name);
    294   DCHECK(version);
    295 #if defined(OS_ANDROID)
    296   *product_name = "Chrome_Android";
    297 #elif defined(OS_CHROMEOS)
    298   *product_name = "Chrome_ChromeOS";
    299 #else  // OS_LINUX
    300 #if !defined(ADDRESS_SANITIZER)
    301   *product_name = "Chrome_Linux";
    302 #else
    303   *product_name = "Chrome_Linux_ASan";
    304 #endif
    305 #endif
    306 
    307   *version = PRODUCT_VERSION;
    308 }
    309 
    310 base::FilePath ChromeBreakpadClient::GetReporterLogFilename() {
    311   return base::FilePath(CrashUploadList::kReporterLogFilename);
    312 }
    313 #endif
    314 
    315 bool ChromeBreakpadClient::GetCrashDumpLocation(base::FilePath* crash_dir) {
    316   // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
    317   // location to write breakpad crash dumps can be set.
    318   scoped_ptr<base::Environment> env(base::Environment::Create());
    319   std::string alternate_crash_dump_location;
    320   if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) {
    321     base::FilePath crash_dumps_dir_path =
    322         base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location);
    323     PathService::Override(chrome::DIR_CRASH_DUMPS, crash_dumps_dir_path);
    324   }
    325 
    326   return PathService::Get(chrome::DIR_CRASH_DUMPS, crash_dir);
    327 }
    328 
    329 #if defined(OS_POSIX)
    330 void ChromeBreakpadClient::SetDumpWithoutCrashingFunction(void (*function)()) {
    331   logging::SetDumpWithoutCrashingFunction(function);
    332 }
    333 #endif
    334 
    335 size_t ChromeBreakpadClient::RegisterCrashKeys() {
    336   // Note: This is not called on Windows because Breakpad is initialized in the
    337   // EXE module, but code that uses crash keys is in the DLL module.
    338   // RegisterChromeCrashKeys() will be called after the DLL is loaded.
    339   return crash_keys::RegisterChromeCrashKeys();
    340 }
    341 
    342 bool ChromeBreakpadClient::IsRunningUnattended() {
    343   scoped_ptr<base::Environment> env(base::Environment::Create());
    344   return env->HasVar(env_vars::kHeadless);
    345 }
    346 
    347 bool ChromeBreakpadClient::GetCollectStatsConsent() {
    348   // Convert #define to a variable so that we can use if() rather than
    349   // #if below and so at least compile-test the Chrome code in
    350   // Chromium builds.
    351 #if defined(GOOGLE_CHROME_BUILD)
    352   bool is_chrome_build = true;
    353 #else
    354   bool is_chrome_build = false;
    355 #endif
    356 
    357 #if defined(OS_CHROMEOS)
    358   bool is_guest_session = CommandLine::ForCurrentProcess()->HasSwitch(
    359       chromeos::switches::kGuestSession);
    360   bool is_stable_channel =
    361       chrome::VersionInfo::GetChannel() == chrome::VersionInfo::CHANNEL_STABLE;
    362 
    363   if (is_guest_session && is_stable_channel)
    364     return false;
    365 #endif  // defined(OS_CHROMEOS)
    366 
    367 #if defined(OS_ANDROID)
    368   // TODO(jcivelli): we should not initialize the crash-reporter when it was not
    369   // enabled. Right now if it is disabled we still generate the minidumps but we
    370   // do not upload them.
    371   return is_chrome_build;
    372 #else  // !defined(OS_ANDROID)
    373   return is_chrome_build && GoogleUpdateSettings::GetCollectStatsConsent();
    374 #endif  // defined(OS_ANDROID)
    375 }
    376 
    377 #if defined(OS_ANDROID)
    378 int ChromeBreakpadClient::GetAndroidMinidumpDescriptor() {
    379   return kAndroidMinidumpDescriptor;
    380 }
    381 #endif
    382 
    383 bool ChromeBreakpadClient::EnableBreakpadForProcess(
    384     const std::string& process_type) {
    385   return process_type == switches::kRendererProcess ||
    386          process_type == switches::kPluginProcess ||
    387          process_type == switches::kPpapiPluginProcess ||
    388          process_type == switches::kZygoteProcess ||
    389          process_type == switches::kGpuProcess;
    390 }
    391 
    392 }  // namespace chrome
    393