mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-28 08:10:47 +02:00
* Added breakpad support for Linux.
This commit is contained in:
193
thirdparty/breakpad/client/mac/crash_generation/Inspector.h
vendored
Normal file
193
thirdparty/breakpad/client/mac/crash_generation/Inspector.h
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
// Copyright (c) 2007, 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.
|
||||
//
|
||||
// Interface file between the Breakpad.framework and
|
||||
// the Inspector process.
|
||||
|
||||
#import "common/mac/SimpleStringDictionary.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "client/mac/handler/minidump_generator.h"
|
||||
|
||||
#define VERBOSE 0
|
||||
|
||||
extern bool gDebugLog;
|
||||
|
||||
#define DEBUGLOG if (gDebugLog) fprintf
|
||||
|
||||
// Types of mach messsages (message IDs)
|
||||
enum {
|
||||
kMsgType_InspectorInitialInfo = 0, // data is InspectorInfo
|
||||
kMsgType_InspectorKeyValuePair = 1, // data is KeyValueMessageData
|
||||
kMsgType_InspectorAcknowledgement = 2 // no data sent
|
||||
};
|
||||
|
||||
// Initial information sent from the crashed process by
|
||||
// Breakpad.framework to the Inspector process
|
||||
// The mach message with this struct as data will also include
|
||||
// several descriptors for sending mach port rights to the crashed
|
||||
// task, etc.
|
||||
struct InspectorInfo {
|
||||
int exception_type;
|
||||
int exception_code;
|
||||
int exception_subcode;
|
||||
unsigned int parameter_count; // key-value pairs
|
||||
};
|
||||
|
||||
// Key/value message data to be sent to the Inspector
|
||||
struct KeyValueMessageData {
|
||||
public:
|
||||
KeyValueMessageData() {}
|
||||
KeyValueMessageData(const google_breakpad::KeyValueEntry &inEntry) {
|
||||
strlcpy(key, inEntry.GetKey(), sizeof(key) );
|
||||
strlcpy(value, inEntry.GetValue(), sizeof(value) );
|
||||
}
|
||||
|
||||
char key[google_breakpad::KeyValueEntry::MAX_STRING_STORAGE_SIZE];
|
||||
char value[google_breakpad::KeyValueEntry::MAX_STRING_STORAGE_SIZE];
|
||||
};
|
||||
|
||||
using std::string;
|
||||
using google_breakpad::MinidumpGenerator;
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
BOOL EnsureDirectoryPathExists(NSString *dirPath);
|
||||
|
||||
//=============================================================================
|
||||
class ConfigFile {
|
||||
public:
|
||||
ConfigFile() {
|
||||
config_file_ = -1;
|
||||
config_file_path_[0] = 0;
|
||||
has_created_file_ = false;
|
||||
};
|
||||
|
||||
~ConfigFile() {
|
||||
};
|
||||
|
||||
void WriteFile(const SimpleStringDictionary *configurationParameters,
|
||||
const char *dump_dir,
|
||||
const char *minidump_id);
|
||||
|
||||
const char *GetFilePath() { return config_file_path_; }
|
||||
|
||||
void Unlink() {
|
||||
if (config_file_ != -1)
|
||||
unlink(config_file_path_);
|
||||
|
||||
config_file_ = -1;
|
||||
}
|
||||
|
||||
private:
|
||||
BOOL WriteData(const void *data, size_t length);
|
||||
|
||||
BOOL AppendConfigData(const char *key,
|
||||
const void *data,
|
||||
size_t length);
|
||||
|
||||
BOOL AppendConfigString(const char *key,
|
||||
const char *value);
|
||||
|
||||
int config_file_; // descriptor for config file
|
||||
char config_file_path_[PATH_MAX]; // Path to configuration file
|
||||
bool has_created_file_;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
class MinidumpLocation {
|
||||
public:
|
||||
MinidumpLocation(NSString *minidumpDir) {
|
||||
// Ensure that the path exists. Fallback to /tmp if unable to locate path.
|
||||
assert(minidumpDir);
|
||||
if (!EnsureDirectoryPathExists(minidumpDir)) {
|
||||
DEBUGLOG(stderr, "Unable to create: %s\n", [minidumpDir UTF8String]);
|
||||
minidumpDir = @"/tmp";
|
||||
}
|
||||
|
||||
strlcpy(minidump_dir_path_, [minidumpDir fileSystemRepresentation],
|
||||
sizeof(minidump_dir_path_));
|
||||
|
||||
// now generate a unique ID
|
||||
string dump_path(minidump_dir_path_);
|
||||
string next_minidump_id;
|
||||
|
||||
string next_minidump_path_ =
|
||||
(MinidumpGenerator::UniqueNameInDirectory(dump_path, &next_minidump_id));
|
||||
|
||||
strlcpy(minidump_id_, next_minidump_id.c_str(), sizeof(minidump_id_));
|
||||
};
|
||||
|
||||
const char *GetPath() { return minidump_dir_path_; }
|
||||
const char *GetID() { return minidump_id_; }
|
||||
|
||||
private:
|
||||
char minidump_dir_path_[PATH_MAX]; // Path to minidump directory
|
||||
char minidump_id_[128];
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
class Inspector {
|
||||
public:
|
||||
Inspector() {};
|
||||
|
||||
// given a bootstrap service name, receives mach messages
|
||||
// from a crashed process, then inspects it, creates a minidump file
|
||||
// and asks the user if he wants to upload it to a server.
|
||||
void Inspect(const char *receive_port_name);
|
||||
|
||||
private:
|
||||
kern_return_t ServiceCheckIn(const char *receive_port_name);
|
||||
kern_return_t ServiceCheckOut(const char *receive_port_name);
|
||||
|
||||
kern_return_t ReadMessages();
|
||||
|
||||
bool InspectTask();
|
||||
kern_return_t SendAcknowledgement();
|
||||
void LaunchReporter(const char *inConfigFilePath);
|
||||
|
||||
void SetCrashTimeParameters();
|
||||
|
||||
mach_port_t service_rcv_port_;
|
||||
|
||||
int exception_type_;
|
||||
int exception_code_;
|
||||
int exception_subcode_;
|
||||
mach_port_t remote_task_;
|
||||
mach_port_t crashing_thread_;
|
||||
mach_port_t handler_thread_;
|
||||
mach_port_t ack_port_;
|
||||
|
||||
SimpleStringDictionary config_params_;
|
||||
|
||||
ConfigFile config_file_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
555
thirdparty/breakpad/client/mac/crash_generation/Inspector.mm
vendored
Normal file
555
thirdparty/breakpad/client/mac/crash_generation/Inspector.mm
vendored
Normal file
@@ -0,0 +1,555 @@
|
||||
// Copyright (c) 2007, 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.
|
||||
//
|
||||
// Utility that can inspect another process and write a crash dump
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <sys/time.h>
|
||||
|
||||
#import "client/mac/crash_generation/Inspector.h"
|
||||
|
||||
#import "client/mac/Framework/Breakpad.h"
|
||||
#import "client/mac/handler/minidump_generator.h"
|
||||
|
||||
#import "common/mac/SimpleStringDictionary.h"
|
||||
#import "common/mac/MachIPC.h"
|
||||
|
||||
#import "GTMDefines.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if VERBOSE
|
||||
bool gDebugLog = true;
|
||||
#else
|
||||
bool gDebugLog = false;
|
||||
#endif
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
//=============================================================================
|
||||
BOOL EnsureDirectoryPathExists(NSString *dirPath) {
|
||||
NSFileManager *mgr = [NSFileManager defaultManager];
|
||||
|
||||
// If we got a relative path, prepend the current directory
|
||||
if (![dirPath isAbsolutePath])
|
||||
dirPath = [[mgr currentDirectoryPath] stringByAppendingPathComponent:dirPath];
|
||||
|
||||
NSString *path = dirPath;
|
||||
|
||||
// Ensure that no file exists within the path which would block creation
|
||||
while (1) {
|
||||
BOOL isDir;
|
||||
if ([mgr fileExistsAtPath:path isDirectory:&isDir]) {
|
||||
if (isDir)
|
||||
break;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
path = [path stringByDeletingLastPathComponent];
|
||||
}
|
||||
|
||||
// Path now contains the first valid directory (or is empty)
|
||||
if (![path length])
|
||||
return NO;
|
||||
|
||||
NSString *common =
|
||||
[dirPath commonPrefixWithString:path options:NSLiteralSearch];
|
||||
|
||||
// If everything is good
|
||||
if ([common isEqualToString:dirPath])
|
||||
return YES;
|
||||
|
||||
// Break up the difference into components
|
||||
NSString *diff = [dirPath substringFromIndex:[common length] + 1];
|
||||
NSArray *components = [diff pathComponents];
|
||||
NSUInteger count = [components count];
|
||||
|
||||
// Rebuild the path one component at a time
|
||||
NSDictionary *attrs =
|
||||
[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750]
|
||||
forKey:NSFilePosixPermissions];
|
||||
path = common;
|
||||
for (NSUInteger i = 0; i < count; ++i) {
|
||||
path = [path stringByAppendingPathComponent:[components objectAtIndex:i]];
|
||||
|
||||
if (![mgr createDirectoryAtPath:path attributes:attrs])
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
BOOL ConfigFile::WriteData(const void *data, size_t length) {
|
||||
size_t result = write(config_file_, data, length);
|
||||
|
||||
return result == length;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
BOOL ConfigFile::AppendConfigData(const char *key,
|
||||
const void *data, size_t length) {
|
||||
assert(config_file_ != -1);
|
||||
|
||||
if (!key) {
|
||||
DEBUGLOG(stderr, "Breakpad: Missing Key\n");
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
DEBUGLOG(stderr, "Breakpad: Missing data for key: %s\n", key ? key :
|
||||
"<Unknown Key>");
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Write the key, \n, length of data (ascii integer), \n, data
|
||||
char buffer[16];
|
||||
char nl = '\n';
|
||||
BOOL result = WriteData(key, strlen(key));
|
||||
|
||||
snprintf(buffer, sizeof(buffer) - 1, "\n%lu\n", length);
|
||||
result &= WriteData(buffer, strlen(buffer));
|
||||
result &= WriteData(data, length);
|
||||
result &= WriteData(&nl, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
BOOL ConfigFile::AppendConfigString(const char *key,
|
||||
const char *value) {
|
||||
return AppendConfigData(key, value, strlen(value));
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void ConfigFile::WriteFile(const SimpleStringDictionary *configurationParameters,
|
||||
const char *dump_dir,
|
||||
const char *minidump_id) {
|
||||
|
||||
assert(config_file_ == -1);
|
||||
|
||||
// Open and write out configuration file preamble
|
||||
strlcpy(config_file_path_, "/tmp/Config-XXXXXX",
|
||||
sizeof(config_file_path_));
|
||||
config_file_ = mkstemp(config_file_path_);
|
||||
|
||||
if (config_file_ == -1) {
|
||||
DEBUGLOG(stderr,
|
||||
"mkstemp(config_file_path_) == -1 (%s)\n",
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
else {
|
||||
DEBUGLOG(stderr, "Writing config file to (%s)\n", config_file_path_);
|
||||
}
|
||||
|
||||
has_created_file_ = true;
|
||||
|
||||
// Add the minidump dir
|
||||
AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir);
|
||||
AppendConfigString(kReporterMinidumpIDKey, minidump_id);
|
||||
|
||||
// Write out the configuration parameters
|
||||
BOOL result = YES;
|
||||
const SimpleStringDictionary &dictionary = *configurationParameters;
|
||||
|
||||
const KeyValueEntry *entry = NULL;
|
||||
SimpleStringDictionaryIterator iter(dictionary);
|
||||
|
||||
while ((entry = iter.Next())) {
|
||||
DEBUGLOG(stderr,
|
||||
"config: (%s) -> (%s)\n",
|
||||
entry->GetKey(),
|
||||
entry->GetValue());
|
||||
result = AppendConfigString(entry->GetKey(), entry->GetValue());
|
||||
|
||||
if (!result)
|
||||
break;
|
||||
}
|
||||
|
||||
close(config_file_);
|
||||
config_file_ = -1;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Inspector::Inspect(const char *receive_port_name) {
|
||||
kern_return_t result = ServiceCheckIn(receive_port_name);
|
||||
|
||||
if (result == KERN_SUCCESS) {
|
||||
result = ReadMessages();
|
||||
|
||||
if (result == KERN_SUCCESS) {
|
||||
// Inspect the task and write a minidump file.
|
||||
bool wrote_minidump = InspectTask();
|
||||
|
||||
// Send acknowledgement to the crashed process that the inspection
|
||||
// has finished. It will then be able to cleanly exit.
|
||||
// The return value is ignored because failure isn't fatal. If the process
|
||||
// didn't get the message there's nothing we can do, and we still want to
|
||||
// send the report.
|
||||
SendAcknowledgement();
|
||||
|
||||
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());
|
||||
} else {
|
||||
fprintf(stderr, "Inspection of crashed process failed\n");
|
||||
}
|
||||
|
||||
// Now that we're done reading messages, cleanup the service, but only
|
||||
// if there was an actual exception
|
||||
// Otherwise, it means the dump was generated on demand and the process
|
||||
// lives on, and we might be needed again in the future.
|
||||
if (exception_code_) {
|
||||
ServiceCheckOut(receive_port_name);
|
||||
}
|
||||
} else {
|
||||
PRINT_MACH_RESULT(result, "Inspector: WaitForMessage()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
kern_return_t Inspector::ServiceCheckIn(const char *receive_port_name) {
|
||||
// We need to get the mach port representing this service, so we can
|
||||
// get information from the crashed process.
|
||||
kern_return_t kr = bootstrap_check_in(bootstrap_port,
|
||||
(char*)receive_port_name,
|
||||
&service_rcv_port_);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
#if VERBOSE
|
||||
PRINT_MACH_RESULT(kr, "Inspector: bootstrap_check_in()");
|
||||
#endif
|
||||
}
|
||||
|
||||
return kr;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
kern_return_t Inspector::ServiceCheckOut(const char *receive_port_name) {
|
||||
// We're done receiving mach messages from the crashed process,
|
||||
// so clean up a bit.
|
||||
kern_return_t kr;
|
||||
|
||||
// DO NOT use mach_port_deallocate() here -- it will fail and the
|
||||
// following bootstrap_register() will also fail leaving our service
|
||||
// name hanging around forever (until reboot)
|
||||
kr = mach_port_destroy(mach_task_self(), service_rcv_port_);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
PRINT_MACH_RESULT(kr,
|
||||
"Inspector: UNREGISTERING: service_rcv_port mach_port_deallocate()");
|
||||
return kr;
|
||||
}
|
||||
|
||||
// Unregister the service associated with the receive port.
|
||||
kr = bootstrap_register(bootstrap_port,
|
||||
(char*)receive_port_name,
|
||||
MACH_PORT_NULL);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
PRINT_MACH_RESULT(kr, "Inspector: UNREGISTERING: bootstrap_register()");
|
||||
}
|
||||
|
||||
return kr;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
kern_return_t Inspector::ReadMessages() {
|
||||
// Wait for an initial message from the crashed process containing basic
|
||||
// information about the crash.
|
||||
ReceivePort receive_port(service_rcv_port_);
|
||||
|
||||
MachReceiveMessage message;
|
||||
kern_return_t result = receive_port.WaitForMessage(&message, 1000);
|
||||
|
||||
if (result == KERN_SUCCESS) {
|
||||
InspectorInfo &info = (InspectorInfo &)*message.GetData();
|
||||
exception_type_ = info.exception_type;
|
||||
exception_code_ = info.exception_code;
|
||||
exception_subcode_ = info.exception_subcode;
|
||||
|
||||
#if VERBOSE
|
||||
printf("message ID = %d\n", message.GetMessageID());
|
||||
#endif
|
||||
|
||||
remote_task_ = message.GetTranslatedPort(0);
|
||||
crashing_thread_ = message.GetTranslatedPort(1);
|
||||
handler_thread_ = message.GetTranslatedPort(2);
|
||||
ack_port_ = message.GetTranslatedPort(3);
|
||||
|
||||
#if VERBOSE
|
||||
printf("exception_type = %d\n", exception_type_);
|
||||
printf("exception_code = %d\n", exception_code_);
|
||||
printf("exception_subcode = %d\n", exception_subcode_);
|
||||
printf("remote_task = %d\n", remote_task_);
|
||||
printf("crashing_thread = %d\n", crashing_thread_);
|
||||
printf("handler_thread = %d\n", handler_thread_);
|
||||
printf("ack_port_ = %d\n", ack_port_);
|
||||
printf("parameter count = %d\n", info.parameter_count);
|
||||
#endif
|
||||
|
||||
// In certain situations where multiple crash requests come
|
||||
// through quickly, we can end up with the mach IPC messages not
|
||||
// coming through correctly. Since we don't know what parameters
|
||||
// we've missed, we can't do much besides abort the crash dump
|
||||
// situation in this case.
|
||||
unsigned int parameters_read = 0;
|
||||
// The initial message contains the number of key value pairs that
|
||||
// we are expected to read.
|
||||
// Read each key/value pair, one mach message per key/value pair.
|
||||
for (unsigned int i = 0; i < info.parameter_count; ++i) {
|
||||
MachReceiveMessage parameter_message;
|
||||
result = receive_port.WaitForMessage(¶meter_message, 1000);
|
||||
|
||||
if(result == KERN_SUCCESS) {
|
||||
KeyValueMessageData &key_value_data =
|
||||
(KeyValueMessageData&)*parameter_message.GetData();
|
||||
// If we get a blank key, make sure we don't increment the
|
||||
// parameter count; in some cases (notably on-demand generation
|
||||
// many times in a short period of time) caused the Mach IPC
|
||||
// messages to not come through correctly.
|
||||
if (strlen(key_value_data.key) == 0) {
|
||||
continue;
|
||||
}
|
||||
parameters_read++;
|
||||
|
||||
config_params_.SetKeyValue(key_value_data.key, key_value_data.value);
|
||||
} else {
|
||||
PRINT_MACH_RESULT(result, "Inspector: key/value message");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parameters_read != info.parameter_count) {
|
||||
DEBUGLOG(stderr, "Only read %d parameters instead of %d, aborting crash "
|
||||
"dump generation.", parameters_read, info.parameter_count);
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Sets keys in the parameters dictionary that are specific to process uptime.
|
||||
// The two we set are process up time, and process crash time.
|
||||
void Inspector::SetCrashTimeParameters() {
|
||||
// Set process uptime parameter
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
char processUptimeString[32], processCrashtimeString[32];
|
||||
const char *processStartTimeString =
|
||||
config_params_.GetValueForKey(BREAKPAD_PROCESS_START_TIME);
|
||||
|
||||
// Set up time if we've received the start time.
|
||||
if (processStartTimeString) {
|
||||
time_t processStartTime = strtol(processStartTimeString, NULL, 10);
|
||||
time_t processUptime = tv.tv_sec - processStartTime;
|
||||
sprintf(processUptimeString, "%zd", processUptime);
|
||||
config_params_.SetKeyValue(BREAKPAD_PROCESS_UP_TIME, processUptimeString);
|
||||
}
|
||||
|
||||
sprintf(processCrashtimeString, "%zd", tv.tv_sec);
|
||||
config_params_.SetKeyValue(BREAKPAD_PROCESS_CRASH_TIME,
|
||||
processCrashtimeString);
|
||||
}
|
||||
|
||||
bool Inspector::InspectTask() {
|
||||
// keep the task quiet while we're looking at it
|
||||
task_suspend(remote_task_);
|
||||
DEBUGLOG(stderr, "Suspended Remote task\n");
|
||||
|
||||
NSString *minidumpDir;
|
||||
|
||||
const char *minidumpDirectory =
|
||||
config_params_.GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
|
||||
|
||||
SetCrashTimeParameters();
|
||||
// If the client app has not specified a minidump directory,
|
||||
// use a default of Library/<kDefaultLibrarySubdirectory>/<Product Name>
|
||||
if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) {
|
||||
NSArray *libraryDirectories =
|
||||
NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
|
||||
NSUserDomainMask,
|
||||
YES);
|
||||
|
||||
NSString *applicationSupportDirectory =
|
||||
[libraryDirectories objectAtIndex:0];
|
||||
NSString *library_subdirectory = [NSString
|
||||
stringWithUTF8String:kDefaultLibrarySubdirectory];
|
||||
NSString *breakpad_product = [NSString
|
||||
stringWithUTF8String:config_params_.GetValueForKey(BREAKPAD_PRODUCT)];
|
||||
|
||||
NSArray *path_components = [NSArray
|
||||
arrayWithObjects:applicationSupportDirectory,
|
||||
library_subdirectory,
|
||||
breakpad_product,
|
||||
nil];
|
||||
|
||||
minidumpDir = [NSString pathWithComponents:path_components];
|
||||
} else {
|
||||
minidumpDir = [[NSString stringWithUTF8String:minidumpDirectory]
|
||||
stringByExpandingTildeInPath];
|
||||
}
|
||||
DEBUGLOG(stderr,
|
||||
"Writing minidump to directory (%s)\n",
|
||||
[minidumpDir UTF8String]);
|
||||
|
||||
MinidumpLocation minidumpLocation(minidumpDir);
|
||||
|
||||
// Obscure bug alert:
|
||||
// Don't use [NSString stringWithFormat] to build up the path here since it
|
||||
// assumes system encoding and in RTL locales will prepend an LTR override
|
||||
// character for paths beginning with '/' which fileSystemRepresentation does
|
||||
// not remove. Filed as rdar://6889706 .
|
||||
NSString *path_ns = [NSString
|
||||
stringWithUTF8String:minidumpLocation.GetPath()];
|
||||
NSString *pathid_ns = [NSString
|
||||
stringWithUTF8String:minidumpLocation.GetID()];
|
||||
NSString *minidumpPath = [path_ns stringByAppendingPathComponent:pathid_ns];
|
||||
minidumpPath = [minidumpPath
|
||||
stringByAppendingPathExtension:@"dmp"];
|
||||
|
||||
DEBUGLOG(stderr,
|
||||
"minidump path (%s)\n",
|
||||
[minidumpPath UTF8String]);
|
||||
|
||||
|
||||
config_file_.WriteFile( &config_params_,
|
||||
minidumpLocation.GetPath(),
|
||||
minidumpLocation.GetID());
|
||||
|
||||
|
||||
MinidumpGenerator generator(remote_task_, handler_thread_);
|
||||
|
||||
if (exception_type_ && exception_code_) {
|
||||
generator.SetExceptionInformation(exception_type_,
|
||||
exception_code_,
|
||||
exception_subcode_,
|
||||
crashing_thread_);
|
||||
}
|
||||
|
||||
|
||||
bool result = generator.Write([minidumpPath fileSystemRepresentation]);
|
||||
|
||||
if (result) {
|
||||
DEBUGLOG(stderr, "Wrote minidump - OK\n");
|
||||
} else {
|
||||
DEBUGLOG(stderr, "Error writing minidump - errno=%s\n", strerror(errno));
|
||||
}
|
||||
|
||||
// let the task continue
|
||||
task_resume(remote_task_);
|
||||
DEBUGLOG(stderr, "Resumed remote task\n");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// The crashed task needs to be told that the inspection has finished.
|
||||
// It will wait on a mach port (with timeout) until we send acknowledgement.
|
||||
kern_return_t Inspector::SendAcknowledgement() {
|
||||
if (ack_port_ != MACH_PORT_DEAD) {
|
||||
MachPortSender sender(ack_port_);
|
||||
MachSendMessage ack_message(kMsgType_InspectorAcknowledgement);
|
||||
|
||||
DEBUGLOG(stderr, "Inspector: trying to send acknowledgement to port %d\n",
|
||||
ack_port_);
|
||||
|
||||
kern_return_t result = sender.SendMessage(ack_message, 2000);
|
||||
|
||||
#if VERBOSE
|
||||
PRINT_MACH_RESULT(result, "Inspector: sent acknowledgement");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DEBUGLOG(stderr, "Inspector: port translation failure!\n");
|
||||
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);
|
||||
DEBUGLOG(stderr, "reporter path = %s\n", reporterExecutablePath);
|
||||
|
||||
// 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
|
||||
DEBUGLOG(stderr, "Inspector: unable to launch reporter app\n");
|
||||
_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) {
|
||||
DEBUGLOG(stderr, "Inspector: waitpid error (%d) waiting for reporter app\n",
|
||||
errno);
|
||||
break;
|
||||
} else {
|
||||
// child has finished
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
65
thirdparty/breakpad/client/mac/crash_generation/InspectorMain.mm
vendored
Normal file
65
thirdparty/breakpad/client/mac/crash_generation/InspectorMain.mm
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2007, 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.
|
||||
//
|
||||
// Main driver for Inspector
|
||||
|
||||
#import "client/mac/crash_generation/Inspector.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
//=============================================================================
|
||||
extern "C" {
|
||||
|
||||
int main(int argc, char *const argv[]) {
|
||||
#if DEBUG
|
||||
// Since we're launched on-demand, this is necessary to see debugging
|
||||
// output in the console window.
|
||||
freopen("/dev/console", "w", stdout);
|
||||
freopen("/dev/console", "w", stderr);
|
||||
#endif
|
||||
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
if (argc != 2) {
|
||||
exit(0);
|
||||
}
|
||||
// Our first command-line argument contains the name of the service
|
||||
// that we're providing.
|
||||
google_breakpad::Inspector inspector;
|
||||
inspector.Inspect(argv[1]);
|
||||
|
||||
[pool release];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
} // namespace google_breakpad
|
47
thirdparty/breakpad/client/mac/crash_generation/client_info.h
vendored
Normal file
47
thirdparty/breakpad/client/mac/crash_generation/client_info.h
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2010 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 CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
|
||||
#define CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ClientInfo {
|
||||
public:
|
||||
explicit ClientInfo(pid_t pid) : pid_(pid) {}
|
||||
|
||||
pid_t pid() const { return pid_; }
|
||||
|
||||
private:
|
||||
pid_t pid_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
|
72
thirdparty/breakpad/client/mac/crash_generation/crash_generation_client.cc
vendored
Normal file
72
thirdparty/breakpad/client/mac/crash_generation/crash_generation_client.cc
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2010 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 "client/mac/crash_generation/crash_generation_client.h"
|
||||
|
||||
#include "client/mac/crash_generation/crash_generation_server.h"
|
||||
#include "common/mac/MachIPC.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
bool CrashGenerationClient::RequestDumpForException(
|
||||
int exception_type,
|
||||
int exception_code,
|
||||
int exception_subcode,
|
||||
mach_port_t crashing_thread) {
|
||||
// The server will send a message to this port indicating that it
|
||||
// has finished its work.
|
||||
ReceivePort acknowledge_port;
|
||||
|
||||
MachSendMessage message(kDumpRequestMessage);
|
||||
message.AddDescriptor(mach_task_self()); // this task
|
||||
message.AddDescriptor(crashing_thread); // crashing thread
|
||||
message.AddDescriptor(mach_thread_self()); // handler thread
|
||||
message.AddDescriptor(acknowledge_port.GetPort()); // message receive port
|
||||
|
||||
ExceptionInfo info;
|
||||
info.exception_type = exception_type;
|
||||
info.exception_code = exception_code;
|
||||
info.exception_subcode = exception_subcode;
|
||||
message.SetData(&info, sizeof(info));
|
||||
|
||||
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
|
||||
kern_return_t result = sender_.SendMessage(message, kSendTimeoutMs);
|
||||
if (result != KERN_SUCCESS)
|
||||
return false;
|
||||
|
||||
// Give the server slightly longer to reply since it has to
|
||||
// inspect this task and write the minidump.
|
||||
const mach_msg_timeout_t kReceiveTimeoutMs = 5 * 1000;
|
||||
MachReceiveMessage acknowledge_message;
|
||||
result = acknowledge_port.WaitForMessage(&acknowledge_message,
|
||||
kReceiveTimeoutMs);
|
||||
return result == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
65
thirdparty/breakpad/client/mac/crash_generation/crash_generation_client.h
vendored
Normal file
65
thirdparty/breakpad/client/mac/crash_generation/crash_generation_client.h
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2010 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 GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
||||
#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
||||
|
||||
#include "common/mac/MachIPC.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class CrashGenerationClient {
|
||||
public:
|
||||
explicit CrashGenerationClient(const char* mach_port_name)
|
||||
: sender_(mach_port_name) {
|
||||
}
|
||||
|
||||
// Request the crash server to generate a dump.
|
||||
//
|
||||
// Return true if the dump was successful; false otherwise.
|
||||
bool RequestDumpForException(int exception_type,
|
||||
int exception_code,
|
||||
int exception_subcode,
|
||||
mach_port_t crashing_thread);
|
||||
|
||||
bool RequestDump() {
|
||||
return RequestDumpForException(0, 0, 0, MACH_PORT_NULL);
|
||||
}
|
||||
|
||||
private:
|
||||
MachPortSender sender_;
|
||||
|
||||
// Prevent copy construction and assignment.
|
||||
CrashGenerationClient(const CrashGenerationClient&);
|
||||
CrashGenerationClient& operator=(const CrashGenerationClient&);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
160
thirdparty/breakpad/client/mac/crash_generation/crash_generation_server.cc
vendored
Normal file
160
thirdparty/breakpad/client/mac/crash_generation/crash_generation_server.cc
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
// Copyright (c) 2010 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 "client/mac/crash_generation/crash_generation_server.h"
|
||||
|
||||
#include "client/mac/crash_generation/client_info.h"
|
||||
#include "client/mac/handler/minidump_generator.h"
|
||||
#include "common/mac/scoped_task_suspend-inl.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
CrashGenerationServer::CrashGenerationServer(
|
||||
const char *mach_port_name,
|
||||
OnClientDumpRequestCallback dump_callback,
|
||||
void *dump_context,
|
||||
OnClientExitingCallback exit_callback,
|
||||
void *exit_context,
|
||||
bool generate_dumps,
|
||||
const std::string &dump_path)
|
||||
: dump_callback_(dump_callback),
|
||||
dump_context_(dump_context),
|
||||
exit_callback_(exit_callback),
|
||||
exit_context_(exit_context),
|
||||
generate_dumps_(generate_dumps),
|
||||
dump_dir_(dump_path.empty() ? "/tmp" : dump_path),
|
||||
started_(false),
|
||||
receive_port_(mach_port_name),
|
||||
mach_port_name_(mach_port_name) {
|
||||
}
|
||||
|
||||
CrashGenerationServer::~CrashGenerationServer() {
|
||||
if (started_)
|
||||
Stop();
|
||||
}
|
||||
|
||||
bool CrashGenerationServer::Start() {
|
||||
int thread_create_result = pthread_create(&server_thread_, NULL,
|
||||
&WaitForMessages, this);
|
||||
started_ = thread_create_result == 0;
|
||||
return started_;
|
||||
}
|
||||
|
||||
bool CrashGenerationServer::Stop() {
|
||||
if (!started_)
|
||||
return false;
|
||||
|
||||
// Send a quit message to the background thread, and then join it.
|
||||
MachPortSender sender(mach_port_name_.c_str());
|
||||
MachSendMessage quit_message(kQuitMessage);
|
||||
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
|
||||
kern_return_t result = sender.SendMessage(quit_message, kSendTimeoutMs);
|
||||
if (result == KERN_SUCCESS) {
|
||||
int thread_join_result = pthread_join(server_thread_, NULL);
|
||||
started_ = thread_join_result != 0;
|
||||
}
|
||||
|
||||
return !started_;
|
||||
}
|
||||
|
||||
// static
|
||||
void *CrashGenerationServer::WaitForMessages(void *server) {
|
||||
CrashGenerationServer *self =
|
||||
reinterpret_cast<CrashGenerationServer*>(server);
|
||||
while (self->WaitForOneMessage()) {}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CrashGenerationServer::WaitForOneMessage() {
|
||||
MachReceiveMessage message;
|
||||
kern_return_t result = receive_port_.WaitForMessage(&message,
|
||||
MACH_MSG_TIMEOUT_NONE);
|
||||
if (result == KERN_SUCCESS) {
|
||||
switch (message.GetMessageID()) {
|
||||
case kDumpRequestMessage: {
|
||||
ExceptionInfo &info = (ExceptionInfo &)*message.GetData();
|
||||
|
||||
mach_port_t remote_task = message.GetTranslatedPort(0);
|
||||
mach_port_t crashing_thread = message.GetTranslatedPort(1);
|
||||
mach_port_t handler_thread = message.GetTranslatedPort(2);
|
||||
mach_port_t ack_port = message.GetTranslatedPort(3);
|
||||
pid_t remote_pid = -1;
|
||||
pid_for_task(remote_task, &remote_pid);
|
||||
ClientInfo client(remote_pid);
|
||||
|
||||
bool result;
|
||||
std::string dump_path;
|
||||
if (generate_dumps_) {
|
||||
ScopedTaskSuspend suspend(remote_task);
|
||||
|
||||
MinidumpGenerator generator(remote_task, handler_thread);
|
||||
dump_path = generator.UniqueNameInDirectory(dump_dir_, NULL);
|
||||
|
||||
if (info.exception_type && info.exception_code) {
|
||||
generator.SetExceptionInformation(info.exception_type,
|
||||
info.exception_code,
|
||||
info.exception_subcode,
|
||||
crashing_thread);
|
||||
}
|
||||
result = generator.Write(dump_path.c_str());
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
|
||||
if (result && dump_callback_) {
|
||||
dump_callback_(dump_context_, client, dump_path);
|
||||
}
|
||||
|
||||
// TODO(ted): support a way for the client to send additional data,
|
||||
// perhaps with a callback so users of the server can read the data
|
||||
// themselves?
|
||||
|
||||
if (ack_port != MACH_PORT_DEAD && ack_port != MACH_PORT_NULL) {
|
||||
MachPortSender sender(ack_port);
|
||||
MachSendMessage ack_message(kAcknowledgementMessage);
|
||||
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
|
||||
|
||||
sender.SendMessage(ack_message, kSendTimeoutMs);
|
||||
}
|
||||
|
||||
if (exit_callback_) {
|
||||
exit_callback_(exit_context_, client);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kQuitMessage:
|
||||
return false;
|
||||
}
|
||||
} else { // result != KERN_SUCCESS
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
141
thirdparty/breakpad/client/mac/crash_generation/crash_generation_server.h
vendored
Normal file
141
thirdparty/breakpad/client/mac/crash_generation/crash_generation_server.h
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2010 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 GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
|
||||
#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/mac/MachIPC.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ClientInfo;
|
||||
|
||||
// Messages the server can read via its mach port
|
||||
enum {
|
||||
kDumpRequestMessage = 1,
|
||||
kAcknowledgementMessage = 2,
|
||||
kQuitMessage = 3
|
||||
};
|
||||
|
||||
// Exception details sent by the client when requesting a dump.
|
||||
struct ExceptionInfo {
|
||||
int32_t exception_type;
|
||||
int32_t exception_code;
|
||||
int32_t exception_subcode;
|
||||
};
|
||||
|
||||
class CrashGenerationServer {
|
||||
public:
|
||||
// WARNING: callbacks may be invoked on a different thread
|
||||
// than that which creates the CrashGenerationServer. They must
|
||||
// be thread safe.
|
||||
typedef void (*OnClientDumpRequestCallback)(void *context,
|
||||
const ClientInfo &client_info,
|
||||
const std::string &file_path);
|
||||
|
||||
typedef void (*OnClientExitingCallback)(void *context,
|
||||
const ClientInfo &client_info);
|
||||
|
||||
// Create an instance with the given parameters.
|
||||
//
|
||||
// mach_port_name: Named server port to listen on.
|
||||
// dump_callback: Callback for a client crash dump request.
|
||||
// dump_context: Context for client crash dump request callback.
|
||||
// exit_callback: Callback for client process exit.
|
||||
// exit_context: Context for client exit callback.
|
||||
// generate_dumps: Whether to automatically generate dumps.
|
||||
// Client code of this class might want to generate dumps explicitly
|
||||
// in the crash dump request callback. In that case, false can be
|
||||
// passed for this parameter.
|
||||
// dump_path: Path for generating dumps; required only if true is
|
||||
// passed for generateDumps parameter; NULL can be passed otherwise.
|
||||
CrashGenerationServer(const char *mach_port_name,
|
||||
OnClientDumpRequestCallback dump_callback,
|
||||
void *dump_context,
|
||||
OnClientExitingCallback exit_callback,
|
||||
void *exit_context,
|
||||
bool generate_dumps,
|
||||
const std::string &dump_path);
|
||||
|
||||
~CrashGenerationServer();
|
||||
|
||||
// Perform initialization steps needed to start listening to clients.
|
||||
//
|
||||
// Return true if initialization is successful; false otherwise.
|
||||
bool Start();
|
||||
|
||||
// Stop the server.
|
||||
bool Stop();
|
||||
|
||||
private:
|
||||
// Return a unique filename at which a minidump can be written.
|
||||
bool MakeMinidumpFilename(std::string &outFilename);
|
||||
|
||||
// Loop reading client messages and responding to them until
|
||||
// a quit message is received.
|
||||
static void *WaitForMessages(void *server);
|
||||
|
||||
// Wait for a single client message and respond to it. Returns false
|
||||
// if a quit message was received or if an error occurred.
|
||||
bool WaitForOneMessage();
|
||||
|
||||
OnClientDumpRequestCallback dump_callback_;
|
||||
void *dump_context_;
|
||||
|
||||
OnClientExitingCallback exit_callback_;
|
||||
void *exit_context_;
|
||||
|
||||
bool generate_dumps_;
|
||||
|
||||
std::string dump_dir_;
|
||||
|
||||
bool started_;
|
||||
|
||||
// The mach port that receives requests to dump from child processes.
|
||||
ReceivePort receive_port_;
|
||||
|
||||
// The name of the mach port. Stored so the Stop method can message
|
||||
// the background thread to shut it down.
|
||||
std::string mach_port_name_;
|
||||
|
||||
// The thread that waits on the receive port.
|
||||
pthread_t server_thread_;
|
||||
|
||||
// Disable copy constructor and operator=.
|
||||
CrashGenerationServer(const CrashGenerationServer&);
|
||||
CrashGenerationServer& operator=(const CrashGenerationServer&);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
|
Reference in New Issue
Block a user