bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] fully enable rpctrace to trace multitask programs.


From: Zheng Da
Subject: [PATCH] fully enable rpctrace to trace multitask programs.
Date: Sun, 31 May 2009 11:32:25 +0800

Hi,

I rewrite rpctrace so that it can trace multitask programs.
There are two major modifications:
1. tracing the source and destination of the RPC requests/replies. It
is required when we move the port rights and it also allows rpctrace
to show the caller of RPCs (I think it is a nice feature).
    In order to do it, I create a wrapper for each send right in each
task (each send right to the same port in different tasks has their
own wrappers in rpctrace). When rpctrace gets a RPC request/reply, it
can find the corresponding wrapper and thus, find the source task. I
also keep tracing the owner of the receive right, so I can find the
destination of messages.
    All traced tasks are identified with their original kernel task
ports and the tasks outside (no matter it's the kernel or other tasks
such as proc server) are all considered as UNKNOWN_TASK (as if there
is only one task in the system in addition to the traced tasks).
2. changing the strategy for moving port rights.
    The strategy for moving the send right is: if the destination task
has the receive right, the send right moved to the destination is the
one to the same port as the receive right; otherwise, it is the one to
the send wrapper in rpctrace.
    The strategy for the receive right is always the same: rpctrace
wraps the receive right from the source task and move the receive
right of the send wrapper for the destination task to the destination.

The patch can also fix some bugs reported in savannah, for example,
the traced task hangs when the it gets signals. I cannot access
savannah now, so I cannot list all references of these bugs.

I test the modified rpctrace with test, /hurd/hello, /hurd/ext2fs,
/hurd/pfinet, make, gcc/mig and some of my own test programs and it
works for most of these programs. It has known bugs that also exist in
the original rpctrace:
1. rpctrace hangs when it traces itself. I think the reason is that
the tracer and tracee both need to set the kernel task port and thread
port.
2. pfinet dies when it is traced by rpctrace. The reason is unknown.
3. gcc/mig reports "server type check failure" when it is traced by rpctrace.
(null):~/hurd/tmp# ../utils/rpctrace -o mig.rpc mig /usr/include/mach/mach.defs
i486-gnu-gcc: /usr/include/mach/mach.defs: (ipc/mig) server type check failure
i486-gnu-gcc: warning: '-x c' after last input file has no effect
i486-gnu-gcc: no input files
mig: fatal: no SubSystem declaration

The patch is below. Comments and tests are very welcome.
Thank you,
Zheng Da

2009-05-31  Zheng Da  <zhengda1936@gmail.com>

        *rpctrace.c: include sys/mman.h.
        (UNKNOWN_TASK): New macro.
        (UNKNOWN_NAME): Likewise.
        (traced_tasks): New variable.
        (nb_traced_tasks): Likewise.
        (add_task): New function.
        (remove_task): Likewise.
        (get_task): Likewise.
        (task): New field in traced_info.
        (portname): Likewise.
        (receive_right): Likewise.
        (next): Likewise.
        (receive): Renamed field in traced_info.
        (req_info): New structure.
        (req_head): New variable.
        (add_request): New function.
        (remove_request): Likewise.
        (notify_pi): New variable.
        (receive_right_list): New variable.
        (dummy_wrapper): New variable.
        (other_class): Likewise.
        (new_receive_wrapper): New function.
        (destroy_receive_wrapper): Likewise.
        (new_send_wrapper): Rewritten.
        (traced_clean): New function.
        (traced_dropweak): Rewritten; all send wrappers are destroyed 
completely.
        (seen_receive_right): New function.
        (discover_receive_right): Likewise.
        (get_send_wrapper): Likewise.
        (rewrite_right): Rewritten the way of handling send and receive rights.
        (print_contents): Add the argument 'req';
        don't treat mach_port_insert_right specially;
        handle thread_create and task_create specially;
        (trace_and_forward): Rewritten.
        (traced_spawn): Wrap new created task port properly.
        (main): Initialize the ports.

diff --git a/utils/rpctrace.c b/utils/rpctrace.c
index d80f41d..eb33af7 100644
--- a/utils/rpctrace.c
+++ b/utils/rpctrace.c
@@ -39,6 +39,7 @@
 #include <stdbool.h>
 #include <stddef.h>
 #include <argz.h>
+#include <sys/mman.h>

 const char *argp_program_version = STANDARD_HURD_VERSION (rpctrace);

@@ -59,6 +60,9 @@ static const struct argp_option options[] =
   {0}
 };

+#define UNKNOWN_TASK -1
+#define UNKNOWN_NAME -1
+
 static const char args_doc[] = "COMMAND [ARG...]";
 static const char doc[] = "Trace Mach Remote Procedure Calls.";
 
@@ -82,6 +86,49 @@ msgid_ihash_cleanup (void *element, void *arg)
 static struct hurd_ihash msgid_ihash
   = HURD_IHASH_INITIALIZER (HURD_IHASH_NO_LOCP);

+/* all traced tasks. */
+task_t *traced_tasks;
+size_t nb_traced_tasks;
+
+void
+add_task (task_t task)
+{
+  static size_t capability = 100;
+
+  if (traced_tasks == NULL)
+    traced_tasks = malloc (sizeof (task_t) * capability);
+  else if (nb_traced_tasks == capability)
+    {
+      capability *= 2;
+      traced_tasks = realloc (traced_tasks, capability);
+    }
+  assert (traced_tasks);
+
+  traced_tasks[nb_traced_tasks++] = task;
+}
+
+void
+remove_task (task_t task)
+{
+  int i,j;
+  for (i = 0, j = 0; i < nb_traced_tasks && j < nb_traced_tasks; i++, j++)
+    {
+      if (traced_tasks[j] == task)
+       j++;
+      traced_tasks[i] = traced_tasks[j];
+    }
+  nb_traced_tasks = i;
+}
+
+task_t
+get_task (int idx)
+{
+  if (idx >= nb_traced_tasks)
+    return -1;
+  assert (traced_tasks);
+  return traced_tasks[idx];
+}
+
 /* Parse a file of RPC names and message IDs as output by mig's -list
    option: "subsystem base-id routine n request-id reply-id".  Put each
    request-id value into `msgid_ihash' with the routine name as its value.  */
