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

	* rpctrace.c (req_info): New structure.
	(receive_task): New field in traced_info.
	(new_send_wrapper): Initialize receive_task.
	(new_send_once_wrapper): Likewise.
	(rewrite_right): Deallocate the send right; don't destroy the port.
	(handle_port_insert_send_right): New function.
	(print_contents): Add a new argument; handle mach_port_insert_right().
	(handle_port_alloc_reply): New function.
	(trace_and_forward): Handle mach_port_allocate() and
			     mach_port_allocate_name() specially.

diff --git a/utils/rpctrace.c b/utils/rpctrace.c
index 595cf11..49219c7 100644
--- a/utils/rpctrace.c
+++ b/utils/rpctrace.c
@@ -70,6 +70,18 @@ struct msgid_info
   char *subsystem;
 };
 
+struct req_info
+{
+  boolean_t valid;
+  mach_msg_id_t req_id;
+  mach_port_seqno_t seqno;
+  task_t task;
+  union 
+    {
+      mach_port_t portname;
+    } u;
+};
+
 static void
 msgid_ihash_cleanup (void *element, void *arg)
 {
@@ -197,6 +209,9 @@ struct traced_info
   mach_port_t forward;		/* real port */
   mach_msg_type_name_t type;
 
+  task_t receive_task;		/* the task who has the receive right.
+				   it only used by the traced receive right */
+				
   char *name;			/* null or a string describing this */
 
   union
@@ -277,6 +292,7 @@ new_send_wrapper (mach_port_t right, mach_port_t *wrapper_right)
 
   info->forward = right;
   info->type = MACH_MSG_TYPE_MOVE_SEND;
+  info->receive_task = -1;
 
   /* Store it in the reverse-lookup hash table, so we can
      look up this same right again to find the wrapper port.
@@ -316,6 +332,7 @@ 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->receive_task = -1;
 
   /* Send-once rights never compare equal to any other right (even
      another send-once right), so there is no point in putting them
@@ -385,6 +402,7 @@ rewrite_right (mach_port_t *right, mach_msg_type_name_t *type)
 	{
 	  /* We are already tracing this port.  We will pass on a right
 	     to our existing wrapper port.  */
+	  mach_port_deallocate (mach_task_self (), *right);
 	  *right = ports_get_right (info);
 	  *type = MACH_MSG_TYPE_MAKE_SEND;
 	  return info;
@@ -470,7 +488,10 @@ rewrite_right (mach_port_t *right, mach_msg_type_name_t *type)
 	   die right here and release its send right to RR.
 	   XXX what to do?
 	*/
+	/* TODO where should I destroy the port. */
+#if 0
 	ports_port_deref (info);
+#endif
 
 	*right = rr;
 	return info;
@@ -483,8 +504,76 @@ rewrite_right (mach_port_t *right, mach_msg_type_name_t *type)
 }
 
 static void
