]> git.sur5r.net Git - openldap/commitdiff
LDAP_THREAD_DEBUG revamping, cleanup, bug fixes.
authorHallvard Furuseth <hallvard@openldap.org>
Wed, 5 Apr 2006 19:52:07 +0000 (19:52 +0000)
committerHallvard Furuseth <hallvard@openldap.org>
Wed, 5 Apr 2006 19:52:07 +0000 (19:52 +0000)
Expand usage struct with magic numbers and state info.
Add options "noreinit", "nosync", "nomem", "threadID".  Remove "dupinit".
Detect address changes.  Turn off thread ID tracking by default.

include/ldap_int_thread.h
libraries/libldap_r/thr_debug.c

index 1f2e81dd5856a03f748c70c8bba98b0c92c551e0..ccf293bf2a6c973a7a36cc80449f704b1fad15f5 100644 (file)
@@ -247,9 +247,25 @@ LDAP_BEGIN_DECL
 #define LDAP_UINTPTR_T unsigned long
 #endif
 
-typedef union {
-       unsigned char                   *ptr;
-       LDAP_UINTPTR_T                  num;
+typedef enum {
+       ldap_debug_magic =      -(int) (((unsigned)-1)/19)
+} ldap_debug_magic_t;
+
+typedef enum {
+       /* Could fill in "locked" etc here later */
+       ldap_debug_state_inited = (int) (((unsigned)-1)/11),
+       ldap_debug_state_destroyed
+} ldap_debug_state_t;
+
+typedef struct {
+       /* Enclosed in magic numbers in the hope of catching overwrites */
+       ldap_debug_magic_t      magic;  /* bit pattern to recognize usages  */
+       LDAP_UINTPTR_T          self;   /* ~(LDAP_UINTPTR_T)&(this struct) */
+       union ldap_debug_mem_u {        /* Dummy memory reference */
+               unsigned char   *ptr;
+               LDAP_UINTPTR_T  num;
+       } mem;
+       ldap_debug_state_t      state;  /* doubles as another magic number */
 } ldap_debug_usage_info_t;
 
 typedef struct {
index efd573d552b5df48af080fad3bdc6fef1d8c15e4..142f9db23315dcb55d42e6883bcab058e2343bd0 100644 (file)
  */
 
 /*
- * This package provides three types of thread operation debugging:
+ * This package provides several types of thread operation debugging:
  *
- * - Print error messages and abort() when thread operations fail:
- *   Operations on threads, mutexes, condition variables, rdwr locks.
- *   Some thread pool operations are also checked, but not those for
- *   which failure can happen in normal slapd operation.
+ * - Check the results of operations on threads, mutexes, condition
+ *   variables and read/write locks.  Also check some thread pool
+ *   operations, but not those for which failure can happen in normal
+ *   slapd operation.
  *
- * - Wrap those types except threads and pools in structs that
- *   contain a state variable or a pointer to dummy allocated memory,
- *   and check that on all operations.  The dummy memory variant lets
- *   malloc debuggers see some incorrect use as memory leaks, access
- *   to freed memory, etc.
+ * - Wrap those types except threads and pools in structs with state
+ *   information, and check that on all operations:
+ *
+ *   + Check that the resources are initialized and are only used at
+ *     their original address (i.e. not realloced or copied).
+ *
+ *   + Check the owner (thread ID) on mutex operations.
+ *
+ *   + Optionally allocate a reference to a byte of dummy memory.
+ *     This lets malloc debuggers see some incorrect use as memory
+ *     leaks, access to freed memory, etc.
+ *
+ * - Print an error message and by default abort() upon errors.
  *
  * - Print a count of leaked thread resources after cleanup.
  *
@@ -38,6 +46,8 @@
  *   LDAP_UINTPTR_T=integer type to hold pointers, preferably unsigned.
  *      Used by dummy memory option "scramble". Default = unsigned long.
  *
+ *   LDAP_DEBUG_THREAD_NONE = initializer for a "no thread" thread ID.
+ *
  *   In addition, you may need to set up an implementation-specific way
  *      to enable whatever error checking your thread library provides.
  *      Currently only implemented for Posix threads (pthreads), where
  *      is PTHREAD_MUTEX_ERRORCHECK, or PTHREAD_MUTEX_ERRORCHECK_NP for
  *      Linux threads.  See pthread_mutexattr_settype(3).
  *
- * Run-time configuration:  Environment variable LDAP_THREAD_DEBUG.
+ * Run-time configuration:
+ *
+ *  Setup of memory debugging tools:
+ *   Tools that report uninitialized memory accesses should disable
+ *   such warnings about the function debug_already_initialized().
+ *   Alternatively, include "noreinit" (below) in $LDAP_THREAD_DEBUG.
  *
+ *  Environment variable $LDAP_THREAD_DEBUG:
  *   The variable may contain a comma- or space-separated option list.
  *   Options:
- *      off      - Disable this package.
- *   Error checking:
+ *      off      - Disable this package.  (It still slows things down).
+ *      tracethreads - Report create/join/exit/kill of threads.
  *      noabort  - Do not abort() on errors.
  *      noerror  - Do not report errors.  Implies noabort.
  *      nocount  - Do not report counts of unreleased resources.
- *   State variable/dummy memory, unless type wrapping is disabled:
- *      noalloc  - Default.  Use a state variable, not dummy memory.
- *      dupinit  - Implies noalloc.  Check if resources that have
- *                 not been destroyed are reinitialized.  Tools that
- *                 report uninitialized memory access should disable
- *                 such warnings about debug_already_initialized().
- *      alloc    - Allocate dummy memory and store pointers as-is.
- *                 Malloc debuggers might not notice unreleased
- *                 resources in global variables as memory leaks.
+ *      nosync   - Disable tests that use synchronizaion and thus
+ *                 clearly affect thread scheduling:
+ *                 Implies nocount, and cancels threadID if that is set.
+ *                 Note that if you turn on tracethreads or malloc
+ *                 debugging, these also use library calls which may
+ *                 affect thread scheduling (fprintf and malloc).
+ *   The following options do not apply if type wrapping is disabled:
+ *      nomem    - Do not check memory operations.
+ *                 Implies noreinit,noalloc.
+ *      noreinit - Do not catch reinitialization of existing resources.
+ *                 (That test accesses uninitialized memory).
+ *      threadID - Trace thread IDs.  Currently mostly useless.
+ *     Malloc debugging -- allocate dummy memory for initialized
+ *     resources, so malloc debuggers will report them as memory leaks:
+ *      noalloc  - Default.  Do not allocate dummy memory.
+ *      alloc    - Store a pointer to dummy memory.   However, leak
+ *                 detectors might not catch unreleased resources in
+ *                 global variables.
  *      scramble - Store bitwise complement of dummy memory pointer.
  *                 That never escapes memory leak detectors -
  *                 but detection while the program is running will
@@ -71,8 +96,6 @@
  *      adjptr   - Point to end of dummy memory.
  *                 Purify reports these as "potential leaks" (PLK).
  *                 I have not checked other malloc debuggers.
- *   Tracing:
- *      tracethreads - Report create/join/exit/kill of threads.
  */
 
 #include "portable.h"
 #define LDAP_THREAD_POOL_IMPLEMENTATION
 #include "ldap_thr_debug.h"  /* Get the underlying implementation */
 
+#ifndef LDAP_THREAD_DEBUG_WRAP
+#undef LDAP_THREAD_DEBUG_THREAD_ID
+#elif !defined LDAP_THREAD_DEBUG_THREAD_ID
+#define        LDAP_THREAD_DEBUG_THREAD_ID 1
+#endif
+
+/* Use native malloc - the OpenLDAP wrappers may defeat malloc debuggers */
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+
 
 /* Options from environment variable $LDAP_THREAD_DEBUG */
 enum { Count_no = 0, Count_yes, Count_reported, Count_reported_more };
-static int nodebug, noabort, noerror, count = Count_yes, options_done;
+static int count = Count_yes;
 #ifdef LDAP_THREAD_DEBUG_WRAP
 enum { Wrap_noalloc, Wrap_alloc, Wrap_scramble, Wrap_adjptr };
-static int dupinit, wraptype = Wrap_noalloc, wrap_offset, unwrap_offset;
+static int wraptype = Wrap_noalloc, wrap_offset, unwrap_offset;
+static int nomem, noreinit;
+#endif
+#if LDAP_THREAD_DEBUG_THREAD_ID +0
+static int threadID;
+#else
+enum { threadID = 0 };
 #endif
-static int tracethreads;
+static int nodebug, noabort, noerror, nosync, tracethreads;
+static int options_done;
+
 
+/* ldap_pvt_thread_initialize() called, ldap_pvt_thread_destroy() not called */
 static int threading_enabled;
 
+
+/* Resource counts */
 enum {
        Idx_unexited_thread, Idx_unjoined_thread, Idx_locked_mutex,
        Idx_mutex, Idx_cond, Idx_rdwr, Idx_tpool, Idx_max
@@ -115,52 +161,72 @@ static const char *const resource_names[] = {
 static ldap_int_thread_mutex_t resource_mutexes[Idx_max];
 
 
-/*
- * Making ldap_pvt_thread_t a wrapper around ldap_int_thread_t would
- * slow down ldap_pvt_thread_self(), so keep a list of threads instead.
- */
-typedef struct ldap_debug_thread_s {
-       ldap_pvt_thread_t                       wrapped;
-       ldap_debug_usage_info_t         usage;
-       int                                                     detached;
-       int                                                     freeme, idx;
-} ldap_debug_thread_t;
-
-static ldap_debug_thread_t             **thread_info;
-static unsigned int                            thread_info_size, thread_info_used;
-static ldap_int_thread_mutex_t thread_info_mutex;
+/* Hide pointers from malloc debuggers. */
+#define SCRAMBLE(ptr) (~(LDAP_UINTPTR_T) (ptr))
+#define UNSCRAMBLE_usagep(num) ((ldap_debug_usage_info_t *) ~(num))
+#define UNSCRAMBLE_dummyp(num) ((unsigned char *) ~(num))
 
 
 #define WARN(var, msg)   (warn (__FILE__, __LINE__, (msg), #var, (var)))
 #define WARN_IF(rc, msg) {if (rc) warn (__FILE__, __LINE__, (msg), #rc, (rc));}
-#define ERROR(var, msg) \
-       {if (!noerror) error(__FILE__, __LINE__, (msg), #var, (var));}
-#define ERROR_IF(rc, msg) \
-       {if (!noerror && (rc)) error(__FILE__, __LINE__, (msg), #rc, (rc));}
+
+#define ERROR(var, msg) { \
+       if (!noerror) { \
+               errmsg(__FILE__, __LINE__, (msg), #var, (var)); \
+               if( !noabort ) abort(); \
+       } \
+}
+
+#define ERROR_IF(rc, msg) { \
+       if (!noerror) { \
+               int rc_ = (rc); \
+               if (rc_) { \
+                       errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \
+                       if( !noabort ) abort(); \
+               } \
+       } \
+}
+
+#ifdef LDAP_THREAD_DEBUG_WRAP
+#define MEMERROR_IF(rc, msg, mem_act) { \
+       if (!noerror) { \
+               int rc_ = (rc); \
+               if (rc_) { \
+                       errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \
+                       if( wraptype != Wrap_noalloc ) { mem_act; } \
+                       if( !noabort ) abort(); \
+               } \
+       } \
+}
+#endif /* LDAP_THREAD_DEBUG_WRAP */
 
 #if 0
 static void
 warn( const char *file, int line, const char *msg, const char *var, int val )
 {
-       fprintf( stderr, "%s:%d: %s warning: %s is %d\n",
+       fprintf( stderr,
+               (strpbrk( var, "!=" )
+                ? "%s:%d: %s warning: %s\n"
+                : "%s:%d: %s warning: %s is %d\n"),
                file, line, msg, var, val );
 }
 #endif
 
 static void
-error( const char *file, int line, const char *msg, const char *var, int val )
+errmsg( const char *file, int line, const char *msg, const char *var, int val )
 {
-       fprintf( stderr, "%s:%d: %s error: %s is %d\n",
+       fprintf( stderr,
+               (strpbrk( var, "!=" )
+                ? "%s:%d: %s error: %s\n"
+                : "%s:%d: %s error: %s is %d\n"),
                file, line, msg, var, val );
-       if( !noabort )
-               abort();
 }
 
 static void
 count_resource_leaks( void )
 {
        int i, j;
-       char errbuf[200], *delim = "Leaked";
+       char errbuf[200];
        if( count == Count_yes ) {
                count = Count_reported;
 #if 0 /* Could break if there are still threads after atexit */
@@ -168,15 +234,12 @@ count_resource_leaks( void )
                        j |= ldap_int_thread_mutex_destroy( &resource_mutexes[i] );
                WARN_IF( j, "ldap_debug_thread_destroy:mutexes" );
 #endif
-               for( i = j = 0; i < Idx_max; i++ ) {
-                       if( resource_counts[i] ) {
-                               j += sprintf( errbuf + j, "%s %d %s",
-                                       delim, resource_counts[i], resource_names[i] );
-                               delim = ",";
-                       }
-               }
+               for( i = j = 0; i < Idx_max; i++ )
+                       if( resource_counts[i] )
+                               j += sprintf( errbuf + j, ", %d %s",
+                                       resource_counts[i], resource_names[i] );
                if( j )
-                       fprintf( stderr, "%s:%d: %s.\n", __FILE__, __LINE__, errbuf );
+                       fprintf( stderr, "== thr_debug: Leaked%s. ==\n", errbuf + 1 );
        }
 }
 
@@ -191,9 +254,14 @@ get_options( void )
                { "noabort",    &noabort,  1 },
                { "noerror",    &noerror,  1 },
                { "nocount",    &count,    Count_no },
+               { "nosync",     &nosync,   1 },
+#if LDAP_THREAD_DEBUG_THREAD_ID +0
+               { "threadID",   &threadID, 1 },
+#endif
 #ifdef LDAP_THREAD_DEBUG_WRAP
+               { "nomem",      &nomem,    1 },
+               { "noreinit",   &noreinit, 1 },
                { "noalloc",    &wraptype, Wrap_noalloc },
-               { "dupinit",    &dupinit,  1 },
                { "alloc",      &wraptype, Wrap_alloc },
                { "adjptr",     &wraptype, Wrap_adjptr },
                { "scramble",   &wraptype, Wrap_scramble },
@@ -211,7 +279,8 @@ get_options( void )
                                        if( oi->name && oi->name[optlen] == '\0' ) {
                                                *oi->var = oi->val;
                                        } else {
-                                               fprintf( stderr, "Unknown $%s option '%.*s'\n",
+                                               fprintf( stderr,
+                                                       "== thr_debug: Unknown $%s option '%.*s' ==\n",
                                                        "LDAP_THREAD_DEBUG", (int) optlen, s );
                                        }
                                        break;
@@ -221,75 +290,58 @@ get_options( void )
                }
        }
        if( nodebug ) {
-               noabort = noerror = 1;
-               tracethreads = dupinit = 0;
-               count = Count_no;
+               tracethreads = 0;
+               nosync = noerror = 1;
        }
+       if( nosync )
+               count = Count_no;
+       if( noerror )
+               noabort = 1;
+#if LDAP_THREAD_DEBUG_THREAD_ID +0
+       if( nosync )
+               threadID = 0;
+#endif
 #ifdef LDAP_THREAD_DEBUG_WRAP
-       if( nodebug || dupinit ) {
-               wraptype = Wrap_noalloc;
-       } else if( wraptype == Wrap_scramble ) {
-               const unsigned char *dummy = (const unsigned char *)&option_info;
-               if( sizeof(LDAP_UINTPTR_T) < sizeof(void *)
-                       || (unsigned char *)~~(LDAP_UINTPTR_T) dummy != dummy
-                       || (unsigned char *)~~(LDAP_UINTPTR_T) (unsigned char *)0 )
+       if( noerror )
+               nomem = 1;
+       if( !nomem ) {
+               static const ldap_debug_usage_info_t usage;
+               if( sizeof(LDAP_UINTPTR_T) < sizeof(unsigned char *)
+                       || sizeof(LDAP_UINTPTR_T) < sizeof(ldap_debug_usage_info_t *)
+                       || UNSCRAMBLE_usagep( SCRAMBLE( &usage ) ) != &usage
+                       || UNSCRAMBLE_dummyp( SCRAMBLE( (unsigned char *) 0 ) ) )
                {
-                       fprintf( stderr, "Misconfigured for $%s %s.  Using %s.\n",
-                               "LDAP_THREAD_DEBUG", "scramble", "adjptr" );
-                       wraptype = Wrap_adjptr;
+                       fputs( "== thr_debug: Memory checks unsupported, "
+                               "adding nomem to $LDAP_THREAD_DEBUG ==\n", stderr );
+                       nomem = 1;
                }
        }
+       if( nomem ) {
+               noreinit = 1;
+               wraptype = Wrap_noalloc;
+       }
        unwrap_offset = -(wrap_offset = (wraptype == Wrap_adjptr));
 #endif
        options_done = 1;
 }
 
 
-static char *
-thread_name( char *buf, int bufsize, ldap_pvt_thread_t thread )
-{
-       int i;
-       --bufsize;
-       if( bufsize > 2*sizeof(thread) )
-               bufsize = 2*sizeof(thread);
-       for( i = 0; i < bufsize; i += 2 )
-               snprintf( buf+i, 3, "%02x", ((unsigned char *)&thread)[i/2] );
-       return buf;
-}
-
-static void
-exit_thread_message( const ldap_pvt_thread_t thread )
-{
-       if( tracethreads ) {
-               char buf[40];
-               fprintf( stderr, "== Exiting thread %s ==\n",
-                       thread_name( buf, sizeof(buf), thread ) );
-       }
-}
-
-
 #ifndef LDAP_THREAD_DEBUG_WRAP
 
 #define        WRAPPED(ptr)                    (ptr)
-#define        SET_OWNER(ptr)                  ((void) 0)
+#define        GET_OWNER(ptr)                  0
+#define        SET_OWNER(ptr, thread)  ((void) 0)
 #define        RESET_OWNER(ptr)                ((void) 0)
 #define        ASSERT_OWNER(ptr, msg)  ((void) 0)
 #define        ASSERT_NO_OWNER(ptr, msg) ((void) 0)
 
-#define alloc_usage(ptr, msg)  ((void) 0)
+#define init_usage(ptr, msg)   ((void) 0)
 #define check_usage(ptr, msg)  ((void) 0)
-#define free_usage(ptr, msg)   ((void) 0)
-
-#define with_threads_lock(statement)   { statement; }
-#define get_new_thread_info(msg)               NULL
-#define update_thread_info(ti, th, det)        {}
-#define remove_thread_info(ti, msg)            ((void)0)
-#define get_thread_info(thread, msg)   NULL
-#define exiting_thread(msg)    exit_thread_message(ldap_pvt_thread_self())
+#define destroy_usage(ptr)             ((void) 0)
 
 #else /* LDAP_THREAD_DEBUG_WRAP */
 
-/* Specialize this if initializer is not appropriate. */
+/* Specialize this if the initializer is not appropriate. */
 /* The ASSERT_NO_OWNER() definition may also need an override. */
 #ifndef LDAP_DEBUG_THREAD_NONE
 #define        LDAP_DEBUG_THREAD_NONE { -1 } /* "no thread" ldap_int_thread_t value */
@@ -298,7 +350,7 @@ exit_thread_message( const ldap_pvt_thread_t thread )
 static const ldap_int_thread_t ldap_debug_thread_none = LDAP_DEBUG_THREAD_NONE;
 
 #define THREAD_MUTEX_OWNER(mutex) \
-       ldap_pvt_thread_equal( (mutex)->owner, ldap_pvt_thread_self() )
+       ldap_int_thread_equal( (mutex)->owner, ldap_int_thread_self() )
 
 void
 ldap_debug_thread_assert_mutex_owner(
@@ -307,101 +359,193 @@ ldap_debug_thread_assert_mutex_owner(
        const char *msg,
        ldap_pvt_thread_mutex_t *mutex )
 {
-       if( !(noerror || THREAD_MUTEX_OWNER( mutex )) )
-               error( file, line, "ASSERT_MUTEX_OWNER", msg, 0 );
+       if( !(noerror || THREAD_MUTEX_OWNER( mutex )) ) {
+               errmsg( file, line, msg, "ASSERT_MUTEX_OWNER", 0 );
+               if( !noabort ) abort();
+       }
 }
 
 #define        WRAPPED(ptr)                    (&(ptr)->wrapped)
-#define        SET_OWNER(ptr)                  ((ptr)->owner = ldap_pvt_thread_self())
-#define RESET_OWNER(ptr)               ((ptr)->owner = ldap_debug_thread_none)
-#define ASSERT_OWNER(ptr, msg) ERROR_IF( !THREAD_MUTEX_OWNER( ptr ), msg )
-#ifndef ASSERT_NO_OWNER
-#define ASSERT_NO_OWNER(ptr, msg) ERROR_IF( \
+#define        GET_OWNER(ptr)                  ((ptr)->owner)
+#define        SET_OWNER(ptr, thread)  ((ptr)->owner = (thread))
+#define        RESET_OWNER(ptr)                ((ptr)->owner = ldap_debug_thread_none)
+#define        ASSERT_OWNER(ptr, msg)  ERROR_IF( !THREAD_MUTEX_OWNER( ptr ), msg )
+#ifndef        ASSERT_NO_OWNER
+#define        ASSERT_NO_OWNER(ptr, msg) ERROR_IF( \
        !ldap_int_thread_equal( (ptr)->owner, ldap_debug_thread_none ), msg )
 #endif
 
-#define INITED_VALUE           0x12345678UL
-#define INITED_BYTE_VALUE      0xd5
+/* Try to provoke memory access error (for malloc debuggers) */
+#define PEEK(mem) {if (-*(volatile const unsigned char *)(mem)) debug_noop();}
 
-static int
-debug_already_initialized( const LDAP_UINTPTR_T *num )
-{
-       /* Valid programs will access uninitialized memory if dupinit */
-       return dupinit && *num == INITED_VALUE;
-}
+static void debug_noop( void );
+static int debug_already_initialized( const ldap_debug_usage_info_t *usage );
+
+/* Names used to give clearer error messages */
+#define IS_COPY_OR_MOVED(usage) ((usage)->self != SCRAMBLE( usage ))
+enum { Is_destroyed = 1 };
+
+#define DUMMY_ADDR(usage) \
+       (wraptype == Wrap_scramble \
+        ? UNSCRAMBLE_dummyp( (usage)->mem.num ) \
+        : (usage)->mem.ptr + unwrap_offset)
 
+/* Mark resource as initialized */
 static void
-alloc_usage( ldap_debug_usage_info_t *usage, const char *msg )
+init_usage( ldap_debug_usage_info_t *usage, const char *msg )
 {
        if( !options_done )
                get_options();
-       if( wraptype == Wrap_noalloc ) {
-               ERROR_IF( debug_already_initialized( &usage->num ), msg );
-               usage->num = INITED_VALUE;
-       } else {
-               unsigned char *dummy = malloc( 1 );
-               assert( dummy != NULL );
-               *dummy = INITED_BYTE_VALUE;
-               if( wraptype == Wrap_scramble ) {
-                       usage->num = ~(LDAP_UINTPTR_T) dummy;
-                       assert( (unsigned char *)~usage->num == dummy );
-               } else {
-                       usage->ptr = dummy + wrap_offset;
+       if( !nomem ) {
+               if( !noreinit ) {
+                       MEMERROR_IF( debug_already_initialized( usage ), msg, {
+                               /* Provoke malloc debuggers */
+                               unsigned char *dummy = DUMMY_ADDR( usage );
+                               PEEK( dummy );
+                               free( dummy );
+                               free( dummy );
+                       } );
+               }
+               usage->self = SCRAMBLE( usage );
+               if( wraptype != Wrap_noalloc ) {
+                       unsigned char *dummy = malloc( 1 );
+                       assert( dummy != NULL );
+                       if( wraptype == Wrap_scramble ) {
+                               usage->mem.num = SCRAMBLE( dummy );
+                               assert( UNSCRAMBLE_dummyp( usage->mem.num ) == dummy );
+                       } else {
+                               usage->mem.ptr = dummy + wrap_offset;
+                       }
                }
        }
+       usage->magic = ldap_debug_magic;
+       usage->state = ldap_debug_state_inited;
 }
 
+/* Check that resource is initialized and not copied/realloced */
 static void
-check_usage( ldap_debug_usage_info_t *usage, const char *msg )
+check_usage( const ldap_debug_usage_info_t *usage, const char *msg )
 {
-       if( wraptype == Wrap_noalloc ) {
-               ERROR_IF( usage->num != INITED_VALUE, msg );
-       } else if( wraptype == Wrap_scramble ) {
-               ERROR_IF( !usage->num, msg );
-               ERROR_IF( *(unsigned char *)~usage->num != INITED_BYTE_VALUE, msg );
-       } else {
-               ERROR_IF( !usage->ptr, msg );
-               ERROR_IF( usage->ptr[unwrap_offset] != INITED_BYTE_VALUE, msg );
+       if( usage->magic != ldap_debug_magic ) {
+               ERROR( usage->magic, msg );
+               return;
+       }
+       switch( usage->state ) {
+       case ldap_debug_state_destroyed:
+               MEMERROR_IF( Is_destroyed, msg, {
+                       PEEK( DUMMY_ADDR( usage ) );
+               } );
+               break;
+       default:
+               ERROR( usage->state, msg );
+               break;
+       case ldap_debug_state_inited:
+               if( !nomem ) {
+                       MEMERROR_IF( IS_COPY_OR_MOVED( usage ), msg, {
+                               PEEK( DUMMY_ADDR( usage ) );
+                               PEEK( UNSCRAMBLE_usagep( usage->self ) );
+                       } );
+               }
+               break;
        }
 }
 
+/* Mark resource as destroyed. */
+/* Does not check for errors, call check_usage()/init_usage() first. */
 static void
-free_usage( ldap_debug_usage_info_t *usage, const char *msg )
+destroy_usage( ldap_debug_usage_info_t *usage )
 {
-       if( wraptype == Wrap_noalloc ) {
-               usage->num = ~(LDAP_UINTPTR_T)INITED_VALUE;
-       } else {
-               unsigned char *dummy = (wraptype == Wrap_scramble
-                                       ? (unsigned char *)~usage->num
-                                       : usage->ptr + unwrap_offset);
-               *(volatile unsigned char *)dummy = (unsigned char)-1;
-               free( dummy );
+       if( usage->state == ldap_debug_state_inited ) {
+               if( wraptype != Wrap_noalloc ) {
+                       free( DUMMY_ADDR( usage ) );
+                       /* Do not reset the DUMMY_ADDR, leave it for malloc debuggers
+                        * in case the resource is used after it is freed. */
+               }
+               usage->state = ldap_debug_state_destroyed;
        }
 }
 
-#define with_threads_lock(statement) { \
-       if( !nodebug ) { \
-               int rc_wtl_ = ldap_int_thread_mutex_lock( &thread_info_mutex ); \
-               assert( rc_wtl_ == 0 ); \
-       } \
-    statement; \
-       if( !nodebug ) { \
-               int rc_wtl_ = ldap_int_thread_mutex_unlock( &thread_info_mutex ); \
-               assert( rc_wtl_ == 0 ); \
+/* Define these after they are used, so they are hopefully not inlined */
+
+static void
+debug_noop( void )
+{
+}
+
+/* Return true if the resource is initialized and not copied/realloced. */
+/* Valid programs access uninitialized memory here unless "noreinit".   */
+static int
+debug_already_initialized( const ldap_debug_usage_info_t *usage )
+{
+       return (usage->state == ldap_debug_state_inited &&
+               !IS_COPY_OR_MOVED( usage ) &&
+               usage->magic == ldap_debug_magic);
+}
+
+#endif /* LDAP_THREAD_DEBUG_WRAP */
+
+
+#if !(LDAP_THREAD_DEBUG_THREAD_ID +0)
+
+typedef int ldap_debug_thread_t;
+#define init_thread_info()     {}
+#define with_thread_info_lock(statements) { statements; }
+#define thread_info_detached(t)        0
+#define add_thread_info(msg, thr, det) ((void) 0)
+#define remove_thread_info(tinfo, msg) ((void) 0)
+#define get_thread_info(thread, msg)   NULL
+
+#else /* LDAP_THREAD_DEBUG_THREAD_ID */
+
+/*
+ * Thread ID tracking.  Currently acieves little.
+ * Should be either expanded or deleted.
+ */
+
+/*
+ * Array of threads.  Used instead of making ldap_pvt_thread_t a wrapper
+ * around ldap_int_thread_t, which would slow down ldap_pvt_thread_self().
+ */
+typedef struct {
+       ldap_pvt_thread_t           wrapped;
+       ldap_debug_usage_info_t     usage;
+       int                         detached;
+       int                         idx;
+} ldap_debug_thread_t;
+
+static ldap_debug_thread_t      **thread_info;
+static unsigned int             thread_info_size, thread_info_used;
+static ldap_int_thread_mutex_t  thread_info_mutex;
+
+#define init_thread_info() { \
+       if( threadID ) { \
+               int mutex_init_rc = ldap_int_thread_mutex_init( &thread_info_mutex ); \
+               assert( mutex_init_rc == 0 ); \
        } \
 }
 
-static ldap_debug_thread_t *
-get_new_thread_info( const char *msg )
+#define with_thread_info_lock(statements) { \
+       int rc_wtl_ = ldap_int_thread_mutex_lock( &thread_info_mutex ); \
+       assert( rc_wtl_ == 0 ); \
+       { statements; } \
+       rc_wtl_ = ldap_int_thread_mutex_unlock( &thread_info_mutex ); \
+       assert( rc_wtl_ == 0 ); \
+}
+
+#define thread_info_detached(t) ((t)->detached)
+
+static void
+add_thread_info(
+       const char *msg,
+       const ldap_pvt_thread_t *thread,
+       int detached )
 {
-       if( nodebug )
-               return NULL;
+       ldap_debug_thread_t *t;
        if( thread_info_used >= thread_info_size ) {
-               unsigned int more = thread_info_size + 1; /* debug value. increase. */
+               unsigned int more = thread_info_size + 8;
                unsigned int new_size = thread_info_size + more;
-               ldap_debug_thread_t *t = calloc( more, sizeof(ldap_debug_thread_t) );
+               t = calloc( more, sizeof(ldap_debug_thread_t) );
                assert( t != NULL );
-               t->freeme = 1;
                thread_info = realloc( thread_info, new_size * sizeof(*thread_info) );
                assert( thread_info != NULL );
                while( thread_info_size < new_size ) {
@@ -409,36 +553,26 @@ get_new_thread_info( const char *msg )
                        thread_info[thread_info_size++] = t++;
                }
        }
-       alloc_usage( &thread_info[thread_info_used]->usage, msg );
-       return thread_info[thread_info_used++];
-}
-
-static void
-update_thread_info(
-       ldap_debug_thread_t *t,
-       const ldap_pvt_thread_t *thread,
-       int detached )
-{
-       if( !nodebug ) {
-               t->wrapped = *thread;
-               t->detached = detached;
-       }
+       t = thread_info[thread_info_used];
+       init_usage( &t->usage, msg );
+       t->wrapped = *thread;
+       t->detached = detached;
+       thread_info_used++;
 }
 
 static void
 remove_thread_info( ldap_debug_thread_t *t, const char *msg )
 {
-       if( !nodebug ) {
                ldap_debug_thread_t *last;
                int idx;
-               free_usage( &t->usage, msg );
+               check_usage( &t->usage, msg );
+               destroy_usage( &t->usage );
                idx = t->idx;
                assert( thread_info[idx] == t );
                last = thread_info[--thread_info_used];
                assert( last->idx == thread_info_used );
                (thread_info[idx]              = last)->idx = idx;
                (thread_info[thread_info_used] = t   )->idx = thread_info_used;
-       }
 }
 
 ldap_debug_thread_t *
@@ -446,8 +580,6 @@ get_thread_info( ldap_pvt_thread_t *thread, const char *msg )
 {
        unsigned int i;
        ldap_debug_thread_t *t;
-       if( nodebug )
-               return NULL;
        for( i = 0; i < thread_info_used; i++ ) {
                if( ldap_pvt_thread_equal( *thread, thread_info[i]->wrapped ) )
                        break;
@@ -458,24 +590,23 @@ get_thread_info( ldap_pvt_thread_t *thread, const char *msg )
        return t;
 }
 
-static void
-exiting_thread( const char *msg )
+#endif /* LDAP_THREAD_DEBUG_THREAD_ID */
+
+
+static char *
+thread_name( char *buf, int bufsize, ldap_pvt_thread_t thread )
 {
-       if( !nodebug ) {
-               ldap_pvt_thread_t thread;
-               thread = ldap_pvt_thread_self();
-               exit_thread_message( thread );
-               with_threads_lock({
-                       ldap_debug_thread_t *t = get_thread_info( &thread, msg );
-                       if( t->detached )
-                               remove_thread_info( t, msg );
-               });
-       }
+       int i;
+       --bufsize;
+       if( bufsize > 2*sizeof(thread) )
+               bufsize = 2*sizeof(thread);
+       for( i = 0; i < bufsize; i += 2 )
+               snprintf( buf+i, 3, "%02x", ((unsigned char *)&thread)[i/2] );
+       return buf;
 }
 
-#endif /* LDAP_THREAD_DEBUG_WRAP */
-
 
+/* Add <adjust> (+/-1) to resource count <which> unless "nocount". */
 static void
 adjust_count( int which, int adjust )
 {
@@ -490,7 +621,7 @@ adjust_count( int which, int adjust )
                rc = ldap_int_thread_mutex_unlock( &resource_mutexes[which] );
                assert( rc == 0 );
        case Count_reported:
-               fputs( "...more ldap_debug_thread activity after exit...\n", stderr );
+               fputs( "== thr_debug: More thread activity after exit ==\n", stderr );
                count = Count_reported_more;
                /* FALL THROUGH */
        case Count_reported_more:
@@ -518,8 +649,7 @@ ldap_debug_thread_initialize( void )
                ERROR( rc, "ldap_debug_thread_initialize:threads" );
                threading_enabled = 0;
        } else {
-               rc2 = ldap_int_thread_mutex_init( &thread_info_mutex );
-               assert( rc2 == 0 );
+               init_thread_info();
                if( count != Count_no ) {
                        for( i = rc2 = 0; i < Idx_max; i++ )
                                rc2 |= ldap_int_thread_mutex_init( &resource_mutexes[i] );
@@ -585,26 +715,28 @@ ldap_pvt_thread_create(
        void *arg )
 {
        int rc;
-       ERROR_IF( !threading_enabled, "ldap_pvt_thread_create" );
        if( !options_done )
                get_options();
-       with_threads_lock({
-               ldap_debug_thread_t *t;
-               t = get_new_thread_info( "ldap_pvt_thread_create" );
+       ERROR_IF( !threading_enabled, "ldap_pvt_thread_create" );
+       if( threadID ) {
+               with_thread_info_lock({
+                       rc = ldap_int_thread_create( thread, detach, start_routine, arg );
+                       if( rc == 0 )
+                               add_thread_info( "ldap_pvt_thread_create", thread, detach );
+               });
+       } else {
                rc = ldap_int_thread_create( thread, detach, start_routine, arg );
-               if( rc ) {
-                       ERROR( rc, "ldap_pvt_thread_create" );
-                       remove_thread_info( t, "ldap_pvt_thread_init" );
-               } else {
-                       update_thread_info( t, thread, detach );
-               }
-       });
-       if( rc == 0 ) {
+       }
+       if( rc ) {
+               ERROR( rc, "ldap_pvt_thread_create" );
+       } else {
                if( tracethreads ) {
-                       char buf[40];
-                       fprintf( stderr, "== Created thread %s%s ==\n",
+                       char buf[40], buf2[40];
+                       fprintf( stderr,
+                               "== thr_debug: Created thread %s%s from thread %s ==\n",
                                thread_name( buf, sizeof(buf), *thread ),
-                               detach ? " (detached)" : "" );
+                               detach ? " (detached)" : "",
+                               thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
                }
                adjust_count( Idx_unexited_thread, +1 );
                if( !detach )
@@ -616,9 +748,23 @@ ldap_pvt_thread_create(
 void
 ldap_pvt_thread_exit( void *retval )
 {
+       ldap_pvt_thread_t thread;
        ERROR_IF( !threading_enabled, "ldap_pvt_thread_exit" );
+       thread = ldap_pvt_thread_self();
+       if( tracethreads ) {
+               char buf[40];
+               fprintf( stderr, "== thr_debug: Exiting thread %s ==\n",
+                       thread_name( buf, sizeof(buf), thread ) );
+       }
+       if( threadID ) {
+               with_thread_info_lock({
+                       ldap_debug_thread_t *t = get_thread_info(
+                               &thread, "ldap_pvt_thread_exit" );
+                       if( thread_info_detached( t ) )
+                               remove_thread_info( t, "ldap_pvt_thread_exit" );
+               });
+       }
        adjust_count( Idx_unexited_thread, -1 );
-       exiting_thread( "ldap_pvt_thread_exit" );
        ldap_int_thread_exit( retval );
 }
 
@@ -626,21 +772,26 @@ int
 ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
 {
        int rc;
-       ldap_debug_thread_t *t;
+       ldap_debug_thread_t *t = NULL;
        ERROR_IF( !threading_enabled, "ldap_pvt_thread_join" );
        if( tracethreads ) {
-               char buf[40];
-               fprintf( stderr, "== Joining thread %s ==\n",
-                       thread_name( buf, sizeof(buf), thread ) );
+               char buf[40], buf2[40];
+               fprintf( stderr, "== thr_debug: Joining thread %s in thread %s ==\n",
+                       thread_name( buf, sizeof(buf), thread ),
+                       thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
        }
-       with_threads_lock(
-               t = get_thread_info( &thread, "ldap_pvt_thread_join" ) );
+       if( threadID )
+               with_thread_info_lock( {
+                       t = get_thread_info( &thread, "ldap_pvt_thread_join" );
+                       ERROR_IF( thread_info_detached( t ), "ldap_pvt_thread_join" );
+               } );
        rc = ldap_int_thread_join( thread, thread_return );
        if( rc ) {
                ERROR( rc, "ldap_pvt_thread_join" );
        } else {
-               with_threads_lock(
-                       remove_thread_info( t, "ldap_pvt_thread_join" ) );
+               if( threadID )
+                       with_thread_info_lock(
+                               remove_thread_info( t, "ldap_pvt_thread_join" ) );
                adjust_count( Idx_unjoined_thread, -1 );
        }
        return rc;
@@ -652,9 +803,11 @@ ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
        int rc;
        ERROR_IF( !threading_enabled, "ldap_pvt_thread_kill" );
        if( tracethreads ) {
-               char buf[40];
-               fprintf( stderr, "== Killing thread %s (sig %i) ==\n",
-                       thread_name( buf, sizeof(buf), thread ), signo );
+               char buf[40], buf2[40];
+               fprintf( stderr,
+                       "== thr_debug: Killing thread %s (sig %i) from thread %s ==\n",
+                       thread_name( buf, sizeof(buf), thread ), signo,
+                       thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
        }
        rc = ldap_int_thread_kill( thread, signo );
        ERROR_IF( rc, "ldap_pvt_thread_kill" );
@@ -684,11 +837,11 @@ int
 ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
 {
        int rc;
-       alloc_usage( &cond->usage, "ldap_pvt_thread_cond_init" );
+       init_usage( &cond->usage, "ldap_pvt_thread_cond_init" );
        rc = ldap_int_thread_cond_init( WRAPPED( cond ) );
        if( rc ) {
                ERROR( rc, "ldap_pvt_thread_cond_init" );
-               free_usage( &cond->usage, "ldap_pvt_thread_cond_init" );
+               destroy_usage( &cond->usage );
        } else {
                adjust_count( Idx_cond, +1 );
        }
@@ -704,7 +857,7 @@ ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond )
        if( rc ) {
                ERROR( rc, "ldap_pvt_thread_cond_destroy" );
        } else {
-               free_usage( &cond->usage, "ldap_pvt_thread_cond_destroy" );
+               destroy_usage( &cond->usage );
                adjust_count( Idx_cond, -1 );
        }
        return rc;
@@ -736,14 +889,16 @@ ldap_pvt_thread_cond_wait(
        ldap_pvt_thread_mutex_t *mutex )
 {
        int rc;
+       ldap_int_thread_t owner;
        check_usage( &cond->usage, "ldap_pvt_thread_cond_wait:cond" );
        check_usage( &mutex->usage, "ldap_pvt_thread_cond_wait:mutex" );
        adjust_count( Idx_locked_mutex, -1 );
+       owner = GET_OWNER( mutex );
        ASSERT_OWNER( mutex, "ldap_pvt_thread_cond_wait" );
-       RESET_OWNER( mutex ); /* Breaks if this thread did not own the mutex */
+       RESET_OWNER( mutex );
        rc = ldap_int_thread_cond_wait( WRAPPED( cond ), WRAPPED( mutex ) );
        ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_cond_wait" );
-       SET_OWNER( mutex );
+       SET_OWNER( mutex, rc ? owner : ldap_int_thread_self() );
        adjust_count( Idx_locked_mutex, +1 );
        ERROR_IF( rc, "ldap_pvt_thread_cond_wait" );
        return rc;
@@ -753,11 +908,11 @@ int
 ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
 {
        int rc;
-       alloc_usage( &mutex->usage, "ldap_pvt_thread_mutex_init" );
+       init_usage( &mutex->usage, "ldap_pvt_thread_mutex_init" );
        rc = ldap_int_thread_mutex_init( WRAPPED( mutex ) );
        if( rc ) {
                ERROR( rc, "ldap_pvt_thread_mutex_init" );
-               free_usage( &mutex->usage, "ldap_pvt_thread_mutex_init" );
+               destroy_usage( &mutex->usage );
        } else {
                RESET_OWNER( mutex );
                adjust_count( Idx_mutex, +1 );
@@ -774,9 +929,8 @@ ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
        rc = ldap_int_thread_mutex_destroy( WRAPPED( mutex ) );
        if( rc ) {
                ERROR( rc, "ldap_pvt_thread_mutex_destroy" );
-               /* mutex->owner may now be scrambled, sorry */
        } else {
-               free_usage( &mutex->usage, "ldap_pvt_thread_mutex_destroy" );
+               destroy_usage( &mutex->usage );
                RESET_OWNER( mutex );
                adjust_count( Idx_mutex, -1 );
        }
@@ -793,7 +947,7 @@ ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
                ERROR_IF( rc, "ldap_pvt_thread_mutex_lock" );
        } else {
                ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_lock" );
-               SET_OWNER( mutex );
+               SET_OWNER( mutex, ldap_int_thread_self() );
                adjust_count( Idx_locked_mutex, +1 );
        }
        return rc;
@@ -807,7 +961,7 @@ ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
        rc = ldap_int_thread_mutex_trylock( WRAPPED( mutex ) );
        if( rc == 0 ) {
                ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_trylock" );
-               SET_OWNER( mutex );
+               SET_OWNER( mutex, ldap_int_thread_self() );
                adjust_count( Idx_locked_mutex, +1 );
        }
        return rc;
@@ -836,11 +990,11 @@ int
 ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock )
 {
        int rc;
-       alloc_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_init" );
+       init_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_init" );
        rc = ldap_int_thread_rdwr_init( WRAPPED( rwlock ) );
        if( rc ) {
                ERROR( rc, "ldap_pvt_thread_rdwr_init" );
-               free_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_init" );
+               destroy_usage( &rwlock->usage );
        } else {
                adjust_count( Idx_rdwr, +1 );
        }
@@ -856,7 +1010,7 @@ ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock )
        if( rc ) {
                ERROR( rc, "ldap_pvt_thread_rdwr_destroy" );
        } else {
-               free_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_destroy" );
+               destroy_usage( &rwlock->usage );
                adjust_count( Idx_rdwr, -1 );
        }
        return rc;
@@ -916,7 +1070,7 @@ ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock )
        return rc;
 }
 
-#ifdef LDAP_RDWR_DEBUG
+#if defined(LDAP_RDWR_DEBUG) && !defined(LDAP_THREAD_HAVE_RDWR)
 
 int
 ldap_pvt_thread_rdwr_readers( ldap_pvt_thread_rdwr_t *rwlock )
@@ -939,7 +1093,7 @@ ldap_pvt_thread_rdwr_active( ldap_pvt_thread_rdwr_t *rwlock )
        return ldap_int_thread_rdwr_active( WRAPPED( rwlock ) );
 }
 
-#endif /* LDAP_RDWR_DEBUG */
+#endif /* LDAP_RDWR_DEBUG && !LDAP_THREAD_HAVE_RDWR */
 
 
 /* Some wrappers for LDAP_THREAD_POOL_IMPLEMENTATION: */
@@ -952,6 +1106,8 @@ ldap_pvt_thread_pool_init(
        int max_pending )
 {
        int rc;
+       if( !options_done )
+               get_options();
        ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_init" );
        rc = ldap_int_thread_pool_init( tpool, max_threads, max_pending );
        if( rc ) {