blob: f5c1a32372f74df87967a25353f9e203a93bcf5b [file] [log] [blame]
// Copyright 2019 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 "cobalt/updater/crash_reporter.h"
#include <map>
#include <memory>
#include <vector>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/stl_util.h"
#include "base/strings/strcat.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "cobalt/updater/updater_constants.h"
// #include "cobalt/updater/updater_version.h"
#include "cobalt/updater/utils.h"
// #include "third_party/crashpad/crashpad/client/crashpad_client.h"
// #include "third_party/crashpad/crashpad/handler/handler_main.h"
namespace {
// True if the current process is connected to a crash handler process.
bool g_is_connected_to_crash_handler = false;
crashpad::CrashpadClient* GetCrashpadClient() {
static auto* crashpad_client = new crashpad::CrashpadClient();
return crashpad_client;
}
void RemoveSwitchIfExisting(const char* switch_to_remove,
std::vector<base::CommandLine::StringType>* argv) {
const std::string pattern = base::StrCat({"--", switch_to_remove});
auto matches_switch =
[&pattern](const base::CommandLine::StringType& argument) -> bool {
#if defined(OS_WIN)
return base::StartsWith(argument, base::UTF8ToUTF16(pattern),
base::CompareCase::SENSITIVE);
#else
return base::StartsWith(argument, pattern, base::CompareCase::SENSITIVE);
#endif // OS_WIN
};
base::EraseIf(*argv, matches_switch);
}
} // namespace
namespace cobalt {
namespace updater {
void StartCrashReporter(const std::string& version) {
static bool started = false;
DCHECK(!started);
started = true;
base::FilePath handler_path;
base::PathService::Get(base::FILE_EXE, &handler_path);
base::FilePath database_path;
if (!CreateProductDirectory(&database_path)) {
LOG(DFATAL) << "Failed to get the database path.";
return;
}
std::map<std::string, std::string> annotations; // Crash keys.
annotations["ver"] = version;
annotations["prod"] = PRODUCT_FULLNAME_STRING;
std::vector<std::string> arguments;
arguments.push_back(base::StrCat({"--", kCrashHandlerSwitch}));
crashpad::CrashpadClient* client = GetCrashpadClient();
if (!client->StartHandler(handler_path, database_path,
/*metrics_dir=*/base::FilePath(),
kCrashStagingUploadURL, annotations, arguments,
/*restartable=*/true,
/*asynchronous_start=*/false)) {
LOG(DFATAL) << "Failed to start handler.";
return;
}
g_is_connected_to_crash_handler = true;
VLOG(1) << "Crash handler launched and ready.";
}
int CrashReporterMain() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
DCHECK(command_line->HasSwitch(kCrashHandlerSwitch));
// Disable rate-limiting until this is fixed:
// https://bugs.chromium.org/p/crashpad/issues/detail?id=23
command_line->AppendSwitch(kNoRateLimitSwitch);
std::vector<base::CommandLine::StringType> 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.
RemoveSwitchIfExisting(kCrashHandlerSwitch, &argv);
// |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) {
#if defined(OS_WIN)
storage.push_back(base::UTF16ToUTF8(argv[i]));
#else
storage.push_back(argv[i]);
#endif
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);
}
#if defined(OS_WIN)
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;
return;
}
g_is_connected_to_crash_handler = true;
VLOG(1) << "Crash handler is ready.";
}
#endif // OS_WIN
} // namespace updater
} // namespace cobalt