// run.cc (tested on Mac OS X 10.5/10.6) #include #include #include #include #include #include #include #include #define SLEEP_NSEC 10000 // 0.01s // smaller value => fine-grained memory monitoring (overhead increases runtime) struct mach_send_port_msg { mach_msg_header_t header; mach_msg_body_t body; mach_msg_port_descriptor_t task_port; }; struct mach_recv_port_msg { mach_send_port_msg m; mach_msg_trailer_t trailer; }; static int setup_recv_port (mach_port_t *recv_port) { mach_port_t port = MACH_PORT_NULL; if (KERN_SUCCESS != mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &port)) std::fprintf (stderr, "mach_port_allocate failed.\n"); if (KERN_SUCCESS != mach_port_insert_right (mach_task_self (), port, port, MACH_MSG_TYPE_MAKE_SEND)) std::fprintf (stderr, "mach_port_insert_right failed.\n"); *recv_port = port; return 0; } static int send_port (mach_port_t remote_port, mach_port_t port) { mach_send_port_msg msg; msg.header.msgh_remote_port = remote_port; msg.header.msgh_local_port = MACH_PORT_NULL; msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX; msg.header.msgh_size = sizeof (msg); msg.body.msgh_descriptor_count = 1; msg.task_port.name = port; msg.task_port.disposition = MACH_MSG_TYPE_COPY_SEND; msg.task_port.type = MACH_MSG_PORT_DESCRIPTOR; if (KERN_SUCCESS != mach_msg_send (&msg.header)) std::fprintf (stderr, "mach_msg_send failed.\n"); return 0; } static int recv_port (mach_port_t recv_port, mach_port_t *port) { mach_recv_port_msg msg; if (KERN_SUCCESS != mach_msg (&msg.m.header, MACH_RCV_MSG, 0, sizeof (msg), recv_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL)) std::fprintf (stderr, "mach_msg failed.\n"); *port = msg.m.task_port.name; return 0; } int main (int argc, char **argv) { char ** cmd = &argv[1]; struct timeval start, end; task_t task = MACH_PORT_NULL; mach_port_t parent_recv_port = MACH_PORT_NULL; mach_port_t child_recv_port = MACH_PORT_NULL; // setup port as bootstrap port if (setup_recv_port (&parent_recv_port) != 0) return -1; if (KERN_SUCCESS != task_set_bootstrap_port (mach_task_self (), parent_recv_port)) std::fprintf (stderr, "task_set_bootstrap_port failed.\n"); // set timer gettimeofday (&start, 0); // create new task pid_t pid = fork (); if (pid < 0) { if (KERN_SUCCESS != mach_port_deallocate (mach_task_self(), parent_recv_port)) std::fprintf (stderr, "mach_port_deallocate failed.\n"); std::fprintf (stderr, "cannot fork.\n"); std::exit (1); } else if (pid == 0) { // child process if (KERN_SUCCESS != task_get_bootstrap_port (mach_task_self (), &parent_recv_port)) std::fprintf (stderr, "task_get_bootstrap_port failed.\n"); if (setup_recv_port (&child_recv_port) != 0) return -1; if (send_port (parent_recv_port, mach_task_self ()) != 0) return -1; if (send_port (parent_recv_port, child_recv_port) != 0) return -1; if (recv_port (child_recv_port, &bootstrap_port) != 0) return -1; if (KERN_SUCCESS != task_set_bootstrap_port (mach_task_self (), bootstrap_port)) std::fprintf (stderr, "task_set_bootstrap_port failed.\n"); execvp (cmd[0], cmd); std::fprintf (stderr, "cannot run %s\n", cmd[0]); _exit (errno == ENOENT ? 127 : 126); } // parent process std::signal (SIGINT, SIG_IGN); std::signal (SIGQUIT, SIG_IGN); if (KERN_SUCCESS != task_set_bootstrap_port (mach_task_self (), bootstrap_port)) std::fprintf (stderr, "task_set_bootstrap_port failed.\n"); if (recv_port (parent_recv_port, &task) != 0) return -1; if (recv_port (parent_recv_port, &child_recv_port) != 0) return -1; if (send_port (child_recv_port, bootstrap_port) != 0) return -1; if (KERN_SUCCESS != mach_port_deallocate (mach_task_self(), parent_recv_port)) std::fprintf (stderr, "mach_port_deallocate failed.\n"); // monitor memory size_t mem = 0; struct task_basic_info t_info; mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; do { if (KERN_SUCCESS != task_info (task, TASK_BASIC_INFO, (task_info_t) &t_info, &t_info_count)) break; if (mem < t_info.resident_size) mem = t_info.resident_size; } while (! usleep (SLEEP_NSEC)); // stop timer int status; if (waitpid (pid, &status, 0) == -1) // std::fprintf (stderr, "error waiting for child process.\n"); if ((status & 0xff) == 0x7f) std::fprintf (stderr, "Command stopped by signal %d\n", (status >> 8) & 0xff); else if ((status & 0xff) != 0) std::fprintf (stderr, "Command terminated by signal %d\n", status & 0xff); else if ((status >> 8) & 0xff) std::fprintf (stderr, "Command exited with non-zero status %d\n", (status >> 8) & 0xff); gettimeofday (&end, 0); std::fprintf (stderr, "elapsed (real): %.3fs; RSS=%.2f MiB (%ld bytes)\n", end.tv_sec - start.tv_sec + (end.tv_usec - start.tv_usec) * 1e-6, mem / 1048576.0, mem); std::signal (SIGINT, SIG_DFL); std::signal (SIGQUIT, SIG_DFL); return 0; }