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 }