summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--utils/rpctrace.c706
1 files 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 <josem@gnu.org>
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 <hurd.h>
#include <hurd/ports.h>
#include <hurd/ihash.h>
@@ -32,6 +32,7 @@
#include <string.h>
#include <version.h>
#include <sys/wait.h>
+#include <inttypes.h>
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;
-}