summaryrefslogtreecommitdiff
path: root/usr/src/cmd/ctfdiff/ctfdiff.c
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2019-01-17 17:50:46 +0000
committerRobert Mustacchi <rm@joyent.com>2019-02-11 17:40:04 +0000
commitbc1f688b4872ace323eaddbb1a6365d054e7bf56 (patch)
tree3b6f2f4caaa4bafcfb4f757be7ea4de2858201ce /usr/src/cmd/ctfdiff/ctfdiff.c
parent2b987d42b0ad07d74e39b18a2498709e5195d7e3 (diff)
downloadillumos-joyent-bc1f688b4872ace323eaddbb1a6365d054e7bf56.tar.gz
6885 CTF Everywhere Part 1
6886 Want ctfdiff 6887 ctfdump should be written in terms of libctf 6888 ctfmerge should be implemented in terms of libctf 6889 ctfconvert should be implemented in terms of libctf 6890 Want general workq 6891 Want general mergeq 6892 ctf_add_encoded assigns() incorrect byte size to types 6893 ctf_add_{struct,union,enum} can reuse forwards 6894 ctf_add_{struct,union,enum} occasionally forget to dirty the ctf_file_t 6895 ctf_add_member could better handle bitfields 6896 ctf_type_size() reports wrong size for forwards 6897 Want libctf ctf_kind_name() function 6898 Want libctf function to set struct/union size Portions contributed by: John Levon <john.levon@joyent.com> Portions contributed by: Richard Lowe <richlowe@richlowe.net> Reviewed by: John Levon <john.levon@joyent.com> Reviewed by: Andy Fiddaman <andy@omniosce.org> Reviewed by: Gergő Doma <domag02@gmail.com> Approved by: Dan McDonald <danmcd@joyent.com>
Diffstat (limited to 'usr/src/cmd/ctfdiff/ctfdiff.c')
-rw-r--r--usr/src/cmd/ctfdiff/ctfdiff.c518
1 files changed, 518 insertions, 0 deletions
diff --git a/usr/src/cmd/ctfdiff/ctfdiff.c b/usr/src/cmd/ctfdiff/ctfdiff.c
new file mode 100644
index 0000000000..731fba4af8
--- /dev/null
+++ b/usr/src/cmd/ctfdiff/ctfdiff.c
@@ -0,0 +1,518 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2015, Joyent, Inc.
+ */
+
+/*
+ * diff two CTF containers
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <strings.h>
+#include <libctf.h>
+#include <libgen.h>
+#include <stdarg.h>
+
+#define CTFDIFF_NAMELEN 256
+
+#define CTFDIFF_EXIT_SIMILAR 0
+#define CTFDIFF_EXIT_DIFFERENT 1
+#define CTFDIFF_EXIT_USAGE 2
+#define CTFDIFF_EXIT_ERROR 3
+
+typedef enum ctf_diff_cmd {
+ CTF_DIFF_TYPES = 0x01,
+ CTF_DIFF_FUNCS = 0x02,
+ CTF_DIFF_OBJS = 0x04,
+ CTF_DIFF_DEFAULT = 0x07,
+ CTF_DIFF_LABEL = 0x08,
+ CTF_DIFF_ALL = 0x0f
+} ctf_diff_cmd_t;
+
+typedef struct {
+ int dil_next;
+ const char **dil_labels;
+} ctfdiff_label_t;
+
+static char *g_progname;
+static const char *g_iname;
+static ctf_file_t *g_ifp;
+static const char *g_oname;
+static ctf_file_t *g_ofp;
+static char **g_typelist = NULL;
+static int g_nexttype = 0;
+static int g_ntypes = 0;
+static char **g_objlist = NULL;
+static int g_nextfunc = 0;
+static int g_nfuncs = 0;
+static char **g_funclist = NULL;
+static int g_nextobj = 0;
+static int g_nobjs = 0;
+static boolean_t g_onlydiff = B_FALSE;
+static boolean_t g_different = B_FALSE;
+static ctf_diff_cmd_t g_flag = 0;
+
+static void
+ctfdiff_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ exit(CTFDIFF_EXIT_ERROR);
+}
+
+static const char *
+ctfdiff_fp_to_name(ctf_file_t *fp)
+{
+ if (fp == g_ifp)
+ return (g_iname);
+ if (fp == g_ofp)
+ return (g_oname);
+ return (NULL);
+}
+
+/* ARGSUSED */
+static void
+ctfdiff_func_cb(ctf_file_t *ifp, ulong_t iidx, boolean_t similar,
+ ctf_file_t *ofp, ulong_t oidx, void *arg)
+{
+ char namebuf[CTFDIFF_NAMELEN];
+
+ if (similar == B_TRUE)
+ return;
+
+ if (ctf_symbol_name(ifp, iidx, namebuf, sizeof (namebuf)) == NULL) {
+ if (g_nextfunc != 0)
+ return;
+ (void) printf("ctf container %s function %lu is different\n",
+ ctfdiff_fp_to_name(ifp), iidx);
+ } else {
+ if (g_nextfunc != 0) {
+ int i;
+ for (i = 0; i < g_nextfunc; i++) {
+ if (strcmp(g_funclist[i], namebuf) == 0)
+ break;
+ }
+ if (i == g_nextfunc)
+ return;
+ }
+ (void) printf("ctf container %s function %s (%lu) is "
+ "different\n", ctfdiff_fp_to_name(ifp), namebuf, iidx);
+ }
+
+ g_different = B_TRUE;
+}
+
+/* ARGSUSED */
+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)
+{
+ char namebuf[CTFDIFF_NAMELEN];
+
+ if (similar == B_TRUE)
+ return;
+
+ if (ctf_symbol_name(ifp, iidx, namebuf, sizeof (namebuf)) == NULL) {
+ if (g_nextobj != 0)
+ return;
+ (void) printf("ctf container %s object %lu is different\n",
+ ctfdiff_fp_to_name(ifp), iidx);
+ } else {
+ if (g_nextobj != 0) {
+ int i;
+ for (i = 0; i < g_nextobj; i++) {
+ if (strcmp(g_objlist[i], namebuf) == 0)
+ break;
+ }
+ if (i == g_nextobj)
+ return;
+ }
+ (void) printf("ctf container %s object %s (%lu) is different\n",
+ ctfdiff_fp_to_name(ifp), namebuf, iidx);
+ }
+
+ g_different = B_TRUE;
+}
+
+/* ARGSUSED */
+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)
+{
+ if (similar == B_TRUE)
+ return;
+
+ if (ctf_type_kind(ifp, iid) == CTF_K_UNKNOWN)
+ return;
+
+ /*
+ * Check if it's the type the user cares about.
+ */
+ if (g_nexttype != 0) {
+ int i;
+ char namebuf[CTFDIFF_NAMELEN];
+
+ if (ctf_type_name(ifp, iid, namebuf, sizeof (namebuf)) ==
+ NULL) {
+ ctfdiff_fatal("failed to obtain the name "
+ "of type %ld from %s: %s\n",
+ iid, ctfdiff_fp_to_name(ifp),
+ ctf_errmsg(ctf_errno(ifp)));
+ }
+
+ for (i = 0; i < g_nexttype; i++) {
+ if (strcmp(g_typelist[i], namebuf) == 0)
+ break;
+ }
+
+ if (i == g_nexttype)
+ return;
+ }
+
+ g_different = B_TRUE;
+
+ if (g_onlydiff == B_TRUE)
+ return;
+
+ (void) printf("ctf container %s type %ld is different\n",
+ ctfdiff_fp_to_name(ifp), iid);
+}
+
+/* ARGSUSED */
+static int
+ctfdiff_labels_count(const char *name, const ctf_lblinfo_t *li, void *arg)
+{
+ uint32_t *count = arg;
+ *count = *count + 1;
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ctfdiff_labels_fill(const char *name, const ctf_lblinfo_t *li, void *arg)
+{
+ ctfdiff_label_t *dil = arg;
+
+ dil->dil_labels[dil->dil_next] = name;
+ dil->dil_next++;
+
+ return (0);
+}
+
+static int
+ctfdiff_labels(ctf_file_t *ifp, ctf_file_t *ofp)
+{
+ int ret;
+ uint32_t nilabel, nolabel, i, j;
+ ctfdiff_label_t idl, odl;
+ const char **ilptr, **olptr;
+
+ nilabel = nolabel = 0;
+ ret = ctf_label_iter(ifp, ctfdiff_labels_count, &nilabel);
+ if (ret == CTF_ERR)
+ return (ret);
+ ret = ctf_label_iter(ofp, ctfdiff_labels_count, &nolabel);
+ if (ret == CTF_ERR)
+ return (ret);
+
+ if (nilabel != nolabel) {
+ (void) printf("ctf container %s labels differ from ctf "
+ "container %s\n", ctfdiff_fp_to_name(ifp),
+ ctfdiff_fp_to_name(ofp));
+ g_different = B_TRUE;
+ return (0);
+ }
+
+ if (nilabel == 0)
+ return (0);
+
+ ilptr = malloc(sizeof (char *) * nilabel);
+ olptr = malloc(sizeof (char *) * nolabel);
+ if (ilptr == NULL || olptr == NULL) {
+ ctfdiff_fatal("failed to allocate memory for label "
+ "comparison\n");
+ }
+
+ idl.dil_next = 0;
+ idl.dil_labels = ilptr;
+ odl.dil_next = 0;
+ odl.dil_labels = olptr;
+
+ if ((ret = ctf_label_iter(ifp, ctfdiff_labels_fill, &idl)) != 0)
+ goto out;
+ if ((ret = ctf_label_iter(ofp, ctfdiff_labels_fill, &odl)) != 0)
+ goto out;
+
+ for (i = 0; i < nilabel; i++) {
+ for (j = 0; j < nolabel; j++) {
+ if (strcmp(ilptr[i], olptr[j]) == 0)
+ break;
+ }
+
+ if (j == nolabel) {
+ (void) printf("ctf container %s labels differ from ctf "
+ "container %s\n", ctfdiff_fp_to_name(ifp),
+ ctfdiff_fp_to_name(ofp));
+ g_different = B_TRUE;
+ break;
+ }
+ }
+
+ ret = 0;
+out:
+ free(ilptr);
+ free(olptr);
+ return (ret);
+}
+
+static void
+ctfdiff_usage(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ (void) fprintf(stderr, "Usage: %s [-afIloqt] [-F function] [-O object]"
+ "[-p parent] [-P parent]\n"
+ "\t[-T type] file1 file2\n"
+ "\n"
+ "\t-a diff label, types, objects, and functions\n"
+ "\t-f diff function type information\n"
+ "\t-F when diffing functions, only consider those named\n"
+ "\t-I ignore the names of integral types\n"
+ "\t-l diff CTF labels\n"
+ "\t-o diff global object type information\n"
+ "\t-O when diffing objects, only consider those named\n"
+ "\t-p set the CTF parent for file1\n"
+ "\t-P set the CTF parent for file2\n"
+ "\t-q set quiet mode (no diff information sent to stdout)\n"
+ "\t-t diff CTF type information\n"
+ "\t-T when diffing types, only consider those named\n",
+ g_progname);
+}
+
+int
+main(int argc, char *argv[])
+{
+ ctf_diff_flag_t flags = 0;
+ int err, c;
+ ctf_file_t *ifp, *ofp;
+ ctf_diff_t *cdp;
+ ctf_file_t *pifp = NULL;
+ ctf_file_t *pofp = NULL;
+
+ g_progname = basename(argv[0]);
+
+ while ((c = getopt(argc, argv, ":aqtfolIp:F:O:P:T:")) != -1) {
+ switch (c) {
+ case 'a':
+ g_flag |= CTF_DIFF_ALL;
+ break;
+ case 't':
+ g_flag |= CTF_DIFF_TYPES;
+ break;
+ case 'f':
+ g_flag |= CTF_DIFF_FUNCS;
+ break;
+ case 'o':
+ g_flag |= CTF_DIFF_OBJS;
+ break;
+ case 'l':
+ g_flag |= CTF_DIFF_LABEL;
+ break;
+ case 'q':
+ g_onlydiff = B_TRUE;
+ break;
+ case 'p':
+ pifp = ctf_open(optarg, &err);
+ if (pifp == NULL) {
+ ctfdiff_fatal("failed to open parent input "
+ "container %s: %s\n", optarg,
+ ctf_errmsg(err));
+ }
+ break;
+ case 'F':
+ if (g_nextfunc == g_nfuncs) {
+ if (g_nfuncs == 0)
+ g_nfuncs = 16;
+ else
+ g_nfuncs *= 2;
+ g_funclist = realloc(g_funclist,
+ sizeof (char *) * g_nfuncs);
+ if (g_funclist == NULL) {
+ ctfdiff_fatal("failed to allocate "
+ "memory for the %dth -F option: "
+ "%s\n", g_nexttype + 1,
+ strerror(errno));
+ }
+ }
+ g_funclist[g_nextfunc] = optarg;
+ g_nextfunc++;
+ break;
+ case 'O':
+ if (g_nextobj == g_nobjs) {
+ if (g_nobjs == 0)
+ g_nobjs = 16;
+ else
+ g_nobjs *= 2;
+ g_objlist = realloc(g_objlist,
+ sizeof (char *) * g_nobjs);
+ if (g_objlist == NULL) {
+ ctfdiff_fatal("failed to allocate "
+ "memory for the %dth -F option: "
+ "%s\n", g_nexttype + 1,
+ strerror(errno));
+ return (CTFDIFF_EXIT_ERROR);
+ }
+ }
+ g_objlist[g_nextobj] = optarg;
+ g_nextobj++;
+ break;
+ case 'I':
+ flags |= CTF_DIFF_F_IGNORE_INTNAMES;
+ break;
+ case 'P':
+ pofp = ctf_open(optarg, &err);
+ if (pofp == NULL) {
+ ctfdiff_fatal("failed to open parent output "
+ "container %s: %s\n", optarg,
+ ctf_errmsg(err));
+ }
+ break;
+ case 'T':
+ if (g_nexttype == g_ntypes) {
+ if (g_ntypes == 0)
+ g_ntypes = 16;
+ else
+ g_ntypes *= 2;
+ g_typelist = realloc(g_typelist,
+ sizeof (char *) * g_ntypes);
+ if (g_typelist == NULL) {
+ ctfdiff_fatal("failed to allocate "
+ "memory for the %dth -T option: "
+ "%s\n", g_nexttype + 1,
+ strerror(errno));
+ }
+ }
+ g_typelist[g_nexttype] = optarg;
+ g_nexttype++;
+ break;
+ case ':':
+ ctfdiff_usage("Option -%c requires an operand\n",
+ optopt);
+ return (CTFDIFF_EXIT_USAGE);
+ case '?':
+ ctfdiff_usage("Unknown option: -%c\n", optopt);
+ return (CTFDIFF_EXIT_USAGE);
+ }
+ }
+
+ argc -= optind - 1;
+ argv += optind - 1;
+
+ if (g_flag == 0)
+ g_flag = CTF_DIFF_DEFAULT;
+
+ if (argc != 3) {
+ ctfdiff_usage(NULL);
+ return (CTFDIFF_EXIT_USAGE);
+ }
+
+ if (g_nexttype != 0 && !(g_flag & CTF_DIFF_TYPES)) {
+ ctfdiff_usage("-T cannot be used if not diffing types\n");
+ return (CTFDIFF_EXIT_USAGE);
+ }
+
+ if (g_nextfunc != 0 && !(g_flag & CTF_DIFF_FUNCS)) {
+ ctfdiff_usage("-F cannot be used if not diffing functions\n");
+ return (CTFDIFF_EXIT_USAGE);
+ }
+
+ if (g_nextobj != 0 && !(g_flag & CTF_DIFF_OBJS)) {
+ ctfdiff_usage("-O cannot be used if not diffing objects\n");
+ return (CTFDIFF_EXIT_USAGE);
+ }
+
+ ifp = ctf_open(argv[1], &err);
+ if (ifp == NULL) {
+ ctfdiff_fatal("failed to open %s: %s\n", argv[1],
+ ctf_errmsg(err));
+ }
+ if (pifp != NULL) {
+ err = ctf_import(ifp, pifp);
+ if (err != 0) {
+ ctfdiff_fatal("failed to set parent container: %s\n",
+ ctf_errmsg(ctf_errno(pifp)));
+ }
+ }
+ g_iname = argv[1];
+ g_ifp = ifp;
+
+ ofp = ctf_open(argv[2], &err);
+ if (ofp == NULL) {
+ ctfdiff_fatal("failed to open %s: %s\n", argv[2],
+ ctf_errmsg(err));
+ }
+
+ if (pofp != NULL) {
+ err = ctf_import(ofp, pofp);
+ if (err != 0) {
+ ctfdiff_fatal("failed to set parent container: %s\n",
+ ctf_errmsg(ctf_errno(pofp)));
+ }
+ }
+ g_oname = argv[2];
+ g_ofp = ofp;
+
+ if (ctf_diff_init(ifp, ofp, &cdp) != 0) {
+ ctfdiff_fatal("failed to initialize libctf diff engine: %s\n",
+ ctf_errmsg(ctf_errno(ifp)));
+ }
+
+ if (ctf_diff_setflags(cdp, flags) != 0) {
+ ctfdiff_fatal("failed to set ctfdiff flags: %s\n",
+ ctf_errmsg(ctf_errno(ifp)));
+ }
+
+ err = 0;
+ if ((g_flag & CTF_DIFF_TYPES) && err != CTF_ERR)
+ err = ctf_diff_types(cdp, ctfdiff_cb, NULL);
+ if ((g_flag & CTF_DIFF_FUNCS) && err != CTF_ERR)
+ err = ctf_diff_functions(cdp, ctfdiff_func_cb, NULL);
+ if ((g_flag & CTF_DIFF_OBJS) && err != CTF_ERR)
+ err = ctf_diff_objects(cdp, ctfdiff_obj_cb, NULL);
+ if ((g_flag & CTF_DIFF_LABEL) && err != CTF_ERR)
+ err = ctfdiff_labels(ifp, ofp);
+
+ ctf_diff_fini(cdp);
+ if (err == CTF_ERR) {
+ ctfdiff_fatal("encountered a libctf error: %s!\n",
+ ctf_errmsg(ctf_errno(ifp)));
+ }
+
+ return (g_different == B_TRUE ? CTFDIFF_EXIT_DIFFERENT :
+ CTFDIFF_EXIT_SIMILAR);
+}