Commit e00760c6 authored by Joe Mason's avatar Joe Mason Committed by Commit Bot

Add chrome_cleaner/crash dir

Also add crash and logging deps to test_main.cc

R=proberge

Bug: 830892
Change-Id: I1fae62bfcff0beded07a1827cab1d58dab79f3ac
Reviewed-on: https://chromium-review.googlesource.com/1163635
Commit-Queue: Joe Mason <joenotcharles@chromium.org>
Reviewed-by: default avatarproberge <proberge@chromium.org>
Reviewed-by: default avatarMark Mentovai <mark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#581388}
parent 9c02c1b0
...@@ -11,11 +11,22 @@ test("chrome_cleaner_unittests") { ...@@ -11,11 +11,22 @@ test("chrome_cleaner_unittests") {
] ]
deps = [ deps = [
# Dependencies of the test harness.
":other_executable_definitions", ":other_executable_definitions",
"//base",
"//base/test:test_support",
"//chrome/chrome_cleaner/crash:crashpad_lib",
"//chrome/chrome_cleaner/engines:resources", "//chrome/chrome_cleaner/engines:resources",
"//chrome/chrome_cleaner/logging:common",
"//chrome/chrome_cleaner/os:cleaner_os",
"//chrome/chrome_cleaner/os:common_os",
"//chrome/chrome_cleaner/os:common_os", "//chrome/chrome_cleaner/os:common_os",
"//chrome/chrome_cleaner/pup_data:pup_data_base", "//chrome/chrome_cleaner/pup_data:pup_data_base",
"//chrome/chrome_cleaner/settings:settings_types",
"//chrome/chrome_cleaner/test:test_pup_data", "//chrome/chrome_cleaner/test:test_pup_data",
"//chrome/chrome_cleaner/test:test_util",
"//sandbox/win:sandbox",
"//testing/gtest",
# Tests from sub-directories. # Tests from sub-directories.
"//chrome/chrome_cleaner/http:unittest_sources", "//chrome/chrome_cleaner/http:unittest_sources",
...@@ -25,15 +36,6 @@ test("chrome_cleaner_unittests") { ...@@ -25,15 +36,6 @@ test("chrome_cleaner_unittests") {
"//chrome/chrome_cleaner/settings:unittest_sources", "//chrome/chrome_cleaner/settings:unittest_sources",
"//chrome/chrome_cleaner/strings:unittest_sources", "//chrome/chrome_cleaner/strings:unittest_sources",
"//chrome/chrome_cleaner/test:unittest_sources", "//chrome/chrome_cleaner/test:unittest_sources",
# Dependencies of the test harness.
"//base",
"//base/test:test_support",
"//chrome/chrome_cleaner/os:cleaner_os",
"//chrome/chrome_cleaner/os:common_os",
"//chrome/chrome_cleaner/test:test_util",
"//sandbox/win:sandbox",
"//testing/gtest",
] ]
} }
......
# Copyright 2018 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.
source_set("crash_keys") {
sources = [
"crash_keys.cc",
"crash_keys.h",
]
deps = [
"//base:base",
"//third_party/crashpad/crashpad/client:client",
]
}
source_set("crashpad_lib") {
sources = [
"crash_client.h",
"crash_reporter.h",
"crashpad_crash_client.cc",
"crashpad_crash_client.h",
"crashpad_crash_reporter.cc",
]
deps = [
":crash_keys",
"//base:base",
"//chrome/chrome_cleaner/chrome_utils:chrome_util_lib",
"//chrome/chrome_cleaner/constants:common_strings",
"//chrome/chrome_cleaner/logging:common",
"//chrome/chrome_cleaner/os:common_os",
"//chrome/chrome_cleaner/settings:settings",
"//chrome/chrome_cleaner/settings:settings_types",
"//third_party/crashpad/crashpad/client",
"//third_party/crashpad/crashpad/handler",
]
}
include_rules = [
"+third_party/crashpad",
]
// Copyright 2018 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.
#ifndef CHROME_CHROME_CLEANER_CRASH_CRASH_CLIENT_H_
#define CHROME_CHROME_CLEANER_CRASH_CRASH_CLIENT_H_
#include <map>
#include "base/macros.h"
#include "base/strings/string16.h"
#include "base/synchronization/lock.h"
#include "chrome/chrome_cleaner/settings/settings_types.h"
namespace chrome_cleaner {
// This class manages interaction with the crash reporter.
class CrashClient {
public:
enum class Mode { REPORTER, CLEANER, MODE_COUNT };
static CrashClient* GetInstance();
// Set |client_id| to the current guid associated with crashes. |client_id|
// may be empty if no guid is associated.
static void GetClientId(base::string16* client_id);
// Returns whether upload of crashes is enabled or not.
static bool IsUploadEnabled();
CrashClient() = default;
virtual ~CrashClient() = default;
// Initializes collection and upload of crash reports. This will only be done
// if the user has agreed to crash dump reporting.
//
// Crash reporting has to be initialized as early as possible (e.g., the first
// thing in main()) to catch crashes occurring during process startup. Crashes
// which occur during the global static construction phase will not be caught
// and reported. This should not be a problem as static non-POD objects are
// not allowed by the style guide and exceptions to this rule are rare.
//
// |mode| controls a custom info entry present in the generated dumps to allow
// distinguishing between cleaner and reporter crashes on the backend.
//
// |process_type| identifies the type of process that reported the crash.
virtual bool InitializeCrashReporting(Mode mode,
SandboxType process_type) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(CrashClient);
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_CRASH_CRASH_CLIENT_H_
// Copyright 2018 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 "chrome/chrome_cleaner/crash/crash_keys.h"
#include "base/command_line.h"
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "third_party/crashpad/crashpad/client/crashpad_info.h"
#include "third_party/crashpad/crashpad/client/simple_string_dictionary.h"
namespace chrome_cleaner {
namespace {
base::Lock& GetCrashKeyLock() {
static base::Lock* crash_key_lock = new base::Lock();
return *crash_key_lock;
}
crashpad::SimpleStringDictionary* GetCrashKeys() {
// TODO(crbug.com/870715): Use the new crash key API from
// https://cs.chromium.org/chromium/src/components/crash/core/common/crash_key.h
static crashpad::SimpleStringDictionary* crash_keys =
new crashpad::SimpleStringDictionary();
return crash_keys;
}
void SetCrashKeyInDictionary(crashpad::SimpleStringDictionary* crash_keys,
base::StringPiece key,
base::StringPiece value) {
if (crash_keys->GetValueForKey(key))
LOG(WARNING) << "Crash key \"" << key << "\" being overwritten";
else
DCHECK_LT(crash_keys->GetCount(), crash_keys->num_entries);
crash_keys->SetKeyValue(key, value);
}
} // namespace
void SetCrashKey(base::StringPiece key, base::StringPiece value) {
base::AutoLock lock(GetCrashKeyLock());
SetCrashKeyInDictionary(GetCrashKeys(), key, value);
}
void SetCrashKeysFromCommandLine() {
// Based on Chrome's crash_keys::SetSwitchesFromCommandLine.
static constexpr size_t kMaxArgs = 16;
static constexpr char kKeyFormat[] = "CommandLineArg-%02" PRIuS;
static constexpr char kSizeKey[] = "CommandLineSize";
base::AutoLock lock(GetCrashKeyLock());
crashpad::SimpleStringDictionary* crash_keys = GetCrashKeys();
// Make sure this was only called once.
DCHECK(!crash_keys->GetValueForKey(kSizeKey));
const base::CommandLine::StringVector& argv =
base::CommandLine::ForCurrentProcess()->argv();
// Record the true number of arguments in case there are too many to store.
SetCrashKeyInDictionary(crash_keys, kSizeKey,
base::NumberToString(argv.size()));
// Go through the argv, including the exec path in argv[0]. Stop if there are
// too many arguments to hold in crash keys.
for (size_t key_i = 0; key_i < argv.size() && key_i < kMaxArgs; ++key_i) {
SetCrashKeyInDictionary(crash_keys, base::StringPrintf(kKeyFormat, key_i),
base::WideToUTF8(argv[key_i]));
}
}
void UseCrashKeysToAnnotate(crashpad::CrashpadInfo* crashpad_info) {
crashpad_info->set_simple_annotations(GetCrashKeys());
}
} // namespace chrome_cleaner
// Copyright 2018 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.
#ifndef CHROME_CHROME_CLEANER_CRASH_CRASH_KEYS_H_
#define CHROME_CHROME_CLEANER_CRASH_CRASH_KEYS_H_
#include <string>
#include "base/strings/string_piece_forward.h"
namespace crashpad {
// Forward-declare CrashpadInfo instead of including crashpad_info.h because
// the header pulls in extra dependencies that would need to be inherited by
// every file using crash_keys.h.
struct CrashpadInfo;
} // namespace crashpad
namespace chrome_cleaner {
// Sets the crash key |key| to the specified |value|. The key will be
// overwritten if it was already present. This is thread-safe.
void SetCrashKey(base::StringPiece key, base::StringPiece value);
// Records the current process's command-line in a set of crash keys. This is
// thread-safe.
void SetCrashKeysFromCommandLine();
// Sets |crashpad_info| to use this process's crash key dictionary for
// annotations. Note the annotations are not used in a thread-safe way by
// Crashpad, but that should be acceptable because they are only used while
// dumping a crash.
void UseCrashKeysToAnnotate(crashpad::CrashpadInfo* crashpad_info);
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_CRASH_CRASH_KEYS_H_
// Copyright 2018 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.
#ifndef CHROME_CHROME_CLEANER_CRASH_CRASH_REPORTER_H_
#define CHROME_CHROME_CLEANER_CRASH_CRASH_REPORTER_H_
#include <string>
#include "base/strings/string16.h"
// Starts a new instance of this executable running as the crash reporter
// process.
void StartCrashReporter(const std::string version);
// Runs the crash reporter message loop within the current process. On return,
// the current process should exit.
int CrashReporterMain();
// Returns the name of the IPC pipe that is used to communicate with the crash
// reporter process, or an empty string if the current process is not connected
// to a crash reporter process.
base::string16 GetCrashReporterIPCPipeName();
// Uses the crash reporter with the specified |ipc_pipe_name|, instead of
// starting a new crash reporter process.
void UseCrashReporter(const base::string16& ipc_pipe_name);
#endif // CHROME_CHROME_CLEANER_CRASH_CRASH_REPORTER_H_
// Copyright 2018 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 "chrome/chrome_cleaner/crash/crashpad_crash_client.h"
#include <process.h>
#include <psapi.h>
#include <stdio.h>
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "base/win/wrapped_window_proc.h"
#include "chrome/chrome_cleaner/chrome_utils/chrome_util.h"
#include "chrome/chrome_cleaner/constants/chrome_cleaner_switches.h"
#include "chrome/chrome_cleaner/crash/crash_keys.h"
#include "chrome/chrome_cleaner/os/disk_util.h"
#include "chrome/chrome_cleaner/os/file_path_sanitization.h"
#include "chrome/chrome_cleaner/settings/settings.h"
#include "chrome/chrome_cleaner/settings/settings_types.h"
#include "third_party/crashpad/crashpad/client/crash_report_database.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
#include "third_party/crashpad/crashpad/client/crashpad_info.h"
#include "third_party/crashpad/crashpad/client/settings.h"
namespace chrome_cleaner {
namespace {
base::LazyInstance<base::Lock>::Leaky record_memory_usage_and_crash_lock =
LAZY_INSTANCE_INITIALIZER;
// NOTE: the following functions will be executed when the application is likely
// in the process of crashing. That could be because there is no memory left, so
// we must avoid allocating memory when they are run.
// Helper function to format a DWORD value to a string. Does not allocate
// memory. The returned value is valid until the next call to this function.
// This function is not thread-safe.
const char* ConvertDwordToString(DWORD value) {
static_assert(sizeof(value) == 4, "DWORD is not 32 bits?");
// The maximum value represented by a DWORD value is 4294967295, which is 10
// characters long. Leave space for a NULL terminator character.
static char buffer[11] = {};
_snprintf(buffer, base::size(buffer), "%u", value);
buffer[base::size(buffer) - 1] = '\0';
return buffer;
}
// Helper function to format a SIZE_T value to a string. Does not allocate
// memory. The returned value is valid until the next call to this function.
// This function is not thread-safe.
const char* ConvertSizeTToString(SIZE_T value) {
static_assert(sizeof(value) <= 8, "SIZE_T is more than 64 bits?");
// The maximum value represented by a 64-bit SIZE_T value is
// 18446744073709551616, which is 20 characters long. Leave space for a NULL
// terminator character.
static char buffer[21] = {};
_snprintf(buffer, base::size(buffer), "%Iu", value);
buffer[base::size(buffer) - 1] = '\0';
return buffer;
}
// Exception handler. Adds crash keys with memory usage information, then
// hands control over to the Crashpad crash handler.
LONG WINAPI RecordMemoryUsageAndCrash(EXCEPTION_POINTERS* exception_pointers) {
static PROCESS_MEMORY_COUNTERS counters = {};
{
base::AutoLock auto_lock(record_memory_usage_and_crash_lock.Get());
// Call GetProcessMemoryInfo directly instead of using base::ProcessMetrics,
// to keep stack allocations to a minimum.
if (GetProcessMemoryInfo(GetCurrentProcess(), &counters,
sizeof(counters))) {
// Crashpad pre-allocates space for 64 crash keys, so setting these here
// does not allocate memory. Both key and value can be up to 256
// characters long.
SetCrashKey("PageFaultCount",
ConvertDwordToString(counters.PageFaultCount));
SetCrashKey("PeakWorkingSetSize",
ConvertSizeTToString(counters.PeakWorkingSetSize));
SetCrashKey("WorkingSetSize",
ConvertSizeTToString(counters.WorkingSetSize));
SetCrashKey("QuotaPeakPagedPoolUsage",
ConvertSizeTToString(counters.QuotaPeakPagedPoolUsage));
SetCrashKey("QuotaPagedPoolUsage",
ConvertSizeTToString(counters.QuotaPagedPoolUsage));
SetCrashKey("QuotaPeakNonPagedPoolUsage",
ConvertSizeTToString(counters.QuotaPeakNonPagedPoolUsage));
SetCrashKey("QuotaNonPagedPoolUsage",
ConvertSizeTToString(counters.QuotaNonPagedPoolUsage));
SetCrashKey("PagefileUsage",
ConvertSizeTToString(counters.PagefileUsage));
SetCrashKey("PeakPagefileUsage",
ConvertSizeTToString(counters.PeakPagefileUsage));
}
}
crashpad::CrashpadClient::DumpAndCrash(exception_pointers);
return EXCEPTION_CONTINUE_SEARCH;
}
// Wraps the DumpAndCrash function info a function that has the right return
// type to be passed to base::win::SetWinProcExceptionFilter.
int __cdecl HandleWinProcException(EXCEPTION_POINTERS* info) {
return RecordMemoryUsageAndCrash(info);
}
} // namespace
// static
CrashClient* CrashClient::GetInstance() {
return CrashpadCrashClient::GetInstance();
}
// static
void CrashClient::GetClientId(base::string16* client_id) {
CrashpadCrashClient::GetClientId(client_id);
}
// static
bool CrashClient::IsUploadEnabled() {
return CrashpadCrashClient::IsUploadEnabled();
}
CrashpadCrashClient::~CrashpadCrashClient() = default;
bool CrashpadCrashClient::InitializeDatabaseOnly() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::FilePath database_path;
if (!chrome_cleaner::GetAppDataProductDirectory(&database_path)) {
LOG(ERROR) << "Failed to get AppData product directory";
return false;
}
database_.reset(
crashpad::CrashReportDatabase::Initialize(database_path).release());
if (!database_) {
LOG(ERROR) << "Failed to initialize Crashpad database.";
return false;
}
return true;
}
bool CrashpadCrashClient::InitializeCrashReporting(Mode mode,
SandboxType process_type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
static bool initialized = false;
DCHECK(!initialized);
initialized = true;
UseCrashKeysToAnnotate(crashpad::CrashpadInfo::GetCrashpadInfo());
SetCrashKey("pid", base::NumberToString(_getpid()));
static_assert(static_cast<int>(Mode::MODE_COUNT) == 2,
"Update the annotation below if a new Mode is added");
constexpr char kModeString[] = "mode";
switch (mode) {
case Mode::REPORTER:
SetCrashKey(kModeString, "reporter");
break;
case Mode::CLEANER:
SetCrashKey(kModeString, "cleaner");
break;
default:
NOTREACHED();
}
constexpr char kProcessType[] = "process_type";
switch (process_type) {
case SandboxType::kNonSandboxed:
SetCrashKey(kProcessType, "broker");
break;
case SandboxType::kEset:
SetCrashKey(kProcessType, "eset");
break;
default:
NOTREACHED();
}
SetCrashKey("uma", Settings::GetInstance()->metrics_enabled() ? "1" : "0");
SetCrashKey("testing",
base::CommandLine::ForCurrentProcess()->HasSwitch(kTestingSwitch)
? "1"
: "0");
base::string16 chrome_version;
bool chrome_system_install;
RetrieveChromeVersionAndInstalledDomain(&chrome_version,
&chrome_system_install);
SetCrashKey("ChromeVersion", base::UTF16ToUTF8(chrome_version));
SetCrashKey("ChromeSystemInstall", chrome_system_install ? "1" : "0");
SetCrashKeysFromCommandLine();
static_assert(static_cast<int>(Mode::MODE_COUNT) == 2,
"Update the condition below if a new Mode is added");
if (mode == Mode::CLEANER)
SetCrashKey("CleanupId", Settings::GetInstance()->cleanup_id());
const std::string engine_version = Settings::GetInstance()->engine_version();
if (!engine_version.empty())
SetCrashKey("EngineVersion", engine_version);
if (!InitializeDatabaseOnly())
return false;
// Replace Crashpad's exception filter with our own filter, which records
// memory usage at the time of the crash, then tells Crashpad to dump and
// crash.
SetUnhandledExceptionFilter(&RecordMemoryUsageAndCrash);
// Catch exceptions thrown from a window procedure.
base::win::WinProcExceptionFilter exception_filter =
base::win::SetWinProcExceptionFilter(&HandleWinProcException);
LOG_IF(DFATAL, exception_filter) << "Exception filter already present";
// Log completed crash reports in the logs that will be uploaded to Safe
// Browsing.
std::vector<crashpad::CrashReportDatabase::Report> completed_reports;
const crashpad::CrashReportDatabase::OperationStatus status_completed =
database_->GetCompletedReports(&completed_reports);
if (status_completed == crashpad::CrashReportDatabase::kNoError) {
LOG(INFO) << "Found " << completed_reports.size()
<< " completed crash reports";
for (const auto& report : completed_reports) {
LOG(INFO) << "Crash since last run: ID \"" << report.id
<< "\", created at " << report.creation_time << ", "
<< report.upload_attempts << " upload attempts, file path \""
<< SanitizePath(report.file_path) << "\", unique ID \""
<< report.uuid.ToString()
<< "\"; uploaded: " << (report.uploaded ? "yes" : "no");
}
} else {
LOG(ERROR) << "Failed to fetch completed crash reports: "
<< status_completed;
}
std::vector<crashpad::CrashReportDatabase::Report> pending_reports;
const crashpad::CrashReportDatabase::OperationStatus status_pending =
database_->GetPendingReports(&pending_reports);
if (status_pending == crashpad::CrashReportDatabase::kNoError) {
LOG(INFO) << "Found " << pending_reports.size() << " pending crash reports";
for (const auto& report : pending_reports) {
LOG(INFO) << "Crash since last run: (pending), created at "
<< report.creation_time << ", " << report.upload_attempts
<< " upload attempts, file path \""
<< SanitizePath(report.file_path) << "\", unique ID \""
<< report.uuid.ToString() << "\"";
}
} else {
LOG(ERROR) << "Failed to fetch pending crash reports: " << status_pending;
}
DeleteStaleReports();
// Enable or disable crash reporting based on settings.
crashpad::Settings* crashpad_settings = database_->GetSettings();
DCHECK(crashpad_settings);
crashpad_settings->SetUploadsEnabled(
Settings::GetInstance()->allow_crash_report_upload());
return true;
}
void CrashpadCrashClient::DeleteStaleReports() {
const int kMaxReportCount = 3;
std::vector<crashpad::CrashReportDatabase::Report> completed_reports;
if (database_->GetCompletedReports(&completed_reports) !=
crashpad::CrashReportDatabase::kNoError) {
return;
}
std::vector<crashpad::CrashReportDatabase::Report> not_uploaded_reports;
for (const auto& report : completed_reports) {
if (report.uploaded)
database_->DeleteReport(report.uuid);
else
not_uploaded_reports.push_back(report);
}
// Sort reports from newest to oldest, then delete all but the newest
// kMaxReportCount reports.
std::sort(not_uploaded_reports.begin(), not_uploaded_reports.end(),
[](const auto& report1, const auto& report2) {
return report1.creation_time > report2.creation_time;
});
for (size_t i = kMaxReportCount; i < not_uploaded_reports.size(); ++i)
database_->DeleteReport(not_uploaded_reports[i].uuid);
}
// static
CrashpadCrashClient* CrashpadCrashClient::GetInstance() {
return base::Singleton<CrashpadCrashClient, base::LeakySingletonTraits<
CrashpadCrashClient>>::get();
}
// static
void CrashpadCrashClient::GetClientId(base::string16* client_id) {
DCHECK(client_id);
DCHECK_CALLED_ON_VALID_SEQUENCE(GetInstance()->sequence_checker_);
DCHECK(GetInstance()->database_) << "Crash reporting not initialized";
crashpad::Settings* settings = GetInstance()->database_->GetSettings();
DCHECK(settings);
crashpad::UUID uuid;
if (!settings->GetClientID(&uuid)) {
LOG(ERROR) << "Unable to retrieve client ID from Crashpad database";
*client_id = base::string16();
return;
}
std::string uuid_string = uuid.ToString();
base::ReplaceSubstringsAfterOffset(&uuid_string, 0, "-", "");
*client_id = base::UTF8ToWide(uuid_string);
}
// static
bool CrashpadCrashClient::IsUploadEnabled() {
DCHECK_CALLED_ON_VALID_SEQUENCE(GetInstance()->sequence_checker_);
DCHECK(GetInstance()->database_) << "Crash reporting not initialized";
crashpad::Settings* settings = GetInstance()->database_->GetSettings();
DCHECK(settings);
bool upload_enabled = false;
if (settings->GetUploadsEnabled(&upload_enabled)) {
return upload_enabled;
} else {
LOG(ERROR) << "Unable to verify if crash uploads are enabled or not";
return false;
}
}
CrashpadCrashClient::CrashpadCrashClient() = default;
} // namespace chrome_cleaner
// Copyright 2018 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.
#ifndef CHROME_CHROME_CLEANER_CRASH_CRASHPAD_CRASH_CLIENT_H_
#define CHROME_CHROME_CLEANER_CRASH_CRASHPAD_CRASH_CLIENT_H_
#include <map>
#include <memory>
#include "base/macros.h"
#include "base/memory/singleton.h"
#include "base/sequence_checker.h"
#include "base/strings/string16.h"
#include "chrome/chrome_cleaner/crash/crash_client.h"
#include "chrome/chrome_cleaner/settings/settings_types.h"
#include "third_party/crashpad/crashpad/client/crash_report_database.h"
namespace base {
template <typename T>
struct DefaultSingletonTraits;
} // namespace base
namespace chrome_cleaner {
// This class manages interaction with the Crashpad reporter.
class CrashpadCrashClient : public CrashClient {
public:
~CrashpadCrashClient() override;
// Initializes the crash database only. Used in the crash reporter, which
// cannot connect to itself to upload its own crashes.
bool InitializeDatabaseOnly();
crashpad::CrashReportDatabase* database() { return database_.get(); }
// CrashClient:
bool InitializeCrashReporting(Mode mode, SandboxType process_type) override;
static CrashpadCrashClient* GetInstance();
// Sets |client_id| to the current guid associated with crashes. |client_id|
// may be empty if no guid is associated.
static void GetClientId(base::string16* client_id);
// Returns whether upload of crashes is enabled or not.
static bool IsUploadEnabled();
private:
friend class base::Singleton<CrashpadCrashClient>;
friend struct base::DefaultSingletonTraits<CrashpadCrashClient>;
CrashpadCrashClient();
// Removes already uploaded reports and limits the number of reports that
// stay on disk.
void DeleteStaleReports();
SEQUENCE_CHECKER(sequence_checker_);
std::unique_ptr<crashpad::CrashReportDatabase> database_;
DISALLOW_COPY_AND_ASSIGN(CrashpadCrashClient);
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_CRASH_CRASHPAD_CRASH_CLIENT_H_
// Copyright 2018 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 <algorithm>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/chrome_cleaner/constants/chrome_cleaner_switches.h"
#include "chrome/chrome_cleaner/crash/crashpad_crash_client.h"
#include "chrome/chrome_cleaner/logging/scoped_logging.h"
#include "chrome/chrome_cleaner/os/disk_util.h"
#include "chrome/chrome_cleaner/os/pre_fetched_paths.h"
#include "chrome/chrome_cleaner/os/system_util.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
#include "third_party/crashpad/crashpad/handler/handler_main.h"
namespace {
// The URL where crash reports are uploaded.
const char kReportUploadURL[] = "https://clients2.google.com/cr/report";
// Whether the current process is connected to a crash handler process.
bool g_is_connected_to_crash_handler = false;
} // namespace
crashpad::CrashpadClient* GetCrashpadClient() {
static auto* crashpad_client = new crashpad::CrashpadClient();
return crashpad_client;
}
void AppendSwitchIfExisting(const base::CommandLine& command_line,
const std::string& switch_name,
std::vector<std::string>* arguments) {
if (command_line.HasSwitch(switch_name)) {
// String format: --%s=%s
arguments->push_back(
base::StrCat({"--", switch_name, "=",
command_line.GetSwitchValueASCII(switch_name)}));
}
}
void StartCrashReporter(const std::string version) {
static bool started = false;
DCHECK(!started);
started = true;
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
base::FilePath handler_path =
chrome_cleaner::PreFetchedPaths::GetInstance()->GetExecutablePath();
base::FilePath database_path;
if (!chrome_cleaner::GetAppDataProductDirectory(&database_path)) {
LOG(DFATAL) << "Failed to get AppData product directory";
return;
}
std::map<std::string, std::string> annotations; // Crash keys.
annotations["ver"] = version;
annotations["prod"] = "ChromeFoil";
annotations["plat"] = "Win32";
std::vector<std::string> arguments;
arguments.push_back(
base::StrCat({"--", chrome_cleaner::kCrashHandlerSwitch}));
AppendSwitchIfExisting(*command_line, chrome_cleaner::kTestLoggingURLSwitch,
&arguments);
AppendSwitchIfExisting(*command_line, chrome_cleaner::kCleanupIdSwitch,
&arguments);
crashpad::CrashpadClient* client = GetCrashpadClient();
if (!client->StartHandler(handler_path, database_path,
/*metrics_dir=*/base::FilePath(), kReportUploadURL,
annotations, arguments, /*restartable=*/true,
/*asynchronous_start=*/false)) {
LOG(DFATAL) << "Failed to start handler.";
} else {
g_is_connected_to_crash_handler = true;
LOG(INFO) << "Crash handler launched and ready.";
}
}
void RemoveSwitchIfExisting(const char* const switch_to_remove,
std::vector<base::string16>* argv) {
const base::string16 pattern =
base::StrCat({L"--", base::UTF8ToWide(switch_to_remove)});
auto matches_switch = [&pattern](const base::string16& argument) -> bool {
return base::StartsWith(argument, pattern, base::CompareCase::SENSITIVE);
};
argv->erase(std::remove_if(argv->begin(), argv->end(), matches_switch),
argv->end());
}
int CrashReporterMain() {
chrome_cleaner::ScopedLogging scoped_logging(L"-crashpad");
// Make sure not to take too much of the machines's resources.
chrome_cleaner::SetBackgroundMode();
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
// This function should only run if --crash-handler switch is present.
DCHECK(command_line->HasSwitch(chrome_cleaner::kCrashHandlerSwitch));
std::vector<base::string16> argv = command_line->argv();
// Because of https://bugs.chromium.org/p/crashpad/issues/detail?id=82,
// Crashpad fails on the presence of flags it doesn't handle. Until that bug
// is fixed, we need to remove any custom flag passed by the cleaner to the
// Crashpad process.
RemoveSwitchIfExisting(chrome_cleaner::kCrashHandlerSwitch, &argv);
RemoveSwitchIfExisting(chrome_cleaner::kTestLoggingURLSwitch, &argv);
RemoveSwitchIfExisting(chrome_cleaner::kCleanupIdSwitch, &argv);
// Disable rate-limiting until this is fixed:
// https://bugs.chromium.org/p/crashpad/issues/detail?id=23
argv.push_back(L"--no-rate-limit");
// |storage| must be declared before |argv_as_utf8|, to ensure it outlives
// |argv_as_utf8|, which will hold pointers into |storage|.
std::vector<std::string> storage;
std::unique_ptr<char* []> argv_as_utf8(new char*[argv.size() + 1]);
storage.reserve(argv.size());
for (size_t i = 0; i < argv.size(); ++i) {
storage.push_back(base::UTF16ToUTF8(argv[i]));
argv_as_utf8[i] = &storage[i][0];
}
argv_as_utf8[argv.size()] = nullptr;
return crashpad::HandlerMain(static_cast<int>(argv.size()),
argv_as_utf8.get(),
/* user_stream_sources */ nullptr);
}
base::string16 GetCrashReporterIPCPipeName() {
return g_is_connected_to_crash_handler
? GetCrashpadClient()->GetHandlerIPCPipe()
: base::string16();
}
void UseCrashReporter(const base::string16& ipc_pipe_name) {
DCHECK(!ipc_pipe_name.empty());
crashpad::CrashpadClient* crashpad_client = GetCrashpadClient();
if (!crashpad_client->SetHandlerIPCPipe(ipc_pipe_name)) {
LOG(DFATAL) << "Failed to set handler IPC pipe name: " << ipc_pipe_name;
} else {
g_is_connected_to_crash_handler = true;
LOG(INFO) << "Crash handler launched and ready.";
}
}
...@@ -9,10 +9,13 @@ ...@@ -9,10 +9,13 @@
#include "base/test/launcher/unit_test_launcher.h" #include "base/test/launcher/unit_test_launcher.h"
#include "base/test/test_suite.h" #include "base/test/test_suite.h"
#include "base/win/scoped_com_initializer.h" #include "base/win/scoped_com_initializer.h"
#include "chrome/chrome_cleaner/crash/crash_client.h"
#include "chrome/chrome_cleaner/logging/scoped_logging.h"
#include "chrome/chrome_cleaner/os/rebooter.h" #include "chrome/chrome_cleaner/os/rebooter.h"
#include "chrome/chrome_cleaner/os/secure_dll_loading.h" #include "chrome/chrome_cleaner/os/secure_dll_loading.h"
#include "chrome/chrome_cleaner/os/system_util_cleaner.h" #include "chrome/chrome_cleaner/os/system_util_cleaner.h"
#include "chrome/chrome_cleaner/os/task_scheduler.h" #include "chrome/chrome_cleaner/os/task_scheduler.h"
#include "chrome/chrome_cleaner/settings/settings_types.h"
#include "chrome/chrome_cleaner/test/test_util.h" #include "chrome/chrome_cleaner/test/test_util.h"
#include "sandbox/win/src/sandbox_factory.h" #include "sandbox/win/src/sandbox_factory.h"
...@@ -24,6 +27,24 @@ bool IsSandboxedProcess() { ...@@ -24,6 +27,24 @@ bool IsSandboxedProcess() {
return is_sandboxed_process; return is_sandboxed_process;
} }
// base::TestSuite's Initialize method initializes logging differently than we
// do. This subclass ensures logging is properly initialized using ScopedLogging
// after base::TestSuite::Initialize has run.
class ChromeCleanerTestSuite : public base::TestSuite {
public:
// Inherit constructors.
using base::TestSuite::TestSuite;
protected:
void Initialize() override {
base::TestSuite::Initialize();
scoped_logging.reset(new chrome_cleaner::ScopedLogging(nullptr));
}
private:
std::unique_ptr<chrome_cleaner::ScopedLogging> scoped_logging;
};
} // namespace } // namespace
int main(int argc, char** argv) { int main(int argc, char** argv) {
...@@ -34,7 +55,7 @@ int main(int argc, char** argv) { ...@@ -34,7 +55,7 @@ int main(int argc, char** argv) {
// affect the behaviour of functionality that's tested. // affect the behaviour of functionality that's tested.
chrome_cleaner::EnableSecureDllLoading(); chrome_cleaner::EnableSecureDllLoading();
base::TestSuite test_suite(argc, argv); ChromeCleanerTestSuite test_suite(argc, argv);
if (!chrome_cleaner::SetupTestConfigs()) if (!chrome_cleaner::SetupTestConfigs())
return 1; return 1;
...@@ -60,6 +81,15 @@ int main(int argc, char** argv) { ...@@ -60,6 +81,15 @@ int main(int argc, char** argv) {
success = chrome_cleaner::TaskScheduler::Initialize(); success = chrome_cleaner::TaskScheduler::Initialize();
DCHECK(success) << "TaskScheduler::Initialize() failed."; DCHECK(success) << "TaskScheduler::Initialize() failed.";
// Crash reporting must be initialized only once, so it cannot be
// initialized by individual tests or fixtures. Also, since crashpad does
// not actually enable uploading of crash reports in non-official builds
// (unless forced to by the --enable-crash-reporting flag) we don't need to
// disable crash reporting.
chrome_cleaner::CrashClient::GetInstance()->InitializeCrashReporting(
chrome_cleaner::CrashClient::Mode::CLEANER,
chrome_cleaner::SandboxType::kNonSandboxed);
} }
// Some tests will fail if two tests try to launch test_process.exe // Some tests will fail if two tests try to launch test_process.exe
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment