Index: head/cddl/contrib/opensolaris/lib/libctf/common/ctf_lib.c =================================================================== --- head/cddl/contrib/opensolaris/lib/libctf/common/ctf_lib.c (revision 271694) +++ head/cddl/contrib/opensolaris/lib/libctf/common/ctf_lib.c (revision 271695) @@ -1,527 +1,527 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #if defined(sun) #include #else #include #endif #include #if defined(sun) #ifdef _LP64 static const char *_libctf_zlib = "/usr/lib/64/libz.so"; #else static const char *_libctf_zlib = "/usr/lib/libz.so"; #endif #endif static struct { int (*z_uncompress)(uchar_t *, ulong_t *, const uchar_t *, ulong_t); const char *(*z_error)(int); void *z_dlp; } zlib; static size_t _PAGESIZE; static size_t _PAGEMASK; #if defined(sun) #pragma init(_libctf_init) #else void _libctf_init(void) __attribute__ ((constructor)); #endif void _libctf_init(void) { #if defined(sun) const char *p = getenv("LIBCTF_DECOMPRESSOR"); if (p != NULL) _libctf_zlib = p; /* use alternate decompression library */ #endif _libctf_debug = getenv("LIBCTF_DEBUG") != NULL; _PAGESIZE = getpagesize(); _PAGEMASK = ~(_PAGESIZE - 1); } /* * Attempt to dlopen the decompression library and locate the symbols of * interest that we will need to call. This information in cached so * that multiple calls to ctf_bufopen() do not need to reopen the library. */ void * ctf_zopen(int *errp) { #if defined(sun) ctf_dprintf("decompressing CTF data using %s\n", _libctf_zlib); if (zlib.z_dlp != NULL) return (zlib.z_dlp); /* library is already loaded */ if (access(_libctf_zlib, R_OK) == -1) return (ctf_set_open_errno(errp, ECTF_ZMISSING)); if ((zlib.z_dlp = dlopen(_libctf_zlib, RTLD_LAZY | RTLD_LOCAL)) == NULL) return (ctf_set_open_errno(errp, ECTF_ZINIT)); zlib.z_uncompress = (int (*)(uchar_t *, ulong_t *, const uchar_t *, ulong_t)) dlsym(zlib.z_dlp, "uncompress"); zlib.z_error = (const char *(*)(int)) dlsym(zlib.z_dlp, "zError"); if (zlib.z_uncompress == NULL || zlib.z_error == NULL) { (void) dlclose(zlib.z_dlp); bzero(&zlib, sizeof (zlib)); return (ctf_set_open_errno(errp, ECTF_ZINIT)); } #else zlib.z_uncompress = uncompress; zlib.z_error = zError; /* Dummy return variable as 'no error' */ zlib.z_dlp = (void *) (uintptr_t) 1; #endif return (zlib.z_dlp); } /* * The ctf_bufopen() routine calls these subroutines, defined by , * which we then patch through to the functions in the decompression library. */ int z_uncompress(void *dst, size_t *dstlen, const void *src, size_t srclen) { return (zlib.z_uncompress(dst, (ulong_t *)dstlen, src, srclen)); } const char * z_strerror(int err) { return (zlib.z_error(err)); } /* * Convert a 32-bit ELF file header into GElf. */ static void ehdr_to_gelf(const Elf32_Ehdr *src, GElf_Ehdr *dst) { bcopy(src->e_ident, dst->e_ident, EI_NIDENT); dst->e_type = src->e_type; dst->e_machine = src->e_machine; dst->e_version = src->e_version; dst->e_entry = (Elf64_Addr)src->e_entry; dst->e_phoff = (Elf64_Off)src->e_phoff; dst->e_shoff = (Elf64_Off)src->e_shoff; dst->e_flags = src->e_flags; dst->e_ehsize = src->e_ehsize; dst->e_phentsize = src->e_phentsize; dst->e_phnum = src->e_phnum; dst->e_shentsize = src->e_shentsize; dst->e_shnum = src->e_shnum; dst->e_shstrndx = src->e_shstrndx; } /* * Convert a 32-bit ELF section header into GElf. */ static void shdr_to_gelf(const Elf32_Shdr *src, GElf_Shdr *dst) { dst->sh_name = src->sh_name; dst->sh_type = src->sh_type; dst->sh_flags = src->sh_flags; dst->sh_addr = src->sh_addr; dst->sh_offset = src->sh_offset; dst->sh_size = src->sh_size; dst->sh_link = src->sh_link; dst->sh_info = src->sh_info; dst->sh_addralign = src->sh_addralign; dst->sh_entsize = src->sh_entsize; } /* * In order to mmap a section from the ELF file, we must round down sh_offset * to the previous page boundary, and mmap the surrounding page. We store * the pointer to the start of the actual section data back into sp->cts_data. */ const void * ctf_sect_mmap(ctf_sect_t *sp, int fd) { size_t pageoff = sp->cts_offset & ~_PAGEMASK; caddr_t base = mmap64(NULL, sp->cts_size + pageoff, PROT_READ, MAP_PRIVATE, fd, sp->cts_offset & _PAGEMASK); if (base != MAP_FAILED) sp->cts_data = base + pageoff; return (base); } /* * Since sp->cts_data has the adjusted offset, we have to again round down * to get the actual mmap address and round up to get the size. */ void ctf_sect_munmap(const ctf_sect_t *sp) { uintptr_t addr = (uintptr_t)sp->cts_data; uintptr_t pageoff = addr & ~_PAGEMASK; (void) munmap((void *)(addr - pageoff), sp->cts_size + pageoff); } /* * Open the specified file descriptor and return a pointer to a CTF container. * The file can be either an ELF file or raw CTF file. The caller is * responsible for closing the file descriptor when it is no longer needed. */ ctf_file_t * ctf_fdopen(int fd, int *errp) { ctf_sect_t ctfsect, symsect, strsect; ctf_file_t *fp = NULL; size_t shstrndx, shnum; struct stat64 st; ssize_t nbytes; union { ctf_preamble_t ctf; Elf32_Ehdr e32; GElf_Ehdr e64; } hdr; bzero(&ctfsect, sizeof (ctf_sect_t)); bzero(&symsect, sizeof (ctf_sect_t)); bzero(&strsect, sizeof (ctf_sect_t)); bzero(&hdr.ctf, sizeof (hdr)); if (fstat64(fd, &st) == -1) return (ctf_set_open_errno(errp, errno)); if ((nbytes = pread64(fd, &hdr.ctf, sizeof (hdr), 0)) <= 0) return (ctf_set_open_errno(errp, nbytes < 0? errno : ECTF_FMT)); /* * If we have read enough bytes to form a CTF header and the magic * string matches, attempt to interpret the file as raw CTF. */ if (nbytes >= (ssize_t) sizeof (ctf_preamble_t) && hdr.ctf.ctp_magic == CTF_MAGIC) { if (hdr.ctf.ctp_version > CTF_VERSION) return (ctf_set_open_errno(errp, ECTF_CTFVERS)); ctfsect.cts_data = mmap64(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (ctfsect.cts_data == MAP_FAILED) return (ctf_set_open_errno(errp, errno)); ctfsect.cts_name = _CTF_SECTION; ctfsect.cts_type = SHT_PROGBITS; ctfsect.cts_flags = SHF_ALLOC; ctfsect.cts_size = (size_t)st.st_size; ctfsect.cts_entsize = 1; ctfsect.cts_offset = 0; if ((fp = ctf_bufopen(&ctfsect, NULL, NULL, errp)) == NULL) ctf_sect_munmap(&ctfsect); return (fp); } /* * If we have read enough bytes to form an ELF header and the magic * string matches, attempt to interpret the file as an ELF file. We * do our own largefile ELF processing, and convert everything to * GElf structures so that clients can operate on any data model. */ if (nbytes >= (ssize_t) sizeof (Elf32_Ehdr) && bcmp(&hdr.e32.e_ident[EI_MAG0], ELFMAG, SELFMAG) == 0) { -#ifdef _BIG_ENDIAN +#if BYTE_ORDER == _BIG_ENDIAN uchar_t order = ELFDATA2MSB; #else uchar_t order = ELFDATA2LSB; #endif GElf_Shdr *sp; void *strs_map; size_t strs_mapsz, i; char *strs; if (hdr.e32.e_ident[EI_DATA] != order) return (ctf_set_open_errno(errp, ECTF_ENDIAN)); if (hdr.e32.e_version != EV_CURRENT) return (ctf_set_open_errno(errp, ECTF_ELFVERS)); if (hdr.e32.e_ident[EI_CLASS] == ELFCLASS64) { if (nbytes < (ssize_t) sizeof (GElf_Ehdr)) return (ctf_set_open_errno(errp, ECTF_FMT)); } else { Elf32_Ehdr e32 = hdr.e32; ehdr_to_gelf(&e32, &hdr.e64); } shnum = hdr.e64.e_shnum; shstrndx = hdr.e64.e_shstrndx; /* Extended ELF sections */ if ((shstrndx == SHN_XINDEX) || (shnum == 0)) { if (hdr.e32.e_ident[EI_CLASS] == ELFCLASS32) { Elf32_Shdr x32; if (pread64(fd, &x32, sizeof (x32), hdr.e64.e_shoff) != sizeof (x32)) return (ctf_set_open_errno(errp, errno)); shnum = x32.sh_size; shstrndx = x32.sh_link; } else { Elf64_Shdr x64; if (pread64(fd, &x64, sizeof (x64), hdr.e64.e_shoff) != sizeof (x64)) return (ctf_set_open_errno(errp, errno)); shnum = x64.sh_size; shstrndx = x64.sh_link; } } if (shstrndx >= shnum) return (ctf_set_open_errno(errp, ECTF_CORRUPT)); nbytes = sizeof (GElf_Shdr) * shnum; if ((sp = malloc(nbytes)) == NULL) return (ctf_set_open_errno(errp, errno)); /* * Read in and convert to GElf the array of Shdr structures * from e_shoff so we can locate sections of interest. */ if (hdr.e32.e_ident[EI_CLASS] == ELFCLASS32) { Elf32_Shdr *sp32; nbytes = sizeof (Elf32_Shdr) * shnum; if ((sp32 = malloc(nbytes)) == NULL || pread64(fd, sp32, nbytes, hdr.e64.e_shoff) != nbytes) { free(sp); return (ctf_set_open_errno(errp, errno)); } for (i = 0; i < shnum; i++) shdr_to_gelf(&sp32[i], &sp[i]); free(sp32); } else if (pread64(fd, sp, nbytes, hdr.e64.e_shoff) != nbytes) { free(sp); return (ctf_set_open_errno(errp, errno)); } /* * Now mmap the section header strings section so that we can * perform string comparison on the section names. */ strs_mapsz = sp[shstrndx].sh_size + (sp[shstrndx].sh_offset & ~_PAGEMASK); strs_map = mmap64(NULL, strs_mapsz, PROT_READ, MAP_PRIVATE, fd, sp[shstrndx].sh_offset & _PAGEMASK); strs = (char *)strs_map + (sp[shstrndx].sh_offset & ~_PAGEMASK); if (strs_map == MAP_FAILED) { free(sp); return (ctf_set_open_errno(errp, ECTF_MMAP)); } /* * Iterate over the section header array looking for the CTF * section and symbol table. The strtab is linked to symtab. */ for (i = 0; i < shnum; i++) { const GElf_Shdr *shp = &sp[i]; const GElf_Shdr *lhp = &sp[shp->sh_link]; if (shp->sh_link >= shnum) continue; /* corrupt sh_link field */ if (shp->sh_name >= sp[shstrndx].sh_size || lhp->sh_name >= sp[shstrndx].sh_size) continue; /* corrupt sh_name field */ if (shp->sh_type == SHT_PROGBITS && strcmp(strs + shp->sh_name, _CTF_SECTION) == 0) { ctfsect.cts_name = strs + shp->sh_name; ctfsect.cts_type = shp->sh_type; ctfsect.cts_flags = shp->sh_flags; ctfsect.cts_size = shp->sh_size; ctfsect.cts_entsize = shp->sh_entsize; ctfsect.cts_offset = (off64_t)shp->sh_offset; } else if (shp->sh_type == SHT_SYMTAB) { symsect.cts_name = strs + shp->sh_name; symsect.cts_type = shp->sh_type; symsect.cts_flags = shp->sh_flags; symsect.cts_size = shp->sh_size; symsect.cts_entsize = shp->sh_entsize; symsect.cts_offset = (off64_t)shp->sh_offset; strsect.cts_name = strs + lhp->sh_name; strsect.cts_type = lhp->sh_type; strsect.cts_flags = lhp->sh_flags; strsect.cts_size = lhp->sh_size; strsect.cts_entsize = lhp->sh_entsize; strsect.cts_offset = (off64_t)lhp->sh_offset; } } free(sp); /* free section header array */ if (ctfsect.cts_type == SHT_NULL) { (void) munmap(strs_map, strs_mapsz); return (ctf_set_open_errno(errp, ECTF_NOCTFDATA)); } /* * Now mmap the CTF data, symtab, and strtab sections and * call ctf_bufopen() to do the rest of the work. */ if (ctf_sect_mmap(&ctfsect, fd) == MAP_FAILED) { (void) munmap(strs_map, strs_mapsz); return (ctf_set_open_errno(errp, ECTF_MMAP)); } if (symsect.cts_type != SHT_NULL && strsect.cts_type != SHT_NULL) { if (ctf_sect_mmap(&symsect, fd) == MAP_FAILED || ctf_sect_mmap(&strsect, fd) == MAP_FAILED) { (void) ctf_set_open_errno(errp, ECTF_MMAP); goto bad; /* unmap all and abort */ } fp = ctf_bufopen(&ctfsect, &symsect, &strsect, errp); } else fp = ctf_bufopen(&ctfsect, NULL, NULL, errp); bad: if (fp == NULL) { ctf_sect_munmap(&ctfsect); ctf_sect_munmap(&symsect); ctf_sect_munmap(&strsect); } else fp->ctf_flags |= LCTF_MMAP; (void) munmap(strs_map, strs_mapsz); return (fp); } return (ctf_set_open_errno(errp, ECTF_FMT)); } /* * Open the specified file and return a pointer to a CTF container. The file * can be either an ELF file or raw CTF file. This is just a convenient * wrapper around ctf_fdopen() for callers. */ ctf_file_t * ctf_open(const char *filename, int *errp) { ctf_file_t *fp; int fd; if ((fd = open64(filename, O_RDONLY)) == -1) { if (errp != NULL) *errp = errno; return (NULL); } fp = ctf_fdopen(fd, errp); (void) close(fd); return (fp); } /* * Write the uncompressed CTF data stream to the specified file descriptor. * This is useful for saving the results of dynamic CTF containers. */ int ctf_write(ctf_file_t *fp, int fd) { const uchar_t *buf = fp->ctf_base; ssize_t resid = fp->ctf_size; ssize_t len; while (resid != 0) { if ((len = write(fd, buf, resid)) <= 0) return (ctf_set_errno(fp, errno)); resid -= len; buf += len; } return (0); } /* * Set the CTF library client version to the specified version. If version is * zero, we just return the default library version number. */ int ctf_version(int version) { if (version < 0) { errno = EINVAL; return (-1); } if (version > 0) { if (version > CTF_VERSION) { errno = ENOTSUP; return (-1); } ctf_dprintf("ctf_version: client using version %d\n", version); _libctf_version = version; } return (_libctf_version); } Index: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c =================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c (revision 271694) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c (revision 271695) @@ -1,706 +1,706 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2011 by Delphix. All rights reserved. */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ /* * DTrace print() action * * This file contains the post-processing logic for the print() action. The * print action behaves identically to trace() in that it generates a * DTRACEACT_DIFEXPR action, but the action argument field refers to a CTF type * string stored in the DOF string table (similar to printf formats). We * take the result of the trace action and post-process it in the fashion of * MDB's ::print dcmd. * * This implementation differs from MDB's in the following ways: * * - We do not expose any options or flags. The behavior of print() is * equivalent to "::print -tn". * * - MDB will display "holes" in structures (unused padding between * members). * * - When printing arrays of structures, MDB will leave a trailing ',' * after the last element. * * - MDB will print time_t types as date and time. * * - MDB will detect when an enum is actually the OR of several flags, * and print it out with the constituent flags separated. * * - For large arrays, MDB will print the first few members and then * print a "..." continuation line. * * - MDB will break and wrap arrays at 80 columns. * * - MDB prints out floats and doubles by hand, as it must run in kmdb * context. We're able to leverage the printf() format strings, * but the result is a slightly different format. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* determines whether the given integer CTF encoding is a character */ #define CTF_IS_CHAR(e) \ (((e).cte_format & (CTF_INT_CHAR | CTF_INT_SIGNED)) == \ (CTF_INT_CHAR | CTF_INT_SIGNED) && (e).cte_bits == NBBY) /* determines whether the given CTF kind is a struct or union */ #define CTF_IS_STRUCTLIKE(k) \ ((k) == CTF_K_STRUCT || (k) == CTF_K_UNION) /* * Print structure passed down recursively through printing algorithm. */ typedef struct dt_printarg { dtrace_hdl_t *pa_dtp; /* libdtrace handle */ caddr_t pa_addr; /* base address of trace data */ ctf_file_t *pa_ctfp; /* CTF container */ int pa_depth; /* member depth */ int pa_nest; /* nested array depth */ FILE *pa_file; /* output file */ } dt_printarg_t; static int dt_print_member(const char *, ctf_id_t, ulong_t, int, void *); /* * Safe version of ctf_type_name() that will fall back to just "" if it * can't resolve the type. */ static void dt_print_type_name(ctf_file_t *ctfp, ctf_id_t id, char *buf, size_t buflen) { if (ctf_type_name(ctfp, id, buf, buflen) == NULL) (void) snprintf(buf, buflen, "<%ld>", id); } /* * Print any necessary trailing braces for structures or unions. We don't get * invoked when a struct or union ends, so we infer the need to print braces * based on the depth the last time we printed something and the new depth. */ static void dt_print_trailing_braces(dt_printarg_t *pap, int depth) { int d; for (d = pap->pa_depth; d > depth; d--) { (void) fprintf(pap->pa_file, "%*s}%s", (d + pap->pa_nest - 1) * 4, "", d == depth + 1 ? "" : "\n"); } } /* * Print the appropriate amount of indentation given the current depth and * array nesting. */ static void dt_print_indent(dt_printarg_t *pap) { (void) fprintf(pap->pa_file, "%*s", (pap->pa_depth + pap->pa_nest) * 4, ""); } /* * Print a bitfield. It's worth noting that the D compiler support for * bitfields is currently broken; printing "D`user_desc_t" (pulled in by the * various D provider files) will produce incorrect results compared to * "genunix`user_desc_t". */ static void print_bitfield(dt_printarg_t *pap, ulong_t off, ctf_encoding_t *ep) { FILE *fp = pap->pa_file; caddr_t addr = pap->pa_addr + off / NBBY; uint64_t mask = (1ULL << ep->cte_bits) - 1; uint64_t value = 0; size_t size = (ep->cte_bits + (NBBY - 1)) / NBBY; uint8_t *buf = (uint8_t *)&value; uint8_t shift; /* * On big-endian machines, we need to adjust the buf pointer to refer * to the lowest 'size' bytes in 'value', and we need to shift based on * the offset from the end of the data, not the offset of the start. */ -#ifdef _BIG_ENDIAN +#if BYTE_ORDER == _BIG_ENDIAN buf += sizeof (value) - size; off += ep->cte_bits; #endif bcopy(addr, buf, size); shift = off % NBBY; /* * Offsets are counted from opposite ends on little- and * big-endian machines. */ -#ifdef _BIG_ENDIAN +#if BYTE_ORDER == _BIG_ENDIAN shift = NBBY - shift; #endif /* * If the bits we want do not begin on a byte boundary, shift the data * right so that the value is in the lowest 'cte_bits' of 'value'. */ if (off % NBBY != 0) value >>= shift; value &= mask; (void) fprintf(fp, "%#llx", (u_longlong_t)value); } /* * Dump the contents of memory as a fixed-size integer in hex. */ static void dt_print_hex(FILE *fp, caddr_t addr, size_t size) { switch (size) { case sizeof (uint8_t): (void) fprintf(fp, "%#x", *(uint8_t *)addr); break; case sizeof (uint16_t): /* LINTED - alignment */ (void) fprintf(fp, "%#x", *(uint16_t *)addr); break; case sizeof (uint32_t): /* LINTED - alignment */ (void) fprintf(fp, "%#x", *(uint32_t *)addr); break; case sizeof (uint64_t): (void) fprintf(fp, "%#llx", /* LINTED - alignment */ (unsigned long long)*(uint64_t *)addr); break; default: (void) fprintf(fp, "", (uint_t)size); } } /* * Print an integer type. Before dumping the contents via dt_print_hex(), we * first check the encoding to see if it's part of a bitfield or a character. */ static void dt_print_int(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; ctf_encoding_t e; size_t size; caddr_t addr = pap->pa_addr + off / NBBY; if (ctf_type_encoding(ctfp, base, &e) == CTF_ERR) { (void) fprintf(fp, ""); return; } /* * This comes from MDB - it's not clear under what circumstances this * would be found. */ if (e.cte_format & CTF_INT_VARARGS) { (void) fprintf(fp, "..."); return; } /* * We print this as a bitfield if the bit encoding indicates it's not * an even power of two byte size, or is larger than 8 bytes. */ size = e.cte_bits / NBBY; if (size > 8 || (e.cte_bits % NBBY) != 0 || (size & (size - 1)) != 0) { print_bitfield(pap, off, &e); return; } /* * If this is a character, print it out as such. */ if (CTF_IS_CHAR(e)) { char c = *(char *)addr; if (isprint(c)) (void) fprintf(fp, "'%c'", c); else if (c == 0) (void) fprintf(fp, "'\\0'"); else (void) fprintf(fp, "'\\%03o'", c); return; } dt_print_hex(fp, addr, size); } /* * Print a floating point (float, double, long double) value. */ /* ARGSUSED */ static void dt_print_float(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; ctf_encoding_t e; caddr_t addr = pap->pa_addr + off / NBBY; if (ctf_type_encoding(ctfp, base, &e) == 0) { if (e.cte_format == CTF_FP_SINGLE && e.cte_bits == sizeof (float) * NBBY) { /* LINTED - alignment */ (void) fprintf(fp, "%+.7e", *((float *)addr)); } else if (e.cte_format == CTF_FP_DOUBLE && e.cte_bits == sizeof (double) * NBBY) { /* LINTED - alignment */ (void) fprintf(fp, "%+.7e", *((double *)addr)); } else if (e.cte_format == CTF_FP_LDOUBLE && e.cte_bits == sizeof (long double) * NBBY) { /* LINTED - alignment */ (void) fprintf(fp, "%+.16LE", *((long double *)addr)); } else { (void) fprintf(fp, ""); } } } /* * A pointer is generally printed as a fixed-size integer. If we have a * function pointer, we try to look up its name. */ static void dt_print_ptr(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; caddr_t addr = pap->pa_addr + off / NBBY; size_t size = ctf_type_size(ctfp, base); ctf_id_t bid = ctf_type_reference(ctfp, base); uint64_t pc; dtrace_syminfo_t dts; GElf_Sym sym; if (bid == CTF_ERR || ctf_type_kind(ctfp, bid) != CTF_K_FUNCTION) { dt_print_hex(fp, addr, size); } else { /* LINTED - alignment */ pc = *((uint64_t *)addr); if (dtrace_lookup_by_addr(pap->pa_dtp, pc, &sym, &dts) != 0) { dt_print_hex(fp, addr, size); } else { (void) fprintf(fp, "%s`%s", dts.dts_object, dts.dts_name); } } } /* * Print out an array. This is somewhat complex, as we must manually visit * each member, and recursively invoke ctf_type_visit() for each member. If * the members are non-structs, then we print them out directly: * * [ 0x14, 0x2e, 0 ] * * If they are structs, then we print out the necessary leading and trailing * braces, to end up with: * * [ * type { * ... * }, * type { * ... * } * ] * * We also use a heuristic to detect whether the array looks like a character * array. If the encoding indicates it's a character, and we have all * printable characters followed by a null byte, then we display it as a * string: * * [ "string" ] */ static void dt_print_array(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; caddr_t addr = pap->pa_addr + off / NBBY; ctf_arinfo_t car; ssize_t eltsize; ctf_encoding_t e; int i; boolean_t isstring; int kind; ctf_id_t rtype; if (ctf_array_info(ctfp, base, &car) == CTF_ERR) { (void) fprintf(fp, "0x%p", (void *)addr); return; } if ((eltsize = ctf_type_size(ctfp, car.ctr_contents)) < 0 || (rtype = ctf_type_resolve(ctfp, car.ctr_contents)) == CTF_ERR || (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR) { (void) fprintf(fp, "", car.ctr_contents); return; } /* see if this looks like a string */ isstring = B_FALSE; if (kind == CTF_K_INTEGER && ctf_type_encoding(ctfp, rtype, &e) != CTF_ERR && CTF_IS_CHAR(e)) { char c; for (i = 0; i < car.ctr_nelems; i++) { c = *((char *)addr + eltsize * i); if (!isprint(c) || c == '\0') break; } if (i != car.ctr_nelems && c == '\0') isstring = B_TRUE; } /* * As a slight aesthetic optimization, if we are a top-level type, then * don't bother printing out the brackets. This lets print("foo") look * like: * * string "foo" * * As D will internally represent this as a char[256] array. */ if (!isstring || pap->pa_depth != 0) (void) fprintf(fp, "[ "); if (isstring) (void) fprintf(fp, "\""); for (i = 0; i < car.ctr_nelems; i++) { if (isstring) { char c = *((char *)addr + eltsize * i); if (c == '\0') break; (void) fprintf(fp, "%c", c); } else { /* * Recursively invoke ctf_type_visit() on each member. * We setup a new printarg struct with 'pa_nest' set to * indicate that we are within a nested array. */ dt_printarg_t pa = *pap; pa.pa_nest += pap->pa_depth + 1; pa.pa_depth = 0; pa.pa_addr = addr + eltsize * i; (void) ctf_type_visit(ctfp, car.ctr_contents, dt_print_member, &pa); dt_print_trailing_braces(&pa, 0); if (i != car.ctr_nelems - 1) (void) fprintf(fp, ", "); else if (CTF_IS_STRUCTLIKE(kind)) (void) fprintf(fp, "\n"); } } if (isstring) (void) fprintf(fp, "\""); if (!isstring || pap->pa_depth != 0) { if (CTF_IS_STRUCTLIKE(kind)) dt_print_indent(pap); else (void) fprintf(fp, " "); (void) fprintf(fp, "]"); } } /* * This isued by both structs and unions to print the leading brace. */ /* ARGSUSED */ static void dt_print_structlike(ctf_id_t id, ulong_t off, dt_printarg_t *pap) { (void) fprintf(pap->pa_file, "{"); } /* * For enums, we try to print the enum name, and fall back to the value if it * can't be determined. We do not do any fancy flag processing like mdb. */ /* ARGSUSED */ static void dt_print_enum(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; const char *ename; ssize_t size; caddr_t addr = pap->pa_addr + off / NBBY; int value = 0; /* * The C standard says that an enum will be at most the sizeof (int). * But if all the values are less than that, the compiler can use a * smaller size. Thanks standards. */ size = ctf_type_size(ctfp, base); switch (size) { case sizeof (uint8_t): value = *(uint8_t *)addr; break; case sizeof (uint16_t): value = *(uint16_t *)addr; break; case sizeof (int32_t): value = *(int32_t *)addr; break; default: (void) fprintf(fp, "", (uint_t)size); return; } if ((ename = ctf_enum_name(ctfp, base, value)) != NULL) (void) fprintf(fp, "%s", ename); else (void) fprintf(fp, "%d", value); } /* * Forward declaration. There's not much to do here without the complete * type information, so just print out this fact and drive on. */ /* ARGSUSED */ static void dt_print_tag(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { (void) fprintf(pap->pa_file, ""); } typedef void dt_printarg_f(ctf_id_t, ulong_t, dt_printarg_t *); static dt_printarg_f *const dt_printfuncs[] = { dt_print_int, /* CTF_K_INTEGER */ dt_print_float, /* CTF_K_FLOAT */ dt_print_ptr, /* CTF_K_POINTER */ dt_print_array, /* CTF_K_ARRAY */ dt_print_ptr, /* CTF_K_FUNCTION */ dt_print_structlike, /* CTF_K_STRUCT */ dt_print_structlike, /* CTF_K_UNION */ dt_print_enum, /* CTF_K_ENUM */ dt_print_tag /* CTF_K_FORWARD */ }; /* * Print one member of a structure. This callback is invoked from * ctf_type_visit() recursively. */ static int dt_print_member(const char *name, ctf_id_t id, ulong_t off, int depth, void *data) { char type[DT_TYPE_NAMELEN]; int kind; dt_printarg_t *pap = data; FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; boolean_t arraymember; boolean_t brief; ctf_encoding_t e; ctf_id_t rtype; dt_print_trailing_braces(pap, depth); /* * dt_print_trailing_braces() doesn't include the trailing newline; add * it here if necessary. */ if (depth < pap->pa_depth) (void) fprintf(fp, "\n"); pap->pa_depth = depth; if ((rtype = ctf_type_resolve(ctfp, id)) == CTF_ERR || (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR || kind < CTF_K_INTEGER || kind > CTF_K_FORWARD) { dt_print_indent(pap); (void) fprintf(fp, "%s = ", name, id); return (0); } dt_print_type_name(ctfp, id, type, sizeof (type)); arraymember = (pap->pa_nest != 0 && depth == 0); brief = (arraymember && !CTF_IS_STRUCTLIKE(kind)); if (!brief) { /* * If this is a direct array member and a struct (otherwise * brief would be true), then print a trailing newline, as the * array printing code doesn't include it because it might be a * simple type. */ if (arraymember) (void) fprintf(fp, "\n"); dt_print_indent(pap); /* always print the type */ (void) fprintf(fp, "%s", type); if (name[0] != '\0') { /* * For aesthetics, we don't include a space between the * type name and member name if the type is a pointer. * This will give us "void *foo =" instead of "void * * foo =". Unions also have the odd behavior that the * type name is returned as "union ", with a trailing * space, so we also avoid printing a space if the type * name already ends with a space. */ if (type[strlen(type) - 1] != '*' && type[strlen(type) -1] != ' ') { (void) fprintf(fp, " "); } (void) fprintf(fp, "%s", name); /* * If this looks like a bitfield, or is an integer not * aligned on a byte boundary, print the number of * bits after the name. */ if (kind == CTF_K_INTEGER && ctf_type_encoding(ctfp, id, &e) == 0) { ulong_t bits = e.cte_bits; ulong_t size = bits / NBBY; if (bits % NBBY != 0 || off % NBBY != 0 || size > 8 || size != ctf_type_size(ctfp, id)) { (void) fprintf(fp, " :%lu", bits); } } (void) fprintf(fp, " ="); } (void) fprintf(fp, " "); } dt_printfuncs[kind - 1](rtype, off, pap); /* direct simple array members are not separated by newlines */ if (!brief) (void) fprintf(fp, "\n"); return (0); } /* * Main print function invoked by dt_consume_cpu(). */ int dtrace_print(dtrace_hdl_t *dtp, FILE *fp, const char *typename, caddr_t addr, size_t len) { const char *s; char *object; dt_printarg_t pa; ctf_id_t id; dt_module_t *dmp; ctf_file_t *ctfp; int libid; /* * Split the fully-qualified type ID (module`id). This should * always be the format, but if for some reason we don't find the * expected value, return 0 to fall back to the generic trace() * behavior. In the case of userland CTF modules this will actually be * of the format (module`lib`id). This is due to the fact that those * modules have multiple CTF containers which `lib` identifies. */ for (s = typename; *s != '\0' && *s != '`'; s++) ; if (*s != '`') return (0); object = alloca(s - typename + 1); bcopy(typename, object, s - typename); object[s - typename] = '\0'; dmp = dt_module_lookup_by_name(dtp, object); if (dmp == NULL) return (0); if (dmp->dm_pid != 0) { libid = atoi(s + 1); s = strchr(s + 1, '`'); if (s == NULL || libid > dmp->dm_nctflibs) return (0); ctfp = dmp->dm_libctfp[libid]; } else { ctfp = dt_module_getctf(dtp, dmp); } id = atoi(s + 1); /* * Try to get the CTF kind for this id. If something has gone horribly * wrong and we can't resolve the ID, bail out and let trace() do the * work. */ if (ctfp == NULL || ctf_type_kind(ctfp, id) == CTF_ERR) return (0); /* setup the print structure and kick off the main print routine */ pa.pa_dtp = dtp; pa.pa_addr = addr; pa.pa_ctfp = ctfp; pa.pa_nest = 0; pa.pa_depth = 0; pa.pa_file = fp; (void) ctf_type_visit(pa.pa_ctfp, id, dt_print_member, &pa); dt_print_trailing_braces(&pa, 0); return (len); }