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