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