summaryrefslogtreecommitdiff
path: root/usr/src/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd')
-rw-r--r--usr/src/cmd/Makefile1
-rw-r--r--usr/src/cmd/ctfconvert/Makefile33
-rw-r--r--usr/src/cmd/ctfconvert/ctfconvert.c389
-rw-r--r--usr/src/cmd/ctfdiff/ctfdiff.c76
-rw-r--r--usr/src/cmd/ctfdump/ctfdump.c64
-rw-r--r--usr/src/cmd/ctfmerge/ctfmerge.c81
6 files changed, 570 insertions, 74 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 4c984d1458..8bb04e9a29 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -103,6 +103,7 @@ COMMON_SUBDIRS= \
crypt \
csh \
csplit \
+ ctfconvert \
ctfdiff \
ctfdump \
ctfmerge \
diff --git a/usr/src/cmd/ctfconvert/Makefile b/usr/src/cmd/ctfconvert/Makefile
new file mode 100644
index 0000000000..688addd9d1
--- /dev/null
+++ b/usr/src/cmd/ctfconvert/Makefile
@@ -0,0 +1,33 @@
+#
+# 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.
+#
+
+PROG= ctfconvert
+
+include ../Makefile.cmd
+
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lctf -lelf
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+clean:
+
+lint: lint_PROG
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/ctfconvert/ctfconvert.c b/usr/src/cmd/ctfconvert/ctfconvert.c
new file mode 100644
index 0000000000..dd3d40c6cc
--- /dev/null
+++ b/usr/src/cmd/ctfconvert/ctfconvert.c
@@ -0,0 +1,389 @@
+/*
+ * 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.
+ */
+
+/*
+ * Create CTF from extant debugging information
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <libelf.h>
+#include <libctf.h>
+#include <string.h>
+#include <libgen.h>
+#include <limits.h>
+#include <strings.h>
+#include <sys/debug.h>
+
+#define CTFCONVERT_OK 0
+#define CTFCONVERT_FATAL 1
+#define CTFCONVERT_USAGE 2
+
+#define CTFCONVERT_DEFAULT_NTHREADS 4
+
+#define CTFCONVERT_ALTEXEC "CTFCONVERT_ALTEXEC"
+
+static char *ctfconvert_progname;
+
+static void
+ctfconvert_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", ctfconvert_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ exit(CTFCONVERT_FATAL);
+}
+
+
+static void
+ctfconvert_usage(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", ctfconvert_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ (void) fprintf(stderr, "Usage: %s [-is] [-j nthrs] [-l label | "
+ "-L labelenv] [-o outfile] input\n"
+ "\n"
+ "\t-i ignore files not built partially from C sources\n"
+ "\t-j use nthrs threads to perform the merge\n"
+ "\t-k keep around original input file on failure\n"
+ "\t-o copy input to outfile and add CTF\n"
+ "\t-l set output container's label to specified value\n"
+ "\t-L set output container's label to value from environment\n",
+ ctfconvert_progname);
+}
+
+/*
+ * This is a bit unfortunate. Traditionally we do type uniquification across all
+ * modules in the kernel, including ip and unix against genunix. However, when
+ * _MACHDEP is defined, then the cpu_t ends up having an additional member
+ * (cpu_m), thus changing the ability for us to uniquify against it. This in
+ * turn causes a lot of type sprawl, as there's a lot of things that end up
+ * referring to the cpu_t and it chains out from there.
+ *
+ * So, if we find that a cpu_t has been defined and it has a couple of useful
+ * sentinel members and it does *not* have the cpu_m member, then we will try
+ * and lookup or create a forward declaration to the machcpu, append it to the
+ * end, and update the file.
+ *
+ * This currently is only invoked if an undocumented option -X is passed. This
+ * value is private to illumos and it can be changed at any time inside of it,
+ * so if -X wants to be used for something, it should be. The ability to rely on
+ * -X for others is strictly not an interface in any way, shape, or form.
+ *
+ * The following struct contains most of the information that we care about and
+ * that we want to validate exists before we decide what to do.
+ */
+
+typedef struct ctfconvert_fixup {
+ boolean_t cf_cyclic; /* Do we have a cpu_cyclic member */
+ boolean_t cf_mcpu; /* We have a cpu_m member */
+ boolean_t cf_lastpad; /* Is the pad member the last entry */
+ ulong_t cf_padoff; /* offset of the pad */
+} ctfconvert_fixup_t;
+
+/* ARGSUSED */
+static int
+ctfconvert_fixup_genunix_cb(const char *name, ctf_id_t tid, ulong_t off,
+ void *arg)
+{
+ ctfconvert_fixup_t *cfp = arg;
+
+ cfp->cf_lastpad = B_FALSE;
+ if (strcmp(name, "cpu_cyclic") == 0) {
+ cfp->cf_cyclic = B_TRUE;
+ return (0);
+ }
+
+ if (strcmp(name, "cpu_m") == 0) {
+ cfp->cf_mcpu = B_TRUE;
+ return (0);
+ }
+
+ if (strcmp(name, "cpu_m_pad") == 0) {
+ cfp->cf_lastpad = B_TRUE;
+ cfp->cf_padoff = off;
+ return (0);
+ }
+
+ return (0);
+}
+
+static void
+ctfconvert_fixup_genunix(ctf_file_t *fp)
+{
+ ctf_id_t cpuid, mcpu;
+ ssize_t sz;
+ ctfconvert_fixup_t cf;
+ int model, ptrsz;
+
+ cpuid = ctf_lookup_by_name(fp, "struct cpu");
+ if (cpuid == CTF_ERR)
+ return;
+
+ if (ctf_type_kind(fp, cpuid) != CTF_K_STRUCT)
+ return;
+
+ if ((sz = ctf_type_size(fp, cpuid)) == CTF_ERR)
+ return;
+
+ model = ctf_getmodel(fp);
+ VERIFY(model == CTF_MODEL_ILP32 || model == CTF_MODEL_LP64);
+ ptrsz = model == CTF_MODEL_ILP32 ? 4 : 8;
+
+ bzero(&cf, sizeof (ctfconvert_fixup_t));
+ if (ctf_member_iter(fp, cpuid, ctfconvert_fixup_genunix_cb, &cf) ==
+ CTF_ERR)
+ return;
+
+ /*
+ * Finally, we want to verify that the cpu_m is actually the last member
+ * that we have here.
+ */
+ if (cf.cf_cyclic == B_FALSE || cf.cf_mcpu == B_TRUE ||
+ cf.cf_lastpad == B_FALSE) {
+ return;
+ }
+
+ if (cf.cf_padoff + ptrsz * NBBY != sz * NBBY) {
+ return;
+ }
+
+ /*
+ * Okay, we're going to do this, try to find a struct machcpu. We either
+ * want a forward or a struct. If we find something else, error. If we
+ * find nothing, add a forward and then add the member.
+ */
+ mcpu = ctf_lookup_by_name(fp, "struct machcpu");
+ if (mcpu == CTF_ERR) {
+ mcpu = ctf_add_forward(fp, CTF_ADD_NONROOT, "struct machcpu",
+ CTF_K_STRUCT);
+ if (mcpu == CTF_ERR) {
+ ctfconvert_fatal("failed to add 'struct machcpu' "
+ "forward: %s", ctf_errmsg(ctf_errno(fp)));
+ }
+ } else {
+ int kind;
+ if ((kind = ctf_type_kind(fp, mcpu)) == CTF_ERR) {
+ ctfconvert_fatal("failed to get the type kind for "
+ "the struct machcpu: %s",
+ ctf_errmsg(ctf_errno(fp)));
+ }
+
+ if (kind != CTF_K_STRUCT && kind != CTF_K_FORWARD)
+ ctfconvert_fatal("encountered a struct machcpu of the "
+ "wrong type, found type kind %d\n", kind);
+ }
+
+ if (ctf_update(fp) == CTF_ERR) {
+ ctfconvert_fatal("failed to update output file: %s\n",
+ ctf_errmsg(ctf_errno(fp)));
+ }
+
+ if (ctf_add_member(fp, cpuid, "cpu_m", mcpu, sz * NBBY) == CTF_ERR) {
+ ctfconvert_fatal("failed to add the m_cpu member: %s\n",
+ ctf_errmsg(ctf_errno(fp)));
+ }
+
+ if (ctf_update(fp) == CTF_ERR) {
+ ctfconvert_fatal("failed to update output file: %s\n",
+ ctf_errmsg(ctf_errno(fp)));
+ }
+
+ VERIFY(ctf_type_size(fp, cpuid) == sz);
+}
+
+static void
+ctfconvert_altexec(char **argv)
+{
+ const char *alt;
+ char *altexec;
+
+ alt = getenv(CTFCONVERT_ALTEXEC);
+ if (alt == NULL || *alt == '\0')
+ return;
+
+ altexec = strdup(alt);
+ if (altexec == NULL)
+ ctfconvert_fatal("failed to allocate memory for altexec\n");
+ if (unsetenv(CTFCONVERT_ALTEXEC) != 0)
+ ctfconvert_fatal("failed to unset %s from environment: %s\n",
+ CTFCONVERT_ALTEXEC, strerror(errno));
+
+ (void) execv(altexec, argv);
+ ctfconvert_fatal("failed to execute alternate program %s: %s",
+ altexec, strerror(errno));
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c, ifd, err;
+ boolean_t keep = B_FALSE;
+ uint_t flags = 0;
+ uint_t nthreads = CTFCONVERT_DEFAULT_NTHREADS;
+ const char *outfile = NULL;
+ const char *label = NULL;
+ const char *infile = NULL;
+ char *tmpfile;
+ ctf_file_t *ofp;
+ long argj;
+ char *eptr;
+ char buf[4096];
+ boolean_t optx = B_FALSE;
+
+ ctfconvert_progname = basename(argv[0]);
+
+ ctfconvert_altexec(argv);
+
+ while ((c = getopt(argc, argv, ":j:kl:L:o:iX")) != -1) {
+ switch (c) {
+ case 'k':
+ keep = B_TRUE;
+ break;
+ case 'l':
+ label = optarg;
+ break;
+ case 'L':
+ label = getenv(optarg);
+ break;
+ case 'j':
+ errno = 0;
+ argj = strtol(optarg, &eptr, 10);
+ if (errno != 0 || argj == LONG_MAX ||
+ argj == LONG_MIN || argj <= 0 ||
+ argj > UINT_MAX || *eptr != '\0') {
+ ctfconvert_fatal("invalid argument for -j: "
+ "%s\n", optarg);
+ }
+ nthreads = (uint_t)argj;
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+ case 'i':
+ flags |= CTF_CONVERT_F_IGNNONC;
+ break;
+ case 'X':
+ optx = B_TRUE;
+ break;
+ case ':':
+ ctfconvert_usage("Option -%c requires an operand\n",
+ optopt);
+ return (CTFCONVERT_USAGE);
+ case '?':
+ ctfconvert_usage("Unknown option: -%c\n", optopt);
+ return (CTFCONVERT_USAGE);
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ if (argc < 1) {
+ ctfconvert_usage("Missing required input file\n");
+ return (CTFCONVERT_USAGE);
+ }
+ infile = argv[0];
+
+ if (elf_version(EV_CURRENT) == EV_NONE)
+ ctfconvert_fatal("failed to initialize libelf: library is "
+ "out of date\n");
+
+ ifd = open(infile, O_RDONLY);
+ if (ifd < 0) {
+ ctfconvert_fatal("failed to open input file %s: %s\n", infile,
+ strerror(errno));
+ }
+
+ /*
+ * By default we remove the input file on failure unless we've been
+ * given an output file or -k has been specified.
+ */
+ if (outfile != NULL && strcmp(infile, outfile) != 0)
+ keep = B_TRUE;
+
+ ofp = ctf_fdconvert(ifd, label, nthreads, flags, &err, buf,
+ sizeof (buf));
+ if (ofp == NULL) {
+ /*
+ * -i says that we shouldn't concern ourselves with source files
+ * that weren't built from C source code in part. Because this
+ * has been traditionally used across all of illumos, we still
+ * honor it.
+ */
+ if ((flags & CTF_CONVERT_F_IGNNONC) != 0 &&
+ err == ECTF_CONVNOCSRC) {
+ exit(CTFCONVERT_OK);
+ }
+ if (keep == B_FALSE)
+ (void) unlink(infile);
+ ctfconvert_fatal("CTF conversion failed: %s\n",
+ err == ECTF_CONVBKERR ? buf : ctf_errmsg(err));
+ }
+
+ if (optx == B_TRUE)
+ ctfconvert_fixup_genunix(ofp);
+
+ tmpfile = NULL;
+ if (outfile == NULL || strcmp(infile, outfile) == 0) {
+ if (asprintf(&tmpfile, "%s.ctf", infile) == -1) {
+ if (keep == B_FALSE)
+ (void) unlink(infile);
+ ctfconvert_fatal("failed to allocate memory for "
+ "temporary file: %s\n", strerror(errno));
+ }
+ outfile = tmpfile;
+ }
+ err = ctf_elfwrite(ofp, infile, outfile, CTF_ELFWRITE_F_COMPRESS);
+ if (err == CTF_ERR) {
+ (void) unlink(outfile);
+ if (keep == B_FALSE)
+ (void) unlink(infile);
+ ctfconvert_fatal("failed to write CTF section to output file: "
+ "%s", ctf_errmsg(ctf_errno(ofp)));
+ }
+ ctf_close(ofp);
+
+ if (tmpfile != NULL) {
+ if (rename(tmpfile, infile) != 0) {
+ int e = errno;
+ (void) unlink(outfile);
+ if (keep == B_FALSE)
+ (void) unlink(infile);
+ ctfconvert_fatal("failed to rename temporary file: "
+ "%s\n", strerror(e));
+ }
+ }
+ free(tmpfile);
+
+ return (CTFCONVERT_OK);
+}
diff --git a/usr/src/cmd/ctfdiff/ctfdiff.c b/usr/src/cmd/ctfdiff/ctfdiff.c
index 2537c15bcb..8018761257 100644
--- a/usr/src/cmd/ctfdiff/ctfdiff.c
+++ b/usr/src/cmd/ctfdiff/ctfdiff.c
@@ -44,7 +44,7 @@ typedef enum ctf_diff_cmd {
typedef struct {
int dil_next;
const char **dil_labels;
-} diff_label_t;
+} ctfdiff_label_t;
static char *g_progname;
static const char *g_iname;
@@ -78,7 +78,7 @@ ctfdiff_fatal(const char *fmt, ...)
}
static const char *
-fp_to_name(ctf_file_t *fp)
+ctfdiff_fp_to_name(ctf_file_t *fp)
{
if (fp == g_ifp)
return (g_iname);
@@ -89,8 +89,8 @@ fp_to_name(ctf_file_t *fp)
/* ARGSUSED */
static void
-diff_func_cb(ctf_file_t *ifp, ulong_t iidx, boolean_t similar, ctf_file_t *ofp,
- ulong_t oidx, void *arg)
+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];
@@ -101,7 +101,7 @@ diff_func_cb(ctf_file_t *ifp, ulong_t iidx, boolean_t similar, ctf_file_t *ofp,
if (g_nextfunc != 0)
return;
(void) printf("ctf container %s function %ld is different\n",
- fp_to_name(ifp), iidx);
+ ctfdiff_fp_to_name(ifp), iidx);
} else {
if (g_nextfunc != 0) {
int i;
@@ -113,7 +113,7 @@ diff_func_cb(ctf_file_t *ifp, ulong_t iidx, boolean_t similar, ctf_file_t *ofp,
return;
}
(void) printf("ctf container %s function %s (%ld) is "
- "different\n", fp_to_name(ifp), namebuf, iidx);
+ "different\n", ctfdiff_fp_to_name(ifp), namebuf, iidx);
}
g_different = B_TRUE;
@@ -121,7 +121,7 @@ diff_func_cb(ctf_file_t *ifp, ulong_t iidx, boolean_t similar, ctf_file_t *ofp,
/* ARGSUSED */
static void
-diff_obj_cb(ctf_file_t *ifp, ulong_t iidx, ctf_id_t iid, boolean_t similar,
+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];
@@ -133,7 +133,7 @@ diff_obj_cb(ctf_file_t *ifp, ulong_t iidx, ctf_id_t iid, boolean_t similar,
if (g_nextobj != 0)
return;
(void) printf("ctf container %s object %ld is different\n",
- fp_to_name(ifp), iidx);
+ ctfdiff_fp_to_name(ifp), iidx);
} else {
if (g_nextobj != 0) {
int i;
@@ -145,7 +145,7 @@ diff_obj_cb(ctf_file_t *ifp, ulong_t iidx, ctf_id_t iid, boolean_t similar,
return;
}
(void) printf("ctf container %s object %s (%ld) is different\n",
- fp_to_name(ifp), namebuf, iidx);
+ ctfdiff_fp_to_name(ifp), namebuf, iidx);
}
g_different = B_TRUE;
@@ -153,7 +153,7 @@ diff_obj_cb(ctf_file_t *ifp, ulong_t iidx, ctf_id_t iid, boolean_t similar,
/* ARGSUSED */
static void
-diff_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t similar, ctf_file_t *ofp,
+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)
@@ -173,7 +173,7 @@ diff_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t similar, ctf_file_t *ofp,
NULL) {
ctfdiff_fatal("failed to obtain the name "
"of type %ld from %s: %s\n",
- iid, fp_to_name(ifp),
+ iid, ctfdiff_fp_to_name(ifp),
ctf_errmsg(ctf_errno(ifp)));
}
@@ -192,12 +192,12 @@ diff_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t similar, ctf_file_t *ofp,
return;
(void) printf("ctf container %s type %ld is different\n",
- fp_to_name(ifp), iid);
+ ctfdiff_fp_to_name(ifp), iid);
}
/* ARGSUSED */
static int
-diff_labels_count(const char *name, const ctf_lblinfo_t *li, void *arg)
+ctfdiff_labels_count(const char *name, const ctf_lblinfo_t *li, void *arg)
{
uint32_t *count = arg;
*count = *count + 1;
@@ -207,9 +207,9 @@ diff_labels_count(const char *name, const ctf_lblinfo_t *li, void *arg)
/* ARGSUSED */
static int
-diff_labels_fill(const char *name, const ctf_lblinfo_t *li, void *arg)
+ctfdiff_labels_fill(const char *name, const ctf_lblinfo_t *li, void *arg)
{
- diff_label_t *dil = arg;
+ ctfdiff_label_t *dil = arg;
dil->dil_labels[dil->dil_next] = name;
dil->dil_next++;
@@ -218,24 +218,25 @@ diff_labels_fill(const char *name, const ctf_lblinfo_t *li, void *arg)
}
static int
-diff_labels(ctf_file_t *ifp, ctf_file_t *ofp)
+ctfdiff_labels(ctf_file_t *ifp, ctf_file_t *ofp)
{
int ret;
uint32_t nilabel, nolabel, i, j;
- diff_label_t idl, odl;
+ ctfdiff_label_t idl, odl;
const char **ilptr, **olptr;
nilabel = nolabel = 0;
- ret = ctf_label_iter(ifp, diff_labels_count, &nilabel);
+ ret = ctf_label_iter(ifp, ctfdiff_labels_count, &nilabel);
if (ret == CTF_ERR)
return (ret);
- ret = ctf_label_iter(ofp, diff_labels_count, &nolabel);
+ 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", fp_to_name(ifp), fp_to_name(ofp));
+ "container %s\n", ctfdiff_fp_to_name(ifp),
+ ctfdiff_fp_to_name(ofp));
g_different = B_TRUE;
return (0);
}
@@ -255,9 +256,9 @@ diff_labels(ctf_file_t *ifp, ctf_file_t *ofp)
odl.dil_next = 0;
odl.dil_labels = olptr;
- if ((ret = ctf_label_iter(ifp, diff_labels_fill, &idl)) != 0)
+ if ((ret = ctf_label_iter(ifp, ctfdiff_labels_fill, &idl)) != 0)
goto out;
- if ((ret = ctf_label_iter(ofp, diff_labels_fill, &odl)) != 0)
+ if ((ret = ctf_label_iter(ofp, ctfdiff_labels_fill, &odl)) != 0)
goto out;
for (i = 0; i < nilabel; i++) {
@@ -268,7 +269,8 @@ diff_labels(ctf_file_t *ifp, ctf_file_t *ofp)
if (j == nolabel) {
(void) printf("ctf container %s labels differ from ctf "
- "container %s\n", fp_to_name(ifp), fp_to_name(ofp));
+ "container %s\n", ctfdiff_fp_to_name(ifp),
+ ctfdiff_fp_to_name(ofp));
g_different = B_TRUE;
break;
}
@@ -282,7 +284,7 @@ out:
}
static void
-diff_usage(const char *fmt, ...)
+ctfdiff_usage(const char *fmt, ...)
{
if (fmt != NULL) {
va_list ap;
@@ -324,7 +326,7 @@ main(int argc, char *argv[])
g_progname = basename(argv[0]);
- while ((c = getopt(argc, argv, "aqtfolIp:F:O:P:T:")) != -1) {
+ while ((c = getopt(argc, argv, ":aqtfolIp:F:O:P:T:")) != -1) {
switch (c) {
case 'a':
g_flag |= CTF_DIFF_ALL;
@@ -417,6 +419,14 @@ main(int argc, char *argv[])
}
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);
}
}
@@ -427,22 +437,22 @@ main(int argc, char *argv[])
g_flag = CTF_DIFF_DEFAULT;
if (argc != 3) {
- diff_usage(NULL);
+ ctfdiff_usage(NULL);
return (CTFDIFF_EXIT_USAGE);
}
if (g_nexttype != 0 && !(g_flag & CTF_DIFF_TYPES)) {
- diff_usage("-T cannot be used if not diffing types\n");
+ ctfdiff_usage("-T cannot be used if not diffing types\n");
return (CTFDIFF_EXIT_USAGE);
}
if (g_nextfunc != 0 && !(g_flag & CTF_DIFF_FUNCS)) {
- diff_usage("-F cannot be used if not diffing functions\n");
+ ctfdiff_usage("-F cannot be used if not diffing functions\n");
return (CTFDIFF_EXIT_USAGE);
}
if (g_nextobj != 0 && !(g_flag & CTF_DIFF_OBJS)) {
- diff_usage("-O cannot be used if not diffing objects\n");
+ ctfdiff_usage("-O cannot be used if not diffing objects\n");
return (CTFDIFF_EXIT_USAGE);
}
@@ -489,13 +499,13 @@ main(int argc, char *argv[])
err = 0;
if ((g_flag & CTF_DIFF_TYPES) && err != CTF_ERR)
- err = ctf_diff_types(cdp, diff_cb, NULL);
+ err = ctf_diff_types(cdp, ctfdiff_cb, NULL);
if ((g_flag & CTF_DIFF_FUNCS) && err != CTF_ERR)
- err = ctf_diff_functions(cdp, diff_func_cb, NULL);
+ err = ctf_diff_functions(cdp, ctfdiff_func_cb, NULL);
if ((g_flag & CTF_DIFF_OBJS) && err != CTF_ERR)
- err = ctf_diff_objects(cdp, diff_obj_cb, NULL);
+ err = ctf_diff_objects(cdp, ctfdiff_obj_cb, NULL);
if ((g_flag & CTF_DIFF_LABEL) && err != CTF_ERR)
- err = diff_labels(ifp, ofp);
+ err = ctfdiff_labels(ifp, ofp);
ctf_diff_fini(cdp);
if (err == CTF_ERR) {
diff --git a/usr/src/cmd/ctfdump/ctfdump.c b/usr/src/cmd/ctfdump/ctfdump.c
index 327da82d63..d0ff63a02a 100644
--- a/usr/src/cmd/ctfdump/ctfdump.c
+++ b/usr/src/cmd/ctfdump/ctfdump.c
@@ -74,6 +74,8 @@ static ctfdump_stat_t g_stats;
static ctf_id_t *g_fargc;
static int g_nfargc;
+static int g_exit = 0;
+
static const char *ctfdump_fpenc[] = {
NULL,
"SINGLE",
@@ -90,17 +92,6 @@ static const char *ctfdump_fpenc[] = {
"LDIMAGINARY"
};
-static const char *ctfdump_knames[CTF_K_MAX+1] = {
- "unknown", "integer", "float", "pointer",
- "array", "function", "struct", "union",
- "enum", "forward", "typedef", "volatile",
- "const", "restrict", "CTF_K_14", "CTF_K_15",
- "CTF_K_16", "CTF_K_17", "CTF_K_18", "CTF_K_19",
- "CTF_K_20", "CTF_K_21", "CTF_K_22", "CTF_K_23",
- "CTF_K_24", "CTF_K_25", "CTF_K_26", "CTF_K_27",
- "CTF_K_28", "CTF_K_29", "CTF_K_30", "CTF_K_MAX"
-};
-
/*
* When stats are requested, we have to go through everything. To make our lives
* easier, we'll just always allow the code to print everything out, but only
@@ -120,6 +111,17 @@ ctfdump_printf(ctfdump_arg_t arg, const char *fmt, ...)
}
static void
+ctfdump_warn(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+static void
ctfdump_fatal(const char *fmt, ...)
{
va_list ap;
@@ -183,9 +185,11 @@ static void
ctfdump_objects(void)
{
ctfdump_title(CTFDUMP_OBJECTS, "Data Objects");
- if (ctf_object_iter(g_fp, ctfdump_objects_cb, NULL) == CTF_ERR)
- ctfdump_fatal("failed to dump objects: %s\n",
+ if (ctf_object_iter(g_fp, ctfdump_objects_cb, NULL) == CTF_ERR) {
+ ctfdump_warn("failed to dump objects: %s\n",
ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
}
static void
@@ -236,9 +240,11 @@ ctfdump_functions(void)
{
ctfdump_title(CTFDUMP_FUNCTIONS, "Functions");
- if (ctf_function_iter(g_fp, ctfdump_functions_cb, NULL) == CTF_ERR)
- ctfdump_fatal("failed to dump functions: %s\n",
+ if (ctf_function_iter(g_fp, ctfdump_functions_cb, NULL) == CTF_ERR) {
+ ctfdump_warn("failed to dump functions: %s\n",
ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
}
static void
@@ -286,9 +292,11 @@ static void
ctfdump_labels(void)
{
ctfdump_title(CTFDUMP_LABELS, "Label Table");
- if (ctf_label_iter(g_fp, ctfdump_labels_cb, NULL) == CTF_ERR)
- ctfdump_fatal("failed to dump labels: %s\n",
+ if (ctf_label_iter(g_fp, ctfdump_labels_cb, NULL) == CTF_ERR) {
+ ctfdump_warn("failed to dump labels: %s\n",
ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
}
static int
@@ -311,9 +319,11 @@ ctfdump_strings(void)
ulong_t stroff = 0;
ctfdump_title(CTFDUMP_STRINGS, "String Table");
- if (ctf_string_iter(g_fp, ctfdump_strings_cb, &stroff) == CTF_ERR)
- ctfdump_fatal("failed to dump strings: %s\n",
+ if (ctf_string_iter(g_fp, ctfdump_strings_cb, &stroff) == CTF_ERR) {
+ ctfdump_warn("failed to dump strings: %s\n",
ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
}
static void
@@ -504,7 +514,7 @@ ctfdump_types_cb(ctf_id_t id, boolean_t root, void *arg)
ctfdump_fatal("type %lu missing name: %s\n", id,
ctf_errmsg(ctf_errno(g_fp)));
(void) snprintf(name, sizeof (name), "(unknown %s)",
- ctfdump_knames[kind]);
+ ctf_kind_name(g_fp, kind));
}
g_stats.cs_ntypes[kind]++;
@@ -651,9 +661,11 @@ ctfdump_types(void)
{
ctfdump_title(CTFDUMP_TYPES, "Types");
- if (ctf_type_iter(g_fp, B_TRUE, ctfdump_types_cb, NULL) == CTF_ERR)
- ctfdump_fatal("failed to dump labels: %s\n",
+ if (ctf_type_iter(g_fp, B_TRUE, ctfdump_types_cb, NULL) == CTF_ERR) {
+ ctfdump_warn("failed to dump labels: %s\n",
ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
}
static void
@@ -728,11 +740,11 @@ main(int argc, char *argv[])
ufile = optarg;
break;
case '?':
- ctfdump_usage("Option -%c requires an operand\n",
- optopt);
+ ctfdump_usage("Unknown option: -%c\n", optopt);
return (2);
case ':':
- ctfdump_usage("Unknown option: -%c\n", optopt);
+ ctfdump_usage("Option -%c requires an operand\n",
+ optopt);
return (2);
}
}
@@ -799,5 +811,5 @@ main(int argc, char *argv[])
if (g_dump & CTFDUMP_OUTPUT)
ctfdump_output(ufile);
- return (0);
+ return (g_exit);
}
diff --git a/usr/src/cmd/ctfmerge/ctfmerge.c b/usr/src/cmd/ctfmerge/ctfmerge.c
index a64936ccbe..5dea32b3aa 100644
--- a/usr/src/cmd/ctfmerge/ctfmerge.c
+++ b/usr/src/cmd/ctfmerge/ctfmerge.c
@@ -33,6 +33,7 @@
#include <sys/mman.h>
#include <libgen.h>
#include <stdarg.h>
+#include <limits.h>
static char *g_progname;
static char *g_unique;
@@ -40,6 +41,13 @@ static char *g_outfile;
static boolean_t g_req;
static uint_t g_nctf;
+#define CTFMERGE_OK 0
+#define CTFMERGE_FATAL 1
+#define CTFMERGE_USAGE 2
+
+#define CTFMERGE_DEFAULT_NTHREADS 8
+#define CTFMERGE_ALTEXEC "CTFMERGE_ALTEXEC"
+
static void
ctfmerge_fatal(const char *fmt, ...)
{
@@ -53,7 +61,7 @@ ctfmerge_fatal(const char *fmt, ...)
if (g_outfile != NULL)
(void) unlink(g_outfile);
- exit(1);
+ exit(CTFMERGE_FATAL);
}
static boolean_t
@@ -224,10 +232,9 @@ ctfmerge_elfopen(const char *name, Elf *elf, ctf_merge_t *cmh)
name, ctf_errmsg(err));
}
} else {
- if (ctf_merge_add(cmh, fp) != 0) {
+ if ((err = ctf_merge_add(cmh, fp)) != 0) {
ctfmerge_fatal("failed to add input %s: %s\n",
- name, ctf_errmsg(ctf_errno(fp)));
- exit(1);
+ name, ctf_errmsg(err));
}
g_nctf++;
}
@@ -294,10 +301,11 @@ ctfmerge_usage(const char *fmt, ...)
}
(void) fprintf(stderr, "Usage: %s [-gt] [-d uniqfile] [-l label] "
- "[-L labelenv] -o outfile file ...\n"
+ "[-L labelenv] [-j nthrs] -o outfile file ...\n"
"\n"
"\t-d uniquify merged output against uniqfile\n"
"\t-g do not remove source debug information (STABS, DWARF)\n"
+ "\t-j use nthrs threads to perform the merge\n"
"\t-l set output container's label to specified value\n"
"\t-L set output container's label to value from environment\n"
"\t-o file to add CTF data to\n"
@@ -305,22 +313,49 @@ ctfmerge_usage(const char *fmt, ...)
g_progname);
}
+static void
+ctfmerge_altexec(char **argv)
+{
+ const char *alt;
+ char *altexec;
+
+ alt = getenv(CTFMERGE_ALTEXEC);
+ if (alt == NULL || *alt == '\0')
+ return;
+
+ altexec = strdup(alt);
+ if (altexec == NULL)
+ ctfmerge_fatal("failed to allocate memory for altexec\n");
+ if (unsetenv(CTFMERGE_ALTEXEC) != 0)
+ ctfmerge_fatal("failed to unset %s from environment: %s\n",
+ CTFMERGE_ALTEXEC, strerror(errno));
+
+ (void) execv(altexec, argv);
+ ctfmerge_fatal("failed to execute alternate program %s: %s",
+ altexec, strerror(errno));
+}
+
int
main(int argc, char *argv[])
{
int err, i, c, ofd;
+ uint_t nthreads = CTFMERGE_DEFAULT_NTHREADS;
char *tmpfile = NULL, *label = NULL;
int wflags = CTF_ELFWRITE_F_COMPRESS;
ctf_file_t *ofp;
ctf_merge_t *cmh;
+ long argj;
+ char *eptr;
g_progname = basename(argv[0]);
+ ctfmerge_altexec(argv);
+
/*
* We support a subset of the old CTF merge flags, mostly for
* compatability.
*/
- while ((c = getopt(argc, argv, ":d:fgL:o:t")) != -1) {
+ while ((c = getopt(argc, argv, ":d:fgj:L:o:t")) != -1) {
switch (c) {
case 'd':
g_unique = optarg;
@@ -331,6 +366,17 @@ main(int argc, char *argv[])
case 'g':
/* Silently ignored for compatibility */
break;
+ case 'j':
+ errno = 0;
+ argj = strtol(optarg, &eptr, 10);
+ if (errno != 0 || argj == LONG_MAX ||
+ argj == LONG_MIN || argj <= 0 ||
+ argj > UINT_MAX || *eptr != '\0') {
+ ctfmerge_fatal("invalid argument for -j: %s\n",
+ optarg);
+ }
+ nthreads = (uint_t)argj;
+ break;
case 'l':
label = optarg;
break;
@@ -344,18 +390,18 @@ main(int argc, char *argv[])
g_req = B_TRUE;
break;
case ':':
- ctfmerge_usage("ctfmerge: Option -%c requires an "
- "operand\n", optopt);
- return (2);
+ ctfmerge_usage("Option -%c requires an operand\n",
+ optopt);
+ return (CTFMERGE_USAGE);
case '?':
ctfmerge_usage("Unknown option: -%c\n", optopt);
- return (2);
+ return (CTFMERGE_USAGE);
}
}
if (g_outfile == NULL) {
ctfmerge_usage("missing required -o output file\n");
- return (2);
+ return (CTFMERGE_USAGE);
}
(void) elf_version(EV_CURRENT);
@@ -373,7 +419,7 @@ main(int argc, char *argv[])
if (argc < 1) {
ctfmerge_usage("no input files specified");
- return (2);
+ return (CTFMERGE_USAGE);
}
cmh = ctf_merge_init(ofd, &err);
@@ -381,6 +427,10 @@ main(int argc, char *argv[])
ctfmerge_fatal("failed to create merge handle: %s\n",
ctf_errmsg(err));
+ if ((err = ctf_merge_set_nthreads(cmh, nthreads)) != 0)
+ ctfmerge_fatal("failed to set parallelism to %d: %s\n",
+ nthreads, ctf_errmsg(err));
+
for (i = 0; i < argc; i++) {
ctf_file_t *ifp;
int fd;
@@ -449,7 +499,6 @@ main(int argc, char *argv[])
if (ufp == NULL) {
ctfmerge_fatal("failed to open uniquify file %s: %s\n",
g_unique, ctf_errmsg(err));
- return (1);
}
base = basename(g_unique);
@@ -470,18 +519,20 @@ main(int argc, char *argv[])
if (asprintf(&tmpfile, "%s.ctf", g_outfile) == -1)
ctfmerge_fatal("ran out of memory for temporary file name\n");
err = ctf_elfwrite(ofp, g_outfile, tmpfile, wflags);
- free(tmpfile);
if (err == CTF_ERR) {
(void) unlink(tmpfile);
+ free(tmpfile);
ctfmerge_fatal("encountered a libctf error: %s!\n",
ctf_errmsg(ctf_errno(ofp)));
}
if (rename(tmpfile, g_outfile) != 0) {
(void) unlink(tmpfile);
+ free(tmpfile);
ctfmerge_fatal("failed to rename temporary file: %s\n",
strerror(errno));
}
+ free(tmpfile);
- return (0);
+ return (CTFMERGE_OK);
}