bug-hurd
[Top][All Lists]
Advanced

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

[PATCH hurd 8/8] utils/rpctrace: support attaching to servers


From: Justus Winter
Subject: [PATCH hurd 8/8] utils/rpctrace: support attaching to servers
Date: Thu, 23 Oct 2014 17:16:46 +0200

* utils/rpctrace.c (options): Add `--pid' and `--reference-port'.
(print_contents): Prevent the translation of rights if `req' is NULL.
We will use this to print messages in `trace_server'.
(parse_task): New function.
(trace_server): Mach server function that displays relayed messages.
(trace_class_rpcs): New function that attaches to a server and starts
tracing.
(parse_opt): Handle `--pid' and `--reference-port'.
(main): Handle new arguments, call trace_class_rpcs if desired.
---
 utils/Makefile   |   5 +-
 utils/rpctrace.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 220 insertions(+), 3 deletions(-)

diff --git a/utils/Makefile b/utils/Makefile
index 241b060..df96d33 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -34,7 +34,7 @@ SRCS = shd.c ps.c settrans.c syncfs.c showtrans.c addauth.c 
rmauth.c \
        nullauth.c match-options.c
 
 OBJS = $(filter-out %.sh,$(SRCS:.c=.o))
-HURDLIBS = ps ihash store fshelp ports ftpconn shouldbeinlibc
+HURDLIBS = ps ihash store fshelp ports ftpconn shouldbeinlibc introspection
 LDLIBS += -lpthread
 login-LDLIBS = -lutil -lcrypt
 addauth-LDLIBS = -lcrypt
@@ -67,7 +67,8 @@ ps w ids settrans syncfs showtrans fsysopts storeinfo login 
vmstat portinfo \
 
 $(filter-out $(special-targets), $(targets)): %: %.o
 
-rpctrace: ../libports/libports.a ../libihash/libihash.a
+rpctrace: ../libports/libports.a ../libihash/libihash.a \
+       ../libintrospection/libintrospection.a hurd_portUser.o
 rpctrace-CPPFLAGS = -DDATADIR=\"${datadir}\"
 
 fakeauth: authServer.o auth_requestUser.o interruptServer.o \
diff --git a/utils/rpctrace.c b/utils/rpctrace.c
index b11fea4..2c7f7ea 100644
--- a/utils/rpctrace.c
+++ b/utils/rpctrace.c
@@ -23,6 +23,7 @@
 #include <hurd.h>
 #include <hurd/ports.h>
 #include <hurd/ihash.h>
+#include <hurd/introspection.h>
 #include <mach/message.h>
 #include <assert.h>
 #include <fcntl.h>
@@ -41,6 +42,8 @@
 #include <argz.h>
 #include <envz.h>
 
+#include "hurd_port_U.h"
+
 const char *argp_program_version = STANDARD_HURD_VERSION (rpctrace);
 
 #define STD_MSGIDS_DIR DATADIR "/msgids/"
