diff options
author | siggi@chromium.org <siggi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-01 21:33:28 +0000 |
---|---|---|
committer | siggi@chromium.org <siggi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-01 21:33:28 +0000 |
commit | 9e95fd3e871e0bb43c08329e113965ea6d140837 (patch) | |
tree | acdc4eab0476fc20a0b633b63a539601928dd1ce | |
parent | 7d0d57e4f016f17a951102ab8fc04273b9f15a7c (diff) | |
download | chromium_src-9e95fd3e871e0bb43c08329e113965ea6d140837.zip chromium_src-9e95fd3e871e0bb43c08329e113965ea6d140837.tar.gz chromium_src-9e95fd3e871e0bb43c08329e113965ea6d140837.tar.bz2 |
Tweak minidump flags to include a little more detail.
In release channel this adds PEB/TEB and the unloaded module list to crash dumps.
In dev/beta channel builds this additionally adds stack-referenced memory to crash dumps.
For full memory dumps, this adds capture of the PEB/TEB, unloaded module list and all handle information.
These minidump flags have been verified safe against the DbgHelp.dll version shipping with XP SP2 and later.
Move some code from platform_util to install_util to allow reusing it in the chrome exe project.
Add a test for the additional install_util code.
BUG=32441
TEST=Unittests in this change.
Review URL: http://codereview.chromium.org/659001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40300 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/app/breakpad_win.cc | 35 | ||||
-rw-r--r-- | chrome/common/platform_util_win.cc | 64 | ||||
-rw-r--r-- | chrome/installer/installer.gyp | 1 | ||||
-rw-r--r-- | chrome/installer/util/google_update_settings.cc | 25 | ||||
-rw-r--r-- | chrome/installer/util/google_update_settings.h | 5 | ||||
-rw-r--r-- | chrome/installer/util/google_update_settings_unittest.cc | 153 |
6 files changed, 220 insertions, 63 deletions
diff --git a/chrome/app/breakpad_win.cc b/chrome/app/breakpad_win.cc index 9668038..a0ed497 100644 --- a/chrome/app/breakpad_win.cc +++ b/chrome/app/breakpad_win.cc @@ -25,6 +25,24 @@ namespace { +// Minidump with stacks, PEB, TEB, and unloaded module list. +const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>( + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithUnloadedModules); // Get unloaded modules when available. + +// Minidump with all of the above, plus memory referenced from stack. +const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>( + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithUnloadedModules | // Get unloaded modules when available. + MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack. + +// Large dump with all process memory. +const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>( + MiniDumpWithFullMemory | // Full memory from process. + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithHandleData | // Get all handle information. + MiniDumpWithUnloadedModules); // Get unloaded modules when available. + const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\"; const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; @@ -326,6 +344,8 @@ static DWORD __stdcall InitCrashReporterThread(void* param) { const CommandLine& command = *CommandLine::ForCurrentProcess(); bool use_crash_service = command.HasSwitch(switches::kNoErrorDialogs) || GetEnvironmentVariable(L"CHROME_HEADLESS", NULL, 0); + bool is_per_user_install = + InstallUtil::IsPerUserInstall(info->dll_path.c_str()); std::wstring pipe_name; if (use_crash_service) { @@ -346,7 +366,7 @@ static DWORD __stdcall InitCrashReporterThread(void* param) { // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18" // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>" std::wstring user_sid; - if (InstallUtil::IsPerUserInstall(info->dll_path.c_str())) { + if (is_per_user_install) { if (!win_util::GetUserSidString(&user_sid)) { if (callback) InitDefaultCrashCallback(); @@ -364,8 +384,17 @@ static DWORD __stdcall InitCrashReporterThread(void* param) { wchar_t temp_dir[MAX_PATH] = {0}; ::GetTempPathW(MAX_PATH, temp_dir); - bool full_dump = command.HasSwitch(switches::kFullMemoryCrashReport); - MINIDUMP_TYPE dump_type = full_dump ? MiniDumpWithFullMemory : MiniDumpNormal; + MINIDUMP_TYPE dump_type = kSmallDumpType; + // Capture full memory if explicitly instructed to. + if (command.HasSwitch(switches::kFullMemoryCrashReport)) { + dump_type = kFullDumpType; + } else { + // Capture more detail in crash dumps for beta and dev channel builds. + string16 channel_string; + GoogleUpdateSettings::GetChromeChannel(&channel_string); + if (channel_string == L"dev" || channel_string == L"beta") + dump_type = kLargerDumpType; + } g_breakpad = new google_breakpad::ExceptionHandler(temp_dir, &FilterCallback, callback, NULL, diff --git a/chrome/common/platform_util_win.cc b/chrome/common/platform_util_win.cc index 5553c81..9313a51 100644 --- a/chrome/common/platform_util_win.cc +++ b/chrome/common/platform_util_win.cc @@ -17,6 +17,7 @@ #include "base/registry.h" #include "base/scoped_comptr_win.h" #include "base/string_util.h" +#include "chrome/installer/util/google_update_settings.h" #include "chrome/installer/util/google_update_constants.h" #include "googleurl/src/gurl.h" @@ -151,68 +152,11 @@ void SimpleErrorBox(gfx::NativeWindow parent, win_util::MessageBox(parent, message, title, MB_OK | MB_SETFOREGROUND); } - -namespace { - -std::wstring CurrentChromeChannel() { - // Start by seeing if we can find the Clients key on the HKLM branch. - // For each search, require confirmation by looking for "name" - // inside it. We've noticed problems cleaning up the registry on - // uninstall or upgrade (http://crbug.com/33532, - // http://crbug.com/33534), and have other reports of inconsistency - // (http://crbug.com/32479). - HKEY registry_hive = HKEY_LOCAL_MACHINE; - std::wstring key = google_update::kRegPathClients + std::wstring(L"\\") + - google_update::kChromeUpgradeCode; - RegKey google_update_hklm(registry_hive, key.c_str(), KEY_READ); - - if (!google_update_hklm.Valid() || - !google_update_hklm.ValueExists(google_update::kRegNameField)) { - // HKLM failed us, try the same for the HKCU branch. - registry_hive = HKEY_CURRENT_USER; - RegKey google_update_hkcu(registry_hive, key.c_str(), KEY_READ); - if (!google_update_hkcu.Valid() || - !google_update_hkcu.ValueExists(google_update::kRegNameField)) { - // Unknown. - registry_hive = 0; - } - } - - std::wstring update_branch(L"unknown"); // the default. - if (registry_hive != 0) { - // Now that we know which hive to use, read the 'ap' key from it. - std::wstring key = google_update::kRegPathClientState + - std::wstring(L"\\") + google_update::kChromeUpgradeCode; - RegKey client_state(registry_hive, key.c_str(), KEY_READ); - client_state.ReadValue(google_update::kRegApField, &update_branch); - // If the parent folder exists (we have a valid install) but the - // 'ap' key is empty, we necessarily are the stable channel. - // So we print nothing. - if (update_branch.empty() && client_state.Valid()) { - update_branch = L""; - } - } - - // Map to something pithy for human consumption. There are no rules as to - // what the ap string can contain, but generally it will contain a number - // followed by a dash followed by the branch name (and then some random - // suffix). We fall back on empty string in case we fail to parse. - // Only ever return "", "unknown", "dev" or "beta". - if (update_branch.find(L"-beta") != std::wstring::npos) - update_branch = L"beta"; - else if (update_branch.find(L"-dev") != std::wstring::npos) - update_branch = L"dev"; - else if (!update_branch.empty()) - update_branch = L"unknown"; - - return update_branch; -} - -} // namespace - string16 GetVersionStringModifier() { #if defined(GOOGLE_CHROME_BUILD) - return CurrentChromeChannel(); + string16 channel; + GoogleUpdateSettings::GetChromeChannel(&channel); + return channel; #else return string16(); #endif diff --git a/chrome/installer/installer.gyp b/chrome/installer/installer.gyp index 818817d..6d4a832 100644 --- a/chrome/installer/installer.gyp +++ b/chrome/installer/installer.gyp @@ -86,6 +86,7 @@ 'util/delete_reg_value_work_item_unittest.cc', 'util/delete_tree_work_item_unittest.cc', 'util/google_chrome_distribution_unittest.cc', + 'util/google_update_settings_unittest.cc', 'util/helper_unittest.cc', 'util/installer_util_unittests.rc', 'util/installer_util_unittests_resource.h', diff --git a/chrome/installer/util/google_update_settings.cc b/chrome/installer/util/google_update_settings.cc index a5298e5..2dabe50 100644 --- a/chrome/installer/util/google_update_settings.cc +++ b/chrome/installer/util/google_update_settings.cc @@ -143,3 +143,28 @@ bool GoogleUpdateSettings::ClearReferral() { return ClearGoogleUpdateStrKey(google_update::kRegReferralField); } +bool GoogleUpdateSettings::GetChromeChannel(std::wstring* channel) { + std::wstring update_branch; + if (!ReadGoogleUpdateStrKey(google_update::kRegApField, &update_branch)) { + *channel = L"unknown"; + return false; + } + + // Map to something pithy for human consumption. There are no rules as to + // what the ap string can contain, but generally it will contain a number + // followed by a dash followed by the branch name (and then some random + // suffix). We fall back on empty string in case we fail to parse. + // Only ever return "", "unknown", "dev" or "beta". + if (update_branch.find(L"-beta") != std::wstring::npos) + *channel = L"beta"; + else if (update_branch.find(L"-dev") != std::wstring::npos) + *channel = L"dev"; + else if (update_branch.empty()) + *channel = L""; + else + *channel = L"unknown"; + + return true; +} + + diff --git a/chrome/installer/util/google_update_settings.h b/chrome/installer/util/google_update_settings.h index 06adb44..3edfedf 100644 --- a/chrome/installer/util/google_update_settings.h +++ b/chrome/installer/util/google_update_settings.h @@ -78,6 +78,11 @@ class GoogleUpdateSettings { // true if this operation succeeded. static bool ClearReferral(); + // Return a human readable modifier for the version string, e.g. + // the channel (dev, beta, stable). Returns true if this operation succeeded, + // on success, channel contains one of "", "unknown", "dev" or "beta". + static bool GetChromeChannel(std::wstring* channel); + private: DISALLOW_IMPLICIT_CONSTRUCTORS(GoogleUpdateSettings); }; diff --git a/chrome/installer/util/google_update_settings_unittest.cc b/chrome/installer/util/google_update_settings_unittest.cc new file mode 100644 index 0000000..bb2e46c --- /dev/null +++ b/chrome/installer/util/google_update_settings_unittest.cc @@ -0,0 +1,153 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <windows.h> + +#include "base/registry.h" +#include "chrome/installer/util/browser_distribution.h" +#include "chrome/installer/util/google_update_settings.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +const wchar_t* kHKCUReplacement = + L"Software\\Google\\InstallUtilUnittest\\HKCU"; +const wchar_t* kHKLMReplacement = + L"Software\\Google\\InstallUtilUnittest\\HKLM"; + +// This test fixture redirects the HKLM and HKCU registry hives for +// the duration of the test to make it independent of the machine +// and user settings. +class GoogleUpdateSettingsTest: public testing::Test { + protected: + virtual void SetUp() { + // Wipe the keys we redirect to. + // This gives us a stable run, even in the presence of previous + // crashes or failures. + LSTATUS err = SHDeleteKey(HKEY_CURRENT_USER, kHKCUReplacement); + EXPECT_TRUE(err == ERROR_SUCCESS || err == ERROR_FILE_NOT_FOUND); + err = SHDeleteKey(HKEY_CURRENT_USER, kHKLMReplacement); + EXPECT_TRUE(err == ERROR_SUCCESS || err == ERROR_FILE_NOT_FOUND); + + // Create the keys we're redirecting HKCU and HKLM to. + ASSERT_TRUE(hkcu_.Create(HKEY_CURRENT_USER, kHKCUReplacement)); + ASSERT_TRUE(hklm_.Create(HKEY_CURRENT_USER, kHKLMReplacement)); + + // And do the switcharoo. + ASSERT_EQ(ERROR_SUCCESS, + ::RegOverridePredefKey(HKEY_CURRENT_USER, hkcu_.Handle())); + ASSERT_EQ(ERROR_SUCCESS, + ::RegOverridePredefKey(HKEY_LOCAL_MACHINE, hklm_.Handle())); + } + + virtual void TearDown() { + // Undo the redirection. + EXPECT_EQ(ERROR_SUCCESS, ::RegOverridePredefKey(HKEY_CURRENT_USER, NULL)); + EXPECT_EQ(ERROR_SUCCESS, ::RegOverridePredefKey(HKEY_LOCAL_MACHINE, NULL)); + + // Close our handles and delete the temp keys we redirected to. + hkcu_.Close(); + hklm_.Close(); + EXPECT_EQ(ERROR_SUCCESS, SHDeleteKey(HKEY_CURRENT_USER, kHKCUReplacement)); + EXPECT_EQ(ERROR_SUCCESS, SHDeleteKey(HKEY_CURRENT_USER, kHKLMReplacement)); + } + + enum SystemUserInstall { + SYSTEM_INSTALL, + USER_INSTALL, + }; + + void SetApField(SystemUserInstall is_system, const wchar_t* value) { + HKEY root = is_system == SYSTEM_INSTALL ? + HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + RegKey update_key; + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + std::wstring path = dist->GetStateKey(); + ASSERT_TRUE(update_key.Create(root, path.c_str(), KEY_WRITE)); + ASSERT_TRUE(update_key.WriteValue(L"ap", value)); + } + + // Tests setting the ap= value to various combinations of values with + // prefixes and suffixes, while asserting on the correct channel value. + // Note that any non-empty ap= value that doesn't match ".*-{dev|beta}.*" + // will return the "unknown" channel. + void TestCurrentChromeChannelWithVariousApValues(SystemUserInstall install) { + static struct Expectations { + const wchar_t* ap_value; + const wchar_t* channel; + } expectations[] = { + { L"dev", L"unknown" }, + { L"-dev", L"dev" }, + { L"-developer", L"dev" }, + { L"beta", L"unknown" }, + { L"-beta", L"beta" }, + { L"-betamax", L"beta" }, + }; + bool is_system = install == SYSTEM_INSTALL; + const wchar_t* prefixes[] = { + L"", + L"prefix", + L"prefix-with-dash", + }; + const wchar_t* suffixes[] = { + L"", + L"suffix", + L"suffix-with-dash", + }; + + for (size_t i = 0; i < arraysize(prefixes); ++i) { + for (size_t j = 0; j < arraysize(expectations); ++j) { + for (size_t k = 0; k < arraysize(suffixes); ++k) { + std::wstring ap = prefixes[i]; + ap += expectations[j].ap_value; + ap += suffixes[k]; + const wchar_t* channel = expectations[j].channel; + + SetApField(install, ap.c_str()); + std::wstring ret_channel; + + EXPECT_TRUE(GoogleUpdateSettings::GetChromeChannel(&ret_channel)); + EXPECT_STREQ(channel, ret_channel.c_str()) + << "Expecting channel \"" << channel + << "\" for ap=\"" << ap << "\""; + } + } + } + } + + RegKey hkcu_; + RegKey hklm_; +}; + +} // namespace + +// Verify that we return failure on no registration. +TEST_F(GoogleUpdateSettingsTest, CurrentChromeChannelAbsent) { + std::wstring channel; + EXPECT_FALSE(GoogleUpdateSettings::GetChromeChannel(&channel)); + EXPECT_STREQ(L"unknown", channel.c_str()); +} + +// Test an empty Ap key for system and user. +TEST_F(GoogleUpdateSettingsTest, CurrentChromeChannelEmptySystem) { + SetApField(SYSTEM_INSTALL, L""); + std::wstring channel; + EXPECT_TRUE(GoogleUpdateSettings::GetChromeChannel(&channel)); + EXPECT_STREQ(L"", channel.c_str()); +} + +TEST_F(GoogleUpdateSettingsTest, CurrentChromeChannelEmptyUser) { + SetApField(USER_INSTALL, L""); + std::wstring channel; + EXPECT_TRUE(GoogleUpdateSettings::GetChromeChannel(&channel)); + EXPECT_STREQ(L"", channel.c_str()); +} + +TEST_F(GoogleUpdateSettingsTest, CurrentChromeChannelVariousApValuesSystem) { + TestCurrentChromeChannelWithVariousApValues(SYSTEM_INSTALL); +} + +TEST_F(GoogleUpdateSettingsTest, CurrentChromeChannelVariousApValuesUser) { + TestCurrentChromeChannelWithVariousApValues(USER_INSTALL); +} |