crashpad/handler/mac/main.cc
Mark Mentovai ae1ccf621b handler: Add report upload.
The handler is now capable of uploading crash reports from the database.
At present, only one upload attempt is made, and the report will be
moved to “completed” in the database after the attempt, regardless of
whether it succeeded or failed.

The handler also has support to push annotations from its command line
into the process annotations map of each crash report it writes. This is
intended to set basic information about each crash report, such as the
product ID and version. Each potentially crashy process can’t be relied
on to maintain this information on their own.

With this change, Crashpad is now 100% capable of running a handler that
maintains a database and uploads crash reports to a Breakpad-type server
such that Breakpad properly interprets the reports. This is all possible
from the command line.

TEST=run_with_crashpad --handler crashpad_handler \
         -a --database=/tmp/crashpad_db \
         -a --url=https://clients2.google.com/cr/staging_report \
         -a --annotation=prod=crashpad \
         -a --annotation=ver=0.6.0 \
         crashy_program

R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/982613002
2015-03-05 15:40:47 -05:00

202 lines
5.7 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 <libgen.h>
#include <stdlib.h>
#include <map>
#include <string>
#include <utility>
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "client/crash_report_database.h"
#include "tools/tool_support.h"
#include "handler/mac/crash_report_exception_handler.h"
#include "handler/mac/crash_report_upload_thread.h"
#include "handler/mac/exception_handler_server.h"
#include "util/mach/child_port_handshake.h"
#include "util/posix/close_stdio.h"
#include "util/stdlib/string_number_conversion.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"
"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"
" --handshake-fd=FD establish communication with the client over FD\n"
" --help display this help and exit\n"
" --version output version information and exit\n",
me.c_str());
ToolSupport::UsageTail(me);
}
int HandlerMain(int argc, char* argv[]) {
const std::string me(basename(argv[0]));
enum OptionFlags {
// Long options without short equivalents.
kOptionLastChar = 255,
kOptionAnnotation,
kOptionDatabase,
kOptionHandshakeFD,
kOptionURL,
// Standard options.
kOptionHelp = -2,
kOptionVersion = -3,
};
struct {
std::map<std::string, std::string> annotations;
std::string url;
const char* database;
int handshake_fd;
} options = {};
options.handshake_fd = -1;
const struct option long_options[] = {
{"annotation", required_argument, nullptr, kOptionAnnotation},
{"database", required_argument, nullptr, kOptionDatabase},
{"handshake-fd", required_argument, nullptr, kOptionHandshakeFD},
{"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;
}
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 kOptionDatabase: {
options.database = optarg;
break;
}
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 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 (options.handshake_fd < 0) {
ToolSupport::UsageHint(me, "--handshake-fd is required");
return EXIT_FAILURE;
}
if (!options.database) {
ToolSupport::UsageHint(me, "--database is required");
return EXIT_FAILURE;
}
if (argc) {
ToolSupport::UsageHint(me, nullptr);
return EXIT_FAILURE;
}
CloseStdinAndStdout();
ExceptionHandlerServer exception_handler_server;
ChildPortHandshake::RunClient(options.handshake_fd,
exception_handler_server.receive_port(),
MACH_MSG_TYPE_MAKE_SEND);
scoped_ptr<CrashReportDatabase> database(
CrashReportDatabase::Initialize(base::FilePath(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
int main(int argc, char* argv[]) {
return crashpad::HandlerMain(argc, argv);
}