bug 587747 - Implement writing of minidumps from hang detection on OS X. r=cjones a=blocking

--HG--
extra : rebase_source : fef081b5b0fae828ecc0c2678d83787778dfc70d
This commit is contained in:
Ted Mielczarek 2010-08-27 09:32:45 -04:00
Родитель 08204a7ca2
Коммит 201396dcf8
10 изменённых файлов: 335 добавлений и 153 удалений

Просмотреть файл

@ -233,7 +233,13 @@ PluginModuleParent::ShouldContinueFromReplyTimeout()
#ifdef MOZ_CRASHREPORTER
nsCOMPtr<nsILocalFile> pluginDump;
nsCOMPtr<nsILocalFile> browserDump;
if (CrashReporter::CreatePairedMinidumps(OtherProcess(),
CrashReporter::ProcessHandle child;
#ifdef XP_MACOSX
child = mSubprocess->GetChildTask();
#else
child = OtherProcess();
#endif
if (CrashReporter::CreatePairedMinidumps(child,
mPluginThread,
&mHangID,
getter_AddRefs(pluginDump),

Просмотреть файл

@ -54,7 +54,7 @@ namespace mozilla {
namespace plugins {
//-----------------------------------------------------------------------------
class PluginProcessParent : mozilla::ipc::GeckoChildProcessHost
class PluginProcessParent : public mozilla::ipc::GeckoChildProcessHost
{
public:
PluginProcessParent(const std::string& aPluginFilePath);

Просмотреть файл

@ -17,6 +17,8 @@
#include <dirent.h>
#include <limits.h>
#include <sys/types.h>
#elif defined(OS_MACOSX)
#include <mach/mach.h>
#endif
#include <map>
@ -129,12 +131,28 @@ bool LaunchApp(const std::wstring& cmdline,
//
// Note that the first argument in argv must point to the filename,
// and must be fully specified.
#ifdef OS_MACOSX
typedef std::vector<std::pair<int, int> > file_handle_mapping_vector;
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
bool wait, ProcessHandle* process_handle,
task_t* task_handle);
#if defined(OS_LINUX) || defined(OS_MACOSX)
typedef std::map<std::string, std::string> environment_map;
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
const environment_map& env_vars_to_set,
bool wait, ProcessHandle* process_handle,
task_t* task_handle);
#endif
#else // !OS_MACOSX
typedef std::vector<std::pair<int, int> > file_handle_mapping_vector;
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
bool wait, ProcessHandle* process_handle);
#if defined(CHROMIUM_MOZILLA_BUILD) && (defined(OS_LINUX) || defined(OS_MACOSX))
#if defined(OS_LINUX) || defined(OS_MACOSX)
typedef std::map<std::string, std::string> environment_map;
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
@ -142,6 +160,7 @@ bool LaunchApp(const std::vector<std::string>& argv,
bool wait, ProcessHandle* process_handle);
#endif
#endif
#endif
// Executes the application specified by cl. This function delegates to one
// of the above two platform-specific functions.

Просмотреть файл

@ -16,132 +16,193 @@
#include "base/eintr_wrapper.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "base/time.h"
#include "chrome/common/mach_ipc_mac.h"
namespace base {
#if defined(CHROMIUM_MOZILLA_BUILD)
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
bool wait, ProcessHandle* process_handle) {
return LaunchApp(argv, fds_to_remap, environment_map(),
wait, process_handle);
static std::string MachErrorCode(kern_return_t err) {
return StringPrintf("0x%x %s", err, mach_error_string(err));
}
// Forks the current process and returns the child's |task_t| in the parent
// process.
static pid_t fork_and_get_task(task_t* child_task) {
const int kTimeoutMs = 100;
kern_return_t err;
// Put a random number into the channel name, so that a compromised renderer
// can't pretend being the child that's forked off.
std::string mach_connection_name = StringPrintf(
"org.mozilla.samplingfork.%p.%d",
child_task, base::RandInt(0, std::numeric_limits<int>::max()));
ReceivePort parent_recv_port(mach_connection_name.c_str());
// Error handling philosophy: If Mach IPC fails, don't touch |child_task| but
// return a valid pid. If IPC fails in the child, the parent will have to wait
// until kTimeoutMs is over. This is not optimal, but I've never seen it
// happen, and stuff should still mostly work.
pid_t pid = fork();
switch (pid) {
case -1:
return pid;
case 0: { // child
ReceivePort child_recv_port;
MachSendMessage child_message(/* id= */0);
if (!child_message.AddDescriptor(mach_task_self())) {
LOG(ERROR) << "child AddDescriptor(mach_task_self()) failed.";
return pid;
}
mach_port_t raw_child_recv_port = child_recv_port.GetPort();
if (!child_message.AddDescriptor(raw_child_recv_port)) {
LOG(ERROR) << "child AddDescriptor(" << raw_child_recv_port
<< ") failed.";
return pid;
}
MachPortSender child_sender(mach_connection_name.c_str());
err = child_sender.SendMessage(child_message, kTimeoutMs);
if (err != KERN_SUCCESS) {
LOG(ERROR) << "child SendMessage() failed: " << MachErrorCode(err);
return pid;
}
MachReceiveMessage parent_message;
err = child_recv_port.WaitForMessage(&parent_message, kTimeoutMs);
if (err != KERN_SUCCESS) {
LOG(ERROR) << "child WaitForMessage() failed: " << MachErrorCode(err);
return pid;
}
if (parent_message.GetTranslatedPort(0) == MACH_PORT_NULL) {
LOG(ERROR) << "child GetTranslatedPort(0) failed.";
return pid;
}
err = task_set_bootstrap_port(mach_task_self(),
parent_message.GetTranslatedPort(0));
if (err != KERN_SUCCESS) {
LOG(ERROR) << "child task_set_bootstrap_port() failed: "
<< MachErrorCode(err);
return pid;
}
break;
}
default: { // parent
MachReceiveMessage child_message;
err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs);
if (err != KERN_SUCCESS) {
LOG(ERROR) << "parent WaitForMessage() failed: " << MachErrorCode(err);
return pid;
}
if (child_message.GetTranslatedPort(0) == MACH_PORT_NULL) {
LOG(ERROR) << "parent GetTranslatedPort(0) failed.";
return pid;
}
*child_task = child_message.GetTranslatedPort(0);
if (child_message.GetTranslatedPort(1) == MACH_PORT_NULL) {
LOG(ERROR) << "parent GetTranslatedPort(1) failed.";
return pid;
}
MachPortSender parent_sender(child_message.GetTranslatedPort(1));
MachSendMessage parent_message(/* id= */0);
if (!parent_message.AddDescriptor(bootstrap_port)) {
LOG(ERROR) << "parent AddDescriptor(" << bootstrap_port << ") failed.";
return pid;
}
err = parent_sender.SendMessage(parent_message, kTimeoutMs);
if (err != KERN_SUCCESS) {
LOG(ERROR) << "parent SendMessage() failed: " << MachErrorCode(err);
return pid;
}
break;
}
}
return pid;
}
#endif
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
#if defined(CHROMIUM_MOZILLA_BUILD)
const environment_map& env_vars_to_set,
#endif
bool wait, ProcessHandle* process_handle) {
bool retval = true;
bool wait, ProcessHandle* process_handle,
task_t* process_task) {
return LaunchApp(argv, fds_to_remap, environment_map(),
wait, process_handle, process_task);
}
char* argv_copy[argv.size() + 1];
for (size_t i = 0; i < argv.size(); i++) {
argv_copy[i] = const_cast<char*>(argv[i].c_str());
}
argv_copy[argv.size()] = NULL;
bool LaunchApp(
const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
const environment_map& environ,
bool wait,
ProcessHandle* process_handle,
task_t* task_handle) {
pid_t pid;
// Make sure we don't leak any FDs to the child process by marking all FDs
// as close-on-exec.
SetAllFDsToCloseOnExec();
#if defined(CHROMIUM_MOZILLA_BUILD)
// Copy _NSGetEnviron() to a new char array and add the variables
// in env_vars_to_set.
// Existing variables are overwritten by env_vars_to_set.
int pos = 0;
environment_map combined_env_vars = env_vars_to_set;
while((*_NSGetEnviron())[pos] != NULL) {
std::string varString = (*_NSGetEnviron())[pos];
std::string varName = varString.substr(0, varString.find_first_of('='));
std::string varValue = varString.substr(varString.find_first_of('=') + 1);
if (combined_env_vars.find(varName) == combined_env_vars.end()) {
combined_env_vars[varName] = varValue;
}
pos++;
}
int varsLen = combined_env_vars.size() + 1;
char** vars = new char*[varsLen];
int i = 0;
for (environment_map::const_iterator it = combined_env_vars.begin();
it != combined_env_vars.end(); ++it) {
std::string entry(it->first);
entry += "=";
entry += it->second;
vars[i] = strdup(entry.c_str());
i++;
}
vars[i] = NULL;
#endif
posix_spawn_file_actions_t file_actions;
if (posix_spawn_file_actions_init(&file_actions) != 0) {
#if defined(CHROMIUM_MOZILLA_BUILD)
for(int j = 0; j < varsLen; j++) {
free(vars[j]);
}
delete[] vars;
#endif
return false;
}
// Turn fds_to_remap array into a set of dup2 calls.
for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin();
it != fds_to_remap.end();
++it) {
int src_fd = it->first;
int dest_fd = it->second;
if (src_fd == dest_fd) {
int flags = fcntl(src_fd, F_GETFD);
if (flags != -1) {
fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC);
}
} else {
if (posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd) != 0)
{
posix_spawn_file_actions_destroy(&file_actions);
#if defined(CHROMIUM_MOZILLA_BUILD)
for(int j = 0; j < varsLen; j++) {
free(vars[j]);
}
delete[] vars;
#endif
return false;
}
}
}
int pid = 0;
int spawn_succeeded = (posix_spawnp(&pid,
argv_copy[0],
&file_actions,
NULL,
argv_copy,
#if defined(CHROMIUM_MOZILLA_BUILD)
vars) == 0);
#else
*_NSGetEnviron()) == 0);
#endif
#if defined(CHROMIUM_MOZILLA_BUILD)
for(int j = 0; j < varsLen; j++) {
free(vars[j]);
}
delete[] vars;
#endif
posix_spawn_file_actions_destroy(&file_actions);
bool process_handle_valid = pid > 0;
if (!spawn_succeeded || !process_handle_valid) {
retval = false;
if (task_handle == NULL) {
pid = fork();
} else {
// On OS X, the task_t for a process is needed for several reasons. Sadly,
// the function task_for_pid() requires privileges a normal user doesn't
// have. Instead, a short-lived Mach IPC connection is opened between parent
// and child, and the child sends its task_t to the parent at fork time.
*task_handle = MACH_PORT_NULL;
pid = fork_and_get_task(task_handle);
}
if (pid < 0)
return false;
if (pid == 0) {
// Child process
InjectiveMultimap fd_shuffle;
for (file_handle_mapping_vector::const_iterator
it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
fd_shuffle.push_back(InjectionArc(it->first, it->second, false));
}
for (environment_map::const_iterator it = environ.begin();
it != environ.end(); ++it) {
if (it->first.empty())
continue;
if (it->second.empty()) {
unsetenv(it->first.c_str());
} else {
setenv(it->first.c_str(), it->second.c_str(), 1);
}
}
// Obscure fork() rule: in the child, if you don't end up doing exec*(),
// you call _exit() instead of exit(). This is because _exit() does not
// call any previously-registered (in the parent) exit handlers, which
// might do things like block waiting for threads that don't even exist
// in the child.
if (!ShuffleFileDescriptors(fd_shuffle))
_exit(127);
// If we are using the SUID sandbox, it sets a magic environment variable
// ("SBX_D"), so we remove that variable from the environment here on the
// off chance that it's already set.
unsetenv("SBX_D");
CloseSuperfluousFds(fd_shuffle);
scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
for (size_t i = 0; i < argv.size(); i++)
argv_cstr[i] = const_cast<char*>(argv[i].c_str());
argv_cstr[argv.size()] = NULL;
execvp(argv_cstr[0], argv_cstr.get());
_exit(127);
} else {
// Parent process
if (wait)
HANDLE_EINTR(waitpid(pid, 0, 0));
@ -149,14 +210,14 @@ bool LaunchApp(const std::vector<std::string>& argv,
*process_handle = pid;
}
return retval;
return true;
}
bool LaunchApp(const CommandLine& cl,
bool wait, bool start_hidden, ProcessHandle* process_handle) {
// TODO(playmobil): Do we need to respect the start_hidden flag?
file_handle_mapping_vector no_files;
return LaunchApp(cl.argv(), no_files, wait, process_handle);
return LaunchApp(cl.argv(), no_files, wait, process_handle, NULL);
}
NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,

Просмотреть файл

@ -83,6 +83,9 @@ GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType,
mChannelInitialized(false),
mDelegate(aDelegate),
mChildProcessHandle(0)
#if defined(XP_MACOSX)
, mChildTask(MACH_PORT_NULL)
#endif
{
MOZ_COUNT_CTOR(GeckoChildProcessHost);
@ -105,6 +108,11 @@ GeckoChildProcessHost::~GeckoChildProcessHost()
, false // don't "force"
#endif
);
#if defined(XP_MACOSX)
if (mChildTask != MACH_PORT_NULL)
mach_port_deallocate(mach_task_self(), mChildTask);
#endif
}
#ifdef XP_WIN
@ -216,6 +224,9 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts)
}
base::ProcessHandle process;
#if defined(XP_MACOSX)
task_t child_task;
#endif
// send the child the PID so that it can open a ProcessHandle back to us.
// probably don't want to do this in the long run
@ -326,7 +337,11 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts)
#if defined(OS_LINUX) || defined(OS_MACOSX)
newEnvVars,
#endif
false, &process);
false, &process
#if defined(XP_MACOSX)
, &child_task
#endif
);
//--------------------------------------------------
#elif defined(OS_WIN)
@ -376,6 +391,9 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts)
return false;
}
SetHandle(process);
#if defined(XP_MACOSX)
mChildTask = child_task;
#endif
return true;
}

