diff options
author | sr | 2013-07-16 21:20:07 +0200 |
---|---|---|
committer | sr | 2013-07-16 21:20:07 +0200 |
commit | c31b8d90a4e4809ada5b60fcdcc93b6ecf9326e4 (patch) | |
tree | cc5d5901ddb7bc7ba9c2c2beeaee3154c8a3b0f7 | |
parent | Fix more bugs, remove debug messages (diff) | |
download | dnbd3-c31b8d90a4e4809ada5b60fcdcc93b6ecf9326e4.tar.gz dnbd3-c31b8d90a4e4809ada5b60fcdcc93b6ecf9326e4.tar.xz dnbd3-c31b8d90a4e4809ada5b60fcdcc93b6ecf9326e4.zip |
Add debug-lock functions that will helpt to spot deadlocks etc. while developing
-rw-r--r-- | src/server/globals.c | 1 | ||||
-rw-r--r-- | src/server/globals.h | 2 | ||||
-rw-r--r-- | src/server/locks.c | 220 | ||||
-rw-r--r-- | src/server/locks.h | 30 | ||||
-rw-r--r-- | src/server/net.c | 8 |
5 files changed, 258 insertions, 3 deletions
diff --git a/src/server/globals.c b/src/server/globals.c index 9919ee3..4a62dd6 100644 --- a/src/server/globals.c +++ b/src/server/globals.c @@ -4,3 +4,4 @@ char *_basePath = NULL; int _vmdkLegacyMode = FALSE; +int _shutdown = 0; diff --git a/src/server/globals.h b/src/server/globals.h index e8203ea..0047a06 100644 --- a/src/server/globals.h +++ b/src/server/globals.h @@ -83,4 +83,6 @@ extern char *_basePath; */ extern int _vmdkLegacyMode; +extern int _shutdown; + #endif /* GLOBALS_H_ */ diff --git a/src/server/locks.c b/src/server/locks.c new file mode 100644 index 0000000..5a68747 --- /dev/null +++ b/src/server/locks.c @@ -0,0 +1,220 @@ +/* + * locks.c + * + * Created on: 16.07.2013 + * Author: sr + */ + +#include "locks.h" + +#ifdef _DEBUG + +#include <time.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include "globals.h" + +#define MAXLOCKS 500 +#define MAXTHREADS 500 +#define LOCKLEN 60 +typedef struct +{ + void *lock; + volatile time_t locktime; + volatile char locked; + pthread_t thread; + int lockId; + char name[LOCKLEN]; + char where[LOCKLEN]; +} debug_lock_t; + +typedef struct +{ + pthread_t tid; + time_t time; + char name[LOCKLEN]; + char where[LOCKLEN]; + +} debug_thread_t; + +static debug_lock_t locks[MAXLOCKS]; +static debug_thread_t threads[MAXTHREADS]; +static int init_done = 0; +static pthread_spinlock_t initdestory; +static volatile int lockId = 0; + +int debug_spin_init(const char *name, const char *file, int line, pthread_spinlock_t *lock, int shared) +{ + if ( !init_done ) { + memset( locks, 0, MAXLOCKS * sizeof(debug_lock_t) ); + memset( threads, 0, MAXTHREADS * sizeof(debug_thread_t) ); + pthread_spin_init( &initdestory, PTHREAD_PROCESS_PRIVATE ); + init_done = 1; + } + int first = -1; + pthread_spin_lock( &initdestory ); + for (int i = 0; i < MAXLOCKS; ++i) { + if ( locks[i].lock == lock ) { + printf( "[ERROR] Lock %p (%s) already initialized (%s:%d)\n", lock, name, file, line ); + exit( 4 ); + } + if ( first == -1 ) first = i; + } + if ( first == -1 ) { + printf( "[ERROR] No more free debug locks (%s:%d)\n", file, line ); + exit( 4 ); + } + locks[first].lock = (void*)lock; + locks[first].locked = 0; + snprintf( locks[first].name, LOCKLEN, "%s", name ); + snprintf( locks[first].where, LOCKLEN, "I %s:%d", file, line ); + pthread_spin_unlock( &initdestory ); + return pthread_spin_init( lock, shared ); +} + +int debug_spin_lock(const char *name, const char *file, int line, pthread_spinlock_t *lock) +{ + debug_lock_t *l = NULL; + pthread_spin_lock( &initdestory ); + for (int i = 0; i < MAXLOCKS; ++i) { + if ( locks[i].lock == lock ) { + l = &locks[i]; + break; + } + } + pthread_spin_unlock( &initdestory ); + if ( l == NULL ) { + printf( "[ERROR] Tried to lock uninitialized lock %p (%s) at %s:%d\n", lock, name, file, line ); + exit( 4 ); + } + debug_thread_t *t = NULL; + pthread_spin_lock( &initdestory ); + for (int i = 0; i < MAXTHREADS; ++i) { + if ( threads[i].tid != 0 ) continue; + threads[i].tid = pthread_self(); + threads[i].time = time( NULL ); + snprintf( threads[i].name, LOCKLEN, "%s", name ); + snprintf( threads[i].where, LOCKLEN, "%s:%d", file, line ); + t = &threads[i]; + } + pthread_spin_unlock( &initdestory ); + int retval = pthread_spin_lock( lock ); + pthread_spin_lock( &initdestory ); + t->tid = 0; + pthread_spin_unlock( &initdestory ); + if ( l->locked ) { + printf( "[ERROR] Lock sanity check: lock %p (%s) already locked at %s:%d\n", lock, name, file, line ); + exit( 4 ); + } + l->locked = 1; + l->locktime = time( NULL ); + l->thread = pthread_self(); + snprintf( l->where, LOCKLEN, "L %s:%d", file, line ); + pthread_spin_lock( &initdestory ); + l->lockId = ++lockId; + pthread_spin_unlock( &initdestory ); + return retval; +} + +int debug_spin_unlock(const char *name, const char *file, int line, pthread_spinlock_t *lock) +{ + debug_lock_t *l = NULL; + pthread_spin_lock( &initdestory ); + for (int i = 0; i < MAXLOCKS; ++i) { + if ( locks[i].lock == lock ) { + l = &locks[i]; + break; + } + } + pthread_spin_unlock( &initdestory ); + if ( l == NULL ) { + printf( "[ERROR] Tried to unlock uninitialized lock %p (%s) at %s:%d\n", lock, name, file, line ); + exit( 4 ); + } + int retval = pthread_spin_unlock( lock ); + if ( !l->locked ) { + printf( "[ERROR] Unlock sanity check: lock %p (%s) not locked at %s:%d\n", lock, name, file, line ); + exit( 4 ); + } + l->locked = 0; + l->locktime = 0; + l->thread = 0; + snprintf( l->where, LOCKLEN, "U %s:%d", file, line ); + return retval; +} + +int debug_spin_destory(const char *name, const char *file, int line, pthread_spinlock_t *lock) +{ + pthread_spin_lock( &initdestory ); + for (int i = 0; i < MAXLOCKS; ++i) { + if ( locks[i].lock == lock ) { + if ( locks[i].locked ) { + printf( "[ERROR] Tried to destroy lock %p (%s) at %s:%d when it is still locked\n", lock, name, file, line ); + exit( 4 ); + } + locks[i].lock = NULL; + snprintf( locks[i].where, LOCKLEN, "D %s:%d", file, line ); + pthread_spin_unlock( &initdestory ); + return pthread_spin_destroy( lock ); + } + } + printf( "[ERROR] Tried to destroy non-existent lock %p (%s) at %s:%d\n", lock, name, file, line ); + exit( 4 ); +} + +void debug_dump_lock_stats() +{ + time_t now = time( NULL ); + pthread_spin_lock( &initdestory ); + printf( "\n **** LOCKS ****\n\n" ); + for (int i = 0; i < MAXLOCKS; ++i) { + if ( locks[i].lock == NULL ) continue; + if ( locks[i].locked ) { + printf( "* *** %s ***\n" + "* Where: %s\n" + "* When: %d secs ago\n" + "* Locked: %d\n" + "* Serial: %d\n" + "* Thread: %d\n", locks[i].name, locks[i].where, (int)(now - locks[i].locktime), (int)locks[i].locked, locks[i].lockId, + (int)locks[i].thread ); + } else { + printf( "* *** %s ***\n" + "* Where: %s\n" + "* Locked: %d\n", locks[i].name, locks[i].where, (int)locks[i].locked ); + } + } + printf( "\n **** THREADS ****\n\n" ); + for (int i = 0; i < MAXTHREADS; ++i) { + if ( threads[i].tid == 0 ) continue; + printf( "* *** Thread %d ***\n" + "* Lock: %s\n" + "* Where: %s\n" + "* How long: %d secs\n", (int)threads[i].tid, threads[i].name, threads[i].where, (int)(now - threads[i].time) ); + } + pthread_spin_unlock( &initdestory ); +} + +void *debug_thread_watchdog(void *something) +{ + while (!_shutdown) { + time_t now = time(NULL); + pthread_spin_lock( &initdestory ); + for (int i = 0; i < MAXTHREADS; ++i) { + if ( threads[i].tid == 0 ) continue; + const int diff = now - threads[i].time; + if ( diff > 6 && diff < 100000 ) { + printf("\n\n +++++++++ DEADLOCK ++++++++++++\n\n"); + pthread_spin_unlock( &initdestory ); + debug_dump_lock_stats(); + exit(99); + } + } + pthread_spin_unlock( &initdestory ); + sleep(10); + } + return NULL; +} + +#endif diff --git a/src/server/locks.h b/src/server/locks.h new file mode 100644 index 0000000..9a0a024 --- /dev/null +++ b/src/server/locks.h @@ -0,0 +1,30 @@ +#ifndef _LOCKS_H_ +#define _LOCKS_H_ + +#ifdef _DEBUG + +#include <pthread.h> + +#define spin_init( lock, type ) debug_spin_init( #lock, __FILE__, __LINE__, lock, type) +#define spin_lock( lock ) debug_spin_lock( #lock, __FILE__, __LINE__, lock) +#define spin_unlock( lock ) debug_spin_unlock( #lock, __FILE__, __LINE__, lock) +#define spin_destroy( lock ) debug_spin_destroy( #lock, __FILE__, __LINE__, lock) + +int debug_spin_init(const char *name, const char *file, int line, pthread_spinlock_t *lock, int shared); +int debug_spin_lock(const char *name, const char *file, int line, pthread_spinlock_t *lock); +int debug_spin_unlock(const char *name, const char *file, int line, pthread_spinlock_t *lock); +int debug_spin_destory(const char *name, const char *file, int line, pthread_spinlock_t *lock); + +void debug_dump_lock_stats(); +void *debug_thread_watchdog(void *something); + +#else + +#define spin_init( lock, type ) pthread_spin_init(lock, type) +#define spin_lock( lock ) pthread_spin_lock(lock) +#define spin_unlock( lock ) pthread_spin_unlock(lock) +#define spin_destroy( lock ) pthread_spin_destroy(lock) + +#endif + +#endif /* LOCKS_H_ */ diff --git a/src/server/net.c b/src/server/net.c index 51d8415..214f1cd 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -224,8 +224,10 @@ void *net_client_handler(void *dnbd3_client) goto exit_client_cleanup; } - if ( request.size == 0 ) // Request for 0 bytes, done after sending header - break; + if ( request.size == 0 ) { // Request for 0 bytes, done after sending header + send( client->sock, &reply, 0, 0 ); // Since we used MSG_MORE above... + break; + } // no cache map means image is complete if ( image->cache_map == NULL ) { @@ -357,7 +359,7 @@ void *net_client_handler(void *dnbd3_client) } } - exit_client_cleanup: if ( client->sock != -1 ) close( client->sock ); + exit_client_cleanup: ; if ( image_file != -1 ) close( image_file ); dnbd3_remove_client( client ); client = dnbd3_free_client( client ); |