summaryrefslogtreecommitdiff
path: root/usr/src/cmd/ctfdump/ctfdump.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/ctfdump/ctfdump.c')
-rw-r--r--usr/src/cmd/ctfdump/ctfdump.c803
1 files changed, 803 insertions, 0 deletions
diff --git a/usr/src/cmd/ctfdump/ctfdump.c b/usr/src/cmd/ctfdump/ctfdump.c
new file mode 100644
index 0000000000..327da82d63
--- /dev/null
+++ b/usr/src/cmd/ctfdump/ctfdump.c
@@ -0,0 +1,803 @@
+/*
+ * 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.
+ */
+
+/*
+ * Dump information about CTF containers. This was inspired by the original
+ * ctfdump written in tools/ctf, but this has been reimplemented in terms of
+ * libctf.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <libctf.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+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_arg_t;
+
+typedef struct ctfdump_stat {
+ ulong_t cs_ndata; /* number of data objects */
+ ulong_t cs_nfuncs; /* number of functions */
+ ulong_t cs_nfuncargs; /* number of function args */
+ ulong_t cs_nfuncmax; /* largest number of args */
+ ulong_t cs_ntypes[CTF_K_MAX]; /* number of types */
+ ulong_t cs_nsmembs; /* number of struct members */
+ ulong_t cs_nsmax; /* largest number of members */
+ ulong_t cs_structsz; /* sum of structures sizes */
+ ulong_t cs_sszmax; /* largest structure */
+ ulong_t cs_numembs; /* number of union members */
+ ulong_t cs_numax; /* largest number of members */
+ ulong_t cs_unionsz; /* sum of unions sizes */
+ ulong_t cs_uszmax; /* largest union */
+ ulong_t cs_nemembs; /* number of enum members */
+ ulong_t cs_nemax; /* largest number of members */
+ ulong_t cs_nstrings; /* number of strings */
+ ulong_t cs_strsz; /* string size */
+ ulong_t cs_strmax; /* longest string */
+} ctfdump_stat_t;
+
+static const char *g_progname;
+static ctfdump_arg_t g_dump;
+static ctf_file_t *g_fp;
+static ctfdump_stat_t g_stats;
+static ctf_id_t *g_fargc;
+static int g_nfargc;
+
+static const char *ctfdump_fpenc[] = {
+ NULL,
+ "SINGLE",
+ "DOUBLE",
+ "COMPLEX",
+ "DCOMPLEX",
+ "LDCOMPLEX",
+ "LDOUBLE",
+ "INTERVAL",
+ "DINTERVAL",
+ "LDINTERVAL",
+ "IMAGINARY",
+ "DIMAGINARY",
+ "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
+ * output it if we have actually enabled that section.
+ */
+static void
+ctfdump_printf(ctfdump_arg_t arg, const char *fmt, ...)
+{
+ va_list ap;
+
+ if ((arg & g_dump) == 0)
+ return;
+
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+}
+
+static void
+ctfdump_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+static void
+ctfdump_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 [-dfhlsSt] [-p parent] [-u outfile] "
+ "file\n"
+ "\n"
+ "\t-d dump object data\n"
+ "\t-f dump function data\n"
+ "\t-h dump the CTF header\n"
+ "\t-l dump the label table\n"
+ "\t-p use parent to supply additional information\n"
+ "\t-s dump the string table\n"
+ "\t-S dump statistics about the CTF container\n"
+ "\t-t dump type information\n"
+ "\t-u dump uncompressed CTF data to outfile\n",
+ g_progname);
+}
+
+static void
+ctfdump_title(ctfdump_arg_t arg, const char *header)
+{
+ static const char line[] = "----------------------------------------"
+ "----------------------------------------";
+ ctfdump_printf(arg, "\n- %s %.*s\n\n", header, (int)78 - strlen(header),
+ line);
+}
+
+static int
+ctfdump_objects_cb(const char *name, ctf_id_t id, ulong_t symidx, void *arg)
+{
+ int len;
+
+ len = snprintf(NULL, 0, " [%u] %u", g_stats.cs_ndata, id);
+ ctfdump_printf(CTFDUMP_OBJECTS, " [%u] %u %*s%s (%u)\n",
+ g_stats.cs_ndata, id, MAX(15 - len, 0), "", name, symidx);
+ g_stats.cs_ndata++;
+ return (0);
+}
+
+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",
+ ctf_errmsg(ctf_errno(g_fp)));
+}
+
+static void
+ctfdump_fargs_grow(int nargs)
+{
+ if (g_nfargc < nargs) {
+ g_fargc = realloc(g_fargc, sizeof (ctf_id_t) * nargs);
+ if (g_fargc == NULL)
+ ctfdump_fatal("failed to get memory for %d "
+ "ctf_id_t's\n", nargs);
+ g_nfargc = nargs;
+ }
+}
+
+static int
+ctfdump_functions_cb(const char *name, ulong_t symidx, ctf_funcinfo_t *ctc,
+ void *arg)
+{
+ int i;
+
+ if (ctc->ctc_argc != 0) {
+ ctfdump_fargs_grow(ctc->ctc_argc);
+ if (ctf_func_args(g_fp, symidx, g_nfargc, g_fargc) == CTF_ERR)
+ ctfdump_fatal("failed to get arguments for function "
+ "%s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ ctfdump_printf(CTFDUMP_FUNCTIONS,
+ " [%lu] %s (%lu) returns: %u args: (", g_stats.cs_nfuncs, name,
+ symidx, ctc->ctc_return);
+ for (i = 0; i < ctc->ctc_argc; i++)
+ ctfdump_printf(CTFDUMP_FUNCTIONS, "%lu%s", g_fargc[i],
+ i + 1 == ctc->ctc_argc ? "" : ", ");
+ if (ctc->ctc_flags & CTF_FUNC_VARARG)
+ ctfdump_printf(CTFDUMP_FUNCTIONS, "%s...",
+ ctc->ctc_argc == 0 ? "" : ", ");
+ ctfdump_printf(CTFDUMP_FUNCTIONS, ")\n");
+
+ g_stats.cs_nfuncs++;
+ g_stats.cs_nfuncargs += ctc->ctc_argc;
+ g_stats.cs_nfuncmax = MAX(ctc->ctc_argc, g_stats.cs_nfuncmax);
+
+ return (0);
+}
+
+static void
+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",
+ ctf_errmsg(ctf_errno(g_fp)));
+}
+
+static void
+ctfdump_header(void)
+{
+ const ctf_header_t *hp;
+ const char *parname, *parlabel;
+
+ ctfdump_title(CTFDUMP_HEADER, "CTF Header");
+ ctf_dataptr(g_fp, (const void **)&hp, NULL);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_magic = 0x%04x\n",
+ hp->cth_magic);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_version = %u\n",
+ hp->cth_version);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_flags = 0x%02x\n",
+ ctf_flags(g_fp));
+ parname = ctf_parent_name(g_fp);
+ parlabel = ctf_parent_label(g_fp);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_parlabel = %s\n",
+ parlabel == NULL ? "(anon)" : parlabel);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_parname = %s\n",
+ parname == NULL ? "(anon)" : parname);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_lbloff = %u\n",
+ hp->cth_lbloff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_objtoff = %u\n",
+ hp->cth_objtoff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_funcoff = %u\n",
+ hp->cth_funcoff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_typeoff = %u\n",
+ hp->cth_typeoff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_stroff = %u\n",
+ hp->cth_stroff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_strlen = %u\n",
+ hp->cth_strlen);
+}
+
+static int
+ctfdump_labels_cb(const char *name, const ctf_lblinfo_t *li, void *arg)
+{
+ ctfdump_printf(CTFDUMP_LABELS, " %5lu %s\n", li->ctb_typeidx, name);
+ return (0);
+}
+
+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",
+ ctf_errmsg(ctf_errno(g_fp)));
+}
+
+static int
+ctfdump_strings_cb(const char *s, void *arg)
+{
+ size_t len = strlen(s) + 1;
+ ulong_t *stroff = arg;
+ ctfdump_printf(CTFDUMP_STRINGS, " [%lu] %s\n", *stroff,
+ *s == '\0' ? "\\0" : s);
+ *stroff = *stroff + len;
+ g_stats.cs_nstrings++;
+ g_stats.cs_strsz += len;
+ g_stats.cs_strmax = MAX(g_stats.cs_strmax, len);
+ return (0);
+}
+
+static void
+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",
+ ctf_errmsg(ctf_errno(g_fp)));
+}
+
+static void
+ctfdump_stat_int(const char *name, ulong_t value)
+{
+ ctfdump_printf(CTFDUMP_STATS, " %-36s= %lu\n", name, value);
+}
+
+static void
+ctfdump_stat_fp(const char *name, float value)
+{
+ ctfdump_printf(CTFDUMP_STATS, " %-36s= %.2f\n", name, value);
+}
+
+static void
+ctfdump_stats(void)
+{
+ int i;
+ ulong_t sum;
+
+ ctfdump_title(CTFDUMP_STATS, "CTF Statistics");
+
+ ctfdump_stat_int("total number of data objects", g_stats.cs_ndata);
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+ ctfdump_stat_int("total number of functions", g_stats.cs_nfuncs);
+ ctfdump_stat_int("total number of function arguments",
+ g_stats.cs_nfuncargs);
+ ctfdump_stat_int("maximum argument list length", g_stats.cs_nfuncmax);
+ if (g_stats.cs_nfuncs != 0)
+ ctfdump_stat_fp("average argument list length",
+ (float)g_stats.cs_nfuncargs / (float)g_stats.cs_nfuncs);
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+
+ sum = 0;
+ for (i = 0; i < CTF_K_MAX; i++)
+ sum += g_stats.cs_ntypes[i];
+ ctfdump_stat_int("total number of types", sum);
+ ctfdump_stat_int("total number of integers",
+ g_stats.cs_ntypes[CTF_K_INTEGER]);
+ ctfdump_stat_int("total number of floats",
+ g_stats.cs_ntypes[CTF_K_FLOAT]);
+ ctfdump_stat_int("total number of pointers",
+ g_stats.cs_ntypes[CTF_K_POINTER]);
+ ctfdump_stat_int("total number of arrays",
+ g_stats.cs_ntypes[CTF_K_ARRAY]);
+ ctfdump_stat_int("total number of func types",
+ g_stats.cs_ntypes[CTF_K_FUNCTION]);
+ ctfdump_stat_int("total number of structs",
+ g_stats.cs_ntypes[CTF_K_STRUCT]);
+ ctfdump_stat_int("total number of unions",
+ g_stats.cs_ntypes[CTF_K_UNION]);
+ ctfdump_stat_int("total number of enums",
+ g_stats.cs_ntypes[CTF_K_ENUM]);
+ ctfdump_stat_int("total number of forward tags",
+ g_stats.cs_ntypes[CTF_K_FORWARD]);
+ ctfdump_stat_int("total number of typedefs",
+ g_stats.cs_ntypes[CTF_K_TYPEDEF]);
+ ctfdump_stat_int("total number of volatile types",
+ g_stats.cs_ntypes[CTF_K_VOLATILE]);
+ ctfdump_stat_int("total number of const types",
+ g_stats.cs_ntypes[CTF_K_CONST]);
+ ctfdump_stat_int("total number of restrict types",
+ g_stats.cs_ntypes[CTF_K_RESTRICT]);
+ ctfdump_stat_int("total number of unknowns (holes)",
+ g_stats.cs_ntypes[CTF_K_UNKNOWN]);
+
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+ ctfdump_stat_int("total number of struct members", g_stats.cs_nsmembs);
+ ctfdump_stat_int("maximum number of struct members", g_stats.cs_nsmax);
+ ctfdump_stat_int("total size of all structs", g_stats.cs_structsz);
+ ctfdump_stat_int("maximum size of a struct", g_stats.cs_sszmax);
+ if (g_stats.cs_ntypes[CTF_K_STRUCT] != 0) {
+ ctfdump_stat_fp("average number of struct members",
+ (float)g_stats.cs_nsmembs /
+ (float)g_stats.cs_ntypes[CTF_K_STRUCT]);
+ ctfdump_stat_fp("average size of a struct",
+ (float)g_stats.cs_structsz /
+ (float)g_stats.cs_ntypes[CTF_K_STRUCT]);
+ }
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+ ctfdump_stat_int("total number of union members", g_stats.cs_numembs);
+ ctfdump_stat_int("maximum number of union members", g_stats.cs_numax);
+ ctfdump_stat_int("total size of all unions", g_stats.cs_unionsz);
+ ctfdump_stat_int("maximum size of a union", g_stats.cs_uszmax);
+ if (g_stats.cs_ntypes[CTF_K_UNION] != 0) {
+ ctfdump_stat_fp("average number of union members",
+ (float)g_stats.cs_numembs /
+ (float)g_stats.cs_ntypes[CTF_K_UNION]);
+ ctfdump_stat_fp("average size of a union",
+ (float)g_stats.cs_unionsz /
+ (float)g_stats.cs_ntypes[CTF_K_UNION]);
+ }
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+
+ ctfdump_stat_int("total number of enum members", g_stats.cs_nemembs);
+ ctfdump_stat_int("maximum number of enum members", g_stats.cs_nemax);
+ if (g_stats.cs_ntypes[CTF_K_ENUM] != 0) {
+ ctfdump_stat_fp("average number of enum members",
+ (float)g_stats.cs_nemembs /
+ (float)g_stats.cs_ntypes[CTF_K_ENUM]);
+ }
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+
+ ctfdump_stat_int("total number of strings", g_stats.cs_nstrings);
+ ctfdump_stat_int("bytes of string data", g_stats.cs_strsz);
+ ctfdump_stat_int("maximum string length", g_stats.cs_strmax);
+ if (g_stats.cs_nstrings != 0)
+ ctfdump_stat_fp("average string length",
+ (float)g_stats.cs_strsz / (float)g_stats.cs_nstrings);
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+}
+
+static void
+ctfdump_intenc_name(ctf_encoding_t *cte, char *buf, int len)
+{
+ int off = 0;
+ boolean_t space = B_FALSE;
+
+ if (cte->cte_format == 0 || (cte->cte_format &
+ ~(CTF_INT_SIGNED | CTF_INT_CHAR | CTF_INT_BOOL |
+ CTF_INT_VARARGS)) != 0) {
+ (void) snprintf(buf, len, "0x%x", cte->cte_format);
+ return;
+ }
+
+ if (cte->cte_format & CTF_INT_SIGNED) {
+ off += snprintf(buf + off, MAX(len - off, 0), "%sSIGNED",
+ space == B_TRUE ? " " : "");
+ space = B_TRUE;
+ }
+
+ if (cte->cte_format & CTF_INT_CHAR) {
+ off += snprintf(buf + off, MAX(len - off, 0), "%sCHAR",
+ space == B_TRUE ? " " : "");
+ space = B_TRUE;
+ }
+
+ if (cte->cte_format & CTF_INT_BOOL) {
+ off += snprintf(buf + off, MAX(len - off, 0), "%sBOOL",
+ space == B_TRUE ? " " : "");
+ space = B_TRUE;
+ }
+
+ if (cte->cte_format & CTF_INT_VARARGS) {
+ off += snprintf(buf + off, MAX(len - off, 0), "%sVARARGS",
+ space == B_TRUE ? " " : "");
+ space = B_TRUE;
+ }
+}
+
+static int
+ctfdump_member_cb(const char *member, ctf_id_t type, ulong_t off, void *arg)
+{
+ int *count = arg;
+ ctfdump_printf(CTFDUMP_TYPES, "\t%s type=%lu off=%lu\n", member, type,
+ off);
+ *count = *count + 1;
+ return (0);
+}
+
+static int
+ctfdump_enum_cb(const char *name, int value, void *arg)
+{
+ int *count = arg;
+ ctfdump_printf(CTFDUMP_TYPES, "\t%s = %d\n", name, value);
+ *count = *count + 1;
+ return (0);
+}
+
+static int
+ctfdump_types_cb(ctf_id_t id, boolean_t root, void *arg)
+{
+ int kind, i, count;
+ ctf_id_t ref;
+ char name[512], ienc[128];
+ const char *encn;
+ ctf_funcinfo_t ctc;
+ ctf_arinfo_t ar;
+ ctf_encoding_t cte;
+ ssize_t size;
+
+ 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)));
+
+ if (ctf_type_name(g_fp, id, name, sizeof (name)) == NULL) {
+ if (ctf_errno(g_fp) != ECTF_NOPARENT)
+ 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]);
+ }
+
+ g_stats.cs_ntypes[kind]++;
+ if (root == B_TRUE)
+ ctfdump_printf(CTFDUMP_TYPES, " <%lu> ", id);
+ else
+ ctfdump_printf(CTFDUMP_TYPES, " [%lu] ", id);
+
+ switch (kind) {
+ case CTF_K_UNKNOWN:
+ break;
+ case CTF_K_INTEGER:
+ if (ctf_type_encoding(g_fp, id, &cte) == CTF_ERR)
+ ctfdump_fatal("failed to get encoding information "
+ "for %s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_intenc_name(&cte, ienc, sizeof (ienc));
+ ctfdump_printf(CTFDUMP_TYPES,
+ "%s encoding=%s offset=%u bits=%u",
+ name, ienc, cte.cte_offset, cte.cte_bits);
+ break;
+ case CTF_K_FLOAT:
+ if (ctf_type_encoding(g_fp, id, &cte) == CTF_ERR)
+ ctfdump_fatal("failed to get encoding information "
+ "for %s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ if (cte.cte_format < 1 || cte.cte_format > 12)
+ encn = "unknown";
+ else
+ encn = ctfdump_fpenc[cte.cte_format];
+ ctfdump_printf(CTFDUMP_TYPES, "%s encoding=%s offset=%u "
+ "bits=%u", name, encn, cte.cte_offset, cte.cte_bits);
+ break;
+ case CTF_K_POINTER:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s refers to %lu", name,
+ ref);
+ break;
+ case CTF_K_ARRAY:
+ if (ctf_array_info(g_fp, id, &ar) == CTF_ERR)
+ ctfdump_fatal("failed to get array information for "
+ "%s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s contents: %lu, index: %lu",
+ name, ar.ctr_contents, ar.ctr_index);
+ break;
+ case CTF_K_FUNCTION:
+ if (ctf_func_info_by_id(g_fp, id, &ctc) == CTF_ERR)
+ ctfdump_fatal("failed to get function info for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ if (ctc.ctc_argc > 0) {
+ ctfdump_fargs_grow(ctc.ctc_argc);
+ if (ctf_func_args_by_id(g_fp, id, g_nfargc, g_fargc) ==
+ CTF_ERR)
+ ctfdump_fatal("failed to get function "
+ "arguments for %s: %s\n", name,
+ ctf_errmsg(ctf_errno(g_fp)));
+ }
+ ctfdump_printf(CTFDUMP_TYPES,
+ "%s returns: %lu args: (", name, ctc.ctc_return);
+ for (i = 0; i < ctc.ctc_argc; i++) {
+ ctfdump_printf(CTFDUMP_TYPES, "%lu%s", g_fargc[i],
+ i + 1 == ctc.ctc_argc ? "" : ", ");
+ }
+ if (ctc.ctc_flags & CTF_FUNC_VARARG)
+ ctfdump_printf(CTFDUMP_TYPES, "%s...",
+ ctc.ctc_argc == 0 ? "" : ", ");
+ ctfdump_printf(CTFDUMP_TYPES, ")");
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ size = ctf_type_size(g_fp, id);
+ if (size == CTF_ERR)
+ ctfdump_fatal("failed to get size of %s: %s\n", name,
+ ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s (%d bytes)\n", name, size);
+ count = 0;
+ if (ctf_member_iter(g_fp, id, ctfdump_member_cb, &count) != 0)
+ ctfdump_fatal("failed to iterate members of %s: %s\n",
+ name, ctf_errmsg(ctf_errno(g_fp)));
+ if (kind == CTF_K_STRUCT) {
+ g_stats.cs_nsmembs += count;
+ g_stats.cs_nsmax = MAX(count, g_stats.cs_nsmax);
+ g_stats.cs_structsz += size;
+ g_stats.cs_sszmax = MAX(size, g_stats.cs_sszmax);
+ } else {
+ g_stats.cs_numembs += count;
+ g_stats.cs_numax = MAX(count, g_stats.cs_numax);
+ g_stats.cs_unionsz += size;
+ g_stats.cs_uszmax = MAX(count, g_stats.cs_uszmax);
+ }
+ break;
+ case CTF_K_ENUM:
+ ctfdump_printf(CTFDUMP_TYPES, "%s\n", name);
+ count = 0;
+ if (ctf_enum_iter(g_fp, id, ctfdump_enum_cb, &count) != 0)
+ ctfdump_fatal("failed to iterate enumerators of %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ g_stats.cs_nemembs += count;
+ g_stats.cs_nemax = MAX(g_stats.cs_nemax, count);
+ break;
+ case CTF_K_FORWARD:
+ ctfdump_printf(CTFDUMP_TYPES, "forward %s\n", name);
+ break;
+ case CTF_K_TYPEDEF:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "typedef %s refers to %lu", name,
+ ref);
+ break;
+ case CTF_K_VOLATILE:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s refers to %lu", name,
+ ref);
+ break;
+ case CTF_K_CONST:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s refers to %lu", name,
+ ref);
+ break;
+ case CTF_K_RESTRICT:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s refers to %lu", name,
+ ref);
+ break;
+ default:
+ ctfdump_fatal("encountered unknown kind for type %s: %d\n",
+ name, kind);
+ }
+
+ ctfdump_printf(CTFDUMP_TYPES, "\n");
+
+ return (0);
+}
+
+static void
+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",
+ ctf_errmsg(ctf_errno(g_fp)));
+}
+
+static void
+ctfdump_output(const char *out)
+{
+ int fd, ret;
+ const void *data;
+ size_t len;
+
+ ctf_dataptr(g_fp, &data, &len);
+ if ((fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0)
+ ctfdump_fatal("failed to open output file %s: %s\n", out,
+ strerror(errno));
+
+ while (len > 0) {
+ ret = write(fd, data, len);
+ if (ret == -1 && errno == EINTR)
+ continue;
+ else if (ret == -1 && (errno == EFAULT || errno == EBADF))
+ abort();
+ else if (ret == -1)
+ ctfdump_fatal("failed to write to %s: %s\n", out,
+ strerror(errno));
+ data += ret;
+ len -= ret;
+ }
+
+ do {
+ ret = close(fd);
+ } while (ret == -1 && errno == EINTR);
+ if (ret != 0 && errno == EBADF)
+ abort();
+ if (ret != 0)
+ ctfdump_fatal("failed to close %s: %s\n", out, strerror(errno));
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c, fd, err;
+ const char *ufile = NULL, *parent = NULL;
+
+ g_progname = basename(argv[0]);
+ while ((c = getopt(argc, argv, ":dfhlp:sStu:")) != -1) {
+ switch (c) {
+ case 'd':
+ g_dump |= CTFDUMP_OBJECTS;
+ break;
+ case 'f':
+ g_dump |= CTFDUMP_FUNCTIONS;
+ break;
+ case 'h':
+ g_dump |= CTFDUMP_HEADER;
+ break;
+ case 'l':
+ g_dump |= CTFDUMP_LABELS;
+ break;
+ case 'p':
+ parent = optarg;
+ break;
+ case 's':
+ g_dump |= CTFDUMP_STRINGS;
+ break;
+ case 'S':
+ g_dump |= CTFDUMP_STATS;
+ break;
+ case 't':
+ g_dump |= CTFDUMP_TYPES;
+ break;
+ case 'u':
+ g_dump |= CTFDUMP_OUTPUT;
+ ufile = optarg;
+ break;
+ case '?':
+ ctfdump_usage("Option -%c requires an operand\n",
+ optopt);
+ return (2);
+ case ':':
+ ctfdump_usage("Unknown option: -%c\n", optopt);
+ return (2);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * Dump all information by default.
+ */
+ if (g_dump == 0)
+ g_dump = CTFDUMP_DEFAULT;
+
+ if (argc != 1) {
+ ctfdump_usage("no file to dump\n");
+ return (2);
+ }
+
+ if ((fd = open(argv[0], O_RDONLY)) < 0)
+ ctfdump_fatal("failed to open file %s: %s\n", argv[0],
+ strerror(errno));
+
+ g_fp = ctf_fdopen(fd, &err);
+ if (g_fp == NULL)
+ ctfdump_fatal("failed to open file %s: %s\n", argv[0],
+ ctf_errmsg(err));
+
+ if (parent != NULL) {
+ ctf_file_t *pfp = ctf_open(parent, &err);
+
+ if (pfp == NULL)
+ ctfdump_fatal("failed to open parent file %s: %s\n",
+ parent, ctf_errmsg(err));
+ if (ctf_import(g_fp, pfp) != 0)
+ ctfdump_fatal("failed to import parent %s: %s\n",
+ parent, ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ /*
+ * If stats is set, we must run through everything exect CTFDUMP_OUTPUT.
+ * We also do CTFDUMP_STATS last as a result.
+ */
+ if (g_dump & CTFDUMP_HEADER)
+ ctfdump_header();
+
+ if (g_dump & (CTFDUMP_LABELS | CTFDUMP_STATS))
+ ctfdump_labels();
+
+ if (g_dump & (CTFDUMP_OBJECTS | CTFDUMP_STATS))
+ ctfdump_objects();
+
+ if (g_dump & (CTFDUMP_FUNCTIONS | CTFDUMP_STATS))
+ ctfdump_functions();
+
+ if (g_dump & (CTFDUMP_TYPES | CTFDUMP_STATS))
+ ctfdump_types();
+
+ if (g_dump & (CTFDUMP_STRINGS | CTFDUMP_STATS))
+ ctfdump_strings();
+
+ if (g_dump & CTFDUMP_STATS)
+ ctfdump_stats();
+
+ if (g_dump & CTFDUMP_OUTPUT)
+ ctfdump_output(ufile);
+
+ return (0);
+}