Whamcloud - gitweb
Land b_release_1_4_6 onto HEAD (20060223_1455)
[fs/lustre-release.git] / libsysio / src / init.c
index ce253cc..b50ce12 100644 (file)
@@ -9,7 +9,7 @@
  *    terms of the GNU Lesser General Public License
  *    (see cit/LGPL or http://www.gnu.org/licenses/lgpl.html)
  *
- *    Cplant(TM) Copyright 1998-2004 Sandia Corporation. 
+ *    Cplant(TM) Copyright 1998-2005 Sandia Corporation. 
  *    Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
  *    license for use of this work by or on behalf of the US Government.
  *    Export of this program may require a license from the United States
 
 #define _BSD_SOURCE
 
+#if SYSIO_TRACING
+#include <stdio.h>
+#endif
 #include <stdlib.h>
+#if SYSIO_TRACING
+#include <sys/syscall.h>
+#endif
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#if SYSIO_TRACING
+#include <stdarg.h>
+#endif
 #include <limits.h>
 #include <assert.h>
 #include <sys/types.h>
 #include <sys/uio.h>
 #include <sys/queue.h>
 
-#include "xtio.h"
 #include "sysio.h"
+#include "xtio.h"
+#if SYSIO_TRACING
+#include "native.h"
+#endif
 #include "inode.h"
 #include "fs.h"
 #include "mount.h"
 #include "stdfd.h"
 #endif
 
+#if SYSIO_TRACING
+
+/*
+ * Tracing callback record.
+ */
+struct trace_callback {
+       TAILQ_ENTRY(trace_callback) links;
+       void    (*f)(const char *file, const char *func, int line);
+};
+
+/*
+ * Initialize a tracing callback record.
+ */
+#define TCB_INIT(__tcb, __f) \
+       do { \
+               (__tcb)->f = (__f); \
+       } while (0);
+
+/*
+ * Trace queue head record.
+ */
+TAILQ_HEAD(trace_q, trace_callback);
+
+/*
+ * The entry and exit queue heads, and queue pointers.
+ */
+static struct trace_q _sysio_entry_trace_head;
+void   *_sysio_entry_trace_q = &_sysio_entry_trace_head;
+static struct trace_q _sysio_exit_trace_head;
+void   *_sysio_exit_trace_q = &_sysio_exit_trace_head;
+#endif
+
 /*
  * White space characters.
  */
 #define IGNORE_WHITE           " \t\r\n"
 
 /*
+ * Check if long overflows integer range.
+ */
+#if LONG_MAX <= INT_MAX
+#define _irecheck(_l, _e) \
+       ((_l) == LONG_MAX && (_e) == ERANGE)
+#else
+#define _irecheck(_l, _e) \
+       ((_l) > INT_MAX)
+#endif
+
+/*
  * Sysio library initialization. Must be called before anything else in the
  * library.
  */
@@ -81,7 +136,15 @@ _sysio_init()
 {
        int     err;
 #ifdef WITH_SOCKETS
-        int     _sysio_sockets_init(void);
+       extern int _sysio_sockets_init(void);
+#endif
+
+#if SYSIO_TRACING
+       /*
+        * Initialize tracing callback queues.
+        */
+       TAILQ_INIT(&_sysio_entry_trace_head);
+       TAILQ_INIT(&_sysio_exit_trace_head);
 #endif
 
        err = _sysio_ioctx_init();
@@ -134,9 +197,179 @@ _sysio_shutdown()
        _sysio_fd_shutdown();
        _sysio_i_shutdown();
        _sysio_fssw_shutdown();
+#if SYSIO_TRACING
+       {
+               struct trace_callback *tcb;
+
+               /*
+                * Empty the trace queues and free the entries.
+                */
+               while ((tcb = _sysio_entry_trace_head.tqh_first) != NULL) {
+                       TAILQ_REMOVE(&_sysio_entry_trace_head, tcb, links);
+                       free(tcb);
+               }
+               while ((tcb = _sysio_exit_trace_head.tqh_first) != NULL) {
+                       TAILQ_REMOVE(&_sysio_exit_trace_head, tcb, links);
+                       free(tcb);
+               }
+       }
+#endif
 #endif
 }
 
+#if SYSIO_TRACING
+
+#if !(defined(_HAVE_ASPRINTF) && _HAVE_ASPRINTF)
+/*
+ * Print a string to allocated memory.
+ */
+static int
+vasprintf(char **strp, const char *fmt, va_list ap)
+{
+       size_t  siz;
+       int     oerrno;
+       char    *s;
+       va_list aq;
+       int     n;
+
+       siz = 50;
+       oerrno = errno;
+       if (!(s = malloc(siz))) {
+               errno = oerrno;
+               return -1;
+       }
+       for (;;) {
+               va_copy(aq, ap);
+               n = vsnprintf (s, siz, fmt, aq);
+               va_end(aq);
+               if (n > -1 && (size_t )n < siz)
+                       break;
+               if (n > -1)                             /* glibc 2.1 */
+                       siz = n+1;                      /* precise */
+               else                                    /* glibc 2.0 */
+                       siz *= 2;                       /* twice the old */
+               if (!(s = realloc (s, siz)))
+                       break;
+       }
+       *strp = s;
+       errno = oerrno;
+       return n;
+}
+
+#if 0
+static int
+asprintf(char **strp, const char *fmt, ...)
+{
+       va_list ap;
+       int     n;
+
+       va_start(ap, fmt);
+       n = vasprintf(strp, fmt, ap);
+       va_end(ap);
+       return n;
+}
+#endif
+#endif /* !(defined(_HAVE_ASPRINTF) && _HAVE_ASPRINTF) */
+
+static void
+_sysio_cwrite(const char *buf, size_t len)
+{
+       int     oerrno;
+
+       oerrno = errno;
+       (void )syscall(SYSIO_SYS_write, STDERR_FILENO, buf, len);
+       errno = oerrno;
+}
+
+/*
+ * Console printf.
+ */
+void
+_sysio_cprintf(const char *fmt, ...)
+{
+       va_list ap;
+       int     len;
+       char    *buf;
+
+       va_start(ap, fmt);
+       buf = NULL;
+       len = vasprintf(&buf, fmt, ap);
+       va_end(ap);
+       if (len < 0)
+               return;
+       _sysio_cwrite(buf, len);
+       free(buf);
+}
+
+/*
+ * Register a trace callback.
+ *
+ * The pointer to the trace record is returned.
+ */
+void *
+_sysio_register_trace(void *q,
+                     void (*f)(const char *file,
+                               const char *func,
+                               int line))
+{
+       struct trace_callback *tcb;
+
+       tcb = malloc(sizeof(struct trace_callback));
+       if (!tcb)
+               return NULL;
+       TCB_INIT(tcb, f);
+       TAILQ_INSERT_TAIL((struct trace_q *)q, tcb, links);
+       return tcb;
+}
+
+/*
+ * Remove a registered trace callback.
+ */
+void
+_sysio_remove_trace(void *q, void *p)
+{
+
+       TAILQ_REMOVE((struct trace_q *)q, (struct trace_callback *)p, links);
+       free(p);
+}
+
+void
+/*
+ * Run a trace queue, making all the callbacks.
+ */
+_sysio_run_trace_q(void *q,
+                  const char *file,
+                  const char *func,
+                  int line)
+{
+       struct trace_callback *tcb;
+
+       tcb = ((struct trace_q *)q)->tqh_first;
+       while (tcb) {
+               (*tcb->f)(file, func, line);
+               tcb = tcb->links.tqe_next;
+       }
+}
+
+static void
+_sysio_trace_entry(const char *file __IS_UNUSED,
+                  const char *func,
+                  int line __IS_UNUSED)
+{
+
+       _sysio_cprintf("+ENTER+ %s\n", func);
+}
+
+static void
+_sysio_trace_exit(const char *file __IS_UNUSED,
+                 const char *func,
+                 int line __IS_UNUSED)
+{
+
+       _sysio_cprintf("+EXIT+ %s\n", func);
+}
+#endif /* defined(SYSIO_TRACING) */
+
 /* 
  * (kind of)Duplicates strtok function.
  *
@@ -581,17 +814,6 @@ do_open(char *args)
        int     err;
        struct file *fil;
 
-/*
- * Check if long overflows integer range.
- */
-#if LONG_MAX <= INT_MAX
-#define _irecheck(_l, _e) \
-       ((_l) == LONG_MAX && (_e) == ERANGE)
-#else
-#define _irecheck(_l, _e) \
-       ((_l) > INT_MAX)
-#endif
-
        len = strlen(args);
        if (_sysio_get_args(args, v) - args != (ssize_t )len ||
            !(v[0].ovi_value && v[1].ovi_value && v[2].ovi_value))
@@ -634,8 +856,6 @@ do_open(char *args)
        if (pno)
                P_RELE(pno);
        return err;
-
-#undef _irecheck
 }
 
 /*
@@ -668,38 +888,76 @@ do_command(char *buf)
        return -EINVAL;
 }
 
+#if SYSIO_TRACING
 /*
- * Given a command sequence buffer, parse it and run the given
- * commands 
+ * Set/Unset tracing.
  */
