diff options
author | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-12 00:39:15 +0000 |
---|---|---|
committer | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-12 00:39:15 +0000 |
commit | c002879632b0e46b11e91e5595a83652f7cafd3c (patch) | |
tree | 87477d9e6b972292fbb98484ec3721f084ab41c4 /base/process_util_posix.cc | |
parent | 62ed4d36e4357deaf863711bdfd1f00b367c426a (diff) | |
download | chromium_src-c002879632b0e46b11e91e5595a83652f7cafd3c.zip chromium_src-c002879632b0e46b11e91e5595a83652f7cafd3c.tar.gz chromium_src-c002879632b0e46b11e91e5595a83652f7cafd3c.tar.bz2 |
Mac: Other approach for IPCing child task_ts.
Also move mach_ipc_mac to base, where it's now used.
Based on http://www.foldr.org/~michaelw/log/2009/03/13/ , but uses a named connection instead. Do the IPC right after fork-time, so that the sandbox is not yet in effect.
See the codereview comments for a benchmark that proves that this shouldn't be expensive, and for pros and cons for using a named connection vs temporarily switching out the bootstrap port.
Works for worker processes too and seems more reliable in general.
Measured perf impact in http://src.chromium.org/viewvc/chrome?view=rev&revision=35888 , it's negligible.
BUG=13156
TEST=(requires that one enables the task manager in browser.cc)
1.) Open one tab that plays a youtube video
2.) Open a second and visit http://www.whatwg.org/demos/workers/primes/page.html
3.) Install e.g. the gmail checker extension
4.) Open the task manager
It should report metrics for
* one browser process
* two renderer processes
* one plugin process
* one worker process
* one extension process
Check that %cpu etc more or less match what Activity Monitor displays if you filter for "Chromium".
Also choose "Open all bookmarks" on the bookmarks bar with the task manager open and check that metrics for all tabs appear immediately.
Review URL: http://codereview.chromium.org/549002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@35977 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/process_util_posix.cc')
-rw-r--r-- | base/process_util_posix.cc | 144 |
1 files changed, 143 insertions, 1 deletions
diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc index 21626732..a771fa2 100644 --- a/base/process_util_posix.cc +++ b/base/process_util_posix.cc @@ -21,11 +21,16 @@ #include "base/logging.h" #include "base/platform_thread.h" #include "base/process_util.h" +#include "base/rand_util.h" #include "base/scoped_ptr.h" #include "base/sys_info.h" #include "base/time.h" #include "base/waitable_event.h" +#if defined(OS_MACOSX) +#include "base/mach_ipc_mac.h" +#endif + const int kMicrosecondsPerSecond = 1000000; namespace base { @@ -280,11 +285,148 @@ void SetAllFDsToCloseOnExec() { } } +#if defined(OS_MACOSX) +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( + "com.google.Chrome.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; +} + bool LaunchApp(const std::vector<std::string>& argv, const environment_vector& environ, const file_handle_mapping_vector& fds_to_remap, bool wait, ProcessHandle* process_handle) { - pid_t pid = fork(); + return LaunchAppAndGetTask( + argv, environ, fds_to_remap, wait, NULL, process_handle); +} +#endif // defined(OS_MACOSX) + +#if defined(OS_MACOSX) +bool LaunchAppAndGetTask( +#else +bool LaunchApp( +#endif + const std::vector<std::string>& argv, + const environment_vector& environ, + const file_handle_mapping_vector& fds_to_remap, + bool wait, +#if defined(OS_MACOSX) + task_t* task_handle, +#endif + ProcessHandle* process_handle) { + pid_t pid; +#if defined(OS_MACOSX) + 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); + } +#else + pid = fork(); +#endif if (pid < 0) return false; |