Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/tests/zfs-tests/cmd/btree_test.c
48529 views
1
// SPDX-License-Identifier: CDDL-1.0
2
/*
3
* This file and its contents are supplied under the terms of the
4
* Common Development and Distribution License ("CDDL"), version 1.0.
5
* You may only use this file in accordance with the terms of version
6
* 1.0 of the CDDL.
7
*
8
* A full copy of the text of the CDDL should have accompanied this
9
* source. A copy of the CDDL is also available via the Internet at
10
* http://www.illumos.org/license/CDDL.
11
*/
12
13
/*
14
* Copyright (c) 2019 by Delphix. All rights reserved.
15
*/
16
17
#include <stdio.h>
18
#include <stdlib.h>
19
#include <string.h>
20
#include <sys/avl.h>
21
#include <sys/btree.h>
22
#include <sys/time.h>
23
#include <sys/resource.h>
24
25
#define BUFSIZE 256
26
27
static int seed = 0;
28
static int stress_timeout = 180;
29
static int contents_frequency = 100;
30
static int tree_limit = 64 * 1024;
31
static boolean_t stress_only = B_FALSE;
32
33
static void
34
usage(int exit_value)
35
{
36
(void) fprintf(stderr, "Usage:\tbtree_test -n <test_name>\n");
37
(void) fprintf(stderr, "\tbtree_test -s [-r <seed>] [-l <limit>] "
38
"[-t timeout>] [-c check_contents]\n");
39
(void) fprintf(stderr, "\tbtree_test [-r <seed>] [-l <limit>] "
40
"[-t timeout>] [-c check_contents]\n");
41
(void) fprintf(stderr, "\n With the -n option, run the named "
42
"negative test. With the -s option,\n");
43
(void) fprintf(stderr, " run the stress test according to the "
44
"other options passed. With\n");
45
(void) fprintf(stderr, " neither, run all the positive tests, "
46
"including the stress test with\n");
47
(void) fprintf(stderr, " the default options.\n");
48
(void) fprintf(stderr, "\n Options that control the stress test\n");
49
(void) fprintf(stderr, "\t-c stress iterations after which to compare "
50
"tree contents [default: 100]\n");
51
(void) fprintf(stderr, "\t-l the largest value to allow in the tree "
52
"[default: 1M]\n");
53
(void) fprintf(stderr, "\t-r random seed [default: from "
54
"gettimeofday()]\n");
55
(void) fprintf(stderr, "\t-t seconds to let the stress test run "
56
"[default: 180]\n");
57
exit(exit_value);
58
}
59
60
typedef struct int_node {
61
avl_node_t node;
62
uint64_t data;
63
} int_node_t;
64
65
/*
66
* Utility functions
67
*/
68
69
static int
70
avl_compare(const void *v1, const void *v2)
71
{
72
const int_node_t *n1 = v1;
73
const int_node_t *n2 = v2;
74
uint64_t a = n1->data;
75
uint64_t b = n2->data;
76
77
return (TREE_CMP(a, b));
78
}
79
80
static int
81
zfs_btree_compare(const void *v1, const void *v2)
82
{
83
const uint64_t *a = v1;
84
const uint64_t *b = v2;
85
86
return (TREE_CMP(*a, *b));
87
}
88
89
static void
90
verify_contents(avl_tree_t *avl, zfs_btree_t *bt)
91
{
92
static int count = 0;
93
zfs_btree_index_t bt_idx = {0};
94
int_node_t *node;
95
uint64_t *data;
96
97
boolean_t forward = count % 2 == 0 ? B_TRUE : B_FALSE;
98
count++;
99
100
ASSERT3U(avl_numnodes(avl), ==, zfs_btree_numnodes(bt));
101
if (forward == B_TRUE) {
102
node = avl_first(avl);
103
data = zfs_btree_first(bt, &bt_idx);
104
} else {
105
node = avl_last(avl);
106
data = zfs_btree_last(bt, &bt_idx);
107
}
108
109
while (node != NULL) {
110
ASSERT3U(*data, ==, node->data);
111
if (forward == B_TRUE) {
112
data = zfs_btree_next(bt, &bt_idx, &bt_idx);
113
node = AVL_NEXT(avl, node);
114
} else {
115
data = zfs_btree_prev(bt, &bt_idx, &bt_idx);
116
node = AVL_PREV(avl, node);
117
}
118
}
119
}
120
121
static void
122
verify_node(avl_tree_t *avl, zfs_btree_t *bt, int_node_t *node)
123
{
124
zfs_btree_index_t bt_idx = {0};
125
zfs_btree_index_t bt_idx2 = {0};
126
int_node_t *inp;
127
uint64_t data = node->data;
128
uint64_t *rv = NULL;
129
130
ASSERT3U(avl_numnodes(avl), ==, zfs_btree_numnodes(bt));
131
ASSERT3P((rv = (uint64_t *)zfs_btree_find(bt, &data, &bt_idx)), !=,
132
NULL);
133
ASSERT3S(*rv, ==, data);
134
ASSERT3P(zfs_btree_get(bt, &bt_idx), !=, NULL);
135
ASSERT3S(data, ==, *(uint64_t *)zfs_btree_get(bt, &bt_idx));
136
137
if ((inp = AVL_NEXT(avl, node)) != NULL) {
138
ASSERT3P((rv = zfs_btree_next(bt, &bt_idx, &bt_idx2)), !=,
139
NULL);
140
ASSERT3P(rv, ==, zfs_btree_get(bt, &bt_idx2));
141
ASSERT3S(inp->data, ==, *rv);
142
} else {
143
ASSERT3U(data, ==, *(uint64_t *)zfs_btree_last(bt, &bt_idx));
144
}
145
146
if ((inp = AVL_PREV(avl, node)) != NULL) {
147
ASSERT3P((rv = zfs_btree_prev(bt, &bt_idx, &bt_idx2)), !=,
148
NULL);
149
ASSERT3P(rv, ==, zfs_btree_get(bt, &bt_idx2));
150
ASSERT3S(inp->data, ==, *rv);
151
} else {
152
ASSERT3U(data, ==, *(uint64_t *)zfs_btree_first(bt, &bt_idx));
153
}
154
}
155
156
/*
157
* Tests
158
*/
159
160
/* Verify that zfs_btree_find works correctly with a NULL index. */
161
static int
162
find_without_index(zfs_btree_t *bt, char *why)
163
{
164
u_longlong_t *p, i = 12345;
165
166
zfs_btree_add(bt, &i);
167
if ((p = (u_longlong_t *)zfs_btree_find(bt, &i, NULL)) == NULL ||
168
*p != i) {
169
(void) snprintf(why, BUFSIZE, "Unexpectedly found %llu\n",
170
p == NULL ? 0 : *p);
171
return (1);
172
}
173
174
i++;
175
176
if ((p = (u_longlong_t *)zfs_btree_find(bt, &i, NULL)) != NULL) {
177
(void) snprintf(why, BUFSIZE, "Found bad value: %llu\n", *p);
178
return (1);
179
}
180
181
return (0);
182
}
183
184
/* Verify simple insertion and removal from the tree. */
185
static int
186
insert_find_remove(zfs_btree_t *bt, char *why)
187
{
188
u_longlong_t *p, i = 12345;
189
zfs_btree_index_t bt_idx = {0};
190
191
/* Insert 'i' into the tree, and attempt to find it again. */
192
zfs_btree_add(bt, &i);
193
if ((p = (u_longlong_t *)zfs_btree_find(bt, &i, &bt_idx)) == NULL) {
194
(void) snprintf(why, BUFSIZE, "Didn't find value in tree\n");
195
return (1);
196
} else if (*p != i) {
197
(void) snprintf(why, BUFSIZE, "Found (%llu) in tree\n", *p);
198
return (1);
199
}
200
ASSERT3S(zfs_btree_numnodes(bt), ==, 1);
201
zfs_btree_verify(bt);
202
203
/* Remove 'i' from the tree, and verify it is not found. */
204
zfs_btree_remove(bt, &i);
205
if ((p = (u_longlong_t *)zfs_btree_find(bt, &i, &bt_idx)) != NULL) {
206
(void) snprintf(why, BUFSIZE,
207
"Found removed value (%llu)\n", *p);
208
return (1);
209
}
210
ASSERT0(zfs_btree_numnodes(bt));
211
zfs_btree_verify(bt);
212
213
return (0);
214
}
215
216
/*
217
* Add a number of random entries into a btree and avl tree. Then walk them
218
* backwards and forwards while emptying the tree, verifying the trees look
219
* the same.
220
*/
221
static int
222
drain_tree(zfs_btree_t *bt, char *why)
223
{
224
avl_tree_t avl;
225
int i = 0;
226
int_node_t *node;
227
avl_index_t avl_idx = {0};
228
zfs_btree_index_t bt_idx = {0};
229
230
avl_create(&avl, avl_compare, sizeof (int_node_t),
231
offsetof(int_node_t, node));
232
233
/* Fill both trees with the same data */
234
for (i = 0; i < 64 * 1024; i++) {
235
u_longlong_t randval = random();
236
if (zfs_btree_find(bt, &randval, &bt_idx) != NULL) {
237
continue;
238
}
239
zfs_btree_add_idx(bt, &randval, &bt_idx);
240
241
node = malloc(sizeof (int_node_t));
242
if (node == NULL) {
243
perror("malloc");
244
exit(EXIT_FAILURE);
245
}
246
247
node->data = randval;
248
if (avl_find(&avl, node, &avl_idx) != NULL) {
249
(void) snprintf(why, BUFSIZE,
250
"Found in avl: %llu\n", randval);
251
return (1);
252
}
253
avl_insert(&avl, node, avl_idx);
254
}
255
256
/* Remove data from either side of the trees, comparing the data */
257
while (avl_numnodes(&avl) != 0) {
258
uint64_t *data;
259
260
ASSERT3U(avl_numnodes(&avl), ==, zfs_btree_numnodes(bt));
261
if (avl_numnodes(&avl) % 2 == 0) {
262
node = avl_first(&avl);
263
data = zfs_btree_first(bt, &bt_idx);
264
} else {
265
node = avl_last(&avl);
266
data = zfs_btree_last(bt, &bt_idx);
267
}
268
ASSERT3U(node->data, ==, *data);
269
zfs_btree_remove_idx(bt, &bt_idx);
270
avl_remove(&avl, node);
271
272
if (avl_numnodes(&avl) == 0) {
273
break;
274
}
275
276
node = avl_first(&avl);
277
ASSERT3U(node->data, ==,
278
*(uint64_t *)zfs_btree_first(bt, NULL));
279
node = avl_last(&avl);
280
ASSERT3U(node->data, ==, *(uint64_t *)zfs_btree_last(bt, NULL));
281
}
282
ASSERT0(zfs_btree_numnodes(bt));
283
284
void *avl_cookie = NULL;
285
while ((node = avl_destroy_nodes(&avl, &avl_cookie)) != NULL)
286
free(node);
287
avl_destroy(&avl);
288
289
return (0);
290
}
291
292
/*
293
* This test uses an avl and btree, and continually processes new random
294
* values. Each value is either removed or inserted, depending on whether
295
* or not it is found in the tree. The test periodically checks that both
296
* trees have the same data and does consistency checks. This stress
297
* option can also be run on its own from the command line.
298
*/
299
static int
300
stress_tree(zfs_btree_t *bt, char *why)
301
{
302
(void) why;
303
avl_tree_t avl;
304
int_node_t *node;
305
struct timeval tp;
306
time_t t0;
307
int insertions = 0, removals = 0, iterations = 0;
308
u_longlong_t max = 0, min = UINT64_MAX;
309
310
(void) gettimeofday(&tp, NULL);
311
t0 = tp.tv_sec;
312
313
avl_create(&avl, avl_compare, sizeof (int_node_t),
314
offsetof(int_node_t, node));
315
316
while (1) {
317
zfs_btree_index_t bt_idx = {0};
318
avl_index_t avl_idx = {0};
319
320
uint64_t randval = random() % tree_limit;
321
node = malloc(sizeof (*node));
322
if (node == NULL) {
323
perror("malloc");
324
exit(EXIT_FAILURE);
325
}
326
node->data = randval;
327
328
max = randval > max ? randval : max;
329
min = randval < min ? randval : min;
330
331
void *ret = avl_find(&avl, node, &avl_idx);
332
if (ret == NULL) {
333
insertions++;
334
avl_insert(&avl, node, avl_idx);
335
ASSERT3P(zfs_btree_find(bt, &randval, &bt_idx), ==,
336
NULL);
337
zfs_btree_add_idx(bt, &randval, &bt_idx);
338
verify_node(&avl, bt, node);
339
} else {
340
removals++;
341
verify_node(&avl, bt, ret);
342
zfs_btree_remove(bt, &randval);
343
avl_remove(&avl, ret);
344
free(ret);
345
free(node);
346
}
347
348
zfs_btree_verify(bt);
349
350
iterations++;
351
if (iterations % contents_frequency == 0) {
352
verify_contents(&avl, bt);
353
}
354
355
zfs_btree_verify(bt);
356
357
(void) gettimeofday(&tp, NULL);
358
if (tp.tv_sec > t0 + stress_timeout) {
359
fprintf(stderr, "insertions/removals: %u/%u\nmax/min: "
360
"%llu/%llu\n", insertions, removals, max, min);
361
break;
362
}
363
}
364
365
void *avl_cookie = NULL;
366
while ((node = avl_destroy_nodes(&avl, &avl_cookie)) != NULL)
367
free(node);
368
avl_destroy(&avl);
369
370
if (stress_only) {
371
zfs_btree_index_t *idx = NULL;
372
while (zfs_btree_destroy_nodes(bt, &idx) != NULL)
373
;
374
zfs_btree_verify(bt);
375
}
376
377
return (0);
378
}
379
380
/*
381
* Verify inserting a duplicate value will cause a crash.
382
* Note: negative test; return of 0 is a failure.
383
*/
384
static int
385
insert_duplicate(zfs_btree_t *bt)
386
{
387
uint64_t i = 23456;
388
zfs_btree_index_t bt_idx = {0};
389
390
if (zfs_btree_find(bt, &i, &bt_idx) != NULL) {
391
fprintf(stderr, "Found value in empty tree.\n");
392
return (0);
393
}
394
zfs_btree_add_idx(bt, &i, &bt_idx);
395
if (zfs_btree_find(bt, &i, &bt_idx) == NULL) {
396
fprintf(stderr, "Did not find expected value.\n");
397
return (0);
398
}
399
400
/* Crash on inserting a duplicate */
401
zfs_btree_add_idx(bt, &i, NULL);
402
403
return (0);
404
}
405
406
/*
407
* Verify removing a non-existent value will cause a crash.
408
* Note: negative test; return of 0 is a failure.
409
*/
410
static int
411
remove_missing(zfs_btree_t *bt)
412
{
413
uint64_t i = 23456;
414
zfs_btree_index_t bt_idx = {0};
415
416
if (zfs_btree_find(bt, &i, &bt_idx) != NULL) {
417
fprintf(stderr, "Found value in empty tree.\n");
418
return (0);
419
}
420
421
/* Crash removing a nonexistent entry */
422
zfs_btree_remove(bt, &i);
423
424
return (0);
425
}
426
427
static int
428
do_negative_test(zfs_btree_t *bt, char *test_name)
429
{
430
int rval = 0;
431
struct rlimit rlim = {0};
432
433
(void) setrlimit(RLIMIT_CORE, &rlim);
434
435
if (strcmp(test_name, "insert_duplicate") == 0) {
436
rval = insert_duplicate(bt);
437
} else if (strcmp(test_name, "remove_missing") == 0) {
438
rval = remove_missing(bt);
439
}
440
441
/*
442
* Return 0, since callers will expect non-zero return values for
443
* these tests, and we should have crashed before getting here anyway.
444
*/
445
(void) fprintf(stderr, "Test: %s returned %d.\n", test_name, rval);
446
return (0);
447
}
448
449
typedef struct btree_test {
450
const char *name;
451
int (*func)(zfs_btree_t *, char *);
452
} btree_test_t;
453
454
static btree_test_t test_table[] = {
455
{ "insert_find_remove", insert_find_remove },
456
{ "find_without_index", find_without_index },
457
{ "drain_tree", drain_tree },
458
{ "stress_tree", stress_tree },
459
{ NULL, NULL }
460
};
461
462
int
463
main(int argc, char *argv[])
464
{
465
char *negative_test = NULL;
466
int failed_tests = 0;
467
struct timeval tp;
468
zfs_btree_t bt;
469
int c;
470
471
while ((c = getopt(argc, argv, "c:l:n:r:st:")) != -1) {
472
switch (c) {
473
case 'c':
474
contents_frequency = atoi(optarg);
475
break;
476
case 'l':
477
tree_limit = atoi(optarg);
478
break;
479
case 'n':
480
negative_test = optarg;
481
break;
482
case 'r':
483
seed = atoi(optarg);
484
break;
485
case 's':
486
stress_only = B_TRUE;
487
break;
488
case 't':
489
stress_timeout = atoi(optarg);
490
break;
491
case 'h':
492
default:
493
usage(1);
494
break;
495
}
496
}
497
498
if (seed == 0) {
499
(void) gettimeofday(&tp, NULL);
500
seed = tp.tv_sec;
501
}
502
srandom(seed);
503
504
zfs_btree_init();
505
zfs_btree_create(&bt, zfs_btree_compare, NULL, sizeof (uint64_t));
506
507
/*
508
* This runs the named negative test. None of them should
509
* return, as they both cause crashes.
510
*/
511
if (negative_test) {
512
return (do_negative_test(&bt, negative_test));
513
}
514
515
fprintf(stderr, "Seed: %u\n", seed);
516
517
/*
518
* This is a stress test that does operations on a btree over the
519
* requested timeout period, verifying them against identical
520
* operations in an avl tree.
521
*/
522
if (stress_only != 0) {
523
return (stress_tree(&bt, NULL));
524
}
525
526
/* Do the positive tests */
527
btree_test_t *test = &test_table[0];
528
while (test->name) {
529
int retval;
530
char why[BUFSIZE] = {0};
531
zfs_btree_index_t *idx = NULL;
532
533
(void) fprintf(stdout, "%-20s", test->name);
534
retval = test->func(&bt, why);
535
536
if (retval == 0) {
537
(void) fprintf(stdout, "ok\n");
538
} else {
539
(void) fprintf(stdout, "failed with %d\n", retval);
540
if (strlen(why) != 0)
541
(void) fprintf(stdout, "\t%s\n", why);
542
why[0] = '\0';
543
failed_tests++;
544
}
545
546
/* Remove all the elements and re-verify the tree */
547
while (zfs_btree_destroy_nodes(&bt, &idx) != NULL)
548
;
549
zfs_btree_verify(&bt);
550
551
test++;
552
}
553
554
zfs_btree_verify(&bt);
555
zfs_btree_fini();
556
557
return (failed_tests);
558
}
559
560