xref: /netbsd-src/external/bsd/unbound/dist/testcode/checklocks.c (revision f42d8de7d1744f0ae38eedac13b4320e5351d1d6)
13b6c3722Schristos /**
23b6c3722Schristos  * testcode/checklocks.c - wrapper on locks that checks access.
33b6c3722Schristos  *
43b6c3722Schristos  * Copyright (c) 2007, NLnet Labs. All rights reserved.
53b6c3722Schristos  *
63b6c3722Schristos  * This software is open source.
73b6c3722Schristos  *
83b6c3722Schristos  * Redistribution and use in source and binary forms, with or without
93b6c3722Schristos  * modification, are permitted provided that the following conditions
103b6c3722Schristos  * are met:
113b6c3722Schristos  *
123b6c3722Schristos  * Redistributions of source code must retain the above copyright notice,
133b6c3722Schristos  * this list of conditions and the following disclaimer.
143b6c3722Schristos  *
153b6c3722Schristos  * Redistributions in binary form must reproduce the above copyright notice,
163b6c3722Schristos  * this list of conditions and the following disclaimer in the documentation
173b6c3722Schristos  * and/or other materials provided with the distribution.
183b6c3722Schristos  *
193b6c3722Schristos  * Neither the name of the NLNET LABS nor the names of its contributors may
203b6c3722Schristos  * be used to endorse or promote products derived from this software without
213b6c3722Schristos  * specific prior written permission.
223b6c3722Schristos  *
233b6c3722Schristos  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
243b6c3722Schristos  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
253b6c3722Schristos  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
263b6c3722Schristos  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
273b6c3722Schristos  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
283b6c3722Schristos  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
293b6c3722Schristos  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
303b6c3722Schristos  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
313b6c3722Schristos  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
323b6c3722Schristos  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
333b6c3722Schristos  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
343b6c3722Schristos  */
353b6c3722Schristos 
363b6c3722Schristos #include "config.h"
373b6c3722Schristos #include <signal.h>
383b6c3722Schristos #include "util/locks.h"   /* include before checklocks.h */
393b6c3722Schristos #include "testcode/checklocks.h"
403b6c3722Schristos 
413b6c3722Schristos /**
423b6c3722Schristos  * \file
433b6c3722Schristos  * Locks that are checked.
443b6c3722Schristos  *
453b6c3722Schristos  * Ugly hack: uses the fact that workers start with an int thread_num, and
463b6c3722Schristos  * are passed to thread_create to make the thread numbers here the same as
473b6c3722Schristos  * those used for logging which is nice.
483b6c3722Schristos  *
493b6c3722Schristos  * Todo:
503b6c3722Schristos  *	 - debug status print, of thread lock stacks, and current waiting.
513b6c3722Schristos  */
523b6c3722Schristos #ifdef USE_THREAD_DEBUG
533b6c3722Schristos 
543b6c3722Schristos /** How long to wait before lock attempt is a failure. */
553b6c3722Schristos #define CHECK_LOCK_TIMEOUT 120 /* seconds */
563b6c3722Schristos /** How long to wait before join attempt is a failure. */
573b6c3722Schristos #define CHECK_JOIN_TIMEOUT 120 /* seconds */
583b6c3722Schristos 
593b6c3722Schristos /** if key has been created */
603b6c3722Schristos static int key_created = 0;
613b6c3722Schristos /** if the key was deleted, i.e. we have quit */
623b6c3722Schristos static int key_deleted = 0;
633b6c3722Schristos /** we hide the thread debug info with this key. */
640cd9f4ecSchristos static ub_thread_key_type thr_debug_key;
653b6c3722Schristos /** the list of threads, so all threads can be examined. NULL if unused. */
663b6c3722Schristos static struct thr_check* thread_infos[THRDEBUG_MAX_THREADS];
673b6c3722Schristos /** do we check locking order */
683b6c3722Schristos int check_locking_order = 1;
693b6c3722Schristos /** the pid of this runset, reasonably unique. */
703b6c3722Schristos static pid_t check_lock_pid;
713b6c3722Schristos 
723b6c3722Schristos /** print all possible debug info on the state of the system */
733b6c3722Schristos static void total_debug_info(void);
74*f42d8de7Schristos /** print pretty lock error and exit (decl for NORETURN attribute) */
75*f42d8de7Schristos static void lock_error(struct checked_lock* lock, const char* func,
76*f42d8de7Schristos 	const char* file, int line, const char* err) ATTR_NORETURN;
773b6c3722Schristos 
783b6c3722Schristos /** print pretty lock error and exit */
lock_error(struct checked_lock * lock,const char * func,const char * file,int line,const char * err)793b6c3722Schristos static void lock_error(struct checked_lock* lock,
803b6c3722Schristos 	const char* func, const char* file, int line, const char* err)
813b6c3722Schristos {
823b6c3722Schristos 	log_err("lock error (description follows)");
833b6c3722Schristos 	log_err("Created at %s %s:%d", lock->create_func,
843b6c3722Schristos 		lock->create_file, lock->create_line);
853b6c3722Schristos 	if(lock->holder_func && lock->holder_file)
863b6c3722Schristos 		log_err("Previously %s %s:%d", lock->holder_func,
873b6c3722Schristos 			lock->holder_file, lock->holder_line);
883b6c3722Schristos 	log_err("At %s %s:%d", func, file, line);
893b6c3722Schristos 	log_err("Error for %s lock: %s",
903b6c3722Schristos 		(lock->type==check_lock_mutex)?"mutex": (
913b6c3722Schristos 		(lock->type==check_lock_spinlock)?"spinlock": (
923b6c3722Schristos 		(lock->type==check_lock_rwlock)?"rwlock": "badtype")), err);
933b6c3722Schristos 	log_err("complete status display:");
943b6c3722Schristos 	total_debug_info();
953b6c3722Schristos 	fatal_exit("bailing out");
963b6c3722Schristos }
973b6c3722Schristos 
983b6c3722Schristos /**
993b6c3722Schristos  * Obtain lock on debug lock structure. This could be a deadlock by the caller.
1003b6c3722Schristos  * The debug code itself does not deadlock. Anyway, check with timeouts.
1013b6c3722Schristos  * @param lock: on what to acquire lock.
1023b6c3722Schristos  * @param func: user level caller identification.
1033b6c3722Schristos  * @param file: user level caller identification.
1043b6c3722Schristos  * @param line: user level caller identification.
1053b6c3722Schristos  */
1063b6c3722Schristos static void
acquire_locklock(struct checked_lock * lock,const char * func,const char * file,int line)1073b6c3722Schristos acquire_locklock(struct checked_lock* lock,
1083b6c3722Schristos 	const char* func, const char* file, int line)
1093b6c3722Schristos {
1103b6c3722Schristos 	struct timespec to;
1113b6c3722Schristos 	int err;
1123b6c3722Schristos 	int contend = 0;
1133b6c3722Schristos 	/* first try; inc contention counter if not immediately */
1143b6c3722Schristos 	if((err = pthread_mutex_trylock(&lock->lock))) {
1153b6c3722Schristos 		if(err==EBUSY)
1163b6c3722Schristos 			contend++;
1173b6c3722Schristos 		else fatal_exit("error in mutex_trylock: %s", strerror(err));
1183b6c3722Schristos 	}
1193b6c3722Schristos 	if(!err)
1203b6c3722Schristos 		return; /* immediate success */
1213b6c3722Schristos 	to.tv_sec = time(NULL) + CHECK_LOCK_TIMEOUT;
1223b6c3722Schristos 	to.tv_nsec = 0;
1233b6c3722Schristos 	err = pthread_mutex_timedlock(&lock->lock, &to);
1243b6c3722Schristos 	if(err) {
1253b6c3722Schristos 		log_err("in acquiring locklock: %s", strerror(err));
1263b6c3722Schristos 		lock_error(lock, func, file, line, "acquire locklock");
1273b6c3722Schristos 	}
1283b6c3722Schristos 	/* since we hold the lock, we can edit the contention_count */
1293b6c3722Schristos 	lock->contention_count += contend;
1303b6c3722Schristos }
1313b6c3722Schristos 
1323b6c3722Schristos /** add protected region */
1333b6c3722Schristos void
lock_protect(void * p,void * area,size_t size)1343b6c3722Schristos lock_protect(void *p, void* area, size_t size)
1353b6c3722Schristos {
1363b6c3722Schristos 	struct checked_lock* lock = *(struct checked_lock**)p;
1373b6c3722Schristos 	struct protected_area* e = (struct protected_area*)malloc(
1383b6c3722Schristos 		sizeof(struct protected_area));
1393b6c3722Schristos 	if(!e)
1403b6c3722Schristos 		fatal_exit("lock_protect: out of memory");
1413b6c3722Schristos 	e->region = area;
1423b6c3722Schristos 	e->size = size;
1433b6c3722Schristos 	e->hold = malloc(size);
1443b6c3722Schristos 	if(!e->hold)
1453b6c3722Schristos 		fatal_exit("lock_protect: out of memory");
1463b6c3722Schristos 	memcpy(e->hold, e->region, e->size);
1473b6c3722Schristos 
1483b6c3722Schristos 	acquire_locklock(lock, __func__, __FILE__, __LINE__);
1493b6c3722Schristos 	e->next = lock->prot;
1503b6c3722Schristos 	lock->prot = e;
1513b6c3722Schristos 	LOCKRET(pthread_mutex_unlock(&lock->lock));
1523b6c3722Schristos }
1533b6c3722Schristos 
1543b6c3722Schristos /** remove protected region */
1553b6c3722Schristos void
lock_unprotect(void * mangled,void * area)1563b6c3722Schristos lock_unprotect(void* mangled, void* area)
1573b6c3722Schristos {
1583b6c3722Schristos 	struct checked_lock* lock = *(struct checked_lock**)mangled;
1593b6c3722Schristos 	struct protected_area* p, **prevp;
1603b6c3722Schristos 	if(!lock)
1613b6c3722Schristos 		return;
1623b6c3722Schristos 	acquire_locklock(lock, __func__, __FILE__, __LINE__);
1633b6c3722Schristos 	p = lock->prot;
1643b6c3722Schristos 	prevp = &lock->prot;
1653b6c3722Schristos 	while(p) {
1663b6c3722Schristos 		if(p->region == area) {
1673b6c3722Schristos 			*prevp = p->next;
1683b6c3722Schristos 			free(p->hold);
1693b6c3722Schristos 			free(p);
1703b6c3722Schristos 			LOCKRET(pthread_mutex_unlock(&lock->lock));
1713b6c3722Schristos 			return;
1723b6c3722Schristos 		}
1733b6c3722Schristos 		prevp = &p->next;
1743b6c3722Schristos 		p = p->next;
1753b6c3722Schristos 	}
1763b6c3722Schristos 	LOCKRET(pthread_mutex_unlock(&lock->lock));
1773b6c3722Schristos }
1783b6c3722Schristos 
1793b6c3722Schristos /**
1803b6c3722Schristos  * Check protected memory region. Memory compare. Exit on error.
1813b6c3722Schristos  * @param lock: which lock to check.
1823b6c3722Schristos  * @param func: location we are now (when failure is detected).
1833b6c3722Schristos  * @param file: location we are now (when failure is detected).
1843b6c3722Schristos  * @param line: location we are now (when failure is detected).
1853b6c3722Schristos  */
1863b6c3722Schristos static void
prot_check(struct checked_lock * lock,const char * func,const char * file,int line)1873b6c3722Schristos prot_check(struct checked_lock* lock,
1883b6c3722Schristos 	const char* func, const char* file, int line)
1893b6c3722Schristos {
1903b6c3722Schristos 	struct protected_area* p = lock->prot;
1913b6c3722Schristos 	while(p) {
1923b6c3722Schristos 		if(memcmp(p->hold, p->region, p->size) != 0) {
1933b6c3722Schristos 			log_hex("memory prev", p->hold, p->size);
1943b6c3722Schristos 			log_hex("memory here", p->region, p->size);
1953b6c3722Schristos 			lock_error(lock, func, file, line,
1963b6c3722Schristos 				"protected area modified");
1973b6c3722Schristos 		}
1983b6c3722Schristos 		p = p->next;
1993b6c3722Schristos 	}
2003b6c3722Schristos }
2013b6c3722Schristos 
2023b6c3722Schristos /** Copy protected memory region */
2033b6c3722Schristos static void
prot_store(struct checked_lock * lock)2043b6c3722Schristos prot_store(struct checked_lock* lock)
2053b6c3722Schristos {
2063b6c3722Schristos 	struct protected_area* p = lock->prot;
2073b6c3722Schristos 	while(p) {
2083b6c3722Schristos 		memcpy(p->hold, p->region, p->size);
2093b6c3722Schristos 		p = p->next;
2103b6c3722Schristos 	}
2113b6c3722Schristos }
2123b6c3722Schristos 
2133b6c3722Schristos /** get memory held by lock */
2143b6c3722Schristos size_t
lock_get_mem(void * pp)2153b6c3722Schristos lock_get_mem(void* pp)
2163b6c3722Schristos {
2173b6c3722Schristos 	size_t s;
2183b6c3722Schristos 	struct checked_lock* lock = *(struct checked_lock**)pp;
2193b6c3722Schristos 	struct protected_area* p;
2203b6c3722Schristos 	s = sizeof(struct checked_lock);
2213b6c3722Schristos 	acquire_locklock(lock, __func__, __FILE__, __LINE__);
2223b6c3722Schristos 	for(p = lock->prot; p; p = p->next) {
2233b6c3722Schristos 		s += sizeof(struct protected_area);
2243b6c3722Schristos 		s += p->size;
2253b6c3722Schristos 	}
2263b6c3722Schristos 	LOCKRET(pthread_mutex_unlock(&lock->lock));
2273b6c3722Schristos 	return s;
2283b6c3722Schristos }
2293b6c3722Schristos 
2303b6c3722Schristos /** write lock trace info to file, while you hold those locks */
2313b6c3722Schristos static void
ordercheck_locklock(struct thr_check * thr,struct checked_lock * lock)2323b6c3722Schristos ordercheck_locklock(struct thr_check* thr, struct checked_lock* lock)
2333b6c3722Schristos {
2343b6c3722Schristos 	int info[4];
2353b6c3722Schristos 	if(!check_locking_order) return;
2363b6c3722Schristos 	if(!thr->holding_first) return; /* no older lock, no info */
2373b6c3722Schristos 	/* write: <lock id held> <lock id new> <file> <line> */
2383b6c3722Schristos 	info[0] = thr->holding_first->create_thread;
2393b6c3722Schristos 	info[1] = thr->holding_first->create_instance;
2403b6c3722Schristos 	info[2] = lock->create_thread;
2413b6c3722Schristos 	info[3] = lock->create_instance;
2423b6c3722Schristos 	if(fwrite(info, 4*sizeof(int), 1, thr->order_info) != 1 ||
2433b6c3722Schristos 		fwrite(lock->holder_file, strlen(lock->holder_file)+1, 1,
2443b6c3722Schristos 		thr->order_info) != 1 ||
2453b6c3722Schristos 		fwrite(&lock->holder_line, sizeof(int), 1,
2463b6c3722Schristos 		thr->order_info) != 1)
2473b6c3722Schristos 		log_err("fwrite: %s", strerror(errno));
2483b6c3722Schristos }
2493b6c3722Schristos 
2503b6c3722Schristos /** write ordercheck lock creation details to file */
2513b6c3722Schristos static void
ordercheck_lockcreate(struct thr_check * thr,struct checked_lock * lock)2523b6c3722Schristos ordercheck_lockcreate(struct thr_check* thr, struct checked_lock* lock)
2533b6c3722Schristos {
2543b6c3722Schristos 	/* write: <ffff = create> <lock id> <file> <line> */
2553b6c3722Schristos 	int cmd = -1;
2563b6c3722Schristos 	if(!check_locking_order) return;
2573b6c3722Schristos 
2583b6c3722Schristos 	if( fwrite(&cmd, sizeof(int), 1, thr->order_info) != 1 ||
2593b6c3722Schristos 		fwrite(&lock->create_thread, sizeof(int), 1,
2603b6c3722Schristos 			thr->order_info) != 1 ||
2613b6c3722Schristos 		fwrite(&lock->create_instance, sizeof(int), 1,
2623b6c3722Schristos 			thr->order_info) != 1 ||
2633b6c3722Schristos 		fwrite(lock->create_file, strlen(lock->create_file)+1, 1,
2643b6c3722Schristos 			thr->order_info) != 1 ||
2653b6c3722Schristos 		fwrite(&lock->create_line, sizeof(int), 1,
2663b6c3722Schristos 		thr->order_info) != 1)
2673b6c3722Schristos 		log_err("fwrite: %s", strerror(errno));
2683b6c3722Schristos }
2693b6c3722Schristos 
2703b6c3722Schristos /** alloc struct, init lock empty */
2713b6c3722Schristos void
checklock_init(enum check_lock_type type,struct checked_lock ** lock,const char * func,const char * file,int line)2723b6c3722Schristos checklock_init(enum check_lock_type type, struct checked_lock** lock,
2733b6c3722Schristos         const char* func, const char* file, int line)
2743b6c3722Schristos {
2753b6c3722Schristos 	struct checked_lock* e = (struct checked_lock*)calloc(1,
2763b6c3722Schristos 		sizeof(struct checked_lock));
2773b6c3722Schristos 	struct thr_check *thr = (struct thr_check*)pthread_getspecific(
2783b6c3722Schristos 		thr_debug_key);
2793b6c3722Schristos 	if(!e)
2803b6c3722Schristos 		fatal_exit("%s %s %d: out of memory", func, file, line);
2813b6c3722Schristos 	if(!thr) {
2823b6c3722Schristos 		/* this is called when log_init() calls lock_init()
2833b6c3722Schristos 		 * functions, and the test check code has not yet
2843b6c3722Schristos 		 * been initialised.  But luckily, the checklock_start()
2853b6c3722Schristos 		 * routine can be called multiple times without ill effect.
2863b6c3722Schristos 		 */
2873b6c3722Schristos 		checklock_start();
2883b6c3722Schristos 		thr = (struct thr_check*)pthread_getspecific(thr_debug_key);
2893b6c3722Schristos 	}
2903b6c3722Schristos 	if(!thr)
2913b6c3722Schristos 		fatal_exit("%s %s %d: lock_init no thread info", func, file,
2923b6c3722Schristos 			line);
2933b6c3722Schristos 	*lock = e;
2943b6c3722Schristos 	e->type = type;
2953b6c3722Schristos 	e->create_func = func;
2963b6c3722Schristos 	e->create_file = file;
2973b6c3722Schristos 	e->create_line = line;
2983b6c3722Schristos 	e->create_thread = thr->num;
2993b6c3722Schristos 	e->create_instance = thr->locks_created++;
3003b6c3722Schristos 	ordercheck_lockcreate(thr, e);
3013b6c3722Schristos 	LOCKRET(pthread_mutex_init(&e->lock, NULL));
3023b6c3722Schristos 	switch(e->type) {
3033b6c3722Schristos 		case check_lock_mutex:
3043b6c3722Schristos 			LOCKRET(pthread_mutex_init(&e->u.mutex, NULL));
3053b6c3722Schristos 			break;
3063b6c3722Schristos 		case check_lock_spinlock:
3073b6c3722Schristos 			LOCKRET(pthread_spin_init(&e->u.spinlock, PTHREAD_PROCESS_PRIVATE));
3083b6c3722Schristos 			break;
3093b6c3722Schristos 		case check_lock_rwlock:
3103b6c3722Schristos 			LOCKRET(pthread_rwlock_init(&e->u.rwlock, NULL));
3113b6c3722Schristos 			break;
3123b6c3722Schristos 		default:
3133b6c3722Schristos 			log_assert(0);
3143b6c3722Schristos 	}
3153b6c3722Schristos }
3163b6c3722Schristos 
3173b6c3722Schristos /** delete prot items */
3183b6c3722Schristos static void
prot_clear(struct checked_lock * lock)3193b6c3722Schristos prot_clear(struct checked_lock* lock)
3203b6c3722Schristos {
3213b6c3722Schristos 	struct protected_area* p=lock->prot, *np;
3223b6c3722Schristos 	while(p) {
3233b6c3722Schristos 		np = p->next;
3243b6c3722Schristos 		free(p->hold);
3253b6c3722Schristos 		free(p);
3263b6c3722Schristos 		p = np;
3273b6c3722Schristos 	}
3283b6c3722Schristos }
3293b6c3722Schristos 
3303b6c3722Schristos /** check if type is OK for the lock given */
3313b6c3722Schristos static void
checktype(enum check_lock_type type,struct checked_lock * lock,const char * func,const char * file,int line)3323b6c3722Schristos checktype(enum check_lock_type type, struct checked_lock* lock,
3333b6c3722Schristos         const char* func, const char* file, int line)
3343b6c3722Schristos {
3353b6c3722Schristos 	if(!lock)
3363b6c3722Schristos 		fatal_exit("use of null/deleted lock at %s %s:%d",
3373b6c3722Schristos 			func, file, line);
3383b6c3722Schristos 	if(type != lock->type) {
3393b6c3722Schristos 		lock_error(lock, func, file, line, "wrong lock type");
3403b6c3722Schristos 	}
3413b6c3722Schristos }
3423b6c3722Schristos 
3433b6c3722Schristos /** check if OK, free struct */
3443b6c3722Schristos void
checklock_destroy(enum check_lock_type type,struct checked_lock ** lock,const char * func,const char * file,int line)3453b6c3722Schristos checklock_destroy(enum check_lock_type type, struct checked_lock** lock,
3463b6c3722Schristos         const char* func, const char* file, int line)
3473b6c3722Schristos {
3483b6c3722Schristos 	const size_t contention_interest = 1; /* promille contented locks */
3493b6c3722Schristos 	struct checked_lock* e;
3503b6c3722Schristos 	if(!lock)
3513b6c3722Schristos 		return;
3523b6c3722Schristos 	e = *lock;
3533b6c3722Schristos 	if(!e)
3543b6c3722Schristos 		return;
3553b6c3722Schristos 	checktype(type, e, func, file, line);
3563b6c3722Schristos 
3573b6c3722Schristos 	/* check if delete is OK */
3583b6c3722Schristos 	acquire_locklock(e, func, file, line);
3593b6c3722Schristos 	if(e->hold_count != 0)
3603b6c3722Schristos 		lock_error(e, func, file, line, "delete while locked.");
3613b6c3722Schristos 	if(e->wait_count != 0)
3623b6c3722Schristos 		lock_error(e, func, file, line, "delete while waited on.");
3633b6c3722Schristos 	prot_check(e, func, file, line);
3643b6c3722Schristos 	*lock = NULL; /* use after free will fail */
3653b6c3722Schristos 	LOCKRET(pthread_mutex_unlock(&e->lock));
3663b6c3722Schristos 
3673b6c3722Schristos 	/* contention, look at fraction in trouble. */
3683b6c3722Schristos 	if(e->history_count > 1 &&
3693b6c3722Schristos 	   1000*e->contention_count/e->history_count > contention_interest) {
3703b6c3722Schristos 		log_info("lock created %s %s %d has contention %u of %u (%d%%)",
3713b6c3722Schristos 			e->create_func, e->create_file, e->create_line,
3723b6c3722Schristos 			(unsigned int)e->contention_count,
3733b6c3722Schristos 			(unsigned int)e->history_count,
3743b6c3722Schristos 			(int)(100*e->contention_count/e->history_count));
3753b6c3722Schristos 	}
3763b6c3722Schristos 
3773b6c3722Schristos 	/* delete it */
3783b6c3722Schristos 	LOCKRET(pthread_mutex_destroy(&e->lock));
3793b6c3722Schristos 	prot_clear(e);
3803b6c3722Schristos 	/* since nobody holds the lock - see check above, no need to unlink
3813b6c3722Schristos 	 * from the thread-held locks list. */
3823b6c3722Schristos 	switch(e->type) {
3833b6c3722Schristos 		case check_lock_mutex:
3843b6c3722Schristos 			LOCKRET(pthread_mutex_destroy(&e->u.mutex));
3853b6c3722Schristos 			break;
3863b6c3722Schristos 		case check_lock_spinlock:
3873b6c3722Schristos 			LOCKRET(pthread_spin_destroy(&e->u.spinlock));
3883b6c3722Schristos 			break;
3893b6c3722Schristos 		case check_lock_rwlock:
3903b6c3722Schristos 			LOCKRET(pthread_rwlock_destroy(&e->u.rwlock));
3913b6c3722Schristos 			break;
3923b6c3722Schristos 		default:
3933b6c3722Schristos 			log_assert(0);
3943b6c3722Schristos 	}
3953b6c3722Schristos 	memset(e, 0, sizeof(struct checked_lock));
3963b6c3722Schristos 	free(e);
3973b6c3722Schristos }
3983b6c3722Schristos 
3993b6c3722Schristos /** finish acquiring lock, shared between _(rd|wr||)lock() routines */
4003b6c3722Schristos static void
finish_acquire_lock(struct thr_check * thr,struct checked_lock * lock,const char * func,const char * file,int line)4013b6c3722Schristos finish_acquire_lock(struct thr_check* thr, struct checked_lock* lock,
4023b6c3722Schristos         const char* func, const char* file, int line)
4033b6c3722Schristos {
4043b6c3722Schristos 	thr->waiting = NULL;
4053b6c3722Schristos 	lock->wait_count --;
4063b6c3722Schristos 	lock->holder = thr;
4073b6c3722Schristos 	lock->hold_count ++;
4083b6c3722Schristos 	lock->holder_func = func;
4093b6c3722Schristos 	lock->holder_file = file;
4103b6c3722Schristos 	lock->holder_line = line;
4113b6c3722Schristos 	ordercheck_locklock(thr, lock);
4123b6c3722Schristos 
4133b6c3722Schristos 	/* insert in thread lock list, as first */
4143b6c3722Schristos 	lock->prev_held_lock[thr->num] = NULL;
4153b6c3722Schristos 	lock->next_held_lock[thr->num] = thr->holding_first;
4163b6c3722Schristos 	if(thr->holding_first)
4173b6c3722Schristos 		/* no need to lock it, since this thread already holds the
4183b6c3722Schristos 		 * lock (since it is on this list) and we only edit thr->num
4193b6c3722Schristos 		 * member in array. So it is safe.  */
4203b6c3722Schristos 		thr->holding_first->prev_held_lock[thr->num] = lock;
4213b6c3722Schristos 	else	thr->holding_last = lock;
4223b6c3722Schristos 	thr->holding_first = lock;
4233b6c3722Schristos }
4243b6c3722Schristos 
4253b6c3722Schristos /**
4263b6c3722Schristos  * Locking routine.
4273b6c3722Schristos  * @param type: as passed by user.
4283b6c3722Schristos  * @param lock: as passed by user.
4293b6c3722Schristos  * @param func: caller location.
4303b6c3722Schristos  * @param file: caller location.
4313b6c3722Schristos  * @param line: caller location.
4323b6c3722Schristos  * @param tryfunc: the pthread_mutex_trylock or similar function.
4333b6c3722Schristos  * @param timedfunc: the pthread_mutex_timedlock or similar function.
4343b6c3722Schristos  *	Uses absolute timeout value.
4353b6c3722Schristos  * @param arg: what to pass to tryfunc and timedlock.
4363b6c3722Schristos  * @param exclusive: if lock must be exclusive (only one allowed).
4373b6c3722Schristos  * @param getwr: if attempts to get writelock (or readlock) for rwlocks.
4383b6c3722Schristos  */
4393b6c3722Schristos static void
checklock_lockit(enum check_lock_type type,struct checked_lock * lock,const char * func,const char * file,int line,int (* tryfunc)(void *),int (* timedfunc)(void *,struct timespec *),void * arg,int exclusive,int getwr)4403b6c3722Schristos checklock_lockit(enum check_lock_type type, struct checked_lock* lock,
4413b6c3722Schristos         const char* func, const char* file, int line,
4423b6c3722Schristos 	int (*tryfunc)(void*), int (*timedfunc)(void*, struct timespec*),
4433b6c3722Schristos 	void* arg, int exclusive, int getwr)
4443b6c3722Schristos {
4453b6c3722Schristos 	int err;
4463b6c3722Schristos 	int contend = 0;
4473b6c3722Schristos 	struct thr_check *thr = (struct thr_check*)pthread_getspecific(
4483b6c3722Schristos 		thr_debug_key);
4493b6c3722Schristos 	checktype(type, lock, func, file, line);
4503b6c3722Schristos 	if(!thr) lock_error(lock, func, file, line, "no thread info");
4513b6c3722Schristos 
4523b6c3722Schristos 	acquire_locklock(lock, func, file, line);
4533b6c3722Schristos 	lock->wait_count ++;
4543b6c3722Schristos 	thr->waiting = lock;
4553b6c3722Schristos 	if(exclusive && lock->hold_count > 0 && lock->holder == thr)
4563b6c3722Schristos 		lock_error(lock, func, file, line, "thread already owns lock");
4573b6c3722Schristos 	if(type==check_lock_rwlock && getwr && lock->writeholder == thr)
4583b6c3722Schristos 		lock_error(lock, func, file, line, "thread already has wrlock");
4593b6c3722Schristos 	LOCKRET(pthread_mutex_unlock(&lock->lock));
4603b6c3722Schristos 
4613b6c3722Schristos 	/* first try; if busy increase contention counter */
4623b6c3722Schristos 	if((err=tryfunc(arg))) {
4633b6c3722Schristos 		struct timespec to;
4643b6c3722Schristos 		if(err != EBUSY) log_err("trylock: %s", strerror(err));
4653b6c3722Schristos 		to.tv_sec = time(NULL) + CHECK_LOCK_TIMEOUT;
4663b6c3722Schristos 		to.tv_nsec = 0;
4673b6c3722Schristos 		if((err=timedfunc(arg, &to))) {
4683b6c3722Schristos 			if(err == ETIMEDOUT)
4693b6c3722Schristos 				lock_error(lock, func, file, line,
4703b6c3722Schristos 					"timeout possible deadlock");
4713b6c3722Schristos 			log_err("timedlock: %s", strerror(err));
4723b6c3722Schristos 		}
4733b6c3722Schristos 		contend ++;
4743b6c3722Schristos 	}
4753b6c3722Schristos 	/* got the lock */
4763b6c3722Schristos 
4773b6c3722Schristos 	acquire_locklock(lock, func, file, line);
4783b6c3722Schristos 	lock->contention_count += contend;
4793b6c3722Schristos 	lock->history_count++;
4803b6c3722Schristos 	if(exclusive && lock->hold_count > 0)
4813b6c3722Schristos 		lock_error(lock, func, file, line, "got nonexclusive lock");
4823b6c3722Schristos 	if(type==check_lock_rwlock && getwr && lock->writeholder)
4833b6c3722Schristos 		lock_error(lock, func, file, line, "got nonexclusive wrlock");
4843b6c3722Schristos 	if(type==check_lock_rwlock && getwr)
4853b6c3722Schristos 		lock->writeholder = thr;
4863b6c3722Schristos 	/* check the memory areas for unauthorized changes,
4873b6c3722Schristos 	 * between last unlock time and current lock time.
4883b6c3722Schristos 	 * we check while holding the lock (threadsafe).
4893b6c3722Schristos 	 */
4903b6c3722Schristos 	if(getwr || exclusive)
4913b6c3722Schristos 		prot_check(lock, func, file, line);
4923b6c3722Schristos 	finish_acquire_lock(thr, lock, func, file, line);
4933b6c3722Schristos 	LOCKRET(pthread_mutex_unlock(&lock->lock));
4943b6c3722Schristos }
4953b6c3722Schristos 
4963b6c3722Schristos /** helper for rdlock: try */
try_rd(void * arg)4973b6c3722Schristos static int try_rd(void* arg)
4983b6c3722Schristos { return pthread_rwlock_tryrdlock((pthread_rwlock_t*)arg); }
4993b6c3722Schristos /** helper for rdlock: timed */
timed_rd(void * arg,struct timespec * to)5003b6c3722Schristos static int timed_rd(void* arg, struct timespec* to)
5013b6c3722Schristos { return pthread_rwlock_timedrdlock((pthread_rwlock_t*)arg, to); }
5023b6c3722Schristos 
5033b6c3722Schristos /** check if OK, lock */
5043b6c3722Schristos void
checklock_rdlock(enum check_lock_type type,struct checked_lock * lock,const char * func,const char * file,int line)5053b6c3722Schristos checklock_rdlock(enum check_lock_type type, struct checked_lock* lock,
5063b6c3722Schristos         const char* func, const char* file, int line)
5073b6c3722Schristos {
5083b6c3722Schristos 	if(key_deleted)
5093b6c3722Schristos 		return;
5103b6c3722Schristos 
5113b6c3722Schristos 	log_assert(type == check_lock_rwlock);
5123b6c3722Schristos 	checklock_lockit(type, lock, func, file, line,
5133b6c3722Schristos 		try_rd, timed_rd, &lock->u.rwlock, 0, 0);
5143b6c3722Schristos }
5153b6c3722Schristos 
5163b6c3722Schristos /** helper for wrlock: try */
try_wr(void * arg)5173b6c3722Schristos static int try_wr(void* arg)
5183b6c3722Schristos { return pthread_rwlock_trywrlock((pthread_rwlock_t*)arg); }
5193b6c3722Schristos /** helper for wrlock: timed */
timed_wr(void * arg,struct timespec * to)5203b6c3722Schristos static int timed_wr(void* arg, struct timespec* to)
5213b6c3722Schristos { return pthread_rwlock_timedwrlock((pthread_rwlock_t*)arg, to); }
5223b6c3722Schristos 
5233b6c3722Schristos /** check if OK, lock */
5243b6c3722Schristos void
checklock_wrlock(enum check_lock_type type,struct checked_lock * lock,const char * func,const char * file,int line)5253b6c3722Schristos checklock_wrlock(enum check_lock_type type, struct checked_lock* lock,
5263b6c3722Schristos         const char* func, const char* file, int line)
5273b6c3722Schristos {
5283b6c3722Schristos 	if(key_deleted)
5293b6c3722Schristos 		return;
5303b6c3722Schristos 	log_assert(type == check_lock_rwlock);
5313b6c3722Schristos 	checklock_lockit(type, lock, func, file, line,
5323b6c3722Schristos 		try_wr, timed_wr, &lock->u.rwlock, 0, 1);
5333b6c3722Schristos }
5343b6c3722Schristos 
5353b6c3722Schristos /** helper for lock mutex: try */
try_mutex(void * arg)5363b6c3722Schristos static int try_mutex(void* arg)
5373b6c3722Schristos { return pthread_mutex_trylock((pthread_mutex_t*)arg); }
5383b6c3722Schristos /** helper for lock mutex: timed */
timed_mutex(void * arg,struct timespec * to)5393b6c3722Schristos static int timed_mutex(void* arg, struct timespec* to)
5403b6c3722Schristos { return pthread_mutex_timedlock((pthread_mutex_t*)arg, to); }
5413b6c3722Schristos 
5423b6c3722Schristos /** helper for lock spinlock: try */
try_spinlock(void * arg)5433b6c3722Schristos static int try_spinlock(void* arg)
5443b6c3722Schristos { return pthread_spin_trylock((pthread_spinlock_t*)arg); }
5453b6c3722Schristos /** helper for lock spinlock: timed */
timed_spinlock(void * arg,struct timespec * to)5463b6c3722Schristos static int timed_spinlock(void* arg, struct timespec* to)
5473b6c3722Schristos {
5483b6c3722Schristos 	int err;
5493b6c3722Schristos 	/* spin for 5 seconds. (ouch for the CPU, but it beats forever) */
5503b6c3722Schristos 	while( (err=try_spinlock(arg)) == EBUSY) {
5513b6c3722Schristos #ifndef S_SPLINT_S
5523b6c3722Schristos 		if(time(NULL) >= to->tv_sec)
5533b6c3722Schristos 			return ETIMEDOUT;
5543b6c3722Schristos 		usleep(1000); /* in 1/1000000s of a second */
5553b6c3722Schristos #endif
5563b6c3722Schristos 	}
5573b6c3722Schristos 	return err;
5583b6c3722Schristos }
5593b6c3722Schristos 
5603b6c3722Schristos /** check if OK, lock */
5613b6c3722Schristos void
checklock_lock(enum check_lock_type type,struct checked_lock * lock,const char * func,const char * file,int line)5623b6c3722Schristos checklock_lock(enum check_lock_type type, struct checked_lock* lock,
5633b6c3722Schristos         const char* func, const char* file, int line)
5643b6c3722Schristos {
5653b6c3722Schristos 	if(key_deleted)
5663b6c3722Schristos 		return;
5673b6c3722Schristos 	log_assert(type != check_lock_rwlock);
5683b6c3722Schristos 	switch(type) {
5693b6c3722Schristos 		case check_lock_mutex:
5703b6c3722Schristos 			checklock_lockit(type, lock, func, file, line,
5713b6c3722Schristos 				try_mutex, timed_mutex, &lock->u.mutex, 1, 0);
5723b6c3722Schristos 			break;
5733b6c3722Schristos 		case check_lock_spinlock:
5743b6c3722Schristos 			/* void* cast needed because 'volatile' on some OS */
5753b6c3722Schristos 			checklock_lockit(type, lock, func, file, line,
5763b6c3722Schristos 				try_spinlock, timed_spinlock,
5773b6c3722Schristos 				(void*)&lock->u.spinlock, 1, 0);
5783b6c3722Schristos 			break;
5793b6c3722Schristos 		default:
5803b6c3722Schristos 			log_assert(0);
5813b6c3722Schristos 	}
5823b6c3722Schristos }
5833b6c3722Schristos 
5843b6c3722Schristos /** check if OK, unlock */
5853b6c3722Schristos void
checklock_unlock(enum check_lock_type type,struct checked_lock * lock,const char * func,const char * file,int line)5863b6c3722Schristos checklock_unlock(enum check_lock_type type, struct checked_lock* lock,
5873b6c3722Schristos         const char* func, const char* file, int line)
5883b6c3722Schristos {
5893b6c3722Schristos 	struct thr_check *thr;
5903b6c3722Schristos 	if(key_deleted)
5913b6c3722Schristos 		return;
5923b6c3722Schristos 	thr = (struct thr_check*)pthread_getspecific(thr_debug_key);
5933b6c3722Schristos 	checktype(type, lock, func, file, line);
5943b6c3722Schristos 	if(!thr) lock_error(lock, func, file, line, "no thread info");
5953b6c3722Schristos 
5963b6c3722Schristos 	acquire_locklock(lock, func, file, line);
5973b6c3722Schristos 	/* was this thread even holding this lock? */
5983b6c3722Schristos 	if(thr->holding_first != lock &&
5993b6c3722Schristos 		lock->prev_held_lock[thr->num] == NULL) {
6003b6c3722Schristos 		lock_error(lock, func, file, line, "unlock nonlocked lock");
6013b6c3722Schristos 	}
6023b6c3722Schristos 	if(lock->hold_count <= 0)
6033b6c3722Schristos 		lock_error(lock, func, file, line, "too many unlocks");
6043b6c3722Schristos 
6053b6c3722Schristos 	/* store this point as last touched by */
6063b6c3722Schristos 	lock->holder = thr;
6073b6c3722Schristos 	lock->hold_count --;
6083b6c3722Schristos 	lock->holder_func = func;
6093b6c3722Schristos 	lock->holder_file = file;
6103b6c3722Schristos 	lock->holder_line = line;
6113b6c3722Schristos 
6123b6c3722Schristos 	/* delete from thread holder list */
6133b6c3722Schristos 	/* no need to lock other lockstructs, because they are all on the
6143b6c3722Schristos 	 * held-locks list, and this thread holds their locks.
6153b6c3722Schristos 	 * we only touch the thr->num members, so it is safe.  */
6163b6c3722Schristos 	if(thr->holding_first == lock)
6173b6c3722Schristos 		thr->holding_first = lock->next_held_lock[thr->num];
6183b6c3722Schristos 	if(thr->holding_last == lock)
6193b6c3722Schristos 		thr->holding_last = lock->prev_held_lock[thr->num];
6203b6c3722Schristos 	if(lock->next_held_lock[thr->num])
6213b6c3722Schristos 		lock->next_held_lock[thr->num]->prev_held_lock[thr->num] =
6223b6c3722Schristos 			lock->prev_held_lock[thr->num];
6233b6c3722Schristos 	if(lock->prev_held_lock[thr->num])
6243b6c3722Schristos 		lock->prev_held_lock[thr->num]->next_held_lock[thr->num] =
6253b6c3722Schristos 			lock->next_held_lock[thr->num];
6263b6c3722Schristos 	lock->next_held_lock[thr->num] = NULL;
6273b6c3722Schristos 	lock->prev_held_lock[thr->num] = NULL;
6283b6c3722Schristos 
6293b6c3722Schristos 	if(type==check_lock_rwlock && lock->writeholder == thr) {
6303b6c3722Schristos 		lock->writeholder = NULL;
6313b6c3722Schristos 		prot_store(lock);
6323b6c3722Schristos 	} else if(type != check_lock_rwlock) {
6333b6c3722Schristos 		/* store memory areas that are protected, for later checks */
6343b6c3722Schristos 		prot_store(lock);
6353b6c3722Schristos 	}
6363b6c3722Schristos 	LOCKRET(pthread_mutex_unlock(&lock->lock));
6373b6c3722Schristos 
6383b6c3722Schristos 	/* unlock it */
6393b6c3722Schristos 	switch(type) {
6403b6c3722Schristos 		case check_lock_mutex:
6413b6c3722Schristos 			LOCKRET(pthread_mutex_unlock(&lock->u.mutex));
6423b6c3722Schristos 			break;
6433b6c3722Schristos 		case check_lock_spinlock:
6443b6c3722Schristos 			LOCKRET(pthread_spin_unlock(&lock->u.spinlock));
6453b6c3722Schristos 			break;
6463b6c3722Schristos 		case check_lock_rwlock:
6473b6c3722Schristos 			LOCKRET(pthread_rwlock_unlock(&lock->u.rwlock));
6483b6c3722Schristos 			break;
6493b6c3722Schristos 		default:
6503b6c3722Schristos 			log_assert(0);
6513b6c3722Schristos 	}
6523b6c3722Schristos }
6533b6c3722Schristos 
6543b6c3722Schristos /** open order info debug file, thr->num must be valid */
6553b6c3722Schristos static void
open_lockorder(struct thr_check * thr)6563b6c3722Schristos open_lockorder(struct thr_check* thr)
6573b6c3722Schristos {
6583b6c3722Schristos 	char buf[24];
6593b6c3722Schristos 	time_t t;
6603b6c3722Schristos 	snprintf(buf, sizeof(buf), "ublocktrace.%d", thr->num);
6613b6c3722Schristos 	thr->order_info = fopen(buf, "w");
6623b6c3722Schristos 	if(!thr->order_info)
6633b6c3722Schristos 		fatal_exit("could not open %s: %s", buf, strerror(errno));
6643b6c3722Schristos 	thr->locks_created = 0;
6653b6c3722Schristos 	t = time(NULL);
6663b6c3722Schristos 	/* write: <time_stamp> <runpid> <thread_num> */
6673b6c3722Schristos 	if(fwrite(&t, sizeof(t), 1, thr->order_info) != 1 ||
6683b6c3722Schristos 		fwrite(&thr->num, sizeof(thr->num), 1, thr->order_info) != 1 ||
6693b6c3722Schristos 		fwrite(&check_lock_pid, sizeof(check_lock_pid), 1,
6703b6c3722Schristos 		thr->order_info) != 1)
6713b6c3722Schristos 		log_err("fwrite: %s", strerror(errno));
6723b6c3722Schristos }
6733b6c3722Schristos 
6743b6c3722Schristos /** checklock thread main, Inits thread structure */
checklock_main(void * arg)6753b6c3722Schristos static void* checklock_main(void* arg)
6763b6c3722Schristos {
6773b6c3722Schristos 	struct thr_check* thr = (struct thr_check*)arg;
6783b6c3722Schristos 	void* ret;
6793b6c3722Schristos 	thr->id = pthread_self();
6803b6c3722Schristos 	/* Hack to get same numbers as in log file */
6813b6c3722Schristos 	thr->num = *(int*)(thr->arg);
6823b6c3722Schristos 	log_assert(thr->num < THRDEBUG_MAX_THREADS);
6833b6c3722Schristos 	/* as an aside, due to this, won't work for libunbound bg thread */
6843b6c3722Schristos 	if(thread_infos[thr->num] != NULL)
6853b6c3722Schristos 		log_warn("thread warning, thr->num %d not NULL", thr->num);
6863b6c3722Schristos 	thread_infos[thr->num] = thr;
6873b6c3722Schristos 	LOCKRET(pthread_setspecific(thr_debug_key, thr));
6883b6c3722Schristos 	if(check_locking_order)
6893b6c3722Schristos 		open_lockorder(thr);
6903b6c3722Schristos 	ret = thr->func(thr->arg);
6913b6c3722Schristos 	thread_infos[thr->num] = NULL;
6923b6c3722Schristos 	if(check_locking_order)
6933b6c3722Schristos 		fclose(thr->order_info);
6943b6c3722Schristos 	free(thr);
6953b6c3722Schristos 	return ret;
6963b6c3722Schristos }
6973b6c3722Schristos 
6983b6c3722Schristos /** init the main thread */
checklock_start(void)6993b6c3722Schristos void checklock_start(void)
7003b6c3722Schristos {
7013b6c3722Schristos 	if(key_deleted)
7023b6c3722Schristos 		return;
7033b6c3722Schristos 	if(!key_created) {
7043b6c3722Schristos 		struct thr_check* thisthr = (struct thr_check*)calloc(1,
7053b6c3722Schristos 			sizeof(struct thr_check));
7063b6c3722Schristos 		if(!thisthr)
7073b6c3722Schristos 			fatal_exit("thrcreate: out of memory");
7083b6c3722Schristos 		key_created = 1;
7093b6c3722Schristos 		check_lock_pid = getpid();
7103b6c3722Schristos 		LOCKRET(pthread_key_create(&thr_debug_key, NULL));
7113b6c3722Schristos 		LOCKRET(pthread_setspecific(thr_debug_key, thisthr));
7123b6c3722Schristos 		thread_infos[0] = thisthr;
7133b6c3722Schristos 		if(check_locking_order)
7143b6c3722Schristos 			open_lockorder(thisthr);
7153b6c3722Schristos 	}
7163b6c3722Schristos }
7173b6c3722Schristos 
7183b6c3722Schristos /** stop checklocks */
checklock_stop(void)7193b6c3722Schristos void checklock_stop(void)
7203b6c3722Schristos {
7213b6c3722Schristos 	if(key_created) {
7223b6c3722Schristos 		int i;
7233b6c3722Schristos 		key_deleted = 1;
7243b6c3722Schristos 		if(check_locking_order)
7253b6c3722Schristos 			fclose(thread_infos[0]->order_info);
7263b6c3722Schristos 		free(thread_infos[0]);
7273b6c3722Schristos 		thread_infos[0] = NULL;
7283b6c3722Schristos 		for(i = 0; i < THRDEBUG_MAX_THREADS; i++)
7293b6c3722Schristos 			log_assert(thread_infos[i] == NULL);
7303b6c3722Schristos 			/* should have been cleaned up. */
7313b6c3722Schristos 		LOCKRET(pthread_key_delete(thr_debug_key));
7323b6c3722Schristos 		key_created = 0;
7333b6c3722Schristos 	}
7343b6c3722Schristos }
7353b6c3722Schristos 
7363b6c3722Schristos /** allocate debug info and create thread */
7373b6c3722Schristos void
checklock_thrcreate(pthread_t * id,void * (* func)(void *),void * arg)7383b6c3722Schristos checklock_thrcreate(pthread_t* id, void* (*func)(void*), void* arg)
7393b6c3722Schristos {
7403b6c3722Schristos 	struct thr_check* thr = (struct thr_check*)calloc(1,
7413b6c3722Schristos 		sizeof(struct thr_check));
7423b6c3722Schristos 	if(!thr)
7433b6c3722Schristos 		fatal_exit("thrcreate: out of memory");
7443b6c3722Schristos 	if(!key_created) {
7453b6c3722Schristos 		checklock_start();
7463b6c3722Schristos 	}
7473b6c3722Schristos 	thr->func = func;
7483b6c3722Schristos 	thr->arg = arg;
7493b6c3722Schristos 	LOCKRET(pthread_create(id, NULL, checklock_main, thr));
7503b6c3722Schristos }
7513b6c3722Schristos 
7523b6c3722Schristos /** count number of thread infos */
7533b6c3722Schristos static int
count_thread_infos(void)7543b6c3722Schristos count_thread_infos(void)
7553b6c3722Schristos {
7563b6c3722Schristos 	int cnt = 0;
7573b6c3722Schristos 	int i;
7583b6c3722Schristos 	for(i=0; i<THRDEBUG_MAX_THREADS; i++)
7593b6c3722Schristos 		if(thread_infos[i])
7603b6c3722Schristos 			cnt++;
7613b6c3722Schristos 	return cnt;
7623b6c3722Schristos }
7633b6c3722Schristos 
7643b6c3722Schristos /** print lots of info on a lock */
7653b6c3722Schristos static void
lock_debug_info(struct checked_lock * lock)7663b6c3722Schristos lock_debug_info(struct checked_lock* lock)
7673b6c3722Schristos {
7683b6c3722Schristos 	if(!lock) return;
7693b6c3722Schristos 	log_info("+++ Lock %llx, %d %d create %s %s %d",
7703b6c3722Schristos 		(unsigned long long)(size_t)lock,
7713b6c3722Schristos 		lock->create_thread, lock->create_instance,
7723b6c3722Schristos 		lock->create_func, lock->create_file, lock->create_line);
7733b6c3722Schristos 	log_info("lock type: %s",
7743b6c3722Schristos 		(lock->type==check_lock_mutex)?"mutex": (
7753b6c3722Schristos 		(lock->type==check_lock_spinlock)?"spinlock": (
7763b6c3722Schristos 		(lock->type==check_lock_rwlock)?"rwlock": "badtype")));
7773b6c3722Schristos 	log_info("lock contention %u, history:%u, hold:%d, wait:%d",
7783b6c3722Schristos 		(unsigned)lock->contention_count, (unsigned)lock->history_count,
7793b6c3722Schristos 		lock->hold_count, lock->wait_count);
7803b6c3722Schristos 	log_info("last touch %s %s %d", lock->holder_func, lock->holder_file,
7813b6c3722Schristos 		lock->holder_line);
7823b6c3722Schristos 	log_info("holder thread %d, writeholder thread %d",
7833b6c3722Schristos 		lock->holder?lock->holder->num:-1,
7843b6c3722Schristos 		lock->writeholder?lock->writeholder->num:-1);
7853b6c3722Schristos }
7863b6c3722Schristos 
7873b6c3722Schristos /** print debug locks held by a thread */
7883b6c3722Schristos static void
held_debug_info(struct thr_check * thr,struct checked_lock * lock)7893b6c3722Schristos held_debug_info(struct thr_check* thr, struct checked_lock* lock)
7903b6c3722Schristos {
7913b6c3722Schristos 	if(!lock) return;
7923b6c3722Schristos 	lock_debug_info(lock);
7933b6c3722Schristos 	held_debug_info(thr, lock->next_held_lock[thr->num]);
7943b6c3722Schristos }
7953b6c3722Schristos 
7963b6c3722Schristos /** print debug info for a thread */
7973b6c3722Schristos static void
thread_debug_info(struct thr_check * thr)7983b6c3722Schristos thread_debug_info(struct thr_check* thr)
7993b6c3722Schristos {
8003b6c3722Schristos 	struct checked_lock* w = NULL;
8013b6c3722Schristos 	struct checked_lock* f = NULL;
8023b6c3722Schristos 	struct checked_lock* l = NULL;
8033b6c3722Schristos 	if(!thr) return;
8043b6c3722Schristos 	log_info("pthread id is %x", (int)thr->id);
8053b6c3722Schristos 	log_info("thread func is %llx", (unsigned long long)(size_t)thr->func);
8063b6c3722Schristos 	log_info("thread arg is %llx (%d)",
8073b6c3722Schristos 		(unsigned long long)(size_t)thr->arg,
8083b6c3722Schristos 		(thr->arg?*(int*)thr->arg:0));
8093b6c3722Schristos 	log_info("thread num is %d", thr->num);
8103b6c3722Schristos 	log_info("locks created %d", thr->locks_created);
8113b6c3722Schristos 	log_info("open file for lockinfo: %s",
8123b6c3722Schristos 		thr->order_info?"yes, flushing":"no");
8133b6c3722Schristos 	fflush(thr->order_info);
8143b6c3722Schristos 	w = thr->waiting;
8153b6c3722Schristos 	f = thr->holding_first;
8163b6c3722Schristos 	l = thr->holding_last;
8173b6c3722Schristos 	log_info("thread waiting for a lock: %s %llx", w?"yes":"no",
8183b6c3722Schristos 		(unsigned long long)(size_t)w);
8193b6c3722Schristos 	lock_debug_info(w);
8203b6c3722Schristos 	log_info("thread holding first: %s, last: %s", f?"yes":"no",
8213b6c3722Schristos 		l?"yes":"no");
8223b6c3722Schristos 	held_debug_info(thr, f);
8233b6c3722Schristos }
8243b6c3722Schristos 
8253b6c3722Schristos static void
total_debug_info(void)8263b6c3722Schristos total_debug_info(void)
8273b6c3722Schristos {
8283b6c3722Schristos 	int i;
8293b6c3722Schristos 	log_info("checklocks: supervising %d threads.",
8303b6c3722Schristos 		count_thread_infos());
8313b6c3722Schristos 	if(!key_created) {
8323b6c3722Schristos 		log_info("No thread debug key created yet");
8333b6c3722Schristos 	}
8343b6c3722Schristos 	for(i=0; i<THRDEBUG_MAX_THREADS; i++) {
8353b6c3722Schristos 		if(thread_infos[i]) {
8363b6c3722Schristos 			log_info("*** Thread %d information: ***", i);
8373b6c3722Schristos 			thread_debug_info(thread_infos[i]);
8383b6c3722Schristos 		}
8393b6c3722Schristos 	}
8403b6c3722Schristos }
8413b6c3722Schristos 
8423b6c3722Schristos /** signal handler for join timeout, Exits */
joinalarm(int ATTR_UNUSED (sig))8433b6c3722Schristos static RETSIGTYPE joinalarm(int ATTR_UNUSED(sig))
8443b6c3722Schristos {
8453b6c3722Schristos 	log_err("join thread timeout. hangup or deadlock. Info follows.");
8463b6c3722Schristos 	total_debug_info();
8473b6c3722Schristos 	fatal_exit("join thread timeout. hangup or deadlock.");
8483b6c3722Schristos }
8493b6c3722Schristos 
8503b6c3722Schristos /** wait for thread with a timeout */
8513b6c3722Schristos void
checklock_thrjoin(pthread_t thread)8523b6c3722Schristos checklock_thrjoin(pthread_t thread)
8533b6c3722Schristos {
8543b6c3722Schristos 	/* wait with a timeout */
8553b6c3722Schristos 	if(signal(SIGALRM, joinalarm) == SIG_ERR)
8563b6c3722Schristos 		fatal_exit("signal(): %s", strerror(errno));
8573b6c3722Schristos 	(void)alarm(CHECK_JOIN_TIMEOUT);
8583b6c3722Schristos 	LOCKRET(pthread_join(thread, NULL));
8593b6c3722Schristos 	(void)alarm(0);
8603b6c3722Schristos }
8613b6c3722Schristos 
8623b6c3722Schristos #endif /* USE_THREAD_DEBUG */
863