mirror of
https://github.com/chromium/crashpad.git
synced 2025-01-14 01:08:01 +08:00
CrashpadClient::StartHandler(): accept database, url, and annotations arguments.
This makes it easier for clients to start the Crashpad handler, instead of requiring them to know how to construct arguments for the handler themselves. Note in the TEST that -a is no longer required. TEST=run_with_crashpad --handler crashpad_handler \ --database=/tmp/crashpad_db \ --url=https://clients2.google.com/cr/staging_report \ --annotation=prod=crashpad \ --annotation=ver=0.7.0 \ crashy_program R=rsesek@chromium.org Review URL: https://codereview.chromium.org/1001993002
This commit is contained in:
parent
5f19d639e1
commit
29cdc74579
@ -15,6 +15,7 @@
|
||||
#ifndef CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_
|
||||
#define CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -49,13 +50,24 @@ class CrashpadClient {
|
||||
//! The handler process runs an exception server on this port.
|
||||
//!
|
||||
//! \param[in] handler The path to a Crashpad handler executable.
|
||||
//! \param[in] handler_arguments Arguments to pass to the Crashpad handler.
|
||||
//! Arguments required to perform the handshake are the responsibility of
|
||||
//! this method, and must not be specified in this parameter.
|
||||
//! \param[in] database The path to a Crashpad database. The handler will be
|
||||
//! started with this path as its `--database` argument.
|
||||
//! \param[in] url The URL of an upload server. The handler will be started
|
||||
//! with this URL as its `--url` argument.
|
||||
//! \param[in] annotations Process annotations to set in each crash report.
|
||||
//! The handler will be started with an `--annotation` argument for each
|
||||
//! element in this map.
|
||||
//! \param[in] arguments Additional arguments to pass to the Crashpad handler.
|
||||
//! Arguments passed in other parameters and arguments required to perform
|
||||
//! the handshake are the responsibility of this method, and must not be
|
||||
//! specified in this parameter.
|
||||
//!
|
||||
//! \return `true` on success, `false` on failure with a message logged.
|
||||
bool StartHandler(const base::FilePath& handler,
|
||||
const std::vector<std::string>& handler_arguments);
|
||||
const base::FilePath& database,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments);
|
||||
|
||||
//! \brief Configures the process to direct its crashes to a Crashpad handler.
|
||||
//!
|
||||
|
@ -27,6 +27,19 @@
|
||||
#include "util/mach/mach_extensions.h"
|
||||
#include "util/posix/close_multiple.h"
|
||||
|
||||
namespace {
|
||||
|
||||
std::string FormatArgumentString(const std::string& name,
|
||||
const std::string& value) {
|
||||
return base::StringPrintf("--%s=%s", name.c_str(), value.c_str());
|
||||
}
|
||||
|
||||
std::string FormatArgumentInt(const std::string& name, int value) {
|
||||
return base::StringPrintf("--%s=%d", name.c_str(), value);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
CrashpadClient::CrashpadClient()
|
||||
@ -38,28 +51,49 @@ CrashpadClient::~CrashpadClient() {
|
||||
|
||||
bool CrashpadClient::StartHandler(
|
||||
const base::FilePath& handler,
|
||||
const std::vector<std::string>& handler_arguments) {
|
||||
const base::FilePath& database,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments) {
|
||||
DCHECK_EQ(exception_port_, kMachPortNull);
|
||||
|
||||
// Set up the arguments for execve() first. These aren’t needed until execve()
|
||||
// is called, but it’s dangerous to do this in a child process after fork().
|
||||
ChildPortHandshake child_port_handshake;
|
||||
int handshake_fd = child_port_handshake.ReadPipeFD();
|
||||
std::string handshake_fd_arg =
|
||||
base::StringPrintf("--handshake-fd=%d", handshake_fd);
|
||||
|
||||
const std::string& handler_s = handler.value();
|
||||
const char* const handler_c = handler_s.c_str();
|
||||
|
||||
// Use handler as argv[0], followed by handler_arguments, handshake_fd_arg,
|
||||
// and a nullptr terminator.
|
||||
std::vector<const char*> argv(1, handler_c);
|
||||
argv.reserve(1 + handler_arguments.size() + 1 + 1);
|
||||
for (const std::string& handler_argument : handler_arguments) {
|
||||
argv.push_back(handler_argument.c_str());
|
||||
// Use handler as argv[0], followed by arguments directed by this method’s
|
||||
// parameters and a --handshake-fd argument. |arguments| are added first so
|
||||
// that if it erroneously contains an argument such as --url, the actual |url|
|
||||
// argument passed to this method will supersede it. In normal command-line
|
||||
// processing, the last parameter wins in the case of a conflict.
|
||||
std::vector<std::string> argv(1, handler.value());
|
||||
argv.reserve(1 + arguments.size() + 2 + annotations.size() + 1);
|
||||
for (const std::string& argument : arguments) {
|
||||
argv.push_back(argument);
|
||||
}
|
||||
argv.push_back(handshake_fd_arg.c_str());
|
||||
argv.push_back(nullptr);
|
||||
if (!database.value().empty()) {
|
||||
argv.push_back(FormatArgumentString("database", database.value()));
|
||||
}
|
||||
if (!url.empty()) {
|
||||
argv.push_back(FormatArgumentString("url", url));
|
||||
}
|
||||
for (const auto& kv : annotations) {
|
||||
argv.push_back(
|
||||
FormatArgumentString("annotation", kv.first + '=' + kv.second));
|
||||
}
|
||||
argv.push_back(FormatArgumentInt("handshake-fd", handshake_fd));
|
||||
|
||||
// argv_c contains const char* pointers and is terminated by nullptr. argv
|
||||
// is required because the pointers in argv_c need to point somewhere, and
|
||||
// they can’t point to temporaries such as those returned by
|
||||
// FormatArgumentString().
|
||||
std::vector<const char*> argv_c;
|
||||
argv_c.reserve(argv.size() + 1);
|
||||
for (const std::string& argument : argv) {
|
||||
argv_c.push_back(argument.c_str());
|
||||
}
|
||||
argv_c.push_back(nullptr);
|
||||
|
||||
// Double-fork(). The three processes involved are parent, child, and
|
||||
// grandchild. The grandchild will become the handler process. The child exits
|
||||
@ -112,12 +146,12 @@ bool CrashpadClient::StartHandler(
|
||||
|
||||
CloseMultipleNowOrOnExec(STDERR_FILENO + 1, handshake_fd);
|
||||
|
||||
// &argv[0] is a pointer to a pointer to const char data, but because of how
|
||||
// C (not C++) works, execvp() wants a pointer to a const pointer to char
|
||||
// data. It modifies neither the data nor the pointers, so the const_cast is
|
||||
// safe.
|
||||
execvp(handler_c, const_cast<char* const*>(&argv[0]));
|
||||
PLOG(FATAL) << "execvp " << handler_s;
|
||||
// &argv_c[0] is a pointer to a pointer to const char data, but because of
|
||||
// how C (not C++) works, execvp() wants a pointer to a const pointer to
|
||||
// char data. It modifies neither the data nor the pointers, so the
|
||||
// const_cast is safe.
|
||||
execvp(handler.value().c_str(), const_cast<char* const*>(&argv_c[0]));
|
||||
PLOG(FATAL) << "execvp " << handler.value();
|
||||
}
|
||||
|
||||
// Parent process.
|
||||
|
@ -31,24 +31,12 @@
|
||||
#include "util/mach/child_port_handshake.h"
|
||||
#include "util/posix/close_stdio.h"
|
||||
#include "util/stdlib/string_number_conversion.h"
|
||||
#include "util/string/split_string.h"
|
||||
#include "util/synchronization/semaphore.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace {
|
||||
|
||||
bool SplitString(const std::string& string,
|
||||
std::string* left,
|
||||
std::string* right) {
|
||||
size_t equals_pos = string.find('=');
|
||||
if (equals_pos == 0 || equals_pos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
left->assign(string, 0, equals_pos);
|
||||
right->assign(string, equals_pos + 1, std::string::npos);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Usage(const std::string& me) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s [OPTION]...\n"
|
||||
@ -57,6 +45,7 @@ void Usage(const std::string& me) {
|
||||
" --annotation=KEY=VALUE set a process annotation in each crash report\n"
|
||||
" --database=PATH store the crash report database at PATH\n"
|
||||
" --handshake-fd=FD establish communication with the client over FD\n"
|
||||
" --url=URL send crash reports to this Breakpad server URL\n"
|
||||
" --help display this help and exit\n"
|
||||
" --version output version information and exit\n",
|
||||
me.c_str());
|
||||
@ -103,7 +92,7 @@ int HandlerMain(int argc, char* argv[]) {
|
||||
case kOptionAnnotation: {
|
||||
std::string key;
|
||||
std::string value;
|
||||
if (!SplitString(optarg, &key, &value)) {
|
||||
if (!SplitString(optarg, '=', &key, &value)) {
|
||||
ToolSupport::UsageHint(me, "--annotation requires KEY=VALUE");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -26,6 +27,7 @@
|
||||
#include "base/logging.h"
|
||||
#include "client/crashpad_client.h"
|
||||
#include "tools/tool_support.h"
|
||||
#include "util/string/split_string.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace {
|
||||
@ -35,10 +37,13 @@ void Usage(const std::string& me) {
|
||||
"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",
|
||||
" -h, --handler=HANDLER invoke HANDLER instead of crashpad_handler\n"
|
||||
" --annotation=KEY=VALUE passed to the handler as an --annotation argument\n"
|
||||
" --database=PATH passed to the handler as its --database argument\n"
|
||||
" --url=URL passed to the handler as its --url argument\n"
|
||||
" -a, --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);
|
||||
}
|
||||
@ -65,10 +70,13 @@ int RunWithCrashpadMain(int argc, char* argv[]) {
|
||||
enum OptionFlags {
|
||||
// “Short” (single-character) options.
|
||||
kOptionHandler = 'h',
|
||||
kOptionHandlerArgument = 'a',
|
||||
kOptionArgument = 'a',
|
||||
|
||||
// Long options without short equivalents.
|
||||
kOptionLastChar = 255,
|
||||
kOptionAnnotation,
|
||||
kOptionDatabase,
|
||||
kOptionURL,
|
||||
|
||||
// Standard options.
|
||||
kOptionHelp = -2,
|
||||
@ -77,7 +85,10 @@ int RunWithCrashpadMain(int argc, char* argv[]) {
|
||||
|
||||
const struct option long_options[] = {
|
||||
{"handler", required_argument, nullptr, kOptionHandler},
|
||||
{"handler-argument", required_argument, nullptr, kOptionHandlerArgument},
|
||||
{"annotation", required_argument, nullptr, kOptionAnnotation},
|
||||
{"database", required_argument, nullptr, kOptionDatabase},
|
||||
{"url", required_argument, nullptr, kOptionURL},
|
||||
{"argument", required_argument, nullptr, kOptionArgument},
|
||||
{"help", no_argument, nullptr, kOptionHelp},
|
||||
{"version", no_argument, nullptr, kOptionVersion},
|
||||
{nullptr, 0, nullptr, 0},
|
||||
@ -85,7 +96,10 @@ int RunWithCrashpadMain(int argc, char* argv[]) {
|
||||
|
||||
struct {
|
||||
std::string handler;
|
||||
std::vector<std::string> handler_arguments;
|
||||
std::map<std::string, std::string> annotations;
|
||||
std::string database;
|
||||
std::string url;
|
||||
std::vector<std::string> arguments;
|
||||
} options = {};
|
||||
options.handler = "crashpad_handler";
|
||||
|
||||
@ -93,21 +107,51 @@ int RunWithCrashpadMain(int argc, char* argv[]) {
|
||||
while ((opt = getopt_long(argc, argv, "+a:h:", long_options, nullptr)) !=
|
||||
-1) {
|
||||
switch (opt) {
|
||||
case kOptionHandler:
|
||||
case kOptionHandler: {
|
||||
options.handler = optarg;
|
||||
break;
|
||||
case kOptionHandlerArgument:
|
||||
options.handler_arguments.push_back(optarg);
|
||||
}
|
||||
case kOptionAnnotation: {
|
||||
std::string key;
|
||||
std::string value;
|
||||
if (!SplitString(optarg, '=', &key, &value)) {
|
||||
ToolSupport::UsageHint(me, "--annotation requires KEY=VALUE");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
auto it = options.annotations.find(key);
|
||||
if (it != options.annotations.end()) {
|
||||
LOG(WARNING) << "duplicate key " << key << ", discarding value "
|
||||
<< it->second;
|
||||
it->second = value;
|
||||
} else {
|
||||
options.annotations.insert(std::make_pair(key, value));
|
||||
}
|
||||
break;
|
||||
case kOptionHelp:
|
||||
}
|
||||
case kOptionDatabase: {
|
||||
options.database = optarg;
|
||||
break;
|
||||
}
|
||||
case kOptionURL: {
|
||||
options.url = optarg;
|
||||
break;
|
||||
}
|
||||
case kOptionArgument: {
|
||||
options.arguments.push_back(optarg);
|
||||
break;
|
||||
}
|
||||
case kOptionHelp: {
|
||||
Usage(me);
|
||||
return kExitSuccess;
|
||||
case kOptionVersion:
|
||||
}
|
||||
case kOptionVersion: {
|
||||
ToolSupport::Version(me);
|
||||
return kExitSuccess;
|
||||
default:
|
||||
}
|
||||
default: {
|
||||
ToolSupport::UsageHint(me, nullptr);
|
||||
return kExitFailure;
|
||||
}
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
@ -121,7 +165,10 @@ int RunWithCrashpadMain(int argc, char* argv[]) {
|
||||
// Start the handler process and direct exceptions to it.
|
||||
CrashpadClient crashpad_client;
|
||||
if (!crashpad_client.StartHandler(base::FilePath(options.handler),
|
||||
options.handler_arguments)) {
|
||||
base::FilePath(options.database),
|
||||
options.url,
|
||||
options.annotations,
|
||||
options.arguments)) {
|
||||
return kExitFailure;
|
||||
}
|
||||
|
||||
|
@ -155,6 +155,7 @@
|
||||
'../client/client.gyp:crashpad_client',
|
||||
'../compat/compat.gyp:crashpad_compat',
|
||||
'../third_party/mini_chromium/mini_chromium.gyp:base',
|
||||
'../util/util.gyp:crashpad_util',
|
||||
],
|
||||
'include_dirs': [
|
||||
'..',
|
||||
|
33
util/string/split_string.cc
Normal file
33
util/string/split_string.cc
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2015 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 "util/string/split_string.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
bool SplitString(const std::string& string,
|
||||
char delimiter,
|
||||
std::string* left,
|
||||
std::string* right) {
|
||||
size_t delimiter_pos = string.find(delimiter);
|
||||
if (delimiter_pos == 0 || delimiter_pos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
left->assign(string, 0, delimiter_pos);
|
||||
right->assign(string, delimiter_pos + 1, std::string::npos);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
41
util/string/split_string.h
Normal file
41
util/string/split_string.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
#ifndef CRASHPAD_UTIL_STRING_SPLIT_STRING_H_
|
||||
#define CRASHPAD_UTIL_STRING_SPLIT_STRING_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief Splits a string into two parts at the first delimiter found.
|
||||
//!
|
||||
//! \param[in] string The string to split.
|
||||
//! \param[in] delimiter The delimiter to split at.
|
||||
//! \param[out] left The portion of \a string up to, but not including, the
|
||||
//! first \a delimiter character.
|
||||
//! \param[out] right The portion of \a string after the first \a delimiter
|
||||
//! character.
|
||||
//!
|
||||
//! \return `true` if \a string was split successfully. `false` if \a string
|
||||
//! did not contain a \delimiter character or began with a \delimiter
|
||||
//! character.
|
||||
bool SplitString(const std::string& string,
|
||||
char delimiter,
|
||||
std::string* left,
|
||||
std::string* right);
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_STRING_SPLIT_STRING_H_
|
63
util/string/split_string_test.cc
Normal file
63
util/string/split_string_test.cc
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2015 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 "util/string/split_string.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
TEST(SplitString, SplitString) {
|
||||
std::string left;
|
||||
std::string right;
|
||||
|
||||
EXPECT_FALSE(SplitString("", '=', &left, &right));
|
||||
EXPECT_FALSE(SplitString("no equals", '=', &left, &right));
|
||||
EXPECT_FALSE(SplitString("=", '=', &left, &right));
|
||||
EXPECT_FALSE(SplitString("=beginequals", '=', &left, &right));
|
||||
|
||||
ASSERT_TRUE(SplitString("a=b", '=', &left, &right));
|
||||
EXPECT_EQ("a", left);
|
||||
EXPECT_EQ("b", right);
|
||||
|
||||
ASSERT_TRUE(SplitString("EndsEquals=", '=', &left, &right));
|
||||
EXPECT_EQ("EndsEquals", left);
|
||||
EXPECT_TRUE(right.empty());
|
||||
|
||||
ASSERT_TRUE(SplitString("key=VALUE", '=', &left, &right));
|
||||
EXPECT_EQ("key", left);
|
||||
EXPECT_EQ("VALUE", right);
|
||||
|
||||
EXPECT_FALSE(SplitString("a=b", '|', &left, &right));
|
||||
|
||||
ASSERT_TRUE(SplitString("ls | less", '|', &left, &right));
|
||||
EXPECT_EQ("ls ", left);
|
||||
EXPECT_EQ(" less", right);
|
||||
|
||||
ASSERT_TRUE(SplitString("when in", ' ', &left, &right));
|
||||
EXPECT_EQ("when", left);
|
||||
EXPECT_EQ("in", right);
|
||||
|
||||
ASSERT_TRUE(SplitString("zoo", 'o', &left, &right));
|
||||
EXPECT_EQ("z", left);
|
||||
EXPECT_EQ("o", right);
|
||||
|
||||
ASSERT_FALSE(SplitString("ooze", 'o', &left, &right));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -129,6 +129,8 @@
|
||||
'stdlib/strlcpy.h',
|
||||
'stdlib/strnlen.cc',
|
||||
'stdlib/strnlen.h',
|
||||
'string/split_string.cc',
|
||||
'string/split_string.h',
|
||||
'synchronization/semaphore_mac.cc',
|
||||
'synchronization/semaphore_posix.cc',
|
||||
'synchronization/semaphore_win.cc',
|
||||
@ -312,6 +314,7 @@
|
||||
'stdlib/string_number_conversion_test.cc',
|
||||
'stdlib/strlcpy_test.cc',
|
||||
'stdlib/strnlen_test.cc',
|
||||
'string/split_string_test.cc',
|
||||
'synchronization/semaphore_test.cc',
|
||||
'test/mac/mach_multiprocess_test.cc',
|
||||
'test/multiprocess_exec_test.cc',
|
||||
|
Loading…
x
Reference in New Issue
Block a user