Mac: Add support for in-process crash reporting to Breakpad.

Add new option BREAKPAD_IN_PROCESS.
If YES, Breakpad will write the dump file in-process and then launch the reporter
executable as a child  process.

Originally reviewed at https://codereview.chromium.org/571523004/

BUG=chromium:414239
R=mark@chromium.org

Review URL: https://breakpad.appspot.com/1714002

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1375 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
andresantoso@chromium.org 2014-09-15 22:48:18 +00:00
parent c75e316359
commit def0b7a7b0
8 changed files with 196 additions and 49 deletions

View file

@ -62,6 +62,7 @@
#define BREAKPAD_EMAIL "BreakpadEmail"
#define BREAKPAD_SERVER_TYPE "BreakpadServerType"
#define BREAKPAD_SERVER_PARAMETER_DICT "BreakpadServerParameters"
#define BREAKPAD_IN_PROCESS "BreakpadInProcess"
// The keys below are NOT user supplied, and are used internally.
#define BREAKPAD_PROCESS_START_TIME "BreakpadProcStartTime"

View file

@ -158,6 +158,11 @@ typedef bool (*BreakpadFilterCallback)(int exception_type,
// but pass as URL parameters when
// uploading theminidump to the crash
// server.
//
// BREAKPAD_IN_PROCESS A boolean NSNumber value. If YES, Breakpad
// will write the dump file in-process and then
// launch the reporter executable as a child
// process.
//=============================================================================
// The BREAKPAD_PRODUCT, BREAKPAD_VERSION and BREAKPAD_URL are
// required to have non-NULL values. By default, the BREAKPAD_PRODUCT

View file

@ -44,6 +44,7 @@
#import "client/mac/Framework/Breakpad.h"
#import "client/mac/Framework/OnDemandServer.h"
#import "client/mac/handler/protected_memory_allocator.h"
#include "common/mac/launch_reporter.h"
#import "common/mac/MachIPC.h"
#import "common/simple_string_dictionary.h"
@ -173,6 +174,8 @@ class Breakpad {
}
bool Initialize(NSDictionary *parameters);
bool InitializeInProcess(NSDictionary *parameters);
bool InitializeOutOfProcess(NSDictionary *parameters);
bool ExtractParameters(NSDictionary *parameters);
@ -188,6 +191,17 @@ class Breakpad {
int exception_subcode,
mach_port_t crashing_thread);
// Dispatches to HandleMinidump().
// This gets called instead of ExceptionHandlerDirectCallback when running
// with the BREAKPAD_IN_PROCESS option.
static bool HandleMinidumpCallback(const char *dump_dir,
const char *minidump_id,
void *context,
bool succeeded);
// This is only used when BREAKPAD_IN_PROCESS is YES.
bool HandleMinidump(const char *dump_dir, const char *minidump_id);
// Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's
// MachineExceptions.h, we have to explicitly name the handler.
google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG)
@ -265,6 +279,21 @@ bool Breakpad::ExceptionHandlerDirectCallback(void *context,
crashing_thread);
}
//=============================================================================
bool Breakpad::HandleMinidumpCallback(const char *dump_dir,
const char *minidump_id,
void *context,
bool succeeded) {
Breakpad *breakpad = (Breakpad *)context;
// If our context is damaged or something, just return false to indicate that
// the handler should continue without us.
if (!breakpad || !succeeded)
return false;
return breakpad->HandleMinidump(dump_dir, minidump_id);
}
//=============================================================================
#pragma mark -
@ -326,6 +355,25 @@ bool Breakpad::Initialize(NSDictionary *parameters) {
return false;
}
if ([[parameters objectForKey:@BREAKPAD_IN_PROCESS] boolValue])
return InitializeInProcess(parameters);
else
return InitializeOutOfProcess(parameters);
}
//=============================================================================
bool Breakpad::InitializeInProcess(NSDictionary* parameters) {
handler_ =
new (gBreakpadAllocator->Allocate(
sizeof(google_breakpad::ExceptionHandler)))
google_breakpad::ExceptionHandler(
config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY),
0, &HandleMinidumpCallback, this, true, 0);
return true;
}
//=============================================================================
bool Breakpad::InitializeOutOfProcess(NSDictionary* parameters) {
// Get path to Inspector executable.
NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION);
@ -710,6 +758,16 @@ bool Breakpad::HandleException(int exception_type,
return false;
}
//=============================================================================
bool Breakpad::HandleMinidump(const char *dump_dir, const char *minidump_id) {
google_breakpad::ConfigFile config_file;
config_file.WriteFile(dump_dir, config_params_, dump_dir, minidump_id);
google_breakpad::LaunchReporter(
config_params_->GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION),
config_file.GetFilePath());
return true;
}
//=============================================================================
//=============================================================================

