xref: /illumos-gate/usr/src/test/os-tests/tests/hexdump/hexdump_basic.c (revision 43379a280422204006ceee5a5c5380444cb515d9)
1 /*
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
5  * 1.0 of the CDDL.
6  *
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.
10  */
11 
12 /*
13  * Copyright 2024 Oxide Computer Company
14  */
15 
16 /*
17  * Basic tests for the common hexdump routine.
18  */
19 
20 #include <err.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <strings.h>
27 #include <unistd.h>
28 #include <sys/debug.h>
29 #include <sys/ilstr.h>
30 #include <sys/hexdump.h>
31 #include <sys/mman.h>
32 #include <sys/stat.h>
33 #include <sys/stdbool.h>
34 #include <sys/sysmacros.h>
35 #include <sys/types.h>
36 
37 #define	DATADIR	"/opt/os-tests/tests/hexdump/data"
38 
39 const char *
_umem_debug_init(void)40 _umem_debug_init(void)
41 {
42 	return ("default,verbose");
43 }
44 
45 const char *
_umem_logging_init(void)46 _umem_logging_init(void)
47 {
48 	return ("transaction,contents,fail");
49 }
50 
51 typedef struct test {
52 	const char		*name;
53 	hexdump_flag_t		flags;
54 	uint8_t			width;
55 	uint8_t			grouping;
56 	uint64_t		addr;
57 	uint8_t			indent;
58 	uint8_t			marker;
59 } test_t;
60 
61 test_t tests[] = {
62 	{
63 		.name = "basic",
64 	}, {
65 		.name = "header",
66 		.flags = HDF_HEADER,
67 	}, {
68 		.name = "address",
69 		.flags = HDF_ADDRESS,
70 	}, {
71 		.name = "ascii",
72 		.flags = HDF_ASCII,
73 	}, {
74 		.name = "dedup",
75 		.flags = HDF_DEDUP,
76 	}, {
77 		.name = "doublespace",
78 		.flags = HDF_DOUBLESPACE,
79 	}, {
80 		.name = "address+header",
81 		.flags = HDF_ADDRESS | HDF_HEADER,
82 	}, {
83 		.name = "default",
84 		.flags = HDF_DEFAULT,
85 	}, {
86 		.name = "marker1",
87 		.flags = HDF_DEFAULT | HDF_DEDUP,
88 		.marker = 5
89 	}, {
90 		.name = "addr1",
91 		.flags = HDF_DEFAULT | HDF_DEDUP,
92 		.addr = 0x876543
93 	}, {
94 		.name = "addr2",
95 		.flags = HDF_DEFAULT | HDF_DEDUP,
96 		.addr = 0xffff8
97 	}, {
98 		.name = "align1",
99 		.flags = HDF_DEFAULT | HDF_DEDUP | HDF_ALIGN,
100 		.addr = 0x876543
101 	}, {
102 		.name = "indent",
103 		.flags = HDF_DEFAULT | HDF_DEDUP,
104 		.indent = 3
105 	}, {
106 		.name = "group2",
107 		.flags = HDF_DEFAULT | HDF_DEDUP,
108 		.addr = 0x876543,
109 		.grouping = 2,
110 	}, {
111 		.name = "group4",
112 		.flags = HDF_DEFAULT | HDF_DEDUP,
113 		.addr = 0x876543,
114 		.grouping = 4,
115 	}, {
116 		.name = "group8",
117 		.flags = HDF_DEFAULT | HDF_DEDUP,
118 		.addr = 0x876543,
119 		.grouping = 8,
120 	}, {
121 		.name = "width12",
122 		.flags = HDF_DEFAULT | HDF_DEDUP,
123 		.addr = 0x876543,
124 		.width = 12,
125 		.grouping = 4
126 	}, {
127 		.name = "wide1",
128 		.flags = HDF_ADDRESS | HDF_HEADER,
129 		.addr = 0x876543,
130 		.width = 32,
131 		.grouping = 8
132 	}, {
133 		.name = "narrow1",
134 		.flags = HDF_DEFAULT | HDF_DOUBLESPACE,
135 		.addr = 0x876543,
136 		.width = 4,
137 		.grouping = 2
138 	}, {
139 		.name = "narrow2",
140 		.flags = HDF_DEFAULT | HDF_DEDUP,
141 		.addr = 0x876543,
142 		.width = 1,
143 		.grouping = 1
144 	}
145 };
146 
147 static char *flagdescr[] = {
148 	"HEADER",
149 	"ADDRESS",
150 	"ASCII",
151 	"ALIGN",
152 	"DEDUP",
153 	"DOUBLESPACE",
154 };
155 
156 static void
descr(test_t * t,ilstr_t * i)157 descr(test_t *t, ilstr_t *i)
158 {
159 	ilstr_append_str(i, "=============================================\n");
160 	ilstr_aprintf(i, "[%s] w=%u g=%u a=0x%x - ",
161 	    t->name, t->width, t->grouping, t->addr);
162 
163 	int flags = t->flags;
164 	bool first = true;
165 	while (flags != 0) {
166 		int b = fls(flags);
167 		if (b == 0)
168 			break;
169 		b--;
170 		VERIFY3S(b, <, ARRAY_SIZE(flagdescr));
171 		if (first)
172 			first = false;
173 		else
174 			ilstr_append_char(i, ' ');
175 		ilstr_aprintf(i, "%s", flagdescr[b]);
176 		flags &= ~(1<< b);
177 	}
178 	ilstr_append_char(i, '\n');
179 	ilstr_append_str(i, "=============================================\n");
180 }
181 
182 static int
cb(void * arg,uint64_t addr __unused,const char * str,size_t l)183 cb(void *arg, uint64_t addr __unused, const char *str, size_t l)
184 {
185 	ilstr_t *i = arg;
186 
187 	ilstr_append_str(i, str);
188 	ilstr_append_char(i, '\n');
189 
190 	return (0);
191 }
192 
193 static void
run(test_t * t,uint8_t * data,size_t len,ilstr_t * i)194 run(test_t *t, uint8_t *data, size_t len, ilstr_t *i)
195 {
196 	hexdump_t hd;
197 
198 	descr(t, i);
199 
200 	hexdump_init(&hd);
201 	if (t->width != 0)
202 		hexdump_set_width(&hd, t->width);
203 	if (t->grouping != 0)
204 		hexdump_set_grouping(&hd, t->grouping);
205 	if (t->addr != 0)
206 		hexdump_set_addr(&hd, t->addr);
207 	if (t->indent != 0)
208 		hexdump_set_indent(&hd, t->indent);
209 	if (t->marker != 0)
210 		hexdump_set_marker(&hd, t->marker);
211 
212 	VERIFY0(hexdumph(&hd, data, len, t->flags, cb, (void *)i));
213 
214 	hexdump_fini(&hd);
215 
216 	VERIFY3U(ilstr_errno(i), ==, ILSTR_ERROR_OK);
217 }
218 
219 static uint8_t *
mapfile(const char * filename,size_t * lenp)220 mapfile(const char *filename, size_t *lenp)
221 {
222 	uint8_t *p;
223 	struct stat st;
224 	int fd;
225 
226 	if ((fd = open(filename, O_RDONLY)) == -1)
227 		err(EXIT_FAILURE, "could not open '%s'", filename);
228 
229 	if (fstat(fd, &st) == -1)
230 		err(EXIT_FAILURE, "failed to stat '%s'", filename);
231 
232 	p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
233 	if (p == MAP_FAILED)
234 		err(EXIT_FAILURE, "failed to mmap 0x%lx bytes from '%s'",
235 		    st.st_size, filename);
236 
237 	VERIFY0(close(fd));
238 
239 	*lenp = st.st_size;
240 	return (p);
241 }
242 
243 static void __PRINTFLIKE(2) __NORETURN
usage(int ec,const char * fmt,...)244 usage(int ec, const char *fmt, ...)
245 {
246 	va_list ap;
247 
248 	if (fmt != NULL) {
249 		va_start(ap, fmt);
250 		(void) vfprintf(stderr, fmt, ap);
251 		va_end(ap);
252 		(void) fprintf(stderr, "\n");
253 	}
254 
255 	(void) fprintf(stderr,
256 	    "Usage:\n"
257 	    "    -d <directory>  specify data directory\n"
258 	    "                    (default: %s)\n"
259 	    "    -g              generate baseline files\n"
260 	    "    -h              show usage\n"
261 	    "    -t <test>       run just the test named <test>\n"
262 	    "    -v              send test output to stdout\n",
263 	    DATADIR);
264 	exit(ec);
265 }
266 
267 int
main(int argc,char ** argv)268 main(int argc, char **argv)
269 {
270 	uint8_t *data;
271 	const char *datadir = DATADIR;
272 	char buf[MAXPATHLEN + 1];
273 	const char *test = NULL;
274 	ilstr_t testout;
275 	uint_t failures = 0;
276 	size_t maplen;
277 	int c;
278 
279 	enum {
280 		MODE_TEST,
281 		MODE_GENERATE,
282 		MODE_DUMP
283 	} testmode = MODE_TEST;
284 
285 	while ((c = getopt(argc, argv, ":d:ght:v")) != -1) {
286 		switch (c) {
287 		case 'd':
288 			datadir = optarg;
289 			break;
290 		case 'g':
291 			testmode = MODE_GENERATE;
292 			break;
293 		case 'h':
294 			usage(0, NULL);
295 		case 't':
296 			test = optarg;
297 			break;
298 		case 'v':
299 			testmode = MODE_DUMP;
300 			break;
301 		case ':':
302 			usage(EXIT_FAILURE,
303 			    "Option -%c requires an operand\n", optopt);
304 		case '?':
305 			usage(EXIT_FAILURE, "Unknown option: -%c", optopt);
306 		}
307 	}
308 
309 	if (snprintf(buf, sizeof (buf), "%s/_input", datadir) >= sizeof (buf))
310 		errx(EXIT_FAILURE, "Overflow building data dir path");
311 
312 	data = mapfile(buf, &maplen);
313 
314 	ilstr_init(&testout, 0);
315 
316 	for (size_t i = 0; i < ARRAY_SIZE(tests); i++) {
317 		if (test != NULL && strcmp(test, tests[i].name) != 0)
318 			continue;
319 
320 		if (snprintf(buf, sizeof (buf), "%s/%s", datadir,
321 		    tests[i].name) >= sizeof (buf)) {
322 			errx(EXIT_FAILURE, "Overflow building output path");
323 		}
324 
325 		run(&tests[i], data, maplen, &testout);
326 
327 		switch (testmode) {
328 		case MODE_TEST: {
329 			uint8_t *refdata;
330 			size_t reflen;
331 
332 			refdata = mapfile(buf, &reflen);
333 
334 			if (ilstr_len(&testout) != reflen ||
335 			    memcmp(ilstr_cstr(&testout), refdata,
336 			    reflen) != 0) {
337 				failures++;
338 				(void) fprintf(stderr,
339 				    "Hexdump '%s' output mismatch",
340 				    tests[i].name);
341 				(void) fprintf(stderr, "== Expected:\n%s\n",
342 				    refdata);
343 				(void) fprintf(stderr, "== Got:\n%s\n",
344 				    ilstr_cstr(&testout));
345 			}
346 
347 			VERIFY0(munmap(refdata, reflen));
348 			break;
349 		}
350 		case MODE_GENERATE: {
351 			FILE *fp;
352 
353 			fp = fopen(buf, "w");
354 			if (fp == NULL)
355 				err(EXIT_FAILURE, "Failed to create %s", buf);
356 			(void) fprintf(fp, "%s", ilstr_cstr(&testout));
357 			VERIFY0(fclose(fp));
358 			break;
359 		}
360 		case MODE_DUMP:
361 			(void) fprintf(stdout, "%s\n", ilstr_cstr(&testout));
362 			break;
363 		}
364 		ilstr_reset(&testout);
365 	}
366 
367 	ilstr_fini(&testout);
368 
369 	VERIFY0(munmap(data, maplen));
370 
371 	if (testmode == MODE_TEST && failures == 0)
372 		(void) printf("All hexdump tests have passed.\n");
373 
374 	return (failures > 0 ? EXIT_FAILURE : 0);
375 }
376