From 21e14dbdaa558ce1147bece1fba787c4ff494bc3 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Tue, 27 Feb 2001 09:57:17 +0000 Subject: 2001-02-27 Roland McGrath * rpctrace.c: Overhaul output handling. --- utils/rpctrace.c | 706 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 481 insertions(+), 225 deletions(-) diff --git a/utils/rpctrace.c b/utils/rpctrace.c index 8fad04bc..3b3257d3 100644 --- a/utils/rpctrace.c +++ b/utils/rpctrace.c @@ -1,7 +1,6 @@ /* Trace RPCs sent to selected ports - Copyright (C) 1998, 1999 Free Software Foundation, Inc. - + Copyright (C) 1998,1999,2001 Free Software Foundation, Inc. Written by Jose M. Moya This file is part of the GNU Hurd. @@ -20,6 +19,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ + #include #include #include @@ -32,6 +32,7 @@ #include #include #include +#include const char *argp_program_version = STANDARD_HURD_VERSION (rpctrace); @@ -45,41 +46,75 @@ static const char *doc = "Trace Mach Remote Procedure Calls." "\v."; -pid_t traced_spawn (char **argv, char **envp); - - +/* We keep one of these structures for each port right we are tracing. */ struct traced_info { struct port_info pi; + + mach_port_t forward; /* real port */ + mach_msg_type_name_t type; + + char *name; /* null or a string describing this */ + union { struct traced_info *nextfree; /* Link when on free list. */ - /* For a send right wrapper, the position in the traced_names hash table. - For a send-once right wrapper, this is null. */ - void **locp; -#define INFO_SEND_ONCE(info) ((info)->u.locp == 0) + struct /* For a send right wrapper. */ + { + void **locp; /* position in the traced_names hash table */ + } send; + + struct /* For a send-once right wrapper. */ + { + /* We keep track of the send right to which the message containing + this send-once right as its reply port was sent, and the msgid of + that request. We don't hold a reference to the send right; it is + just a hint to indicate a match with a send right on which we just + forwarded a message. */ + mach_port_t sent_to; + mach_msg_id_t sent_msgid; + } send_once; } u; - mach_port_t forward; }; +#define INFO_SEND_ONCE(info) ((info)->type == MACH_MSG_TYPE_MOVE_SEND_ONCE) static struct traced_info *freelist; -task_t traced_task; ihash_t traced_names; struct port_class *traced_class; struct port_bucket *traced_bucket; FILE *ostream; +/* These are the calls made from the tracing engine into + the output formatting code. */ + +/* Called for a message that does not look like an RPC reply. + The header has already been swapped into the sender's view + with interposed ports. */ +static void print_request_header (struct traced_info *info, + mach_msg_header_t *header); + +/* Called for a message that looks like an RPC reply. */ +static void print_reply_header (struct traced_info *info, + mig_reply_header_t *header); + +/* Called for each data item (which might be an array). + Always called after one of the above two. */ +static void print_data (mach_msg_type_name_t type, + const void *data, + mach_msg_type_number_t nelt, + mach_msg_type_number_t eltsize); + +/*** Mechanics of tracing messages and interposing on ports ***/ + /* Create a new wrapper port and do `ports_get_right' on it. */ -static mach_port_t -new_send_wrapper (mach_port_t right) +static struct traced_info * +new_send_wrapper (mach_port_t right, mach_port_t *wrapper_right) { error_t err; struct traced_info *info; - mach_port_t wrapper; - /* Use a free send-once wrapper port if we have one. */ if (freelist) @@ -93,31 +128,32 @@ new_send_wrapper (mach_port_t right) err = ports_create_port (traced_class, traced_bucket, sizeof *info, &info); assert_perror (err); + info->name = 0; } info->forward = right; + info->type = MACH_MSG_TYPE_MOVE_SEND; /* Store it in the reverse-lookup hash table, so we can look up this same right again to find the wrapper port. The entry in the hash table holds a weak ref on INFO. */ - err = ihash_add (traced_names, info->forward, info, &info->u.locp); + err = ihash_add (traced_names, info->forward, info, &info->u.send.locp); assert_perror (err); ports_port_ref_weak (info); - assert (info->u.locp != 0); + assert (info->u.send.locp != 0); - wrapper = ports_get_right (info); + *wrapper_right = ports_get_right (info); ports_port_deref (info); - return wrapper; + return info; } /* Create a new wrapper port and do `ports_get_right' on it. */ -static mach_port_t -new_send_once_wrapper (mach_port_t right) +static struct traced_info * +new_send_once_wrapper (mach_port_t right, mach_port_t *wrapper_right) { error_t err; struct traced_info *info; - mach_port_t wrapper; /* Use a free send-once wrapper port if we have one. */ if (freelist) @@ -131,13 +167,15 @@ new_send_once_wrapper (mach_port_t right) err = ports_create_port (traced_class, traced_bucket, sizeof *info, &info); assert_perror (err); + info->name = 0; } info->forward = right; + info->type = MACH_MSG_TYPE_MOVE_SEND_ONCE; - /* Send-once never compare equal to any other right (even another - send-once right), so there is no point in putting them in the - reverse-lookup table. + /* Send-once rights never compare equal to any other right (even + another send-once right), so there is no point in putting them + in the reverse-lookup table. Since we never make send rights to this port, we don't want to use the normal libports mechanisms (ports_get_right) that are @@ -146,10 +184,10 @@ new_send_once_wrapper (mach_port_t right) receive a message on it. The kernel automatically sends a MACH_NOTIFY_SEND_ONCE message if the send-once right dies. */ - info->u.locp = 0; /* Used to mark this as send-once. */ - wrapper = info->pi.port_right; + *wrapper_right = info->pi.port_right; + memset (&info->u.send_once, 0, sizeof info->u.send_once); - return wrapper; + return info; } @@ -161,10 +199,11 @@ traced_dropweak (void *pi) { struct traced_info *const info = pi; - assert (info->u.locp); + assert (info->type == MACH_MSG_TYPE_MOVE_SEND); + assert (info->u.send.locp); /* Remove INFO from the hash table. */ - ihash_locp_remove (traced_names, info->u.locp); + ihash_locp_remove (traced_names, info->u.send.locp); ports_port_deref_weak (info); /* Deallocate the forward port, so the real port also sees no-senders. */ @@ -173,14 +212,17 @@ traced_dropweak (void *pi) /* There are no rights to this port, so we can reuse it. Add a hard ref and put INFO on the free list. */ ports_port_ref (info); + + free (info->name); + info->name = 0; + info->u.nextfree = freelist; freelist = info; } - /* Rewrite a port right in a message with an appropriate wrapper port. */ -static error_t +static struct traced_info * rewrite_right (mach_port_t *right, mach_msg_type_name_t *type) { error_t err; @@ -201,7 +243,7 @@ rewrite_right (mach_port_t *right, mach_msg_type_name_t *type) to our existing wrapper port. */ *right = ports_get_right (info); *type = MACH_MSG_TYPE_MAKE_SEND; - return 0; + return info; } /* See if this is already one of our own wrapper ports. */ @@ -216,14 +258,13 @@ rewrite_right (mach_port_t *right, mach_msg_type_name_t *type) MACH_PORT_RIGHT_SEND, +1); assert_perror (err); ports_port_deref (info); - return 0; + return info; } /* We have never seen this port before. Create a new wrapper port and replace the right in the message with a right to it. */ - *right = new_send_wrapper (*right); *type = MACH_MSG_TYPE_MAKE_SEND; - return 0; + return new_send_wrapper (*right, right); case MACH_MSG_TYPE_PORT_SEND_ONCE: /* There is no way to know if this send-once right is to the same @@ -233,8 +274,7 @@ rewrite_right (mach_port_t *right, mach_msg_type_name_t *type) make a new send-once wrapper object, that will trace the one message it receives, and then die. */ *type = MACH_MSG_TYPE_MAKE_SEND_ONCE; - *right = new_send_once_wrapper (*right); - return 0; + return new_send_once_wrapper (*right, right); case MACH_MSG_TYPE_PORT_RECEIVE: /* We have got a receive right, call it A. We will pass along a @@ -248,22 +288,29 @@ rewrite_right (mach_port_t *right, mach_msg_type_name_t *type) If not, we create a new receive right. */ { mach_port_t rr; /* B */ + char *name; info = ihash_find (traced_names, *right); if (info) { /* This is a receive right that we have been tracing sends to. */ + name = info->name; rr = ports_claim_right (info); /* That released the refs on INFO, so it's been freed now. */ } else - /* This is a port we know nothing about. */ - rr = mach_reply_port (); + { + /* This is a port we know nothing about. */ + rr = mach_reply_port (); + name = 0; + } /* Create a new wrapper object that receives on this port. */ err = ports_import_port (traced_class, traced_bucket, *right, sizeof *info, &info); assert_perror (err); + info->name = name; + info->type = MACH_MSG_TYPE_MOVE_SEND; /* XXX ? */ /* Get us a send right that we will forward on. */ err = mach_port_insert_right (mach_task_self (), rr, rr, @@ -271,7 +318,8 @@ rewrite_right (mach_port_t *right, mach_msg_type_name_t *type) assert_perror (err); info->forward = rr; - err = ihash_add (traced_names, info->forward, info, &info->u.locp); + err = ihash_add (traced_names, info->forward, info, + &info->u.send.locp); assert_perror (err); ports_port_ref_weak (info); @@ -282,137 +330,22 @@ rewrite_right (mach_port_t *right, mach_msg_type_name_t *type) ports_port_deref (info); *right = rr; - return 0; + return info; } default: assert (!"??? bogus port type from kernel!"); - return EGRATUITOUS; } -} - - -static void -print_header (mach_msg_header_t *msg) -{ - fprintf (ostream, - "msgid %d, %d bytes in msg\n" - "\treply port %d (type %d)\n", - msg->msgh_id, - msg->msgh_size, - msg->msgh_remote_port, MACH_MSGH_BITS_REMOTE(msg->msgh_bits)); + return 0; } static void -print_data (mach_msg_type_name_t type, - const void *data, - mach_msg_type_number_t nelt, - mach_msg_type_number_t eltsize) -{ - switch (type) - { - case MACH_MSG_TYPE_STRING: - fprintf (ostream, "\t\"%.*s\"\n", - (int) (nelt * eltsize), (const char *) data); - break; - - case MACH_MSG_TYPE_BIT: - case MACH_MSG_TYPE_INTEGER_8: - case MACH_MSG_TYPE_INTEGER_16: - case MACH_MSG_TYPE_INTEGER_32: - case MACH_MSG_TYPE_INTEGER_64: - case MACH_MSG_TYPE_CHAR: - case MACH_MSG_TYPE_REAL: - default: - /* XXX */ - fprintf (ostream, "\t%#x (type %d, %d*%d)\n", *(const int *)data, type, - nelt, eltsize); - break; - } -} - - -int -trace_and_forward (mach_msg_header_t *inp, mach_msg_header_t *outp) +print_contents (mach_msg_header_t *inp, + void *msg_buf_ptr) { error_t err; - struct traced_info *info; - void *msg_buf_ptr = inp + 1; - mach_msg_bits_t complex; - /* Look up our record for the receiving port. There is no need to check - the class, because our port bucket only ever contains one class of - ports (traced_class). */ - info = ports_lookup_port (traced_bucket, inp->msgh_local_port, 0); - assert (info); - - if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE) - { - if (inp->msgh_id == MACH_NOTIFY_DEAD_NAME) - { - /* If INFO is a send-once wrapper, this could be a forged - notification; oh well. XXX */ - - const mach_dead_name_notification_t *const n = (void *) inp; - - assert (n->not_port == info->forward); - /* Deallocate extra ref allocated by the notification. */ - mach_port_deallocate (mach_task_self (), n->not_port); - ports_destroy_right (info); - ports_port_deref (info); - ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY; - return 1; - } - else if (inp->msgh_id == MACH_NOTIFY_NO_SENDERS - && !INFO_SEND_ONCE (info)) - { - /* No more senders for a send right we are tracing. Now INFO - will die, and we will release the tracee send right so it too - can see a no-senders notification. */ - mach_no_senders_notification_t *n = (void *) inp; - ports_no_senders (info, n->not_count); - ports_port_deref (info); - ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY; - return 1; - } - } - - complex = inp->msgh_bits & MACH_MSGH_BITS_COMPLEX; - - /* Print something about the message header. */ - fprintf (ostream, "port %d(=>%d) receives (type %d) ", - inp->msgh_local_port, info->forward, - MACH_MSGH_BITS_LOCAL (inp->msgh_bits)); - print_header (inp); - - /* Swap the header data like a crossover cable. */ - { - mach_msg_type_name_t this_type = MACH_MSGH_BITS_LOCAL (inp->msgh_bits); - mach_msg_type_name_t reply_type = MACH_MSGH_BITS_REMOTE (inp->msgh_bits); - - inp->msgh_local_port = inp->msgh_remote_port; - if (reply_type) - { - err = rewrite_right (&inp->msgh_local_port, &reply_type); - assert_perror (err); - } - - inp->msgh_remote_port = info->forward; - if (this_type == MACH_MSG_TYPE_MOVE_SEND_ONCE) - { - /* We have a message to forward for a send-once wrapper object. - Since each wrapper object only lives for a single message, this - one can be reclaimed now. We continue to hold a hard ref to the - ports object, but we know that nothing else refers to it now, and - we are consuming its `forward' right in the message we send. */ - info->u.nextfree = freelist; - freelist = info; - } - else - this_type = MACH_MSG_TYPE_COPY_SEND; - - inp->msgh_bits = complex | MACH_MSGH_BITS (this_type, reply_type); - } + int first = 1; /* Process the message data, wrapping ports and printing data. */ while (msg_buf_ptr < (void *) inp + inp->msgh_size) @@ -450,6 +383,11 @@ trace_and_forward (mach_msg_header_t *inp, mach_msg_header_t *outp) msg_buf_ptr += ((nelt * eltsize + sizeof(natural_t) - 1) & ~(sizeof(natural_t) - 1)); + if (first) + first = 0; + else + putc (' ', ostream); + /* Note that MACH_MSG_TYPE_PORT_NAME does not indicate a port right. It indicates a port name, i.e. just an integer--and we don't know what task that port name is meaningful in. If it's meaningful in @@ -462,16 +400,14 @@ trace_and_forward (mach_msg_header_t *inp, mach_msg_header_t *outp) mach_msg_type_number_t i; mach_msg_type_name_t newtypes[nelt]; int poly; + struct traced_info *ti; - assert (complex); + assert (inp->msgh_bits & MACH_MSGH_BITS_COMPLEX); assert (eltsize == sizeof (mach_port_t)); - fprintf (ostream, "\t%d ports, type %d\n", nelt, name); - poly = 0; for (i = 0; i < nelt; ++i) { - mach_port_t o=portnames[i]; newtypes[i] = name; if (inp->msgh_id == 3215) /* mach_port_insert_right */ @@ -484,21 +420,27 @@ trace_and_forward (mach_msg_header_t *inp, mach_msg_header_t *outp) continue; } - err = rewrite_right (&portnames[i], &newtypes[i]); - assert_perror (err); + ti = rewrite_right (&portnames[i], &newtypes[i]); + + putc ((i == 0 && nelt > 1) ? '{' : ' ', ostream); if (portnames[i] == MACH_PORT_NULL) - fprintf (ostream, "\t\t[%d] = null\n", i); + fprintf (ostream, "(null)"); else if (portnames[i] == MACH_PORT_DEAD) - fprintf (ostream, "\t\t[%d] = dead name\n", i); + fprintf (ostream, "(dead)"); else - fprintf (ostream, - "\t\t[%d] = port %d, type %d => port %d, type %d\n", - i, o, name, portnames[i], newtypes[i]); - + { + assert (ti); + if (ti->name != 0) + fprintf (ostream, "%s", ti->name); + else + fprintf (ostream, "%3u", (unsigned int) portnames[i]); + } if (i > 0 && newtypes[i] != newtypes[0]) poly = 1; } + if (nelt > 1) + putc ('}', ostream); if (poly) { @@ -552,6 +494,132 @@ trace_and_forward (mach_msg_header_t *inp, mach_msg_header_t *outp) else print_data (name, data, nelt, eltsize); } +} + +int +trace_and_forward (mach_msg_header_t *inp, mach_msg_header_t *outp) +{ + const mach_msg_type_t RetCodeType = + { + MACH_MSG_TYPE_INTEGER_32, /* msgt_name = */ + 32, /* msgt_size = */ + 1, /* msgt_number = */ + TRUE, /* msgt_inline = */ + FALSE, /* msgt_longform = */ + FALSE, /* msgt_deallocate = */ + 0 /* msgt_unused = */ + }; + + error_t err; + struct traced_info *info; + mach_msg_bits_t complex; + + /* Look up our record for the receiving port. There is no need to check + the class, because our port bucket only ever contains one class of + ports (traced_class). */ + info = ports_lookup_port (traced_bucket, inp->msgh_local_port, 0); + assert (info); + + if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE) + { + if (inp->msgh_id == MACH_NOTIFY_DEAD_NAME) + { + /* If INFO is a send-once wrapper, this could be a forged + notification; oh well. XXX */ + + const mach_dead_name_notification_t *const n = (void *) inp; + + assert (n->not_port == info->forward); + /* Deallocate extra ref allocated by the notification. */ + mach_port_deallocate (mach_task_self (), n->not_port); + ports_destroy_right (info); + ports_port_deref (info); + ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY; + return 1; + } + else if (inp->msgh_id == MACH_NOTIFY_NO_SENDERS + && !INFO_SEND_ONCE (info)) + { + /* No more senders for a send right we are tracing. Now INFO + will die, and we will release the tracee send right so it too + can see a no-senders notification. */ + mach_no_senders_notification_t *n = (void *) inp; + ports_no_senders (info, n->not_count); + ports_port_deref (info); + ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY; + return 1; + } + } + + complex = inp->msgh_bits & MACH_MSGH_BITS_COMPLEX; + + /* Swap the header data like a crossover cable. */ + { + mach_msg_type_name_t this_type = MACH_MSGH_BITS_LOCAL (inp->msgh_bits); + mach_msg_type_name_t reply_type = MACH_MSGH_BITS_REMOTE (inp->msgh_bits); + + inp->msgh_local_port = inp->msgh_remote_port; + if (reply_type) + { + struct traced_info *info; + info = rewrite_right (&inp->msgh_local_port, &reply_type); + assert (info); + if (info->name == 0) + asprintf (&info->name, "reply(%u:%u)", + (unsigned int) info->pi.port_right, + (unsigned int) inp->msgh_id); + if (info->type == MACH_MSG_TYPE_MOVE_SEND_ONCE) + { + info->u.send_once.sent_to = info->pi.port_right; + info->u.send_once.sent_msgid = inp->msgh_id; + } + } + + inp->msgh_remote_port = info->forward; + if (this_type == MACH_MSG_TYPE_MOVE_SEND_ONCE) + { + /* We have a message to forward for a send-once wrapper object. + Since each wrapper object only lives for a single message, this + one can be reclaimed now. We continue to hold a hard ref to the + ports object, but we know that nothing else refers to it now, and + we are consuming its `forward' right in the message we send. */ + free (info->name); + info->name = 0; + info->u.nextfree = freelist; + freelist = info; + } + else + this_type = MACH_MSG_TYPE_COPY_SEND; + + inp->msgh_bits = complex | MACH_MSGH_BITS (this_type, reply_type); + } + + /* The message now appears as it would if we were the sender. + It is ready to be resent. */ + + if (inp->msgh_local_port == MACH_PORT_NULL + && inp->msgh_size >= sizeof (mig_reply_header_t) + && (*(int *) &((mig_reply_header_t *) inp)->RetCodeType + == *(int *)&RetCodeType)) + { + /* This sure looks like an RPC reply message. */ + mig_reply_header_t *rh = (void *) inp; + print_reply_header (info, rh); + putc (' ', ostream); + print_contents (&rh->Head, rh + 1); + putc ('\n', ostream); + } + else + { + /* Print something about the message header. */ + print_request_header (info, inp); + print_contents (inp, inp + 1); + if (inp->msgh_local_port == MACH_PORT_NULL) /* simpleroutine */ + fprintf (ostream, ");\n"); + else + /* Leave a partial line that will be finished later. */ + fprintf (ostream, ")"); + } /* Resend the message to the tracee. */ err = mach_msg (inp, MACH_SEND_MSG, inp->msgh_size, 0, @@ -574,7 +642,6 @@ trace_and_forward (mach_msg_header_t *inp, mach_msg_header_t *outp) return 1; } - /* This function runs in the tracing thread and drives all the tracing. */ static any_t trace_thread_function (void *arg) @@ -583,6 +650,245 @@ trace_thread_function (void *arg) ports_manage_port_operations_one_thread (bucket, trace_and_forward, 0); return 0; } + +/*** Output formatting ***/ + +#if 0 +struct msg_type +{ + const char *name; + const char *letter; +}; + +static const char *const msg_types[] = +{ + [MACH_MSG_TYPE_BIT] = {"bool", "b"}, + [MACH_MSG_TYPE_INTEGER_16] = {"int16", "h"}, + [MACH_MSG_TYPE_INTEGER_32] = {"int32", "i"}, + [MACH_MSG_TYPE_CHAR] = {"char", "c"}, + [MACH_MSG_TYPE_INTEGER_8] = {"int8", "B"}, + [MACH_MSG_TYPE_REAL] = {"float", "f"}, + [MACH_MSG_TYPE_INTEGER_64] = {"int64", "q"}, + [MACH_MSG_TYPE_STRING] = {"string", "s"}, + [MACH_MSG_TYPE_MOVE_RECEIVE] = {"move-receive", "R"}, + [MACH_MSG_TYPE_MOVE_SEND] = {"move-send", "S"}, + [MACH_MSG_TYPE_MOVE_SEND_ONCE]= {"move-send-once", "O"}, + [MACH_MSG_TYPE_COPY_SEND] = {"copy-send", "s"}, + [MACH_MSG_TYPE_MAKE_SEND] = {"make-send", ""}, + [MACH_MSG_TYPE_MAKE_SEND_ONCE]= {"make-send-once", ""}, + [MACH_MSG_TYPE_PORT_NAME] = {"port-name", "n"}, +}; +#endif + +static mach_port_t expected_reply_port; + +static void +print_request_header (struct traced_info *receiver, mach_msg_header_t *msg) +{ + expected_reply_port = msg->msgh_local_port; + + if (receiver->name != 0) + fprintf (ostream, "%4s->%5u (", receiver->name, msg->msgh_id); + else + fprintf (ostream, "%4u->%5u (", + (unsigned int) receiver->pi.port_right, msg->msgh_id); +} + +static void +unfinished_line (void) +{ + /* A partial line was printed by print_request_header, but + cannot be finished before we print something else. + Finish this line with the name of the reply port that + will appear in the disconnected reply later on. */ + fprintf (ostream, " > %4u ...\n", expected_reply_port); +} + +static void +print_reply_header (struct traced_info *info, mig_reply_header_t *reply) +{ + if (info->pi.port_right == expected_reply_port) + { + /* We have printed a partial line for the request message, + and now we have the corresponding reply. */ + if (reply->Head.msgh_id == info->u.send_once.sent_msgid + 100) + fprintf (ostream, " = "); /* normal case */ + else + /* This is not the proper reply message ID. */ + fprintf (ostream, " =(%u != %u) ", + reply->Head.msgh_id, + info->u.send_once.sent_msgid + 100); + } + else + { + /* This does not match up with the last thing printed. */ + unfinished_line (); + if (info->name == 0) + /* This was not a reply port in previous message sent + through our wrappers. */ + fprintf (ostream, "reply?%4u", + (unsigned int) info->pi.port_right); + else + fprintf (ostream, "%s%4u", + info->name, (unsigned int) info->pi.port_right); + if (reply->Head.msgh_id == info->u.send_once.sent_msgid + 100) + /* This is a normal reply to a previous request. */ + fprintf (ostream, " > "); + else + /* Weirdo. */ + fprintf (ostream, " >(%u) ", reply->Head.msgh_id); + } + + if (reply->RetCode == 0) + fprintf (ostream, "0"); + else + { + const char *str = strerror (reply->RetCode); + if (str == 0) + fprintf (ostream, "%#x", reply->RetCode); + else + fprintf (ostream, "%#x (%s)", reply->RetCode, str); + } + + expected_reply_port = MACH_PORT_NULL; +} + + +static void +print_data (mach_msg_type_name_t type, + const void *data, + mach_msg_type_number_t nelt, + mach_msg_type_number_t eltsize) +{ + switch (type) + { + case MACH_MSG_TYPE_PORT_NAME: + assert (eltsize == sizeof (mach_port_t)); + { + mach_msg_type_number_t i; + fprintf (ostream, "pn{"); + for (i = 0; i < nelt; ++i) + { + fprintf (ostream, "%*u", (i > 0) ? 4 : 3, + (unsigned int) ((mach_port_t *) data)[i]); + } + fprintf (ostream, "}"); + return; + } + + case MACH_MSG_TYPE_STRING: + case MACH_MSG_TYPE_CHAR: + fprintf (ostream, "\"%.*s\"", + (int) (nelt * eltsize), (const char *) data); + return; + +#if 0 + case MACH_MSG_TYPE_CHAR: + if (eltsize == 1) + FMT ("'%c'", unsigned char); + break; +#endif + +#define FMT(fmt, ctype) do { \ + mach_msg_type_number_t i; \ + for (i = 0; i < nelt; ++i) \ + { \ + fprintf (ostream, "%s" fmt, \ + (i == 0 && nelt > 1) ? "{" : i > 0 ? " " : "", \ + *(const ctype *) data); \ + data += eltsize; \ + } \ + if (nelt > 1) \ + putc ('}', ostream); \ + return; \ + } while (0) + + case MACH_MSG_TYPE_BIT: + case MACH_MSG_TYPE_INTEGER_8: + case MACH_MSG_TYPE_INTEGER_16: + case MACH_MSG_TYPE_INTEGER_32: + case MACH_MSG_TYPE_INTEGER_64: + switch (eltsize) + { + case 1: FMT ("%"PRId8, int8_t); + case 2: FMT ("%"PRId16, int16_t); + case 4: FMT ("%"PRId32, int32_t); + case 8: FMT ("%"PRId64, int64_t); + } + break; + + case MACH_MSG_TYPE_REAL: + switch (eltsize) + { + case sizeof (float): FMT ("%g", float); + case sizeof (double): FMT ("%g", double); + case sizeof (long double): FMT ("%Lg", long double); + } + break; + } + + /* XXX */ + fprintf (ostream, "\t%#x (type %d, %d*%d)\n", *(const int *)data, type, + nelt, eltsize); +} + + +/*** Main program and child startup ***/ + +task_t traced_task; + + +/* Run a child and have it do more or else `execvpe (argv, envp);'. */ +pid_t +traced_spawn (char **argv, char **envp) +{ + error_t err; + pid_t pid; + mach_port_t task_wrapper; + struct traced_info *ti; + file_t file = file_name_path_lookup (argv[0], getenv ("PATH"), + O_EXEC, 0, 0); + + if (file == MACH_PORT_NULL) + error (1, errno, "command not found: %s", argv[0]); + + err = task_create (mach_task_self (), 0, &traced_task); + assert_perror (err); + + /* Declare the new task to be our child. This is what a fork does. */ + err = proc_child (getproc (), traced_task); + if (err) + error (2, err, "proc_child"); + pid = task2pid (traced_task); + if (pid < 0) + error (2, errno, "task2pid"); + + /* Create a trace wrapper for the task port. */ + ti = new_send_wrapper (traced_task, &task_wrapper);/* consumes ref */ + asprintf (&ti->name, "task%d", (int) pid); + + /* Replace the task's kernel port with the wrapper. When this task calls + `mach_task_self ()', it will get our wrapper send right instead of its + own real task port. */ + err = mach_port_insert_right (mach_task_self (), task_wrapper, + task_wrapper, MACH_MSG_TYPE_MAKE_SEND); + assert_perror (err); + err = task_set_special_port (traced_task, TASK_KERNEL_PORT, task_wrapper); + assert_perror (err); + + /* Now actually run the command they told us to trace. We do the exec on + the actual task, so the RPCs to map in the program itself do not get + traced. Could have an option to use TASK_WRAPPER here instead. */ + err = _hurd_exec (traced_task, file, argv, envp); + if (err) + error (2, err, "cannot exec `%s'", argv[0]); + + /* We were keeping this send right alive so that the wrapper object + cannot die and hence our TRACED_TASK ref cannot have been released. */ + mach_port_deallocate (mach_task_self (), task_wrapper); + + return pid; +} int @@ -628,6 +934,7 @@ main (int argc, char **argv, char **envp) } else ostream = stderr; + setlinebuf (ostream); traced_bucket = ports_create_bucket (); traced_class = ports_create_class (0, &traced_dropweak); @@ -660,54 +967,3 @@ main (int argc, char **argv, char **envp) return 0; } - - -/* Run a child and have it do more or else `execvpe (argv, envp);'. */ -pid_t -traced_spawn (char **argv, char **envp) -{ - error_t err; - pid_t pid; - mach_port_t task_wrapper; - file_t file = file_name_path_lookup (argv[0], getenv ("PATH"), - O_EXEC, 0, 0); - - if (file == MACH_PORT_NULL) - error (1, errno, "command not found: %s", argv[0]); - - err = task_create (mach_task_self (), 0, &traced_task); - assert_perror (err); - - /* Create a trace wrapper for the task port. */ - task_wrapper = new_send_wrapper (traced_task);/* consumes ref */ - - /* Replace the task's kernel port with the wrapper. When this task calls - `mach_task_self ()', it will get our wrapper send right instead of its - own real task port. */ - err = mach_port_insert_right (mach_task_self (), task_wrapper, - task_wrapper, MACH_MSG_TYPE_MAKE_SEND); - assert_perror (err); - err = task_set_special_port (traced_task, TASK_KERNEL_PORT, task_wrapper); - assert_perror (err); - - /* Declare the new task to be our child. This is what a fork does. */ - err = proc_child (getproc (), traced_task); - if (err) - error (2, err, "proc_child"); - pid = task2pid (traced_task); - if (pid < 0) - error (2, errno, "task2pid"); - - /* Now actually run the command they told us to trace. We do the exec on - the actual task, so the RPCs to map in the program itself do not get - traced. Could have an option to use TASK_WRAPPER here instead. */ - err = _hurd_exec (traced_task, file, argv, envp); - if (err) - error (2, err, "cannot exec `%s'", argv[0]); - - /* We were keeping this send right alive so that the wrapper object - cannot die and hence our TRACED_TASK ref cannot have been released. */ - mach_port_deallocate (mach_task_self (), task_wrapper); - - return pid; -} -- cgit v1.2.3