diff options
Diffstat (limited to 'usr/src/cmd')
-rw-r--r-- | usr/src/cmd/Makefile | 1 | ||||
-rw-r--r-- | usr/src/cmd/ctfconvert/Makefile | 33 | ||||
-rw-r--r-- | usr/src/cmd/ctfconvert/ctfconvert.c | 389 | ||||
-rw-r--r-- | usr/src/cmd/ctfdiff/ctfdiff.c | 76 | ||||
-rw-r--r-- | usr/src/cmd/ctfdump/ctfdump.c | 64 | ||||
-rw-r--r-- | usr/src/cmd/ctfmerge/ctfmerge.c | 81 |
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); } |