Add odxprint, a replacement for FSHOW which can be configured at run-time
authorDavid Lichteblau <david@lichteblau.com>
Tue, 4 Sep 2012 15:16:01 +0000 (17:16 +0200)
committerDavid Lichteblau <david@lichteblau.com>
Tue, 11 Sep 2012 10:14:50 +0000 (12:14 +0200)
  - A new macro odxprint(flag, "fmt", ...) performs the equivalent of
    a printf("fmt", ...), but only if `flag' has been enabled at
    run-time.

  - Environment variables can be used to set flags, using either
    SBCL_DYNDEBUG="flag1 flag2 flag3" syntax, or
    SBCL_DYNDEBUG__FLAG1="nonempty string" syntax.

  - Lisp feature SB-QSHOW enables support for odxprint-based FSHOW.
    (Users who prefer to edit runtime.h to enable QSHOW can still do
    so...)  SB-QSHOW is enabled by default on Windows, where the
    odxprint mechanism was first used.

  - Implement FSHOW, FSHOW_SIGNAL on top of odxprint.  Corresponding
    flags are called fshow, fshow_signal.

  - For gencgc_verbose, support a flag of the same name, since it is
    conditional on QSHOW (inspite of not being implemented on top of
    FSHOW).

 - Does not yet support odxprint features specific to Windows debugger
   integration; output is currently directed to stderr unconditionally.

This commit backports Anton Kovalenko's Windows-specific odxprint to
POSIX and integrates it with FSHOW.

base-target-features.lisp-expr
make-config.sh
src/runtime/gencgc.c
src/runtime/interrupt.c
src/runtime/print.c
src/runtime/runtime.c
src/runtime/runtime.h
src/runtime/safepoint.c

index f670123..680e29d 100644 (file)
  ;; original CMU CL code.)
  ; :sb-show-assem
 
+ ;; Compile the C runtime with support for low-level debugging output
+ ;; through FSHOW and FSHOW_SIGNAL.  If enabled, this feature allows
+ ;; users to turn on such debugging output using environment variables at
+ ;; run-time.
+ ; :sb-qshow
+
  ;; Setting this makes SBCL more "fluid", i.e. more amenable to
  ;; modification at runtime, by suppressing various INLINE declarations,
  ;; compiler macro definitions, FREEZE-TYPE declarations; and by
index b5c425f..f251797 100644 (file)
@@ -526,6 +526,7 @@ case "$sbcl_os" in
         ;;
     win32)
         printf ' :win32' >> $ltf
+        printf ' :sb-qshow' >> $ltf
         link_or_copy Config.$sbcl_arch-win32 Config
         link_or_copy $sbcl_arch-win32-os.h target-arch-os.h
         link_or_copy win32-os.h target-os.h
index 175be61..18f8179 100644 (file)
@@ -96,7 +96,7 @@ os_vm_size_t large_allocation = 0;
 
 /* the verbosity level. All non-error messages are disabled at level 0;
  * and only a few rare messages are printed at level 1. */
-#if QSHOW
+#if QSHOW == 2
 boolean gencgc_verbose = 1;
 #else
 boolean gencgc_verbose = 0;
index 6a4d25b..b8ab510 100644 (file)
@@ -808,7 +808,7 @@ interrupt_internal_error(os_context_t *context, boolean continuable)
 #endif
 
     SHOW("in interrupt_internal_error");
-#if QSHOW
+#if QSHOW == 2
     /* Display some rudimentary debugging information about the
      * error, so that even if the Lisp error handler gets badly
      * confused, we have a chance to determine what's going on. */
index 5b66b4e..e50da2e 100644 (file)
 #include "sbcl.h"
 #include "print.h"
 #include "runtime.h"
+#include <stdarg.h>
+#include "thread.h"              /* genesis/primitive-objects.h needs this */
+#include <errno.h>
+
+/* FSHOW and odxprint provide debugging output for low-level information
+ * (signal handling, exceptions, safepoints) which is hard to debug by
+ * other means.
+ *
+ * If enabled at all, environment variables control whether calls of the
+ * form odxprint(name, ...) are enabled at run-time, e.g. using
+ * SBCL_DYNDEBUG="fshow fshow_signal safepoints".
+ *
+ * In the case of FSHOW and FSHOW_SIGNAL, old-style code from runtime.h
+ * can also be used to enable or disable these more aggressively.
+ */
+
+struct dyndebug_config dyndebug_config = {
+    QSHOW == 2, QSHOW_SIGNALS == 2
+};
+
+void
+dyndebug_init()
+{
+#define DYNDEBUG_NFLAGS (sizeof(struct dyndebug_config) / sizeof(int))
+#define dyndebug_init1(lowercase, uppercase)                    \
+    do {                                                        \
+        int *ptr = &dyndebug_config.dyndebug_##lowercase;       \
+        ptrs[n] = ptr;                                          \
+        names[n] = #lowercase;                                  \
+        char *val = getenv("SBCL_DYNDEBUG__" uppercase);        \
+        *ptr = val && strlen(val);                              \
+        n++;                                                    \
+    } while (0)
+    int n = 0;
+    char *names[DYNDEBUG_NFLAGS];
+    int *ptrs[DYNDEBUG_NFLAGS];
+
+    dyndebug_init1(fshow,          "FSHOW");
+    dyndebug_init1(fshow_signal,   "FSHOW_SIGNAL");
+    dyndebug_init1(gencgc_verbose, "GENCGC_VERBOSE");
+    dyndebug_init1(safepoints,     "SAFEPOINTS");
+    dyndebug_init1(seh,            "SEH");
+    dyndebug_init1(misc,           "MISC");
+    dyndebug_init1(pagefaults,     "PAGEFAULTS");
+
+    if (n != DYNDEBUG_NFLAGS)
+        fprintf(stderr, "Bug in dyndebug_init\n");
+
+    gencgc_verbose = dyndebug_config.dyndebug_gencgc_verbose;
+
+    char *featurelist = getenv("SBCL_DYNDEBUG");
+    if (featurelist) {
+        int err = 0;
+        featurelist = strdup(featurelist);
+        char *ptr = featurelist;
+        for (;;) {
+            char *token = strtok(ptr, " ");
+            if (!token) break;
+            unsigned i;
+            if (!strcmp(token, "all"))
+                for (i = 0; i < DYNDEBUG_NFLAGS; i++)
+                    *ptrs[i] = 1;
+            else {
+                for (i = 0; i < DYNDEBUG_NFLAGS; i++)
+                    if (!strcmp(token, names[i])) {
+                        *ptrs[i] = 1;
+                        break;
+                    }
+                if (i == DYNDEBUG_NFLAGS) {
+                    fprintf(stderr, "No such dyndebug flag: `%s'\n", token);
+                    err = 1;
+                }
+            }
+            ptr = 0;
+        }
+        free(featurelist);
+        if (err) {
+            fprintf(stderr, "Valid flags are:\n");
+            fprintf(stderr, "  all  ;enables all of the following:\n");
+            unsigned i;
+            for (i = 0; i < DYNDEBUG_NFLAGS; i++)
+                fprintf(stderr, "  %s\n", names[i]);
+        }
+    }
+
+#undef dyndebug_init1
+#undef DYNDEBUG_NFLAGS
+}
+
+/* Temporarily, odxprint merely performs the equivalent of a traditional
+ * FSHOW call, i.e. it merely formats to stderr.  Ultimately, it should
+ * be restored to its full win32 branch functionality, where output to a
+ * file or to the debugger can be selected at runtime. */
+
+void
+odxprint_fun(const char *fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    vodxprint_fun(fmt, args);
+    va_end(args);
+}
+
+void
+vodxprint_fun(const char *fmt, va_list args)
+{
+#ifdef LISP_FEATURE_WIN32
+    DWORD lastError = GetLastError();
+#else
+    int original_errno = errno;
+#endif
+
+    QSHOW_BLOCK;
+
+    char buf[1024];
+
+#ifdef LISP_FEATURE_SB_THREAD
+    struct thread *arch_os_get_current_thread(void);
+    struct thread *self = arch_os_get_current_thread();
+    void *pth = self ? (void *) self->os_thread : 0;
+    snprintf(buf, sizeof(buf), "[%p/%p] ", self, pth);
+#endif
+
+    int n = strlen(buf);
+    vsnprintf(buf + n, sizeof(buf) - n - 1, fmt, args);
+    /* buf is now zero-terminated (even in case of overflow).
+     * Our caller took care of the newline (if any) through `fmt'. */
+
+    /* A sufficiently POSIXy implementation of stdio will provide
+     * per-FILE locking, as defined in the spec for flockfile.  At least
+     * glibc complies with this.  Hence we do not need to perform
+     * locking ourselves here.  (Should it turn out, of course, that
+     * other libraries opt for speed rather than safety, we need to
+     * revisit this decision.) */
+    fputs(buf, stderr);
+
+#ifdef LISP_FEATURE_WIN32
+    /* stdio's stderr is line-bufferred, i.e. \n ought to flush it.
+     * Unfortunately, MinGW does not behave the way I would expect it
+     * to.  Let's be safe: */
+    fflush(stderr);
+#endif
+
+    QSHOW_UNBLOCK;
+
+#ifdef LISP_FEATURE_WIN32
+    SetLastError(lastError);
+#else
+    errno = original_errno;
+#endif
+}
+
+/* Translate the rather awkward syntax
+ *   FSHOW((stderr, "xyz"))
+ * into the new and cleaner
+ *   odxprint("xyz").
+ * If we were willing to clean up all existing call sites, we could remove
+ * this wrapper function.  (This is a function, because I don't know how to
+ * strip the extra parens in a macro.) */
+void
+fshow_fun(void __attribute__((__unused__)) *ignored,
+          const char *fmt,
+          ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    vodxprint_fun(fmt, args);
+    va_end(args);
+}
 
 /* This file can be skipped if we're not supporting LDB. */
 #if defined(LISP_FEATURE_SB_LDB)
 #include "gencgc-alloc-region.h" /* genesis/thread.h needs this */
 #endif
 #include "genesis/static-symbols.h"
