/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
- * Copyright 2005-2006 The OpenLDAP Foundation.
+ * Copyright 2005-2007 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
*
* Run-time configuration:
*
- * Setup of memory debugging tools:
+ * 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.
enum { threadID = 0 };
#endif
static int nodebug, noabort, noerror, nosync, tracethreads;
+static int wrap_threads;
static int options_done;
if( s != NULL ) {
while( *(s += strspn( s, ", \t\r\n" )) != '\0' ) {
size_t optlen = strcspn( s, ", \t\r\n" );
- const struct option_info_s *oi;
- for( oi = option_info; oi->name; oi++ ) {
- if( strncasecmp( oi->name, s, optlen ) == 0 ) {
- if( oi->name && oi->name[optlen] == '\0' ) {
- *oi->var = oi->val;
- } else {
- fprintf( stderr,
- "== thr_debug: Unknown $%s option '%.*s' ==\n",
- "LDAP_THREAD_DEBUG", (int) optlen, s );
- }
- break;
- }
- }
+ const struct option_info_s *oi = option_info;
+ while( oi->name &&
+ (strncasecmp( oi->name, s, optlen ) || oi->name[optlen]) )
+ oi++;
+ if( oi->name )
+ *oi->var = oi->val;
+ else
+ fprintf( stderr,
+ "== thr_debug: Unknown $%s option '%.*s' ==\n",
+ "LDAP_THREAD_DEBUG", (int) optlen, s );
s += optlen;
}
}
}
unwrap_offset = -(wrap_offset = (wraptype == Wrap_adjptr));
#endif
+ wrap_threads = (tracethreads || threadID || count);
options_done = 1;
}
static void debug_noop( void );
static int debug_already_initialized( const ldap_debug_usage_info_t *usage );
-/* Names used to give clearer error messages */
+/* Name used for clearer error message */
#define IS_COPY_OR_MOVED(usage) ((usage)->self != SCRAMBLE( usage ))
-enum { Is_destroyed = 1 };
#define DUMMY_ADDR(usage) \
(wraptype == Wrap_scramble \
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 );
+ /* Verify that ptr<->integer casts work on this host */
assert( UNSCRAMBLE_dummyp( usage->mem.num ) == dummy );
} else {
usage->mem.ptr = dummy + wrap_offset;
}
}
+ } else {
+ /* Unused, but set for readability in debugger */
+ usage->mem.ptr = NULL;
}
+ usage->self = SCRAMBLE( usage ); /* If nomem, only for debugger */
usage->magic = ldap_debug_magic;
usage->state = ldap_debug_state_inited;
}
static void
check_usage( const ldap_debug_usage_info_t *usage, const char *msg )
{
+ enum { Is_destroyed = 1 }; /* Name used for clearer error message */
+
if( usage->magic != ldap_debug_magic ) {
ERROR( usage->magic, msg );
return;
{
}
-/* Return true if the resource is initialized and not copied/realloced. */
-/* Valid programs access uninitialized memory here unless "noreinit". */
+/*
+ * Valid programs access uninitialized memory here unless "noreinit".
+ *
+ * Returns true if the resource is initialized and not copied/realloced.
+ */
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);
+ /*
+ * 'ret' keeps the Valgrind warning "Conditional jump or move
+ * depends on uninitialised value(s)" _inside_ this function.
+ */
+ volatile int ret = 0;
+ if( usage->state == ldap_debug_state_inited )
+ if( !IS_COPY_OR_MOVED( usage ) )
+ if( usage->magic == ldap_debug_magic )
+ ret = 1;
+ return ret;
}
#endif /* LDAP_THREAD_DEBUG_WRAP */
#if !(LDAP_THREAD_DEBUG_THREAD_ID +0)
-typedef int ldap_debug_thread_t;
+typedef void ldap_debug_thread_t;
#define init_thread_info() {}
#define with_thread_info_lock(statements) { statements; }
#define thread_info_detached(t) 0
(thread_info[thread_info_used] = t )->idx = thread_info_used;
}
-ldap_debug_thread_t *
-get_thread_info( ldap_pvt_thread_t *thread, const char *msg )
+static ldap_debug_thread_t *
+get_thread_info( ldap_pvt_thread_t thread, const char *msg )
{
unsigned int i;
ldap_debug_thread_t *t;
for( i = 0; i < thread_info_used; i++ ) {
- if( ldap_pvt_thread_equal( *thread, thread_info[i]->wrapped ) )
+ if( ldap_pvt_thread_equal( thread, thread_info[i]->wrapped ) )
break;
}
ERROR_IF( i == thread_info_used, msg );
resource_counts[which] += adjust;
rc = ldap_int_thread_mutex_unlock( &resource_mutexes[which] );
assert( rc == 0 );
+ break;
case Count_reported:
fputs( "== thr_debug: More thread activity after exit ==\n", stderr );
count = Count_reported_more;
return 0;
}
+static void
+thread_exiting( const char *how, const char *msg )
+{
+ ldap_pvt_thread_t thread;
+#if 0 /* Detached threads may exit after ldap_debug_thread_destroy(). */
+ ERROR_IF( !threading_enabled, msg );
+#endif
+ thread = ldap_pvt_thread_self();
+ if( tracethreads ) {
+ char buf[40];
+ fprintf( stderr, "== thr_debug: %s thread %s ==\n",
+ how, thread_name( buf, sizeof(buf), thread ) );
+ }
+ if( threadID ) {
+ with_thread_info_lock({
+ ldap_debug_thread_t *t = get_thread_info( thread, msg );
+ if( thread_info_detached( t ) )
+ remove_thread_info( t, msg );
+ });
+ }
+ adjust_count( Idx_unexited_thread, -1 );
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ thread_exiting( "Exiting", "ldap_pvt_thread_exit" );
+ ldap_int_thread_exit( retval );
+}
+
+typedef struct {
+ void *(*start_routine)( void * );
+ void *arg;
+} ldap_debug_thread_call_t;
+
+static void *
+ldap_debug_thread_wrapper( void *arg )
+{
+ void *ret;
+ ldap_debug_thread_call_t call = *(ldap_debug_thread_call_t *)arg;
+ free( arg );
+ ret = call.start_routine( call.arg );
+ thread_exiting( "Returning from", "ldap_debug_thread_wrapper" );
+ return ret;
+}
+
int
ldap_pvt_thread_create(
ldap_pvt_thread_t *thread,
if( !options_done )
get_options();
ERROR_IF( !threading_enabled, "ldap_pvt_thread_create" );
+ if( wrap_threads ) {
+ ldap_debug_thread_call_t *call = malloc(
+ sizeof( ldap_debug_thread_call_t ) );
+ assert( call != NULL );
+ call->start_routine = start_routine;
+ call->arg = arg;
+ start_routine = ldap_debug_thread_wrapper;
+ arg = call;
+ }
if( threadID ) {
with_thread_info_lock({
rc = ldap_int_thread_create( thread, detach, start_routine, arg );
}
if( rc ) {
ERROR( rc, "ldap_pvt_thread_create" );
+ if( wrap_threads )
+ free( arg );
} else {
if( tracethreads ) {
char buf[40], buf2[40];
return rc;
}
-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 );
- ldap_int_thread_exit( retval );
-}
-
int
ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
{
}
if( threadID )
with_thread_info_lock( {
- t = get_thread_info( &thread, "ldap_pvt_thread_join" );
+ 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 );
{
int rc, has_pool;
ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_submit" );
- has_pool = (tpool != NULL && *tpool != NULL);
+ has_pool = (tpool && *tpool);
rc = ldap_int_thread_pool_submit( tpool, start_routine, arg );
if( has_pool )
ERROR_IF( rc, "ldap_pvt_thread_pool_submit" );
{
int rc, has_pool;
ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_destroy" );
- has_pool = (tpool != NULL && *tpool != NULL);
+ has_pool = (tpool && *tpool);
rc = ldap_int_thread_pool_destroy( tpool, run_pending );
if( has_pool ) {
if( rc ) {