1712b2f30Ssthen /**
2712b2f30Ssthen * testcode/checklocks.c - wrapper on locks that checks access.
3712b2f30Ssthen *
4712b2f30Ssthen * Copyright (c) 2007, NLnet Labs. All rights reserved.
5712b2f30Ssthen *
6712b2f30Ssthen * This software is open source.
7712b2f30Ssthen *
8712b2f30Ssthen * Redistribution and use in source and binary forms, with or without
9712b2f30Ssthen * modification, are permitted provided that the following conditions
10712b2f30Ssthen * are met:
11712b2f30Ssthen *
12712b2f30Ssthen * Redistributions of source code must retain the above copyright notice,
13712b2f30Ssthen * this list of conditions and the following disclaimer.
14712b2f30Ssthen *
15712b2f30Ssthen * Redistributions in binary form must reproduce the above copyright notice,
16712b2f30Ssthen * this list of conditions and the following disclaimer in the documentation
17712b2f30Ssthen * and/or other materials provided with the distribution.
18712b2f30Ssthen *
19712b2f30Ssthen * Neither the name of the NLNET LABS nor the names of its contributors may
20712b2f30Ssthen * be used to endorse or promote products derived from this software without
21712b2f30Ssthen * specific prior written permission.
22712b2f30Ssthen *
23712b2f30Ssthen * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24712b2f30Ssthen * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25712b2f30Ssthen * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26712b2f30Ssthen * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27712b2f30Ssthen * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28712b2f30Ssthen * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29712b2f30Ssthen * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30712b2f30Ssthen * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31712b2f30Ssthen * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32712b2f30Ssthen * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33712b2f30Ssthen * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34712b2f30Ssthen */
35712b2f30Ssthen
36712b2f30Ssthen #include "config.h"
37712b2f30Ssthen #include <signal.h>
38712b2f30Ssthen #include "util/locks.h" /* include before checklocks.h */
39712b2f30Ssthen #include "testcode/checklocks.h"
40712b2f30Ssthen
41712b2f30Ssthen /**
42712b2f30Ssthen * \file
43712b2f30Ssthen * Locks that are checked.
44712b2f30Ssthen *
45712b2f30Ssthen * Ugly hack: uses the fact that workers start with an int thread_num, and
46712b2f30Ssthen * are passed to thread_create to make the thread numbers here the same as
47712b2f30Ssthen * those used for logging which is nice.
48712b2f30Ssthen *
49712b2f30Ssthen * Todo:
50712b2f30Ssthen * - debug status print, of thread lock stacks, and current waiting.
51712b2f30Ssthen */
52712b2f30Ssthen #ifdef USE_THREAD_DEBUG
53712b2f30Ssthen
54712b2f30Ssthen /** How long to wait before lock attempt is a failure. */
55712b2f30Ssthen #define CHECK_LOCK_TIMEOUT 120 /* seconds */
56712b2f30Ssthen /** How long to wait before join attempt is a failure. */
57712b2f30Ssthen #define CHECK_JOIN_TIMEOUT 120 /* seconds */
58712b2f30Ssthen
59712b2f30Ssthen /** if key has been created */
60712b2f30Ssthen static int key_created = 0;
61712b2f30Ssthen /** if the key was deleted, i.e. we have quit */
62712b2f30Ssthen static int key_deleted = 0;
63712b2f30Ssthen /** we hide the thread debug info with this key. */
64712b2f30Ssthen static ub_thread_key_type thr_debug_key;
65712b2f30Ssthen /** the list of threads, so all threads can be examined. NULL if unused. */
66712b2f30Ssthen static struct thr_check* thread_infos[THRDEBUG_MAX_THREADS];
67712b2f30Ssthen /** do we check locking order */
68712b2f30Ssthen int check_locking_order = 1;
69712b2f30Ssthen /** the pid of this runset, reasonably unique. */
70712b2f30Ssthen static pid_t check_lock_pid;
71*2682a17cSsthen /**
72*2682a17cSsthen * Should checklocks print a trace of the lock and unlock calls.
73*2682a17cSsthen * It uses fprintf for that because the log function uses a lock and that
74*2682a17cSsthen * would loop otherwise.
75*2682a17cSsthen */
76*2682a17cSsthen static int verbose_locking = 0;
77*2682a17cSsthen /**
78*2682a17cSsthen * Assume lock 0 0 (create_thread, create_instance), is the log lock and
79*2682a17cSsthen * do not print for that. Otherwise the output is full of log lock accesses.
80*2682a17cSsthen */
81*2682a17cSsthen static int verbose_locking_not_loglock = 1;
82712b2f30Ssthen
83712b2f30Ssthen /** print all possible debug info on the state of the system */
84712b2f30Ssthen static void total_debug_info(void);
85dc90232dSsthen /** print pretty lock error and exit (decl for NORETURN attribute) */
86dc90232dSsthen static void lock_error(struct checked_lock* lock, const char* func,
87dc90232dSsthen const char* file, int line, const char* err) ATTR_NORETURN;
88712b2f30Ssthen
89712b2f30Ssthen /** print pretty lock error and exit */
lock_error(struct checked_lock * lock,const char * func,const char * file,int line,const char * err)90712b2f30Ssthen static void lock_error(struct checked_lock* lock,
91712b2f30Ssthen const char* func, const char* file, int line, const char* err)
92712b2f30Ssthen {
93712b2f30Ssthen log_err("lock error (description follows)");
94712b2f30Ssthen log_err("Created at %s %s:%d", lock->create_func,
95712b2f30Ssthen lock->create_file, lock->create_line);
96712b2f30Ssthen if(lock->holder_func && lock->holder_file)
97712b2f30Ssthen log_err("Previously %s %s:%d", lock->holder_func,
98712b2f30Ssthen lock->holder_file, lock->holder_line);
99712b2f30Ssthen log_err("At %s %s:%d", func, file, line);
100712b2f30Ssthen log_err("Error for %s lock: %s",
101712b2f30Ssthen (lock->type==check_lock_mutex)?"mutex": (
102712b2f30Ssthen (lock->type==check_lock_spinlock)?"spinlock": (
103712b2f30Ssthen (lock->type==check_lock_rwlock)?"rwlock": "badtype")), err);
104712b2f30Ssthen log_err("complete status display:");
105712b2f30Ssthen total_debug_info();
106712b2f30Ssthen fatal_exit("bailing out");
107712b2f30Ssthen }
108712b2f30Ssthen
109712b2f30Ssthen /**
110712b2f30Ssthen * Obtain lock on debug lock structure. This could be a deadlock by the caller.
111712b2f30Ssthen * The debug code itself does not deadlock. Anyway, check with timeouts.
112712b2f30Ssthen * @param lock: on what to acquire lock.
113712b2f30Ssthen * @param func: user level caller identification.
114712b2f30Ssthen * @param file: user level caller identification.
115712b2f30Ssthen * @param line: user level caller identification.
116712b2f30Ssthen */
117712b2f30Ssthen static void
acquire_locklock(struct checked_lock * lock,const char * func,const char * file,int line)118712b2f30Ssthen acquire_locklock(struct checked_lock* lock,
119712b2f30Ssthen const char* func, const char* file, int line)
120712b2f30Ssthen {
121712b2f30Ssthen struct timespec to;
122712b2f30Ssthen int err;
123712b2f30Ssthen int contend = 0;
124712b2f30Ssthen /* first try; inc contention counter if not immediately */
125712b2f30Ssthen if((err = pthread_mutex_trylock(&lock->lock))) {
126712b2f30Ssthen if(err==EBUSY)
127712b2f30Ssthen contend++;
128712b2f30Ssthen else fatal_exit("error in mutex_trylock: %s", strerror(err));
129712b2f30Ssthen }
130712b2f30Ssthen if(!err)
131712b2f30Ssthen return; /* immediate success */
132712b2f30Ssthen to.tv_sec = time(NULL) + CHECK_LOCK_TIMEOUT;
133712b2f30Ssthen to.tv_nsec = 0;
134712b2f30Ssthen err = pthread_mutex_timedlock(&lock->lock, &to);
135712b2f30Ssthen if(err) {
136712b2f30Ssthen log_err("in acquiring locklock: %s", strerror(err));
137712b2f30Ssthen lock_error(lock, func, file, line, "acquire locklock");
138712b2f30Ssthen }
139712b2f30Ssthen /* since we hold the lock, we can edit the contention_count */
140712b2f30Ssthen lock->contention_count += contend;
141712b2f30Ssthen }
142712b2f30Ssthen
143712b2f30Ssthen /** add protected region */
144712b2f30Ssthen void
lock_protect(void * p,void * area,size_t size)145712b2f30Ssthen lock_protect(void *p, void* area, size_t size)
146712b2f30Ssthen {
147712b2f30Ssthen struct checked_lock* lock = *(struct checked_lock**)p;
148712b2f30Ssthen struct protected_area* e = (struct protected_area*)malloc(
149712b2f30Ssthen sizeof(struct protected_area));
150712b2f30Ssthen if(!e)
151712b2f30Ssthen fatal_exit("lock_protect: out of memory");
152712b2f30Ssthen e->region = area;
153712b2f30Ssthen e->size = size;
154712b2f30Ssthen e->hold = malloc(size);
155712b2f30Ssthen if(!e->hold)
156712b2f30Ssthen fatal_exit("lock_protect: out of memory");
157712b2f30Ssthen memcpy(e->hold, e->region, e->size);
158712b2f30Ssthen
159712b2f30Ssthen acquire_locklock(lock, __func__, __FILE__, __LINE__);
160712b2f30Ssthen e->next = lock->prot;
161712b2f30Ssthen lock->prot = e;
162712b2f30Ssthen LOCKRET(pthread_mutex_unlock(&lock->lock));
163712b2f30Ssthen }
164712b2f30Ssthen
165712b2f30Ssthen /** remove protected region */
166712b2f30Ssthen void
lock_unprotect(void * mangled,void * area)167712b2f30Ssthen lock_unprotect(void* mangled, void* area)
168712b2f30Ssthen {
169712b2f30Ssthen struct checked_lock* lock = *(struct checked_lock**)mangled;
170712b2f30Ssthen struct protected_area* p, **prevp;
171712b2f30Ssthen if(!lock)
172712b2f30Ssthen return;
173712b2f30Ssthen acquire_locklock(lock, __func__, __FILE__, __LINE__);
174712b2f30Ssthen p = lock->prot;
175712b2f30Ssthen prevp = &lock->prot;
176712b2f30Ssthen while(p) {
177712b2f30Ssthen if(p->region == area) {
178712b2f30Ssthen *prevp = p->next;
179712b2f30Ssthen free(p->hold);
180712b2f30Ssthen free(p);
181712b2f30Ssthen LOCKRET(pthread_mutex_unlock(&lock->lock));
182712b2f30Ssthen return;
183712b2f30Ssthen }
184712b2f30Ssthen prevp = &p->next;
185712b2f30Ssthen p = p->next;
186712b2f30Ssthen }
187712b2f30Ssthen LOCKRET(pthread_mutex_unlock(&lock->lock));
188712b2f30Ssthen }
189712b2f30Ssthen
190712b2f30Ssthen /**
191712b2f30Ssthen * Check protected memory region. Memory compare. Exit on error.
192712b2f30Ssthen * @param lock: which lock to check.
193712b2f30Ssthen * @param func: location we are now (when failure is detected).
194712b2f30Ssthen * @param file: location we are now (when failure is detected).
195712b2f30Ssthen * @param line: location we are now (when failure is detected).
196712b2f30Ssthen */
197712b2f30Ssthen static void
prot_check(struct checked_lock * lock,const char * func,const char * file,int line)198712b2f30Ssthen prot_check(struct checked_lock* lock,
199712b2f30Ssthen const char* func, const char* file, int line)
200712b2f30Ssthen {
201712b2f30Ssthen struct protected_area* p = lock->prot;
202712b2f30Ssthen while(p) {
203712b2f30Ssthen if(memcmp(p->hold, p->region, p->size) != 0) {
204712b2f30Ssthen log_hex("memory prev", p->hold, p->size);
205712b2f30Ssthen log_hex("memory here", p->region, p->size);
206712b2f30Ssthen lock_error(lock, func, file, line,
207712b2f30Ssthen "protected area modified");
208712b2f30Ssthen }
209712b2f30Ssthen p = p->next;
210712b2f30Ssthen }
211712b2f30Ssthen }
212712b2f30Ssthen
213712b2f30Ssthen /** Copy protected memory region */
214712b2f30Ssthen static void
prot_store(struct checked_lock * lock)215712b2f30Ssthen prot_store(struct checked_lock* lock)
216712b2f30Ssthen {
217712b2f30Ssthen struct protected_area* p = lock->prot;
218712b2f30Ssthen while(p) {
219712b2f30Ssthen memcpy(p->hold, p->region, p->size);
220712b2f30Ssthen p = p->next;
221712b2f30Ssthen }
222712b2f30Ssthen }
223712b2f30Ssthen
224712b2f30Ssthen /** get memory held by lock */
225712b2f30Ssthen size_t
lock_get_mem(void * pp)226712b2f30Ssthen lock_get_mem(void* pp)
227712b2f30Ssthen {
228712b2f30Ssthen size_t s;
229712b2f30Ssthen struct checked_lock* lock = *(struct checked_lock**)pp;
230712b2f30Ssthen struct protected_area* p;
231712b2f30Ssthen s = sizeof(struct checked_lock);
232712b2f30Ssthen acquire_locklock(lock, __func__, __FILE__, __LINE__);
233712b2f30Ssthen for(p = lock->prot; p; p = p->next) {
234712b2f30Ssthen s += sizeof(struct protected_area);
235712b2f30Ssthen s += p->size;
236712b2f30Ssthen }
237712b2f30Ssthen LOCKRET(pthread_mutex_unlock(&lock->lock));
238712b2f30Ssthen return s;
239712b2f30Ssthen }
240712b2f30Ssthen
241712b2f30Ssthen /** write lock trace info to file, while you hold those locks */
242712b2f30Ssthen static void
ordercheck_locklock(struct thr_check * thr,struct checked_lock * lock)243712b2f30Ssthen ordercheck_locklock(struct thr_check* thr, struct checked_lock* lock)
244712b2f30Ssthen {
245712b2f30Ssthen int info[4];
246712b2f30Ssthen if(!check_locking_order) return;
247712b2f30Ssthen if(!thr->holding_first) return; /* no older lock, no info */
248712b2f30Ssthen /* write: <lock id held> <lock id new> <file> <line> */
249712b2f30Ssthen info[0] = thr->holding_first->create_thread;
250712b2f30Ssthen info[1] = thr->holding_first->create_instance;
251712b2f30Ssthen info[2] = lock->create_thread;
252712b2f30Ssthen info[3] = lock->create_instance;
253712b2f30Ssthen if(fwrite(info, 4*sizeof(int), 1, thr->order_info) != 1 ||
254712b2f30Ssthen fwrite(lock->holder_file, strlen(lock->holder_file)+1, 1,
255712b2f30Ssthen thr->order_info) != 1 ||
256712b2f30Ssthen fwrite(&lock->holder_line, sizeof(int), 1,
257712b2f30Ssthen thr->order_info) != 1)
258712b2f30Ssthen log_err("fwrite: %s", strerror(errno));
259712b2f30Ssthen }
260712b2f30Ssthen
261712b2f30Ssthen /** write ordercheck lock creation details to file */
262712b2f30Ssthen static void
ordercheck_lockcreate(struct thr_check * thr,struct checked_lock * lock)263712b2f30Ssthen ordercheck_lockcreate(struct thr_check* thr, struct checked_lock* lock)
264712b2f30Ssthen {
265712b2f30Ssthen /* write: <ffff = create> <lock id> <file> <line> */
266712b2f30Ssthen int cmd = -1;
267712b2f30Ssthen if(!check_locking_order) return;
268712b2f30Ssthen
269712b2f30Ssthen if( fwrite(&cmd, sizeof(int), 1, thr->order_info) != 1 ||
270712b2f30Ssthen fwrite(&lock->create_thread, sizeof(int), 1,
271712b2f30Ssthen thr->order_info) != 1 ||
272712b2f30Ssthen fwrite(&lock->create_instance, sizeof(int), 1,
273712b2f30Ssthen thr->order_info) != 1 ||
274712b2f30Ssthen fwrite(lock->create_file, strlen(lock->create_file)+1, 1,
275712b2f30Ssthen thr->order_info) != 1 ||
276712b2f30Ssthen fwrite(&lock->create_line, sizeof(int), 1,
277712b2f30Ssthen thr->order_info) != 1)
278712b2f30Ssthen log_err("fwrite: %s", strerror(errno));
279712b2f30Ssthen }
280712b2f30Ssthen
281712b2f30Ssthen /** alloc struct, init lock empty */
282712b2f30Ssthen void
checklock_init(enum check_lock_type type,struct checked_lock ** lock,const char * func,const char * file,int line)283712b2f30Ssthen checklock_init(enum check_lock_type type, struct checked_lock** lock,
284712b2f30Ssthen const char* func, const char* file, int line)
285712b2f30Ssthen {
286712b2f30Ssthen struct checked_lock* e = (struct checked_lock*)calloc(1,
287712b2f30Ssthen sizeof(struct checked_lock));
288712b2f30Ssthen struct thr_check *thr = (struct thr_check*)pthread_getspecific(
289712b2f30Ssthen thr_debug_key);
290712b2f30Ssthen if(!e)
291712b2f30Ssthen fatal_exit("%s %s %d: out of memory", func, file, line);
292712b2f30Ssthen if(!thr) {
293712b2f30Ssthen /* this is called when log_init() calls lock_init()
294712b2f30Ssthen * functions, and the test check code has not yet
295712b2f30Ssthen * been initialised. But luckily, the checklock_start()
296712b2f30Ssthen * routine can be called multiple times without ill effect.
297712b2f30Ssthen */
298712b2f30Ssthen checklock_start();
299712b2f30Ssthen thr = (struct thr_check*)pthread_getspecific(thr_debug_key);
300712b2f30Ssthen }
301712b2f30Ssthen if(!thr)
302712b2f30Ssthen fatal_exit("%s %s %d: lock_init no thread info", func, file,
303712b2f30Ssthen line);
304712b2f30Ssthen *lock = e;
305712b2f30Ssthen e->type = type;
306712b2f30Ssthen e->create_func = func;
307712b2f30Ssthen e->create_file = file;
308712b2f30Ssthen e->create_line = line;
309712b2f30Ssthen e->create_thread = thr->num;
310712b2f30Ssthen e->create_instance = thr->locks_created++;
311712b2f30Ssthen ordercheck_lockcreate(thr, e);
312712b2f30Ssthen LOCKRET(pthread_mutex_init(&e->lock, NULL));
313712b2f30Ssthen switch(e->type) {
314712b2f30Ssthen case check_lock_mutex:
315712b2f30Ssthen LOCKRET(pthread_mutex_init(&e->u.mutex, NULL));
316712b2f30Ssthen break;
317712b2f30Ssthen case check_lock_spinlock:
318712b2f30Ssthen LOCKRET(pthread_spin_init(&e->u.spinlock, PTHREAD_PROCESS_PRIVATE));
319712b2f30Ssthen break;
320712b2f30Ssthen case check_lock_rwlock:
321712b2f30Ssthen LOCKRET(pthread_rwlock_init(&e->u.rwlock, NULL));
322712b2f30Ssthen break;
323712b2f30Ssthen default:
324712b2f30Ssthen log_assert(0);
325712b2f30Ssthen }
326712b2f30Ssthen }
327712b2f30Ssthen
328712b2f30Ssthen /** delete prot items */
329712b2f30Ssthen static void
prot_clear(struct checked_lock * lock)330712b2f30Ssthen prot_clear(struct checked_lock* lock)
331712b2f30Ssthen {
332712b2f30Ssthen struct protected_area* p=lock->prot, *np;
333712b2f30Ssthen while(p) {
334712b2f30Ssthen np = p->next;
335712b2f30Ssthen free(p->hold);
336712b2f30Ssthen free(p);
337712b2f30Ssthen p = np;
338712b2f30Ssthen }
339712b2f30Ssthen }
340712b2f30Ssthen
341712b2f30Ssthen /** check if type is OK for the lock given */
342712b2f30Ssthen static void
checktype(enum check_lock_type type,struct checked_lock * lock,const char * func,const char * file,int line)343712b2f30Ssthen checktype(enum check_lock_type type, struct checked_lock* lock,
344712b2f30Ssthen const char* func, const char* file, int line)
345712b2f30Ssthen {
346712b2f30Ssthen if(!lock)
347712b2f30Ssthen fatal_exit("use of null/deleted lock at %s %s:%d",
348712b2f30Ssthen func, file, line);
349712b2f30Ssthen if(type != lock->type) {
350712b2f30Ssthen lock_error(lock, func, file, line, "wrong lock type");
351712b2f30Ssthen }
352712b2f30Ssthen }
353712b2f30Ssthen
354712b2f30Ssthen /** check if OK, free struct */
355712b2f30Ssthen void
checklock_destroy(enum check_lock_type type,struct checked_lock ** lock,const char * func,const char * file,int line)356712b2f30Ssthen checklock_destroy(enum check_lock_type type, struct checked_lock** lock,
357712b2f30Ssthen const char* func, const char* file, int line)
358712b2f30Ssthen {
359712b2f30Ssthen const size_t contention_interest = 1; /* promille contented locks */
360712b2f30Ssthen struct checked_lock* e;
361712b2f30Ssthen if(!lock)
362712b2f30Ssthen return;
363712b2f30Ssthen e = *lock;
364712b2f30Ssthen if(!e)
365712b2f30Ssthen return;
366712b2f30Ssthen checktype(type, e, func, file, line);
367712b2f30Ssthen
368712b2f30Ssthen /* check if delete is OK */
369712b2f30Ssthen acquire_locklock(e, func, file, line);
370712b2f30Ssthen if(e->hold_count != 0)
371712b2f30Ssthen lock_error(e, func, file, line, "delete while locked.");
372712b2f30Ssthen if(e->wait_count != 0)
373712b2f30Ssthen lock_error(e, func, file, line, "delete while waited on.");
374712b2f30Ssthen prot_check(e, func, file, line);
375712b2f30Ssthen *lock = NULL; /* use after free will fail */
376712b2f30Ssthen LOCKRET(pthread_mutex_unlock(&e->lock));
377712b2f30Ssthen
378712b2f30Ssthen /* contention, look at fraction in trouble. */
379712b2f30Ssthen if(e->history_count > 1 &&
380712b2f30Ssthen 1000*e->contention_count/e->history_count > contention_interest) {
381712b2f30Ssthen log_info("lock created %s %s %d has contention %u of %u (%d%%)",
382712b2f30Ssthen e->create_func, e->create_file, e->create_line,
383712b2f30Ssthen (unsigned int)e->contention_count,
384712b2f30Ssthen (unsigned int)e->history_count,
385712b2f30Ssthen (int)(100*e->contention_count/e->history_count));
386712b2f30Ssthen }
387712b2f30Ssthen
388712b2f30Ssthen /* delete it */
389712b2f30Ssthen LOCKRET(pthread_mutex_destroy(&e->lock));
390712b2f30Ssthen prot_clear(e);
391712b2f30Ssthen /* since nobody holds the lock - see check above, no need to unlink
392712b2f30Ssthen * from the thread-held locks list. */
393712b2f30Ssthen switch(e->type) {
394712b2f30Ssthen case check_lock_mutex:
395712b2f30Ssthen LOCKRET(pthread_mutex_destroy(&e->u.mutex));
396712b2f30Ssthen break;
397712b2f30Ssthen case check_lock_spinlock:
398712b2f30Ssthen LOCKRET(pthread_spin_destroy(&e->u.spinlock));
399712b2f30Ssthen break;
400712b2f30Ssthen case check_lock_rwlock:
401712b2f30Ssthen LOCKRET(pthread_rwlock_destroy(&e->u.rwlock));
402712b2f30Ssthen break;
403712b2f30Ssthen default:
404712b2f30Ssthen log_assert(0);
405712b2f30Ssthen }
406712b2f30Ssthen memset(e, 0, sizeof(struct checked_lock));
407712b2f30Ssthen free(e);
408712b2f30Ssthen }
409712b2f30Ssthen
410712b2f30Ssthen /** finish acquiring lock, shared between _(rd|wr||)lock() routines */
411712b2f30Ssthen static void
finish_acquire_lock(struct thr_check * thr,struct checked_lock * lock,const char * func,const char * file,int line)412712b2f30Ssthen finish_acquire_lock(struct thr_check* thr, struct checked_lock* lock,
413712b2f30Ssthen const char* func, const char* file, int line)
414712b2f30Ssthen {
415712b2f30Ssthen thr->waiting = NULL;
416712b2f30Ssthen lock->wait_count --;
417712b2f30Ssthen lock->holder = thr;
418712b2f30Ssthen lock->hold_count ++;
419712b2f30Ssthen lock->holder_func = func;
420712b2f30Ssthen lock->holder_file = file;
421712b2f30Ssthen lock->holder_line = line;
422712b2f30Ssthen ordercheck_locklock(thr, lock);
423712b2f30Ssthen
424712b2f30Ssthen /* insert in thread lock list, as first */
425712b2f30Ssthen lock->prev_held_lock[thr->num] = NULL;
426712b2f30Ssthen lock->next_held_lock[thr->num] = thr->holding_first;
427712b2f30Ssthen if(thr->holding_first)
428712b2f30Ssthen /* no need to lock it, since this thread already holds the
429712b2f30Ssthen * lock (since it is on this list) and we only edit thr->num
430712b2f30Ssthen * member in array. So it is safe. */
431712b2f30Ssthen thr->holding_first->prev_held_lock[thr->num] = lock;
432712b2f30Ssthen else thr->holding_last = lock;
433712b2f30Ssthen thr->holding_first = lock;
434712b2f30Ssthen }
435712b2f30Ssthen
436712b2f30Ssthen /**
437712b2f30Ssthen * Locking routine.
438712b2f30Ssthen * @param type: as passed by user.
439712b2f30Ssthen * @param lock: as passed by user.
440712b2f30Ssthen * @param func: caller location.
441712b2f30Ssthen * @param file: caller location.
442712b2f30Ssthen * @param line: caller location.
443712b2f30Ssthen * @param tryfunc: the pthread_mutex_trylock or similar function.
444712b2f30Ssthen * @param timedfunc: the pthread_mutex_timedlock or similar function.
445712b2f30Ssthen * Uses absolute timeout value.
446712b2f30Ssthen * @param arg: what to pass to tryfunc and timedlock.
447712b2f30Ssthen * @param exclusive: if lock must be exclusive (only one allowed).
448712b2f30Ssthen * @param getwr: if attempts to get writelock (or readlock) for rwlocks.
449712b2f30Ssthen */
450712b2f30Ssthen 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)451712b2f30Ssthen checklock_lockit(enum check_lock_type type, struct checked_lock* lock,
452712b2f30Ssthen const char* func, const char* file, int line,
453712b2f30Ssthen int (*tryfunc)(void*), int (*timedfunc)(void*, struct timespec*),
454712b2f30Ssthen void* arg, int exclusive, int getwr)
455712b2f30Ssthen {
456712b2f30Ssthen int err;
457712b2f30Ssthen int contend = 0;
458712b2f30Ssthen struct thr_check *thr = (struct thr_check*)pthread_getspecific(
459712b2f30Ssthen thr_debug_key);
460712b2f30Ssthen checktype(type, lock, func, file, line);
461712b2f30Ssthen if(!thr) lock_error(lock, func, file, line, "no thread info");
462712b2f30Ssthen
463712b2f30Ssthen acquire_locklock(lock, func, file, line);
464712b2f30Ssthen lock->wait_count ++;
465712b2f30Ssthen thr->waiting = lock;
466712b2f30Ssthen if(exclusive && lock->hold_count > 0 && lock->holder == thr)
467712b2f30Ssthen lock_error(lock, func, file, line, "thread already owns lock");
468712b2f30Ssthen if(type==check_lock_rwlock && getwr && lock->writeholder == thr)
469712b2f30Ssthen lock_error(lock, func, file, line, "thread already has wrlock");
470712b2f30Ssthen LOCKRET(pthread_mutex_unlock(&lock->lock));
471712b2f30Ssthen
472712b2f30Ssthen /* first try; if busy increase contention counter */
473712b2f30Ssthen if((err=tryfunc(arg))) {
474712b2f30Ssthen struct timespec to;
475712b2f30Ssthen if(err != EBUSY) log_err("trylock: %s", strerror(err));
476712b2f30Ssthen to.tv_sec = time(NULL) + CHECK_LOCK_TIMEOUT;
477712b2f30Ssthen to.tv_nsec = 0;
478712b2f30Ssthen if((err=timedfunc(arg, &to))) {
479712b2f30Ssthen if(err == ETIMEDOUT)
480712b2f30Ssthen lock_error(lock, func, file, line,
481712b2f30Ssthen "timeout possible deadlock");
482712b2f30Ssthen log_err("timedlock: %s", strerror(err));
483712b2f30Ssthen }
484712b2f30Ssthen contend ++;
485712b2f30Ssthen }
486712b2f30Ssthen /* got the lock */
487712b2f30Ssthen
488712b2f30Ssthen acquire_locklock(lock, func, file, line);
489712b2f30Ssthen lock->contention_count += contend;
490712b2f30Ssthen lock->history_count++;
491712b2f30Ssthen if(exclusive && lock->hold_count > 0)
492712b2f30Ssthen lock_error(lock, func, file, line, "got nonexclusive lock");
493712b2f30Ssthen if(type==check_lock_rwlock && getwr && lock->writeholder)
494712b2f30Ssthen lock_error(lock, func, file, line, "got nonexclusive wrlock");
495712b2f30Ssthen if(type==check_lock_rwlock && getwr)
496712b2f30Ssthen lock->writeholder = thr;
497712b2f30Ssthen /* check the memory areas for unauthorized changes,
498712b2f30Ssthen * between last unlock time and current lock time.
499712b2f30Ssthen * we check while holding the lock (threadsafe).
500712b2f30Ssthen */
501712b2f30Ssthen if(getwr || exclusive)
502712b2f30Ssthen prot_check(lock, func, file, line);
503712b2f30Ssthen finish_acquire_lock(thr, lock, func, file, line);
504712b2f30Ssthen LOCKRET(pthread_mutex_unlock(&lock->lock));
505712b2f30Ssthen }
506712b2f30Ssthen
507712b2f30Ssthen /** helper for rdlock: try */
try_rd(void * arg)508712b2f30Ssthen static int try_rd(void* arg)
509712b2f30Ssthen { return pthread_rwlock_tryrdlock((pthread_rwlock_t*)arg); }
510712b2f30Ssthen /** helper for rdlock: timed */
timed_rd(void * arg,struct timespec * to)511712b2f30Ssthen static int timed_rd(void* arg, struct timespec* to)
512712b2f30Ssthen { return pthread_rwlock_timedrdlock((pthread_rwlock_t*)arg, to); }
513712b2f30Ssthen
514712b2f30Ssthen /** check if OK, lock */
515712b2f30Ssthen void
checklock_rdlock(enum check_lock_type type,struct checked_lock * lock,const char * func,const char * file,int line)516712b2f30Ssthen checklock_rdlock(enum check_lock_type type, struct checked_lock* lock,
517712b2f30Ssthen const char* func, const char* file, int line)
518712b2f30Ssthen {
519712b2f30Ssthen if(key_deleted)
520712b2f30Ssthen return;
521712b2f30Ssthen
522*2682a17cSsthen if(verbose_locking && !(verbose_locking_not_loglock &&
523*2682a17cSsthen lock->create_thread == 0 && lock->create_instance == 0))
524*2682a17cSsthen fprintf(stderr, "checklock_rdlock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line);
525712b2f30Ssthen log_assert(type == check_lock_rwlock);
526712b2f30Ssthen checklock_lockit(type, lock, func, file, line,
527712b2f30Ssthen try_rd, timed_rd, &lock->u.rwlock, 0, 0);
528712b2f30Ssthen }
529712b2f30Ssthen
530712b2f30Ssthen /** helper for wrlock: try */
try_wr(void * arg)531712b2f30Ssthen static int try_wr(void* arg)
532712b2f30Ssthen { return pthread_rwlock_trywrlock((pthread_rwlock_t*)arg); }
533712b2f30Ssthen /** helper for wrlock: timed */
timed_wr(void * arg,struct timespec * to)534712b2f30Ssthen static int timed_wr(void* arg, struct timespec* to)
535712b2f30Ssthen { return pthread_rwlock_timedwrlock((pthread_rwlock_t*)arg, to); }
536712b2f30Ssthen
537712b2f30Ssthen /** check if OK, lock */
538712b2f30Ssthen void
checklock_wrlock(enum check_lock_type type,struct checked_lock * lock,const char * func,const char * file,int line)539712b2f30Ssthen checklock_wrlock(enum check_lock_type type, struct checked_lock* lock,
540712b2f30Ssthen const char* func, const char* file, int line)
541712b2f30Ssthen {
542712b2f30Ssthen if(key_deleted)
543712b2f30Ssthen return;
544712b2f30Ssthen log_assert(type == check_lock_rwlock);
545*2682a17cSsthen if(verbose_locking && !(verbose_locking_not_loglock &&
546*2682a17cSsthen lock->create_thread == 0 && lock->create_instance == 0))
547*2682a17cSsthen fprintf(stderr, "checklock_wrlock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line);
548712b2f30Ssthen checklock_lockit(type, lock, func, file, line,
549712b2f30Ssthen try_wr, timed_wr, &lock->u.rwlock, 0, 1);
550712b2f30Ssthen }
551712b2f30Ssthen
552712b2f30Ssthen /** helper for lock mutex: try */
try_mutex(void * arg)553712b2f30Ssthen static int try_mutex(void* arg)
554712b2f30Ssthen { return pthread_mutex_trylock((pthread_mutex_t*)arg); }
555712b2f30Ssthen /** helper for lock mutex: timed */
timed_mutex(void * arg,struct timespec * to)556712b2f30Ssthen static int timed_mutex(void* arg, struct timespec* to)
557712b2f30Ssthen { return pthread_mutex_timedlock((pthread_mutex_t*)arg, to); }
558712b2f30Ssthen
559712b2f30Ssthen /** helper for lock spinlock: try */
try_spinlock(void * arg)560712b2f30Ssthen static int try_spinlock(void* arg)
561712b2f30Ssthen { return pthread_spin_trylock((pthread_spinlock_t*)arg); }
562712b2f30Ssthen /** helper for lock spinlock: timed */
timed_spinlock(void * arg,struct timespec * to)563712b2f30Ssthen static int timed_spinlock(void* arg, struct timespec* to)
564712b2f30Ssthen {
565712b2f30Ssthen int err;
566712b2f30Ssthen /* spin for 5 seconds. (ouch for the CPU, but it beats forever) */
567712b2f30Ssthen while( (err=try_spinlock(arg)) == EBUSY) {
568712b2f30Ssthen #ifndef S_SPLINT_S
569712b2f30Ssthen if(time(NULL) >= to->tv_sec)
570712b2f30Ssthen return ETIMEDOUT;
571712b2f30Ssthen usleep(1000); /* in 1/1000000s of a second */
572712b2f30Ssthen #endif
573712b2f30Ssthen }
574712b2f30Ssthen return err;
575712b2f30Ssthen }
576712b2f30Ssthen
577712b2f30Ssthen /** check if OK, lock */
578712b2f30Ssthen void
checklock_lock(enum check_lock_type type,struct checked_lock * lock,const char * func,const char * file,int line)579712b2f30Ssthen checklock_lock(enum check_lock_type type, struct checked_lock* lock,
580712b2f30Ssthen const char* func, const char* file, int line)
581712b2f30Ssthen {
582712b2f30Ssthen if(key_deleted)
583712b2f30Ssthen return;
584712b2f30Ssthen log_assert(type != check_lock_rwlock);
585*2682a17cSsthen if(verbose_locking && !(verbose_locking_not_loglock &&
586*2682a17cSsthen lock->create_thread == 0 && lock->create_instance == 0))
587*2682a17cSsthen fprintf(stderr, "checklock_lock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line);
588712b2f30Ssthen switch(type) {
589712b2f30Ssthen case check_lock_mutex:
590712b2f30Ssthen checklock_lockit(type, lock, func, file, line,
591712b2f30Ssthen try_mutex, timed_mutex, &lock->u.mutex, 1, 0);
592712b2f30Ssthen break;
593712b2f30Ssthen case check_lock_spinlock:
594712b2f30Ssthen /* void* cast needed because 'volatile' on some OS */
595712b2f30Ssthen checklock_lockit(type, lock, func, file, line,
596712b2f30Ssthen try_spinlock, timed_spinlock,
597712b2f30Ssthen (void*)&lock->u.spinlock, 1, 0);
598712b2f30Ssthen break;
599712b2f30Ssthen default:
600712b2f30Ssthen log_assert(0);
601712b2f30Ssthen }
602712b2f30Ssthen }
603712b2f30Ssthen
604712b2f30Ssthen /** check if OK, unlock */
605712b2f30Ssthen void
checklock_unlock(enum check_lock_type type,struct checked_lock * lock,const char * func,const char * file,int line)606712b2f30Ssthen checklock_unlock(enum check_lock_type type, struct checked_lock* lock,
607712b2f30Ssthen const char* func, const char* file, int line)
608712b2f30Ssthen {
609712b2f30Ssthen struct thr_check *thr;
610712b2f30Ssthen if(key_deleted)
611712b2f30Ssthen return;
612712b2f30Ssthen thr = (struct thr_check*)pthread_getspecific(thr_debug_key);
613712b2f30Ssthen checktype(type, lock, func, file, line);
614712b2f30Ssthen if(!thr) lock_error(lock, func, file, line, "no thread info");
615712b2f30Ssthen
616712b2f30Ssthen acquire_locklock(lock, func, file, line);
617712b2f30Ssthen /* was this thread even holding this lock? */
618712b2f30Ssthen if(thr->holding_first != lock &&
619712b2f30Ssthen lock->prev_held_lock[thr->num] == NULL) {
620712b2f30Ssthen lock_error(lock, func, file, line, "unlock nonlocked lock");
621712b2f30Ssthen }
622712b2f30Ssthen if(lock->hold_count <= 0)
623712b2f30Ssthen lock_error(lock, func, file, line, "too many unlocks");
624712b2f30Ssthen
625*2682a17cSsthen if(verbose_locking && !(verbose_locking_not_loglock &&
626*2682a17cSsthen lock->create_thread == 0 && lock->create_instance == 0))
627*2682a17cSsthen fprintf(stderr, "checklock_unlock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line);
628*2682a17cSsthen
629712b2f30Ssthen /* store this point as last touched by */
630712b2f30Ssthen lock->holder = thr;
631712b2f30Ssthen lock->hold_count --;
632712b2f30Ssthen lock->holder_func = func;
633712b2f30Ssthen lock->holder_file = file;
634712b2f30Ssthen lock->holder_line = line;
635712b2f30Ssthen
636712b2f30Ssthen /* delete from thread holder list */
637712b2f30Ssthen /* no need to lock other lockstructs, because they are all on the
638712b2f30Ssthen * held-locks list, and this thread holds their locks.
639712b2f30Ssthen * we only touch the thr->num members, so it is safe. */
640712b2f30Ssthen if(thr->holding_first == lock)
641712b2f30Ssthen thr->holding_first = lock->next_held_lock[thr->num];
642712b2f30Ssthen if(thr->holding_last == lock)
643712b2f30Ssthen thr->holding_last = lock->prev_held_lock[thr->num];
644712b2f30Ssthen if(lock->next_held_lock[thr->num])
645712b2f30Ssthen lock->next_held_lock[thr->num]->prev_held_lock[thr->num] =
646712b2f30Ssthen lock->prev_held_lock[thr->num];
647712b2f30Ssthen if(lock->prev_held_lock[thr->num])
648712b2f30Ssthen lock->prev_held_lock[thr->num]->next_held_lock[thr->num] =
649712b2f30Ssthen lock->next_held_lock[thr->num];
650712b2f30Ssthen lock->next_held_lock[thr->num] = NULL;
651712b2f30Ssthen lock->prev_held_lock[thr->num] = NULL;
652712b2f30Ssthen
653712b2f30Ssthen if(type==check_lock_rwlock && lock->writeholder == thr) {
654712b2f30Ssthen lock->writeholder = NULL;
655712b2f30Ssthen prot_store(lock);
656712b2f30Ssthen } else if(type != check_lock_rwlock) {
657712b2f30Ssthen /* store memory areas that are protected, for later checks */
658712b2f30Ssthen prot_store(lock);
659712b2f30Ssthen }
660712b2f30Ssthen LOCKRET(pthread_mutex_unlock(&lock->lock));
661712b2f30Ssthen
662712b2f30Ssthen /* unlock it */
663712b2f30Ssthen switch(type) {
664712b2f30Ssthen case check_lock_mutex:
665712b2f30Ssthen LOCKRET(pthread_mutex_unlock(&lock->u.mutex));
666712b2f30Ssthen break;
667712b2f30Ssthen case check_lock_spinlock:
668712b2f30Ssthen LOCKRET(pthread_spin_unlock(&lock->u.spinlock));
669712b2f30Ssthen break;
670712b2f30Ssthen case check_lock_rwlock:
671712b2f30Ssthen LOCKRET(pthread_rwlock_unlock(&lock->u.rwlock));
672712b2f30Ssthen break;
673712b2f30Ssthen default:
674712b2f30Ssthen log_assert(0);
675712b2f30Ssthen }
676712b2f30Ssthen }
677712b2f30Ssthen
678712b2f30Ssthen /** open order info debug file, thr->num must be valid */
679712b2f30Ssthen static void
open_lockorder(struct thr_check * thr)680712b2f30Ssthen open_lockorder(struct thr_check* thr)
681712b2f30Ssthen {
682712b2f30Ssthen char buf[24];
683712b2f30Ssthen time_t t;
684712b2f30Ssthen snprintf(buf, sizeof(buf), "ublocktrace.%d", thr->num);
685712b2f30Ssthen thr->order_info = fopen(buf, "w");
686712b2f30Ssthen if(!thr->order_info)
687712b2f30Ssthen fatal_exit("could not open %s: %s", buf, strerror(errno));
688712b2f30Ssthen thr->locks_created = 0;
689712b2f30Ssthen t = time(NULL);
690712b2f30Ssthen /* write: <time_stamp> <runpid> <thread_num> */
691712b2f30Ssthen if(fwrite(&t, sizeof(t), 1, thr->order_info) != 1 ||
692712b2f30Ssthen fwrite(&thr->num, sizeof(thr->num), 1, thr->order_info) != 1 ||
693712b2f30Ssthen fwrite(&check_lock_pid, sizeof(check_lock_pid), 1,
694712b2f30Ssthen thr->order_info) != 1)
695712b2f30Ssthen log_err("fwrite: %s", strerror(errno));
696712b2f30Ssthen }
697712b2f30Ssthen
698712b2f30Ssthen /** checklock thread main, Inits thread structure */
checklock_main(void * arg)699712b2f30Ssthen static void* checklock_main(void* arg)
700712b2f30Ssthen {
701712b2f30Ssthen struct thr_check* thr = (struct thr_check*)arg;
702712b2f30Ssthen void* ret;
703712b2f30Ssthen thr->id = pthread_self();
704712b2f30Ssthen /* Hack to get same numbers as in log file */
705712b2f30Ssthen thr->num = *(int*)(thr->arg);
706712b2f30Ssthen log_assert(thr->num < THRDEBUG_MAX_THREADS);
707712b2f30Ssthen /* as an aside, due to this, won't work for libunbound bg thread */
708712b2f30Ssthen if(thread_infos[thr->num] != NULL)
709712b2f30Ssthen log_warn("thread warning, thr->num %d not NULL", thr->num);
710712b2f30Ssthen thread_infos[thr->num] = thr;
711712b2f30Ssthen LOCKRET(pthread_setspecific(thr_debug_key, thr));
712712b2f30Ssthen if(check_locking_order)
713712b2f30Ssthen open_lockorder(thr);
714712b2f30Ssthen ret = thr->func(thr->arg);
715712b2f30Ssthen thread_infos[thr->num] = NULL;
716712b2f30Ssthen if(check_locking_order)
717712b2f30Ssthen fclose(thr->order_info);
718712b2f30Ssthen free(thr);
719712b2f30Ssthen return ret;
720712b2f30Ssthen }
721712b2f30Ssthen
722712b2f30Ssthen /** init the main thread */
checklock_start(void)723712b2f30Ssthen void checklock_start(void)
724712b2f30Ssthen {
725712b2f30Ssthen if(key_deleted)
726712b2f30Ssthen return;
727712b2f30Ssthen if(!key_created) {
728712b2f30Ssthen struct thr_check* thisthr = (struct thr_check*)calloc(1,
729712b2f30Ssthen sizeof(struct thr_check));
730712b2f30Ssthen if(!thisthr)
731712b2f30Ssthen fatal_exit("thrcreate: out of memory");
732712b2f30Ssthen key_created = 1;
733712b2f30Ssthen check_lock_pid = getpid();
734712b2f30Ssthen LOCKRET(pthread_key_create(&thr_debug_key, NULL));
735712b2f30Ssthen LOCKRET(pthread_setspecific(thr_debug_key, thisthr));
736712b2f30Ssthen thread_infos[0] = thisthr;
737712b2f30Ssthen if(check_locking_order)
738712b2f30Ssthen open_lockorder(thisthr);
739712b2f30Ssthen }
740712b2f30Ssthen }
741712b2f30Ssthen
742712b2f30Ssthen /** stop checklocks */
checklock_stop(void)743712b2f30Ssthen void checklock_stop(void)
744712b2f30Ssthen {
745712b2f30Ssthen if(key_created) {
746712b2f30Ssthen int i;
747712b2f30Ssthen key_deleted = 1;
748712b2f30Ssthen if(check_locking_order)
749712b2f30Ssthen fclose(thread_infos[0]->order_info);
750712b2f30Ssthen free(thread_infos[0]);
751712b2f30Ssthen thread_infos[0] = NULL;
752712b2f30Ssthen for(i = 0; i < THRDEBUG_MAX_THREADS; i++)
753712b2f30Ssthen log_assert(thread_infos[i] == NULL);
754712b2f30Ssthen /* should have been cleaned up. */
755712b2f30Ssthen LOCKRET(pthread_key_delete(thr_debug_key));
756712b2f30Ssthen key_created = 0;
757712b2f30Ssthen }
758712b2f30Ssthen }
759712b2f30Ssthen
760712b2f30Ssthen /** allocate debug info and create thread */
761712b2f30Ssthen void
checklock_thrcreate(pthread_t * id,void * (* func)(void *),void * arg)762712b2f30Ssthen checklock_thrcreate(pthread_t* id, void* (*func)(void*), void* arg)
763712b2f30Ssthen {
764712b2f30Ssthen struct thr_check* thr = (struct thr_check*)calloc(1,
765712b2f30Ssthen sizeof(struct thr_check));
766712b2f30Ssthen if(!thr)
767712b2f30Ssthen fatal_exit("thrcreate: out of memory");
768712b2f30Ssthen if(!key_created) {
769712b2f30Ssthen checklock_start();
770712b2f30Ssthen }
771712b2f30Ssthen thr->func = func;
772712b2f30Ssthen thr->arg = arg;
773712b2f30Ssthen LOCKRET(pthread_create(id, NULL, checklock_main, thr));
774712b2f30Ssthen }
775712b2f30Ssthen
776712b2f30Ssthen /** count number of thread infos */
777712b2f30Ssthen static int
count_thread_infos(void)778712b2f30Ssthen count_thread_infos(void)
779712b2f30Ssthen {
780712b2f30Ssthen int cnt = 0;
781712b2f30Ssthen int i;
782712b2f30Ssthen for(i=0; i<THRDEBUG_MAX_THREADS; i++)
783712b2f30Ssthen if(thread_infos[i])
784712b2f30Ssthen cnt++;
785712b2f30Ssthen return cnt;
786712b2f30Ssthen }
787712b2f30Ssthen
788712b2f30Ssthen /** print lots of info on a lock */
789712b2f30Ssthen static void
lock_debug_info(struct checked_lock * lock)790712b2f30Ssthen lock_debug_info(struct checked_lock* lock)
791712b2f30Ssthen {
792712b2f30Ssthen if(!lock) return;
793712b2f30Ssthen log_info("+++ Lock %llx, %d %d create %s %s %d",
794712b2f30Ssthen (unsigned long long)(size_t)lock,
795712b2f30Ssthen lock->create_thread, lock->create_instance,
796712b2f30Ssthen lock->create_func, lock->create_file, lock->create_line);
797712b2f30Ssthen log_info("lock type: %s",
798712b2f30Ssthen (lock->type==check_lock_mutex)?"mutex": (
799712b2f30Ssthen (lock->type==check_lock_spinlock)?"spinlock": (
800712b2f30Ssthen (lock->type==check_lock_rwlock)?"rwlock": "badtype")));
801712b2f30Ssthen log_info("lock contention %u, history:%u, hold:%d, wait:%d",
802712b2f30Ssthen (unsigned)lock->contention_count, (unsigned)lock->history_count,
803712b2f30Ssthen lock->hold_count, lock->wait_count);
804712b2f30Ssthen log_info("last touch %s %s %d", lock->holder_func, lock->holder_file,
805712b2f30Ssthen lock->holder_line);
806712b2f30Ssthen log_info("holder thread %d, writeholder thread %d",
807712b2f30Ssthen lock->holder?lock->holder->num:-1,
808712b2f30Ssthen lock->writeholder?lock->writeholder->num:-1);
809712b2f30Ssthen }
810712b2f30Ssthen
811712b2f30Ssthen /** print debug locks held by a thread */
812712b2f30Ssthen static void
held_debug_info(struct thr_check * thr,struct checked_lock * lock)813712b2f30Ssthen held_debug_info(struct thr_check* thr, struct checked_lock* lock)
814712b2f30Ssthen {
815712b2f30Ssthen if(!lock) return;
816712b2f30Ssthen lock_debug_info(lock);
817712b2f30Ssthen held_debug_info(thr, lock->next_held_lock[thr->num]);
818712b2f30Ssthen }
819712b2f30Ssthen
820712b2f30Ssthen /** print debug info for a thread */
821712b2f30Ssthen static void
thread_debug_info(struct thr_check * thr)822712b2f30Ssthen thread_debug_info(struct thr_check* thr)
823712b2f30Ssthen {
824712b2f30Ssthen struct checked_lock* w = NULL;
825712b2f30Ssthen struct checked_lock* f = NULL;
826712b2f30Ssthen struct checked_lock* l = NULL;
827712b2f30Ssthen if(!thr) return;
828712b2f30Ssthen log_info("pthread id is %x", (int)thr->id);
829712b2f30Ssthen log_info("thread func is %llx", (unsigned long long)(size_t)thr->func);
830712b2f30Ssthen log_info("thread arg is %llx (%d)",
831712b2f30Ssthen (unsigned long long)(size_t)thr->arg,
832712b2f30Ssthen (thr->arg?*(int*)thr->arg:0));
833712b2f30Ssthen log_info("thread num is %d", thr->num);
834712b2f30Ssthen log_info("locks created %d", thr->locks_created);
835712b2f30Ssthen log_info("open file for lockinfo: %s",
836712b2f30Ssthen thr->order_info?"yes, flushing":"no");
837712b2f30Ssthen fflush(thr->order_info);
838712b2f30Ssthen w = thr->waiting;
839712b2f30Ssthen f = thr->holding_first;
840712b2f30Ssthen l = thr->holding_last;
841712b2f30Ssthen log_info("thread waiting for a lock: %s %llx", w?"yes":"no",
842712b2f30Ssthen (unsigned long long)(size_t)w);
843712b2f30Ssthen lock_debug_info(w);
844712b2f30Ssthen log_info("thread holding first: %s, last: %s", f?"yes":"no",
845712b2f30Ssthen l?"yes":"no");
846712b2f30Ssthen held_debug_info(thr, f);
847712b2f30Ssthen }
848712b2f30Ssthen
849712b2f30Ssthen static void
total_debug_info(void)850712b2f30Ssthen total_debug_info(void)
851712b2f30Ssthen {
852712b2f30Ssthen int i;
853712b2f30Ssthen log_info("checklocks: supervising %d threads.",
854712b2f30Ssthen count_thread_infos());
855712b2f30Ssthen if(!key_created) {
856712b2f30Ssthen log_info("No thread debug key created yet");
857712b2f30Ssthen }
858712b2f30Ssthen for(i=0; i<THRDEBUG_MAX_THREADS; i++) {
859712b2f30Ssthen if(thread_infos[i]) {
860712b2f30Ssthen log_info("*** Thread %d information: ***", i);
861712b2f30Ssthen thread_debug_info(thread_infos[i]);
862712b2f30Ssthen }
863712b2f30Ssthen }
864712b2f30Ssthen }
865712b2f30Ssthen
866712b2f30Ssthen /** signal handler for join timeout, Exits */
joinalarm(int ATTR_UNUSED (sig))867712b2f30Ssthen static RETSIGTYPE joinalarm(int ATTR_UNUSED(sig))
868712b2f30Ssthen {
869712b2f30Ssthen log_err("join thread timeout. hangup or deadlock. Info follows.");
870712b2f30Ssthen total_debug_info();
871712b2f30Ssthen fatal_exit("join thread timeout. hangup or deadlock.");
872712b2f30Ssthen }
873712b2f30Ssthen
874712b2f30Ssthen /** wait for thread with a timeout */
875712b2f30Ssthen void
checklock_thrjoin(pthread_t thread)876712b2f30Ssthen checklock_thrjoin(pthread_t thread)
877712b2f30Ssthen {
878712b2f30Ssthen /* wait with a timeout */
879712b2f30Ssthen if(signal(SIGALRM, joinalarm) == SIG_ERR)
880712b2f30Ssthen fatal_exit("signal(): %s", strerror(errno));
881712b2f30Ssthen (void)alarm(CHECK_JOIN_TIMEOUT);
882712b2f30Ssthen LOCKRET(pthread_join(thread, NULL));
883712b2f30Ssthen (void)alarm(0);
884712b2f30Ssthen }
885712b2f30Ssthen
886712b2f30Ssthen #endif /* USE_THREAD_DEBUG */
887