View file

@ -36,7 +36,7 @@
#include <sys/time.h>
#import "client/apple/Framework/BreakpadDefines.h"
#import "GTMDefines.h"
#import "common/mac/GTMDefines.h"
namespace google_breakpad {

View file

@ -138,7 +138,6 @@ class Inspector {
bool InspectTask();
kern_return_t SendAcknowledgement();
void LaunchReporter(const char *inConfigFilePath);
// The bootstrap port in which the inspector is registered and into which it
// must check in.

View file

@ -43,6 +43,7 @@
#import "common/mac/MachIPC.h"
#include "common/mac/bootstrap_compat.h"
#include "common/mac/launch_reporter.h"
#import "GTMDefines.h"
@ -76,7 +77,9 @@ void Inspector::Inspect(const char *receive_port_name) {
if (wrote_minidump) {
// Ask the user if he wants to upload the crash report to a server,
// and do so if he agrees.
LaunchReporter(config_file_.GetFilePath());
LaunchReporter(
config_params_.GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION),
config_file_.GetFilePath());
} else {
fprintf(stderr, "Inspection of crashed process failed\n");
}
@ -355,51 +358,5 @@ kern_return_t Inspector::SendAcknowledgement() {
return KERN_INVALID_NAME;
}
//=============================================================================
void Inspector::LaunchReporter(const char *inConfigFilePath) {
// Extract the path to the reporter executable.
const char *reporterExecutablePath =
config_params_.GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION);
// Setup and launch the crash dump sender.
const char *argv[3];
argv[0] = reporterExecutablePath;
argv[1] = inConfigFilePath;
argv[2] = NULL;
// Launch the reporter
pid_t pid = fork();
// If we're in the child, load in our new executable and run.
// The parent will not wait for the child to complete.
if (pid == 0) {
execv(argv[0], (char * const *)argv);
config_file_.Unlink(); // launch failed - get rid of config file
_exit(1);
}
// Wait until the Reporter child process exits.
//
// We'll use a timeout of one minute.
int timeoutCount = 60; // 60 seconds
while (timeoutCount-- > 0) {
int status;
pid_t result = waitpid(pid, &status, WNOHANG);
if (result == 0) {
// The child has not yet finished.
sleep(1);
} else if (result == -1) {
// error occurred.
break;
} else {
// child has finished
break;
}
}
}
} // namespace google_breakpad

View file

@ -0,0 +1,84 @@
// Copyright (c) 2014, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
namespace google_breakpad {
void LaunchReporter(const char *reporter_executable_path,
const char *config_file_path) {
const char* argv[] = { reporter_executable_path, config_file_path, NULL };
// Launch the reporter
pid_t pid = fork();
if (pid == -1) {
perror("fork");
fprintf(stderr, "Failed to fork reporter process\n");
return;
}
// If we're in the child, load in our new executable and run.
// The parent will not wait for the child to complete.
if (pid == 0) {
execv(argv[0], (char* const*)argv);
perror("exec");
fprintf(stderr,
"Failed to launch reporter process from path %s\n",
reporter_executable_path);
unlink(config_file_path); // launch failed - get rid of config file
_exit(1);
}
// Wait until the Reporter child process exits.
//
// We'll use a timeout of one minute.
int timeout_count = 60; // 60 seconds
while (timeout_count-- > 0) {
int status;
pid_t result = waitpid(pid, &status, WNOHANG);
if (result == 0) {
// The child has not yet finished.
sleep(1);
} else if (result == -1) {
// error occurred.
break;
} else {
// child has finished
break;
}
}
}
} // namespace google_breakpad

View file

@ -0,0 +1,43 @@
// Copyright (c) 2014, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef COMMON_MAC_LAUNCH_REPORTER_H__
#define COMMON_MAC_LAUNCH_REPORTER_H__
namespace google_breakpad {
// Launch the crash dump sender app.
// |reporter_executable_path| is the path to the sender executable.
// |config_file_path| is the path to the config file.
void LaunchReporter(const char *reporter_executable_path,
const char *config_file_path);
} // namespace google_breakpad
#endif // COMMON_MAC_LAUNCH_REPORTER_H__