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.
19 #include <sys/btree.h>
21 #include <sys/resource.h>
26 int stress_timeout
= 180;
27 int contents_frequency
= 100;
28 int tree_limit
= 64 * 1024;
29 boolean_t stress_only
= B_FALSE
;
34 (void) fprintf(stderr
, "Usage:\tbtree_test -n <test_name>\n");
35 (void) fprintf(stderr
, "\tbtree_test -s [-r <seed>] [-l <limit>] "
36 "[-t timeout>] [-c check_contents]\n");
37 (void) fprintf(stderr
, "\tbtree_test [-r <seed>] [-l <limit>] "
38 "[-t timeout>] [-c check_contents]\n");
39 (void) fprintf(stderr
, "\n With the -n option, run the named "
40 "negative test. With the -s option,\n");
41 (void) fprintf(stderr
, " run the stress test according to the "
42 "other options passed. With\n");
43 (void) fprintf(stderr
, " neither, run all the positive tests, "
44 "including the stress test with\n");
45 (void) fprintf(stderr
, " the default options.\n");
46 (void) fprintf(stderr
, "\n Options that control the stress test\n");
47 (void) fprintf(stderr
, "\t-c stress iterations after which to compare "
48 "tree contents [default: 100]\n");
49 (void) fprintf(stderr
, "\t-l the largest value to allow in the tree "
51 (void) fprintf(stderr
, "\t-r random seed [default: from "
53 (void) fprintf(stderr
, "\t-t seconds to let the stress test run "
58 typedef struct int_node
{
68 avl_compare(const void *v1
, const void *v2
)
70 const int_node_t
*n1
= v1
;
71 const int_node_t
*n2
= v2
;
72 uint64_t a
= n1
->data
;
73 uint64_t b
= n2
->data
;
75 return (TREE_CMP(a
, b
));
79 zfs_btree_compare(const void *v1
, const void *v2
)
81 const uint64_t *a
= v1
;
82 const uint64_t *b
= v2
;
84 return (TREE_CMP(*a
, *b
));
88 verify_contents(avl_tree_t
*avl
, zfs_btree_t
*bt
)
91 zfs_btree_index_t bt_idx
= {0};
95 boolean_t forward
= count
% 2 == 0 ? B_TRUE
: B_FALSE
;
98 ASSERT3U(avl_numnodes(avl
), ==, zfs_btree_numnodes(bt
));
99 if (forward
== B_TRUE
) {
100 node
= avl_first(avl
);
101 data
= zfs_btree_first(bt
, &bt_idx
);
103 node
= avl_last(avl
);
104 data
= zfs_btree_last(bt
, &bt_idx
);
107 while (node
!= NULL
) {
108 ASSERT3U(*data
, ==, node
->data
);
109 if (forward
== B_TRUE
) {
110 data
= zfs_btree_next(bt
, &bt_idx
, &bt_idx
);
111 node
= AVL_NEXT(avl
, node
);
113 data
= zfs_btree_prev(bt
, &bt_idx
, &bt_idx
);
114 node
= AVL_PREV(avl
, node
);
120 verify_node(avl_tree_t
*avl
, zfs_btree_t
*bt
, int_node_t
*node
)
122 zfs_btree_index_t bt_idx
= {0};
123 zfs_btree_index_t bt_idx2
= {0};
125 uint64_t data
= node
->data
;
128 ASSERT3U(avl_numnodes(avl
), ==, zfs_btree_numnodes(bt
));
129 ASSERT3P((rv
= (uint64_t *)zfs_btree_find(bt
, &data
, &bt_idx
)), !=,
131 ASSERT3S(*rv
, ==, data
);
132 ASSERT3P(zfs_btree_get(bt
, &bt_idx
), !=, NULL
);
133 ASSERT3S(data
, ==, *(uint64_t *)zfs_btree_get(bt
, &bt_idx
));
135 if ((inp
= AVL_NEXT(avl
, node
)) != NULL
) {
136 ASSERT3P((rv
= zfs_btree_next(bt
, &bt_idx
, &bt_idx2
)), !=,
138 ASSERT3P(rv
, ==, zfs_btree_get(bt
, &bt_idx2
));
139 ASSERT3S(inp
->data
, ==, *rv
);
141 ASSERT3U(data
, ==, *(uint64_t *)zfs_btree_last(bt
, &bt_idx
));
144 if ((inp
= AVL_PREV(avl
, node
)) != NULL
) {
145 ASSERT3P((rv
= zfs_btree_prev(bt
, &bt_idx
, &bt_idx2
)), !=,
147 ASSERT3P(rv
, ==, zfs_btree_get(bt
, &bt_idx2
));
148 ASSERT3S(inp
->data
, ==, *rv
);
150 ASSERT3U(data
, ==, *(uint64_t *)zfs_btree_first(bt
, &bt_idx
));
158 /* Verify that zfs_btree_find works correctly with a NULL index. */
160 find_without_index(zfs_btree_t
*bt
, char *why
)
162 u_longlong_t
*p
, i
= 12345;
164 zfs_btree_add(bt
, &i
);
165 if ((p
= (u_longlong_t
*)zfs_btree_find(bt
, &i
, NULL
)) == NULL
||
167 snprintf(why
, BUFSIZE
, "Unexpectedly found %llu\n",
174 if ((p
= (u_longlong_t
*)zfs_btree_find(bt
, &i
, NULL
)) != NULL
) {
175 snprintf(why
, BUFSIZE
, "Found bad value: %llu\n", *p
);
182 /* Verify simple insertion and removal from the tree. */
184 insert_find_remove(zfs_btree_t
*bt
, char *why
)
186 u_longlong_t
*p
, i
= 12345;
187 zfs_btree_index_t bt_idx
= {0};
189 /* Insert 'i' into the tree, and attempt to find it again. */
190 zfs_btree_add(bt
, &i
);
191 if ((p
= (u_longlong_t
*)zfs_btree_find(bt
, &i
, &bt_idx
)) == NULL
) {
192 snprintf(why
, BUFSIZE
, "Didn't find value in tree\n");
194 } else if (*p
!= i
) {
195 snprintf(why
, BUFSIZE
, "Found (%llu) in tree\n", *p
);
198 ASSERT3S(zfs_btree_numnodes(bt
), ==, 1);
199 zfs_btree_verify(bt
);
201 /* Remove 'i' from the tree, and verify it is not found. */
202 zfs_btree_remove(bt
, &i
);
203 if ((p
= (u_longlong_t
*)zfs_btree_find(bt
, &i
, &bt_idx
)) != NULL
) {
204 snprintf(why
, BUFSIZE
, "Found removed value (%llu)\n", *p
);
207 ASSERT3S(zfs_btree_numnodes(bt
), ==, 0);
208 zfs_btree_verify(bt
);
214 * Add a number of random entries into a btree and avl tree. Then walk them
215 * backwards and forwards while emptying the tree, verifying the trees look
219 drain_tree(zfs_btree_t
*bt
, char *why
)
225 avl_index_t avl_idx
= {0};
226 zfs_btree_index_t bt_idx
= {0};
228 avl_create(&avl
, avl_compare
, sizeof (int_node_t
),
229 offsetof(int_node_t
, node
));
231 /* Fill both trees with the same data */
232 for (i
= 0; i
< 64 * 1024; i
++) {
235 u_longlong_t randval
= random();
236 node
= malloc(sizeof (int_node_t
));
237 if ((p
= (uint64_t *)zfs_btree_find(bt
, &randval
, &bt_idx
)) !=
241 zfs_btree_add_idx(bt
, &randval
, &bt_idx
);
243 node
->data
= randval
;
244 if ((ret
= avl_find(&avl
, node
, &avl_idx
)) != NULL
) {
245 snprintf(why
, BUFSIZE
, "Found in avl: %llu\n", randval
);
248 avl_insert(&avl
, node
, avl_idx
);
251 /* Remove data from either side of the trees, comparing the data */
252 while (avl_numnodes(&avl
) != 0) {
255 ASSERT3U(avl_numnodes(&avl
), ==, zfs_btree_numnodes(bt
));
256 if (avl_numnodes(&avl
) % 2 == 0) {
257 node
= avl_first(&avl
);
258 data
= zfs_btree_first(bt
, &bt_idx
);
260 node
= avl_last(&avl
);
261 data
= zfs_btree_last(bt
, &bt_idx
);
263 ASSERT3U(node
->data
, ==, *data
);
264 zfs_btree_remove_idx(bt
, &bt_idx
);
265 avl_remove(&avl
, node
);
267 if (avl_numnodes(&avl
) == 0) {
271 node
= avl_first(&avl
);
272 ASSERT3U(node
->data
, ==,
273 *(uint64_t *)zfs_btree_first(bt
, NULL
));
274 node
= avl_last(&avl
);
275 ASSERT3U(node
->data
, ==, *(uint64_t *)zfs_btree_last(bt
, NULL
));
277 ASSERT3S(zfs_btree_numnodes(bt
), ==, 0);
279 void *avl_cookie
= NULL
;
280 while ((node
= avl_destroy_nodes(&avl
, &avl_cookie
)) != NULL
)
288 * This test uses an avl and btree, and continually processes new random
289 * values. Each value is either removed or inserted, depending on whether
290 * or not it is found in the tree. The test periodically checks that both
291 * trees have the same data and does consistency checks. This stress
292 * option can also be run on its own from the command line.
295 stress_tree(zfs_btree_t
*bt
, char *why
)
301 int insertions
= 0, removals
= 0, iterations
= 0;
302 u_longlong_t max
= 0, min
= UINT64_MAX
;
304 (void) gettimeofday(&tp
, NULL
);
307 avl_create(&avl
, avl_compare
, sizeof (int_node_t
),
308 offsetof(int_node_t
, node
));
311 zfs_btree_index_t bt_idx
= {0};
312 avl_index_t avl_idx
= {0};
314 uint64_t randval
= random() % tree_limit
;
315 node
= malloc(sizeof (*node
));
316 node
->data
= randval
;
318 max
= randval
> max
? randval
: max
;
319 min
= randval
< min
? randval
: min
;
321 void *ret
= avl_find(&avl
, node
, &avl_idx
);
324 avl_insert(&avl
, node
, avl_idx
);
325 ASSERT3P(zfs_btree_find(bt
, &randval
, &bt_idx
), ==,
327 zfs_btree_add_idx(bt
, &randval
, &bt_idx
);
328 verify_node(&avl
, bt
, node
);
331 verify_node(&avl
, bt
, ret
);
332 zfs_btree_remove(bt
, &randval
);
333 avl_remove(&avl
, ret
);
338 zfs_btree_verify(bt
);
341 if (iterations
% contents_frequency
== 0) {
342 verify_contents(&avl
, bt
);
345 zfs_btree_verify(bt
);
347 (void) gettimeofday(&tp
, NULL
);
348 if (tp
.tv_sec
> t0
+ stress_timeout
) {
349 fprintf(stderr
, "insertions/removals: %u/%u\nmax/min: "
350 "%llu/%llu\n", insertions
, removals
, max
, min
);
355 void *avl_cookie
= NULL
;
356 while ((node
= avl_destroy_nodes(&avl
, &avl_cookie
)) != NULL
)
361 zfs_btree_index_t
*idx
= NULL
;
364 while ((rv
= zfs_btree_destroy_nodes(bt
, &idx
)) != NULL
)
366 zfs_btree_verify(bt
);
373 * Verify inserting a duplicate value will cause a crash.
374 * Note: negative test; return of 0 is a failure.
377 insert_duplicate(zfs_btree_t
*bt
)
379 uint64_t *p
, i
= 23456;
380 zfs_btree_index_t bt_idx
= {0};
382 if ((p
= (uint64_t *)zfs_btree_find(bt
, &i
, &bt_idx
)) != NULL
) {
383 fprintf(stderr
, "Found value in empty tree.\n");
386 zfs_btree_add_idx(bt
, &i
, &bt_idx
);
387 if ((p
= (uint64_t *)zfs_btree_find(bt
, &i
, &bt_idx
)) == NULL
) {
388 fprintf(stderr
, "Did not find expected value.\n");
392 /* Crash on inserting a duplicate */
393 zfs_btree_add_idx(bt
, &i
, NULL
);
399 * Verify removing a non-existent value will cause a crash.
400 * Note: negative test; return of 0 is a failure.
403 remove_missing(zfs_btree_t
*bt
)
405 uint64_t *p
, i
= 23456;
406 zfs_btree_index_t bt_idx
= {0};
408 if ((p
= (uint64_t *)zfs_btree_find(bt
, &i
, &bt_idx
)) != NULL
) {
409 fprintf(stderr
, "Found value in empty tree.\n");
413 /* Crash removing a nonexistent entry */
414 zfs_btree_remove(bt
, &i
);
420 do_negative_test(zfs_btree_t
*bt
, char *test_name
)
423 struct rlimit rlim
= {0};
424 setrlimit(RLIMIT_CORE
, &rlim
);
426 if (strcmp(test_name
, "insert_duplicate") == 0) {
427 rval
= insert_duplicate(bt
);
428 } else if (strcmp(test_name
, "remove_missing") == 0) {
429 rval
= remove_missing(bt
);
433 * Return 0, since callers will expect non-zero return values for
434 * these tests, and we should have crashed before getting here anyway.
436 (void) fprintf(stderr
, "Test: %s returned %d.\n", test_name
, rval
);
440 typedef struct btree_test
{
442 int (*func
)(zfs_btree_t
*, char *);
445 static btree_test_t test_table
[] = {
446 { "insert_find_remove", insert_find_remove
},
447 { "find_without_index", find_without_index
},
448 { "drain_tree", drain_tree
},
449 { "stress_tree", stress_tree
},
454 main(int argc
, char *argv
[])
456 char *negative_test
= NULL
;
457 int failed_tests
= 0;
462 while ((c
= getopt(argc
, argv
, "c:l:n:r:st:")) != -1) {
465 contents_frequency
= atoi(optarg
);
468 tree_limit
= atoi(optarg
);
471 negative_test
= optarg
;
477 stress_only
= B_TRUE
;
480 stress_timeout
= atoi(optarg
);
494 (void) gettimeofday(&tp
, NULL
);
500 zfs_btree_create(&bt
, zfs_btree_compare
, sizeof (uint64_t));
503 * This runs the named negative test. None of them should
504 * return, as they both cause crashes.
507 return (do_negative_test(&bt
, negative_test
));
510 fprintf(stderr
, "Seed: %u\n", seed
);
513 * This is a stress test that does operations on a btree over the
514 * requested timeout period, verifying them against identical
515 * operations in an avl tree.
517 if (stress_only
!= 0) {
518 return (stress_tree(&bt
, NULL
));
521 /* Do the positive tests */
522 btree_test_t
*test
= &test_table
[0];
526 char why
[BUFSIZE
] = {0};
527 zfs_btree_index_t
*idx
= NULL
;
529 (void) fprintf(stdout
, "%-20s", test
->name
);
530 retval
= test
->func(&bt
, why
);
533 (void) fprintf(stdout
, "ok\n");
535 (void) fprintf(stdout
, "failed with %d\n", retval
);
536 if (strlen(why
) != 0)
537 (void) fprintf(stdout
, "\t%s\n", why
);
542 /* Remove all the elements and re-verify the tree */
543 while ((rv
= zfs_btree_destroy_nodes(&bt
, &idx
)) != NULL
)
545 zfs_btree_verify(&bt
);
550 zfs_btree_verify(&bt
);
553 return (failed_tests
);