blob: 9080cd5ca419ee0190f4533063505a09329f802c [file] [log] [blame]
// 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.";
}
}