From f4ec7ba5901968cbcb785b2a81e0608c842e6ddd Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Tue, 30 Dec 2014 14:25:58 -0500 Subject: [PATCH] Add the run_with_crashpad tool. run_with_crashpad runs a program with a Crashpad exception handler. It allows a Crashpad exception-handling server to be started and to direct crashes to it from programs that are not themselves clients of the Crashpad client library. R=rsesek@chromium.org Review URL: https://codereview.chromium.org/810423004 --- tools/mac/run_with_crashpad.ad | 99 +++++++++++++++++++++++ tools/mac/run_with_crashpad.cc | 144 +++++++++++++++++++++++++++++++++ tools/tools.gyp | 16 ++++ 3 files changed, 259 insertions(+) create mode 100644 tools/mac/run_with_crashpad.ad create mode 100644 tools/mac/run_with_crashpad.cc diff --git a/tools/mac/run_with_crashpad.ad b/tools/mac/run_with_crashpad.ad new file mode 100644 index 00000000..c216153e --- /dev/null +++ b/tools/mac/run_with_crashpad.ad @@ -0,0 +1,99 @@ +// 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. + +:doctype: manpage + += run_with_crashpad(1) + +== Name + +run_with_crashpad - Run a program with a Crashpad exception handler + +== Synopsis + +[verse] +*run_with_crashpad* ['OPTION…'] 'COMMAND' ['ARG…'] + +== Description + +Starts a Crashpad exception handler server such as crashpad_handler(8) and +becomes its client, setting an exception port referencing the handler. Then, +executes 'COMMAND' along with any arguments specified ('ARG…') with the new +exception port in effect. + +The exception port is configured to receive exceptions of type +EXC_CRASH+, ++EXC_RESOURCE+, and +EXC_GUARD+. The exception behavior is configured as ++EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES+. The thread state flavor is +set to +MACHINE_THREAD_STATE+. + +Programs that use the Crashpad client library directly will not normally use +this tool. This tool exists to allow programs that are unaware of Crashpad to be +run with a Crashpad exception handler. + +== Options +*-h*, *--handler*='HANDLER':: +Invoke 'HANDLER' as the Crashpad handler program instead of the default, +*crashpad_handler*. + +*-a*, *--handler-argument*='ARGUMENT':: +Invokes the Crashpad handler program with 'ARGUMENT' as one of its arguments. +This option may appear zero, one, or more times. Regardless of this option’s +presence, the handler will always be invoked with the necessary arguments to +perform a handshake. + +*--help*:: +Display help and exit. + +*--version*:: +Output version information and exit. + +== Examples + +Starts a Crashpad exception handler server by its default name, +*crashpad_handler*, and runs a program with this handler in effect. +[subs="quotes"] +---- +$ *run_with_crashpad crash* +Illegal instruction: 4 +---- + +Starts a Crashpad exception handler server at a nonstandard path, and runs +exception_port_tool(1) to show the task-level exception ports. +[subs="quotes"] +---- +$ *run_with_crashpad --handler=/tmp/crashpad_handler exception_port_tool* +task exception port 0, mask 0x1c00 (CRASH|RESOURCE|GUARD), port 0x30b, behavior +0x80000003 (STATE_IDENTITY|MACH), flavor 7 (THREAD) +---- + +== Exit Status + +*0*:: +Success. + +*125*:: +Failure, with a message printed to the standard error stream. + +*126*:: +The program specified by 'COMMAND' was found, but could not be invoked. + +*127*:: +The program specified by 'COMMAND' could not be found. + +== See Also + +crashpad_handler(8), +exception_port_tool(1) + +include::../man_footer.ad[] diff --git a/tools/mac/run_with_crashpad.cc b/tools/mac/run_with_crashpad.cc new file mode 100644 index 00000000..c6499d46 --- /dev/null +++ b/tools/mac/run_with_crashpad.cc @@ -0,0 +1,144 @@ +// 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 +#include +#include +#include +#include +#include + +#include +#include + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "client/crashpad_client.h" +#include "tools/tool_support.h" + +namespace crashpad { +namespace { + +void Usage(const std::string& me) { + fprintf(stderr, +"Usage: %s [OPTION]... COMMAND [ARG]...\n" +"Start a Crashpad handler and have it handle crashes from COMMAND.\n" +"\n" +" -h, --handler=HANDLER invoke HANDLER instead of crashpad_handler\n" +" -a, --handler-argument=ARGUMENT invoke the handler with ARGUMENT\n" +" --help display this help and exit\n" +" --version output version information and exit\n", + me.c_str()); + ToolSupport::UsageTail(me); +} + +int RunWithCrashpadMain(int argc, char* argv[]) { + const std::string me(basename(argv[0])); + + enum ExitCode { + kExitSuccess = EXIT_SUCCESS, + + // To differentiate this tool’s errors from errors in the programs it execs, + // use a high exit code for ordinary failures instead of EXIT_FAILURE. This + // is the same rationale for using the distinct exit codes for exec + // failures. + kExitFailure = 125, + + // Like env, use exit code 126 if the program was found but could not be + // invoked, and 127 if it could not be found. + // http://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html + kExitExecFailure = 126, + kExitExecENOENT = 127, + }; + + enum OptionFlags { + // “Short” (single-character) options. + kOptionHandler = 'h', + kOptionHandlerArgument = 'a', + + // Long options without short equivalents. + kOptionLastChar = 255, + + // Standard options. + kOptionHelp = -2, + kOptionVersion = -3, + }; + + const struct option long_options[] = { + {"handler", required_argument, nullptr, kOptionHandler}, + {"handler-argument", required_argument, nullptr, kOptionHandlerArgument}, + {"help", no_argument, nullptr, kOptionHelp}, + {"version", no_argument, nullptr, kOptionVersion}, + {nullptr, 0, nullptr, 0}, + }; + + struct { + std::string handler; + std::vector handler_arguments; + } options = {}; + options.handler = "crashpad_handler"; + + int opt; + while ((opt = getopt_long(argc, argv, "+a:h:", long_options, nullptr)) != + -1) { + switch (opt) { + case kOptionHandler: + options.handler = optarg; + break; + case kOptionHandlerArgument: + options.handler_arguments.push_back(optarg); + break; + case kOptionHelp: + Usage(me); + return kExitSuccess; + case kOptionVersion: + ToolSupport::Version(me); + return kExitSuccess; + default: + ToolSupport::UsageHint(me, nullptr); + return kExitFailure; + } + } + argc -= optind; + argv += optind; + + if (!argc) { + ToolSupport::UsageHint(me, "COMMAND is required"); + return kExitFailure; + } + + // Start the handler process and direct exceptions to it. + CrashpadClient crashpad_client; + if (!crashpad_client.StartHandler(base::FilePath(options.handler), + options.handler_arguments)) { + return kExitFailure; + } + + if (!crashpad_client.UseHandler()) { + return kExitFailure; + } + + // Using the remaining arguments, start a new program with the new exception + // port in effect. + execvp(argv[0], argv); + PLOG(ERROR) << "execvp " << argv[0]; + return errno == ENOENT ? kExitExecENOENT : kExitExecFailure; +} + +} // namespace +} // namespace crashpad + +int main(int argc, char* argv[]) { + return crashpad::RunWithCrashpadMain(argc, argv); +} diff --git a/tools/tools.gyp b/tools/tools.gyp index fdcbd019..9a1119ac 100644 --- a/tools/tools.gyp +++ b/tools/tools.gyp @@ -102,6 +102,22 @@ 'mac/on_demand_service_tool.mm', ], }, + { + 'target_name': 'run_with_crashpad', + 'type': 'executable', + 'dependencies': [ + 'tool_support', + '../client/client.gyp:client', + '../compat/compat.gyp:compat', + '../third_party/mini_chromium/mini_chromium/base/base.gyp:base', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'mac/run_with_crashpad.cc', + ], + }, ], }, { 'targets': [],