xref: /openbsd-src/usr.sbin/unbound/testcode/lock_verify.c (revision 437d28603d0290bfd9d9c531721c9139b03d4aa6)
1712b2f30Ssthen /*
2712b2f30Ssthen  * testcode/lock_verify.c - verifier program for lock traces, checks order.
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 /**
37712b2f30Ssthen  * \file
38712b2f30Ssthen  *
39712b2f30Ssthen  * This file checks the lock traces generated by checklock.c.
40712b2f30Ssthen  * Checks if locks are consistently locked in the same order.
41712b2f30Ssthen  * If not, this can lead to deadlock if threads execute the different
42712b2f30Ssthen  * ordering at the same time.
43712b2f30Ssthen  *
44712b2f30Ssthen  */
45712b2f30Ssthen 
46712b2f30Ssthen #include "config.h"
47712b2f30Ssthen #ifdef HAVE_TIME_H
48712b2f30Ssthen #include <time.h>
49712b2f30Ssthen #endif
50712b2f30Ssthen #include "util/log.h"
51712b2f30Ssthen #include "util/rbtree.h"
52712b2f30Ssthen #include "util/locks.h"
53712b2f30Ssthen #include "util/fptr_wlist.h"
54712b2f30Ssthen 
55712b2f30Ssthen /* --- data structures --- */
56712b2f30Ssthen struct lock_ref;
57712b2f30Ssthen 
58712b2f30Ssthen /** keep track of lock id in lock-verify application
59712b2f30Ssthen  * Also defined in smallapp/worker_cb.c for fptr_wlist encapsulation
60712b2f30Ssthen  * breakage (the security tests break encapsulation for this test app) */
61712b2f30Ssthen struct order_id {
62712b2f30Ssthen         /** the thread id that created it */
63712b2f30Ssthen         int thr;
64712b2f30Ssthen         /** the instance number of creation */
65712b2f30Ssthen         int instance;
66712b2f30Ssthen };
67712b2f30Ssthen 
68712b2f30Ssthen /** a lock */
69712b2f30Ssthen struct order_lock {
70712b2f30Ssthen 	/** rbnode in all tree */
71712b2f30Ssthen 	rbnode_type node;
72712b2f30Ssthen 	/** lock id */
73712b2f30Ssthen 	struct order_id id;
74712b2f30Ssthen 	/** the creation file */
75712b2f30Ssthen 	char* create_file;
76712b2f30Ssthen 	/** creation line */
77712b2f30Ssthen 	int create_line;
78712b2f30Ssthen 	/** set of all locks that are smaller than this one (locked earlier) */
79712b2f30Ssthen 	rbtree_type* smaller;
80712b2f30Ssthen 	/** during depthfirstsearch, this is a linked list of the stack
81712b2f30Ssthen 	 * of locks. points to the next lock bigger than this one. */
82712b2f30Ssthen 	struct lock_ref* dfs_next;
83712b2f30Ssthen 	/** if lock has been visited (all smaller locks have been compared to
84712b2f30Ssthen 	 * this lock), only need to compare this with all unvisited(bigger)
85712b2f30Ssthen 	 * locks */
86712b2f30Ssthen 	int visited;
87712b2f30Ssthen };
88712b2f30Ssthen 
89712b2f30Ssthen /** reference to a lock in a rbtree set */
90712b2f30Ssthen struct lock_ref {
91712b2f30Ssthen 	/** rbnode, key is an order_id ptr */
92712b2f30Ssthen 	rbnode_type node;
93712b2f30Ssthen 	/** the lock referenced */
94712b2f30Ssthen 	struct order_lock* lock;
95712b2f30Ssthen 	/** why is this ref */
96712b2f30Ssthen 	char* file;
97712b2f30Ssthen 	/** line number */
98712b2f30Ssthen 	int line;
99712b2f30Ssthen };
100712b2f30Ssthen 
101712b2f30Ssthen /** count of errors detected */
102712b2f30Ssthen static int errors_detected = 0;
103712b2f30Ssthen /** verbose? */
104712b2f30Ssthen static int verb = 0;
105712b2f30Ssthen 
106712b2f30Ssthen /** print program usage help */
107712b2f30Ssthen static void
usage(void)108712b2f30Ssthen usage(void)
109712b2f30Ssthen {
110712b2f30Ssthen 	printf("lock_verify <trace files>\n");
111712b2f30Ssthen }
112712b2f30Ssthen 
113712b2f30Ssthen /** read header entry.
114712b2f30Ssthen  * @param in: file to read header of.
115712b2f30Ssthen  * @return: False if it does not belong to the rest. */
116712b2f30Ssthen static int
read_header(FILE * in)117712b2f30Ssthen read_header(FILE* in)
118712b2f30Ssthen {
119712b2f30Ssthen 	time_t t;
120712b2f30Ssthen 	pid_t p;
121712b2f30Ssthen 	int thrno;
122712b2f30Ssthen 	static int have_values = 0;
123712b2f30Ssthen 	static time_t the_time;
124712b2f30Ssthen 	static pid_t the_pid;
125712b2f30Ssthen 	static int threads[256];
126712b2f30Ssthen 
127712b2f30Ssthen 	if(fread(&t, sizeof(t), 1, in) != 1 ||
128712b2f30Ssthen 		fread(&thrno, sizeof(thrno), 1, in) != 1 ||
129712b2f30Ssthen 		fread(&p, sizeof(p), 1, in) != 1) {
130712b2f30Ssthen 		fatal_exit("fread failed");
131712b2f30Ssthen 	}
132712b2f30Ssthen 	/* check these values are sorta OK */
133712b2f30Ssthen 	if(!have_values) {
134712b2f30Ssthen 		the_time = t;
135712b2f30Ssthen 		the_pid = p;
136712b2f30Ssthen 		memset(threads, 0, 256*sizeof(int));
137712b2f30Ssthen 		if(thrno >= 256) {
138712b2f30Ssthen 			fatal_exit("Thread number too big. %d", thrno);
139712b2f30Ssthen 		}
140712b2f30Ssthen 		threads[thrno] = 1;
141712b2f30Ssthen 		have_values = 1;
142712b2f30Ssthen 		printf(" trace %d from pid %u on %s", thrno,
143712b2f30Ssthen 			(unsigned)p, ctime(&t));
144712b2f30Ssthen 	} else {
145712b2f30Ssthen 		if(the_pid != p) {
146712b2f30Ssthen 			printf(" has pid %u, not %u. Skipped.\n",
147712b2f30Ssthen 				(unsigned)p, (unsigned)the_pid);
148712b2f30Ssthen 			return 0;
149712b2f30Ssthen 		}
150712b2f30Ssthen 		if(threads[thrno])
151712b2f30Ssthen 			fatal_exit("same threadno in two files");
152712b2f30Ssthen 		threads[thrno] = 1;
153712b2f30Ssthen 		if( abs((int)(the_time - t)) > 3600)
154712b2f30Ssthen 			fatal_exit("input files from different times: %u %u",
155712b2f30Ssthen 				(unsigned)the_time, (unsigned)t);
156712b2f30Ssthen 		printf(" trace of thread %u:%d\n", (unsigned)p, thrno);
157712b2f30Ssthen 	}
158712b2f30Ssthen 	return 1;
159712b2f30Ssthen }
160712b2f30Ssthen 
161712b2f30Ssthen /** max length of strings: filenames and function names. */
162712b2f30Ssthen #define STRMAX 1024
163712b2f30Ssthen /** read a string from file, false on error */
readup_str(char ** str,FILE * in)164712b2f30Ssthen static int readup_str(char** str, FILE* in)
165712b2f30Ssthen {
166712b2f30Ssthen 	char buf[STRMAX];
167712b2f30Ssthen 	int len = 0;
168712b2f30Ssthen 	int c;
169712b2f30Ssthen 	/* ends in zero */
170712b2f30Ssthen 	while( (c = fgetc(in)) != 0) {
171712b2f30Ssthen 		if(c == EOF)
172712b2f30Ssthen 			fatal_exit("eof in readstr, file too short");
173712b2f30Ssthen 		buf[len++] = c;
174712b2f30Ssthen 		if(len == STRMAX) {
175712b2f30Ssthen 			fatal_exit("string too long, bad file format");
176712b2f30Ssthen 		}
177712b2f30Ssthen 	}
178712b2f30Ssthen 	buf[len] = 0;
179712b2f30Ssthen 	*str = strdup(buf);
180*437d2860Ssthen 	if(!*str)
181*437d2860Ssthen 		fatal_exit("strdup failed: out of memory");
182712b2f30Ssthen 	return 1;
183712b2f30Ssthen }
184712b2f30Ssthen 
185712b2f30Ssthen /** read creation entry */
read_create(rbtree_type * all,FILE * in)186712b2f30Ssthen static void read_create(rbtree_type* all, FILE* in)
187712b2f30Ssthen {
188712b2f30Ssthen 	struct order_lock* o = calloc(1, sizeof(struct order_lock));
189712b2f30Ssthen 	if(!o) fatal_exit("malloc failure");
190712b2f30Ssthen 	if(fread(&o->id.thr, sizeof(int), 1, in) != 1 ||
191712b2f30Ssthen 	   fread(&o->id.instance, sizeof(int), 1, in) != 1 ||
192712b2f30Ssthen 	   !readup_str(&o->create_file, in) ||
193712b2f30Ssthen 	   fread(&o->create_line, sizeof(int), 1, in) != 1)
194712b2f30Ssthen 		fatal_exit("fread failed");
195712b2f30Ssthen 	o->smaller = rbtree_create(order_lock_cmp);
196712b2f30Ssthen 	o->node.key = &o->id;
197712b2f30Ssthen 	if(!rbtree_insert(all, &o->node)) {
198712b2f30Ssthen 		/* already inserted */
199712b2f30Ssthen 		struct order_lock* a = (struct order_lock*)rbtree_search(all,
200712b2f30Ssthen 			&o->id);
201712b2f30Ssthen 		log_assert(a);
202712b2f30Ssthen 		a->create_file = o->create_file;
203712b2f30Ssthen 		a->create_line = o->create_line;
204712b2f30Ssthen 		free(o->smaller);
205712b2f30Ssthen 		free(o);
206712b2f30Ssthen 		o = a;
207712b2f30Ssthen 	}
208712b2f30Ssthen 	if(verb) printf("read create %u %u %s %d\n",
209712b2f30Ssthen 		(unsigned)o->id.thr, (unsigned)o->id.instance,
210712b2f30Ssthen 		o->create_file, o->create_line);
211712b2f30Ssthen }
212712b2f30Ssthen 
213712b2f30Ssthen /** insert lock entry (empty) into list */
214712b2f30Ssthen static struct order_lock*
insert_lock(rbtree_type * all,struct order_id * id)215712b2f30Ssthen insert_lock(rbtree_type* all, struct order_id* id)
216712b2f30Ssthen {
217712b2f30Ssthen 	struct order_lock* o = calloc(1, sizeof(struct order_lock));
218712b2f30Ssthen 	if(!o) fatal_exit("malloc failure");
219712b2f30Ssthen 	o->smaller = rbtree_create(order_lock_cmp);
220712b2f30Ssthen 	o->id = *id;
221712b2f30Ssthen 	o->node.key = &o->id;
222712b2f30Ssthen 	if(!rbtree_insert(all, &o->node))
223712b2f30Ssthen 		fatal_exit("insert fail should not happen");
224712b2f30Ssthen 	return o;
225712b2f30Ssthen }
226712b2f30Ssthen 
227712b2f30Ssthen /** read lock entry */
read_lock(rbtree_type * all,FILE * in,int val)228712b2f30Ssthen static void read_lock(rbtree_type* all, FILE* in, int val)
229712b2f30Ssthen {
230712b2f30Ssthen 	struct order_id prev_id, now_id;
231712b2f30Ssthen 	struct lock_ref* ref;
232712b2f30Ssthen 	struct order_lock* prev, *now;
233712b2f30Ssthen 	ref = (struct lock_ref*)calloc(1, sizeof(struct lock_ref));
234712b2f30Ssthen 	if(!ref) fatal_exit("malloc failure");
235712b2f30Ssthen 	prev_id.thr = val;
236712b2f30Ssthen 	if(fread(&prev_id.instance, sizeof(int), 1, in) != 1 ||
237712b2f30Ssthen 	   fread(&now_id.thr, sizeof(int), 1, in) != 1 ||
238712b2f30Ssthen 	   fread(&now_id.instance, sizeof(int), 1, in) != 1 ||
239712b2f30Ssthen 	   !readup_str(&ref->file, in) ||
240712b2f30Ssthen 	   fread(&ref->line, sizeof(int), 1, in) != 1)
241712b2f30Ssthen 		fatal_exit("fread failed");
242712b2f30Ssthen 	if(verb) printf("read lock %u %u %u %u %s %d\n",
243712b2f30Ssthen 		(unsigned)prev_id.thr, (unsigned)prev_id.instance,
244712b2f30Ssthen 		(unsigned)now_id.thr, (unsigned)now_id.instance,
245712b2f30Ssthen 		ref->file, ref->line);
246712b2f30Ssthen 	/* find the two locks involved */
247712b2f30Ssthen 	prev = (struct order_lock*)rbtree_search(all, &prev_id);
248712b2f30Ssthen 	now = (struct order_lock*)rbtree_search(all, &now_id);
249712b2f30Ssthen 	/* if not there - insert 'em */
250712b2f30Ssthen 	if(!prev) prev = insert_lock(all, &prev_id);
251712b2f30Ssthen 	if(!now) now = insert_lock(all, &now_id);
252712b2f30Ssthen 	ref->lock = prev;
253712b2f30Ssthen 	ref->node.key = &prev->id;
254712b2f30Ssthen 	if(!rbtree_insert(now->smaller, &ref->node)) {
255712b2f30Ssthen 		free(ref->file);
256712b2f30Ssthen 		free(ref);
257712b2f30Ssthen 	}
258712b2f30Ssthen }
259712b2f30Ssthen 
260712b2f30Ssthen /** read input file */
readinput(rbtree_type * all,char * file)261712b2f30Ssthen static void readinput(rbtree_type* all, char* file)
262712b2f30Ssthen {
263712b2f30Ssthen 	FILE *in = fopen(file, "r");
264712b2f30Ssthen 	int fst;
265712b2f30Ssthen 	if(!in) {
266712b2f30Ssthen 		perror(file);
267712b2f30Ssthen 		exit(1);
268712b2f30Ssthen 	}
269712b2f30Ssthen 	printf("file %s", file);
270712b2f30Ssthen 	if(!read_header(in)) {
271712b2f30Ssthen 		fclose(in);
272712b2f30Ssthen 		return;
273712b2f30Ssthen 	}
274712b2f30Ssthen 	while(fread(&fst, sizeof(fst), 1, in) == 1) {
275712b2f30Ssthen 		if(fst == -1)
276712b2f30Ssthen 			read_create(all, in);
277712b2f30Ssthen 		else	read_lock(all, in, fst);
278712b2f30Ssthen 	}
279712b2f30Ssthen 	fclose(in);
280712b2f30Ssthen }
281712b2f30Ssthen 
282712b2f30Ssthen /** print cycle message */
found_cycle(struct lock_ref * visit,int level)283712b2f30Ssthen static void found_cycle(struct lock_ref* visit, int level)
284712b2f30Ssthen {
285712b2f30Ssthen 	struct lock_ref* p;
286712b2f30Ssthen 	int i = 0;
287712b2f30Ssthen 	errors_detected++;
288712b2f30Ssthen 	printf("Found inconsistent locking order of length %d\n", level);
289712b2f30Ssthen 	printf("for lock %d %d created %s %d\n",
290712b2f30Ssthen 		visit->lock->id.thr, visit->lock->id.instance,
291712b2f30Ssthen 		visit->lock->create_file, visit->lock->create_line);
292712b2f30Ssthen 	printf("sequence is:\n");
293712b2f30Ssthen 	p = visit;
294712b2f30Ssthen 	while(p) {
295712b2f30Ssthen 		struct order_lock* next =
296712b2f30Ssthen 			p->lock->dfs_next?p->lock->dfs_next->lock:visit->lock;
297712b2f30Ssthen 		printf("[%d] is locked at line %s %d before lock %d %d\n",
298712b2f30Ssthen 			i, p->file, p->line, next->id.thr, next->id.instance);
299712b2f30Ssthen 		printf("[%d] lock %d %d is created at %s %d\n",
300712b2f30Ssthen 			i, next->id.thr, next->id.instance,
301712b2f30Ssthen 			next->create_file, next->create_line);
302712b2f30Ssthen 		i++;
303712b2f30Ssthen 		p = p->lock->dfs_next;
304712b2f30Ssthen 		if(p && p->lock == visit->lock)
305712b2f30Ssthen 			break;
306712b2f30Ssthen 	}
307712b2f30Ssthen }
308712b2f30Ssthen 
309712b2f30Ssthen /** Detect cycle by comparing visited now with all (unvisited) bigger nodes */
detect_cycle(struct lock_ref * visit,struct lock_ref * from)310712b2f30Ssthen static int detect_cycle(struct lock_ref* visit, struct lock_ref* from)
311712b2f30Ssthen {
312712b2f30Ssthen 	struct lock_ref* p = from;
313712b2f30Ssthen 	while(p) {
314712b2f30Ssthen 		if(p->lock == visit->lock)
315712b2f30Ssthen 			return 1;
316712b2f30Ssthen 		p = p->lock->dfs_next;
317712b2f30Ssthen 	}
318712b2f30Ssthen 	return 0;
319712b2f30Ssthen }
320712b2f30Ssthen 
321712b2f30Ssthen /** recursive function to depth first search for cycles.
322712b2f30Ssthen  * @param visit: the lock visited at this step.
323712b2f30Ssthen  *	its dfs_next pointer gives the visited lock up in recursion.
324712b2f30Ssthen  * 	same as lookfor at level 0.
325712b2f30Ssthen  * @param level: depth of recursion. 0 is start.
326712b2f30Ssthen  * @param from: search for matches from unvisited node upwards.
327712b2f30Ssthen  */
search_cycle(struct lock_ref * visit,int level,struct lock_ref * from)328712b2f30Ssthen static void search_cycle(struct lock_ref* visit, int level,
329712b2f30Ssthen 	struct lock_ref* from)
330712b2f30Ssthen {
331712b2f30Ssthen 	struct lock_ref* ref;
332712b2f30Ssthen 	/* check for cycle */
333712b2f30Ssthen 	if(detect_cycle(visit, from) && level != 0) {
334712b2f30Ssthen 		found_cycle(visit, level);
335712b2f30Ssthen 		fatal_exit("found lock order cycle");
336712b2f30Ssthen 	}
337712b2f30Ssthen 	/* recurse */
338712b2f30Ssthen 	if(!visit->lock->visited)
339712b2f30Ssthen 		from = visit;
340712b2f30Ssthen 	if(verb > 1) fprintf(stderr, "[%d] visit lock %u %u %s %d\n", level,
341712b2f30Ssthen 			(unsigned)visit->lock->id.thr,
342712b2f30Ssthen 			(unsigned)visit->lock->id.instance,
343712b2f30Ssthen 			visit->lock->create_file, visit->lock->create_line);
344712b2f30Ssthen 	RBTREE_FOR(ref, struct lock_ref*, visit->lock->smaller) {
345712b2f30Ssthen 		ref->lock->dfs_next = visit;
346712b2f30Ssthen 		search_cycle(ref, level+1, from);
347712b2f30Ssthen 	}
348712b2f30Ssthen 	visit->lock->visited = 1;
349712b2f30Ssthen }
350712b2f30Ssthen 
351712b2f30Ssthen /** Check ordering of one lock */
check_order_lock(struct order_lock * lock)352712b2f30Ssthen static void check_order_lock(struct order_lock* lock)
353712b2f30Ssthen {
354712b2f30Ssthen 	struct lock_ref start;
355712b2f30Ssthen 	if(lock->visited) return;
356712b2f30Ssthen 
357712b2f30Ssthen 	start.node.key = &lock->id;
358712b2f30Ssthen 	start.lock = lock;
359712b2f30Ssthen 	start.file = lock->create_file;
360712b2f30Ssthen 	start.line = lock->create_line;
361712b2f30Ssthen 
362712b2f30Ssthen 	if(!lock->create_file)
363712b2f30Ssthen 		log_err("lock %u %u does not have create info",
364712b2f30Ssthen 			(unsigned)lock->id.thr, (unsigned)lock->id.instance);
365712b2f30Ssthen 
366712b2f30Ssthen 	/* depth first search to find cycle with this lock at head */
367712b2f30Ssthen 	lock->dfs_next = NULL;
368712b2f30Ssthen 	search_cycle(&start, 0, &start);
369712b2f30Ssthen }
370712b2f30Ssthen 
371712b2f30Ssthen /** Check ordering of locks */
check_order(rbtree_type * all_locks)372712b2f30Ssthen static void check_order(rbtree_type* all_locks)
373712b2f30Ssthen {
374712b2f30Ssthen 	/* check each lock */
375712b2f30Ssthen 	struct order_lock* lock;
376712b2f30Ssthen 	int i=0;
377712b2f30Ssthen 	RBTREE_FOR(lock, struct order_lock*, all_locks) {
378712b2f30Ssthen 		if(verb)
379712b2f30Ssthen 		    printf("[%d/%d] Checking lock %d %d %s %d\n",
380712b2f30Ssthen 			i, (int)all_locks->count,
381712b2f30Ssthen 			lock->id.thr, lock->id.instance,
382712b2f30Ssthen 			lock->create_file, lock->create_line);
383712b2f30Ssthen 		else if (i % ((all_locks->count/75)<1?1:all_locks->count/75)
384712b2f30Ssthen 			== 0)
385712b2f30Ssthen 		    fprintf(stderr, ".");
386712b2f30Ssthen 		i++;
387712b2f30Ssthen 		check_order_lock(lock);
388712b2f30Ssthen 	}
389712b2f30Ssthen 	fprintf(stderr, "\n");
390712b2f30Ssthen }
391712b2f30Ssthen 
39283152a15Ssthen /** delete lock ref */
dellockref(rbnode_type * node,void * ATTR_UNUSED (arg))39383152a15Ssthen static void dellockref(rbnode_type* node, void* ATTR_UNUSED(arg))
39483152a15Ssthen {
39583152a15Ssthen 	struct lock_ref* o = (struct lock_ref*)node;
39683152a15Ssthen 	if(!o) return;
39783152a15Ssthen 	free(o->file);
39883152a15Ssthen 	free(o);
39983152a15Ssthen }
40083152a15Ssthen 
40183152a15Ssthen /** delete lock node */
delnode(rbnode_type * node,void * ATTR_UNUSED (arg))40283152a15Ssthen static void delnode(rbnode_type* node, void* ATTR_UNUSED(arg))
40383152a15Ssthen {
40483152a15Ssthen 	struct order_lock* o = (struct order_lock*)node;
40583152a15Ssthen 	if(!o) return;
40683152a15Ssthen 	free(o->create_file);
40783152a15Ssthen 	if(o->smaller) {
40883152a15Ssthen 		traverse_postorder(o->smaller, &dellockref, NULL);
40983152a15Ssthen 		free(o->smaller);
41083152a15Ssthen 	}
41183152a15Ssthen 	free(o);
41283152a15Ssthen }
41383152a15Ssthen 
41483152a15Ssthen /** delete allocated memory */
locks_free(rbtree_type * all_locks)41583152a15Ssthen static void locks_free(rbtree_type* all_locks)
41683152a15Ssthen {
41783152a15Ssthen 	if(!all_locks)
41883152a15Ssthen 		return;
41983152a15Ssthen 	traverse_postorder(all_locks, &delnode, NULL);
42083152a15Ssthen 	free(all_locks);
42183152a15Ssthen }
42283152a15Ssthen 
423712b2f30Ssthen /** main program to verify all traces passed */
424712b2f30Ssthen int
main(int argc,char * argv[])425712b2f30Ssthen main(int argc, char* argv[])
426712b2f30Ssthen {
427712b2f30Ssthen 	rbtree_type* all_locks;
428712b2f30Ssthen 	int i;
429712b2f30Ssthen 	time_t starttime = time(NULL);
430712b2f30Ssthen #ifdef USE_THREAD_DEBUG
431712b2f30Ssthen 	/* do not overwrite the ublocktrace files with the ones generated
432712b2f30Ssthen 	 * by this program (i.e. when the log code creates a lock) */
433712b2f30Ssthen 	check_locking_order = 0;
434712b2f30Ssthen #endif
435712b2f30Ssthen 	if(argc <= 1) {
436712b2f30Ssthen 		usage();
437712b2f30Ssthen 		return 1;
438712b2f30Ssthen 	}
43983152a15Ssthen 	checklock_start();
440712b2f30Ssthen 	log_init(NULL, 0, NULL);
441712b2f30Ssthen 	log_ident_set("lock-verify");
442712b2f30Ssthen 	/* init */
443712b2f30Ssthen 	all_locks = rbtree_create(order_lock_cmp);
444712b2f30Ssthen 	errors_detected = 0;
445712b2f30Ssthen 
446712b2f30Ssthen 	/* read the input files */
447712b2f30Ssthen 	for(i=1; i<argc; i++) {
448712b2f30Ssthen 		readinput(all_locks, argv[i]);
449712b2f30Ssthen 	}
450712b2f30Ssthen 
451712b2f30Ssthen 	/* check ordering */
452712b2f30Ssthen 	check_order(all_locks);
453712b2f30Ssthen 
454712b2f30Ssthen 	/* do not free a thing, OS will do it */
455712b2f30Ssthen 	printf("checked %d locks in %d seconds with %d errors.\n",
456712b2f30Ssthen 		(int)all_locks->count, (int)(time(NULL)-starttime),
457712b2f30Ssthen 		errors_detected);
45883152a15Ssthen 	locks_free(all_locks);
459712b2f30Ssthen 	if(errors_detected) return 1;
460712b2f30Ssthen 	return 0;
461712b2f30Ssthen }
462