mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-28 15:50:26 +08:00
fc7d8b3a27
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 .
266 lines
7.9 KiB
C++
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
|