summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorthakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-12 00:39:15 +0000
committerthakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-12 00:39:15 +0000
commitc002879632b0e46b11e91e5595a83652f7cafd3c (patch)
tree87477d9e6b972292fbb98484ec3721f084ab41c4 /base
parent62ed4d36e4357deaf863711bdfd1f00b367c426a (diff)
downloadchromium_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')
-rw-r--r--base/base.gypi2
-rw-r--r--base/mach_ipc_mac.h322
-rw-r--r--base/mach_ipc_mac.mm308
-rw-r--r--base/process_util.h15
-rw-r--r--base/process_util_posix.cc144
5 files changed, 789 insertions, 2 deletions
diff --git a/base/base.gypi b/base/base.gypi
index fc7c811..f7584c4 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -103,6 +103,8 @@
'logging.h',
'mac_util.h',
'mac_util.mm',
+ 'mach_ipc_mac.h',
+ 'mach_ipc_mac.mm',
'memory_debug.cc',
'memory_debug.h',
'message_loop.cc',
diff --git a/base/mach_ipc_mac.h b/base/mach_ipc_mac.h
new file mode 100644
index 0000000..7e9839c
--- /dev/null
+++ b/base/mach_ipc_mac.h
@@ -0,0 +1,322 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MACH_IPC_MAC_H_
+#define BASE_MACH_IPC_MAC_H_
+
+#include <mach/mach.h>
+#include <mach/message.h>
+#include <servers/bootstrap.h>
+#include <sys/types.h>
+
+#include <CoreServices/CoreServices.h>
+
+#include "base/basictypes.h"
+
+//==============================================================================
+// DISCUSSION:
+//
+// The three main classes of interest are
+//
+// MachMessage: a wrapper for a mach message of the following form
+// mach_msg_header_t
+// mach_msg_body_t
+// optional descriptors
+// optional extra message data
+//
+// MachReceiveMessage and MachSendMessage subclass MachMessage
+// and are used instead of MachMessage which is an abstract base class
+//
+// ReceivePort:
+// Represents a mach port for which we have receive rights
+//
+// MachPortSender:
+// Represents a mach port for which we have send rights
+//
+// Here's an example to receive a message on a server port:
+//
+// // This creates our named server port
+// ReceivePort receivePort("com.Google.MyService");
+//
+// MachReceiveMessage message;
+// kern_return_t result = receivePort.WaitForMessage(&message, 0);
+//
+// if (result == KERN_SUCCESS && message.GetMessageID() == 57) {
+// mach_port_t task = message.GetTranslatedPort(0);
+// mach_port_t thread = message.GetTranslatedPort(1);
+//
+// char *messageString = message.GetData();
+//
+// printf("message string = %s\n", messageString);
+// }
+//
+// Here is an example of using these classes to send a message to this port:
+//
+// // send to already named port
+// MachPortSender sender("com.Google.MyService");
+// MachSendMessage message(57); // our message ID is 57
+//
+// // add some ports to be translated for us
+// message.AddDescriptor(mach_task_self()); // our task
+// message.AddDescriptor(mach_thread_self()); // this thread
+//
+// char messageString[] = "Hello server!\n";
+// message.SetData(messageString, strlen(messageString)+1);
+// // timeout 1000ms
+// kern_return_t result = sender.SendMessage(message, 1000);
+//
+
+#define PRINT_MACH_RESULT(result_, message_) \
+ printf(message_" %s (%d)\n", mach_error_string(result_), result_ );
+
+//==============================================================================
+// A wrapper class for mach_msg_port_descriptor_t (with same memory layout)
+// with convenient constructors and accessors
+class MachMsgPortDescriptor : public mach_msg_port_descriptor_t {
+ public:
+ // General-purpose constructor
+ MachMsgPortDescriptor(mach_port_t in_name,
+ mach_msg_type_name_t in_disposition) {
+ name = in_name;
+ pad1 = 0;
+ pad2 = 0;
+ disposition = in_disposition;
+ type = MACH_MSG_PORT_DESCRIPTOR;
+ }
+
+ // For passing send rights to a port
+ MachMsgPortDescriptor(mach_port_t in_name) {
+ name = in_name;
+ pad1 = 0;
+ pad2 = 0;
+ disposition = MACH_MSG_TYPE_PORT_SEND;
+ type = MACH_MSG_PORT_DESCRIPTOR;
+ }
+
+ // Copy constructor
+ MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) {
+ name = desc.name;
+ pad1 = desc.pad1;
+ pad2 = desc.pad2;
+ disposition = desc.disposition;
+ type = desc.type;
+ }
+
+ mach_port_t GetMachPort() const {
+ return name;
+ }
+
+ mach_msg_type_name_t GetDisposition() const {
+ return disposition;
+ }
+
+ // We're just a simple wrapper for mach_msg_port_descriptor_t
+ // and have the same memory layout
+ operator mach_msg_port_descriptor_t&() {
+ return *this;
+ }
+
+ // For convenience
+ operator mach_port_t() const {
+ return GetMachPort();
+ }
+};
+
+//==============================================================================
+// MachMessage: a wrapper for a mach message
+// (mach_msg_header_t, mach_msg_body_t, extra data)
+//
+// This considerably simplifies the construction of a message for sending
+// and the getting at relevant data and descriptors for the receiver.
+//
+// This class can be initialized using external storage of an arbitrary size
+// or it can manage storage internally.
+// 1. If storage is allocated internally, the combined size of the descriptors
+// plus data must be less than 1024. But as a benefit no memory allocation is
+// necessary.
+// 2. For external storage, a buffer of at least EmptyMessageSize() must be
+// provided.
+//
+// A MachMessage object is used by ReceivePort::WaitForMessage
+// and MachPortSender::SendMessage
+//
+class MachMessage {
+ public:
+
+ virtual ~MachMessage();
+
+ // The receiver of the message can retrieve the raw data this way
+ u_int8_t *GetData() {
+ return GetDataLength() > 0 ? GetDataPacket()->data : NULL;
+ }
+
+ u_int32_t GetDataLength() {
+ return EndianU32_LtoN(GetDataPacket()->data_length);
+ }
+
+ // The message ID may be used as a code identifying the type of message
+ void SetMessageID(int32_t message_id) {
+ GetDataPacket()->id = EndianU32_NtoL(message_id);
+ }
+
+ int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); }
+
+ // Adds a descriptor (typically a mach port) to be translated
+ // returns true if successful, otherwise not enough space
+ bool AddDescriptor(const MachMsgPortDescriptor &desc);
+
+ int GetDescriptorCount() const {
+ return storage_->body.msgh_descriptor_count;
+ }
+
+ MachMsgPortDescriptor *GetDescriptor(int n);
+
+ // Convenience method which gets the mach port described by the descriptor
+ mach_port_t GetTranslatedPort(int n);
+
+ // A simple message is one with no descriptors
+ bool IsSimpleMessage() const { return GetDescriptorCount() == 0; }
+
+ // Sets raw data for the message (returns false if not enough space)
+ bool SetData(const void* data, int32_t data_length);
+
+ protected:
+ // Consider this an abstract base class - must create an actual instance
+ // of MachReceiveMessage or MachSendMessage
+ MachMessage();
+
+ // Constructor for use with preallocate storage.
+ // storage_length must be >= EmptyMessageSize()
+ MachMessage(void *storage, size_t storage_length);
+
+ friend class ReceivePort;
+ friend class MachPortSender;
+
+ // Represents raw data in our message
+ struct MessageDataPacket {
+ int32_t id; // little-endian
+ int32_t data_length; // little-endian
+ u_int8_t data[1]; // actual size limited by storage_length_bytes_
+ };
+
+ MessageDataPacket* GetDataPacket();
+
+ void SetDescriptorCount(int n);
+ void SetDescriptor(int n, const MachMsgPortDescriptor &desc);
+
+ // Returns total message size setting msgh_size in the header to this value
+ int CalculateSize();
+
+ // Returns total storage size that this object can grow to, this is inclusive
+ // of the mach header.
+ size_t MaxSize() const { return storage_length_bytes_; }
+
+ protected:
+ mach_msg_header_t *Head() { return &(storage_->head); }
+
+ private:
+ struct MachMessageData {
+ mach_msg_header_t head;
+ mach_msg_body_t body;
+ // descriptors and data may be embedded here.
+ u_int8_t padding[1024];
+ };
+
+ // kEmptyMessageSize needs to have the definition of MachMessageData before
+ // it.
+ public:
+ // The size of an empty message with no data.
+ static const size_t kEmptyMessageSize = sizeof(mach_msg_header_t) +
+ sizeof(mach_msg_body_t) +
+ sizeof(MessageDataPacket);
+
+ private:
+ MachMessageData *storage_;
+ size_t storage_length_bytes_;
+ bool own_storage_; // Is storage owned by this object?
+};
+
+//==============================================================================
+// MachReceiveMessage and MachSendMessage are useful to separate the idea
+// of a mach message being sent and being received, and adds increased type
+// safety:
+// ReceivePort::WaitForMessage() only accepts a MachReceiveMessage
+// MachPortSender::SendMessage() only accepts a MachSendMessage
+
+//==============================================================================
+class MachReceiveMessage : public MachMessage {
+ public:
+ MachReceiveMessage() : MachMessage() {}
+ MachReceiveMessage(void *storage, size_t storage_length)
+ : MachMessage(storage, storage_length) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MachReceiveMessage);
+};
+
+//==============================================================================
+class MachSendMessage : public MachMessage {
+ public:
+ explicit MachSendMessage(int32_t message_id);
+ MachSendMessage(void *storage, size_t storage_length, int32_t message_id);
+
+ private:
+ void Initialize(int32_t message_id);
+
+ DISALLOW_COPY_AND_ASSIGN(MachSendMessage);
+};
+
+//==============================================================================
+// Represents a mach port for which we have receive rights
+class ReceivePort {
+ public:
+ // Creates a new mach port for receiving messages and registers a name for it
+ explicit ReceivePort(const char *receive_port_name);
+
+ // Given an already existing mach port, use it. We take ownership of the
+ // port and deallocate it in our destructor.
+ explicit ReceivePort(mach_port_t receive_port);
+
+ // Create a new mach port for receiving messages
+ ReceivePort();
+
+ ~ReceivePort();
+
+ // Waits on the mach port until message received or timeout
+ kern_return_t WaitForMessage(MachReceiveMessage *out_message,
+ mach_msg_timeout_t timeout);
+
+ // The underlying mach port that we wrap
+ mach_port_t GetPort() const { return port_; }
+
+ private:
+ mach_port_t port_;
+ kern_return_t init_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReceivePort);
+};
+
+//==============================================================================
+// Represents a mach port for which we have send rights
+class MachPortSender {
+ public:
+ // get a port with send rights corresponding to a named registered service
+ explicit MachPortSender(const char *receive_port_name);
+
+
+ // Given an already existing mach port, use it. Does not take ownership of
+ // |send_port|.
+ explicit MachPortSender(mach_port_t send_port);
+
+ kern_return_t SendMessage(MachSendMessage &message,
+ mach_msg_timeout_t timeout);
+
+ private:
+ mach_port_t send_port_;
+ kern_return_t init_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(MachPortSender);
+};
+
+#endif // BASE_MACH_IPC_MAC_H_
diff --git a/base/mach_ipc_mac.mm b/base/mach_ipc_mac.mm
new file mode 100644
index 0000000..fc78bcc
--- /dev/null
+++ b/base/mach_ipc_mac.mm
@@ -0,0 +1,308 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/mach_ipc_mac.h"
+
+#import <Foundation/Foundation.h>
+
+#include <stdio.h>
+#include "base/logging.h"
+
+//==============================================================================
+MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() {
+ Initialize(message_id);
+}
+
+MachSendMessage::MachSendMessage(void *storage, size_t storage_length,
+ int32_t message_id)
+ : MachMessage(storage, storage_length) {
+ Initialize(message_id);
+}
+
+void MachSendMessage::Initialize(int32_t message_id) {
+ Head()->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+
+ // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage()
+ Head()->msgh_local_port = MACH_PORT_NULL;
+ Head()->msgh_reserved = 0;
+ Head()->msgh_id = 0;
+
+ SetDescriptorCount(0); // start out with no descriptors
+
+ SetMessageID(message_id);
+ SetData(NULL, 0); // client may add data later
+}
+
+//==============================================================================
+MachMessage::MachMessage()
+ : storage_(new MachMessageData), // Allocate storage_ ourselves
+ storage_length_bytes_(sizeof(MachMessageData)),
+ own_storage_(true) {
+ memset(storage_, 0, storage_length_bytes_);
+}
+
+//==============================================================================
+MachMessage::MachMessage(void *storage, size_t storage_length)
+ : storage_(static_cast<MachMessageData*>(storage)),
+ storage_length_bytes_(storage_length),
+ own_storage_(false) {
+ DCHECK(storage);
+ DCHECK(storage_length >= kEmptyMessageSize);
+}
+
+//==============================================================================
+MachMessage::~MachMessage() {
+ if (own_storage_) {
+ delete storage_;
+ storage_ = NULL;
+ }
+}
+
+//==============================================================================
+// returns true if successful
+bool MachMessage::SetData(const void* data,
+ int32_t data_length) {
+ // Enforce the fact that it's only safe to call this method once on a
+ // message.
+ DCHECK(GetDataPacket()->data_length == 0);
+
+ // first check to make sure we have enough space
+ int size = CalculateSize();
+ int new_size = size + data_length;
+
+ if ((unsigned)new_size > storage_length_bytes_) {
+ return false; // not enough space
+ }
+
+ GetDataPacket()->data_length = EndianU32_NtoL(data_length);
+ if (data) memcpy(GetDataPacket()->data, data, data_length);
+
+ // Update the Mach header with the new aligned size of the message.
+ CalculateSize();
+
+ return true;
+}
+
+//==============================================================================
+// calculates and returns the total size of the message
+// Currently, the entire message MUST fit inside of the MachMessage
+// messsage size <= EmptyMessageSize()
+int MachMessage::CalculateSize() {
+ int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
+
+ // add space for MessageDataPacket
+ int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3;
+ size += 2*sizeof(int32_t) + alignedDataLength;
+
+ // add space for descriptors
+ size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor);
+
+ Head()->msgh_size = size;
+
+ return size;
+}
+
+//==============================================================================
+MachMessage::MessageDataPacket *MachMessage::GetDataPacket() {
+ int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount();
+ MessageDataPacket *packet =
+ reinterpret_cast<MessageDataPacket*>(storage_->padding + desc_size);
+
+ return packet;
+}
+
+//==============================================================================
+void MachMessage::SetDescriptor(int n,
+ const MachMsgPortDescriptor &desc) {
+ MachMsgPortDescriptor *desc_array =
+ reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
+ desc_array[n] = desc;
+}
+
+//==============================================================================
+// returns true if successful otherwise there was not enough space
+bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) {
+ // first check to make sure we have enough space
+ int size = CalculateSize();
+ int new_size = size + sizeof(MachMsgPortDescriptor);
+
+ if ((unsigned)new_size > storage_length_bytes_) {
+ return false; // not enough space
+ }
+
+ // unfortunately, we need to move the data to allow space for the
+ // new descriptor
+ u_int8_t *p = reinterpret_cast<u_int8_t*>(GetDataPacket());
+ bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t));
+
+ SetDescriptor(GetDescriptorCount(), desc);
+ SetDescriptorCount(GetDescriptorCount() + 1);
+
+ CalculateSize();
+
+ return true;
+}
+
+//==============================================================================
+void MachMessage::SetDescriptorCount(int n) {
+ storage_->body.msgh_descriptor_count = n;
+
+ if (n > 0) {
+ Head()->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
+ } else {
+ Head()->msgh_bits &= ~MACH_MSGH_BITS_COMPLEX;
+ }
+}
+
+//==============================================================================
+MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) {
+ if (n < GetDescriptorCount()) {
+ MachMsgPortDescriptor *desc =
+ reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
+ return desc + n;
+ }
+
+ return nil;
+}
+
+//==============================================================================
+mach_port_t MachMessage::GetTranslatedPort(int n) {
+ if (n < GetDescriptorCount()) {
+ return GetDescriptor(n)->GetMachPort();
+ }
+ return MACH_PORT_NULL;
+}
+
+#pragma mark -
+
+//==============================================================================
+// create a new mach port for receiving messages and register a name for it
+ReceivePort::ReceivePort(const char *receive_port_name) {
+ mach_port_t current_task = mach_task_self();
+
+ init_result_ = mach_port_allocate(current_task,
+ MACH_PORT_RIGHT_RECEIVE,
+ &port_);
+
+ if (init_result_ != KERN_SUCCESS)
+ return;
+
+ init_result_ = mach_port_insert_right(current_task,
+ port_,
+ port_,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ if (init_result_ != KERN_SUCCESS)
+ return;
+
+ NSPort *ns_port = [NSMachPort portWithMachPort:port_];
+ NSString *port_name = [NSString stringWithUTF8String:receive_port_name];
+ [[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name];
+}
+
+//==============================================================================
+// create a new mach port for receiving messages
+ReceivePort::ReceivePort() {
+ mach_port_t current_task = mach_task_self();
+
+ init_result_ = mach_port_allocate(current_task,
+ MACH_PORT_RIGHT_RECEIVE,
+ &port_);
+
+ if (init_result_ != KERN_SUCCESS)
+ return;
+
+ init_result_ = mach_port_insert_right(current_task,
+ port_,
+ port_,
+ MACH_MSG_TYPE_MAKE_SEND);
+}
+
+//==============================================================================
+// Given an already existing mach port, use it. We take ownership of the
+// port and deallocate it in our destructor.
+ReceivePort::ReceivePort(mach_port_t receive_port)
+ : port_(receive_port),
+ init_result_(KERN_SUCCESS) {
+}
+
+//==============================================================================
+ReceivePort::~ReceivePort() {
+ if (init_result_ == KERN_SUCCESS)
+ mach_port_deallocate(mach_task_self(), port_);
+}
+
+//==============================================================================
+kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
+ mach_msg_timeout_t timeout) {
+ if (!out_message) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ // return any error condition encountered in constructor
+ if (init_result_ != KERN_SUCCESS)
+ return init_result_;
+
+ out_message->Head()->msgh_bits = 0;
+ out_message->Head()->msgh_local_port = port_;
+ out_message->Head()->msgh_remote_port = MACH_PORT_NULL;
+ out_message->Head()->msgh_reserved = 0;
+ out_message->Head()->msgh_id = 0;
+
+ kern_return_t result = mach_msg(out_message->Head(),
+ MACH_RCV_MSG | MACH_RCV_TIMEOUT,
+ 0,
+ out_message->MaxSize(),
+ port_,
+ timeout, // timeout in ms
+ MACH_PORT_NULL);
+
+ return result;
+}
+
+#pragma mark -
+
+//==============================================================================
+// get a port with send rights corresponding to a named registered service
+MachPortSender::MachPortSender(const char *receive_port_name) {
+ mach_port_t bootstrap_port = 0;
+ init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
+
+ if (init_result_ != KERN_SUCCESS)
+ return;
+
+ init_result_ = bootstrap_look_up(bootstrap_port,
+ const_cast<char*>(receive_port_name),
+ &send_port_);
+}
+
+//==============================================================================
+MachPortSender::MachPortSender(mach_port_t send_port)
+ : send_port_(send_port),
+ init_result_(KERN_SUCCESS) {
+}
+
+//==============================================================================
+kern_return_t MachPortSender::SendMessage(MachSendMessage &message,
+ mach_msg_timeout_t timeout) {
+ if (message.Head()->msgh_size == 0) {
+ NOTREACHED();
+ return KERN_INVALID_VALUE; // just for safety -- never should occur
+ };
+
+ if (init_result_ != KERN_SUCCESS)
+ return init_result_;
+
+ message.Head()->msgh_remote_port = send_port_;
+
+ kern_return_t result = mach_msg(message.Head(),
+ MACH_SEND_MSG | MACH_SEND_TIMEOUT,
+ message.Head()->msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ timeout, // timeout in ms
+ MACH_PORT_NULL);
+
+ return result;
+}
diff --git a/base/process_util.h b/base/process_util.h
index cec37bd..eb21188 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -17,6 +17,7 @@
// kinfo_proc is defined in <sys/sysctl.h>, but this forward declaration
// is sufficient for the vector<kinfo_proc> below.
struct kinfo_proc;
+#include <mach/mach.h>
#elif defined(OS_POSIX)
#include <dirent.h>
#include <limits.h>
@@ -155,13 +156,25 @@ bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
bool wait, ProcessHandle* process_handle);
-// Similar to above, but also (un)set environment variables in child process
+// Similar to the above, but also (un)set environment variables in child process
// through |environ|.
typedef std::vector<std::pair<std::string, std::string> > environment_vector;
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);
+
+#if defined(OS_MACOSX)
+// Similar to the above, but also returns the new process's task_t if
+// |task_handle| is not NULL. If |task_handle| is not NULL, the caller is
+// responsible for calling |mach_port_deallocate()| on the returned handle.
+bool LaunchAppAndGetTask(const std::vector<std::string>& argv,
+ const environment_vector& environ,
+ const file_handle_mapping_vector& fds_to_remap,
+ bool wait,
+ task_t* task_handle,
+ ProcessHandle* process_handle);
+#endif // defined(OS_MACOSX)
#endif // defined(OS_POSIX)
// Executes the application specified by cl. This function delegates to one
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;