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