-#include "thread.h"              /* genesis/primitive-objects.h needs this */
 #include "genesis/primitive-objects.h"
 #include "genesis/static-symbols.h"
 #include "genesis/tagnames.h"
index f66a7e5..f1c50cf 100644 (file)
@@ -544,6 +544,7 @@ main(int argc, char *argv[], char *envp[])
      * systems (e.g. Alpha) arch_init() needs need os_vm_page_size, so
      * it must follow os_init(). -- WHN 2000-01-26 */
     os_init(argv, envp);
+    dyndebug_init(); /* after os_init: do not print output before execve */
     arch_init();
     gc_init();
     validate();
index e99d86b..4fb9539 100644 (file)
@@ -35,8 +35,18 @@ void unmap_gc_page();
 int check_pending_interrupts();
 #endif
 
-/* Block blockable interrupts for each SHOW, if not 0. */
+/*
+ * The next few defines serve as configuration -- edit them inline if
+ * you are a developer and want to affect FSHOW behaviour.
+ */
+
+/* Block blockable interrupts for each SHOW, if not 0.
+ * (On Windows, this setting has no effect.)
+ *
+ * In principle, this is a "configuration option", but I am not aware of
+ * any reason why or when it would be advantageous to disable it. */
 #define QSHOW_SIGNAL_SAFE 1