@@ -190,21 +237,40 @@ msgid_trace_replies (const struct msgid_info *info)
 /* We keep one of these structures for each port right we are tracing.  */
 struct traced_info
 {
+  /* 'pi' is only used for send right or send-once right,
+   * no task can get the send right or send-right to
+   * the wrapper for the receive right.*/
   struct port_info pi;

-  mach_port_t forward;         /* real port */
+  /* real port.
+     it is only used by send-once right and receive right. */
+  mach_port_t forward;
   mach_msg_type_name_t type;
-
+  /* the task who has the right.
+     it is only used by the receive right and send right */
+  task_t task;
+  /* the port name in the owner task.
+   * it is only used by the receive right. */
+  mach_port_t portname;
+
+  /* It is only used by the send right and the receive right.
+   * For the send right, it points to the corresponding receive right.
+   * For the receive right, it links to other receive rights. */
+  struct traced_info *receive_right;
+  /* it is used to form the list of send rights for different tasks.
+   * the head is the receive right. */
+  struct traced_info *next;
+                               
   char *name;                  /* null or a string describing this */

   union
   {
     struct traced_info *nextfree; /* Link when on free list.  */

-    struct                     /* For a send right wrapper.  */
+    struct                     /* For a receive right wrapper.  */
     {
       hurd_ihash_locp_t locp;  /* position in the traced_names hash table */
-    } send;
+    } receive;

     struct                     /* For a send-once right wrapper.  */
     {
@@ -220,11 +286,70 @@ struct traced_info
 };
 #define INFO_SEND_ONCE(info) ((info)->type == MACH_MSG_TYPE_MOVE_SEND_ONCE)

+struct req_info
+{
+  boolean_t valid;
+  mach_msg_id_t req_id;
+  mach_port_t reply_port;
+  task_t from;
+  task_t to;
+  struct req_info *next;
+};
+
+static struct req_info *req_head = NULL;
+
+static struct req_info *
+add_request (mach_msg_id_t req_id, mach_port_t reply_port,
+            task_t from, task_t to)
+{
+  struct req_info *req = malloc (sizeof (*req));
+  assert (req);
+  req->req_id = req_id;
+  req->from = from;
+  req->to = to;
+  req->reply_port = reply_port;
+  req->valid = TRUE;
+
+  req->next = req_head;
+  req_head = req;
+
+  return req;
+}
+
+static struct req_info *
+remove_request (mach_msg_id_t req_id, mach_port_t reply_port)
+{
+  struct req_info **prev;
+  struct req_info *req;
+
+  if (req_head == NULL)
+    return NULL;
+
+  prev = &req_head;
+  while (*prev)
+    {
+      if ((*prev)->req_id == req_id && (*prev)->reply_port == reply_port)
+       break;
+      prev = &(*prev)->next;
+    }
+  if (*prev == NULL)
+    return NULL;
+
+  req = *prev;
+  *prev = req->next;
+  return req;
+}
+
+struct port_info *notify_pi;
+/* The list of receive wrappers, but only the ones for the traced tasks. */
+struct traced_info *receive_right_list;
+static struct traced_info dummy_wrapper;
 static struct traced_info *freelist;

 struct hurd_ihash traced_names
-  = HURD_IHASH_INITIALIZER (offsetof (struct traced_info, u.send.locp));
+  = HURD_IHASH_INITIALIZER (offsetof (struct traced_info, u.receive.locp));
 struct port_class *traced_class;
+struct port_class *other_class;
 struct port_bucket *traced_bucket;
 FILE *ostream;

@@ -247,42 +372,115 @@ 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.  */
+/* Create a new wrapper for the receive right.
+ * The wrapper lives until the traced receive right dies. */
 static struct traced_info *
-new_send_wrapper (mach_port_t right, mach_port_t *wrapper_right)
+new_receive_wrapper (mach_port_t right, mach_port_t owner)
 {
   error_t err;
   struct traced_info *info;
+  mach_port_t foo;

-  /* Use a free send-once wrapper port if we have one.  */
-  if (freelist)
+  info = malloc (sizeof (*info));
+  assert (info);
+  info->forward = right;
+  info->type = MACH_MSG_TYPE_PORT_RECEIVE;
+  info->task = owner;
+  info->portname = UNKNOWN_NAME;
+  info->receive_right = NULL;
+  if (owner != UNKNOWN_TASK)
     {
-      info = freelist;
-      freelist = info->u.nextfree;
+      info->receive_right = receive_right_list;
+      receive_right_list = info;
     }
-  else
+  info->next = 0;
+  info->name = 0;
+
+  /* Request the dead-name notification,
+   * so if the receive right is destroyed,
+   * we can destroy the wrapper. */
+  err = mach_port_request_notification (mach_task_self (), right,
+                                       MACH_NOTIFY_DEAD_NAME, 1,
+                                       notify_pi->port_right,
+                                       MACH_MSG_TYPE_MAKE_SEND_ONCE, &foo);
+  assert_perror (err);
+  mach_port_deallocate (mach_task_self (), foo);
+
+  err = hurd_ihash_add (&traced_names, info->forward, info);
+  assert_perror (err);
+  return info;
+}
+
+static void
+destroy_receive_wrapper (struct traced_info *info)
+{
+  struct traced_info *send_wrapper;
+  struct traced_info **prev;
+
+  mach_port_deallocate (mach_task_self (), info->forward);
+  /* Remove it from the receive right list. */
+  prev = &receive_right_list;
+  if (*prev)
     {
-      /* Create a new wrapper port that forwards to *RIGHT.  */
-      err = ports_create_port (traced_class, traced_bucket,
-                              sizeof *info, &info);
-      assert_perror (err);
-      info->name = 0;
+      while (*prev != info && *prev)
+       prev = &((*prev)->receive_right);
+      /* If we find the receive wrapper in the list. */
+      if (*prev)
+       *prev = info->receive_right;
+    }
+
+  send_wrapper = info->next;
+  while (send_wrapper)
+    {
+      struct traced_info *next = send_wrapper->next;
+      /* we have to complete destroy the port,
+       * so the tasks who have the send right can be notified. */
+      assert (send_wrapper->pi.weakrefcnt == 1);
+      ports_port_deref_weak (send_wrapper);
+      assert (send_wrapper->pi.refcnt == 1);
+      ports_destroy_right (send_wrapper);
+      send_wrapper = next;
     }

-  info->forward = right;
-  info->type = MACH_MSG_TYPE_MOVE_SEND;
+  hurd_ihash_locp_remove (&traced_names, info->u.receive.locp);
+  free (info);
+}

-  /* 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 = hurd_ihash_add (&traced_names, info->forward, info);
+/* Create a new wrapper port and do `ports_get_right' on it.
+ *
+ * The wrapper lives until there is no send right to it,
+ * or the corresponding receive wrapper is destroyed.
+ */
+static struct traced_info *
+new_send_wrapper (struct traced_info *receive, task_t task,
+                 mach_port_t *wrapper_right)
+{
+  error_t err;
+  struct traced_info *info;
+
+  /* Create a new wrapper port that forwards to *RIGHT.  */
+  err = ports_create_port (traced_class, traced_bucket,
+                          sizeof *info, &info);
   assert_perror (err);
+  info->name = 0;
+
+  assert ((info->pi.flags & PORT_HAS_SENDRIGHTS) == 0);
+  asprintf (&info->name, "  %d<--%d(pid%d)",
+           receive->forward, info->pi.port_right, (int) task2pid (task));
+  info->forward = 0;
+  info->type = MACH_MSG_TYPE_MOVE_SEND;
+  info->task = task;
+  info->portname = UNKNOWN_NAME;
+  info->receive_right = receive;
+  info->next = receive->next;
+  receive->next = info;
+  info->u.nextfree = NULL;
+
   ports_port_ref_weak (info);
-  assert (info->u.send.locp != 0);

   *wrapper_right = ports_get_right (info);
   ports_port_deref (info);
@@ -302,6 +500,7 @@ new_send_once_wrapper (mach_port_t right,
mach_port_t *wrapper_right)
     {
       info = freelist;
       freelist = info->u.nextfree;
+      assert (info->pi.weakrefcnt == 0);
     }
   else
     {
@@ -314,6 +513,10 @@ new_send_once_wrapper (mach_port_t right,
mach_port_t *wrapper_right)

   info->forward = right;
   info->type = MACH_MSG_TYPE_MOVE_SEND_ONCE;
+  info->task = UNKNOWN_TASK;
+  info->portname = UNKNOWN_NAME;
+  info->receive_right = NULL;
+  info->next = NULL;

   /* Send-once rights never compare equal to any other right (even
      another send-once right), so there is no point in putting them
@@ -332,6 +535,32 @@ new_send_once_wrapper (mach_port_t right,
mach_port_t *wrapper_right)
   return info;
 }

+static void
+traced_clean (void *pi)
+{
+  struct traced_info **prev;
+  struct traced_info *info = pi;
+
+  assert (info->type == MACH_MSG_TYPE_MOVE_SEND);
+  if (info->name)
+    free (info->name);
+
+  if (info->receive_right)
+    {
+      /* Remove it from the send right list. */
+      prev = &info->receive_right->next;
+      if (*prev)
+       {
+         while (*prev != info && *prev)
+           prev = &((*prev)->next);
+         assert (*prev);
+         *prev = info->next;
+       }
+
+      info->next = NULL;
+      info->receive_right = NULL;
+    }
+}

 /* This gets called when a wrapper port has no hard refs (send rights),
    only weak refs.  The only weak ref is the one held in the reverse-lookup
@@ -339,74 +568,253 @@ new_send_once_wrapper (mach_port_t right,
mach_port_t *wrapper_right)
 static void
 traced_dropweak (void *pi)
 {
+  struct traced_info **prev;
   struct traced_info *const info = pi;

   assert (info->type == MACH_MSG_TYPE_MOVE_SEND);
-  assert (info->u.send.locp);

-  /* Remove INFO from the hash table.  */
-  hurd_ihash_locp_remove (&traced_names, info->u.send.locp);
+  free (info->name);
+  info->name = 0;
+
+  assert (info->pi.port_right);
+
+  /* Remove it from the send right list. */
+  prev = &info->receive_right->next;
+  assert (*prev);
+  if (*prev)
+    {
+      while (*prev != info && *prev)
+       prev = &((*prev)->next);
+      assert (*prev);
+      *prev = info->next;
+    }
+  /* This function is called because the send wrapper doesn't have
+   * any send right. If we find the all send wrappers are gone,
+   * it means that our traced port doesn't have send right.
+   * We notify the owner of the receive right by deallocating
+   * the forward port. */
+  if (info->receive_right->next == NULL)
+    {
+      fprintf (ostream, "no senders\n");
+      destroy_receive_wrapper (info->receive_right);
+    }
+
+  info->next = NULL;
+  info->receive_right = NULL;
+
   ports_port_deref_weak (info);
+}

