summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustus Winter <4winter@informatik.uni-hamburg.de>2014-05-23 08:42:45 +0200
committerJustus Winter <4winter@informatik.uni-hamburg.de>2015-09-10 23:30:09 +0200
commitfa35dc7eb39180e83f22cc8df4c300f6be50b814 (patch)
treee0b76b3005e7875cbcb2c56bd1527e19beb62261
parent984d1271338847605008932b080bc7aca5161070 (diff)
utils/rpctrace: support attaching to serversintrospection-2015-09-11
* 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.
-rw-r--r--utils/Makefile5
-rw-r--r--utils/rpctrace.c218
2 files changed, 220 insertions, 3 deletions
diff --git a/utils/Makefile b/utils/Makefile
index 955789b7..352494ae 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 msgids.c rpcscan.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
+rpctrace: ../libports/libports.a \
+ ../libintrospection/libintrospection.a hurd_portUser.o
rpctrace rpcscan: msgids.o \
../libihash/libihash.a
msgids-CPPFLAGS = -DDATADIR=\"${datadir}\"
diff --git a/utils/rpctrace.c b/utils/rpctrace.c
index d53b510f..c3c311d5 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>
@@ -40,6 +41,7 @@
#include <envz.h>
#include "msgids.h"
+#include "hurd_port_U.h"
const char *argp_program_version = STANDARD_HURD_VERSION (rpctrace);
@@ -48,6 +50,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."},
{0, 's', "SIZE", 0, "Specify the maximum string size to print (the default is 80)."},
{0, 'E', "var[=value]", 0,
"Set/change (var=value) or remove (var) an environment variable among the "
@@ -852,7 +859,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;
@@ -1669,10 +1676,196 @@ traced_spawn (char **argv, char **envp)
return pid;
}
+
+/* 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)
{
+ 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;
pthread_t thread;
@@ -1684,12 +1877,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 's':
strsize = atoi (arg);
break;
@@ -1723,10 +1927,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;
@@ -1746,6 +1956,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);
@@ -1760,6 +1973,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);