]>
Commit | Line | Data |
---|---|---|
89c4ed63 A |
1 | /** |
2 | * testcode/checklocks.h - wrapper on locks that checks access. | |
3 | * | |
4 | * Copyright (c) 2007, NLnet Labs. All rights reserved. | |
5 | * | |
6 | * This software is open source. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * | |
12 | * Redistributions of source code must retain the above copyright notice, | |
13 | * this list of conditions and the following disclaimer. | |
14 | * | |
15 | * Redistributions in binary form must reproduce the above copyright notice, | |
16 | * this list of conditions and the following disclaimer in the documentation | |
17 | * and/or other materials provided with the distribution. | |
18 | * | |
19 | * Neither the name of the NLNET LABS nor the names of its contributors may | |
20 | * be used to endorse or promote products derived from this software without | |
21 | * specific prior written permission. | |
22 | * | |
23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
26 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
27 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
28 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | |
29 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
30 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
31 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
32 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
33 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
34 | */ | |
35 | ||
36 | #ifndef TESTCODE_CHECK_LOCKS_H | |
37 | #define TESTCODE_CHECK_LOCKS_H | |
38 | ||
39 | /** | |
40 | * \file | |
41 | * Locks that are checked. | |
42 | * | |
43 | * Holds information per lock and per thread. | |
44 | * That information is protected by a mutex (unchecked). | |
45 | * | |
46 | * Checks: | |
47 | * o which func, file, line created the lock. | |
48 | * o contention count, measures amount of contention on the lock. | |
49 | * o the memory region(s) that the lock protects are | |
50 | * memcmp'ed to ascertain no race conditions. | |
51 | * o checks that locks are unlocked properly (before deletion). | |
52 | * keeps which func, file, line that locked it. | |
53 | * o checks deadlocks with timeout so it can print errors for them. | |
54 | * | |
55 | * Limitations: | |
56 | * o Detects unprotected memory access when the lock is locked or freed, | |
57 | * which detects races only if they happen, and only if in protected | |
58 | * memory areas. | |
59 | * o Detects deadlocks by timeout, so approximately, as they happen. | |
60 | * o Does not check order of locking. | |
61 | * o Uses a lot of memory. | |
62 | * o The checks use locks themselves, changing scheduling, | |
63 | * thus changing what races you see. | |
64 | */ | |
65 | ||
66 | #ifdef USE_THREAD_DEBUG | |
67 | #ifndef HAVE_PTHREAD | |
68 | /* we need the *timed*lock() routines to use for deadlock detection. */ | |
69 | #error "Need pthreads for checked locks" | |
70 | #endif | |
71 | /******************* THREAD DEBUG ************************/ | |
72 | #include <pthread.h> | |
73 | ||
74 | /** How many threads to allocate for */ | |
75 | #define THRDEBUG_MAX_THREADS 32 /* threads */ | |
76 | /** do we check locking order */ | |
77 | extern int check_locking_order; | |
78 | ||
79 | /** | |
80 | * Protection memory area. | |
81 | * It is copied to a holding buffer to compare against later. | |
82 | * Note that it may encompass the lock structure. | |
83 | */ | |
84 | struct protected_area { | |
85 | /** where the memory region starts */ | |
86 | void* region; | |
87 | /** size of the region */ | |
88 | size_t size; | |
89 | /** backbuffer that holds a copy, of same size. */ | |
90 | void* hold; | |
91 | /** next protected area in list */ | |
92 | struct protected_area* next; | |
93 | }; | |
94 | ||
95 | /** | |
96 | * Per thread information for locking debug wrappers. | |
97 | */ | |
98 | struct thr_check { | |
99 | /** thread id */ | |
100 | pthread_t id; | |
101 | /** real thread func */ | |
102 | void* (*func)(void*); | |
103 | /** func user arg */ | |
104 | void* arg; | |
105 | /** number of thread in list structure */ | |
106 | int num; | |
107 | /** instance number - how many locks have been created by thread */ | |
108 | int locks_created; | |
109 | /** file to write locking order information to */ | |
110 | FILE* order_info; | |
111 | /** | |
112 | * List of locks that this thread is holding, double | |
113 | * linked list. The first element is the most recent lock acquired. | |
114 | * So it represents the stack of locks acquired. (of all types). | |
115 | */ | |
116 | struct checked_lock *holding_first, *holding_last; | |
117 | /** if the thread is currently waiting for a lock, which one */ | |
118 | struct checked_lock* waiting; | |
119 | }; | |
120 | ||
121 | /** | |
122 | * One structure for all types of locks. | |
123 | */ | |
124 | struct checked_lock { | |
125 | /** mutex for exclusive access to this structure */ | |
126 | pthread_mutex_t lock; | |
127 | /** list of memory regions protected by this checked lock */ | |
128 | struct protected_area* prot; | |
129 | /** where was this lock created */ | |
130 | const char* create_func, *create_file; | |
131 | /** where was this lock created */ | |
132 | int create_line; | |
133 | /** unique instance identifier */ | |
134 | int create_thread, create_instance; | |
135 | /** contention count */ | |
136 | size_t contention_count; | |
137 | /** number of times locked, ever */ | |
138 | size_t history_count; | |
139 | /** hold count (how many threads are holding this lock) */ | |
140 | int hold_count; | |
141 | /** how many threads are waiting for this lock */ | |
142 | int wait_count; | |
143 | /** who touched it last */ | |
144 | const char* holder_func, *holder_file; | |
145 | /** who touched it last */ | |
146 | int holder_line; | |
147 | /** who owns the lock now */ | |
148 | struct thr_check* holder; | |
149 | /** for rwlocks, the writelock holder */ | |
150 | struct thr_check* writeholder; | |
151 | ||
152 | /** next lock a thread is holding (less recent) */ | |
153 | struct checked_lock* next_held_lock[THRDEBUG_MAX_THREADS]; | |
154 | /** prev lock a thread is holding (more recent) */ | |
155 | struct checked_lock* prev_held_lock[THRDEBUG_MAX_THREADS]; | |
156 | ||
157 | /** type of lock */ | |
158 | enum check_lock_type { | |
159 | /** basic mutex */ | |
160 | check_lock_mutex, | |
161 | /** fast spinlock */ | |
162 | check_lock_spinlock, | |
163 | /** rwlock */ | |
164 | check_lock_rwlock | |
165 | } type; | |
166 | /** the lock itself, see type to disambiguate the union */ | |
167 | union { | |
168 | /** mutex */ | |
169 | pthread_mutex_t mutex; | |
170 | /** spinlock */ | |
171 | pthread_spinlock_t spinlock; | |
172 | /** rwlock */ | |
173 | pthread_rwlock_t rwlock; | |
174 | } u; | |
175 | }; | |
176 | ||
177 | /** | |
178 | * Additional call for the user to specify what areas are protected | |
179 | * @param lock: the lock that protects the area. It can be inside the area. | |
180 | * The lock must be inited. Call with user lock. (any type). | |
181 | * It demangles the lock itself (struct checked_lock**). | |
182 | * @param area: ptr to mem. | |
183 | * @param size: length of area. | |
184 | * You can call it multiple times with the same lock to give several areas. | |
185 | * Call it when you are done initialising the area, since it will be copied | |
186 | * at this time and protected right away against unauthorised changes until | |
187 | * the next lock() call is done. | |
188 | */ | |
189 | void lock_protect(void* lock, void* area, size_t size); | |
190 | ||
191 | /** | |
192 | * Remove protected area from lock. | |
193 | * No need to call this when deleting the lock. | |
194 | * @param lock: the lock, any type, (struct checked_lock**). | |
195 | * @param area: pointer to memory. | |
196 | */ | |
197 | void lock_unprotect(void* lock, void* area); | |
198 | ||
199 | /** | |
200 | * Get memory associated with a checked lock | |
201 | * @param lock: the checked lock, any type. (struct checked_lock**). | |
202 | * @return: in bytes, including protected areas. | |
203 | */ | |
204 | size_t lock_get_mem(void* lock); | |
205 | ||
206 | /** | |
207 | * Initialise checklock. Sets up internal debug structures. | |
208 | */ | |
209 | void checklock_start(void); | |
210 | ||
211 | /** | |
212 | * Cleanup internal debug state. | |
213 | */ | |
214 | void checklock_stop(void); | |
215 | ||
216 | /** | |
217 | * Init locks. | |
218 | * @param type: what type of lock this is. | |
219 | * @param lock: ptr to user alloced ptr structure. This is inited. | |
220 | * So an alloc is done and the ptr is stored as result. | |
221 | * @param func: caller function name. | |
222 | * @param file: caller file name. | |
223 | * @param line: caller line number. | |
224 | */ | |
225 | void checklock_init(enum check_lock_type type, struct checked_lock** lock, | |
226 | const char* func, const char* file, int line); | |
227 | ||
228 | /** | |
229 | * Destroy locks. Free the structure. | |
230 | * @param type: what type of lock this is. | |
231 | * @param lock: ptr to user alloced structure. This is destroyed. | |
232 | * @param func: caller function name. | |
233 | * @param file: caller file name. | |
234 | * @param line: caller line number. | |
235 | */ | |
236 | void checklock_destroy(enum check_lock_type type, struct checked_lock** lock, | |
237 | const char* func, const char* file, int line); | |
238 | ||
239 | /** | |
240 | * Acquire readlock. | |
241 | * @param type: what type of lock this is. Had better be a rwlock. | |
242 | * @param lock: ptr to lock. | |
243 | * @param func: caller function name. | |
244 | * @param file: caller file name. | |
245 | * @param line: caller line number. | |
246 | */ | |
247 | void checklock_rdlock(enum check_lock_type type, struct checked_lock* lock, | |
248 | const char* func, const char* file, int line); | |
249 | ||
250 | /** | |
251 | * Acquire writelock. | |
252 | * @param type: what type of lock this is. Had better be a rwlock. | |
253 | * @param lock: ptr to lock. | |
254 | * @param func: caller function name. | |
255 | * @param file: caller file name. | |
256 | * @param line: caller line number. | |
257 | */ | |
258 | void checklock_wrlock(enum check_lock_type type, struct checked_lock* lock, | |
259 | const char* func, const char* file, int line); | |
260 | ||
261 | /** | |
262 | * Locks. | |
263 | * @param type: what type of lock this is. Had better be mutex or spinlock. | |
264 | * @param lock: the lock. | |
265 | * @param func: caller function name. | |
266 | * @param file: caller file name. | |
267 | * @param line: caller line number. | |
268 | */ | |
269 | void checklock_lock(enum check_lock_type type, struct checked_lock* lock, | |
270 | const char* func, const char* file, int line); | |
271 | ||
272 | /** | |
273 | * Unlocks. | |
274 | * @param type: what type of lock this is. | |
275 | * @param lock: the lock. | |
276 | * @param func: caller function name. | |
277 | * @param file: caller file name. | |
278 | * @param line: caller line number. | |
279 | */ | |
280 | void checklock_unlock(enum check_lock_type type, struct checked_lock* lock, | |
281 | const char* func, const char* file, int line); | |
282 | ||
283 | /** | |
284 | * Create thread. | |
285 | * @param thr: Thread id, where to store result. | |
286 | * @param func: thread start function. | |
287 | * @param arg: user argument. | |
288 | */ | |
289 | void checklock_thrcreate(pthread_t* thr, void* (*func)(void*), void* arg); | |
290 | ||
291 | /** | |
292 | * Wait for thread to exit. Returns thread return value. | |
293 | * @param thread: thread to wait for. | |
294 | */ | |
295 | void checklock_thrjoin(pthread_t thread); | |
296 | ||
297 | /** structures to enable compiler type checking on the locks. | |
298 | * Also the pointer makes it so that the lock can be part of the protected | |
299 | * region without any possible problem (since the ptr will stay the same.) | |
300 | * i.e. there can be contention and readlocks stored in checked_lock, while | |
301 | * the protected area stays the same, even though it contains (ptr to) lock. | |
302 | */ | |
303 | struct checked_lock_rw { struct checked_lock* c_rw; }; | |
304 | /** structures to enable compiler type checking on the locks. */ | |
305 | struct checked_lock_mutex { struct checked_lock* c_m; }; | |
306 | /** structures to enable compiler type checking on the locks. */ | |
307 | struct checked_lock_spl { struct checked_lock* c_spl; }; | |
308 | ||
309 | /** debugging rwlock */ | |
310 | typedef struct checked_lock_rw lock_rw_t; | |
311 | #define lock_rw_init(lock) checklock_init(check_lock_rwlock, &((lock)->c_rw), __func__, __FILE__, __LINE__) | |
312 | #define lock_rw_destroy(lock) checklock_destroy(check_lock_rwlock, &((lock)->c_rw), __func__, __FILE__, __LINE__) | |
313 | #define lock_rw_rdlock(lock) checklock_rdlock(check_lock_rwlock, (lock)->c_rw, __func__, __FILE__, __LINE__) | |
314 | #define lock_rw_wrlock(lock) checklock_wrlock(check_lock_rwlock, (lock)->c_rw, __func__, __FILE__, __LINE__) | |
315 | #define lock_rw_unlock(lock) checklock_unlock(check_lock_rwlock, (lock)->c_rw, __func__, __FILE__, __LINE__) | |
316 | ||
317 | /** debugging mutex */ | |
318 | typedef struct checked_lock_mutex lock_basic_t; | |
319 | #define lock_basic_init(lock) checklock_init(check_lock_mutex, &((lock)->c_m), __func__, __FILE__, __LINE__) | |
320 | #define lock_basic_destroy(lock) checklock_destroy(check_lock_mutex, &((lock)->c_m), __func__, __FILE__, __LINE__) | |
321 | #define lock_basic_lock(lock) checklock_lock(check_lock_mutex, (lock)->c_m, __func__, __FILE__, __LINE__) | |
322 | #define lock_basic_unlock(lock) checklock_unlock(check_lock_mutex, (lock)->c_m, __func__, __FILE__, __LINE__) | |
323 | ||
324 | /** debugging spinlock */ | |
325 | typedef struct checked_lock_spl lock_quick_t; | |
326 | #define lock_quick_init(lock) checklock_init(check_lock_spinlock, &((lock)->c_spl), __func__, __FILE__, __LINE__) | |
327 | #define lock_quick_destroy(lock) checklock_destroy(check_lock_spinlock, &((lock)->c_spl), __func__, __FILE__, __LINE__) | |
328 | #define lock_quick_lock(lock) checklock_lock(check_lock_spinlock, (lock)->c_spl, __func__, __FILE__, __LINE__) | |
329 | #define lock_quick_unlock(lock) checklock_unlock(check_lock_spinlock, (lock)->c_spl, __func__, __FILE__, __LINE__) | |
330 | ||
331 | /** we use the pthread id, our thr_check structure is kept behind the scenes */ | |
332 | typedef pthread_t ub_thread_t; | |
333 | #define ub_thread_create(thr, func, arg) checklock_thrcreate(thr, func, arg) | |
334 | #define ub_thread_self() pthread_self() | |
335 | #define ub_thread_join(thread) checklock_thrjoin(thread) | |
336 | ||
337 | typedef pthread_key_t ub_thread_key_t; | |
338 | #define ub_thread_key_create(key, f) LOCKRET(pthread_key_create(key, f)) | |
339 | #define ub_thread_key_set(key, v) LOCKRET(pthread_setspecific(key, v)) | |
340 | #define ub_thread_key_get(key) pthread_getspecific(key) | |
341 | ||
342 | #endif /* USE_THREAD_DEBUG */ | |
343 | #endif /* TESTCODE_CHECK_LOCKS_H */ |