diff options
Diffstat (limited to 'usr/src/cmd')
-rw-r--r-- | usr/src/cmd/ctfdump/Makefile | 5 | ||||
-rw-r--r-- | usr/src/cmd/ctfdump/ctfdump.c | 440 | ||||
-rw-r--r-- | usr/src/cmd/mdb/Makefile.libstandctf | 5 |
3 files changed, 429 insertions, 21 deletions
diff --git a/usr/src/cmd/ctfdump/Makefile b/usr/src/cmd/ctfdump/Makefile index 962ca43a8f..37a1ef4cc8 100644 --- a/usr/src/cmd/ctfdump/Makefile +++ b/usr/src/cmd/ctfdump/Makefile @@ -10,7 +10,7 @@ # # -# Copyright (c) 2015, Joyent, Inc. +# Copyright 2018 Joyent, Inc. # PROG= ctfdump @@ -20,6 +20,9 @@ include ../Makefile.cmd CFLAGS += $(CCVERBOSE) LDLIBS += -lctf +CSTD = $(CSTD_GNU99) +C99LMODE = -Xc99=%all + .KEEP_STATE: all: $(PROG) diff --git a/usr/src/cmd/ctfdump/ctfdump.c b/usr/src/cmd/ctfdump/ctfdump.c index d0ff63a02a..19245db8b4 100644 --- a/usr/src/cmd/ctfdump/ctfdump.c +++ b/usr/src/cmd/ctfdump/ctfdump.c @@ -10,7 +10,7 @@ */ /* - * Copyright (c) 2015, Joyent, Inc. + * Copyright (c) 2018, Joyent, Inc. */ /* @@ -29,21 +29,25 @@ #include <sys/sysmacros.h> #include <sys/types.h> #include <sys/stat.h> +#include <sys/note.h> #include <fcntl.h> #include <errno.h> #include <string.h> +#include <strings.h> + +#define MAX_NAMELEN (512) typedef enum ctfdump_arg { - CTFDUMP_OBJECTS = 0x01, - CTFDUMP_FUNCTIONS = 0x02, - CTFDUMP_HEADER = 0x04, - CTFDUMP_LABELS = 0x08, - CTFDUMP_STRINGS = 0x10, - CTFDUMP_STATS = 0x20, - CTFDUMP_TYPES = 0x40, - CTFDUMP_DEFAULT = 0x7f, - CTFDUMP_OUTPUT = 0x80, - CTFDUMP_ALL = 0xff + CTFDUMP_OBJECTS = 0x001, + CTFDUMP_FUNCTIONS = 0x002, + CTFDUMP_HEADER = 0x004, + CTFDUMP_LABELS = 0x008, + CTFDUMP_STRINGS = 0x010, + CTFDUMP_STATS = 0x020, + CTFDUMP_TYPES = 0x040, + CTFDUMP_DEFAULT = 0x07f, + CTFDUMP_OUTPUT = 0x080, + CTFDUMP_SOURCE = 0x100, } ctfdump_arg_t; typedef struct ctfdump_stat { @@ -67,6 +71,14 @@ typedef struct ctfdump_stat { ulong_t cs_strmax; /* longest string */ } ctfdump_stat_t; +typedef struct { + char ci_name[MAX_NAMELEN]; + ctf_id_t ci_id; + ulong_t ci_symidx; + ctf_funcinfo_t ci_funcinfo; +} ctf_idname_t; + +static ctf_idname_t *idnames; static const char *g_progname; static ctfdump_arg_t g_dump; static ctf_file_t *g_fp; @@ -106,7 +118,7 @@ ctfdump_printf(ctfdump_arg_t arg, const char *fmt, ...) return; va_start(ap, fmt); - vfprintf(stdout, fmt, ap); + (void) vfprintf(stdout, fmt, ap); va_end(ap); } @@ -117,7 +129,7 @@ ctfdump_warn(const char *fmt, ...) (void) fprintf(stderr, "%s: ", g_progname); va_start(ap, fmt); - vfprintf(stderr, fmt, ap); + (void) vfprintf(stderr, fmt, ap); va_end(ap); } @@ -128,7 +140,7 @@ ctfdump_fatal(const char *fmt, ...) (void) fprintf(stderr, "%s: ", g_progname); va_start(ap, fmt); - vfprintf(stderr, fmt, ap); + (void) vfprintf(stderr, fmt, ap); va_end(ap); exit(1); @@ -145,9 +157,10 @@ ctfdump_usage(const char *fmt, ...) va_end(ap); } - (void) fprintf(stderr, "Usage: %s [-dfhlsSt] [-p parent] [-u outfile] " + (void) fprintf(stderr, "Usage: %s [-cdfhlsSt] [-p parent] [-u outfile] " "file\n" "\n" + "\t-c dump C-style output\n" "\t-d dump object data\n" "\t-f dump function data\n" "\t-h dump the CTF header\n" @@ -172,6 +185,8 @@ ctfdump_title(ctfdump_arg_t arg, const char *header) static int ctfdump_objects_cb(const char *name, ctf_id_t id, ulong_t symidx, void *arg) { + _NOTE(ARGUNUSED(arg)); + int len; len = snprintf(NULL, 0, " [%u] %u", g_stats.cs_ndata, id); @@ -208,6 +223,7 @@ static int ctfdump_functions_cb(const char *name, ulong_t symidx, ctf_funcinfo_t *ctc, void *arg) { + _NOTE(ARGUNUSED(arg)); int i; if (ctc->ctc_argc != 0) { @@ -284,6 +300,7 @@ ctfdump_header(void) static int ctfdump_labels_cb(const char *name, const ctf_lblinfo_t *li, void *arg) { + _NOTE(ARGUNUSED(arg)); ctfdump_printf(CTFDUMP_LABELS, " %5lu %s\n", li->ctb_typeidx, name); return (0); } @@ -496,9 +513,10 @@ ctfdump_enum_cb(const char *name, int value, void *arg) static int ctfdump_types_cb(ctf_id_t id, boolean_t root, void *arg) { + _NOTE(ARGUNUSED(arg)); int kind, i, count; ctf_id_t ref; - char name[512], ienc[128]; + char name[MAX_NAMELEN], ienc[128]; const char *encn; ctf_funcinfo_t ctc; ctf_arinfo_t ar; @@ -662,12 +680,381 @@ ctfdump_types(void) ctfdump_title(CTFDUMP_TYPES, "Types"); if (ctf_type_iter(g_fp, B_TRUE, ctfdump_types_cb, NULL) == CTF_ERR) { - ctfdump_warn("failed to dump labels: %s\n", + ctfdump_warn("failed to dump types: %s\n", ctf_errmsg(ctf_errno(g_fp))); g_exit = 1; } } +/* + * C-style output. This is designed mainly for comparison purposes, and doesn't + * produce directly valid C: + * + * - the declarations are sorted alphabetically not semantically + * - anonymous enums without other users are elided (e.g. IDCS_PROBE_SENT) + * - doubly-pointed-to functions are wrong (e.g. in kiconv_ops_t) + * - anon unions declared within SOUs aren't expanded + * - function arguments aren't expanded recursively + */ + +static void +ctfsrc_refname(ctf_id_t id, char *buf, size_t bufsize) +{ + ctf_id_t ref; + + if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR) { + ctfdump_fatal("failed to get reference type for %ld: " + "%s\n", id, ctf_errmsg(ctf_errno(g_fp))); + } + + (void) ctf_type_name(g_fp, ref, buf, bufsize); +} + +static int +ctfsrc_member_cb(const char *member, ctf_id_t type, ulong_t off, void *arg) +{ + _NOTE(ARGUNUSED(arg)); + char name[MAX_NAMELEN]; + + if (ctf_type_cname(g_fp, type, name, sizeof (name), member) == NULL) { + if (ctf_errno(g_fp) != ECTF_NOPARENT) { + ctfdump_fatal("type %lu missing name: %s\n", type, + ctf_errmsg(ctf_errno(g_fp))); + } + + (void) snprintf(name, sizeof (name), "unknown_t %s", member); + } + + /* + * A byte offset is friendlier, but we'll print bits too if it's not + * aligned (i.e. a bitfield). + */ + if (off % NBBY != 0) { + (void) printf("\t%s; /* offset: %lu bytes (%lu bits) */\n", + name, off / NBBY, off); + } else { + (void) printf("\t%s; /* offset: %lu bytes */\n", + name, off / NBBY); + } + return (0); +} + +static int +ctfsrc_enum_cb(const char *name, int value, void *arg) +{ + _NOTE(ARGUNUSED(arg)); + (void) printf("\t%s = %d,\n", name, value); + return (0); +} + +static int +is_anon_refname(const char *refname) +{ + return ((strcmp(refname, "struct ") == 0 || + strcmp(refname, "union ") == 0 || + strcmp(refname, "enum ") == 0)); +} + +static int +ctfsrc_collect_types_cb(ctf_id_t id, boolean_t root, void *arg) +{ + _NOTE(ARGUNUSED(root, arg)); + (void) ctf_type_name(g_fp, id, idnames[id].ci_name, + sizeof (idnames[id].ci_name)); + idnames[id].ci_id = id; + return (0); +} + +static void +ctfsrc_type(ctf_id_t id, const char *name) +{ + char refname[MAX_NAMELEN]; + ctf_id_t ref; + ssize_t size; + int kind; + + if ((kind = ctf_type_kind(g_fp, id)) == CTF_ERR) { + ctfdump_fatal("encountered malformed ctf, type %s does not " + "have a kind: %s\n", name, ctf_errmsg(ctf_errno(g_fp))); + } + + switch (kind) { + case CTF_K_STRUCT: + case CTF_K_UNION: + /* + * Delay printing anonymous SOUs; a later typedef will usually + * pick them up. + */ + if (is_anon_refname(name)) + break; + + if ((size = ctf_type_size(g_fp, id)) == CTF_ERR) { + ctfdump_fatal("failed to get size of %s: %s\n", name, + ctf_errmsg(ctf_errno(g_fp))); + } + + (void) printf("%s { /* 0x%x bytes */\n", name, size); + + if (ctf_member_iter(g_fp, id, ctfsrc_member_cb, NULL) != 0) { + ctfdump_fatal("failed to iterate members of %s: %s\n", + name, ctf_errmsg(ctf_errno(g_fp))); + } + + (void) printf("};\n\n"); + break; + case CTF_K_ENUM: + /* + * This will throw away any anon enum that isn't followed by a + * typedef... + */ + if (is_anon_refname(name)) + break; + + (void) printf("%s {\n", name); + + if (ctf_enum_iter(g_fp, id, ctfsrc_enum_cb, NULL) != 0) { + ctfdump_fatal("failed to iterate enumerators of %s: " + "%s\n", name, ctf_errmsg(ctf_errno(g_fp))); + } + + (void) printf("};\n\n"); + break; + case CTF_K_TYPEDEF: + ctfsrc_refname(id, refname, sizeof (refname)); + + if (!is_anon_refname(refname)) { + (void) ctf_type_cname(g_fp, + ctf_type_reference(g_fp, id), refname, + sizeof (refname), name); + + (void) printf("typedef %s;\n\n", refname); + break; + } + + ref = ctf_type_reference(g_fp, id); + + if (ctf_type_kind(g_fp, ref) == CTF_K_ENUM) { + (void) printf("typedef enum {\n"); + + if (ctf_enum_iter(g_fp, ref, + ctfsrc_enum_cb, NULL) != 0) { + ctfdump_fatal("failed to iterate enumerators " + "of %s: %s\n", refname, + ctf_errmsg(ctf_errno(g_fp))); + } + + (void) printf("} %s;\n\n", name); + } else { + if ((size = ctf_type_size(g_fp, ref)) == CTF_ERR) { + ctfdump_fatal("failed to get size of %s: %s\n", + refname, ctf_errmsg(ctf_errno(g_fp))); + } + + (void) printf("typedef %s{ /* 0x%x bytes */\n", + refname, size); + + if (ctf_member_iter(g_fp, ref, + ctfsrc_member_cb, NULL) != 0) { + ctfdump_fatal("failed to iterate members " + "of %s: %s\n", refname, + ctf_errmsg(ctf_errno(g_fp))); + } + + (void) printf("} %s;\n\n", name); + } + + break; + case CTF_K_FORWARD: + (void) printf("%s;\n\n", name); + break; + case CTF_K_UNKNOWN: + case CTF_K_INTEGER: + case CTF_K_FLOAT: + case CTF_K_POINTER: + case CTF_K_ARRAY: + case CTF_K_FUNCTION: + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + break; + default: + ctfdump_fatal("encountered unknown kind for type %s: %d\n", + name, kind); + break; + } +} + +static int +ctfsrc_collect_objects_cb(const char *name, ctf_id_t id, + ulong_t symidx, void *arg) +{ + size_t *count = arg; + + /* local static vars can have an unknown ID */ + if (id == 0) + return (0); + + (void) strlcpy(idnames[*count].ci_name, name, + sizeof (idnames[*count].ci_name)); + idnames[*count].ci_id = id; + idnames[*count].ci_symidx = symidx; + *count = *count + 1; + return (0); +} + +static void +ctfsrc_object(ctf_id_t id, const char *name) +{ + char tname[MAX_NAMELEN]; + + if (ctf_type_cname(g_fp, id, tname, sizeof (tname), name) == NULL) { + if (ctf_errno(g_fp) != ECTF_NOPARENT) { + ctfdump_fatal("type %ld missing name: %s\n", id, + ctf_errmsg(ctf_errno(g_fp))); + } + (void) snprintf(tname, sizeof (tname), "unknown_t %s", name); + } + + (void) printf("extern %s;\n", tname); +} + +static int +ctfsrc_collect_functions_cb(const char *name, ulong_t symidx, + ctf_funcinfo_t *ctc, void *arg) +{ + size_t *count = arg; + + (void) strlcpy(idnames[*count].ci_name, name, + sizeof (idnames[*count].ci_name)); + bcopy(ctc, &idnames[*count].ci_funcinfo, sizeof (*ctc)); + idnames[*count].ci_id = 0; + idnames[*count].ci_symidx = symidx; + *count = *count + 1; + return (0); +} + +static void +ctfsrc_function(ctf_idname_t *idn) +{ + ctf_funcinfo_t *cfi = &idn->ci_funcinfo; + char name[MAX_NAMELEN] = "unknown_t"; + + (void) ctf_type_name(g_fp, cfi->ctc_return, name, sizeof (name)); + + (void) printf("extern %s %s(", name, idn->ci_name); + + if (cfi->ctc_argc != 0) { + ctfdump_fargs_grow(cfi->ctc_argc); + if (ctf_func_args(g_fp, idn->ci_symidx, + g_nfargc, g_fargc) == CTF_ERR) { + ctfdump_fatal("failed to get arguments for function " + "%s: %s\n", idn->ci_name, + ctf_errmsg(ctf_errno(g_fp))); + } + + for (size_t i = 0; i < cfi->ctc_argc; i++) { + ctf_id_t aid = g_fargc[i]; + + name[0] = '\0'; + + (void) ctf_type_name(g_fp, aid, name, sizeof (name)); + + (void) printf("%s%s", name, + i + 1 == cfi->ctc_argc ? "" : ", "); + } + } else { + if (!(cfi->ctc_flags & CTF_FUNC_VARARG)) + (void) printf("void"); + } + + if (cfi->ctc_flags & CTF_FUNC_VARARG) + (void) printf("%s...", cfi->ctc_argc == 0 ? "" : ", "); + + (void) printf(");\n"); +} + +static int +idname_compare(const void *lhs, const void *rhs) +{ + return (strcmp(((ctf_idname_t *)lhs)->ci_name, + ((ctf_idname_t *)rhs)->ci_name)); +} + +static void +ctfdump_source(void) +{ + ulong_t nr_syms = ctf_nr_syms(g_fp); + ctf_id_t max_id = ctf_max_id(g_fp); + size_t count = 0; + + (void) printf("/* Types */\n\n"); + + if ((idnames = calloc(max_id + 1, sizeof (idnames[0]))) == NULL) { + ctfdump_fatal("failed to alloc idnames: %s\n", + strerror(errno)); + } + + if (ctf_type_iter(g_fp, B_FALSE, ctfsrc_collect_types_cb, + idnames) == CTF_ERR) { + ctfdump_warn("failed to collect types: %s\n", + ctf_errmsg(ctf_errno(g_fp))); + g_exit = 1; + } + + qsort(idnames, max_id, sizeof (ctf_idname_t), idname_compare); + + for (size_t i = 0; i < max_id; i++) { + if (idnames[i].ci_id != 0) + ctfsrc_type(idnames[i].ci_id, idnames[i].ci_name); + } + + free(idnames); + + (void) printf("\n\n/* Data Objects */\n\n"); + + if ((idnames = calloc(nr_syms, sizeof (idnames[0]))) == NULL) { + ctfdump_fatal("failed to alloc idnames: %s\n", + strerror(errno)); + } + + if (ctf_object_iter(g_fp, ctfsrc_collect_objects_cb, + &count) == CTF_ERR) { + ctfdump_warn("failed to collect objects: %s\n", + ctf_errmsg(ctf_errno(g_fp))); + g_exit = 1; + } + + qsort(idnames, count, sizeof (ctf_idname_t), idname_compare); + + for (size_t i = 0; i < count; i++) + ctfsrc_object(idnames[i].ci_id, idnames[i].ci_name); + + free(idnames); + + (void) printf("\n\n/* Functions */\n\n"); + + if ((idnames = calloc(nr_syms, sizeof (idnames[0]))) == NULL) { + ctfdump_fatal("failed to alloc idnames: %s\n", + strerror(errno)); + } + + count = 0; + + if (ctf_function_iter(g_fp, ctfsrc_collect_functions_cb, + &count) == CTF_ERR) { + ctfdump_warn("failed to collect functions: %s\n", + ctf_errmsg(ctf_errno(g_fp))); + g_exit = 1; + } + + qsort(idnames, count, sizeof (ctf_idname_t), idname_compare); + + for (size_t i = 0; i < count; i++) + ctfsrc_function(&idnames[i]); + + free(idnames); +} + static void ctfdump_output(const char *out) { @@ -689,7 +1076,7 @@ ctfdump_output(const char *out) else if (ret == -1) ctfdump_fatal("failed to write to %s: %s\n", out, strerror(errno)); - data += ret; + data = ((char *)data) + ret; len -= ret; } @@ -709,8 +1096,11 @@ main(int argc, char *argv[]) const char *ufile = NULL, *parent = NULL; g_progname = basename(argv[0]); - while ((c = getopt(argc, argv, ":dfhlp:sStu:")) != -1) { + while ((c = getopt(argc, argv, ":cdfhlp:sStu:")) != -1) { switch (c) { + case 'c': + g_dump |= CTFDUMP_SOURCE; + break; case 'd': g_dump |= CTFDUMP_OBJECTS; break; @@ -752,8 +1142,13 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; + if ((g_dump & CTFDUMP_SOURCE) && !!(g_dump & ~CTFDUMP_SOURCE)) { + ctfdump_usage("-c must be specified on its own\n"); + return (2); + } + /* - * Dump all information by default. + * Dump all information except C source by default. */ if (g_dump == 0) g_dump = CTFDUMP_DEFAULT; @@ -783,6 +1178,11 @@ main(int argc, char *argv[]) parent, ctf_errmsg(ctf_errno(g_fp))); } + if (g_dump & CTFDUMP_SOURCE) { + ctfdump_source(); + return (0); + } + /* * If stats is set, we must run through everything exect CTFDUMP_OUTPUT. * We also do CTFDUMP_STATS last as a result. diff --git a/usr/src/cmd/mdb/Makefile.libstandctf b/usr/src/cmd/mdb/Makefile.libstandctf index caa621be0e..176291399c 100644 --- a/usr/src/cmd/mdb/Makefile.libstandctf +++ b/usr/src/cmd/mdb/Makefile.libstandctf @@ -22,6 +22,8 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2018 Joyent, Inc. +# .KEEP_STATE: @@ -45,6 +47,9 @@ $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG CPPFLAGS += -I$(SRC)/common/ctf -I../../../common -DCTF_OLD_VERSIONS -D_MDB \ -Dvsnprintf=ctf_vsnprintf -Dassfail=kmdb_prom_assfail +CSTD = $(CSTD_GNU99) +C99LMODE = -Xc99=%all + # # kmdb is a kernel module, so we'll use the kernel's build flags. CFLAGS64 += $(STAND_FLAGS_64) |