+
 /* Enable extra-verbose low-level debugging output for signals? (You
  * probably don't want this unless you're trying to debug very early
  * cold boot on a new machine, or one where you've just messed up
@@ -46,14 +56,70 @@ int check_pending_interrupts();
  * causes output from signal handlers, and the i/o libraries aren't
  * necessarily reentrant. But it can still be very convenient for
  * figuring out what's going on when you have a signal handling
- * problem. */
-#define QSHOW_SIGNALS 0
+ * problem.
+ *
+ * Possible values are:
+ *   0 -- Never show signal-related output.  There is absolutely no
+ *        run-time overhead from FSHOW_SIGNAL in this case.
+ *
+ *   1 -- (recommended)
+ *        Show signal-related output only if selected at run-time
+ *        (otherwise almost no run-time overhead).
+ *
+ *   2 -- Unconditionally show signal-related output.
+ *        Very significant overhead.
+ *
+ * For reasons of tradition, we default to 0 on POSIX and 1 on Windows
+ * through :SB-QSHOW.
+ *
+ * With option 1, set up environment variable SBCL_DYNDEBUG to include
+ * "fshow" or "fshow_signal" before starting SBCL to enable output.
+ *
+ * There is no particular advantage to option 2 except that you do not
+ * need to set environment variables in this case.
+ */
+#ifdef LISP_FEATURE_SB_QSHOW
+# define QSHOW_SIGNALS 1
+#else
+# define QSHOW_SIGNALS 0
+#endif
+
 /* Enable low-level debugging output, if not zero. Defaults to enabled
- * if QSHOW_SIGNALS, disabled otherwise. Change it to 1 if you want
+ * if QSHOW_SIGNALS, disabled otherwise. Change it to 1 or 2 if you want
  * low-level debugging output but not the whole signal mess. */
 #define QSHOW QSHOW_SIGNALS
 