@@ -51,6 +54,11 @@ static unsigned strsize = 80;
 static const struct argp_option options[] =
 {
   {"output", 'o', "FILE", 0, "Send trace output to FILE instead of stderr."},
+  {"pid", 'p', "PID", 0, "Attach to PID and trace all requests to objects "
+   "of the same class as the given reference port.  This will only work "
+   "for Hurd servers implementing the introspection protocol."},
+  {"reference-port", 'P', "PORT", 0, "Trace all requests to ports of the "
+   "same class as PORT.  PORT must denote a receive right in PID."},
   {"nostdinc", OPT_NOSTDINC, 0, 0, 
    "Do not search inside the standard system directory, `" STD_MSGIDS_DIR
    "', for `.msgids' files."},
@@ -962,7 +970,7 @@ print_contents (mach_msg_header_t *inp,
         what task that port name is meaningful in.  If it's meaningful in
         a traced task, then it refers to our intercepting port rather than
         the original port anyway.  */
-      if (MACH_MSG_TYPE_PORT_ANY_RIGHT (name))
+      if (MACH_MSG_TYPE_PORT_ANY_RIGHT (name) && req != NULL)
        {
          /* These are port rights.  Translate them into wrappers.  */
          mach_port_t *const portnames = data;
@@ -1795,12 +1803,197 @@ scan_msgids_dir (char **argz, size_t *argz_len, char 
*dir, bool append)
   /* If the directory couldn't be scanned for whatever reason, just ignore
      it. */
 }
+
+/* Return the task corresponding to the user argument ARG, exiting with an
+   appriate error message if we can't.  */
+static task_t
+parse_task (char *arg)
+{
+  error_t err;
+  task_t task;
+  char *arg_end;
+  pid_t pid = strtoul (arg, &arg_end, 10);
+  static process_t proc = MACH_PORT_NULL;
+
+  if (*arg == '\0' || *arg_end != '\0')
+    error (10, 0, "%s: Invalid process id", arg);
+
+  if (proc == MACH_PORT_NULL)
+    proc = getproc ();
+
+  err = proc_pid2task (proc, pid, &task);
+  if (err)
+    error (11, err, "%s", arg);
+  else if (task == MACH_PORT_NULL)
+    error (11, 0, "%s: Process %d is dead and has no task", arg, (int) pid);
+
+  return task;
+}
+
+static mach_port_t trace_notification_port;
+
+boolean_t
+trace_server (mach_msg_header_t *inp,
+             mach_msg_header_t *outp)
+{
+  error_t err;
+  struct msgid_info *info;
+  int is_reply;
+
+  if (inp->msgh_local_port == trace_notification_port
+      && inp->msgh_id == MACH_NOTIFY_NO_SENDERS)
+    {
+      error (0, 0, "The tracee vanished.");
+      exit (EXIT_SUCCESS);
+    }
+
+  err = introspection_extract_message (inp);
+  if (err)
+    {
+      error (0, err, "introspection_extract_message");
+      goto out;
+    }
+  info = msgid_info (inp->msgh_id);
+
+  /* XXX This hardcodes an assumption about reply message ids.  */
+  is_reply = (inp->msgh_id / 100) % 2 == 1;
+  if (is_reply)
+    {
+      /* This looks like a traced reply or a pseudo-reply.  A
+        pseudo-reply is a message containing the result of a simple
+        procedure that is only sent to us.  */
+      mig_reply_header_t *reply = (mig_reply_header_t *) inp;
+
+      if (last_reply_port != inp->msgh_remote_port)
+       {
+         print_ellipsis ();
+         fprintf (ostream, "%u...", (unsigned int) inp->msgh_remote_port);
+       }
+      last_reply_port = MACH_PORT_NULL;
+
+      fprintf (ostream, " = ");
+
+      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);
+       }
+
+      if (inp->msgh_size > sizeof *reply)
+       {
+         fprintf (ostream, " ");
+         print_contents (inp, (void *) inp + sizeof *reply, NULL);
+       }
+      fprintf (ostream, "\n");
+    }
+  else
+    {
+      /* This looks like a traced request.  */
+      print_ellipsis ();
+      last_reply_port = inp->msgh_remote_port;
+
+      if (info)
+       fprintf (ostream, "% 4d->%s (", inp->msgh_local_port, info->name);
+      else
+       fprintf (ostream, "% 4d->%d (", inp->msgh_local_port, inp->msgh_id);
+
+      print_contents (inp, (void *) inp + sizeof *inp, NULL);
+      fprintf (ostream, ")");
+    }
+
+ out:
+  /* vm_deallocate any out-of-band memory.  */
+  mach_msg_destroy (inp);
+
+  /* Prevent mach_msg_server from sending messages.  */
+  ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+  return TRUE;
+}
 
 int
