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 * 40 _umem_debug_init(void) 41 { 42 return ("default,verbose"); 43 } 44 45 const char * 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 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 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 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 * 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 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 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