Date: Mon, 25 Mar 2013 20:38:09 +0000 (UTC) From: "Pedro F. Giffuni" <pfg@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r248708 - in head: cddl/contrib/opensolaris/lib/libdtrace/common cddl/lib/libdtrace sys/cddl/contrib/opensolaris/uts/common/dtrace Message-ID: <201303252038.r2PKc9CF058888@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: pfg Date: Mon Mar 25 20:38:09 2013 New Revision: 248708 URL: http://svnweb.freebsd.org/changeset/base/248708 Log: Dtrace: Add SUN MDB-like type-aware print() action. Merge change from illumos: 1694 Add type-aware print() action This is a very nice feature implemented in upstream Dtrace. A complete description is available here: http://dtrace.org/blogs/eschrock/2011/10/26/your-mdb-fell-into-my-dtrace/ This change bumps the DT_VERS_* number to 1.9.0 in accordance to what is done in illumos. While here also include some minor cleanups to ease further merging and appease clang with a fix by Fabian Keil. Illumos Revisions: 13501:c3a7090dbc16 13483:f413e6c5d297 Reference: https://www.illumos.org/issues/1560 https://www.illumos.org/issues/1694 Tested by: Fabian Keil Obtained from: Illumos MFC after: 1 month Added: - copied from r248707, vendor/illumos/20120614/cmd/dtrace/test/tst/common/print/ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c - copied unchanged from r248707, vendor/illumos/dist/lib/libdtrace/common/dt_print.c Directory Properties: head/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/print/ (props changed) Modified: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_cc.c head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_dof.c head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_errtags.h head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_map.c head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_program.c head/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h head/cddl/lib/libdtrace/Makefile head/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c Modified: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_cc.c ============================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_cc.c Mon Mar 25 19:12:36 2013 (r248707) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_cc.c Mon Mar 25 20:38:09 2013 (r248708) @@ -22,6 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, Joyent Inc. All rights reserved. + * Copyright (c) 2011 by Delphix. All rights reserved. */ /* @@ -679,6 +680,51 @@ dt_action_trace(dtrace_hdl_t *dtp, dt_no ap->dtad_kind = DTRACEACT_DIFEXPR; } +/* + * The print() action behaves identically to trace(), except that it stores the + * CTF type of the argument (if present) within the DOF for the DIFEXPR action. + * To do this, we set the 'dtsd_strdata' to point to the fully-qualified CTF + * type ID for the result of the DIF action. We use the ID instead of the name + * to handles complex types like arrays and function pointers that can't be + * resolved by ctf_type_lookup(). This is later processed by + * dtrace_dof_create() and turned into a reference into the string table so + * that we can get the type information when we process the data after the + * fact. + */ +static void +dt_action_print(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + dt_node_t *dret; + size_t len; + dt_module_t *dmp; + + if (dt_node_is_void(dnp->dn_args)) { + dnerror(dnp->dn_args, D_PRINT_VOID, + "print( ) may not be applied to a void expression\n"); + } + + if (dt_node_is_dynamic(dnp->dn_args)) { + dnerror(dnp->dn_args, D_PRINT_DYN, + "print( ) may not be applied to a dynamic expression\n"); + } + + dt_cg(yypcb, dnp->dn_args); + + dret = yypcb->pcb_dret; + dmp = dt_module_lookup_by_ctf(dtp, dret->dn_ctfp); + + len = snprintf(NULL, 0, "%s`%ld", dmp->dm_name, dret->dn_type) + 1; + sdp->dtsd_strdata = dt_alloc(dtp, len); + if (sdp->dtsd_strdata == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + (void) snprintf(sdp->dtsd_strdata, len, "%s`%ld", dmp->dm_name, + dret->dn_type); + + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_DIFEXPR; +} + static void dt_action_tracemem(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { @@ -1135,6 +1181,9 @@ dt_compile_fun(dtrace_hdl_t *dtp, dt_nod case DT_ACT_TRACE: dt_action_trace(dtp, dnp->dn_expr, sdp); break; + case DT_ACT_PRINT: + dt_action_print(dtp, dnp->dn_expr, sdp); + break; case DT_ACT_TRACEMEM: dt_action_tracemem(dtp, dnp->dn_expr, sdp); break; Modified: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c ============================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c Mon Mar 25 19:12:36 2013 (r248707) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c Mon Mar 25 20:38:09 2013 (r248708) @@ -25,6 +25,7 @@ /* * Copyright (c) 2011, Joyent, Inc. All rights reserved. + * Copyright (c) 2011 by Delphix. All rights reserved. */ #include <stdlib.h> @@ -2193,6 +2194,7 @@ again: if (act == DTRACEACT_TRACEMEM_DYNSIZE && rec->dtrd_size == sizeof (uint64_t)) { + /* LINTED - alignment */ tracememsize = *((unsigned long long *)addr); continue; } @@ -2300,6 +2302,35 @@ again: goto nextrec; } + /* + * If this is a DIF expression, and the record has a + * format set, this indicates we have a CTF type name + * associated with the data and we should try to print + * it out by type. + */ + if (act == DTRACEACT_DIFEXPR) { + const char *strdata = dt_strdata_lookup(dtp, + rec->dtrd_format); + if (strdata != NULL) { + n = dtrace_print(dtp, fp, strdata, + addr, rec->dtrd_size); + + /* + * dtrace_print() will return -1 on + * error, or return the number of bytes + * consumed. It will return 0 if the + * type couldn't be determined, and we + * should fall through to the normal + * trace method. + */ + if (n < 0) + return (-1); + + if (n > 0) + goto nextrec; + } + } + nofmt: if (act == DTRACEACT_PRINTA) { dt_print_aggdata_t pd; Modified: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_dof.c ============================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_dof.c Mon Mar 25 19:12:36 2013 (r248707) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_dof.c Mon Mar 25 20:38:09 2013 (r248708) @@ -21,6 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011 by Delphix. All rights reserved. */ #include <sys/types.h> @@ -758,16 +759,23 @@ dtrace_dof_create(dtrace_hdl_t *dtp, dtr dofa[i].dofa_difo = DOF_SECIDX_NONE; /* - * If the first action in a statement has format data, - * add the format string to the global string table. + * If the first action in a statement has string data, + * add the string to the global string table. This can + * be due either to a printf() format string + * (dtsd_fmtdata) or a print() type string + * (dtsd_strdata). */ if (sdp != NULL && ap == sdp->dtsd_action) { if (sdp->dtsd_fmtdata != NULL) { (void) dtrace_printf_format(dtp, sdp->dtsd_fmtdata, fmt, maxfmt + 1); strndx = dof_add_string(ddo, fmt); - } else + } else if (sdp->dtsd_strdata != NULL) { + strndx = dof_add_string(ddo, + sdp->dtsd_strdata); + } else { strndx = 0; /* use dtad_arg instead */ + } if ((next = dt_list_next(next)) != NULL) sdp = next->ds_desc; Modified: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_errtags.h ============================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_errtags.h Mon Mar 25 19:12:36 2013 (r248707) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_errtags.h Mon Mar 25 20:38:09 2013 (r248708) @@ -24,11 +24,14 @@ * Use is subject to license terms. */ + /* + * Copyright (c) 2011, Joyent, Inc. All rights reserved. + * Copyright (c) 2011 by Delphix. All rights reserved. + */ + #ifndef _DT_ERRTAGS_H #define _DT_ERRTAGS_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -187,6 +190,8 @@ typedef enum { D_PRINTA_AGGPROTO, /* printa() aggregation mismatch */ D_TRACE_VOID, /* trace() argument has void type */ D_TRACE_DYN, /* trace() argument has dynamic type */ + D_PRINT_VOID, /* print() argument has void type */ + D_PRINT_DYN, /* print() argument has dynamic type */ D_TRACEMEM_ADDR, /* tracemem() address bad type */ D_TRACEMEM_SIZE, /* tracemem() size bad type */ D_TRACEMEM_ARGS, /* tracemem() illegal number of args */ Modified: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h ============================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h Mon Mar 25 19:12:36 2013 (r248707) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h Mon Mar 25 20:38:09 2013 (r248708) @@ -26,6 +26,7 @@ /* * Copyright (c) 2011, Joyent, Inc. All rights reserved. + * Copyright (c) 2011 by Delphix. All rights reserved. */ #ifndef _DT_IMPL_H @@ -253,6 +254,8 @@ struct dtrace_hdl { dtrace_aggdesc_t **dt_aggdesc; /* aggregation descriptions */ int dt_maxformat; /* max format ID */ void **dt_formats; /* pointer to format array */ + int dt_maxstrdata; /* max strdata ID */ + char **dt_strdata; /* pointer to strdata array */ dt_aggregate_t dt_aggregate; /* aggregate */ dtrace_bufdesc_t dt_buf; /* staging buffer */ struct dt_pfdict *dt_pfdict; /* dictionary of printf conversions */ @@ -438,8 +441,9 @@ struct dtrace_hdl { #define DT_ACT_UMOD DT_ACT(26) /* umod() action */ #define DT_ACT_UADDR DT_ACT(27) /* uaddr() action */ #define DT_ACT_SETOPT DT_ACT(28) /* setopt() action */ -#define DT_ACT_PRINTM DT_ACT(29) /* printm() action */ -#define DT_ACT_PRINTT DT_ACT(30) /* printt() action */ +#define DT_ACT_PRINT DT_ACT(29) /* print() action */ +#define DT_ACT_PRINTM DT_ACT(30) /* printm() action */ +#define DT_ACT_PRINTT DT_ACT(31) /* printt() action */ /* * Sentinel to tell freopen() to restore the saved stdout. This must not @@ -641,6 +645,9 @@ extern void dt_aggid_destroy(dtrace_hdl_ extern void *dt_format_lookup(dtrace_hdl_t *, int); extern void dt_format_destroy(dtrace_hdl_t *); +extern const char *dt_strdata_lookup(dtrace_hdl_t *, int); +extern void dt_strdata_destroy(dtrace_hdl_t *); + extern int dt_print_quantize(dtrace_hdl_t *, FILE *, const void *, size_t, uint64_t); extern int dt_print_lquantize(dtrace_hdl_t *, FILE *, Modified: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_map.c ============================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_map.c Mon Mar 25 19:12:36 2013 (r248707) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_map.c Mon Mar 25 20:38:09 2013 (r248708) @@ -23,7 +23,9 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2011 by Delphix. All rights reserved. + */ #include <stdlib.h> #include <strings.h> @@ -35,10 +37,81 @@ #include <dt_printf.h> static int +dt_strdata_add(dtrace_hdl_t *dtp, dtrace_recdesc_t *rec, void ***data, int *max) +{ + int maxformat; + dtrace_fmtdesc_t fmt; + void *result; + + if (rec->dtrd_format == 0) + return (0); + + if (rec->dtrd_format <= *max && + (*data)[rec->dtrd_format - 1] != NULL) { + return (0); + } + + bzero(&fmt, sizeof (fmt)); + fmt.dtfd_format = rec->dtrd_format; + fmt.dtfd_string = NULL; + fmt.dtfd_length = 0; + + if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1) + return (dt_set_errno(dtp, errno)); + + if ((fmt.dtfd_string = dt_alloc(dtp, fmt.dtfd_length)) == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + + if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1) { + free(fmt.dtfd_string); + return (dt_set_errno(dtp, errno)); + } + + while (rec->dtrd_format > (maxformat = *max)) { + int new_max = maxformat ? (maxformat << 1) : 1; + size_t nsize = new_max * sizeof (void *); + size_t osize = maxformat * sizeof (void *); + void **new_data = dt_zalloc(dtp, nsize); + + if (new_data == NULL) { + dt_free(dtp, fmt.dtfd_string); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + bcopy(*data, new_data, osize); + free(*data); + + *data = new_data; + *max = new_max; + } + + switch (rec->dtrd_action) { + case DTRACEACT_DIFEXPR: + result = fmt.dtfd_string; + break; + case DTRACEACT_PRINTA: + result = dtrace_printa_create(dtp, fmt.dtfd_string); + dt_free(dtp, fmt.dtfd_string); + break; + default: + result = dtrace_printf_create(dtp, fmt.dtfd_string); + dt_free(dtp, fmt.dtfd_string); + break; + } + + if (result == NULL) + return (-1); + + (*data)[rec->dtrd_format - 1] = result; + + return (0); +} + +static int dt_epid_add(dtrace_hdl_t *dtp, dtrace_epid_t id) { dtrace_id_t max; - int rval, i, maxformat; + int rval, i; dtrace_eprobedesc_t *enabled, *nenabled; dtrace_probedesc_t *probe; @@ -132,71 +205,23 @@ dt_epid_add(dtrace_hdl_t *dtp, dtrace_ep } for (i = 0; i < enabled->dtepd_nrecs; i++) { - dtrace_fmtdesc_t fmt; dtrace_recdesc_t *rec = &enabled->dtepd_rec[i]; - if (!DTRACEACT_ISPRINTFLIKE(rec->dtrd_action)) - continue; - - if (rec->dtrd_format == 0) - continue; - - if (rec->dtrd_format <= dtp->dt_maxformat && - dtp->dt_formats[rec->dtrd_format - 1] != NULL) - continue; - - bzero(&fmt, sizeof (fmt)); - fmt.dtfd_format = rec->dtrd_format; - fmt.dtfd_string = NULL; - fmt.dtfd_length = 0; - - if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1) { - rval = dt_set_errno(dtp, errno); - goto err; - } - - if ((fmt.dtfd_string = malloc(fmt.dtfd_length)) == NULL) { - rval = dt_set_errno(dtp, EDT_NOMEM); - goto err; - } - - if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1) { - rval = dt_set_errno(dtp, errno); - free(fmt.dtfd_string); - goto err; - } - - while (rec->dtrd_format > (maxformat = dtp->dt_maxformat)) { - int new_max = maxformat ? (maxformat << 1) : 1; - size_t nsize = new_max * sizeof (void *); - size_t osize = maxformat * sizeof (void *); - void **new_formats = malloc(nsize); - - if (new_formats == NULL) { - rval = dt_set_errno(dtp, EDT_NOMEM); - free(fmt.dtfd_string); + if (DTRACEACT_ISPRINTFLIKE(rec->dtrd_action)) { + if (dt_strdata_add(dtp, rec, &dtp->dt_formats, + &dtp->dt_maxformat) != 0) { + rval = -1; + goto err; + } + } else if (rec->dtrd_action == DTRACEACT_DIFEXPR) { + if (dt_strdata_add(dtp, rec, + (void ***)&dtp->dt_strdata, + &dtp->dt_maxstrdata) != 0) { + rval = -1; goto err; } - - bzero(new_formats, nsize); - bcopy(dtp->dt_formats, new_formats, osize); - free(dtp->dt_formats); - - dtp->dt_formats = new_formats; - dtp->dt_maxformat = new_max; } - dtp->dt_formats[rec->dtrd_format - 1] = - rec->dtrd_action == DTRACEACT_PRINTA ? - dtrace_printa_create(dtp, fmt.dtfd_string) : - dtrace_printf_create(dtp, fmt.dtfd_string); - - free(fmt.dtfd_string); - - if (dtp->dt_formats[rec->dtrd_format - 1] == NULL) { - rval = -1; /* dt_errno is set for us */ - goto err; - } } dtp->dt_pdesc[id] = probe; @@ -440,3 +465,28 @@ dt_aggid_destroy(dtrace_hdl_t *dtp) dtp->dt_aggdesc = NULL; dtp->dt_maxagg = 0; } + +const char * +dt_strdata_lookup(dtrace_hdl_t *dtp, int idx) +{ + if (idx == 0 || idx > dtp->dt_maxstrdata) + return (NULL); + + if (dtp->dt_strdata == NULL) + return (NULL); + + return (dtp->dt_strdata[idx - 1]); +} + +void +dt_strdata_destroy(dtrace_hdl_t *dtp) +{ + int i; + + for (i = 0; i < dtp->dt_maxstrdata; i++) { + free(dtp->dt_strdata[i]); + } + + free(dtp->dt_strdata); + dtp->dt_strdata = NULL; +} Modified: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c ============================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c Mon Mar 25 19:12:36 2013 (r248707) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c Mon Mar 25 20:38:09 2013 (r248708) @@ -22,6 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, Joyent, Inc. All rights reserved. + * Copyright (c) 2011 by Delphix. All rights reserved. */ #include <sys/types.h> @@ -119,8 +120,9 @@ #define DT_VERS_1_7_1 DT_VERSION_NUMBER(1, 7, 1) #define DT_VERS_1_8 DT_VERSION_NUMBER(1, 8, 0) #define DT_VERS_1_8_1 DT_VERSION_NUMBER(1, 8, 1) -#define DT_VERS_LATEST DT_VERS_1_8_1 -#define DT_VERS_STRING "Sun D 1.8.1" +#define DT_VERS_1_9 DT_VERSION_NUMBER(1, 9, 0) +#define DT_VERS_LATEST DT_VERS_1_9 +#define DT_VERS_STRING "Sun D 1.9" const dt_version_t _dtrace_versions[] = { DT_VERS_1_0, /* D API 1.0.0 (PSARC 2001/466) Solaris 10 FCS */ @@ -140,6 +142,7 @@ const dt_version_t _dtrace_versions[] = DT_VERS_1_7_1, /* D API 1.7.1 */ DT_VERS_1_8, /* D API 1.8 */ DT_VERS_1_8_1, /* D API 1.8.1 */ + DT_VERS_1_9, /* D API 1.9 */ 0 }; @@ -357,6 +360,8 @@ static const dt_ident_t _dtrace_globals[ &dt_idops_type, "pid_t" }, { "ppid", DT_IDENT_SCALAR, 0, DIF_VAR_PPID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "pid_t" }, +{ "print", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINT, DT_ATTR_STABCMN, DT_VERS_1_9, + &dt_idops_func, "void(@)" }, { "printa", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTA, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, ...)" }, { "printf", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTF, DT_ATTR_STABCMN, DT_VERS_1_0, @@ -1622,6 +1627,7 @@ dtrace_close(dtrace_hdl_t *dtp) dt_epid_destroy(dtp); dt_aggid_destroy(dtp); dt_format_destroy(dtp); + dt_strdata_destroy(dtp); dt_buffered_destroy(dtp); dt_aggregate_destroy(dtp); free(dtp->dt_buf.dtbd_data); Copied: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c (from r248707, vendor/illumos/dist/lib/libdtrace/common/dt_print.c) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c Mon Mar 25 20:38:09 2013 (r248708, copy of r248707, vendor/illumos/dist/lib/libdtrace/common/dt_print.c) @@ -0,0 +1,648 @@ +/* + * 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. + */ + +/* + * 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 <sys/sysmacros.h> +#include <strings.h> +#include <stdlib.h> +#include <alloca.h> +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <dt_module.h> +#include <dt_printf.h> +#include <dt_string.h> +#include <dt_impl.h> + +/* 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 { + 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 "<ctfid>" 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 + 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 + 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, "<invalid size %u>", (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, "<unknown encoding>"); + 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, "<unknown encoding>"); + } + } +} + +/* + * A pointer is printed as a fixed-size integer. This is used both for + * pointers and functions. + */ +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); + + dt_print_hex(fp, addr, size); +} + +/* + * 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, "<invalid type %lu>", 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; + int value = 0; + + 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, "<forward decl>"); +} + +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; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201303252038.r2PKc9CF058888>