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 (c) 2015, Joyent, Inc. 14 */ 15 16 /* 17 * diff two CTF containers 18 */ 19 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <errno.h> 23 #include <strings.h> 24 #include <libctf.h> 25 #include <libgen.h> 26 #include <stdarg.h> 27 28 #define CTFDIFF_NAMELEN 256 29 30 #define CTFDIFF_EXIT_SIMILAR 0 31 #define CTFDIFF_EXIT_DIFFERENT 1 32 #define CTFDIFF_EXIT_USAGE 2 33 #define CTFDIFF_EXIT_ERROR 3 34 35 typedef enum ctf_diff_cmd { 36 CTF_DIFF_TYPES = 0x01, 37 CTF_DIFF_FUNCS = 0x02, 38 CTF_DIFF_OBJS = 0x04, 39 CTF_DIFF_DEFAULT = 0x07, 40 CTF_DIFF_LABEL = 0x08, 41 CTF_DIFF_ALL = 0x0f 42 } ctf_diff_cmd_t; 43 44 typedef struct { 45 int dil_next; 46 const char **dil_labels; 47 } ctfdiff_label_t; 48 49 static char *g_progname; 50 static const char *g_iname; 51 static ctf_file_t *g_ifp; 52 static const char *g_oname; 53 static ctf_file_t *g_ofp; 54 static char **g_typelist = NULL; 55 static int g_nexttype = 0; 56 static int g_ntypes = 0; 57 static char **g_objlist = NULL; 58 static int g_nextfunc = 0; 59 static int g_nfuncs = 0; 60 static char **g_funclist = NULL; 61 static int g_nextobj = 0; 62 static int g_nobjs = 0; 63 static boolean_t g_onlydiff = B_FALSE; 64 static boolean_t g_different = B_FALSE; 65 static ctf_diff_cmd_t g_flag = 0; 66 67 static void 68 ctfdiff_fatal(const char *fmt, ...) 69 { 70 va_list ap; 71 72 (void) fprintf(stderr, "%s: ", g_progname); 73 va_start(ap, fmt); 74 (void) vfprintf(stderr, fmt, ap); 75 va_end(ap); 76 77 exit(CTFDIFF_EXIT_ERROR); 78 } 79 80 static const char * 81 ctfdiff_fp_to_name(ctf_file_t *fp) 82 { 83 if (fp == g_ifp) 84 return (g_iname); 85 if (fp == g_ofp) 86 return (g_oname); 87 return (NULL); 88 } 89 90 /* ARGSUSED */ 91 static void 92 ctfdiff_func_cb(ctf_file_t *ifp, ulong_t iidx, boolean_t similar, 93 ctf_file_t *ofp, ulong_t oidx, void *arg) 94 { 95 char namebuf[CTFDIFF_NAMELEN]; 96 97 if (similar == B_TRUE) 98 return; 99 100 if (ctf_symbol_name(ifp, iidx, namebuf, sizeof (namebuf)) == NULL) { 101 if (g_nextfunc != 0) 102 return; 103 (void) printf("ctf container %s function %lu is different\n", 104 ctfdiff_fp_to_name(ifp), iidx); 105 } else { 106 if (g_nextfunc != 0) { 107 int i; 108 for (i = 0; i < g_nextfunc; i++) { 109 if (strcmp(g_funclist[i], namebuf) == 0) 110 break; 111 } 112 if (i == g_nextfunc) 113 return; 114 } 115 (void) printf("ctf container %s function %s (%lu) is " 116 "different\n", ctfdiff_fp_to_name(ifp), namebuf, iidx); 117 } 118 119 g_different = B_TRUE; 120 } 121 122 /* ARGSUSED */ 123 static void 124 ctfdiff_obj_cb(ctf_file_t *ifp, ulong_t iidx, ctf_id_t iid, boolean_t similar, 125 ctf_file_t *ofp, ulong_t oidx, ctf_id_t oid, void *arg) 126 { 127 char namebuf[CTFDIFF_NAMELEN]; 128 129 if (similar == B_TRUE) 130 return; 131 132 if (ctf_symbol_name(ifp, iidx, namebuf, sizeof (namebuf)) == NULL) { 133 if (g_nextobj != 0) 134 return; 135 (void) printf("ctf container %s object %lu is different\n", 136 ctfdiff_fp_to_name(ifp), iidx); 137 } else { 138 if (g_nextobj != 0) { 139 int i; 140 for (i = 0; i < g_nextobj; i++) { 141 if (strcmp(g_objlist[i], namebuf) == 0) 142 break; 143 } 144 if (i == g_nextobj) 145 return; 146 } 147 (void) printf("ctf container %s object %s (%lu) is different\n", 148 ctfdiff_fp_to_name(ifp), namebuf, iidx); 149 } 150 151 g_different = B_TRUE; 152 } 153 154 /* ARGSUSED */ 155 static void 156 ctfdiff_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t similar, ctf_file_t *ofp, 157 ctf_id_t oid, void *arg) 158 { 159 if (similar == B_TRUE) 160 return; 161 162 if (ctf_type_kind(ifp, iid) == CTF_K_UNKNOWN) 163 return; 164 165 /* 166 * Check if it's the type the user cares about. 167 */ 168 if (g_nexttype != 0) { 169 int i; 170 char namebuf[CTFDIFF_NAMELEN]; 171 172 if (ctf_type_name(ifp, iid, namebuf, sizeof (namebuf)) == 173 NULL) { 174 ctfdiff_fatal("failed to obtain the name " 175 "of type %ld from %s: %s\n", 176 iid, ctfdiff_fp_to_name(ifp), 177 ctf_errmsg(ctf_errno(ifp))); 178 } 179 180 for (i = 0; i < g_nexttype; i++) { 181 if (strcmp(g_typelist[i], namebuf) == 0) 182 break; 183 } 184 185 if (i == g_nexttype) 186 return; 187 } 188 189 g_different = B_TRUE; 190 191 if (g_onlydiff == B_TRUE) 192 return; 193 194 (void) printf("ctf container %s type %ld is different\n", 195 ctfdiff_fp_to_name(ifp), iid); 196 } 197 198 /* ARGSUSED */ 199 static int 200 ctfdiff_labels_count(const char *name, const ctf_lblinfo_t *li, void *arg) 201 { 202 uint32_t *count = arg; 203 *count = *count + 1; 204 205 return (0); 206 } 207 208 /* ARGSUSED */ 209 static int 210 ctfdiff_labels_fill(const char *name, const ctf_lblinfo_t *li, void *arg) 211 { 212 ctfdiff_label_t *dil = arg; 213 214 dil->dil_labels[dil->dil_next] = name; 215 dil->dil_next++; 216 217 return (0); 218 } 219 220 static int 221 ctfdiff_labels(ctf_file_t *ifp, ctf_file_t *ofp) 222 { 223 int ret; 224 uint32_t nilabel, nolabel, i, j; 225 ctfdiff_label_t idl, odl; 226 const char **ilptr, **olptr; 227 228 nilabel = nolabel = 0; 229 ret = ctf_label_iter(ifp, ctfdiff_labels_count, &nilabel); 230 if (ret == CTF_ERR) 231 return (ret); 232 ret = ctf_label_iter(ofp, ctfdiff_labels_count, &nolabel); 233 if (ret == CTF_ERR) 234 return (ret); 235 236 if (nilabel != nolabel) { 237 (void) printf("ctf container %s labels differ from ctf " 238 "container %s\n", ctfdiff_fp_to_name(ifp), 239 ctfdiff_fp_to_name(ofp)); 240 g_different = B_TRUE; 241 return (0); 242 } 243 244 if (nilabel == 0) 245 return (0); 246 247 ilptr = malloc(sizeof (char *) * nilabel); 248 olptr = malloc(sizeof (char *) * nolabel); 249 if (ilptr == NULL || olptr == NULL) { 250 ctfdiff_fatal("failed to allocate memory for label " 251 "comparison\n"); 252 } 253 254 idl.dil_next = 0; 255 idl.dil_labels = ilptr; 256 odl.dil_next = 0; 257 odl.dil_labels = olptr; 258 259 if ((ret = ctf_label_iter(ifp, ctfdiff_labels_fill, &idl)) != 0) 260 goto out; 261 if ((ret = ctf_label_iter(ofp, ctfdiff_labels_fill, &odl)) != 0) 262 goto out; 263 264 for (i = 0; i < nilabel; i++) { 265 for (j = 0; j < nolabel; j++) { 266 if (strcmp(ilptr[i], olptr[j]) == 0) 267 break; 268 } 269 270 if (j == nolabel) { 271 (void) printf("ctf container %s labels differ from ctf " 272 "container %s\n", ctfdiff_fp_to_name(ifp), 273 ctfdiff_fp_to_name(ofp)); 274 g_different = B_TRUE; 275 break; 276 } 277 } 278 279 ret = 0; 280 out: 281 free(ilptr); 282 free(olptr); 283 return (ret); 284 } 285 286 static void 287 ctfdiff_usage(const char *fmt, ...) 288 { 289 if (fmt != NULL) { 290 va_list ap; 291 292 (void) fprintf(stderr, "%s: ", g_progname); 293 va_start(ap, fmt); 294 (void) vfprintf(stderr, fmt, ap); 295 va_end(ap); 296 } 297 298 (void) fprintf(stderr, "Usage: %s [-afIloqt] [-F function] [-O object]" 299 "[-p parent] [-P parent]\n" 300 "\t[-T type] file1 file2\n" 301 "\n" 302 "\t-a diff label, types, objects, and functions\n" 303 "\t-f diff function type information\n" 304 "\t-F when diffing functions, only consider those named\n" 305 "\t-I ignore the names of integral types\n" 306 "\t-l diff CTF labels\n" 307 "\t-o diff global object type information\n" 308 "\t-O when diffing objects, only consider those named\n" 309 "\t-p set the CTF parent for file1\n" 310 "\t-P set the CTF parent for file2\n" 311 "\t-q set quiet mode (no diff information sent to stdout)\n" 312 "\t-t diff CTF type information\n" 313 "\t-T when diffing types, only consider those named\n", 314 g_progname); 315 } 316 317 int 318 main(int argc, char *argv[]) 319 { 320 ctf_diff_flag_t flags = 0; 321 int err, c; 322 ctf_file_t *ifp, *ofp; 323 ctf_diff_t *cdp; 324 ctf_file_t *pifp = NULL; 325 ctf_file_t *pofp = NULL; 326 327 g_progname = basename(argv[0]); 328 329 while ((c = getopt(argc, argv, ":aqtfolIp:F:O:P:T:")) != -1) { 330 switch (c) { 331 case 'a': 332 g_flag |= CTF_DIFF_ALL; 333 break; 334 case 't': 335 g_flag |= CTF_DIFF_TYPES; 336 break; 337 case 'f': 338 g_flag |= CTF_DIFF_FUNCS; 339 break; 340 case 'o': 341 g_flag |= CTF_DIFF_OBJS; 342 break; 343 case 'l': 344 g_flag |= CTF_DIFF_LABEL; 345 break; 346 case 'q': 347 g_onlydiff = B_TRUE; 348 break; 349 case 'p': 350 pifp = ctf_open(optarg, &err); 351 if (pifp == NULL) { 352 ctfdiff_fatal("failed to open parent input " 353 "container %s: %s\n", optarg, 354 ctf_errmsg(err)); 355 } 356 break; 357 case 'F': 358 if (g_nextfunc == g_nfuncs) { 359 if (g_nfuncs == 0) 360 g_nfuncs = 16; 361 else 362 g_nfuncs *= 2; 363 g_funclist = realloc(g_funclist, 364 sizeof (char *) * g_nfuncs); 365 if (g_funclist == NULL) { 366 ctfdiff_fatal("failed to allocate " 367 "memory for the %dth -F option: " 368 "%s\n", g_nexttype + 1, 369 strerror(errno)); 370 } 371 } 372 g_funclist[g_nextfunc] = optarg; 373 g_nextfunc++; 374 break; 375 case 'O': 376 if (g_nextobj == g_nobjs) { 377 if (g_nobjs == 0) 378 g_nobjs = 16; 379 else 380 g_nobjs *= 2; 381 g_objlist = realloc(g_objlist, 382 sizeof (char *) * g_nobjs); 383 if (g_objlist == NULL) { 384 ctfdiff_fatal("failed to allocate " 385 "memory for the %dth -F option: " 386 "%s\n", g_nexttype + 1, 387 strerror(errno)); 388 return (CTFDIFF_EXIT_ERROR); 389 } 390 } 391 g_objlist[g_nextobj] = optarg; 392 g_nextobj++; 393 break; 394 case 'I': 395 flags |= CTF_DIFF_F_IGNORE_INTNAMES; 396 break; 397 case 'P': 398 pofp = ctf_open(optarg, &err); 399 if (pofp == NULL) { 400 ctfdiff_fatal("failed to open parent output " 401 "container %s: %s\n", optarg, 402 ctf_errmsg(err)); 403 } 404 break; 405 case 'T': 406 if (g_nexttype == g_ntypes) { 407 if (g_ntypes == 0) 408 g_ntypes = 16; 409 else 410 g_ntypes *= 2; 411 g_typelist = realloc(g_typelist, 412 sizeof (char *) * g_ntypes); 413 if (g_typelist == NULL) { 414 ctfdiff_fatal("failed to allocate " 415 "memory for the %dth -T option: " 416 "%s\n", g_nexttype + 1, 417 strerror(errno)); 418 } 419 } 420 g_typelist[g_nexttype] = optarg; 421 g_nexttype++; 422 break; 423 case ':': 424 ctfdiff_usage("Option -%c requires an operand\n", 425 optopt); 426 return (CTFDIFF_EXIT_USAGE); 427 case '?': 428 ctfdiff_usage("Unknown option: -%c\n", optopt); 429 return (CTFDIFF_EXIT_USAGE); 430 } 431 } 432 433 argc -= optind - 1; 434 argv += optind - 1; 435 436 if (g_flag == 0) 437 g_flag = CTF_DIFF_DEFAULT; 438 439 if (argc != 3) { 440 ctfdiff_usage(NULL); 441 return (CTFDIFF_EXIT_USAGE); 442 } 443 444 if (g_nexttype != 0 && !(g_flag & CTF_DIFF_TYPES)) { 445 ctfdiff_usage("-T cannot be used if not diffing types\n"); 446 return (CTFDIFF_EXIT_USAGE); 447 } 448 449 if (g_nextfunc != 0 && !(g_flag & CTF_DIFF_FUNCS)) { 450 ctfdiff_usage("-F cannot be used if not diffing functions\n"); 451 return (CTFDIFF_EXIT_USAGE); 452 } 453 454 if (g_nextobj != 0 && !(g_flag & CTF_DIFF_OBJS)) { 455 ctfdiff_usage("-O cannot be used if not diffing objects\n"); 456 return (CTFDIFF_EXIT_USAGE); 457 } 458 459 ifp = ctf_open(argv[1], &err); 460 if (ifp == NULL) { 461 ctfdiff_fatal("failed to open %s: %s\n", argv[1], 462 ctf_errmsg(err)); 463 } 464 if (pifp != NULL) { 465 err = ctf_import(ifp, pifp); 466 if (err != 0) { 467 ctfdiff_fatal("failed to set parent container: %s\n", 468 ctf_errmsg(ctf_errno(pifp))); 469 } 470 } 471 g_iname = argv[1]; 472 g_ifp = ifp; 473 474 ofp = ctf_open(argv[2], &err); 475 if (ofp == NULL) { 476 ctfdiff_fatal("failed to open %s: %s\n", argv[2], 477 ctf_errmsg(err)); 478 } 479 480 if (pofp != NULL) { 481 err = ctf_import(ofp, pofp); 482 if (err != 0) { 483 ctfdiff_fatal("failed to set parent container: %s\n", 484 ctf_errmsg(ctf_errno(pofp))); 485 } 486 } 487 g_oname = argv[2]; 488 g_ofp = ofp; 489 490 if (ctf_diff_init(ifp, ofp, &cdp) != 0) { 491 ctfdiff_fatal("failed to initialize libctf diff engine: %s\n", 492 ctf_errmsg(ctf_errno(ifp))); 493 } 494 495 if (ctf_diff_setflags(cdp, flags) != 0) { 496 ctfdiff_fatal("failed to set ctfdiff flags: %s\n", 497 ctf_errmsg(ctf_errno(ifp))); 498 } 499 500 err = 0; 501 if ((g_flag & CTF_DIFF_TYPES) && err != CTF_ERR) 502 err = ctf_diff_types(cdp, ctfdiff_cb, NULL); 503 if ((g_flag & CTF_DIFF_FUNCS) && err != CTF_ERR) 504 err = ctf_diff_functions(cdp, ctfdiff_func_cb, NULL); 505 if ((g_flag & CTF_DIFF_OBJS) && err != CTF_ERR) 506 err = ctf_diff_objects(cdp, ctfdiff_obj_cb, NULL); 507 if ((g_flag & CTF_DIFF_LABEL) && err != CTF_ERR) 508 err = ctfdiff_labels(ifp, ofp); 509 510 ctf_diff_fini(cdp); 511 if (err == CTF_ERR) { 512 ctfdiff_fatal("encountered a libctf error: %s!\n", 513 ctf_errmsg(ctf_errno(ifp))); 514 } 515 516 return (g_different == B_TRUE ? CTFDIFF_EXIT_DIFFERENT : 517 CTFDIFF_EXIT_SIMILAR); 518 } 519