Просмотреть файл

@ -90,6 +90,13 @@ public:
return mChildProcessHandle;
}
#ifdef XP_MACOSX
task_t GetChildTask() {
return mChildTask;
}
#endif
protected:
GeckoProcessType mProcessType;
Monitor mMonitor;
@ -109,6 +116,9 @@ protected:
base::WaitableEventWatcher::Delegate* mDelegate;
ProcessHandle mChildProcessHandle;
#if defined(OS_MACOSX)
task_t mChildTask;
#endif
private:
DISALLOW_EVIL_CONSTRUCTORS(GeckoChildProcessHost);

Просмотреть файл

@ -53,6 +53,14 @@ namespace google_breakpad {
using std::map;
// Message ID telling the handler thread to write a dump.
static const mach_msg_id_t kWriteDumpMessage = 0;
// Message ID telling the handler thread to quit.
static const mach_msg_id_t kQuitMessage = 1;
// Message ID telling the handler thread to write a dump and include
// an exception stream.
static const mach_msg_id_t kWriteDumpWithExceptionMessage = 2;
// These structures and techniques are illustrated in
// Mac OS X Internals, Amit Singh, ch 9.7
struct ExceptionMessage {
@ -269,7 +277,7 @@ ExceptionHandler::~ExceptionHandler() {
Teardown();
}
bool ExceptionHandler::WriteMinidump() {
bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
// If we're currently writing, just return
if (use_minidump_write_mutex_)
return false;
@ -281,7 +289,9 @@ bool ExceptionHandler::WriteMinidump() {
if (pthread_mutex_lock(&minidump_write_mutex_) == 0) {
// Send an empty message to the handle port so that a minidump will
// be written
SendEmptyMachMessage();
SendMessageToHandlerThread(write_exception_stream ?
kWriteDumpWithExceptionMessage
: kWriteDumpMessage);
// Wait for the minidump writer to complete its writing. It will unlock
// the mutex when completed
@ -295,11 +305,12 @@ bool ExceptionHandler::WriteMinidump() {
// static
bool ExceptionHandler::WriteMinidump(const string &dump_path,
bool write_exception_stream,
MinidumpCallback callback,
void *callback_context) {
ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
NULL);
return handler.WriteMinidump();
return handler.WriteMinidump(write_exception_stream);
}
// static
@ -336,7 +347,8 @@ bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
int exception_code,
int exception_subcode,
mach_port_t thread_name) {
mach_port_t thread_name,
bool exit_after_write) {
bool result = false;
if (directCallback_) {
@ -345,7 +357,7 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
exception_code,
exception_subcode,
thread_name) ) {
if (exception_type && exception_code)
if (exit_after_write)
_exit(exception_type);
}
} else if (IsOutOfProcess()) {
@ -387,7 +399,7 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
// forwarding the exception to the next handler.
if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
result)) {
if (exception_type && exception_code)
if (exit_after_write)
_exit(exception_type);
}
}
@ -522,7 +534,9 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
// to avoid misleading stacks. If appropriate they will be resumed
// afterwards.
if (!receive.exception) {
if (self->is_in_teardown_)
// Don't touch self, since this message could have been sent
// from its destructor.
if (receive.header.msgh_id == kQuitMessage)
return NULL;
self->SuspendThreads();
@ -532,9 +546,26 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
gBreakpadAllocator->Unprotect();
#endif
mach_port_t thread = MACH_PORT_NULL;
int exception_type = 0;
int exception_code = 0;
if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
thread = receive.thread.name;
exception_type = EXC_BREAKPOINT;
#if defined (__i386__) || defined(__x86_64__)
exception_code = EXC_I386_BPT;
#elif defined (__ppc__) || defined (__ppc64__)
exception_code = EXC_PPC_BREAKPOINT;
#else
#error architecture not supported
#endif
}
// Write out the dump and save the result for later retrieval
self->last_minidump_write_result_ =
self->WriteMinidumpWithException(0, 0, 0, 0);
self->WriteMinidumpWithException(exception_type, exception_code,
0, thread,
exception_type != EXC_BREAKPOINT);
self->UninstallHandler(false);
@ -570,7 +601,7 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
// Generate the minidump with the exception data.
self->WriteMinidumpWithException(receive.exception, receive.code[0],
subcode, receive.thread.name);
subcode, receive.thread.name, true);
self->UninstallHandler(true);
@ -705,7 +736,7 @@ bool ExceptionHandler::Teardown() {
return false;
// Send an empty message so that the handler_thread exits
if (SendEmptyMachMessage()) {
if (SendMessageToHandlerThread(kQuitMessage)) {
mach_port_t current_task = mach_task_self();
result = mach_port_deallocate(current_task, handler_port_);
if (result != KERN_SUCCESS)
@ -721,16 +752,24 @@ bool ExceptionHandler::Teardown() {
return result == KERN_SUCCESS;
}
bool ExceptionHandler::SendEmptyMachMessage() {
ExceptionMessage empty;
memset(&empty, 0, sizeof(empty));
empty.header.msgh_size = sizeof(empty) - sizeof(empty.padding);
empty.header.msgh_remote_port = handler_port_;
empty.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
bool ExceptionHandler::SendMessageToHandlerThread(mach_msg_id_t message_id) {
ExceptionMessage msg;
memset(&msg, 0, sizeof(msg));
msg.header.msgh_id = message_id;
if (message_id == kWriteDumpMessage ||
message_id == kWriteDumpWithExceptionMessage) {
// Include this thread's port.
msg.thread.name = mach_thread_self();
msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND;
msg.thread.type = MACH_MSG_PORT_DESCRIPTOR;
}
msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding);
msg.header.msgh_remote_port = handler_port_;
msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
MACH_MSG_TYPE_MAKE_SEND_ONCE);
kern_return_t result = mach_msg(&(empty.header),
kern_return_t result = mach_msg(&(msg.header),
MACH_SEND_MSG | MACH_SEND_TIMEOUT,
empty.header.msgh_size, 0, 0,
msg.header.msgh_size, 0, 0,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
return result == KERN_SUCCESS;

Просмотреть файл

@ -114,11 +114,22 @@ class ExceptionHandler {
// Writes a minidump immediately. This can be used to capture the
// execution state independently of a crash. Returns true on success.
bool WriteMinidump();
bool WriteMinidump() {
return WriteMinidump(false);
}
bool WriteMinidump(bool write_exception_stream);
// Convenience form of WriteMinidump which does not require an
// ExceptionHandler instance.
static bool WriteMinidump(const string &dump_path, MinidumpCallback callback,
void *callback_context) {
return WriteMinidump(dump_path, false, callback, callback_context);
}
static bool WriteMinidump(const string &dump_path,
bool write_exception_stream,
MinidumpCallback callback,
void *callback_context);
// Write a minidump of child immediately. This can be used to capture
@ -149,13 +160,14 @@ class ExceptionHandler {
// thread
bool Teardown();
// Send an "empty" mach message to the exception handler. Return true on
// success, false otherwise
bool SendEmptyMachMessage();
// Send a mach message to the exception handler. Return true on
// success, false otherwise.
bool SendMessageToHandlerThread(mach_msg_id_t message_id);
// All minidump writing goes through this one routine
bool WriteMinidumpWithException(int exception_type, int exception_code,
int exception_subcode, mach_port_t thread_name);
int exception_subcode, mach_port_t thread_name,
bool exit_after_write);
// When installed, this static function will be call from a newly created
// pthread with |this| as the argument

Просмотреть файл

@ -61,6 +61,7 @@
#include <Carbon/Carbon.h>
#include <CoreFoundation/CoreFoundation.h>
#include <fcntl.h>
#include <mach/mach.h>
#include <sys/types.h>
#include <unistd.h>
#include "mac_utils.h"
@ -1763,7 +1764,17 @@ CurrentThreadId()
#elif defined(XP_LINUX)
return sys_gettid();
#elif defined(XP_MACOSX)
return mach_thread_self();
// Just return an index, since Mach ports can't be directly serialized
thread_act_port_array_t threads_for_task;
mach_msg_type_number_t thread_count;
if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
return -1;
for (unsigned int i = 0; i < thread_count; ++i) {
if (threads_for_task[i] == mach_thread_self())
return i;
}
#else
# error "Unsupported platform"
#endif
@ -1776,9 +1787,6 @@ CreatePairedMinidumps(ProcessHandle childPid,
nsILocalFile** childDump,
nsILocalFile** parentDump)
{
#ifdef XP_MACOSX
return false;
#else
if (!GetEnabled())
return false;
@ -1800,6 +1808,19 @@ CreatePairedMinidumps(ProcessHandle childPid,
pairGUID->Cut(0, 1);
pairGUID->Cut(pairGUID->Length()-1, 1);
#ifdef XP_MACOSX
mach_port_t childThread = MACH_PORT_NULL;
thread_act_port_array_t threads_for_task;
mach_msg_type_number_t thread_count;
if (task_threads(childPid, &threads_for_task, &thread_count)
== KERN_SUCCESS && childBlamedThread < thread_count) {
childThread = threads_for_task[childBlamedThread];
}
#else
ThreadId childThread = childBlamedThread;
#endif
// dump the child
nsCOMPtr<nsILocalFile> childMinidump;
nsCOMPtr<nsILocalFile> childExtra;
@ -1809,7 +1830,7 @@ CreatePairedMinidumps(ProcessHandle childPid,
{ &childMinidump, &childExtra, childBlacklist };
if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
childPid,
childBlamedThread,
childThread,
gExceptionHandler->dump_path(),
PairedDumpCallback,
&childCtx))
@ -1824,9 +1845,7 @@ CreatePairedMinidumps(ProcessHandle childPid,
{ &parentMinidump, &parentExtra, parentBlacklist };
if (!google_breakpad::ExceptionHandler::WriteMinidump(
gExceptionHandler->dump_path(),
#ifndef XP_MACOSX
true, // write exception stream
#endif
PairedDumpCallback,
&parentCtx))
return false;
@ -1843,7 +1862,6 @@ CreatePairedMinidumps(ProcessHandle childPid,
parentMinidump.swap(*parentDump);
return true;
#endif
}
bool

Просмотреть файл

@ -102,8 +102,7 @@ bool TakeMinidumpForChild(PRUint32 childPid,
typedef HANDLE ProcessHandle;
typedef DWORD ThreadId;
#elif defined(XP_MACOSX)
//FIXME: mach_port_t
typedef int ProcessHandle;
typedef task_t ProcessHandle;
typedef mach_port_t ThreadId;
#else
typedef int ProcessHandle;