diff options
| author | Dag-Erling Smørgrav <des@FreeBSD.org> | 2012-07-04 14:24:26 +0000 | 
|---|---|---|
| committer | Dag-Erling Smørgrav <des@FreeBSD.org> | 2012-07-04 14:24:26 +0000 | 
| commit | afb79913ce00d885b8b43f7478e1e054edadb567 (patch) | |
| tree | b9037afac70edd3c6342318cedbbadc648b799ca /testcode/checklocks.h | |
Notes
Diffstat (limited to 'testcode/checklocks.h')
| -rw-r--r-- | testcode/checklocks.h | 343 | 
1 files changed, 343 insertions, 0 deletions
diff --git a/testcode/checklocks.h b/testcode/checklocks.h new file mode 100644 index 0000000000000..de901da9afbac --- /dev/null +++ b/testcode/checklocks.h @@ -0,0 +1,343 @@ +/** + * testcode/checklocks.h - wrapper on locks that checks access. + * + * Copyright (c) 2007, NLnet Labs. All rights reserved. + *  + * This software is open source. + *  + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + *  + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + *  + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + *  + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TESTCODE_CHECK_LOCKS_H +#define TESTCODE_CHECK_LOCKS_H + +/** + * \file + * Locks that are checked. + * + * Holds information per lock and per thread. + * That information is protected by a mutex (unchecked). + * + * Checks: + *      o which func, file, line created the lock. + *      o contention count, measures amount of contention on the lock. + *      o the memory region(s) that the lock protects are + *        memcmp'ed to ascertain no race conditions. + *      o checks that locks are unlocked properly (before deletion). + *        keeps which func, file, line that locked it. + *	o checks deadlocks with timeout so it can print errors for them. + * + * Limitations: + *	o Detects unprotected memory access when the lock is locked or freed, + *	  which detects races only if they happen, and only if in protected + *	  memory areas. + *	o Detects deadlocks by timeout, so approximately, as they happen. + *	o Does not check order of locking. + *	o Uses a lot of memory. + *	o The checks use locks themselves, changing scheduling, + *	  thus changing what races you see. + */ + +#ifdef USE_THREAD_DEBUG +#ifndef HAVE_PTHREAD +/* we need the *timed*lock() routines to use for deadlock detection. */ +#error "Need pthreads for checked locks" +#endif +/******************* THREAD DEBUG ************************/ +#include <pthread.h> + +/** How many threads to allocate for */ +#define THRDEBUG_MAX_THREADS 32 /* threads */ +/** do we check locking order */ +extern int check_locking_order; + +/** + * Protection memory area. + * It is copied to a holding buffer to compare against later. + * Note that it may encompass the lock structure. + */ +struct protected_area { +	/** where the memory region starts */ +	void* region; +	/** size of the region */ +	size_t size; +	/** backbuffer that holds a copy, of same size. */ +	void* hold; +	/** next protected area in list */ +	struct protected_area* next; +}; + +/** + * Per thread information for locking debug wrappers.  + */ +struct thr_check { +	/** thread id */ +	pthread_t id; +	/** real thread func */ +	void* (*func)(void*); +	/** func user arg */ +	void* arg; +	/** number of thread in list structure */ +	int num; +	/** instance number - how many locks have been created by thread */ +	int locks_created; +	/** file to write locking order information to */ +	FILE* order_info; +	/**  +	 * List of locks that this thread is holding, double +	 * linked list. The first element is the most recent lock acquired. +	 * So it represents the stack of locks acquired. (of all types). +	 */ +	struct checked_lock *holding_first, *holding_last; +	/** if the thread is currently waiting for a lock, which one */ +	struct checked_lock* waiting; +}; + +/** + * One structure for all types of locks. + */ +struct checked_lock { +	/** mutex for exclusive access to this structure */ +	pthread_mutex_t lock; +	/** list of memory regions protected by this checked lock */ +	struct protected_area* prot; +	/** where was this lock created */ +	const char* create_func, *create_file; +	/** where was this lock created */ +	int create_line; +	/** unique instance identifier */ +	int create_thread, create_instance; +	/** contention count */ +	size_t contention_count; +	/** number of times locked, ever */ +	size_t history_count; +	/** hold count (how many threads are holding this lock) */ +	int hold_count; +	/** how many threads are waiting for this lock */ +	int wait_count; +	/** who touched it last */ +	const char* holder_func, *holder_file; +	/** who touched it last */ +	int holder_line; +	/** who owns the lock now */ +	struct thr_check* holder; +	/** for rwlocks, the writelock holder */ +	struct thr_check* writeholder; + +	/** next lock a thread is holding (less recent) */ +	struct checked_lock* next_held_lock[THRDEBUG_MAX_THREADS]; +	/** prev lock a thread is holding (more recent) */ +	struct checked_lock* prev_held_lock[THRDEBUG_MAX_THREADS]; + +	/** type of lock */ +	enum check_lock_type { +		/** basic mutex */ +		check_lock_mutex, +		/** fast spinlock */ +		check_lock_spinlock, +		/** rwlock */ +		check_lock_rwlock +	} type; +	/** the lock itself, see type to disambiguate the union */ +	union { +		/** mutex */ +		pthread_mutex_t mutex; +		/** spinlock */ +		pthread_spinlock_t spinlock; +		/** rwlock */ +		pthread_rwlock_t rwlock; +	} u; +}; + +/** + * Additional call for the user to specify what areas are protected + * @param lock: the lock that protects the area. It can be inside the area. + *	The lock must be inited. Call with user lock. (any type). + *	It demangles the lock itself (struct checked_lock**). + * @param area: ptr to mem. + * @param size: length of area. + * You can call it multiple times with the same lock to give several areas. + * Call it when you are done initialising the area, since it will be copied + * at this time and protected right away against unauthorised changes until  + * the next lock() call is done. + */ +void lock_protect(void* lock, void* area, size_t size); + +/** + * Remove protected area from lock. + * No need to call this when deleting the lock. + * @param lock: the lock, any type, (struct checked_lock**). + * @param area: pointer to memory. + */ +void lock_unprotect(void* lock, void* area); + +/** + * Get memory associated with a checked lock + * @param lock: the checked lock, any type. (struct checked_lock**). + * @return: in bytes, including protected areas. + */ +size_t lock_get_mem(void* lock); + +/** + * Initialise checklock. Sets up internal debug structures. + */ +void checklock_start(void); + +/** + * Cleanup internal debug state. + */ +void checklock_stop(void); + +/** + * Init locks. + * @param type: what type of lock this is. + * @param lock: ptr to user alloced ptr structure. This is inited. + *     So an alloc is done and the ptr is stored as result. + * @param func: caller function name. + * @param file: caller file name. + * @param line: caller line number. + */ +void checklock_init(enum check_lock_type type, struct checked_lock** lock, +	const char* func, const char* file, int line); + +/** + * Destroy locks. Free the structure. + * @param type: what type of lock this is. + * @param lock: ptr to user alloced structure. This is destroyed. + * @param func: caller function name. + * @param file: caller file name. + * @param line: caller line number. + */ +void checklock_destroy(enum check_lock_type type, struct checked_lock** lock, +	const char* func, const char* file, int line); + +/** + * Acquire readlock. + * @param type: what type of lock this is. Had better be a rwlock. + * @param lock: ptr to lock. + * @param func: caller function name. + * @param file: caller file name. + * @param line: caller line number. + */ +void checklock_rdlock(enum check_lock_type type, struct checked_lock* lock, +	const char* func, const char* file, int line); + +/** + * Acquire writelock. + * @param type: what type of lock this is. Had better be a rwlock. + * @param lock: ptr to lock. + * @param func: caller function name. + * @param file: caller file name. + * @param line: caller line number. + */ +void checklock_wrlock(enum check_lock_type type, struct checked_lock* lock, +	const char* func, const char* file, int line); + +/** + * Locks. + * @param type: what type of lock this is. Had better be mutex or spinlock. + * @param lock: the lock. + * @param func: caller function name. + * @param file: caller file name. + * @param line: caller line number. + */ +void checklock_lock(enum check_lock_type type, struct checked_lock* lock, +	const char* func, const char* file, int line); + +/** + * Unlocks. + * @param type: what type of lock this is. + * @param lock: the lock. + * @param func: caller function name. + * @param file: caller file name. + * @param line: caller line number. + */ +void checklock_unlock(enum check_lock_type type, struct checked_lock* lock, +	const char* func, const char* file, int line); + +/** + * Create thread. + * @param thr: Thread id, where to store result. + * @param func: thread start function. + * @param arg: user argument. + */ +void checklock_thrcreate(pthread_t* thr, void* (*func)(void*), void* arg); + +/** + * Wait for thread to exit. Returns thread return value. + * @param thread: thread to wait for. + */ +void checklock_thrjoin(pthread_t thread); + +/** structures to enable compiler type checking on the locks.  + * Also the pointer makes it so that the lock can be part of the protected + * region without any possible problem (since the ptr will stay the same.) + * i.e. there can be contention and readlocks stored in checked_lock, while + * the protected area stays the same, even though it contains (ptr to) lock. + */ +struct checked_lock_rw { struct checked_lock* c_rw; }; +/** structures to enable compiler type checking on the locks. */ +struct checked_lock_mutex { struct checked_lock* c_m; }; +/** structures to enable compiler type checking on the locks. */ +struct checked_lock_spl { struct checked_lock* c_spl; }; + +/** debugging rwlock */ +typedef struct checked_lock_rw lock_rw_t; +#define lock_rw_init(lock) checklock_init(check_lock_rwlock, &((lock)->c_rw), __func__, __FILE__, __LINE__) +#define lock_rw_destroy(lock) checklock_destroy(check_lock_rwlock, &((lock)->c_rw), __func__, __FILE__, __LINE__) +#define lock_rw_rdlock(lock) checklock_rdlock(check_lock_rwlock, (lock)->c_rw, __func__, __FILE__, __LINE__) +#define lock_rw_wrlock(lock) checklock_wrlock(check_lock_rwlock, (lock)->c_rw, __func__, __FILE__, __LINE__) +#define lock_rw_unlock(lock) checklock_unlock(check_lock_rwlock, (lock)->c_rw, __func__, __FILE__, __LINE__) + +/** debugging mutex */ +typedef struct checked_lock_mutex lock_basic_t; +#define lock_basic_init(lock) checklock_init(check_lock_mutex, &((lock)->c_m), __func__, __FILE__, __LINE__) +#define lock_basic_destroy(lock) checklock_destroy(check_lock_mutex, &((lock)->c_m), __func__, __FILE__, __LINE__) +#define lock_basic_lock(lock) checklock_lock(check_lock_mutex, (lock)->c_m, __func__, __FILE__, __LINE__) +#define lock_basic_unlock(lock) checklock_unlock(check_lock_mutex, (lock)->c_m, __func__, __FILE__, __LINE__) + +/** debugging spinlock */ +typedef struct checked_lock_spl lock_quick_t; +#define lock_quick_init(lock) checklock_init(check_lock_spinlock, &((lock)->c_spl), __func__, __FILE__, __LINE__) +#define lock_quick_destroy(lock) checklock_destroy(check_lock_spinlock, &((lock)->c_spl), __func__, __FILE__, __LINE__) +#define lock_quick_lock(lock) checklock_lock(check_lock_spinlock, (lock)->c_spl, __func__, __FILE__, __LINE__) +#define lock_quick_unlock(lock) checklock_unlock(check_lock_spinlock, (lock)->c_spl, __func__, __FILE__, __LINE__) + +/** we use the pthread id, our thr_check structure is kept behind the scenes */ +typedef pthread_t ub_thread_t; +#define ub_thread_create(thr, func, arg) checklock_thrcreate(thr, func, arg) +#define ub_thread_self() pthread_self() +#define ub_thread_join(thread) checklock_thrjoin(thread) + +typedef pthread_key_t ub_thread_key_t; +#define ub_thread_key_create(key, f) LOCKRET(pthread_key_create(key, f)) +#define ub_thread_key_set(key, v) LOCKRET(pthread_setspecific(key, v)) +#define ub_thread_key_get(key) pthread_getspecific(key) + +#endif /* USE_THREAD_DEBUG */ +#endif /* TESTCODE_CHECK_LOCKS_H */  | 
