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 }