xref: /freebsd-src/sys/contrib/openzfs/tests/zfs-tests/cmd/btree_test.c (revision 4e8d558c9d1cf3e7e424e3fb123b01979c3d57f2)
1716fd348SMartin Matuska /*
2716fd348SMartin Matuska  * This file and its contents are supplied under the terms of the
3716fd348SMartin Matuska  * Common Development and Distribution License ("CDDL"), version 1.0.
4716fd348SMartin Matuska  * You may only use this file in accordance with the terms of version
5716fd348SMartin Matuska  * 1.0 of the CDDL.
6716fd348SMartin Matuska  *
7716fd348SMartin Matuska  * A full copy of the text of the CDDL should have accompanied this
8716fd348SMartin Matuska  * source.  A copy of the CDDL is also available via the Internet at
9716fd348SMartin Matuska  * http://www.illumos.org/license/CDDL.
10716fd348SMartin Matuska  */
11716fd348SMartin Matuska 
12716fd348SMartin Matuska /*
13716fd348SMartin Matuska  * Copyright (c) 2019 by Delphix. All rights reserved.
14716fd348SMartin Matuska  */
15716fd348SMartin Matuska 
16716fd348SMartin Matuska #include <stdio.h>
17716fd348SMartin Matuska #include <stdlib.h>
18be181ee2SMartin Matuska #include <string.h>
19716fd348SMartin Matuska #include <sys/avl.h>
20716fd348SMartin Matuska #include <sys/btree.h>
21716fd348SMartin Matuska #include <sys/time.h>
22716fd348SMartin Matuska #include <sys/resource.h>
23716fd348SMartin Matuska 
24716fd348SMartin Matuska #define	BUFSIZE 256
25716fd348SMartin Matuska 
26dbd5678dSMartin Matuska static int seed = 0;
27dbd5678dSMartin Matuska static int stress_timeout = 180;
28dbd5678dSMartin Matuska static int contents_frequency = 100;
29dbd5678dSMartin Matuska static int tree_limit = 64 * 1024;
30dbd5678dSMartin Matuska static boolean_t stress_only = B_FALSE;
31716fd348SMartin Matuska 
32716fd348SMartin Matuska static void
usage(int exit_value)33716fd348SMartin Matuska usage(int exit_value)
34716fd348SMartin Matuska {
35716fd348SMartin Matuska 	(void) fprintf(stderr, "Usage:\tbtree_test -n <test_name>\n");
36716fd348SMartin Matuska 	(void) fprintf(stderr, "\tbtree_test -s [-r <seed>] [-l <limit>] "
37716fd348SMartin Matuska 	    "[-t timeout>] [-c check_contents]\n");
38716fd348SMartin Matuska 	(void) fprintf(stderr, "\tbtree_test [-r <seed>] [-l <limit>] "
39716fd348SMartin Matuska 	    "[-t timeout>] [-c check_contents]\n");
40716fd348SMartin Matuska 	(void) fprintf(stderr, "\n    With the -n option, run the named "
41716fd348SMartin Matuska 	    "negative test. With the -s option,\n");
42716fd348SMartin Matuska 	(void) fprintf(stderr, "    run the stress test according to the "
43716fd348SMartin Matuska 	    "other options passed. With\n");
44716fd348SMartin Matuska 	(void) fprintf(stderr, "    neither, run all the positive tests, "
45716fd348SMartin Matuska 	    "including the stress test with\n");
46716fd348SMartin Matuska 	(void) fprintf(stderr, "    the default options.\n");
47716fd348SMartin Matuska 	(void) fprintf(stderr, "\n    Options that control the stress test\n");
48716fd348SMartin Matuska 	(void) fprintf(stderr, "\t-c stress iterations after which to compare "
49716fd348SMartin Matuska 	    "tree contents [default: 100]\n");
50716fd348SMartin Matuska 	(void) fprintf(stderr, "\t-l the largest value to allow in the tree "
51716fd348SMartin Matuska 	    "[default: 1M]\n");
52716fd348SMartin Matuska 	(void) fprintf(stderr, "\t-r random seed [default: from "
53716fd348SMartin Matuska 	    "gettimeofday()]\n");
54716fd348SMartin Matuska 	(void) fprintf(stderr, "\t-t seconds to let the stress test run "
55716fd348SMartin Matuska 	    "[default: 180]\n");
56716fd348SMartin Matuska 	exit(exit_value);
57716fd348SMartin Matuska }
58716fd348SMartin Matuska 
59716fd348SMartin Matuska typedef struct int_node {
60716fd348SMartin Matuska 	avl_node_t node;
61716fd348SMartin Matuska 	uint64_t data;
62716fd348SMartin Matuska } int_node_t;
63716fd348SMartin Matuska 
64716fd348SMartin Matuska /*
65716fd348SMartin Matuska  * Utility functions
66716fd348SMartin Matuska  */
67716fd348SMartin Matuska 
68716fd348SMartin Matuska static int
avl_compare(const void * v1,const void * v2)69716fd348SMartin Matuska avl_compare(const void *v1, const void *v2)
70716fd348SMartin Matuska {
71716fd348SMartin Matuska 	const int_node_t *n1 = v1;
72716fd348SMartin Matuska 	const int_node_t *n2 = v2;
73716fd348SMartin Matuska 	uint64_t a = n1->data;
74716fd348SMartin Matuska 	uint64_t b = n2->data;
75716fd348SMartin Matuska 
76716fd348SMartin Matuska 	return (TREE_CMP(a, b));
77716fd348SMartin Matuska }
78716fd348SMartin Matuska 
79716fd348SMartin Matuska static int
zfs_btree_compare(const void * v1,const void * v2)80716fd348SMartin Matuska zfs_btree_compare(const void *v1, const void *v2)
81716fd348SMartin Matuska {
82716fd348SMartin Matuska 	const uint64_t *a = v1;
83716fd348SMartin Matuska 	const uint64_t *b = v2;
84716fd348SMartin Matuska 
85716fd348SMartin Matuska 	return (TREE_CMP(*a, *b));
86716fd348SMartin Matuska }
87716fd348SMartin Matuska 
88716fd348SMartin Matuska static void
verify_contents(avl_tree_t * avl,zfs_btree_t * bt)89716fd348SMartin Matuska verify_contents(avl_tree_t *avl, zfs_btree_t *bt)
90716fd348SMartin Matuska {
91716fd348SMartin Matuska 	static int count = 0;
92716fd348SMartin Matuska 	zfs_btree_index_t bt_idx = {0};
93716fd348SMartin Matuska 	int_node_t *node;
94716fd348SMartin Matuska 	uint64_t *data;
95716fd348SMartin Matuska 
96716fd348SMartin Matuska 	boolean_t forward = count % 2 == 0 ? B_TRUE : B_FALSE;
97716fd348SMartin Matuska 	count++;
98716fd348SMartin Matuska 
99716fd348SMartin Matuska 	ASSERT3U(avl_numnodes(avl), ==, zfs_btree_numnodes(bt));
100716fd348SMartin Matuska 	if (forward == B_TRUE) {
101716fd348SMartin Matuska 		node = avl_first(avl);
102716fd348SMartin Matuska 		data = zfs_btree_first(bt, &bt_idx);
103716fd348SMartin Matuska 	} else {
104716fd348SMartin Matuska 		node = avl_last(avl);
105716fd348SMartin Matuska 		data = zfs_btree_last(bt, &bt_idx);
106716fd348SMartin Matuska 	}
107716fd348SMartin Matuska 
108716fd348SMartin Matuska 	while (node != NULL) {
109716fd348SMartin Matuska 		ASSERT3U(*data, ==, node->data);
110716fd348SMartin Matuska 		if (forward == B_TRUE) {
111716fd348SMartin Matuska 			data = zfs_btree_next(bt, &bt_idx, &bt_idx);
112716fd348SMartin Matuska 			node = AVL_NEXT(avl, node);
113716fd348SMartin Matuska 		} else {
114716fd348SMartin Matuska 			data = zfs_btree_prev(bt, &bt_idx, &bt_idx);
115716fd348SMartin Matuska 			node = AVL_PREV(avl, node);
116716fd348SMartin Matuska 		}
117716fd348SMartin Matuska 	}
118716fd348SMartin Matuska }
119716fd348SMartin Matuska 
120716fd348SMartin Matuska static void
verify_node(avl_tree_t * avl,zfs_btree_t * bt,int_node_t * node)121716fd348SMartin Matuska verify_node(avl_tree_t *avl, zfs_btree_t *bt, int_node_t *node)
122716fd348SMartin Matuska {
123716fd348SMartin Matuska 	zfs_btree_index_t bt_idx = {0};
124716fd348SMartin Matuska 	zfs_btree_index_t bt_idx2 = {0};
125716fd348SMartin Matuska 	int_node_t *inp;
126716fd348SMartin Matuska 	uint64_t data = node->data;
127716fd348SMartin Matuska 	uint64_t *rv = NULL;
128716fd348SMartin Matuska 
129716fd348SMartin Matuska 	ASSERT3U(avl_numnodes(avl), ==, zfs_btree_numnodes(bt));
130716fd348SMartin Matuska 	ASSERT3P((rv = (uint64_t *)zfs_btree_find(bt, &data, &bt_idx)), !=,
131716fd348SMartin Matuska 	    NULL);
132716fd348SMartin Matuska 	ASSERT3S(*rv, ==, data);
133716fd348SMartin Matuska 	ASSERT3P(zfs_btree_get(bt, &bt_idx), !=, NULL);
134716fd348SMartin Matuska 	ASSERT3S(data, ==, *(uint64_t *)zfs_btree_get(bt, &bt_idx));
135716fd348SMartin Matuska 
136716fd348SMartin Matuska 	if ((inp = AVL_NEXT(avl, node)) != NULL) {
137716fd348SMartin Matuska 		ASSERT3P((rv = zfs_btree_next(bt, &bt_idx, &bt_idx2)), !=,
138716fd348SMartin Matuska 		    NULL);
139716fd348SMartin Matuska 		ASSERT3P(rv, ==, zfs_btree_get(bt, &bt_idx2));
140716fd348SMartin Matuska 		ASSERT3S(inp->data, ==, *rv);
141716fd348SMartin Matuska 	} else {
142716fd348SMartin Matuska 		ASSERT3U(data, ==, *(uint64_t *)zfs_btree_last(bt, &bt_idx));
143716fd348SMartin Matuska 	}
144716fd348SMartin Matuska 
145716fd348SMartin Matuska 	if ((inp = AVL_PREV(avl, node)) != NULL) {
146716fd348SMartin Matuska 		ASSERT3P((rv = zfs_btree_prev(bt, &bt_idx, &bt_idx2)), !=,
147716fd348SMartin Matuska 		    NULL);
148716fd348SMartin Matuska 		ASSERT3P(rv, ==, zfs_btree_get(bt, &bt_idx2));
149716fd348SMartin Matuska 		ASSERT3S(inp->data, ==, *rv);
150716fd348SMartin Matuska 	} else {
151716fd348SMartin Matuska 		ASSERT3U(data, ==, *(uint64_t *)zfs_btree_first(bt, &bt_idx));
152716fd348SMartin Matuska 	}
153716fd348SMartin Matuska }
154716fd348SMartin Matuska 
155716fd348SMartin Matuska /*
156716fd348SMartin Matuska  * Tests
157716fd348SMartin Matuska  */
158716fd348SMartin Matuska 
159716fd348SMartin Matuska /* Verify that zfs_btree_find works correctly with a NULL index. */
160716fd348SMartin Matuska static int
find_without_index(zfs_btree_t * bt,char * why)161716fd348SMartin Matuska find_without_index(zfs_btree_t *bt, char *why)
162716fd348SMartin Matuska {
163716fd348SMartin Matuska 	u_longlong_t *p, i = 12345;
164716fd348SMartin Matuska 
165716fd348SMartin Matuska 	zfs_btree_add(bt, &i);
166716fd348SMartin Matuska 	if ((p = (u_longlong_t *)zfs_btree_find(bt, &i, NULL)) == NULL ||
167716fd348SMartin Matuska 	    *p != i) {
168be181ee2SMartin Matuska 		(void) snprintf(why, BUFSIZE, "Unexpectedly found %llu\n",
169716fd348SMartin Matuska 		    p == NULL ? 0 : *p);
170716fd348SMartin Matuska 		return (1);
171716fd348SMartin Matuska 	}
172716fd348SMartin Matuska 
173716fd348SMartin Matuska 	i++;
174716fd348SMartin Matuska 
175716fd348SMartin Matuska 	if ((p = (u_longlong_t *)zfs_btree_find(bt, &i, NULL)) != NULL) {
176be181ee2SMartin Matuska 		(void) snprintf(why, BUFSIZE, "Found bad value: %llu\n", *p);
177716fd348SMartin Matuska 		return (1);
178716fd348SMartin Matuska 	}
179716fd348SMartin Matuska 
180716fd348SMartin Matuska 	return (0);
181716fd348SMartin Matuska }
182716fd348SMartin Matuska 
183716fd348SMartin Matuska /* Verify simple insertion and removal from the tree. */
184716fd348SMartin Matuska static int
insert_find_remove(zfs_btree_t * bt,char * why)185716fd348SMartin Matuska insert_find_remove(zfs_btree_t *bt, char *why)
186716fd348SMartin Matuska {
187716fd348SMartin Matuska 	u_longlong_t *p, i = 12345;
188716fd348SMartin Matuska 	zfs_btree_index_t bt_idx = {0};
189716fd348SMartin Matuska 
190716fd348SMartin Matuska 	/* Insert 'i' into the tree, and attempt to find it again. */
191716fd348SMartin Matuska 	zfs_btree_add(bt, &i);
192716fd348SMartin Matuska 	if ((p = (u_longlong_t *)zfs_btree_find(bt, &i, &bt_idx)) == NULL) {
193be181ee2SMartin Matuska 		(void) snprintf(why, BUFSIZE, "Didn't find value in tree\n");
194716fd348SMartin Matuska 		return (1);
195716fd348SMartin Matuska 	} else if (*p != i) {
196be181ee2SMartin Matuska 		(void) snprintf(why, BUFSIZE, "Found (%llu) in tree\n", *p);
197716fd348SMartin Matuska 		return (1);
198716fd348SMartin Matuska 	}
199716fd348SMartin Matuska 	ASSERT3S(zfs_btree_numnodes(bt), ==, 1);
200716fd348SMartin Matuska 	zfs_btree_verify(bt);
201716fd348SMartin Matuska 
202716fd348SMartin Matuska 	/* Remove 'i' from the tree, and verify it is not found. */
203716fd348SMartin Matuska 	zfs_btree_remove(bt, &i);
204716fd348SMartin Matuska 	if ((p = (u_longlong_t *)zfs_btree_find(bt, &i, &bt_idx)) != NULL) {
205be181ee2SMartin Matuska 		(void) snprintf(why, BUFSIZE,
206be181ee2SMartin Matuska 		    "Found removed value (%llu)\n", *p);
207716fd348SMartin Matuska 		return (1);
208716fd348SMartin Matuska 	}
209716fd348SMartin Matuska 	ASSERT3S(zfs_btree_numnodes(bt), ==, 0);
210716fd348SMartin Matuska 	zfs_btree_verify(bt);
211716fd348SMartin Matuska 
212716fd348SMartin Matuska 	return (0);
213716fd348SMartin Matuska }
214716fd348SMartin Matuska 
215716fd348SMartin Matuska /*
216716fd348SMartin Matuska  * Add a number of random entries into a btree and avl tree. Then walk them
217716fd348SMartin Matuska  * backwards and forwards while emptying the tree, verifying the trees look
218716fd348SMartin Matuska  * the same.
219716fd348SMartin Matuska  */
220716fd348SMartin Matuska static int
drain_tree(zfs_btree_t * bt,char * why)221716fd348SMartin Matuska drain_tree(zfs_btree_t *bt, char *why)
222716fd348SMartin Matuska {
223716fd348SMartin Matuska 	avl_tree_t avl;
224716fd348SMartin Matuska 	int i = 0;
225716fd348SMartin Matuska 	int_node_t *node;
226716fd348SMartin Matuska 	avl_index_t avl_idx = {0};
227716fd348SMartin Matuska 	zfs_btree_index_t bt_idx = {0};
228716fd348SMartin Matuska 
229716fd348SMartin Matuska 	avl_create(&avl, avl_compare, sizeof (int_node_t),
230716fd348SMartin Matuska 	    offsetof(int_node_t, node));
231716fd348SMartin Matuska 
232716fd348SMartin Matuska 	/* Fill both trees with the same data */
233716fd348SMartin Matuska 	for (i = 0; i < 64 * 1024; i++) {
234716fd348SMartin Matuska 		u_longlong_t randval = random();
235dbd5678dSMartin Matuska 		if (zfs_btree_find(bt, &randval, &bt_idx) != NULL) {
236716fd348SMartin Matuska 			continue;
237716fd348SMartin Matuska 		}
238716fd348SMartin Matuska 		zfs_btree_add_idx(bt, &randval, &bt_idx);
239716fd348SMartin Matuska 
240be181ee2SMartin Matuska 		node = malloc(sizeof (int_node_t));
241dbd5678dSMartin Matuska 		if (node == NULL) {
242dbd5678dSMartin Matuska 			perror("malloc");
243dbd5678dSMartin Matuska 			exit(EXIT_FAILURE);
244dbd5678dSMartin Matuska 		}
245be181ee2SMartin Matuska 
246716fd348SMartin Matuska 		node->data = randval;
247dbd5678dSMartin Matuska 		if (avl_find(&avl, node, &avl_idx) != NULL) {
248be181ee2SMartin Matuska 			(void) snprintf(why, BUFSIZE,
249be181ee2SMartin Matuska 			    "Found in avl: %llu\n", randval);
250716fd348SMartin Matuska 			return (1);
251716fd348SMartin Matuska 		}
252716fd348SMartin Matuska 		avl_insert(&avl, node, avl_idx);
253716fd348SMartin Matuska 	}
254716fd348SMartin Matuska 
255716fd348SMartin Matuska 	/* Remove data from either side of the trees, comparing the data */
256716fd348SMartin Matuska 	while (avl_numnodes(&avl) != 0) {
257716fd348SMartin Matuska 		uint64_t *data;
258716fd348SMartin Matuska 
259716fd348SMartin Matuska 		ASSERT3U(avl_numnodes(&avl), ==, zfs_btree_numnodes(bt));
260716fd348SMartin Matuska 		if (avl_numnodes(&avl) % 2 == 0) {
261716fd348SMartin Matuska 			node = avl_first(&avl);
262716fd348SMartin Matuska 			data = zfs_btree_first(bt, &bt_idx);
263716fd348SMartin Matuska 		} else {
264716fd348SMartin Matuska 			node = avl_last(&avl);
265716fd348SMartin Matuska 			data = zfs_btree_last(bt, &bt_idx);
266716fd348SMartin Matuska 		}
267716fd348SMartin Matuska 		ASSERT3U(node->data, ==, *data);
268716fd348SMartin Matuska 		zfs_btree_remove_idx(bt, &bt_idx);
269716fd348SMartin Matuska 		avl_remove(&avl, node);
270716fd348SMartin Matuska 
271716fd348SMartin Matuska 		if (avl_numnodes(&avl) == 0) {
272716fd348SMartin Matuska 			break;
273716fd348SMartin Matuska 		}
274716fd348SMartin Matuska 
275716fd348SMartin Matuska 		node = avl_first(&avl);
276716fd348SMartin Matuska 		ASSERT3U(node->data, ==,
277716fd348SMartin Matuska 		    *(uint64_t *)zfs_btree_first(bt, NULL));
278716fd348SMartin Matuska 		node = avl_last(&avl);
279716fd348SMartin Matuska 		ASSERT3U(node->data, ==, *(uint64_t *)zfs_btree_last(bt, NULL));
280716fd348SMartin Matuska 	}
281716fd348SMartin Matuska 	ASSERT3S(zfs_btree_numnodes(bt), ==, 0);
282716fd348SMartin Matuska 
283716fd348SMartin Matuska 	void *avl_cookie = NULL;
284716fd348SMartin Matuska 	while ((node = avl_destroy_nodes(&avl, &avl_cookie)) != NULL)
285716fd348SMartin Matuska 		free(node);
286716fd348SMartin Matuska 	avl_destroy(&avl);
287716fd348SMartin Matuska 
288716fd348SMartin Matuska 	return (0);
289716fd348SMartin Matuska }
290716fd348SMartin Matuska 
291716fd348SMartin Matuska /*
292716fd348SMartin Matuska  * This test uses an avl and btree, and continually processes new random
293716fd348SMartin Matuska  * values. Each value is either removed or inserted, depending on whether
294716fd348SMartin Matuska  * or not it is found in the tree. The test periodically checks that both
295716fd348SMartin Matuska  * trees have the same data and does consistency checks. This stress
296716fd348SMartin Matuska  * option can also be run on its own from the command line.
297716fd348SMartin Matuska  */
298716fd348SMartin Matuska static int
stress_tree(zfs_btree_t * bt,char * why)299716fd348SMartin Matuska stress_tree(zfs_btree_t *bt, char *why)
300716fd348SMartin Matuska {
301716fd348SMartin Matuska 	(void) why;
302716fd348SMartin Matuska 	avl_tree_t avl;
303716fd348SMartin Matuska 	int_node_t *node;
304716fd348SMartin Matuska 	struct timeval tp;
305716fd348SMartin Matuska 	time_t t0;
306716fd348SMartin Matuska 	int insertions = 0, removals = 0, iterations = 0;
307716fd348SMartin Matuska 	u_longlong_t max = 0, min = UINT64_MAX;
308716fd348SMartin Matuska 
309716fd348SMartin Matuska 	(void) gettimeofday(&tp, NULL);
310716fd348SMartin Matuska 	t0 = tp.tv_sec;
311716fd348SMartin Matuska 
312716fd348SMartin Matuska 	avl_create(&avl, avl_compare, sizeof (int_node_t),
313716fd348SMartin Matuska 	    offsetof(int_node_t, node));
314716fd348SMartin Matuska 
315716fd348SMartin Matuska 	while (1) {
316716fd348SMartin Matuska 		zfs_btree_index_t bt_idx = {0};
317716fd348SMartin Matuska 		avl_index_t avl_idx = {0};
318716fd348SMartin Matuska 
319716fd348SMartin Matuska 		uint64_t randval = random() % tree_limit;
320716fd348SMartin Matuska 		node = malloc(sizeof (*node));
321dbd5678dSMartin Matuska 		if (node == NULL) {
322dbd5678dSMartin Matuska 			perror("malloc");
323dbd5678dSMartin Matuska 			exit(EXIT_FAILURE);
324dbd5678dSMartin Matuska 		}
325716fd348SMartin Matuska 		node->data = randval;
326716fd348SMartin Matuska 
327716fd348SMartin Matuska 		max = randval > max ? randval : max;
328716fd348SMartin Matuska 		min = randval < min ? randval : min;
329716fd348SMartin Matuska 
330716fd348SMartin Matuska 		void *ret = avl_find(&avl, node, &avl_idx);
331716fd348SMartin Matuska 		if (ret == NULL) {
332716fd348SMartin Matuska 			insertions++;
333716fd348SMartin Matuska 			avl_insert(&avl, node, avl_idx);
334716fd348SMartin Matuska 			ASSERT3P(zfs_btree_find(bt, &randval, &bt_idx), ==,
335716fd348SMartin Matuska 			    NULL);
336716fd348SMartin Matuska 			zfs_btree_add_idx(bt, &randval, &bt_idx);
337716fd348SMartin Matuska 			verify_node(&avl, bt, node);
338716fd348SMartin Matuska 		} else {
339716fd348SMartin Matuska 			removals++;
340716fd348SMartin Matuska 			verify_node(&avl, bt, ret);
341716fd348SMartin Matuska 			zfs_btree_remove(bt, &randval);
342716fd348SMartin Matuska 			avl_remove(&avl, ret);
343716fd348SMartin Matuska 			free(ret);
344716fd348SMartin Matuska 			free(node);
345716fd348SMartin Matuska 		}
346716fd348SMartin Matuska 
347716fd348SMartin Matuska 		zfs_btree_verify(bt);
348716fd348SMartin Matuska 
349716fd348SMartin Matuska 		iterations++;
350716fd348SMartin Matuska 		if (iterations % contents_frequency == 0) {
351716fd348SMartin Matuska 			verify_contents(&avl, bt);
352716fd348SMartin Matuska 		}
353716fd348SMartin Matuska 
354716fd348SMartin Matuska 		zfs_btree_verify(bt);
355716fd348SMartin Matuska 
356716fd348SMartin Matuska 		(void) gettimeofday(&tp, NULL);
357716fd348SMartin Matuska 		if (tp.tv_sec > t0 + stress_timeout) {
358716fd348SMartin Matuska 			fprintf(stderr, "insertions/removals: %u/%u\nmax/min: "
359716fd348SMartin Matuska 			    "%llu/%llu\n", insertions, removals, max, min);
360716fd348SMartin Matuska 			break;
361716fd348SMartin Matuska 		}
362716fd348SMartin Matuska 	}
363716fd348SMartin Matuska 
364716fd348SMartin Matuska 	void *avl_cookie = NULL;
365716fd348SMartin Matuska 	while ((node = avl_destroy_nodes(&avl, &avl_cookie)) != NULL)
366716fd348SMartin Matuska 		free(node);
367716fd348SMartin Matuska 	avl_destroy(&avl);
368716fd348SMartin Matuska 
369716fd348SMartin Matuska 	if (stress_only) {
370716fd348SMartin Matuska 		zfs_btree_index_t *idx = NULL;
371dbd5678dSMartin Matuska 		while (zfs_btree_destroy_nodes(bt, &idx) != NULL)
372716fd348SMartin Matuska 			;
373716fd348SMartin Matuska 		zfs_btree_verify(bt);
374716fd348SMartin Matuska 	}
375716fd348SMartin Matuska 
376716fd348SMartin Matuska 	return (0);
377716fd348SMartin Matuska }
378716fd348SMartin Matuska 
379716fd348SMartin Matuska /*
380716fd348SMartin Matuska  * Verify inserting a duplicate value will cause a crash.
381716fd348SMartin Matuska  * Note: negative test; return of 0 is a failure.
382716fd348SMartin Matuska  */
383716fd348SMartin Matuska static int
insert_duplicate(zfs_btree_t * bt)384716fd348SMartin Matuska insert_duplicate(zfs_btree_t *bt)
385716fd348SMartin Matuska {
386dbd5678dSMartin Matuska 	uint64_t i = 23456;
387716fd348SMartin Matuska 	zfs_btree_index_t bt_idx = {0};
388716fd348SMartin Matuska 
389dbd5678dSMartin Matuska 	if (zfs_btree_find(bt, &i, &bt_idx) != NULL) {
390716fd348SMartin Matuska 		fprintf(stderr, "Found value in empty tree.\n");
391716fd348SMartin Matuska 		return (0);
392716fd348SMartin Matuska 	}
393716fd348SMartin Matuska 	zfs_btree_add_idx(bt, &i, &bt_idx);
394dbd5678dSMartin Matuska 	if (zfs_btree_find(bt, &i, &bt_idx) == NULL) {
395716fd348SMartin Matuska 		fprintf(stderr, "Did not find expected value.\n");
396716fd348SMartin Matuska 		return (0);
397716fd348SMartin Matuska 	}
398716fd348SMartin Matuska 
399716fd348SMartin Matuska 	/* Crash on inserting a duplicate */
400716fd348SMartin Matuska 	zfs_btree_add_idx(bt, &i, NULL);
401716fd348SMartin Matuska 
402716fd348SMartin Matuska 	return (0);
403716fd348SMartin Matuska }
404716fd348SMartin Matuska 
405716fd348SMartin Matuska /*
406716fd348SMartin Matuska  * Verify removing a non-existent value will cause a crash.
407716fd348SMartin Matuska  * Note: negative test; return of 0 is a failure.
408716fd348SMartin Matuska  */
409716fd348SMartin Matuska static int
remove_missing(zfs_btree_t * bt)410716fd348SMartin Matuska remove_missing(zfs_btree_t *bt)
411716fd348SMartin Matuska {
412dbd5678dSMartin Matuska 	uint64_t i = 23456;
413716fd348SMartin Matuska 	zfs_btree_index_t bt_idx = {0};
414716fd348SMartin Matuska 
415dbd5678dSMartin Matuska 	if (zfs_btree_find(bt, &i, &bt_idx) != NULL) {
416716fd348SMartin Matuska 		fprintf(stderr, "Found value in empty tree.\n");
417716fd348SMartin Matuska 		return (0);
418716fd348SMartin Matuska 	}
419716fd348SMartin Matuska 
420716fd348SMartin Matuska 	/* Crash removing a nonexistent entry */
421716fd348SMartin Matuska 	zfs_btree_remove(bt, &i);
422716fd348SMartin Matuska 
423716fd348SMartin Matuska 	return (0);
424716fd348SMartin Matuska }
425716fd348SMartin Matuska 
426716fd348SMartin Matuska static int
do_negative_test(zfs_btree_t * bt,char * test_name)427716fd348SMartin Matuska do_negative_test(zfs_btree_t *bt, char *test_name)
428716fd348SMartin Matuska {
429716fd348SMartin Matuska 	int rval = 0;
430716fd348SMartin Matuska 	struct rlimit rlim = {0};
431be181ee2SMartin Matuska 
432be181ee2SMartin Matuska 	(void) setrlimit(RLIMIT_CORE, &rlim);
433716fd348SMartin Matuska 
434716fd348SMartin Matuska 	if (strcmp(test_name, "insert_duplicate") == 0) {
435716fd348SMartin Matuska 		rval = insert_duplicate(bt);
436716fd348SMartin Matuska 	} else if (strcmp(test_name, "remove_missing") == 0) {
437716fd348SMartin Matuska 		rval = remove_missing(bt);
438716fd348SMartin Matuska 	}
439716fd348SMartin Matuska 
440716fd348SMartin Matuska 	/*
441716fd348SMartin Matuska 	 * Return 0, since callers will expect non-zero return values for
442716fd348SMartin Matuska 	 * these tests, and we should have crashed before getting here anyway.
443716fd348SMartin Matuska 	 */
444716fd348SMartin Matuska 	(void) fprintf(stderr, "Test: %s returned %d.\n", test_name, rval);
445716fd348SMartin Matuska 	return (0);
446716fd348SMartin Matuska }
447716fd348SMartin Matuska 
448716fd348SMartin Matuska typedef struct btree_test {
449716fd348SMartin Matuska 	const char	*name;
450716fd348SMartin Matuska 	int		(*func)(zfs_btree_t *, char *);
451716fd348SMartin Matuska } btree_test_t;
452716fd348SMartin Matuska 
453716fd348SMartin Matuska static btree_test_t test_table[] = {
454716fd348SMartin Matuska 	{ "insert_find_remove",		insert_find_remove	},
455716fd348SMartin Matuska 	{ "find_without_index",		find_without_index	},
456716fd348SMartin Matuska 	{ "drain_tree",			drain_tree		},
457716fd348SMartin Matuska 	{ "stress_tree",		stress_tree		},
458716fd348SMartin Matuska 	{ NULL,				NULL			}
459716fd348SMartin Matuska };
460716fd348SMartin Matuska 
461716fd348SMartin Matuska int
main(int argc,char * argv[])462716fd348SMartin Matuska main(int argc, char *argv[])
463716fd348SMartin Matuska {
464716fd348SMartin Matuska 	char *negative_test = NULL;
465716fd348SMartin Matuska 	int failed_tests = 0;
466716fd348SMartin Matuska 	struct timeval tp;
467716fd348SMartin Matuska 	zfs_btree_t bt;
468716fd348SMartin Matuska 	int c;
469716fd348SMartin Matuska 
470716fd348SMartin Matuska 	while ((c = getopt(argc, argv, "c:l:n:r:st:")) != -1) {
471716fd348SMartin Matuska 		switch (c) {
472716fd348SMartin Matuska 		case 'c':
473716fd348SMartin Matuska 			contents_frequency = atoi(optarg);
474716fd348SMartin Matuska 			break;
475716fd348SMartin Matuska 		case 'l':
476716fd348SMartin Matuska 			tree_limit = atoi(optarg);
477716fd348SMartin Matuska 			break;
478716fd348SMartin Matuska 		case 'n':
479716fd348SMartin Matuska 			negative_test = optarg;
480716fd348SMartin Matuska 			break;
481716fd348SMartin Matuska 		case 'r':
482716fd348SMartin Matuska 			seed = atoi(optarg);
483716fd348SMartin Matuska 			break;
484716fd348SMartin Matuska 		case 's':
485716fd348SMartin Matuska 			stress_only = B_TRUE;
486716fd348SMartin Matuska 			break;
487716fd348SMartin Matuska 		case 't':
488716fd348SMartin Matuska 			stress_timeout = atoi(optarg);
489716fd348SMartin Matuska 			break;
490716fd348SMartin Matuska 		case 'h':
491716fd348SMartin Matuska 		default:
492716fd348SMartin Matuska 			usage(1);
493716fd348SMartin Matuska 			break;
494716fd348SMartin Matuska 		}
495716fd348SMartin Matuska 	}
496716fd348SMartin Matuska 
497716fd348SMartin Matuska 	if (seed == 0) {
498716fd348SMartin Matuska 		(void) gettimeofday(&tp, NULL);
499716fd348SMartin Matuska 		seed = tp.tv_sec;
500716fd348SMartin Matuska 	}
501716fd348SMartin Matuska 	srandom(seed);
502716fd348SMartin Matuska 
503716fd348SMartin Matuska 	zfs_btree_init();
504*4e8d558cSMartin Matuska 	zfs_btree_create(&bt, zfs_btree_compare, NULL, sizeof (uint64_t));
505716fd348SMartin Matuska 
506716fd348SMartin Matuska 	/*
507716fd348SMartin Matuska 	 * This runs the named negative test. None of them should
508716fd348SMartin Matuska 	 * return, as they both cause crashes.
509716fd348SMartin Matuska 	 */
510716fd348SMartin Matuska 	if (negative_test) {
511716fd348SMartin Matuska 		return (do_negative_test(&bt, negative_test));
512716fd348SMartin Matuska 	}
513716fd348SMartin Matuska 
514716fd348SMartin Matuska 	fprintf(stderr, "Seed: %u\n", seed);
515716fd348SMartin Matuska 
516716fd348SMartin Matuska 	/*
517716fd348SMartin Matuska 	 * This is a stress test that does operations on a btree over the
518716fd348SMartin Matuska 	 * requested timeout period, verifying them against identical
519716fd348SMartin Matuska 	 * operations in an avl tree.
520716fd348SMartin Matuska 	 */
521716fd348SMartin Matuska 	if (stress_only != 0) {
522716fd348SMartin Matuska 		return (stress_tree(&bt, NULL));
523716fd348SMartin Matuska 	}
524716fd348SMartin Matuska 
525716fd348SMartin Matuska 	/* Do the positive tests */
526716fd348SMartin Matuska 	btree_test_t *test = &test_table[0];
527716fd348SMartin Matuska 	while (test->name) {
528716fd348SMartin Matuska 		int retval;
529716fd348SMartin Matuska 		char why[BUFSIZE] = {0};
530716fd348SMartin Matuska 		zfs_btree_index_t *idx = NULL;
531716fd348SMartin Matuska 
532716fd348SMartin Matuska 		(void) fprintf(stdout, "%-20s", test->name);
533716fd348SMartin Matuska 		retval = test->func(&bt, why);
534716fd348SMartin Matuska 
535716fd348SMartin Matuska 		if (retval == 0) {
536716fd348SMartin Matuska 			(void) fprintf(stdout, "ok\n");
537716fd348SMartin Matuska 		} else {
538716fd348SMartin Matuska 			(void) fprintf(stdout, "failed with %d\n", retval);
539716fd348SMartin Matuska 			if (strlen(why) != 0)
540716fd348SMartin Matuska 				(void) fprintf(stdout, "\t%s\n", why);
541716fd348SMartin Matuska 			why[0] = '\0';
542716fd348SMartin Matuska 			failed_tests++;
543716fd348SMartin Matuska 		}
544716fd348SMartin Matuska 
545716fd348SMartin Matuska 		/* Remove all the elements and re-verify the tree */
546dbd5678dSMartin Matuska 		while (zfs_btree_destroy_nodes(&bt, &idx) != NULL)
547716fd348SMartin Matuska 			;
548716fd348SMartin Matuska 		zfs_btree_verify(&bt);
549716fd348SMartin Matuska 
550716fd348SMartin Matuska 		test++;
551716fd348SMartin Matuska 	}
552716fd348SMartin Matuska 
553716fd348SMartin Matuska 	zfs_btree_verify(&bt);
554716fd348SMartin Matuska 	zfs_btree_fini();
555716fd348SMartin Matuska 
556716fd348SMartin Matuska 	return (failed_tests);
557716fd348SMartin Matuska }
558