+handle_port_insert_send_right (mach_port_t *right, mach_msg_type_name_t *type,
+			       struct req_info *req)
+{
+  error_t err;
+  struct traced_info *info;
+
+  /* We can never do anything special with a null or dead port right.  */
+  if (!MACH_PORT_VALID (*right))
+    return 0;
+
+  switch (*type)
+    {
+    case MACH_MSG_TYPE_PORT_SEND:
+      info = hurd_ihash_find (&traced_names, *right);
+      if (info)
+	{
+	  /* If the send right is moved to the task who has the receive right,
+	   * don't do anything. */
+	  fprintf (ostream,
+		   "port_insert: get from traced_names: req: %d, info: %d\n",
+		   req->task, info->receive_task);
+	  if (req->task == info->receive_task)
+	    ;
+	  else
+	    {
+	      /* We are already tracing this port.  We will pass on a right
+		 to our existing wrapper port.  */
+	      mach_port_deallocate (mach_task_self (), *right);
+	      *right = ports_get_right (info);
+	      *type = MACH_MSG_TYPE_MAKE_SEND;
+	    }
+	  return info;
+	}
+
+      /* See if this is already one of our own wrapper ports.  */
+      info = ports_lookup_port (traced_bucket, *right, 0);
+      if (info)
+	{
+	  fprintf (ostream,
+		   "port_insert: get from traced_bucket: req: %d, info: %d\n",
+		   req->task, info->receive_task);
+	  /* If the send right is moved to the task who has the receive right,
+	   * translate it. Otherwise, don't do anything. */
+	  if (req->task == info->receive_task)
+	    {
+	      /* This is a send right to one of our own wrapper ports.
+		 Instead, send along the original send right.  */
+	      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;
+	}
+
+      /* We have never seen this port before.  Create a new wrapper port
+	 and replace the right in the message with a right to it.  */
+      fprintf (ostream, "create a new send right\n");
+      *type = MACH_MSG_TYPE_MAKE_SEND;
+      return new_send_wrapper (*right, right);
+    case MACH_MSG_TYPE_PORT_RECEIVE:
+      return rewrite_right (right, 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;
 
@@ -561,6 +650,9 @@ print_contents (mach_msg_header_t *inp,
 		  fprintf (ostream,
 			   "\t\t[%d] = pass through port %d, type %d\n",
 			   i, portnames[i], name);
+		  assert (req);
+		  handle_port_insert_send_right (&portnames[i],
+						 &newtypes[i], req);
 		  continue;
 		}
 
@@ -681,8 +773,45 @@ print_contents (mach_msg_header_t *inp,
 }
 
 int
+handle_port_alloc_reply (struct req_info *req)
+{
+  mach_port_t right;
+  mach_msg_type_name_t type;
+  error_t err;
+
+  err = mach_port_extract_right (req->task, req->u.portname,
+				 MACH_MSG_TYPE_PORT_RECEIVE,
+				 &right, &type);
+  if (err)
+    {
+      error (0, err, "mach_port_extract_right");
+      mach_port_destroy (req->task, req->u.portname);
+    }
+  else
+    {
+      struct traced_info *info;
+      assert (type == MACH_MSG_TYPE_PORT_RECEIVE);
+      info = rewrite_right (&right, &type);
+      err = mach_port_insert_right (req->task, req->u.portname, right,
+				    MACH_MSG_TYPE_MOVE_RECEIVE);
+      info->receive_task = req->task;
+      if (err)
+	{
+	  error (0, err, "mach_port_insert_right: name %d",
+		 req->u.portname);
+	  mach_port_destroy (mach_task_self (), right);
+	  ports_destroy_right (info);
+	}
+    }
+  req->valid = FALSE;
+  return err;
+}
+
+int
 trace_and_forward (mach_msg_header_t *inp, mach_msg_header_t *outp)
 {
+  static struct req_info req;
+
   const mach_msg_type_t RetCodeType =
   {
     MACH_MSG_TYPE_INTEGER_32,	/* msgt_name = */
@@ -842,18 +971,89 @@ trace_and_forward (mach_msg_header_t *inp, mach_msg_header_t *outp)
 	  && (*(int *) &((mig_reply_header_t *) inp)->RetCodeType
 	      == *(int *)&RetCodeType))
 	{
+	  /* It's the reply of mach_port_allocate_name */
+	  if (inp->msgh_id == 3303)
+	    {
+	      struct
+		{
+		  mach_msg_header_t Head;
+		  mach_msg_type_t RetCodeType;
+		  kern_return_t RetCode;
+		} *reply = (void *) inp;
+	      assert (inp->msgh_size >= sizeof (*reply));
+
+	      if (reply->RetCode == KERN_SUCCESS && req.valid)
+		reply->RetCode = handle_port_alloc_reply (&req);
+	    }
+	  /* It's the reply of mach_port_allocate */
+	  else if (inp->msgh_id == 3304)
+	    {
+	      struct {
+		  mach_msg_header_t Head;
+		  mach_msg_type_t RetCodeType;
+		  kern_return_t RetCode;
+		  mach_msg_type_t nameType;
+		  mach_port_t name;
+	      } *reply = (void *) inp;
+	      assert (inp->msgh_size >= sizeof (*reply));
+
+	      if (reply->RetCode == KERN_SUCCESS && req.valid)
+		{
+		  req.u.portname = reply->name;
+		  reply->RetCode
+		    = handle_port_alloc_reply (&req);
+		}
+	    }
+
 	  /* 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);
+	  print_contents (&rh->Head, rh + 1, NULL);
 	  putc ('\n', ostream);
+
 	}
       else
 	{
+	  /* save the request info */
+	  req.seqno = inp->msgh_seqno;
+	  req.task = inp->msgh_remote_port;
+	  req.req_id = inp->msgh_id;
+	  req.valid = FALSE;
+	  /* The request msg for mach_port_allocate_name */
+	  if (inp->msgh_id == 3203)
+	    {
+	      struct 
+		{
+		  mach_msg_header_t Head;
+		  mach_msg_type_t rightType;
+		  mach_port_right_t right;
+		  mach_msg_type_t nameType;
+		  mach_port_t name;
+		} *request = (void *) inp;
+	      assert (inp->msgh_size >= sizeof (*request));
+
+	      if (request->right == MACH_PORT_RIGHT_RECEIVE)
+		{
+		  req.u.portname = request->name;
+		  req.valid = TRUE;
+		}
+	    }
+	  /* The request msg for mach_port_allocate */
+	  else if (inp->msgh_id == 3204)
+	    {
+	      struct 
+		{
+		  mach_msg_header_t Head;
+		  mach_msg_type_t rightType;
+		  mach_port_right_t right;
+		} *request = (void *) inp;
+	      if (request->right == MACH_PORT_RIGHT_RECEIVE)
+		req.valid = TRUE;
+	    }
 	  /* Print something about the message header.  */
 	  print_request_header (info, inp);
-	  print_contents (inp, inp + 1);
+	  print_contents (inp, inp + 1, &req);
 	  if (inp->msgh_local_port == MACH_PORT_NULL) /* simpleroutine */
 	    fprintf (ostream, ");\n");
 	  else