-  /* Deallocate the forward port, so the real port also sees no-senders.  */
-  mach_port_deallocate (mach_task_self (), info->forward);
+boolean_t
+seen_receive_right (task_t task, mach_port_t name)
+{
+  struct traced_info *info = receive_right_list;
+  while (info)
+    {
+      if (info->task == task && info->portname == name)
+       return TRUE;
+      info = info->next;
+    }
+  return FALSE;
+}

-  /* 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);
+/* This function is to find the receive right for the send right 'send'
+ * among traced tasks. I assume that all receive rights are moved
+ * under the control of rpctrace.
+ *
+ * Note: 'send' shouldn't be the send right to the wrapper.
+ *
+ * Note: the traced_info returned from the function
+ * might not be the receive right in the traced tasks.
+ * */
+struct traced_info *
+discover_receive_right (mach_port_t send)
+{
+  int i;
+  error_t err;
+  struct traced_info *info;

-  free (info->name);
-  info->name = 0;
+  info = hurd_ihash_find (&traced_names, send);
+  /* If we have seen the send right or send once right. */
+  if (info
+      /* If the receive right is in one of traced tasks,
+       * but we don't know its name
+       * (probably because the receive name has been moved),
+       * we need to find it out. */
+      && !(info->task != UNKNOWN_TASK
+         && info->portname == UNKNOWN_NAME))
+    return info;
+
+  for (i = 0; i < nb_traced_tasks; i++)
+    {
+      int j;
+      mach_port_t *portnames = NULL;
+      mach_msg_type_number_t nportnames = 0;
+      mach_port_type_t *porttypes = NULL;
+      mach_msg_type_number_t nporttypes = 0;
+      struct traced_info *receive_wrapper = NULL;
+      task_t task = traced_tasks[i];
+      err = mach_port_names (task, &portnames, &nportnames,
+                            &porttypes, &nporttypes);
+      if (err)
+       {
+         remove_task (task);
+         continue;
+       }

-  info->u.nextfree = freelist;
-  freelist = info;
+      for (j = 0; j < nportnames; j++)
+       {
+         mach_port_t send_right;
+         mach_msg_type_name_t type;
+         if (!(porttypes[j] & MACH_PORT_TYPE_RECEIVE) /* not a receive right */
+             || (porttypes[j] & MACH_PORT_TYPE_RECEIVE
+                 && seen_receive_right (task, portnames[j])))
+           continue;
+
+         err = mach_port_extract_right (task, portnames[j],
+                                        MACH_MSG_TYPE_MAKE_SEND,
+                                        &send_right, &type);
+         assert_perror (err);
+
+         receive_wrapper = hurd_ihash_find (&traced_names, send_right);
+         /* If we have seen this send right before. */
+         if (receive_wrapper)
+           mach_port_deallocate (mach_task_self (), send_right);
+         else
+           receive_wrapper = new_receive_wrapper (send_right, task);
+
+         receive_wrapper->portname = portnames[j];
+
+         /* We have found the wrapper for the receive right. */
+         if (send_right == send)
+           break;
+         receive_wrapper = NULL;
+       }
+      if (portnames)
+       vm_deallocate (mach_task_self (), (vm_address_t) portnames,
+                      nportnames * sizeof (*portnames));
+      if (porttypes)
+       vm_deallocate (mach_task_self (), (vm_address_t) porttypes,
+                      nporttypes * sizeof (*porttypes));
+
+      if (receive_wrapper)
+       return receive_wrapper;
+    }
+  return NULL;
 }

