xref: /freebsd/sys/contrib/openzfs/cmd/zstream/zstream_selftest.c (revision 22649d4dba730d46244fd2dff4fd174903c8379f)
1*22649d4dSMartin Matuska // SPDX-License-Identifier: CDDL-1.0
2*22649d4dSMartin Matuska /*
3*22649d4dSMartin Matuska  * This file and its contents are supplied under the terms of the
4*22649d4dSMartin Matuska  * Common Development and Distribution License ("CDDL"), version 1.0.
5*22649d4dSMartin Matuska  * You may only use this file in accordance with the terms of version
6*22649d4dSMartin Matuska  * 1.0 of the CDDL.
7*22649d4dSMartin Matuska  *
8*22649d4dSMartin Matuska  * A full copy of the text of the CDDL should have accompanied this
9*22649d4dSMartin Matuska  * source.  A copy of the CDDL is also available via the Internet at
10*22649d4dSMartin Matuska  * https://opensource.org/license/CDDL-1.0.
11*22649d4dSMartin Matuska  */
12*22649d4dSMartin Matuska 
13*22649d4dSMartin Matuska /*
14*22649d4dSMartin Matuska  * Copyright (c) 2026 by Garth Snyder. All rights reserved.
15*22649d4dSMartin Matuska  */
16*22649d4dSMartin Matuska 
17*22649d4dSMartin Matuska /*
18*22649d4dSMartin Matuska  * zstream selftest: in-process unit tests for zstream's internal machinery.
19*22649d4dSMartin Matuska  *
20*22649d4dSMartin Matuska  *   zstream selftest [-l] [-s seed] [-t nthreads] module [test ...]
21*22649d4dSMartin Matuska  *
22*22649d4dSMartin Matuska  * Tests are grouped into modules (see zstream_selftest.h). With no test
23*22649d4dSMartin Matuska  * names, all of a module's tests run in order. -l lists the available
24*22649d4dSMartin Matuska  * tests. -s replays a previous run's PRNG seed. -t sets the size of the
25*22649d4dSMartin Matuska  * shared worker thread pool before any test runs.
26*22649d4dSMartin Matuska  *
27*22649d4dSMartin Matuska  * This subcommand is intentionally undocumented in zstream_usage() and the
28*22649d4dSMartin Matuska  * man page; it exists to be driven by the ZFS test suite.
29*22649d4dSMartin Matuska  */
30*22649d4dSMartin Matuska 
31*22649d4dSMartin Matuska #include <assert.h>
32*22649d4dSMartin Matuska #include <err.h>
33*22649d4dSMartin Matuska #include <libspl.h>
34*22649d4dSMartin Matuska #include <stdio.h>
35*22649d4dSMartin Matuska #include <stdlib.h>
36*22649d4dSMartin Matuska #include <string.h>
37*22649d4dSMartin Matuska #include <sys/random.h>
38*22649d4dSMartin Matuska #include <sys/stdtypes.h>
39*22649d4dSMartin Matuska #include <unistd.h>
40*22649d4dSMartin Matuska 
41*22649d4dSMartin Matuska #include "zstream.h"
42*22649d4dSMartin Matuska #include "zstream_backtrace.h"
43*22649d4dSMartin Matuska #include "zstream_queue.h"
44*22649d4dSMartin Matuska #include "zstream_selftest.h"
45*22649d4dSMartin Matuska 
46*22649d4dSMartin Matuska typedef struct {
47*22649d4dSMartin Matuska 	const char		*sm_name;
48*22649d4dSMartin Matuska 	const test_case_t	*sm_cases;
49*22649d4dSMartin Matuska } selftest_module_t;
50*22649d4dSMartin Matuska 
51*22649d4dSMartin Matuska static const selftest_module_t modules[] = {
52*22649d4dSMartin Matuska 	{ "queue", selftest_queue_cases },
53*22649d4dSMartin Matuska };
54*22649d4dSMartin Matuska 
55*22649d4dSMartin Matuska #define	NUM_MODULES	(sizeof (modules) / sizeof (modules[0]))
56*22649d4dSMartin Matuska 
57*22649d4dSMartin Matuska uint64_t selftest_seed;
58*22649d4dSMartin Matuska 
59*22649d4dSMartin Matuska static const char *current_test = "(startup)";
60*22649d4dSMartin Matuska 
61*22649d4dSMartin Matuska static void
selftest_usage(void)62*22649d4dSMartin Matuska selftest_usage(void)
63*22649d4dSMartin Matuska {
64*22649d4dSMartin Matuska 	(void) fprintf(stderr,
65*22649d4dSMartin Matuska 	    "usage: zstream selftest [-l] [-s seed] [-t nthreads] "
66*22649d4dSMartin Matuska 	    "module [test ...]\n"
67*22649d4dSMartin Matuska 	    "\n"
68*22649d4dSMartin Matuska 	    "\t-l         list available tests\n"
69*22649d4dSMartin Matuska 	    "\t-s seed    seed for pseudo-random workloads (for replays)\n"
70*22649d4dSMartin Matuska 	    "\t-t num     size of the shared worker thread pool\n"
71*22649d4dSMartin Matuska 	    "\n"
72*22649d4dSMartin Matuska 	    "Available modules:");
73*22649d4dSMartin Matuska 	for (int i = 0; i < NUM_MODULES; i++)
74*22649d4dSMartin Matuska 		(void) fprintf(stderr, " %s", modules[i].sm_name);
75*22649d4dSMartin Matuska 	(void) fprintf(stderr, "\n");
76*22649d4dSMartin Matuska 	exit(1);
77*22649d4dSMartin Matuska }
78*22649d4dSMartin Matuska 
79*22649d4dSMartin Matuska static const selftest_module_t *
find_module(const char * name)80*22649d4dSMartin Matuska find_module(const char *name)
81*22649d4dSMartin Matuska {
82*22649d4dSMartin Matuska 	for (int i = 0; i < NUM_MODULES; i++) {
83*22649d4dSMartin Matuska 		if (strcmp(name, modules[i].sm_name) == 0)
84*22649d4dSMartin Matuska 			return (&modules[i]);
85*22649d4dSMartin Matuska 	}
86*22649d4dSMartin Matuska 	warnx("unknown module '%s'", name);
87*22649d4dSMartin Matuska 	selftest_usage();
88*22649d4dSMartin Matuska 	return (NULL);	/* NOTREACHED */
89*22649d4dSMartin Matuska }
90*22649d4dSMartin Matuska 
91*22649d4dSMartin Matuska static void
list_tests(const selftest_module_t * module)92*22649d4dSMartin Matuska list_tests(const selftest_module_t *module)
93*22649d4dSMartin Matuska {
94*22649d4dSMartin Matuska 	for (int i = 0; i < NUM_MODULES; i++) {
95*22649d4dSMartin Matuska 		if (module != NULL && module != &modules[i])
96*22649d4dSMartin Matuska 			continue;
97*22649d4dSMartin Matuska 		(void) printf("%s:\n", modules[i].sm_name);
98*22649d4dSMartin Matuska 		for (const test_case_t *tc = modules[i].sm_cases;
99*22649d4dSMartin Matuska 		    tc->tc_name != NULL; tc++) {
100*22649d4dSMartin Matuska 			(void) printf("\t%s\n", tc->tc_name);
101*22649d4dSMartin Matuska 		}
102*22649d4dSMartin Matuska 	}
103*22649d4dSMartin Matuska }
104*22649d4dSMartin Matuska 
105*22649d4dSMartin Matuska static void
run_case(const test_case_t * tc)106*22649d4dSMartin Matuska run_case(const test_case_t *tc)
107*22649d4dSMartin Matuska {
108*22649d4dSMartin Matuska 	(void) printf("Running %-20s ... ", tc->tc_name);
109*22649d4dSMartin Matuska 	(void) fflush(stdout);
110*22649d4dSMartin Matuska 	current_test = tc->tc_name;
111*22649d4dSMartin Matuska 	watchdog_arm();
112*22649d4dSMartin Matuska 	tc->tc_func();
113*22649d4dSMartin Matuska 	watchdog_disarm();
114*22649d4dSMartin Matuska 	(void) printf("OK\n");
115*22649d4dSMartin Matuska }
116*22649d4dSMartin Matuska 
117*22649d4dSMartin Matuska static const test_case_t *
find_case(const selftest_module_t * module,const char * name)118*22649d4dSMartin Matuska find_case(const selftest_module_t *module, const char *name)
119*22649d4dSMartin Matuska {
120*22649d4dSMartin Matuska 	for (const test_case_t *tc = module->sm_cases;
121*22649d4dSMartin Matuska 	    tc->tc_name != NULL; tc++) {
122*22649d4dSMartin Matuska 		if (strcmp(name, tc->tc_name) == 0)
123*22649d4dSMartin Matuska 			return (tc);
124*22649d4dSMartin Matuska 	}
125*22649d4dSMartin Matuska 	errx(2, "module '%s' has no test named '%s' (try -l)",
126*22649d4dSMartin Matuska 	    module->sm_name, name);
127*22649d4dSMartin Matuska 	return (NULL);	/* NOTREACHED */
128*22649d4dSMartin Matuska }
129*22649d4dSMartin Matuska 
130*22649d4dSMartin Matuska int
zstream_do_selftest(int argc,char * argv[])131*22649d4dSMartin Matuska zstream_do_selftest(int argc, char *argv[])
132*22649d4dSMartin Matuska {
133*22649d4dSMartin Matuska 	boolean_t list_only = B_FALSE;
134*22649d4dSMartin Matuska 	boolean_t have_seed = B_FALSE;
135*22649d4dSMartin Matuska 	int nthreads = 0;
136*22649d4dSMartin Matuska 	char *end;
137*22649d4dSMartin Matuska 	int c;
138*22649d4dSMartin Matuska 
139*22649d4dSMartin Matuska 	while ((c = getopt(argc, argv, "ls:t:")) != -1) {
140*22649d4dSMartin Matuska 		switch (c) {
141*22649d4dSMartin Matuska 		case 'l':
142*22649d4dSMartin Matuska 			list_only = B_TRUE;
143*22649d4dSMartin Matuska 			break;
144*22649d4dSMartin Matuska 		case 's':
145*22649d4dSMartin Matuska 			selftest_seed = strtoull(optarg, &end, 0);
146*22649d4dSMartin Matuska 			if (*optarg == '\0' || *end != '\0') {
147*22649d4dSMartin Matuska 				warnx("failed to parse seed '%s'", optarg);
148*22649d4dSMartin Matuska 				selftest_usage();
149*22649d4dSMartin Matuska 			}
150*22649d4dSMartin Matuska 			have_seed = B_TRUE;
151*22649d4dSMartin Matuska 			break;
152*22649d4dSMartin Matuska 		case 't':
153*22649d4dSMartin Matuska 			if (sscanf(optarg, "%d", &nthreads) != 1) {
154*22649d4dSMartin Matuska 				warnx("failed to parse num_threads '%s'",
155*22649d4dSMartin Matuska 				    optarg);
156*22649d4dSMartin Matuska 				selftest_usage();
157*22649d4dSMartin Matuska 			}
158*22649d4dSMartin Matuska 			zstream_queue_set_num_threads(nthreads);
159*22649d4dSMartin Matuska 			break;
160*22649d4dSMartin Matuska 		case '?':
161*22649d4dSMartin Matuska 			warnx("invalid option '%c'", optopt);
162*22649d4dSMartin Matuska 			selftest_usage();
163*22649d4dSMartin Matuska 			break;
164*22649d4dSMartin Matuska 		}
165*22649d4dSMartin Matuska 	}
166*22649d4dSMartin Matuska 	argc -= optind;
167*22649d4dSMartin Matuska 	argv += optind;
168*22649d4dSMartin Matuska 
169*22649d4dSMartin Matuska 	if (list_only) {
170*22649d4dSMartin Matuska 		list_tests(argc > 0 ? find_module(argv[0]) : NULL);
171*22649d4dSMartin Matuska 		return (0);
172*22649d4dSMartin Matuska 	}
173*22649d4dSMartin Matuska 	if (argc < 1)
174*22649d4dSMartin Matuska 		selftest_usage();
175*22649d4dSMartin Matuska 
176*22649d4dSMartin Matuska 	const selftest_module_t *module = find_module(argv[0]);
177*22649d4dSMartin Matuska 
178*22649d4dSMartin Matuska 	/* Needed for random_get_pseudo_bytes() */
179*22649d4dSMartin Matuska 	libspl_init();
180*22649d4dSMartin Matuska 	watchdog_init();
181*22649d4dSMartin Matuska 
182*22649d4dSMartin Matuska 	if (!have_seed)
183*22649d4dSMartin Matuska 		random_get_pseudo_bytes((uint8_t *)&selftest_seed,
184*22649d4dSMartin Matuska 		    sizeof (selftest_seed));
185*22649d4dSMartin Matuska 	(void) printf("Using seed 0x%016jx (replay with -s 0x%jx)\n",
186*22649d4dSMartin Matuska 	    (uintmax_t)selftest_seed, (uintmax_t)selftest_seed);
187*22649d4dSMartin Matuska 
188*22649d4dSMartin Matuska 	int count = 0;
189*22649d4dSMartin Matuska 	if (argc == 1) {
190*22649d4dSMartin Matuska 		for (const test_case_t *tc = module->sm_cases;
191*22649d4dSMartin Matuska 		    tc->tc_name != NULL; tc++) {
192*22649d4dSMartin Matuska 			run_case(tc);
193*22649d4dSMartin Matuska 			count++;
194*22649d4dSMartin Matuska 		}
195*22649d4dSMartin Matuska 	} else {
196*22649d4dSMartin Matuska 		for (int i = 1; i < argc; i++) {
197*22649d4dSMartin Matuska 			run_case(find_case(module, argv[i]));
198*22649d4dSMartin Matuska 			count++;
199*22649d4dSMartin Matuska 		}
200*22649d4dSMartin Matuska 	}
201*22649d4dSMartin Matuska 	(void) printf("All %d %s selftest%s passed\n", count, module->sm_name,
202*22649d4dSMartin Matuska 	    count == 1 ? "" : "s");
203*22649d4dSMartin Matuska 	libspl_fini();
204*22649d4dSMartin Matuska 	return (0);
205*22649d4dSMartin Matuska }
206