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 2019 Joyent, Inc. 14 */ 15 16 #include <errno.h> 17 #include <limits.h> 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <string.h> 21 #include <sys/sysmacros.h> 22 #include <demangle-sys.h> 23 #include "demangle_test.h" 24 25 #define ALL_TESTS ULONG_MAX 26 27 SET_DECLARE(demangle_tests, demtest_group_t); 28 SET_DECLARE(demangle_mtests, demtest_mgroup_t); 29 30 static boolean_t 31 check_failure(size_t i, demtest_case_t *tc, const char *dem, boolean_t res) 32 { 33 int savederr = errno; 34 35 if (dem == NULL && (savederr == EINVAL || savederr == ENAMETOOLONG)) 36 return (B_TRUE); 37 38 if (res) 39 (void) printf("FAILURE\n"); 40 41 if (dem != NULL) { 42 (void) printf(" [%zu] Successfully demanged an invalid " 43 "name\n", i); 44 (void) printf(" Name: '%s'\n", tc->dtc_mangled); 45 (void) printf(" Demangled: '%s'\n", dem); 46 return (B_FALSE); 47 } 48 49 (void) printf(" [%zu] demangle() returned an unexpected error\n", i); 50 (void) printf(" Name: '%s'\n", tc->dtc_mangled); 51 (void) printf(" Errno: %d %s\n", savederr, strerror(savederr)); 52 return (B_FALSE); 53 } 54 55 static boolean_t 56 check_success(size_t i, demtest_case_t *tc, const char *dem, boolean_t res) 57 { 58 if (dem != NULL && strcmp(tc->dtc_demangled, dem) == 0) 59 return (B_TRUE); 60 61 if (res) 62 (void) printf("FAILURE\n"); 63 64 if (dem == NULL) { 65 (void) printf(" [%zu] Failed to demangle '%s'\n", i, 66 tc->dtc_mangled); 67 return (B_FALSE); 68 } 69 70 (void) printf(" [%zu] Demangled results do not match.\n", i); 71 (void) printf(" Mangled: %s\n", tc->dtc_mangled); 72 (void) printf(" Expected: %s\n", tc->dtc_demangled); 73 (void) printf(" Actual: %s\n", dem); 74 return (B_FALSE); 75 } 76 77 static boolean_t 78 check_msuccess(size_t tcnum, demtest_mcase_t *tm, const char *dem, 79 boolean_t res) 80 { 81 if (dem != NULL) { 82 for (size_t i = 0; tm->dtmc_demangled[i] != NULL; i++) { 83 if (strcmp(tm->dtmc_demangled[i], dem) == 0) 84 return (B_TRUE); 85 } 86 } 87 88 if (res) 89 (void) printf("FAILURE\n"); 90 91 if (dem == NULL) { 92 (void) printf(" [%zu] Failed to demangle '%s'\n", tcnum, 93 tm->dtmc_mangled); 94 return (B_FALSE); 95 } 96 97 (void) printf(" [%zu] Demangled results do not match.\n", tcnum); 98 (void) printf(" Mangled: %s\n", tm->dtmc_mangled); 99 (void) printf(" Expected: "); 100 101 for (size_t i = 0; tm->dtmc_demangled[i] != NULL; i++) { 102 if (i > 0) { 103 int indent = sizeof (" Expected: ") - 1; 104 (void) printf("%*s", indent, ""); 105 } 106 107 (void) printf("%s%s", tm->dtmc_demangled[i]); 108 109 if (tm->dtmc_demangled[i + 1] != NULL) 110 (void) printf(" or"); 111 112 (void) fputc('\n', stdout); 113 } 114 115 (void) printf(" Actual: %s\n", dem); 116 return (B_FALSE); 117 } 118 119 static boolean_t 120 run_test(demtest_group_t *test, unsigned long testnum) 121 { 122 boolean_t res = B_TRUE; 123 124 (void) printf("Test %s: ", test->dtg_name); 125 126 for (size_t i = 0; test->dtg_tests[i].dtc_mangled != NULL; i++) { 127 char *dem; 128 129 if (testnum != ALL_TESTS && i != testnum) 130 continue; 131 132 dem = sysdemangle(test->dtg_tests[i].dtc_mangled, 133 test->dtg_lang, NULL); 134 135 if (test->dtg_tests[i].dtc_demangled == NULL) 136 res &= check_failure(i, &test->dtg_tests[i], dem, res); 137 else 138 res &= check_success(i, &test->dtg_tests[i], dem, res); 139 140 free(dem); 141 } 142 143 if (res) 144 (void) printf("SUCCESS\n"); 145 146 return (res); 147 } 148 149 static boolean_t 150 run_mtest(demtest_mgroup_t *test, unsigned long testnum) 151 { 152 boolean_t res = B_TRUE; 153 154 (void) printf("Test %s: ", test->dtmg_name); 155 156 for (size_t i = 0; test->dtmg_tests[i] != NULL; i++) { 157 char *dem; 158 159 if (testnum != ALL_TESTS && i != testnum) 160 continue; 161 162 dem = sysdemangle(test->dtmg_tests[i]->dtmc_mangled, 163 test->dtmg_lang, NULL); 164 res &= check_msuccess(i, test->dtmg_tests[i], dem, res); 165 free(dem); 166 } 167 168 if (res) 169 (void) printf("SUCCESS\n"); 170 171 return (res); 172 } 173 174 static void 175 list_groups(void) 176 { 177 size_t n; 178 179 n = SET_COUNT(demangle_tests); 180 181 if (n > 0) 182 (void) printf("Demangle tests:\n"); 183 184 for (size_t i = 0; i < n; i++) { 185 demtest_group_t *grp = SET_ITEM(demangle_tests, n - i - 1); 186 size_t ntest = 0; 187 188 for (size_t j = 0; grp->dtg_tests[j].dtc_mangled != NULL; j++) 189 ntest++; 190 191 (void) printf("\t%s (%zu tests)\n", grp->dtg_name, ntest); 192 } 193 194 195 if (n > 0) 196 (void) fputc('\n', stdout); 197 198 n = SET_COUNT(demangle_mtests); 199 200 if (n > 0) 201 (void) printf("Demangle mtests:\n"); 202 203 for (size_t i = 0; i < n; i++) { 204 demtest_mgroup_t *mgrp = SET_ITEM(demangle_mtests, n - i - 1); 205 size_t ntest = 0; 206 207 for (size_t j = 0; mgrp->dtmg_tests[j] != NULL; j++) 208 ntest++; 209 210 (void) printf("\t%s (%zu tests)\n", mgrp->dtmg_name, ntest); 211 } 212 213 } 214 215 __NORETURN static void 216 usage(const char *name) 217 { 218 (void) fprintf(stderr, 219 "%s: [-hl] { [-g group] | [-m mgroup] } [-t test]\n", name); 220 (void) fprintf(stderr, " -h Display this help\n"); 221 (void) fprintf(stderr, " -l List test groups\n"); 222 (void) fprintf(stderr, " -g group Only run the specified group\n"); 223 (void) fprintf(stderr, " -m mgroup Only run the specified mgroup\n"); 224 (void) fprintf(stderr, " -t test Only run the specified test " 225 "(requires -g or -m)\n"); 226 exit(EXIT_FAILURE); 227 } 228 229 int 230 main(int argc, char **argv) 231 { 232 const char *group = NULL; 233 const char *mgroup = NULL; 234 unsigned long testnum = ALL_TESTS; 235 size_t i, n; 236 int c; 237 boolean_t ok = B_TRUE; 238 boolean_t found; 239 240 while ((c = getopt(argc, argv, "hlg:m:t:")) > 0) { 241 switch (c) { 242 case 'h': 243 usage(argv[0]); 244 case 'l': 245 list_groups(); 246 return (EXIT_SUCCESS); 247 case 'g': 248 if (mgroup != NULL) { 249 (void) fprintf(stderr, "-g and -m options are " 250 "mutually exclusive\n"); 251 return (EXIT_FAILURE); 252 } 253 254 group = optarg; 255 break; 256 case 'm': 257 if (group != NULL) { 258 (void) fprintf(stderr, "-g and -m options are " 259 "mutually exclusive\n"); 260 return (EXIT_FAILURE); 261 } 262 263 mgroup = optarg; 264 break; 265 case 't': 266 errno = 0; 267 testnum = strtoul(optarg, NULL, 10); 268 if (errno != 0) { 269 (void) fprintf(stderr, 270 "Unable to parse '%s' as a number: %s\n", 271 optarg, strerror(errno)); 272 return (EXIT_FAILURE); 273 } 274 break; 275 case '?': 276 (void) fprintf(stderr, "Unknown option -%c\n", optopt); 277 usage(argv[0]); 278 } 279 } 280 281 if (testnum != ALL_TESTS && group == NULL && mgroup == NULL) { 282 (void) fprintf(stderr, "Error: -t requires -g or -m option\n"); 283 return (EXIT_FAILURE); 284 } 285 286 /* 287 * A downside to using linker sets is that the items get added in 288 * the reverse order in which they're declared. To match the 289 * declaration order, we iterate in reverse. 290 */ 291 n = SET_COUNT(demangle_tests); 292 found = (group == NULL) ? B_TRUE : B_FALSE; 293 for (i = 0; i < n; i++) { 294 demtest_group_t *grp = SET_ITEM(demangle_tests, n - i - 1); 295 296 if (group == NULL) { 297 ok &= run_test(grp, testnum); 298 } else if (strcmp(grp->dtg_name, group) == 0) { 299 found = B_TRUE; 300 ok &= run_test(grp, testnum); 301 break; 302 } 303 } 304 if (!found) { 305 (void) fprintf(stderr, "Test group %s does not exist\n", group); 306 return (EXIT_FAILURE); 307 } 308 309 n = SET_COUNT(demangle_mtests); 310 found = (mgroup == NULL) ? B_TRUE : B_FALSE; 311 for (i = 0; i < n; i++) { 312 demtest_mgroup_t *mgrp = SET_ITEM(demangle_mtests, n - i - 1); 313 314 if (mgroup == NULL) { 315 ok &= run_mtest(mgrp, testnum); 316 } else if (strcmp(mgrp->dtmg_name, mgroup) == 0) { 317 found = B_TRUE; 318 ok &= run_mtest(mgrp, testnum); 319 break; 320 } 321 } 322 if (!found) { 323 (void) fprintf(stderr, 324 "Test mgroup %s does not exist\n", group); 325 return (EXIT_FAILURE); 326 } 327 328 return (ok ? 0 : 1); 329 } 330 331 const char * 332 _umem_debug_init(void) 333 { 334 return ("default,verbose"); 335 } 336 337 const char * 338 _umem_logging_init(void) 339 { 340 return ("fail,contents"); 341 }