From 3cd552c422a2bed42e5c23c997e0e5039c8f8fbc Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Wed, 21 May 2014 16:47:14 +0200 Subject: [PATCH hurd 3/9] libports: implement the Hurd server introspection protocol Add a compact and self-contained introspection server to libports. Add functions to to label port buckets and classes. Make it possible to provide a function that given an object of a class, returns a human-readable representation for it. * libports/introspection.c: New file. * libports/create-bucket.c (ports_label_bucket): New function. * libports/create-class.c (ports_set_debug_info): Likewise. * libports/manage-multithread.c (internal_demuxer): Trace messages if desired. * libports/manage-one-thread.c (internal_demuxer): Likewise. * libports/ports.h (struct port_bucket): Add label. (struct port_class): Add debug_info and label. (ports_label_bucket): New declaration. (ports_set_debug_info): Likewise. * libports/Makefile (SRCS): Add introspection.c. (OBJS): Add hurd_portServer.o. --- ext2fs/Makefile | 3 +- fatfs/Makefile | 3 +- isofs/Makefile | 3 +- libports/Makefile | 6 +- libports/create-bucket.c | 7 ++ libports/create-class.c | 16 ++++ libports/introspection.c | 194 ++++++++++++++++++++++++++++++++++++++++++ libports/manage-multithread.c | 12 +++ libports/manage-one-thread.c | 13 +++ libports/ports.h | 15 ++++ tmpfs/Makefile | 3 +- 11 files changed, 268 insertions(+), 7 deletions(-) create mode 100644 libports/introspection.c diff --git a/ext2fs/Makefile b/ext2fs/Makefile index 8d2e68c..9d72fda 100644 --- a/ext2fs/Makefile +++ b/ext2fs/Makefile @@ -23,7 +23,8 @@ target = ext2fs SRCS = balloc.c dir.c ext2fs.c getblk.c hyper.c ialloc.c \ inode.c pager.c pokel.c truncate.c storeinfo.c msg.c xinl.c OBJS = $(SRCS:.c=.o) -HURDLIBS = diskfs pager iohelp fshelp store ports ihash shouldbeinlibc +HURDLIBS = diskfs pager iohelp fshelp store ports ihash introspection \ + shouldbeinlibc OTHERLIBS = -lpthread $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz) include ../Makeconf diff --git a/fatfs/Makefile b/fatfs/Makefile index 6224b64..e4f01ec 100644 --- a/fatfs/Makefile +++ b/fatfs/Makefile @@ -22,7 +22,8 @@ target = fatfs SRCS = inode.c main.c dir.c pager.c fat.c virt-inode.c node-create.c OBJS = $(SRCS:.c=.o) -HURDLIBS = diskfs iohelp fshelp store pager ports ihash shouldbeinlibc +HURDLIBS = diskfs iohelp fshelp store pager ports ihash introspection \ + shouldbeinlibc OTHERLIBS = -lpthread $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz) include ../Makeconf diff --git a/isofs/Makefile b/isofs/Makefile index 6475c52..9e399bf 100644 --- a/isofs/Makefile +++ b/isofs/Makefile @@ -21,7 +21,8 @@ target = iso9660fs SRCS = inode.c main.c lookup.c pager.c rr.c OBJS = $(SRCS:.c=.o) -HURDLIBS = diskfs iohelp fshelp store pager ports ihash shouldbeinlibc +HURDLIBS = diskfs iohelp fshelp store pager ports ihash introspection \ + shouldbeinlibc OTHERLIBS = -lpthread $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz) include ../Makeconf diff --git a/libports/Makefile b/libports/Makefile index af881f8..ec98bad 100644 --- a/libports/Makefile +++ b/libports/Makefile @@ -36,13 +36,13 @@ SRCS = create-bucket.c create-class.c \ interrupt-operation.c interrupt-on-notify.c interrupt-notified-rpcs.c \ dead-name.c create-port.c import-port.c default-uninhibitable-rpcs.c \ claim-right.c transfer-right.c create-port-noinstall.c create-internal.c \ - interrupted.c extern-inline.c port-deref-deferred.c + interrupted.c extern-inline.c port-deref-deferred.c introspection.c installhdrs = ports.h port-deref-deferred.h -HURDLIBS= ihash +HURDLIBS= ihash introspection LDLIBS += -lpthread -OBJS = $(SRCS:.c=.o) notifyServer.o interruptServer.o +OBJS = $(SRCS:.c=.o) notifyServer.o interruptServer.o hurd_portServer.o MIGCOMSFLAGS = -prefix ports_ MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h diff --git a/libports/create-bucket.c b/libports/create-bucket.c index 82c00a4..34559f5 100644 --- a/libports/create-bucket.c +++ b/libports/create-bucket.c @@ -49,5 +49,12 @@ ports_create_bucket () hurd_ihash_init (&ret->htable, offsetof (struct port_info, hentry)); ret->rpcs = ret->flags = ret->count = 0; _ports_threadpool_init (&ret->threadpool); + ret->label = "unlabeled bucket"; return ret; } + +/* Label BUCKET with LABEL. */ +void ports_label_bucket (struct port_bucket *bucket, const char *label) +{ + bucket->label = label; +} diff --git a/libports/create-class.c b/libports/create-class.c index 782f52b..8abf643 100644 --- a/libports/create-class.c +++ b/libports/create-class.c @@ -41,6 +41,22 @@ ports_create_class (void (*clean_routine)(void *), cl->rpcs = 0; cl->count = 0; cl->uninhibitable_rpcs = ports_default_uninhibitable_rpcs; + cl->debug_info = NULL; + cl->label = "unlabeled class"; + cl->trace_port = MACH_PORT_NULL; return cl; } + +/* Label CLASS with LABEL. Use DEBUG_INFO to format human-readable + information about a given object belonging to CLASS into an buffer, + or the default formatting function if DEBUG_INFO is NULL. */ +void +ports_label_class (struct port_class *class, + const char *label, + error_t (*debug_info) (const void *, char *, size_t)) +{ + class->label = label; + if (debug_info) + class->debug_info = debug_info; +} diff --git a/libports/introspection.c b/libports/introspection.c new file mode 100644 index 0000000..07f8624 --- /dev/null +++ b/libports/introspection.c @@ -0,0 +1,194 @@ +/* Hurd server introspection. + + Copyright (C) 2014 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see . */ + +#include +#include +#include +#include +#include + +#include "ports.h" +#include "hurd_port_U.h" + +/* We service introspection requests on this port. */ +static mach_port_t introspection_port; + +/* We use a separate thread to service the introspection requests. It + is a straight forward Mach server for the hurd_port protocol. */ +static void * +service_introspection_requests (void *arg) +{ + error_t err; + + err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, + &introspection_port); + if (err) + { + error (0, err, "mach_port_allocate"); + return NULL; + } + + err = mach_port_insert_right (mach_task_self (), + introspection_port, introspection_port, + MACH_MSG_TYPE_MAKE_SEND); + if (err) + { + error (0, err, "mach_port_insert_right"); + return NULL; + } + + err = introspection_set_port (mach_task_self (), introspection_port); + if (err) + { + error (0, err, "introspection_set_port"); + return NULL; + } + + /* XXX mig should emit this declaration. */ + boolean_t ports_hurd_port_server (mach_msg_header_t *InHeadP, + mach_msg_header_t *OutHeadP); + + while (1) + mach_msg_server (ports_hurd_port_server, 0, introspection_port); + + /* Not reached. */ + return NULL; +} + +/* Start the introspection server if it is not already running. */ +void +_ports_start_introspection_server (void) +{ + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + static int initialized = 0; + error_t err; + pthread_t thread; + + pthread_mutex_lock (&lock); + if (! initialized) + { + err = pthread_create (&thread, NULL, + service_introspection_requests, NULL); + if (err) + error (0, err, "Error starting introspection server"); + else + { + pthread_detach (thread); + initialized = 1; + } + } + pthread_mutex_unlock (&lock); +} + +/* Return the number of hard and weak references of the object + directly associated with the receive right NAME. + + Return EINVAL if NAME does not denote a receive right managed by + the port-to-object mapper, or if the concept of reference counting + simply does not apply. */ +error_t +ports_S_hurd_port_get_refcounts (mach_port_t port, + mach_port_t name, + natural_t *hard, + natural_t *weak) +{ + struct references result; + struct port_info *pi; + + if (port != introspection_port) + return EOPNOTSUPP; + + pi = ports_lookup_port (0, name, 0); + if (pi == NULL) + return EINVAL; + + refcounts_references (&pi->refcounts, &result); + + *hard = result.hard - 1; + *weak = result.weak; + ports_port_deref (pi); + return 0; +} + +static error_t +default_debug_info (const void *port, char *buffer, size_t size) +{ + const struct port_info *pi = port; + snprintf (buffer, size, + "bucket: %s, class: %s", + pi->bucket->label, pi->class->label); + return 0; +} + +/* Return a compact, human-readable description of the object related + with the receive right NAME. + + This description is meant for debugging purposes and should include + relevant internal state. If possible, it should include + information that is meaningful in other contexts (like a file name, + or the inode number). + + Return EINVAL if NAME does not denote a receive right managed by + the port-to-object mapper. */ +error_t +ports_S_hurd_port_debug_info (mach_port_t port, + mach_port_t name, + char *info) +{ + error_t err; + struct port_info *pi; + + if (port != introspection_port) + return EOPNOTSUPP; + + pi = ports_lookup_port (0, name, 0); + if (pi == NULL) + return EINVAL; + + if (pi->class->debug_info) + err = pi->class->debug_info (pi, info, 1024 /* XXX */); + else + err = default_debug_info (pi, info, 1024 /* XXX */); + info[1023 /* XXX */] = 0; + + ports_port_deref (pi); + return err; +} + +error_t +ports_S_hurd_port_trace_class_rpcs (mach_port_t port, + mach_port_t name, + mach_port_t trace_port) +{ + struct port_info *pi; + + if (port != introspection_port) + return EOPNOTSUPP; + + pi = ports_lookup_port (0, name, 0); + if (pi == NULL) + return EINVAL; + + if (MACH_PORT_VALID (pi->class->trace_port)) + mach_port_deallocate (mach_task_self (), pi->class->trace_port); + + pi->class->trace_port = trace_port; + ports_port_deref (pi); + return 0; +} diff --git a/libports/manage-multithread.c b/libports/manage-multithread.c index 60743d9..d2e7dbe 100644 --- a/libports/manage-multithread.c +++ b/libports/manage-multithread.c @@ -21,6 +21,7 @@ #include "ports.h" #include #include +#include #include #include #include @@ -189,6 +190,11 @@ ports_manage_port_operations_multithread (struct port_bucket *bucket, if (pi) { + mach_port_t trace_port = pi->class->trace_port; + mach_port_t trace_id; + if (__builtin_expect (MACH_PORT_VALID (trace_port), 0)) + introspection_trace_request (trace_port, inp, &trace_id); + error_t err = ports_begin_rpc (pi, inp->msgh_id, &link); if (err) { @@ -207,6 +213,9 @@ ports_manage_port_operations_multithread (struct port_bucket *bucket, ports_end_rpc (pi, &link); } ports_port_deref (pi); + + if (__builtin_expect (MACH_PORT_VALID (trace_port), 0)) + introspection_trace_message (trace_port, outp, trace_id); } else { @@ -281,5 +290,8 @@ ports_manage_port_operations_multithread (struct port_bucket *bucket, master thread from going away. */ global_timeout = 0; + /* Make sure the introspection server is running. */ + _ports_start_introspection_server (); + thread_function ((void *) 1); } diff --git a/libports/manage-one-thread.c b/libports/manage-one-thread.c index b920338..86d575e 100644 --- a/libports/manage-one-thread.c +++ b/libports/manage-one-thread.c @@ -18,6 +18,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include + #include "ports.h" void @@ -80,6 +82,11 @@ ports_manage_port_operations_one_thread (struct port_bucket *bucket, if (pi) { + mach_port_t trace_port = pi->class->trace_port; + mach_port_t trace_id; + if (__builtin_expect (MACH_PORT_VALID (trace_port), 0)) + introspection_trace_request (trace_port, inp, &trace_id); + err = ports_begin_rpc (pi, inp->msgh_id, &link); if (err) { @@ -96,6 +103,9 @@ ports_manage_port_operations_one_thread (struct port_bucket *bucket, ports_end_rpc (pi, &link); } ports_port_deref (pi); + + if (__builtin_expect (MACH_PORT_VALID (trace_port), 0)) + introspection_trace_message (trace_port, outp, trace_id); } else { @@ -114,6 +124,9 @@ ports_manage_port_operations_one_thread (struct port_bucket *bucket, zero. */ timeout = 0; + /* Make sure the introspection server is running. */ + _ports_start_introspection_server (); + _ports_thread_online (&bucket->threadpool, &thread); do err = mach_msg_server_timeout (internal_demuxer, 0, bucket->portset, diff --git a/libports/ports.h b/libports/ports.h index 9299bc4..e61b38c 100644 --- a/libports/ports.h +++ b/libports/ports.h @@ -76,6 +76,7 @@ struct port_bucket int flags; int count; struct ports_threadpool threadpool; + const char *label; }; /* FLAGS above are the following: */ #define PORT_BUCKET_INHIBITED PORTS_INHIBITED @@ -91,7 +92,10 @@ struct port_class int count; void (*clean_routine) (void *); void (*dropweak_routine) (void *); + error_t (*debug_info) (const void *, char *, size_t); struct ports_msg_id_range *uninhibitable_rpcs; + const char *label; + mach_port_t trace_port; }; /* FLAGS are the following: */ #define PORT_CLASS_INHIBITED PORTS_INHIBITED @@ -160,6 +164,9 @@ extern struct ports_msg_id_range *ports_default_uninhibitable_rpcs; /* Create and return a new bucket. */ struct port_bucket *ports_create_bucket (void); +/* Label BUCKET with LABEL. */ +void ports_label_bucket (struct port_bucket *bucket, const char *label); + /* Create and return a new port class. If nonzero, CLEAN_ROUTINE will be called for each allocated port object in this class when it is being destroyed. If nonzero, DROPWEAK_ROUTINE will be called @@ -169,6 +176,12 @@ struct port_bucket *ports_create_bucket (void); struct port_class *ports_create_class (void (*clean_routine)(void *), void (*dropweak_routine)(void *)); +/* Label CLASS with LABEL. Use DEBUG_INFO to format human-readable + information about a given object belonging to CLASS into an buffer, + or the default formatting function if DEBUG_INFO is NULL. */ +void ports_label_class (struct port_class *class, const char *label, + error_t (*debug_info) (const void *, char *, size_t)); + /* Create and return in RESULT a new port in CLASS and BUCKET; SIZE bytes will be allocated to hold the port structure and whatever private data the user desires. */ @@ -482,5 +495,7 @@ extern int _ports_flags; void _ports_complete_deallocate (struct port_info *); error_t _ports_create_port_internal (struct port_class *, struct port_bucket *, size_t, void *, int); +error_t _ports_trace_message (mach_port_t, const mach_msg_header_t *); +void _ports_start_introspection_server (void); #endif diff --git a/tmpfs/Makefile b/tmpfs/Makefile index fdcae34..fc27909 100644 --- a/tmpfs/Makefile +++ b/tmpfs/Makefile @@ -23,7 +23,8 @@ target = tmpfs SRCS = tmpfs.c node.c dir.c pager-stubs.c OBJS = $(SRCS:.c=.o) default_pagerUser.o # XXX The shared libdiskfs requires libstore even though we don't use it here. -HURDLIBS = diskfs pager iohelp fshelp store ports ihash shouldbeinlibc +HURDLIBS = diskfs pager iohelp fshelp store ports ihash introspection \ + shouldbeinlibc OTHERLIBS = -lpthread include ../Makeconf -- 2.1.4