+/* get_send_wrapper searches for the wrapper
+   of the send right for the target task.
+   if it doesn't exist, create a new one. */
+struct traced_info *get_send_wrapper (struct traced_info *receive_wrapper,
+                                     mach_port_t task, mach_port_t *right)
+{
+  struct traced_info *info = receive_wrapper->next;
+
+  while (info)
+    {
+      if (info->task == task)
+       {
+         *right = ports_get_right (info);
+         assert (info->pi.refcnt == 1);
+         return info;
+       }
+      info = info->next;
+    }
+  /* no send wrapper is found. */
+  return new_send_wrapper (receive_wrapper, task, right);
+}

 /* Rewrite a port right in a message with an appropriate wrapper port.  */
 static struct traced_info *
-rewrite_right (mach_port_t *right, mach_msg_type_name_t *type)
+rewrite_right (mach_port_t *right, mach_msg_type_name_t *type,
+              struct req_info *req)
 {
   error_t err;
-  struct traced_info *info;
+  struct traced_info *receive_wrapper;
+  struct traced_info *send_wrapper;
+  task_t dest = UNKNOWN_TASK;
+  task_t source = UNKNOWN_TASK;

   /* We can never do anything special with a null or dead port right.  */
   if (!MACH_PORT_VALID (*right))
     return 0;

-  switch (*type)
+  if (req)
     {
-    case MACH_MSG_TYPE_PORT_SEND:
-      /* See if we are already tracing this port.  */
-      info = hurd_ihash_find (&traced_names, *right);
-      if (info)
+      if (req->valid)    /* it's a RPC request */
        {
-         /* We are already tracing this port.  We will pass on a right
-            to our existing wrapper port.  */
-         *right = ports_get_right (info);
-         *type = MACH_MSG_TYPE_MAKE_SEND;
-         return info;
+         source = req->from;
+         dest = req->to;
+       }
+      else
+       {
+         source = req->to;
+         dest = req->from;
        }
+    }
+
+  switch (*type)
+    {
+    case MACH_MSG_TYPE_PORT_SEND:
+      assert (req);

       /* See if this is already one of our own wrapper ports.  */
-      info = ports_lookup_port (traced_bucket, *right, 0);
-      if (info)
+      send_wrapper = ports_lookup_port (traced_bucket, *right, 0);
+      if (send_wrapper)
        {
-         /* This is a send right to one of our own wrapper ports.
-            Instead, send along the original send right.  */
+         /* This is a send right to one of our own wrapper ports. */
          mach_port_deallocate (mach_task_self (), *right); /* eat msg ref */
-         *right = info->forward;
-         err = mach_port_mod_refs (mach_task_self (), *right,
-                                   MACH_PORT_RIGHT_SEND, +1);
-         assert_perror (err);
-         ports_port_deref (info);
-         return info;
+         /*  If the send right is moved to the task with the receive right,
+          * translate it.*/
+         assert (send_wrapper->receive_right);
+         if (dest == send_wrapper->receive_right->task)
+           {
+             *right = send_wrapper->receive_right->forward;
+             err = mach_port_mod_refs (mach_task_self (), *right,
+                                       MACH_PORT_RIGHT_SEND, +1);
+             assert_perror (err);
+             ports_port_deref (send_wrapper);
+           }
+         else
+           {
+             struct traced_info *send_wrapper2
+               = get_send_wrapper (send_wrapper->receive_right, dest, right);
+             ports_port_deref (send_wrapper);
+             *type = MACH_MSG_TYPE_MAKE_SEND;
+             send_wrapper = send_wrapper2;
+           }
+         return send_wrapper;
        }

-      /* We have never seen this port before.  Create a new wrapper port
-        and replace the right in the message with a right to it.  */
-      *type = MACH_MSG_TYPE_MAKE_SEND;
-      return new_send_wrapper (*right, right);
+      receive_wrapper = discover_receive_right (*right);
+      if (receive_wrapper == NULL)
+       {
+         /* It's unusual to see an unknown send right from a traced task.
+          * We ignore it. */
+         if (source != UNKNOWN_TASK)
+           {
+             error (0, 0, "get an unknown send right from process %d",
+                    (int) task2pid (source));
+             return &dummy_wrapper;
+           }
+         /* the receive right is owned by an unknown task. */
+         receive_wrapper = new_receive_wrapper (*right, UNKNOWN_TASK);
+         mach_port_mod_refs (mach_task_self (), *right,
+                             MACH_PORT_RIGHT_SEND, 1);
+       }
+      /* if the send right is moved to the task with the receive right,
+       * don't do anything. */
+      if (dest == receive_wrapper->task)
+       return receive_wrapper;
+      else
+       {
+         assert (*right == receive_wrapper->forward);
+         mach_port_deallocate (mach_task_self (), *right);
+         send_wrapper = get_send_wrapper (receive_wrapper, dest, right);
+         *type = MACH_MSG_TYPE_MAKE_SEND;
+         return send_wrapper;
+       }

     case MACH_MSG_TYPE_PORT_SEND_ONCE:
       /* There is no way to know if this send-once right is to the same
@@ -429,49 +837,86 @@ rewrite_right (mach_port_t *right,
mach_msg_type_name_t *type)
         to A that were sent through and replaced with our wrapper (B).
         If not, we create a new receive right.  */
       {
-       mach_port_t rr;         /* B */
-       char *name;
-
-       info = hurd_ihash_find (&traced_names, *right);
-       if (info)
+       assert (req);
+       receive_wrapper = hurd_ihash_find (&traced_names, *right);
+       if (receive_wrapper)
          {
-           /* 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.  */
+           struct traced_info *send_wrapper2;
+           char *name;
+           mach_port_t rr;
+
+           /* The port has at least one send right - the one
+            * in receive_wrapper->forward.
+            * We wrap the port.
+            * if the source task doesn't have the send right, the port
+            * will be destroyed after we deallocate the only send right. */
+           /* We have to deallocate the send right in
+            * receive_wrapper->forward before we import the port to
+            * port_info. So the reference count in the port info will be 1,
+            * if it doesn't have any other send rights. */
+           mach_port_deallocate (mach_task_self (), receive_wrapper->forward);
+           err = ports_import_port (traced_class, traced_bucket,
+                                    *right, sizeof *send_wrapper,
+                                    &send_wrapper);
+           assert_perror (err);
+           send_wrapper->forward = 0;
+           send_wrapper->type = MACH_MSG_TYPE_MOVE_SEND;
+           send_wrapper->task = source;
+           send_wrapper->portname = UNKNOWN_NAME;
+           send_wrapper->name = receive_wrapper->name;
+           /* Initialize them in case that the source task doesn't
+            * have the send right to the port, and the port will
+            * be destroyed immediately. */
+           send_wrapper->receive_right = NULL;
+           send_wrapper->next = NULL;
+           ports_port_ref_weak (send_wrapper);
+           ports_port_deref (send_wrapper);
+
+           hurd_ihash_locp_remove (&traced_names, 
receive_wrapper->u.receive.locp);
+
+           send_wrapper2 = get_send_wrapper (receive_wrapper, dest, &rr);
+           assert (send_wrapper2->pi.refcnt == 1);
+           name = send_wrapper2->name;
+           send_wrapper2->name = NULL;
+           /* The port has been removed from the send wrapper,
+            * so we cannot put it in the free list. */
+           ports_port_deref_weak (send_wrapper2);
+           rr = ports_claim_right (send_wrapper2);
+           /* Get us a send right that we will forward on.  */
+           err = mach_port_insert_right (mach_task_self (), rr, rr,
+                                         MACH_MSG_TYPE_MAKE_SEND);
+           assert_perror (err);
+           receive_wrapper->forward = rr;
+           receive_wrapper->task = dest;
+           if (dest != UNKNOWN_TASK)
+             {
+               receive_wrapper->receive_right = receive_right_list;
+               receive_right_list = receive_wrapper;
+             }
+           /* the port name will be discovered
+            * when we search for this receive right. */
+           receive_wrapper->portname = UNKNOWN_NAME;
+           receive_wrapper->name = name;
+
+           send_wrapper->receive_right = receive_wrapper;
+           send_wrapper->next = receive_wrapper->next;
+           receive_wrapper->next = send_wrapper;
+
+           err = hurd_ihash_add (&traced_names, receive_wrapper->forward,
+                                 receive_wrapper);
+           assert_perror (err);
+           *right = rr;
          }
        else
          {
-           /* This is a port we know nothing about.  */
-           rr = mach_reply_port ();
-           name = 0;
+           /* weird? no send right for the port. */
+           err = mach_port_insert_right (mach_task_self (), *right, *right,
+                                         MACH_MSG_TYPE_MAKE_SEND);
+           assert_perror (err);
+           receive_wrapper = new_receive_wrapper (*right, dest);
          }

-       /* 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,
-                                     MACH_MSG_TYPE_MAKE_SEND);
-       assert_perror (err);
-       info->forward = rr;
-
-       err = hurd_ihash_add (&traced_names, info->forward, info);
-       assert_perror (err);
-       ports_port_ref_weak (info);
-
-       /* If there are no extant send rights to this port, then INFO will
-          die right here and release its send right to RR.
-          XXX what to do?
-       */
-       ports_port_deref (info);
-
-       *right = rr;
-       return info;
+       return receive_wrapper;
       }

     default:
@@ -482,7 +927,7 @@ rewrite_right (mach_port_t *right,
mach_msg_type_name_t *type)

 static void
 print_contents (mach_msg_header_t *inp,
-               void *msg_buf_ptr)
+               void *msg_buf_ptr, struct req_info *req)
 {
   error_t err;

@@ -523,7 +968,7 @@ print_contents (mach_msg_header_t *inp,
       else
        msg_buf_ptr += ((nelt * eltsize + sizeof(natural_t) - 1)
                        & ~(sizeof(natural_t) - 1));
-
+
       if (first)
        first = 0;
       else
@@ -549,20 +994,58 @@ print_contents (mach_msg_header_t *inp,
          poly = 0;
          for (i = 0; i < nelt; ++i)
            {
+             mach_port_t orig_port = portnames[i];
              newtypes[i] = name;

-             if (inp->msgh_id == 3215) /* mach_port_insert_right */
+             ti = rewrite_right (&portnames[i], &newtypes[i], req);
+
+             if (inp->msgh_id == 2161 /* the reply message for thread_create */
+                 || inp->msgh_id == 2107 /* for task_create */)
                {
-                 /* XXX
-                  */
-                 fprintf (ostream,
-                          "\t\t[%d] = pass through port %d, type %d\n",
-                          i, portnames[i], name);
-                 continue;
+                 if (inp->msgh_id == 2161)
+                   {
+                     err = mach_port_insert_right (mach_task_self (),
+                                                   portnames[i], portnames[i],
+                                                   MACH_MSG_TYPE_MAKE_SEND);
+                     assert_perror (err);
+                     err = thread_set_kernel_port (orig_port, portnames[i]);
+                     assert_perror (err);
+                     if (ti->name)
+                       free (ti->name);
+                     asprintf (&ti->name, "thread%d(pid%d)",
+                               (int) orig_port, (int) task2pid (req->from));
+                   }
+                 else
+                   {
+                     pid_t pid;
+                     struct traced_info *receive_wrapper;
+                     struct traced_info *task_wrapper;
+                     mach_port_t pseudo_task;
+                     add_task (orig_port);
+                     receive_wrapper = hurd_ihash_find (&traced_names, 
orig_port);
+                     assert (receive_wrapper == ti->receive_right);
+                     task_wrapper = new_send_wrapper (receive_wrapper,
+                                                      orig_port, &pseudo_task);
+                     err = mach_port_insert_right (mach_task_self (),
+                                                   pseudo_task, pseudo_task,
+                                                   MACH_MSG_TYPE_MAKE_SEND);
+                     assert_perror (err);
+                     err = task_set_kernel_port (orig_port, pseudo_task);
+                     assert_perror (err);
+
+                     pid = task2pid (orig_port);
+                     if (ti->name)
+                       free (ti->name);
+                     asprintf (&ti->name, "pid%d(pid%d)",
+                               (int) pid, (int) task2pid (req->from));
+                     free (task_wrapper->name);
+                     asprintf (&task_wrapper->name, "pid%d(pid%d)",
+                               (int) pid, (int) pid);
+                   }
+
+                 mach_port_deallocate (mach_task_self (), portnames[i]);
                }

-             ti = rewrite_right (&portnames[i], &newtypes[i]);
-
              putc ((i == 0 && nelt > 1) ? '{' : ' ', ostream);

              if (portnames[i] == MACH_PORT_NULL)
@@ -632,19 +1115,24 @@ print_contents (mach_msg_header_t *inp,
                type->msgt_name = name;
            }
          else if (newtypes[0] != name)
-           if (type->msgt_longform)
-             lt->msgtl_name = newtypes[0];
-           else
-             type->msgt_name = newtypes[0];
+           {
+             if (type->msgt_longform)
+               lt->msgtl_name = newtypes[0];
+             else
+               type->msgt_name = newtypes[0];
+           }
        }
       else
        print_data (name, data, nelt, eltsize);
+      fflush (ostream);
     }
 }

 int
 trace_and_forward (mach_msg_header_t *inp, mach_msg_header_t *outp)
 {
+  mach_port_t reply_port;
+
   const mach_msg_type_t RetCodeType =
   {
     MACH_MSG_TYPE_INTEGER_32,  /* msgt_name = */
@@ -671,19 +1159,22 @@ trace_and_forward (mach_msg_header_t *inp,
mach_msg_header_t *outp)
      with a send-once right, even if there have never really been any.  */
   if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE)
     {
-      if (inp->msgh_id == MACH_NOTIFY_DEAD_NAME)
+      if (inp->msgh_id == MACH_NOTIFY_DEAD_NAME && info == (void *) notify_pi)
        {
-         /* If INFO is a send-once wrapper, this could be a forged
-            notification; oh well.  XXX */
-
+         struct traced_info *receive_wrapper;
          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);
+         receive_wrapper = hurd_ihash_find (&traced_names, n->not_port);
+         /* The receive wrapper might have been destroyed. */
+         if (receive_wrapper)
+           {
+             assert (n->not_port == receive_wrapper->forward);
+             destroy_receive_wrapper (receive_wrapper);
+           }
          ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+         ports_port_deref (info);
          return 1;
        }
       else if (inp->msgh_id == MACH_NOTIFY_NO_SENDERS
@@ -698,43 +1189,109 @@ trace_and_forward (mach_msg_header_t *inp,
mach_msg_header_t *outp)
          ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
          return 1;
        }
+      /* Get some unexpected notification for rpctrace itself,
+       * TODO ignore them for now. */
+      else if (info == (void *) notify_pi)
+       {
+         ports_port_deref (info);
+         ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+         return 1;
+       }
     }

+  assert (info != (void *) notify_pi);
   assert (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) == info->type);

   complex = inp->msgh_bits & MACH_MSGH_BITS_COMPLEX;

   msgid = msgid_info (inp->msgh_id);

+  /* If it's the request of exec_startup_get_info,
+   * it means that the traced process starts to run */
+  if (inp->msgh_id == 30500)
+    {
+      struct traced_info *thread_send_wrapper;
+      struct traced_info *thread_receive_wrapper;
+      thread_t *threads = NULL;
+      size_t nthreads = 0;
+      err = task_threads (get_task(0), &threads, &nthreads);
+      if (err)
+       error (1, err, "task_threads");
+
+      for (int i = 0; i < nthreads; i++)
+       {
+         /* If we hasn't seen the port. */
+         thread_receive_wrapper = hurd_ihash_find (&traced_names, threads[i]);
+         if (thread_receive_wrapper == NULL)
+           {
+             mach_port_t new_thread_port;
+             thread_receive_wrapper = new_receive_wrapper (threads[i], 
UNKNOWN_TASK);
+             thread_send_wrapper = new_send_wrapper (thread_receive_wrapper,
+                                                     info->task, 
&new_thread_port);
+             free (thread_send_wrapper->name);
+             asprintf (&thread_send_wrapper->name, "thread%d(pid%d)",
+                       (int) threads[i], (int) task2pid (info->task));
+       
+             err = mach_port_insert_right (mach_task_self (),
+                                           new_thread_port, new_thread_port,
+                                           MACH_MSG_TYPE_MAKE_SEND);
+             if (err)
+               error (0, err, "mach_port_insert_right");
+
+             err = thread_set_kernel_port (threads[i], new_thread_port);
+             if (err)
+               error (0, err, "thread_set_kernel_port");
+
+             mach_port_deallocate (mach_task_self (), new_thread_port);
+           }
+       }
+      munmap (threads, nthreads * sizeof (thread_t));
+    }
+
   /* 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);
+
+    /* Save the original reply port in the RPC request. */
+    reply_port = inp->msgh_remote_port;

     inp->msgh_local_port = inp->msgh_remote_port;
     if (reply_type && msgid_trace_replies (msgid))
       {
-       struct traced_info *info;
-       info = rewrite_right (&inp->msgh_local_port, &reply_type);
-       assert (info);
-       if (info->name == 0)
-         {
-           if (msgid == 0)
-             asprintf (&info->name, "reply(%u:%u)",
-                       (unsigned int) info->pi.port_right,
-                       (unsigned int) inp->msgh_id);
-           else
-             asprintf (&info->name, "reply(%u:%s)",
-                       (unsigned int) info->pi.port_right, msgid->name);
-         }
-       if (info->type == MACH_MSG_TYPE_MOVE_SEND_ONCE)
+       struct traced_info *reply_pi;
+       /* It must be send-once right. */
+       reply_pi = rewrite_right (&inp->msgh_local_port, &reply_type, NULL);
+       /* The reply port might be dead, e.g., the traced task has died.
+        * If so, we don't need to trace the reply message. */
+       if (reply_pi)
          {
-           info->u.send_once.sent_to = info->pi.port_right;
-           info->u.send_once.sent_msgid = inp->msgh_id;
+           assert (inp->msgh_local_port);
+           if (reply_pi->name == 0)
+             {
+               if (msgid == 0)
+                 asprintf (&reply_pi->name, "reply(%u:%u)",
+                           (unsigned int) reply_pi->pi.port_right,
+                           (unsigned int) inp->msgh_id);
+               else
+                 asprintf (&reply_pi->name, "reply(%u:%s)",
+                           (unsigned int) reply_pi->pi.port_right, 
msgid->name);
+             }
+           if (reply_pi->type == MACH_MSG_TYPE_MOVE_SEND_ONCE)
+             {
+               reply_pi->u.send_once.sent_to = reply_pi->pi.port_right;
+               reply_pi->u.send_once.sent_msgid = inp->msgh_id;
+             }
          }
       }

-    inp->msgh_remote_port = info->forward;
+    if (info->type == MACH_MSG_TYPE_MOVE_SEND_ONCE)
+      inp->msgh_remote_port = info->forward;
+    else
+      {
+       assert (info->receive_right);
+       inp->msgh_remote_port = info->receive_right->forward;
+      }
     if (this_type == MACH_MSG_TYPE_MOVE_SEND_ONCE)
       {
        /* We have a message to forward for a send-once wrapper object.
@@ -744,6 +1301,7 @@ trace_and_forward (mach_msg_header_t *inp,
mach_msg_header_t *outp)
           we are consuming its `forward' right in the message we send.  */
        free (info->name);
        info->name = 0;
+       info->forward = 0;
        info->u.nextfree = freelist;
        freelist = info;
       }
@@ -762,22 +1320,57 @@ trace_and_forward (mach_msg_header_t *inp,
mach_msg_header_t *outp)
          && info->type == MACH_MSG_TYPE_MOVE_SEND_ONCE
          && inp->msgh_size >= sizeof (mig_reply_header_t)
          && (*(int *) &((mig_reply_header_t *) inp)->RetCodeType
-             == *(int *)&RetCodeType))
+             == *(int *)&RetCodeType)
+         /* The notification message is considered as a request. */
+         && (inp->msgh_id > 72 || inp->msgh_id < 64))
        {
+         struct req_info *req = remove_request (inp->msgh_id - 100,
+                                                inp->msgh_remote_port);
+         assert (req);
+         req->valid = FALSE;
          /* 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);
+         fflush (ostream);
+         print_contents (&rh->Head, rh + 1, req);
          putc ('\n', ostream);
+         free (req);
        }
       else
        {
+         task_t to = 0;
+         struct req_info *req;
+
          /* Print something about the message header.  */
          print_request_header (info, inp);
-         print_contents (inp, inp + 1);
+         fflush (ostream);
+         /* If it's a nofication message,
+          * I hope there is no port right in the message body. */
+         if (inp->msgh_id <= 72 && inp->msgh_id >= 64)
+           {
+             assert (info->type == MACH_MSG_TYPE_MOVE_SEND_ONCE);
+             /* mach_notify_port_destroyed message has a port,
+              * TODO how do I handle it? */
+             assert (inp->msgh_id != 69);
+           }
+         /* if it's mach_port RPC,
+          * the port rights in the message will be moved to the target task. */
+         else if (inp->msgh_id >= 3200 && inp->msgh_id <= 3218)
+           to = info->receive_right->forward;
+         else
+           to = info->receive_right->task;
+         req = add_request (inp->msgh_id, reply_port, info->task, to);
+         print_contents (inp, inp + 1, req);
          if (inp->msgh_local_port == MACH_PORT_NULL) /* simpleroutine */
-           fprintf (ostream, ");\n");
+           {
+             /* If it's a simpleroutine,
+              * we don't need the request information any more. */
+             req = remove_request (inp->msgh_id, reply_port);
+             assert (req);
+             free (req);
+             fprintf (ostream, ");\n");
+           }
          else
            /* Leave a partial line that will be finished later.  */
            fprintf (ostream, ")");
@@ -1014,8 +1607,6 @@ print_data (mach_msg_type_name_t type,
 
 /*** Main program and child startup ***/

-task_t traced_task;
-

 /* Run a child and have it do more or else `execvpe (argv, envp);'.  */
 pid_t
@@ -1024,7 +1615,9 @@ traced_spawn (char **argv, char **envp)
   error_t err;
   pid_t pid;
   mach_port_t task_wrapper;
+  task_t traced_task;
   struct traced_info *ti;
+  struct traced_info *receive_ti;
   file_t file = file_name_path_lookup (argv[0], getenv ("PATH"),
                                       O_EXEC, 0, 0);

@@ -1038,6 +1631,7 @@ traced_spawn (char **argv, char **envp)
                     0, &traced_task);
   assert_perror (err);

+  add_task (traced_task);
   /* Declare the new task to be our child.  This is what a fork does.  */
   err = proc_child (getproc (), traced_task);
   if (err)
@@ -1046,9 +1640,12 @@ traced_spawn (char **argv, char **envp)
   if (pid < 0)
     error (2, errno, "task2pid");

+  receive_ti = new_receive_wrapper (traced_task, UNKNOWN_TASK);
   /* 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);
+  ti = new_send_wrapper (receive_ti, traced_task, &task_wrapper);
+  ti->task = traced_task;
+  free (ti->name);
+  asprintf (&ti->name, "pid%d(pid%d)", (int) pid, (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
@@ -1124,6 +1721,7 @@ main (int argc, char **argv, char **envp)
   bool nostdinc = FALSE;
   const char *outfile = 0;
   char **cmd_argv = 0;
+  error_t err;

   /* Parse our options...  */
   error_t parse_opt (int key, char *arg, struct argp_state *state)
@@ -1196,7 +1794,11 @@ main (int argc, char **argv, char **envp)
   setlinebuf (ostream);

   traced_bucket = ports_create_bucket ();
-  traced_class = ports_create_class (0, &traced_dropweak);
+  traced_class = ports_create_class (&traced_clean, &traced_dropweak);
+  other_class = ports_create_class (0, 0);
+  err = ports_create_port (other_class, traced_bucket,
+                          sizeof (*notify_pi), &notify_pi);
+  assert_perror (err);

   hurd_ihash_set_cleanup (&msgid_ihash, msgid_ihash_cleanup, 0);

@@ -1222,6 +1824,8 @@ main (int argc, char **argv, char **envp)
     else
       fprintf (ostream, "Child %d %s\n", pid, strsignal (WTERMSIG (status)));
   }
+
+  ports_destroy_right (notify_pi);

   return 0;
 }




reply via email to

[Prev in Thread] Current Thread [Next in Thread]