Merge commit '00f1a4f432b3d8aad1aa270e91c44c57f03ef407'
[unleashed.git] / usr / src / cmd / ctfdiff / ctfdiff.c
blob33c246262fe104ae8b7297c34771a9c1001f2f9b
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.
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.
13 * Copyright (c) 2015, Joyent, Inc.
17 * diff two CTF containers
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>
28 #define CTFDIFF_NAMELEN 256
30 #define CTFDIFF_EXIT_SIMILAR 0
31 #define CTFDIFF_EXIT_DIFFERENT 1
32 #define CTFDIFF_EXIT_USAGE 2
33 #define CTFDIFF_EXIT_ERROR 3
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;
44 typedef struct {
45 int dil_next;
46 const char **dil_labels;
47 } ctfdiff_label_t;
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;
67 static void
68 ctfdiff_fatal(const char *fmt, ...)
70 va_list ap;
72 (void) fprintf(stderr, "%s: ", g_progname);
73 va_start(ap, fmt);
74 (void) vfprintf(stderr, fmt, ap);
75 va_end(ap);
77 exit(CTFDIFF_EXIT_ERROR);
80 static const char *
81 ctfdiff_fp_to_name(ctf_file_t *fp)
83 if (fp == g_ifp)
84 return (g_iname);
85 if (fp == g_ofp)
86 return (g_oname);
87 return (NULL);
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)
95 char namebuf[CTFDIFF_NAMELEN];
97 if (similar == B_TRUE)
98 return;
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 %ld 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;
112 if (i == g_nextfunc)
113 return;
115 (void) printf("ctf container %s function %s (%ld) is "
116 "different\n", ctfdiff_fp_to_name(ifp), namebuf, iidx);
119 g_different = B_TRUE;
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)
127 char namebuf[CTFDIFF_NAMELEN];
129 if (similar == B_TRUE)
130 return;
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 %ld 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;
144 if (i == g_nextobj)
145 return;
147 (void) printf("ctf container %s object %s (%ld) is different\n",
148 ctfdiff_fp_to_name(ifp), namebuf, iidx);
151 g_different = B_TRUE;
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)
159 if (similar == B_TRUE)
160 return;
162 if (ctf_type_kind(ifp, iid) == CTF_K_UNKNOWN)
163 return;
166 * Check if it's the type the user cares about.
168 if (g_nexttype != 0) {
169 int i;
170 char namebuf[CTFDIFF_NAMELEN];
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)));
180 for (i = 0; i < g_nexttype; i++) {
181 if (strcmp(g_typelist[i], namebuf) == 0)
182 break;
185 if (i == g_nexttype)
186 return;
189 g_different = B_TRUE;
191 if (g_onlydiff == B_TRUE)
192 return;
194 (void) printf("ctf container %s type %ld is different\n",
195 ctfdiff_fp_to_name(ifp), iid);
198 /* ARGSUSED */
199 static int
200 ctfdiff_labels_count(const char *name, const ctf_lblinfo_t *li, void *arg)
202 uint32_t *count = arg;
203 *count = *count + 1;
205 return (0);
208 /* ARGSUSED */
209 static int
210 ctfdiff_labels_fill(const char *name, const ctf_lblinfo_t *li, void *arg)
212 ctfdiff_label_t *dil = arg;
214 dil->dil_labels[dil->dil_next] = name;
215 dil->dil_next++;
217 return (0);
220 static int
221 ctfdiff_labels(ctf_file_t *ifp, ctf_file_t *ofp)
223 int ret;
224 uint32_t nilabel, nolabel, i, j;
225 ctfdiff_label_t idl, odl;
226 const char **ilptr, **olptr;
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);
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);
244 if (nilabel == 0)
245 return (0);
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");
254 idl.dil_next = 0;
255 idl.dil_labels = ilptr;
256 odl.dil_next = 0;
257 odl.dil_labels = olptr;
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;
264 for (i = 0; i < nilabel; i++) {
265 for (j = 0; j < nolabel; j++) {
266 if (strcmp(ilptr[i], olptr[j]) == 0)
267 break;
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;
279 ret = 0;
280 out:
281 free(ilptr);
282 free(olptr);
283 return (ret);
286 static void
287 ctfdiff_usage(const char *fmt, ...)
289 if (fmt != NULL) {
290 va_list ap;
292 (void) fprintf(stderr, "%s: ", g_progname);
293 va_start(ap, fmt);
294 (void) vfprintf(stderr, fmt, ap);
295 va_end(ap);
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);
318 main(int argc, char *argv[])
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;
327 g_progname = basename(argv[0]);
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));
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 = reallocarray(g_funclist,
364 g_nfuncs, sizeof (char *));
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));
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 = reallocarray(g_objlist, g_nobjs,
382 sizeof (char *));
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);
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));
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 = reallocarray(g_typelist,
412 g_ntypes, sizeof (char *));
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));
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);
433 argc -= optind - 1;
434 argv += optind - 1;
436 if (g_flag == 0)
437 g_flag = CTF_DIFF_DEFAULT;
439 if (argc != 3) {
440 ctfdiff_usage(NULL);
441 return (CTFDIFF_EXIT_USAGE);
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);
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);
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);
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));
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)));
471 g_iname = argv[1];
472 g_ifp = ifp;
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));
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)));
487 g_oname = argv[2];
488 g_ofp = ofp;
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)));
495 if (ctf_diff_setflags(cdp, flags) != 0) {
496 ctfdiff_fatal("failed to set ctfdiff flags: %s\n",
497 ctf_errmsg(ctf_errno(ifp)));
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);
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)));
516 return (g_different == B_TRUE ? CTFDIFF_EXIT_DIFFERENT :
517 CTFDIFF_EXIT_SIMILAR);