зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
08204a7ca2
Коммит
201396dcf8
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче