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