crashpad/handler/main.cc
Mark Mentovai fc7d8b3a27 mac: Make crashpad_handler get its receive right from its client
Previously, crashpad_handler made its own receive right, and transferred
a corresponding send right to its client. There are two advantages to
making the receive right in the client:

 - It is possible to monitor the receive right for a port-destroyed
   notificaiton in the client, allowing the handler to be restarted if
   it dies.
 - For the future run-from-launchd mode (bug crashpad:25), the handler
   will obtain its receive right from the bootstrap server instead of
   making its own. Having the handler get its receive right from
   different sources allows more code to be shared than if it were to
   sometimes get a receive right and sometimes make a receive right and
   transfer a send right.

This includes a restructuring in crashpad_client_mac.cc that will make
it easier to give it an option to restart crashpad_handler if it dies.
The handler starting logic should all behave the same as before.

BUG=crashpad:68
R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/1409073013 .
2015-10-29 18:09:03 -04:00

266 lines
7.9 KiB
C++

// Copyright 2014 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <getopt.h>
#include <stdlib.h>
#include <map>
#include <string>
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "build/build_config.h"
#include "client/crash_report_database.h"
#include "client/crashpad_client.h"
#include "tools/tool_support.h"
#include "handler/crash_report_upload_thread.h"
#include "util/stdlib/map_insert.h"
#include "util/stdlib/string_number_conversion.h"
#include "util/string/split_string.h"
#include "util/synchronization/semaphore.h"
#if defined(OS_MACOSX)
#include <libgen.h>
#include "base/mac/scoped_mach_port.h"
#include "handler/mac/crash_report_exception_handler.h"
#include "handler/mac/exception_handler_server.h"
#include "util/mach/child_port_handshake.h"
#include "util/posix/close_stdio.h"
#elif defined(OS_WIN)
#include <windows.h>
#include "handler/win/crash_report_exception_handler.h"
#include "util/win/exception_handler_server.h"
#endif // OS_MACOSX
namespace crashpad {
namespace {
void Usage(const base::FilePath& me) {
fprintf(stderr,
"Usage: %" PRFilePath " [OPTION]...\n"
"Crashpad's exception handler server.\n"
"\n"
" --annotation=KEY=VALUE set a process annotation in each crash report\n"
" --database=PATH store the crash report database at PATH\n"
#if defined(OS_MACOSX)
" --handshake-fd=FD establish communication with the client over FD\n"
" --reset-own-crash-exception-port-to-system-default\n"
" reset the server's exception handler to default\n"
#elif defined(OS_WIN)
" --pipe-name=PIPE communicate with the client over PIPE\n"
#endif // OS_MACOSX
" --url=URL send crash reports to this Breakpad server URL,\n"
" only if uploads are enabled for the database\n"
" --help display this help and exit\n"
" --version output version information and exit\n",
me.value().c_str());
ToolSupport::UsageTail(me);
}
int HandlerMain(int argc, char* argv[]) {
const base::FilePath argv0(
ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));
const base::FilePath me(argv0.BaseName());
enum OptionFlags {
// Long options without short equivalents.
kOptionLastChar = 255,
kOptionAnnotation,
kOptionDatabase,
#if defined(OS_MACOSX)
kOptionHandshakeFD,
kOptionResetOwnCrashExceptionPortToSystemDefault,
#elif defined(OS_WIN)
kOptionPipeName,
#endif // OS_MACOSX
kOptionURL,
// Standard options.
kOptionHelp = -2,
kOptionVersion = -3,
};
struct {
std::map<std::string, std::string> annotations;
std::string url;
const char* database;
#if defined(OS_MACOSX)
int handshake_fd;
bool reset_own_crash_exception_port_to_system_default;
#elif defined(OS_WIN)
std::string pipe_name;
#endif
} options = {};
#if defined(OS_MACOSX)
options.handshake_fd = -1;
options.reset_own_crash_exception_port_to_system_default = false;
#endif
const option long_options[] = {
{"annotation", required_argument, nullptr, kOptionAnnotation},
{"database", required_argument, nullptr, kOptionDatabase},
#if defined(OS_MACOSX)
{"handshake-fd", required_argument, nullptr, kOptionHandshakeFD},
{"reset-own-crash-exception-port-to-system-default",
no_argument,
nullptr,
kOptionResetOwnCrashExceptionPortToSystemDefault},
#elif defined(OS_WIN)
{"pipe-name", required_argument, nullptr, kOptionPipeName},
#endif
{"url", required_argument, nullptr, kOptionURL},
{"help", no_argument, nullptr, kOptionHelp},
{"version", no_argument, nullptr, kOptionVersion},
{nullptr, 0, nullptr, 0},
};
int opt;
while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
switch (opt) {
case kOptionAnnotation: {
std::string key;
std::string value;
if (!SplitString(optarg, '=', &key, &value)) {
ToolSupport::UsageHint(me, "--annotation requires KEY=VALUE");
return EXIT_FAILURE;
}
std::string old_value;
if (!MapInsertOrReplace(&options.annotations, key, value, &old_value)) {
LOG(WARNING) << "duplicate key " << key << ", discarding value "
<< old_value;
}
break;
}
case kOptionDatabase: {
options.database = optarg;
break;
}
#if defined(OS_MACOSX)
case kOptionHandshakeFD: {
if (!StringToNumber(optarg, &options.handshake_fd) ||
options.handshake_fd < 0) {
ToolSupport::UsageHint(me,
"--handshake-fd requires a file descriptor");
return EXIT_FAILURE;
}
break;
}
case kOptionResetOwnCrashExceptionPortToSystemDefault: {
options.reset_own_crash_exception_port_to_system_default = true;
break;
}
#elif defined(OS_WIN)
case kOptionPipeName: {
options.pipe_name = optarg;
break;
}
#endif // OS_MACOSX
case kOptionURL: {
options.url = optarg;
break;
}
case kOptionHelp: {
Usage(me);
return EXIT_SUCCESS;
}
case kOptionVersion: {
ToolSupport::Version(me);
return EXIT_SUCCESS;
}
default: {
ToolSupport::UsageHint(me, nullptr);
return EXIT_FAILURE;
}
}
}
argc -= optind;
argv += optind;
#if defined(OS_MACOSX)
if (options.handshake_fd < 0) {
ToolSupport::UsageHint(me, "--handshake-fd is required");
return EXIT_FAILURE;
}
#elif defined(OS_WIN)
if (options.pipe_name.empty()) {
ToolSupport::UsageHint(me, "--pipe-name is required");
return EXIT_FAILURE;
}
#endif
if (!options.database) {
ToolSupport::UsageHint(me, "--database is required");
return EXIT_FAILURE;
}
if (argc) {
ToolSupport::UsageHint(me, nullptr);
return EXIT_FAILURE;
}
#if defined(OS_MACOSX)
CloseStdinAndStdout();
if (options.reset_own_crash_exception_port_to_system_default) {
CrashpadClient::UseSystemDefaultHandler();
}
base::mac::ScopedMachReceiveRight receive_right(
ChildPortHandshake::RunServerForFD(
base::ScopedFD(options.handshake_fd),
ChildPortHandshake::PortRightType::kReceiveRight));
if (!receive_right.is_valid()) {
return EXIT_FAILURE;
}
ExceptionHandlerServer exception_handler_server(receive_right.Pass());
#elif defined(OS_WIN)
ExceptionHandlerServer exception_handler_server(options.pipe_name);
#endif // OS_MACOSX
scoped_ptr<CrashReportDatabase> database(CrashReportDatabase::Initialize(
base::FilePath(ToolSupport::CommandLineArgumentToFilePathStringType(
options.database))));
if (!database) {
return EXIT_FAILURE;
}
CrashReportUploadThread upload_thread(database.get(), options.url);
upload_thread.Start();
CrashReportExceptionHandler exception_handler(
database.get(), &upload_thread, &options.annotations);
exception_handler_server.Run(&exception_handler);
upload_thread.Stop();
return EXIT_SUCCESS;
}
} // namespace
} // namespace crashpad
#if defined(OS_MACOSX)
int main(int argc, char* argv[]) {
return crashpad::HandlerMain(argc, argv);
}
#elif defined(OS_WIN)
int wmain(int argc, wchar_t* argv[]) {
return crashpad::ToolSupport::Wmain(argc, argv, crashpad::HandlerMain);
}
#endif // OS_MACOSX