From ebb604b4422ba886e70ce2e531c1f4d28d44e721 Mon Sep 17 00:00:00 2001 From: David Lichteblau Date: Tue, 4 Sep 2012 17:16:01 +0200 Subject: [PATCH] Add odxprint, a replacement for FSHOW which can be configured at run-time - 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 | 6 ++ make-config.sh | 1 + src/runtime/gencgc.c | 2 +- src/runtime/interrupt.c | 2 +- src/runtime/print.c | 170 +++++++++++++++++++++++++++++++++++++++- src/runtime/runtime.c | 1 + src/runtime/runtime.h | 114 ++++++++++++++++++++------- src/runtime/safepoint.c | 20 ----- 8 files changed, 266 insertions(+), 50 deletions(-) diff --git a/base-target-features.lisp-expr b/base-target-features.lisp-expr index f670123..680e29d 100644 --- a/base-target-features.lisp-expr +++ b/base-target-features.lisp-expr @@ -122,6 +122,12 @@ ;; 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 diff --git a/make-config.sh b/make-config.sh index b5c425f..f251797 100644 --- a/make-config.sh +++ b/make-config.sh @@ -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 diff --git a/src/runtime/gencgc.c b/src/runtime/gencgc.c index 175be61..18f8179 100644 --- a/src/runtime/gencgc.c +++ b/src/runtime/gencgc.c @@ -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; diff --git a/src/runtime/interrupt.c b/src/runtime/interrupt.c index 6a4d25b..b8ab510 100644 --- a/src/runtime/interrupt.c +++ b/src/runtime/interrupt.c @@ -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. */ diff --git a/src/runtime/print.c b/src/runtime/print.c index 5b66b4e..e50da2e 100644 --- a/src/runtime/print.c +++ b/src/runtime/print.c @@ -24,6 +24,175 @@ #include "sbcl.h" #include "print.h" #include "runtime.h" +#include +#include "thread.h" /* genesis/primitive-objects.h needs this */ +#include + +/* 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) @@ -35,7 +204,6 @@ #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" diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index f66a7e5..f1c50cf 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -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(); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index e99d86b..4fb9539 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -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 diff --git a/src/runtime/safepoint.c b/src/runtime/safepoint.c index d730657..1277d24 100644 --- a/src/runtime/safepoint.c +++ b/src/runtime/safepoint.c @@ -46,26 +46,6 @@ #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? -- 1.7.10.4