+trace_class_rpcs (mach_port_t task,
+                 mach_port_t name)
+{
+  error_t err;
+  mach_port_t trace_port;
+  mach_port_t introspection_port;
+  mach_port_t previous;
+  mach_port_t port_set;
+
+  err = introspection_get_port (task, &introspection_port);
+  if (err)
+    error (13, err, "Failed to get introspection port");
+
+  if (! MACH_PORT_VALID (introspection_port))
+    error (13, 0, "The server does not implement the introspection protocol");
+
+  err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+                           &trace_port);
+  if (err)
+    error (13, err, "mach_port_allocate");
+
+  err = hurd_port_trace_class_rpcs (introspection_port, name,
+                                   trace_port, MACH_MSG_TYPE_MAKE_SEND);
+  if (err)
+    {
+      if (err == EINVAL)
+       error (13, 0,
+              "%d does not denote a receive right managed by libports", name);
+      else
+       error (13, err, "hurd_port_trace_class_rpcs");
+    }
+
+  err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+                           &trace_notification_port);
+  if (err)
+    error (13, err, "mach_port_allocate");
+
+  err = mach_port_request_notification (mach_task_self (),
+                                       trace_port,
+                                       MACH_NOTIFY_NO_SENDERS,
+                                       0,
+                                       trace_notification_port,
+                                       MACH_MSG_TYPE_MAKE_SEND_ONCE,
+                                       &previous);
+  if (err)
+    error (13, err, "mach_port_request_notification");
+  assert (! MACH_PORT_VALID (previous));
+
+
+  err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_PORT_SET,
+                           &port_set);
+  if (err)
+    error (13, err, "mach_port_allocate");
+
+  err = mach_port_move_member (mach_task_self (), trace_port, port_set);
+  if (err)
+    error (13, err, "mach_port_move_member");
+
+  err = mach_port_move_member (mach_task_self (), trace_notification_port,
+                              port_set);
+  if (err)
+    error (13, err, "mach_port_move_member");
+
+  error (0, 0, "entering service loop");
+  while (1)
+    mach_msg_server (trace_server, 0, port_set);
+
+  /* Not reached.  */
+  return 0;
+}
+
+int
 main (int argc, char **argv, char **envp)
 {
   char *msgids_files_argz = NULL;
   size_t msgids_files_argz_len = 0;
+  mach_port_t target_task = MACH_PORT_NULL;
+  mach_port_t reference_port = MACH_PORT_NULL;
   bool nostdinc = FALSE;
   const char *outfile = 0;
   char **cmd_argv = 0;
@@ -1813,12 +2006,23 @@ main (int argc, char **argv, char **envp)
   /* Parse our options...  */
   error_t parse_opt (int key, char *arg, struct argp_state *state)
     {
+      char *arg_end;
       switch (key)
        {
        case 'o':
          outfile = arg;
          break;
 
+       case 'p':
+         target_task = parse_task (arg);
+         break;
+
+       case 'P':
+         reference_port = strtoul (arg, &arg_end, 10);
+         if (*arg == '\0' || *arg_end != '\0')
+           argp_error (state, "Invalid port name: %s", arg);
+         break;
+
        case OPT_NOSTDINC:
          nostdinc = TRUE;
          break;
@@ -1867,10 +2071,16 @@ main (int argc, char **argv, char **envp)
          break;
 
        case ARGP_KEY_NO_ARGS:
+         if (MACH_PORT_VALID (target_task))
+           break;
+
          argp_usage (state);
          return EINVAL;
 
        case ARGP_KEY_ARG:
+         if (MACH_PORT_VALID (target_task))
+           argp_error (state, "Superfluous argument: %s", arg);
+
          cmd_argv = &state->argv[state->next - 1];
          state->next = state->argc;
          break;
@@ -1885,6 +2095,9 @@ main (int argc, char **argv, char **envp)
   /* Parse our arguments.  */
   argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
 
+  if (MACH_PORT_VALID (target_task) != MACH_PORT_VALID (reference_port))
+    error (10, 0, "Please specify either both -p and -P, or neither.");
+
   err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_DEAD_NAME,
                            &unknown_task);
   assert_perror (err);
@@ -1916,6 +2129,9 @@ main (int argc, char **argv, char **envp)
     ostream = stderr;
   setlinebuf (ostream);
 
+  if (MACH_PORT_VALID (target_task))
+    return trace_class_rpcs (target_task, reference_port);
+
   traced_bucket = ports_create_bucket ();
   traced_class = ports_create_class (&traced_clean, NULL);
   other_class = ports_create_class (0, 0);
-- 
2.1.1




reply via email to

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