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