From: Hallvard Furuseth Date: Wed, 5 Apr 2006 19:52:07 +0000 (+0000) Subject: LDAP_THREAD_DEBUG revamping, cleanup, bug fixes. X-Git-Tag: OPENLDAP_REL_ENG_2_4_BP~8 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=ca4a4456e8d30666aa8f5e9565b37e3456827756;p=openldap LDAP_THREAD_DEBUG revamping, cleanup, bug fixes. 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. --- diff --git a/include/ldap_int_thread.h b/include/ldap_int_thread.h index 1f2e81dd58..ccf293bf2a 100644 --- a/include/ldap_int_thread.h +++ b/include/ldap_int_thread.h @@ -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 { diff --git a/libraries/libldap_r/thr_debug.c b/libraries/libldap_r/thr_debug.c index efd573d552..142f9db233 100644 --- a/libraries/libldap_r/thr_debug.c +++ b/libraries/libldap_r/thr_debug.c @@ -15,18 +15,26 @@ */ /* - * 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 @@ -45,24 +55,39 @@ * 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" @@ -91,18 +114,41 @@ #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 (+/-1) to resource count 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 ) {