-#if QSHOW
+/*
+ * Configuration options end here -- the following defines do not
+ * generally need customization.
+ */
+
+#define odxprint(topic, fmt, ...)                       \
+    do                                                  \
+        if (dyndebug_config.dyndebug_##topic)           \
+            odxprint_fun(fmt "\n", ##__VA_ARGS__);      \
+    while (0)
+
+void odxprint_fun(const char *fmt, ...);
+void fshow_fun(void *ignored, const char *fmt, ...);
+
+/* Flags defined in a structure to avoid code duplication between
+ * declaration and definition. */
+extern struct dyndebug_config {
+    int dyndebug_fshow;
+    int dyndebug_fshow_signal;
+    int dyndebug_gencgc_verbose;
+    int dyndebug_safepoints;
+    int dyndebug_seh;
+    int dyndebug_misc;
+    int dyndebug_pagefaults;
+} dyndebug_config;
+
+#ifdef LISP_FEATURE_GENCGC
+extern int gencgc_verbose;
+#endif
+
+void dyndebug_init(void);
 
 #if QSHOW_SIGNAL_SAFE == 1 && !defined(LISP_FEATURE_WIN32)
 
@@ -62,39 +128,33 @@ extern sigset_t blockable_sigset;
 
 #define QSHOW_BLOCK                                             \
         sigset_t oldset;                                        \
-        thread_sigmask(SIG_BLOCK, &blockable_sigset, &oldset);
-#define QSHOW_UNBLOCK thread_sigmask(SIG_SETMASK,&oldset,0);
+        thread_sigmask(SIG_BLOCK, &blockable_sigset, &oldset)
+#define QSHOW_UNBLOCK thread_sigmask(SIG_SETMASK,&oldset,0)
 #else
 #define QSHOW_BLOCK
 #define QSHOW_UNBLOCK
 #endif
 
-#ifdef LISP_FEATURE_SB_THREAD
-#define QSHOW_PREFIX fprintf(stderr, "%p ", pthread_self());
-#else
-#define QSHOW_PREFIX
-#endif
-
-#define FSHOW(args)                                             \
-    do {                                                        \
-        QSHOW_BLOCK                                             \
-        QSHOW_PREFIX                                            \
-        fprintf args;                                           \
-        QSHOW_UNBLOCK                                           \
-    } while (0)
-#define SHOW(string) FSHOW((stderr, "/%s\n", string))
+/* The following macros duplicate the expansion of odxprint, because the
+ * extra level of parentheses around `args' prevents us from
+ * implementing FSHOW in terms of odxprint directly.  (They also differ
+ * in a newline.)
+ */
 
+#if QSHOW
+# define FSHOW(args) \
+    do if (dyndebug_config.dyndebug_fshow) fshow_fun args; while (0)
+# define SHOW(string) FSHOW((stderr, "/%s\n", string))
 #else
-
-#define FSHOW(args)
-#define SHOW(string)
-
+# define FSHOW(args)
+# define SHOW(string)
 #endif
 
 #if QSHOW_SIGNALS
-#define FSHOW_SIGNAL FSHOW
+# define FSHOW_SIGNAL(args)                                             \
+    do if (dyndebug_config.dyndebug_fshow_signal) fshow_fun args; while (0)
 #else
-#define FSHOW_SIGNAL(args)
+# define FSHOW_SIGNAL(args)
 #endif
 
 /* KLUDGE: These are in theory machine-dependent and OS-dependent, but
index d730657..1277d24 100644 (file)
 #include "interrupt.h"
 #include "lispregs.h"
 
-/* Temporarily, this macro is a wrapper for FSHOW_SIGNAL.  Ultimately,
- * it will be restored to its full win32 branch functionality, where it
- * provides a very useful tracing mechanism that is configurable at
- * runtime. */
-#define odxprint_show(what, fmt, args...)                       \
-     do {                                                       \
-         struct thread *__self = arch_os_get_current_thread();  \
-         FSHOW_SIGNAL((stderr, "[%p/%p:%s] " fmt "\n",          \
-                       __self,                                  \
-                       __self->os_thread,                       \
-                       #what,                                   \
-                       ##args));                                \
-     } while (0)
-
-#if QSHOW_SIGNALS
-# define odxprint odxprint_show
-#else
-# define odxprint(what, fmt, args...) do {} while (0)
-#endif
-
 #if !defined(LISP_FEATURE_WIN32)
 /* win32-os.c covers these, but there is no unixlike-os.c, so the normal
  * definition goes here.  Fixme: (Why) don't these work for Windows?