xref: /illumos-gate/usr/src/cmd/ctfdiff/ctfdiff.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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