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
ctfdiff_fatal(const char * fmt,...)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 *
ctfdiff_fp_to_name(ctf_file_t * fp)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
ctfdiff_func_cb(ctf_file_t * ifp,ulong_t iidx,boolean_t similar,ctf_file_t * ofp,ulong_t oidx,void * arg)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
ctfdiff_obj_cb(ctf_file_t * ifp,ulong_t iidx,ctf_id_t iid,boolean_t similar,ctf_file_t * ofp,ulong_t oidx,ctf_id_t oid,void * arg)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
ctfdiff_cb(ctf_file_t * ifp,ctf_id_t iid,boolean_t similar,ctf_file_t * ofp,ctf_id_t oid,void * arg)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
ctfdiff_labels_count(const char * name,const ctf_lblinfo_t * li,void * arg)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
ctfdiff_labels_fill(const char * name,const ctf_lblinfo_t * li,void * arg)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
ctfdiff_labels(ctf_file_t * ifp,ctf_file_t * ofp)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
ctfdiff_usage(const char * fmt,...)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
main(int argc,char * argv[])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