-int 
-_sysio_boot(const char *buf
-#if DEFER_INIT_CWD
-           , const char *path
+static int
+_sysio_boot_tracing(const char *arg)
+{
+       long    l;
+       char    *cp;
+       static struct trace_callback
+               *entcb = NULL,
+               *exitcb = NULL;
+
+       l = 0;
+       if (arg) {
+               l = strtol(arg, (char **)&cp, 0);
+               if (*cp || !(l == 0 || l == 1))
+                       return -EINVAL;
+       }
+       if (l) {
+               if (entcb == NULL)
+                       entcb =
+                           _sysio_register_trace(_sysio_entry_trace_q,
+                                                 _sysio_trace_entry);
+               if (entcb == NULL)
+                       return -errno;
+               if (exitcb == NULL)
+                       exitcb =
+                           _sysio_register_trace(_sysio_exit_trace_q,
+                                                 _sysio_trace_exit);
+               if (exitcb == NULL)
+                       return -errno;
+       } else {
+               if (entcb != NULL)
+                       _sysio_remove_trace(_sysio_entry_trace_q, entcb);
+               entcb = NULL;
+               if (exitcb != NULL)
+                       _sysio_remove_trace(_sysio_exit_trace_q, exitcb);
+               exitcb = NULL;
+       }
+       return 0;
+}
 #endif
-          )
+
+/*
+ * Initialize the namespace.
+ */
+static int
+_sysio_boot_namespace(const char *arg)
 {
        char    c, *tok;
        ssize_t len;
        int     err;
-
-       if (!buf)
-               buf = "";
+       unsigned count;
        /*
         * Allocate token buffer.
         */
-       len = strlen(buf);
+       len = strlen(arg);
        tok = malloc(len ? len : 1);
        if (!tok)
                return -ENOMEM;
        err = 0;
+       count = 0;
        while (1) {
                /*
                 * Discard leading white space.
                 */
-               while ((c = *buf) != '\0' &&
+               while ((c = *arg) != '\0' &&
                       !(c == '{' || strchr(IGNORE_WHITE, c) == NULL))
-                       buf++;
+                       arg++;
                if (c == '\0')
                        break;
                if (c != '{') {
@@ -709,16 +967,18 @@ _sysio_boot(const char *buf
                /*
                 * Get the command.
                 */
-               buf =
-                   (char *)_sysio_get_token(buf + 1,
+               *tok = '\0';
+               arg =
+                   (char *)_sysio_get_token(arg + 1,
                                             0,
                                             "}",
                                             IGNORE_WHITE,
                                             tok);
-               if (!buf) {
+               if (!arg) {
                        err = -EINVAL;
                        break;
                }
+               count++;
                /*
                 * Perform.
                 */
@@ -726,11 +986,63 @@ _sysio_boot(const char *buf
                if (err)
                        break;
        }
-       free(tok);
-#if DEFER_INIT_CWD
+#if SYSIO_TRACING
        if (err)
-               return err;
-       _sysio_init_cwd = path;
+               _sysio_cprintf("+NS init+ failed at expr %u (last = %s): %s\n", 
+                              count,
+                              tok && *tok ? tok : "NULL",
+                              strerror(-err));
 #endif
+       free(tok);
        return err;
 }
+
+#if DEFER_INIT_CWD
+/*
+ * Set deferred initial working directory.
+ */
+static int
+_sysio_boot_cwd(const char *arg)
+{
+
+       _sysio_init_cwd = arg;
+       return 0;
+}
+#endif
+
+/*
+ * Given an identifier and it's arguments, perform optional initializations.
+ */
+int 
+_sysio_boot(const char *opt, const char *arg)
+{
+       struct option_value_info vec[] = {
+#if SYSIO_TRACING
+               { "trace",      NULL },                 /* tracing? */
+#endif
+               { "namespace",  NULL },                 /* init namespace? */
+#if DEFER_INIT_CWD
+               { "cwd",        NULL },                 /* init working dir */
+#endif
+               { NULL,         NULL }
+       };
+       struct option_value_info *v;
+       unsigned u;
+       static int (*f[])(const char *) = {
+#if SYSIO_TRACING
+               _sysio_boot_tracing,
+#endif
+               _sysio_boot_namespace,
+#if DEFER_INIT_CWD
+               _sysio_boot_cwd,
+#endif
+               NULL                                    /* can't happen */
+       };
+
+       for (v = vec, u = 0; v->ovi_name; v++, u++)
+               if (strcmp(v->ovi_name, opt) == 0)
+                       break;
+       if (!v->ovi_name)
+               return -EINVAL;
+       return (*f[u])(arg);
+}