xref: /illumos-gate/usr/src/test/util-tests/tests/ctf/check-common.c (revision 5328fc53d11d7151861fa272e4fb0248b8f0e145)
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 /*
17  * Collection of common utilities for CTF testing.
18  */
19 
20 #include <strings.h>
21 #include <libctf.h>
22 #include "check-common.h"
23 
24 typedef struct ctftests_lookup_cb {
25 	ctf_file_t *clc_fp;
26 	ctf_id_t clc_id;
27 	const char *clc_name;
28 } ctftests_lookup_cb_t;
29 
30 typedef struct ctftest_member_cb {
31 	ctf_file_t *cmc_fp;
32 	const check_member_t *cmc_members;
33 	const char *cmc_name;
34 } ctftest_member_cb_t;
35 
36 static int
37 ctftest_lookup_type_cb(ctf_id_t id, boolean_t root, void *arg)
38 {
39 	char buf[2048];
40 	ctftests_lookup_cb_t *clc = arg;
41 
42 	if (ctf_type_name(clc->clc_fp, id, buf, sizeof (buf)) == NULL)
43 		return (0);
44 
45 	if (strcmp(buf, clc->clc_name) != 0)
46 		return (0);
47 
48 	clc->clc_id = id;
49 	return (1);
50 }
51 
52 /*
53  * This is a variant on the classic ctf_lookup_by_name(). ctf_lookup_by_name()
54  * skips qualifiers, which makes sense given what the consumers of it are trying
55  * to do. However, that's not what we want here. So instead we basically have to
56  * walk the type table.
57  */
58 static ctf_id_t
59 ctftest_lookup_type(ctf_file_t *fp, const char *name)
60 {
61 	ctftests_lookup_cb_t clc;
62 
63 	clc.clc_fp = fp;
64 	clc.clc_id = CTF_ERR;
65 	clc.clc_name = name;
66 
67 	(void) ctf_type_iter(fp, B_TRUE, ctftest_lookup_type_cb, &clc);
68 	return (clc.clc_id);
69 }
70 
71 static int
72 ctftest_lookup_object_cb(const char *obj, ctf_id_t type, ulong_t idx, void *arg)
73 {
74 	ctftests_lookup_cb_t *clc = arg;
75 
76 	if (strcmp(obj, clc->clc_name) == 0) {
77 		clc->clc_id = type;
78 		return (1);
79 	}
80 
81 	return (0);
82 }
83 
84 static ctf_id_t
85 ctftest_lookup_symbol(ctf_file_t *fp, const char *name)
86 {
87 	ctftests_lookup_cb_t clc;
88 
89 	clc.clc_fp = fp;
90 	clc.clc_id = CTF_ERR;
91 	clc.clc_name = name;
92 
93 	(void) ctf_object_iter(fp, ctftest_lookup_object_cb, &clc);
94 	return (clc.clc_id);
95 }
96 
97 typedef struct ctf_function_cb {
98 	const char *cfc_name;
99 	ulong_t *cfc_symp;
100 	ctf_funcinfo_t *cfc_fip;
101 } ctf_function_cb_t;
102 
103 static int
104 ctftest_lookup_function_cb(const char *name, ulong_t symidx,
105     ctf_funcinfo_t *fip, void *arg)
106 {
107 	ctf_function_cb_t *cfc = arg;
108 	if (strcmp(name, cfc->cfc_name) != 0)
109 		return (0);
110 
111 	*cfc->cfc_symp = symidx;
112 	*cfc->cfc_fip = *fip;
113 
114 	return (1);
115 }
116 
117 /*
118  * Note, this function finds the first one with a matching name. This must not
119  * be used when performing searches where a given name may occur more than once.
120  */
121 static boolean_t
122 ctftest_lookup_function(ctf_file_t *fp, const char *name, ulong_t *symp,
123     ctf_funcinfo_t *fip)
124 {
125 	ctf_function_cb_t cfc;
126 
127 	*symp = 0;
128 	cfc.cfc_name = name;
129 	cfc.cfc_symp = symp;
130 	cfc.cfc_fip = fip;
131 	(void) ctf_function_iter(fp, ctftest_lookup_function_cb, &cfc);
132 	return (*symp == 0 ? B_FALSE : B_TRUE);
133 }
134 
135 boolean_t
136 ctftest_check_numbers(ctf_file_t *fp, const check_number_t *tests)
137 {
138 	uint_t i;
139 	boolean_t ret = B_TRUE;
140 
141 	for (i = 0; tests[i].cn_tname != NULL; i++) {
142 		ctf_id_t id;
143 		ctf_encoding_t enc;
144 
145 		id = ctftest_lookup_type(fp, tests[i].cn_tname);
146 		if (id == CTF_ERR) {
147 			warnx("failed to look up %s", tests[i].cn_tname);
148 			ret = B_FALSE;
149 			continue;
150 		}
151 
152 		if (ctf_type_kind(fp, id) != tests[i].cn_kind) {
153 			warnx("type kind mismatch for %s: got %u, expected %u",
154 			    tests[i].cn_tname, ctf_type_kind(fp, id),
155 			    tests[i].cn_kind);
156 			ret = B_FALSE;
157 			continue;
158 		}
159 
160 		if (ctf_type_encoding(fp, id, &enc) == CTF_ERR) {
161 			warnx("failed to get type encoding for %s: %s",
162 			    tests[i].cn_tname, ctf_errmsg(ctf_errno(fp)));
163 			ret = B_FALSE;
164 			continue;
165 		}
166 
167 		if (enc.cte_format != tests[i].cn_flags) {
168 			warnx("encoding flags mismatch for %s: got 0x%x, "
169 			    "expected 0x%x", tests[i].cn_tname, enc.cte_format,
170 			    tests[i].cn_flags);
171 			ret = B_FALSE;
172 			continue;
173 		}
174 
175 		if (enc.cte_offset != tests[i].cn_offset) {
176 			warnx("encoding offset mismatch for %s: got 0x%x, "
177 			    "expected 0x%x", tests[i].cn_tname, enc.cte_offset,
178 			    tests[i].cn_offset);
179 			ret = B_FALSE;
180 			continue;
181 		}
182 
183 		if (enc.cte_bits != tests[i].cn_size) {
184 			warnx("encoding size mismatch for %s: got 0x%x, "
185 			    "expected 0x%x", tests[i].cn_tname, enc.cte_bits,
186 			    tests[i].cn_size);
187 			ret = B_FALSE;
188 			continue;
189 		}
190 	}
191 
192 	return (ret);
193 }
194 
195 typedef struct ctftests_symbol_cb {
196 	ctf_file_t	*csc_fp;
197 	boolean_t	csc_ret;
198 	const check_symbol_t *csc_tests;
199 } ctftest_symbol_cb_t;
200 
201 static int
202 ctftest_check_symbol_cb(const char *obj, ctf_id_t type, ulong_t idx, void *arg)
203 {
204 	ctftest_symbol_cb_t *cb = arg;
205 	const check_symbol_t *tests = cb->csc_tests;
206 	ctf_file_t *fp = cb->csc_fp;
207 	uint_t i;
208 
209 	for (i = 0; tests[i].cs_symbol != NULL; i++) {
210 		ctf_id_t id;
211 
212 		if (strcmp(obj, tests[i].cs_symbol) != 0)
213 			continue;
214 
215 		id = ctftest_lookup_type(fp, tests[i].cs_type);
216 		if (id == CTF_ERR) {
217 			warnx("failed to lookup type %s for symbol %s",
218 			    tests[i].cs_type, tests[i].cs_symbol);
219 			cb->csc_ret = B_FALSE;
220 			return (0);
221 		}
222 
223 		if (id != type) {
224 			warnx("type mismatch for symbol %s, has type id %u, "
225 			    "but specified type %s has id %u",
226 			    tests[i].cs_symbol, type, tests[i].cs_type, id);
227 			cb->csc_ret = B_FALSE;
228 			return (0);
229 		}
230 	}
231 
232 	return (0);
233 }
234 
235 boolean_t
236 ctftest_check_symbols(ctf_file_t *fp, const check_symbol_t *tests)
237 {
238 	ctftest_symbol_cb_t cb;
239 
240 	cb.csc_fp = fp;
241 	cb.csc_ret = B_TRUE;
242 	cb.csc_tests = tests;
243 	if (ctf_object_iter(fp, ctftest_check_symbol_cb, &cb) != 0)
244 		return (B_FALSE);
245 	return (cb.csc_ret);
246 }
247 
248 
249 boolean_t
250 ctftest_check_descent(const char *symbol, ctf_file_t *fp,
251     const check_descent_t *tests, boolean_t quiet)
252 {
253 	ctf_id_t base;
254 	uint_t layer = 0;
255 
256 	/*
257 	 * First, find the initial type of the symbol.
258 	 */
259 	base = ctftest_lookup_symbol(fp, symbol);
260 	if (base == CTF_ERR) {
261 		warnx("failed to lookup type for symbol %s", symbol);
262 		return (B_FALSE);
263 	}
264 
265 	while (tests->cd_tname != NULL) {
266 		ctf_id_t tid;
267 		int kind;
268 		ctf_arinfo_t ari;
269 
270 		if (base == CTF_ERR) {
271 			if (!quiet) {
272 				warnx("encountered non-reference type at layer "
273 				    "%u while still expecting type %s for "
274 				    "symbol %s", layer,
275 				    tests->cd_tname, symbol);
276 			}
277 			return (B_FALSE);
278 		}
279 
280 		tid = ctftest_lookup_type(fp, tests->cd_tname);
281 		if (tid == CTF_ERR) {
282 			if (!quiet) {
283 				warnx("failed to lookup type %s",
284 				    tests->cd_tname);
285 			}
286 			return (B_FALSE);
287 		}
288 
289 		if (tid != base) {
290 			if (!quiet) {
291 				warnx("type mismatch at layer %u: found id %u, "
292 				    "but expecting type id %u for type %s, "
293 				    "symbol %s", layer, base, tid,
294 				    tests->cd_tname, symbol);
295 			}
296 			return (B_FALSE);
297 		}
298 
299 		kind = ctf_type_kind(fp, base);
300 		if (kind != tests->cd_kind) {
301 			if (!quiet) {
302 				warnx("type kind mismatch at layer %u: found "
303 				    "kind %u, but expected kind %u for %s, "
304 				    "symbol %s", layer, kind, tests->cd_kind,
305 				    tests->cd_tname, symbol);
306 			}
307 			return (B_FALSE);
308 		}
309 
310 		switch (kind) {
311 		case CTF_K_ARRAY:
312 			if (ctf_array_info(fp, base, &ari) == CTF_ERR) {
313 				if (!quiet) {
314 					warnx("failed to lookup array info at "
315 					    "layer %u for type %s, symbol "
316 					    "%s: %s", base, tests->cd_tname,
317 					    symbol, ctf_errmsg(ctf_errno(fp)));
318 				}
319 				return (B_FALSE);
320 			}
321 
322 			if (tests->cd_nents != ari.ctr_nelems) {
323 				if (!quiet) {
324 					warnx("array element mismatch at layer "
325 					    "%u for type %s, symbol %s: found "
326 					    "%u, expected %u", layer,
327 					    tests->cd_tname, symbol,
328 					    ari.ctr_nelems, tests->cd_nents);
329 				}
330 				return (B_FALSE);
331 			}
332 
333 			tid = ctftest_lookup_type(fp, tests->cd_contents);
334 			if (tid == CTF_ERR) {
335 				if (!quiet) {
336 					warnx("failed to look up type %s",
337 					    tests->cd_contents);
338 				}
339 				return (B_FALSE);
340 			}
341 
342 			if (ari.ctr_contents != tid) {
343 				if (!quiet) {
344 					warnx("array contents mismatch at "
345 					    "layer %u for type %s, symbol %s: "
346 					    "found %u, expected %s/%u", layer,
347 					    tests->cd_tname, symbol,
348 					    ari.ctr_contents,
349 					    tests->cd_contents, tid);
350 				}
351 				return (B_FALSE);
352 			}
353 			base = ari.ctr_contents;
354 			break;
355 		default:
356 			base = ctf_type_reference(fp, base);
357 			break;
358 		}
359 
360 		tests++;
361 		layer++;
362 	}
363 
364 	if (base != CTF_ERR) {
365 		if (!quiet) {
366 			warnx("found additional type %u in chain, "
367 			    "but expected no more", base);
368 		}
369 		return (B_FALSE);
370 	}
371 
372 	return (B_TRUE);
373 }
374 
375 int
376 ctftest_check_enum_count(const char *name, int value, void *arg)
377 {
378 	uint_t *u = arg;
379 	*u = *u + 1;
380 	return (0);
381 }
382 
383 int
384 ctftest_check_enum_value(const char *name, int value, void *arg)
385 {
386 	uint_t i;
387 	const check_enum_t *enums = arg;
388 
389 	for (i = 0; enums[i].ce_name != NULL; i++) {
390 		if (strcmp(enums[i].ce_name, name) != 0)
391 			continue;
392 		if (enums[i].ce_value == (int64_t)value)
393 			return (0);
394 		warnx("enum %s value mismatch: found %d, expected %" PRId64,
395 		    name, value, enums[i].ce_value);
396 		return (1);
397 	}
398 
399 	warnx("found no matching entry for enum member %s", name);
400 	return (1);
401 }
402 
403 boolean_t
404 ctftest_check_enum(const char *type, ctf_file_t *fp, const check_enum_t *enums)
405 {
406 	int ret;
407 	uint_t tcount, ecount;
408 	ctf_id_t base;
409 
410 	if ((base = ctftest_lookup_type(fp, type)) == CTF_ERR) {
411 		warnx("Failed to look up type %s", type);
412 		return (B_FALSE);
413 	}
414 
415 	if (ctf_type_kind(fp, base) != CTF_K_ENUM) {
416 		warnx("%s is not an enum", type);
417 		return (B_FALSE);
418 	}
419 
420 	/*
421 	 * First count how many entries we have.
422 	 */
423 	tcount = 0;
424 	while (enums[tcount].ce_name != NULL) {
425 		tcount++;
426 	}
427 
428 	ecount = 0;
429 	if (ctf_enum_iter(fp, base, ctftest_check_enum_count, &ecount) != 0) {
430 		warnx("failed to walk enum %s: %s", type,
431 		    ctf_errmsg(ctf_errno(fp)));
432 		return (B_FALSE);
433 	}
434 
435 	if (tcount != ecount) {
436 		warnx("enum value mismatch: expected %u values, but found %u",
437 		    tcount, ecount);
438 		return (B_FALSE);
439 	}
440 
441 	if ((ret = ctf_enum_iter(fp, base, ctftest_check_enum_value,
442 	    (void *)enums)) != 0) {
443 		if (ret == -1) {
444 			warnx("failed to walk enum %s: %s", type,
445 			    ctf_errmsg(ctf_errno(fp)));
446 		}
447 		return (B_FALSE);
448 	}
449 
450 	return (B_TRUE);
451 }
452 
453 int
454 ctftest_check_member_count(const char *mname, ctf_id_t mtype, ulong_t bitoff,
455     void *arg)
456 {
457 	uint_t *countp = arg;
458 	*countp = *countp + 1;
459 	return (0);
460 }
461 
462 int
463 ctftest_check_members_cb(const char *mname, ctf_id_t mtype, ulong_t bitoff,
464     void *arg)
465 {
466 	uint_t i;
467 	const ctftest_member_cb_t *cmc = arg;
468 	const check_member_t *members = cmc->cmc_members;
469 	ctf_file_t *fp = cmc->cmc_fp;
470 
471 	for (i = 0; members[i].cm_name != NULL; i++) {
472 		boolean_t bad = B_FALSE;
473 		char buf[2048];
474 
475 		if (strcmp(mname, members[i].cm_name) != 0)
476 			continue;
477 
478 		if (bitoff != members[i].cm_offset) {
479 			warnx("member %s of type %s has mismatched bit offset: "
480 			    "found %lu, expected %lu", mname, cmc->cmc_name,
481 			    bitoff, members[i].cm_offset);
482 			bad = B_TRUE;
483 		}
484 
485 		if (ctf_type_name(fp, mtype, buf, sizeof (buf)) == NULL) {
486 			warnx("failed to obtain type name for member %s",
487 			    mname, ctf_errmsg(ctf_errno(fp)));
488 			bad = B_TRUE;
489 		} else if (strcmp(buf, members[i].cm_type) != 0) {
490 			warnx("member %s has bad type, found %s, expected %s",
491 			    mname, buf, members[i].cm_type);
492 			bad = B_TRUE;
493 		}
494 
495 		return (bad ? 1 : 0);
496 	}
497 
498 	warnx("found no matching entry for member %s of type %s", mname,
499 	    cmc->cmc_name);
500 	return (1);
501 }
502 
503 boolean_t
504 ctftest_check_members(const char *type, ctf_file_t *fp, int kind,
505     size_t size, const check_member_t *members)
506 {
507 	int ret;
508 	uint_t tcount, mcount;
509 	ctf_id_t base;
510 	ctftest_member_cb_t cmc;
511 
512 	if ((base = ctftest_lookup_type(fp, type)) == CTF_ERR) {
513 		warnx("failed to look up type %s", type);
514 		return (B_FALSE);
515 	}
516 
517 	if (ctf_type_kind(fp, base) != kind) {
518 		warnx("%s has kind %s, expected %s", type,
519 		    ctf_kind_name(fp, ctf_type_kind(fp, base)),
520 		    ctf_kind_name(fp, kind));
521 		return (B_FALSE);
522 	}
523 
524 	if (size != ctf_type_size(fp, base)) {
525 		warnx("%s has bad size, expected %lu, found %lu",
526 		    type, size, ctf_type_size(fp, base));
527 		return (B_FALSE);
528 	}
529 
530 	/*
531 	 * First count how many entries we have.
532 	 */
533 	tcount = 0;
534 	while (members[tcount].cm_name != NULL) {
535 		tcount++;
536 	}
537 
538 	mcount = 0;
539 	if (ctf_member_iter(fp, base, ctftest_check_member_count, &mcount) !=
540 	    0) {
541 		warnx("failed to walk members of %s: %s", type,
542 		    ctf_errmsg(ctf_errno(fp)));
543 		return (B_FALSE);
544 	}
545 
546 	if (tcount != mcount) {
547 		warnx("type member mismatch: expected %u values, but found %u",
548 		    tcount, mcount);
549 		return (B_FALSE);
550 	}
551 
552 	cmc.cmc_fp = fp;
553 	cmc.cmc_members = members;
554 	cmc.cmc_name = type;
555 	if ((ret = ctf_member_iter(fp, base, ctftest_check_members_cb,
556 	    &cmc)) != 0) {
557 		if (ret == -1) {
558 			warnx("failed to walk type %s: %s", type,
559 			    ctf_errmsg(ctf_errno(fp)));
560 		}
561 		return (B_FALSE);
562 	}
563 
564 	return (B_TRUE);
565 }
566 
567 boolean_t
568 ctftest_check_function(const char *symbol, ctf_file_t *fp, const char *rtype,
569     uint_t nargs, uint_t flags, const char **argv)
570 {
571 	ulong_t sym;
572 	ctf_funcinfo_t fi;
573 	uint_t i;
574 	boolean_t ret = B_TRUE;
575 	ctf_id_t *args;
576 	char buf[2048];
577 
578 
579 	if (!ctftest_lookup_function(fp, symbol, &sym, &fi)) {
580 		warnx("failed to look up function %s", symbol);
581 		return (B_FALSE);
582 	}
583 
584 	if (ctf_type_name(fp, fi.ctc_return, buf, sizeof (buf)) == NULL) {
585 		warnx("failed to lookup return type name for function %s",
586 		    symbol);
587 		ret = B_FALSE;
588 	} else if (strcmp(rtype, buf) != 0) {
589 		warnx("return type has wrong type: found %s, expected %s",
590 		    buf, rtype);
591 		ret = B_FALSE;
592 	}
593 
594 	if (nargs != fi.ctc_argc) {
595 		warnx("function argument mismatch: found %u, expected %u",
596 		    fi.ctc_argc, nargs);
597 		ret = B_FALSE;
598 	}
599 
600 	if (flags != fi.ctc_flags) {
601 		warnx("function flags mismatch, found 0x%x, expected 0x%x",
602 		    fi.ctc_flags, flags);
603 		ret = B_FALSE;
604 	}
605 
606 	if (!ret || fi.ctc_argc == 0) {
607 		return (ret);
608 	}
609 
610 	if ((args = calloc(fi.ctc_argc, sizeof (ctf_id_t))) == NULL) {
611 		warnx("failed to allocate memory for function arguments");
612 		return (B_FALSE);
613 	}
614 
615 	if (ctf_func_args(fp, sym, fi.ctc_argc, args) != 0) {
616 		warnx("failed to get function information: %s",
617 		    ctf_errmsg(ctf_errno(fp)));
618 		free(args);
619 		return (B_FALSE);
620 	}
621 
622 	for (i = 0; i < fi.ctc_argc; i++) {
623 		if (ctf_type_name(fp, args[i], buf, sizeof (buf)) == NULL) {
624 			warnx("failed to obtain type name for argument %u",
625 			    i, ctf_errmsg(ctf_errno(fp)));
626 			ret = B_FALSE;
627 			break;
628 		}
629 
630 		if (strcmp(buf, argv[i]) != 0) {
631 			warnx("argument %u has wrong type: found %s, "
632 			    "expected %s", i, buf, argv[i]);
633 			ret = B_FALSE;
634 			break;
635 		}
636 	}
637 
638 	free(args);
639 	return (ret);
640 }
641 
642 boolean_t
643 ctftest_check_fptr(const char *type, ctf_file_t *fp, const char *rtype,
644     uint_t nargs, uint_t flags, const char **argv)
645 {
646 	ctf_id_t tid;
647 	ctf_funcinfo_t fi;
648 	uint_t i;
649 	boolean_t ret = B_TRUE;
650 	ctf_id_t *args;
651 	char buf[2048];
652 
653 
654 	if ((tid = ctf_lookup_by_name(fp, type)) == CTF_ERR) {
655 		warnx("failed to look up type %s: %s", type,
656 		    ctf_errmsg(ctf_errno(fp)));
657 		return (B_FALSE);
658 	}
659 
660 	/*
661 	 * Perform two CTF type resolves, one for the function pointer and one
662 	 * for the typedef that gets passed in.
663 	 */
664 	if ((tid = ctf_type_resolve(fp, tid)) == CTF_ERR) {
665 		warnx("failed to convert type %s to base type: %s", type,
666 		    ctf_errmsg(ctf_errno(fp)));
667 		return (B_FALSE);
668 	}
669 
670 	if (ctf_type_kind(fp, tid) == CTF_K_POINTER &&
671 	    (tid = ctf_type_reference(fp, tid)) == CTF_ERR) {
672 		warnx("failed to convert type %s to base type: %s", type,
673 		    ctf_errmsg(ctf_errno(fp)));
674 		return (B_FALSE);
675 	}
676 
677 	if (ctf_func_info_by_id(fp, tid, &fi) != 0) {
678 		warnx("failed to get function information for type %s: %s",
679 		    type, ctf_errmsg(ctf_errno(fp)));
680 		return (B_FALSE);
681 	}
682 
683 	if (ctf_type_name(fp, fi.ctc_return, buf, sizeof (buf)) == NULL) {
684 		warnx("failed to lookup return type name for function %s",
685 		    type);
686 		ret = B_FALSE;
687 	} else if (strcmp(rtype, buf) != 0) {
688 		warnx("return type has wrong type: found %s, expected %s",
689 		    buf, rtype);
690 		ret = B_FALSE;
691 	}
692 
693 	if (nargs != fi.ctc_argc) {
694 		warnx("function argument mismatch: found %u, expected %u",
695 		    fi.ctc_argc, nargs);
696 		ret = B_FALSE;
697 	}
698 
699 	if (flags != fi.ctc_flags) {
700 		warnx("function flags mismatch, found 0x%x, expected 0x%x",
701 		    fi.ctc_flags, flags);
702 		ret = B_FALSE;
703 	}
704 
705 	if (!ret || fi.ctc_argc == 0) {
706 		return (ret);
707 	}
708 
709 	if ((args = calloc(fi.ctc_argc, sizeof (ctf_id_t))) == NULL) {
710 		warnx("failed to allocate memory for function arguments");
711 		return (B_FALSE);
712 	}
713 
714 	if (ctf_func_args_by_id(fp, tid, fi.ctc_argc, args) != 0) {
715 		warnx("failed to get function information: %s",
716 		    ctf_errmsg(ctf_errno(fp)));
717 		free(args);
718 		return (B_FALSE);
719 	}
720 
721 	for (i = 0; i < fi.ctc_argc; i++) {
722 		if (ctf_type_name(fp, args[i], buf, sizeof (buf)) == NULL) {
723 			warnx("failed to obtain type name for argument %u",
724 			    i, ctf_errmsg(ctf_errno(fp)));
725 			ret = B_FALSE;
726 			break;
727 		}
728 
729 		if (strcmp(buf, argv[i]) != 0) {
730 			warnx("argument %u has wrong type: found %s, "
731 			    "expected %s", i, buf, argv[i]);
732 			ret = B_FALSE;
733 			break;
734 		}
735 	}
736 
737 	free(args);
738 	return (ret);
739 }
740 
741 typedef struct ctftest_duplicates {
742 	ctf_file_t *ctd_fp;
743 	char **ctd_names;
744 	size_t ctd_len;
745 	size_t ctd_curent;
746 	boolean_t ctd_ret;
747 } ctftest_duplicates_t;
748 
749 static int
750 ctftest_duplicates_cb(ctf_id_t id, boolean_t root, void *arg)
751 {
752 	char buf[2048];
753 	ctftest_duplicates_t *dup = arg;
754 	size_t i;
755 
756 	if (ctf_type_name(dup->ctd_fp, id, buf, sizeof (buf)) == NULL) {
757 		warnx("failed to lookup name for id %ld", id);
758 		dup->ctd_ret = B_FALSE;
759 		return (1);
760 	}
761 
762 	for (i = 0; i < dup->ctd_curent; i++) {
763 		if (strcmp(buf, dup->ctd_names[i]) == 0) {
764 			warnx("encountered duplicate type '%s'", buf);
765 			dup->ctd_ret = B_FALSE;
766 			/*
767 			 * Don't break out of the loop and keep going in case we
768 			 * find another duplicate.
769 			 */
770 			return (0);
771 		}
772 	}
773 
774 	if (dup->ctd_curent == dup->ctd_len) {
775 		char **n;
776 		size_t newlen = dup->ctd_len * 2;
777 
778 		n = recallocarray(dup->ctd_names, dup->ctd_len, newlen,
779 		    sizeof (char *));
780 		if (n == NULL) {
781 			warnx("failed to resize type name array");
782 			dup->ctd_ret = B_FALSE;
783 			return (1);
784 		}
785 
786 		dup->ctd_names = n;
787 		dup->ctd_len = newlen;
788 	}
789 
790 	dup->ctd_names[dup->ctd_curent] = strdup(buf);
791 	if (dup->ctd_names[dup->ctd_curent] == NULL) {
792 		warn("failed to duplicate type name");
793 		dup->ctd_ret = B_FALSE;
794 		return (1);
795 	}
796 	dup->ctd_curent++;
797 
798 	return (0);
799 }
800 
801 boolean_t
802 ctftest_duplicates(ctf_file_t *fp)
803 {
804 	size_t i;
805 	ctftest_duplicates_t d;
806 
807 	bzero(&d, sizeof (d));
808 	d.ctd_fp = fp;
809 	d.ctd_len = 4;
810 	d.ctd_ret = B_TRUE;
811 	d.ctd_names = recallocarray(NULL, 0, d.ctd_len, sizeof (char *));
812 	if (d.ctd_names == NULL) {
813 		warnx("failed to allocate duplicate name storage");
814 		return (B_FALSE);
815 	}
816 
817 	(void) ctf_type_iter(fp, B_TRUE, ctftest_duplicates_cb, &d);
818 
819 	for (i = 0; i < d.ctd_curent; i++) {
820 		free(d.ctd_names[i]);
821 	}
822 	free(d.ctd_names);
823 
824 	return (d.ctd_ret);
825 }
826