2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
13 * Copyright (c) 2019 by Delphix. All rights reserved.
20 #include <sys/btree.h>
22 #include <sys/resource.h>
27 int stress_timeout
= 180;
28 int contents_frequency
= 100;
29 int tree_limit
= 64 * 1024;
30 boolean_t stress_only
= B_FALSE
;
35 (void) fprintf(stderr
, "Usage:\tbtree_test -n <test_name>\n");
36 (void) fprintf(stderr
, "\tbtree_test -s [-r <seed>] [-l <limit>] "
37 "[-t timeout>] [-c check_contents]\n");
38 (void) fprintf(stderr
, "\tbtree_test [-r <seed>] [-l <limit>] "
39 "[-t timeout>] [-c check_contents]\n");
40 (void) fprintf(stderr
, "\n With the -n option, run the named "
41 "negative test. With the -s option,\n");
42 (void) fprintf(stderr
, " run the stress test according to the "
43 "other options passed. With\n");
44 (void) fprintf(stderr
, " neither, run all the positive tests, "
45 "including the stress test with\n");
46 (void) fprintf(stderr
, " the default options.\n");
47 (void) fprintf(stderr
, "\n Options that control the stress test\n");
48 (void) fprintf(stderr
, "\t-c stress iterations after which to compare "
49 "tree contents [default: 100]\n");
50 (void) fprintf(stderr
, "\t-l the largest value to allow in the tree "
52 (void) fprintf(stderr
, "\t-r random seed [default: from "
54 (void) fprintf(stderr
, "\t-t seconds to let the stress test run "
59 typedef struct int_node
{
69 avl_compare(const void *v1
, const void *v2
)
71 const int_node_t
*n1
= v1
;
72 const int_node_t
*n2
= v2
;
73 uint64_t a
= n1
->data
;
74 uint64_t b
= n2
->data
;
76 return (TREE_CMP(a
, b
));
80 zfs_btree_compare(const void *v1
, const void *v2
)
82 const uint64_t *a
= v1
;
83 const uint64_t *b
= v2
;
85 return (TREE_CMP(*a
, *b
));
89 verify_contents(avl_tree_t
*avl
, zfs_btree_t
*bt
)
92 zfs_btree_index_t bt_idx
= {0};
96 boolean_t forward
= count
% 2 == 0 ? B_TRUE
: B_FALSE
;
99 ASSERT3U(avl_numnodes(avl
), ==, zfs_btree_numnodes(bt
));
100 if (forward
== B_TRUE
) {
101 node
= avl_first(avl
);
102 data
= zfs_btree_first(bt
, &bt_idx
);
104 node
= avl_last(avl
);
105 data
= zfs_btree_last(bt
, &bt_idx
);
108 while (node
!= NULL
) {
109 ASSERT3U(*data
, ==, node
->data
);
110 if (forward
== B_TRUE
) {
111 data
= zfs_btree_next(bt
, &bt_idx
, &bt_idx
);
112 node
= AVL_NEXT(avl
, node
);
114 data
= zfs_btree_prev(bt
, &bt_idx
, &bt_idx
);
115 node
= AVL_PREV(avl
, node
);
121 verify_node(avl_tree_t
*avl
, zfs_btree_t
*bt
, int_node_t
*node
)
123 zfs_btree_index_t bt_idx
= {0};
124 zfs_btree_index_t bt_idx2
= {0};
126 uint64_t data
= node
->data
;
129 ASSERT3U(avl_numnodes(avl
), ==, zfs_btree_numnodes(bt
));
130 ASSERT3P((rv
= (uint64_t *)zfs_btree_find(bt
, &data
, &bt_idx
)), !=,
132 ASSERT3S(*rv
, ==, data
);
133 ASSERT3P(zfs_btree_get(bt
, &bt_idx
), !=, NULL
);
134 ASSERT3S(data
, ==, *(uint64_t *)zfs_btree_get(bt
, &bt_idx
));
136 if ((inp
= AVL_NEXT(avl
, node
)) != NULL
) {
137 ASSERT3P((rv
= zfs_btree_next(bt
, &bt_idx
, &bt_idx2
)), !=,
139 ASSERT3P(rv
, ==, zfs_btree_get(bt
, &bt_idx2
));
140 ASSERT3S(inp
->data
, ==, *rv
);
142 ASSERT3U(data
, ==, *(uint64_t *)zfs_btree_last(bt
, &bt_idx
));
145 if ((inp
= AVL_PREV(avl
, node
)) != NULL
) {
146 ASSERT3P((rv
= zfs_btree_prev(bt
, &bt_idx
, &bt_idx2
)), !=,
148 ASSERT3P(rv
, ==, zfs_btree_get(bt
, &bt_idx2
));
149 ASSERT3S(inp
->data
, ==, *rv
);
151 ASSERT3U(data
, ==, *(uint64_t *)zfs_btree_first(bt
, &bt_idx
));
159 /* Verify that zfs_btree_find works correctly with a NULL index. */
161 find_without_index(zfs_btree_t
*bt
, char *why
)
163 u_longlong_t
*p
, i
= 12345;
165 zfs_btree_add(bt
, &i
);
166 if ((p
= (u_longlong_t
*)zfs_btree_find(bt
, &i
, NULL
)) == NULL
||
168 (void) snprintf(why
, BUFSIZE
, "Unexpectedly found %llu\n",
175 if ((p
= (u_longlong_t
*)zfs_btree_find(bt
, &i
, NULL
)) != NULL
) {
176 (void) snprintf(why
, BUFSIZE
, "Found bad value: %llu\n", *p
);
183 /* Verify simple insertion and removal from the tree. */
185 insert_find_remove(zfs_btree_t
*bt
, char *why
)
187 u_longlong_t
*p
, i
= 12345;
188 zfs_btree_index_t bt_idx
= {0};
190 /* Insert 'i' into the tree, and attempt to find it again. */
191 zfs_btree_add(bt
, &i
);
192 if ((p
= (u_longlong_t
*)zfs_btree_find(bt
, &i
, &bt_idx
)) == NULL
) {
193 (void) snprintf(why
, BUFSIZE
, "Didn't find value in tree\n");
195 } else if (*p
!= i
) {
196 (void) snprintf(why
, BUFSIZE
, "Found (%llu) in tree\n", *p
);
199 ASSERT3S(zfs_btree_numnodes(bt
), ==, 1);
200 zfs_btree_verify(bt
);
202 /* Remove 'i' from the tree, and verify it is not found. */
203 zfs_btree_remove(bt
, &i
);
204 if ((p
= (u_longlong_t
*)zfs_btree_find(bt
, &i
, &bt_idx
)) != NULL
) {
205 (void) snprintf(why
, BUFSIZE
,
206 "Found removed value (%llu)\n", *p
);
209 ASSERT3S(zfs_btree_numnodes(bt
), ==, 0);
210 zfs_btree_verify(bt
);
216 * Add a number of random entries into a btree and avl tree. Then walk them
217 * backwards and forwards while emptying the tree, verifying the trees look
221 drain_tree(zfs_btree_t
*bt
, char *why
)
227 avl_index_t avl_idx
= {0};
228 zfs_btree_index_t bt_idx
= {0};
230 avl_create(&avl
, avl_compare
, sizeof (int_node_t
),
231 offsetof(int_node_t
, node
));
233 /* Fill both trees with the same data */
234 for (i
= 0; i
< 64 * 1024; i
++) {
237 u_longlong_t randval
= random();
238 if ((p
= (uint64_t *)zfs_btree_find(bt
, &randval
, &bt_idx
)) !=
242 zfs_btree_add_idx(bt
, &randval
, &bt_idx
);
244 node
= malloc(sizeof (int_node_t
));
250 node
->data
= randval
;
251 if ((ret
= avl_find(&avl
, node
, &avl_idx
)) != NULL
) {
252 (void) snprintf(why
, BUFSIZE
,
253 "Found in avl: %llu\n", randval
);
256 avl_insert(&avl
, node
, avl_idx
);
259 /* Remove data from either side of the trees, comparing the data */
260 while (avl_numnodes(&avl
) != 0) {
263 ASSERT3U(avl_numnodes(&avl
), ==, zfs_btree_numnodes(bt
));
264 if (avl_numnodes(&avl
) % 2 == 0) {
265 node
= avl_first(&avl
);
266 data
= zfs_btree_first(bt
, &bt_idx
);
268 node
= avl_last(&avl
);
269 data
= zfs_btree_last(bt
, &bt_idx
);
271 ASSERT3U(node
->data
, ==, *data
);
272 zfs_btree_remove_idx(bt
, &bt_idx
);
273 avl_remove(&avl
, node
);
275 if (avl_numnodes(&avl
) == 0) {
279 node
= avl_first(&avl
);
280 ASSERT3U(node
->data
, ==,
281 *(uint64_t *)zfs_btree_first(bt
, NULL
));
282 node
= avl_last(&avl
);
283 ASSERT3U(node
->data
, ==, *(uint64_t *)zfs_btree_last(bt
, NULL
));
285 ASSERT3S(zfs_btree_numnodes(bt
), ==, 0);
287 void *avl_cookie
= NULL
;
288 while ((node
= avl_destroy_nodes(&avl
, &avl_cookie
)) != NULL
)
296 * This test uses an avl and btree, and continually processes new random
297 * values. Each value is either removed or inserted, depending on whether
298 * or not it is found in the tree. The test periodically checks that both
299 * trees have the same data and does consistency checks. This stress
300 * option can also be run on its own from the command line.
303 stress_tree(zfs_btree_t
*bt
, char *why
)
310 int insertions
= 0, removals
= 0, iterations
= 0;
311 u_longlong_t max
= 0, min
= UINT64_MAX
;
313 (void) gettimeofday(&tp
, NULL
);
316 avl_create(&avl
, avl_compare
, sizeof (int_node_t
),
317 offsetof(int_node_t
, node
));
320 zfs_btree_index_t bt_idx
= {0};
321 avl_index_t avl_idx
= {0};
323 uint64_t randval
= random() % tree_limit
;
324 node
= malloc(sizeof (*node
));
329 node
->data
= randval
;
331 max
= randval
> max
? randval
: max
;
332 min
= randval
< min
? randval
: min
;
334 void *ret
= avl_find(&avl
, node
, &avl_idx
);
337 avl_insert(&avl
, node
, avl_idx
);
338 ASSERT3P(zfs_btree_find(bt
, &randval
, &bt_idx
), ==,
340 zfs_btree_add_idx(bt
, &randval
, &bt_idx
);
341 verify_node(&avl
, bt
, node
);
344 verify_node(&avl
, bt
, ret
);
345 zfs_btree_remove(bt
, &randval
);
346 avl_remove(&avl
, ret
);
351 zfs_btree_verify(bt
);
354 if (iterations
% contents_frequency
== 0) {
355 verify_contents(&avl
, bt
);
358 zfs_btree_verify(bt
);
360 (void) gettimeofday(&tp
, NULL
);
361 if (tp
.tv_sec
> t0
+ stress_timeout
) {
362 fprintf(stderr
, "insertions/removals: %u/%u\nmax/min: "
363 "%llu/%llu\n", insertions
, removals
, max
, min
);
368 void *avl_cookie
= NULL
;
369 while ((node
= avl_destroy_nodes(&avl
, &avl_cookie
)) != NULL
)
374 zfs_btree_index_t
*idx
= NULL
;
377 while ((rv
= zfs_btree_destroy_nodes(bt
, &idx
)) != NULL
)
379 zfs_btree_verify(bt
);
386 * Verify inserting a duplicate value will cause a crash.
387 * Note: negative test; return of 0 is a failure.
390 insert_duplicate(zfs_btree_t
*bt
)
392 uint64_t *p
, i
= 23456;
393 zfs_btree_index_t bt_idx
= {0};
395 if ((p
= (uint64_t *)zfs_btree_find(bt
, &i
, &bt_idx
)) != NULL
) {
396 fprintf(stderr
, "Found value in empty tree.\n");
399 zfs_btree_add_idx(bt
, &i
, &bt_idx
);
400 if ((p
= (uint64_t *)zfs_btree_find(bt
, &i
, &bt_idx
)) == NULL
) {
401 fprintf(stderr
, "Did not find expected value.\n");
405 /* Crash on inserting a duplicate */
406 zfs_btree_add_idx(bt
, &i
, NULL
);
412 * Verify removing a non-existent value will cause a crash.
413 * Note: negative test; return of 0 is a failure.
416 remove_missing(zfs_btree_t
*bt
)
418 uint64_t *p
, i
= 23456;
419 zfs_btree_index_t bt_idx
= {0};
421 if ((p
= (uint64_t *)zfs_btree_find(bt
, &i
, &bt_idx
)) != NULL
) {
422 fprintf(stderr
, "Found value in empty tree.\n");
426 /* Crash removing a nonexistent entry */
427 zfs_btree_remove(bt
, &i
);
433 do_negative_test(zfs_btree_t
*bt
, char *test_name
)
436 struct rlimit rlim
= {0};
438 (void) setrlimit(RLIMIT_CORE
, &rlim
);
440 if (strcmp(test_name
, "insert_duplicate") == 0) {
441 rval
= insert_duplicate(bt
);
442 } else if (strcmp(test_name
, "remove_missing") == 0) {
443 rval
= remove_missing(bt
);
447 * Return 0, since callers will expect non-zero return values for
448 * these tests, and we should have crashed before getting here anyway.
450 (void) fprintf(stderr
, "Test: %s returned %d.\n", test_name
, rval
);
454 typedef struct btree_test
{
456 int (*func
)(zfs_btree_t
*, char *);
459 static btree_test_t test_table
[] = {
460 { "insert_find_remove", insert_find_remove
},
461 { "find_without_index", find_without_index
},
462 { "drain_tree", drain_tree
},
463 { "stress_tree", stress_tree
},
468 main(int argc
, char *argv
[])
470 char *negative_test
= NULL
;
471 int failed_tests
= 0;
476 while ((c
= getopt(argc
, argv
, "c:l:n:r:st:")) != -1) {
479 contents_frequency
= atoi(optarg
);
482 tree_limit
= atoi(optarg
);
485 negative_test
= optarg
;
491 stress_only
= B_TRUE
;
494 stress_timeout
= atoi(optarg
);
508 (void) gettimeofday(&tp
, NULL
);
514 zfs_btree_create(&bt
, zfs_btree_compare
, sizeof (uint64_t));
517 * This runs the named negative test. None of them should
518 * return, as they both cause crashes.
521 return (do_negative_test(&bt
, negative_test
));
524 fprintf(stderr
, "Seed: %u\n", seed
);
527 * This is a stress test that does operations on a btree over the
528 * requested timeout period, verifying them against identical
529 * operations in an avl tree.
531 if (stress_only
!= 0) {
532 return (stress_tree(&bt
, NULL
));
535 /* Do the positive tests */
536 btree_test_t
*test
= &test_table
[0];
540 char why
[BUFSIZE
] = {0};
541 zfs_btree_index_t
*idx
= NULL
;
543 (void) fprintf(stdout
, "%-20s", test
->name
);
544 retval
= test
->func(&bt
, why
);
547 (void) fprintf(stdout
, "ok\n");
549 (void) fprintf(stdout
, "failed with %d\n", retval
);
550 if (strlen(why
) != 0)
551 (void) fprintf(stdout
, "\t%s\n", why
);
556 /* Remove all the elements and re-verify the tree */
557 while ((rv
= zfs_btree_destroy_nodes(&bt
, &idx
)) != NULL
)
559 zfs_btree_verify(&bt
);
564 zfs_btree_verify(&bt
);
567 return (failed_tests
);