[DTrace-devel] [PATCH v4 1/2] Add support for the print() action
Kris Van Hees
kris.van.hees at oracle.com
Wed Dec 6 19:47:47 UTC 2023
On Thu, Nov 23, 2023 at 10:52:36AM +0000, Alan Maguire wrote:
> print() [1] prints the address, type and associated values of the data pointed
> to by the pointer passed in. For example:
>
> $ /sbin/dtrace -n 'fbt::ip_rcv_core:entry { print((struct sk_buff *)arg0); }'
> dtrace: description 'fbt::ip_rcv_core:entry ' matched 1 probe
> CPU ID FUNCTION:NAME
> 0 75596 ip_output:entry 0xffff89ab58f88200 = *
> (struct sk_buff) {
> (struct) {
> .sk = (struct sock *)0xffff9b7ad5df2f80,
> .ip_defrag_offset = (int)-706793600,
> },
> (struct) {
> .tstamp = (ktime_t)504907694897760,
> .skb_mstamp_ns = (u64)504907694897760,
> },
> .cb = (char [48]) [
> ],
> (struct) {
> (struct) {
> ._skb_refdst = (long unsigned int)18446633550675199489,
> .destructor = (void (*)(*) ())0xffffffffb66c0b80,
> },
> .tcp_tsorted_anchor = (struct list_head) {
> .next = (struct list_head *)0xffff9b7ad9cc4601,
> .prev = (struct list_head *)0xffffffffb66c0b80,
> },
> ._sk_redir = (long unsigned int)18446633550675199489,
> },
> ._nfct = (long unsigned int)18446633547046898499,
> .len = (unsigned int)8788,
> .data_len = (unsigned int)8736,
> .hdr_len = (__u16)320,
> .cloned = (__u8)1,
> .ip_summed = (__u8)2,
>
> ...etc.
>
> Zero-valued fields are skipped to improve compactness of the representation.
> Since some data structures are large, the dynamic "printsize" option can be
> used to restrict the amount of data displayed; in such cases a "..." connotes
> the fact that additional data was present but not shown.
>
> User-defined structures can also be printed:
>
> CPU ID FUNCTION:NAME
> 0 75596 ip_output:entry 0xffff89ab58f88200 = *
> (struct foo) {
> .a = (int)2,
> }
>
> Signed-off-by: Alan Maguire <alan.maguire at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>
I'll add it to dev.
> [1] https://docs.oracle.com/en/operating-systems/solaris/oracle-solaris/11.4/dtrace-guide/print-action.html#GUID-533E6BD9-8DE8-474E-9770-96F84244911C
> ---
> include/dtrace/actions_defines.h | 1 +
> include/dtrace/options_defines.h | 3 +-
> libdtrace/dt_cg.c | 48 +++++
> libdtrace/dt_consume.c | 26 +++
> libdtrace/dt_errtags.h | 3 +-
> libdtrace/dt_impl.h | 6 +-
> libdtrace/dt_open.c | 2 +
> libdtrace/dt_options.c | 25 +++
> libdtrace/dt_printf.c | 352 ++++++++++++++++++++++++++++++-
> libdtrace/dt_printf.h | 2 +
> 10 files changed, 463 insertions(+), 5 deletions(-)
>
> diff --git a/include/dtrace/actions_defines.h b/include/dtrace/actions_defines.h
> index f161df7c..4ff4fb83 100644
> --- a/include/dtrace/actions_defines.h
> +++ b/include/dtrace/actions_defines.h
> @@ -37,6 +37,7 @@
> #define DTRACEACT_LIBACT 5 /* library-controlled action */
> #define DTRACEACT_TRACEMEM 6 /* tracemem() action */
> #define DTRACEACT_PCAP 7 /* pcap() action */
> +#define DTRACEACT_PRINT 8 /* print() action */
>
> #define DTRACEACT_PROC 0x0100
> #define DTRACEACT_USTACK (DTRACEACT_PROC + 1)
> diff --git a/include/dtrace/options_defines.h b/include/dtrace/options_defines.h
> index 61a23f42..80246be8 100644
> --- a/include/dtrace/options_defines.h
> +++ b/include/dtrace/options_defines.h
> @@ -62,7 +62,8 @@
> #define DTRACEOPT_BPFLOG 32 /* always output BPF verifier log */
> #define DTRACEOPT_SCRATCHSIZE 33 /* max scratch size permitted */
> #define DTRACEOPT_LOCKMEM 34 /* max locked memory */
> -#define DTRACEOPT_MAX 35 /* number of options */
> +#define DTRACEOPT_PRINTSIZE 35 /* max # bytes printed by print() action */
> +#define DTRACEOPT_MAX 36 /* number of options */
>
> #define DTRACEOPT_UNSET (dtrace_optval_t)-2 /* unset option */
>
> diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
> index 581ada4e..9c5bb2cf 100644
> --- a/libdtrace/dt_cg.c
> +++ b/libdtrace/dt_cg.c
> @@ -2560,6 +2560,53 @@ dt_cg_act_ustack(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
> BPF_NOP());
> }
>
> +static void
> +dt_cg_act_print(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
> +{
> + uint_t addr_off, data_off;
> + dt_node_t *addr = dnp->dn_args;
> + dt_irlist_t *dlp = &pcb->pcb_ir;
> + dt_regset_t *drp = pcb->pcb_regs;
> + dtrace_hdl_t *dtp = pcb->pcb_hdl;
> + ctf_file_t *fp = addr->dn_ctfp;
> + ctf_id_t type = addr->dn_type;
> + char n[DT_TYPE_NAMELEN];
> + size_t size;
> +
> + type = ctf_type_reference(fp, type);
> + if (type == CTF_ERR)
> + longjmp(yypcb->pcb_jmpbuf, EDT_CTF);
> + size = ctf_type_size(fp, type);
> + if (size == 0)
> + dnerror(addr, D_PRINT_SIZE,
> + "print( ) argument #1 reference has type '%s' with size 0; cannot print( ) it.\n",
> + ctf_type_name(fp, type, n, sizeof(n)));
> +
> + /* reserve space for addr/type, data/size */
> + addr_off = dt_rec_add(dtp, dt_cg_fill_gap, DTRACEACT_PRINT,
> + sizeof(uint64_t), 8, NULL, type);
> + data_off = dt_rec_add(dtp, dt_cg_fill_gap, DTRACEACT_PRINT,
> + size, 8, NULL, size);
> +
> + dt_cg_node(addr, &pcb->pcb_ir, drp);
> + dt_cg_check_ptr_arg(dlp, drp, addr, NULL);
> +
> + /* store address for later print()ing */
> + emit(dlp, BPF_STORE(BPF_DW, BPF_REG_9, addr_off, addr->dn_reg));
> +
> + if (dt_regset_xalloc_args(drp) == -1)
> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> + emit(dlp, BPF_MOV_REG(BPF_REG_3, addr->dn_reg));
> + dt_regset_free(drp, addr->dn_reg);
> + emit(dlp, BPF_MOV_REG(BPF_REG_1, BPF_REG_9));
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, data_off));
> + emit(dlp, BPF_MOV_IMM(BPF_REG_2, size));
> + dt_regset_xalloc(drp, BPF_REG_0);
> + emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read));
> + dt_regset_free_args(drp);
> + dt_regset_free(drp, BPF_REG_0);
> +}
> +
> typedef void dt_cg_action_f(dt_pcb_t *, dt_node_t *, dtrace_actkind_t);
>
> typedef struct dt_cg_actdesc {
> @@ -2615,6 +2662,7 @@ static const dt_cg_actdesc_t _dt_cg_actions[DT_ACT_MAX] = {
> DTRACEACT_UADDR },
> [DT_ACT_IDX(DT_ACT_SETOPT)] = { &dt_cg_act_setopt, },
> [DT_ACT_IDX(DT_ACT_PCAP)] = { &dt_cg_act_pcap, },
> + [DT_ACT_IDX(DT_ACT_PRINT)] = { &dt_cg_act_print, },
> };
>
> dt_irnode_t *
> diff --git a/libdtrace/dt_consume.c b/libdtrace/dt_consume.c
> index 7e2b1005..343d745f 100644
> --- a/libdtrace/dt_consume.c
> +++ b/libdtrace/dt_consume.c
> @@ -14,6 +14,7 @@
> #include <ctype.h>
> #include <alloca.h>
> #include <dt_impl.h>
> +#include <dt_module.h>
> #include <dt_pcap.h>
> #include <dt_peb.h>
> #include <dt_state.h>
> @@ -1971,6 +1972,25 @@ dt_print_trace(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec,
> return dt_print_rawbytes(dtp, fp, data, rec->dtrd_size);
> }
>
> +static int
> +dt_print_print(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec,
> + const caddr_t buf)
> +{
> + dtrace_recdesc_t *data_rec = rec + 1;
> + size_t max_size = dtp->dt_options[DTRACEOPT_PRINTSIZE];
> + size_t size = (size_t)data_rec->dtrd_arg;
> + uint64_t printaddr;
> +
> + if (size > max_size)
> + size = max_size;
> +
> + if (dt_read_scalar(buf, rec, &printaddr) < 0)
> + return dt_set_errno(dtp, EDT_PRINT);
> +
> + return dt_print_type(dtp, fp, printaddr, (ctf_id_t)rec->dtrd_arg,
> + (caddr_t)buf + data_rec->dtrd_offset, size);
> +}
> +
> /*
> * The lifecycle of speculation buffers is as follows:
> *
> @@ -2530,6 +2550,12 @@ dt_consume_one_probe(dtrace_hdl_t *dtp, FILE *fp, char *data, uint32_t size,
> case DTRACEACT_FREOPEN:
> func = dtrace_freopen;
> break;
> + case DTRACEACT_PRINT:
> + n = dt_print_print(dtp, fp, rec, data);
> + if (n < 0)
> + return -1;
> + i += n - 1;
> + continue;
> default:
> break;
> }
> diff --git a/libdtrace/dt_errtags.h b/libdtrace/dt_errtags.h
> index 86c9bd4e..b8648d17 100644
> --- a/libdtrace/dt_errtags.h
> +++ b/libdtrace/dt_errtags.h
> @@ -230,7 +230,8 @@ typedef enum {
> D_LLQUANT_MATCHSTEPS, /* llquantize() mismatch on steps */
> D_PCAP_ADDR, /* pcap() address bad type */
> D_PCAP_PROTO, /* pcap() prototype mismatch */
> - D_PCAP_SIZE /* pcap() bad size */
> + D_PCAP_SIZE, /* pcap() bad size */
> + D_PRINT_SIZE, /* print() address target bad size */
> } dt_errtag_t;
>
> extern const char *dt_errtag(dt_errtag_t);
> diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
> index 98a74910..5c429946 100644
> --- a/libdtrace/dt_impl.h
> +++ b/libdtrace/dt_impl.h
> @@ -585,8 +585,9 @@ struct dtrace_hdl {
> #define DT_ACT_UADDR DT_ACT(27) /* uaddr() action */
> #define DT_ACT_SETOPT DT_ACT(28) /* setopt() action */
> #define DT_ACT_PCAP DT_ACT(29) /* pcap() action */
> +#define DT_ACT_PRINT DT_ACT(30) /* print() action */
>
> -#define DT_ACT_MAX 30
> +#define DT_ACT_MAX 31
>
> /*
> * Aggregation functions.
> @@ -703,7 +704,8 @@ enum {
> EDT_OBJIO, /* cannot read object file or module name mapping */
> EDT_READMAXSTACK, /* cannot read kernel param perf_event_max_stack */
> EDT_TRACEMEM, /* missing or corrupt tracemem() record */
> - EDT_PCAP /* missing or corrupt pcap() record */
> + EDT_PCAP, /* missing or corrupt pcap() record */
> + EDT_PRINT, /* missing or corrupt print() record */
> };
>
> /*
> diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
> index b521d887..1ec165fc 100644
> --- a/libdtrace/dt_open.c
> +++ b/libdtrace/dt_open.c
> @@ -263,6 +263,8 @@ static const dt_ident_t _dtrace_globals[] = {
> &dt_idops_func, "void(@, ...)" },
> { "printf", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTF, DT_ATTR_STABCMN, DT_VERS_1_0,
> &dt_idops_func, "void(string, ...)" },
> +{ "print", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINT, DT_ATTR_STABCMN, DT_VERS_2_0,
> + &dt_idops_func, "void(void *)" },
> { "probefunc", DT_IDENT_SCALAR, 0, DIF_VAR_PROBEFUNC,
> DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" },
> { "probemod", DT_IDENT_SCALAR, 0, DIF_VAR_PROBEMOD,
> diff --git a/libdtrace/dt_options.c b/libdtrace/dt_options.c
> index 33995a6f..6f7cf9e2 100644
> --- a/libdtrace/dt_options.c
> +++ b/libdtrace/dt_options.c
> @@ -915,6 +915,30 @@ dt_opt_strsize(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
> return 0;
> }
>
> +#define DT_PRINT_DEF_SIZE 8192
> +
> +static int
> +dt_opt_printsize(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
> +{
> + dtrace_optval_t val = DT_PRINT_DEF_SIZE;
> + int rval;
> +
> + if (arg != NULL) {
> + rval = dt_opt_size(dtp, arg, option);
> +
> + if (rval != 0)
> + return rval;
> +
> + val = dtp->dt_options[option];
> +
> + if (val <= 0 || val > 65536)
> + val = DT_PRINT_DEF_SIZE;
> + }
> + dtp->dt_options[option] = P2ROUNDUP(val, 8);
> +
> + return 0;
> +}
> +
> static const struct {
> const char *dtbp_name;
> int dtbp_policy;
> @@ -1178,6 +1202,7 @@ static const dt_option_t _dtrace_drtoptions[] = {
> { "rawbytes", dt_opt_runtime, DTRACEOPT_RAWBYTES },
> { "stackindent", dt_opt_runtime, DTRACEOPT_STACKINDENT },
> { "switchrate", dt_opt_rate, DTRACEOPT_SWITCHRATE },
> + { "printsize", dt_opt_printsize, DTRACEOPT_PRINTSIZE },
> { NULL }
> };
>
> diff --git a/libdtrace/dt_printf.c b/libdtrace/dt_printf.c
> index 6ee317e6..409a34df 100644
> --- a/libdtrace/dt_printf.c
> +++ b/libdtrace/dt_printf.c
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -1914,3 +1914,353 @@ dtrace_fprinta(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata,
>
> return i;
> }
> +
> +#define DT_PRINT_DEPTH 10
> +
> +struct dt_visit_arg {
> + dtrace_hdl_t *dv_dtp;
> + FILE *dv_fp;
> + ctf_file_t *dv_ctfp;
> + void *dv_data;
> + uint64_t dv_size;
> + int dv_kinds[DT_PRINT_DEPTH];
> + int dv_last_depth;
> + int dv_startindent;
> +};
> +
> +#define DT_NAME_LEN 32
> +#define DT_PRINT_LEN 80
> +
> +#define DT_PRINT_STARTINDENT 44
> +
> +/* used to compare nested objects like structs/unions/arrays to see if they are 0. */
> +static char zeros[65536] = { 0 };
> +
> +static int
> +dt_print_close_parens(struct dt_visit_arg *dva, int depth)
> +{
> + int i;
> +
> + switch (dva->dv_kinds[depth]) {
> + case CTF_K_STRUCT:
> + case CTF_K_UNION:
> + /* if struct/union contents were 0, we still opened the
> + * struct "{" at the top level, even if we did not
> + * display any members, so need to close it. An empty
> + * struct looks like this:
> + * (struct foo) {
> + * }
> + *
> + * ...at the top level, but at any other depth of nesting
> + * it is not displayed at all.
> + */
> + if (depth == 0 && dva->dv_last_depth == 0)
> + dva->dv_last_depth = 1;
> + /* add a "}" for each level needed between current depth
> + * and last depth we displayed data at.
> + */
> + for (i = dva->dv_last_depth - depth; i > 0; i--) {
> + if (dt_printf(dva->dv_dtp, dva->dv_fp, "%*s}%s\n",
> + dva->dv_startindent + depth + i - 1, "",
> + (depth > 0 || i > 1) ? "," : "") < 0)
> + return -1;
> + dva->dv_last_depth = depth;
> + }
> + break;
> + }
> + return 0;
> +}
> +
> +static int
> +dt_print_visit(const char *name, ctf_id_t type, unsigned long offset,
> + int depth, void *arg)
> +{
> + struct dt_visit_arg *dva = arg;
> + const void *data = (char *)dva->dv_data + offset/NBBY;
> + const char *membername = NULL;
> + ssize_t typesize, asize = 0;
> + char *typename = NULL;
> + char *datastr = NULL;
> + int64_t intdata = 0;
> + const char *ename;
> + ctf_encoding_t e;
> + ctf_arinfo_t a;
> + ctf_id_t rtype;
> + size_t typelen;
> + int issigned;
> + int ret = 0;
> + int rkind;
> + int i;
> +
> + /* run out of data to display, or hit depth limit? */
> + if ((offset/NBBY) > dva->dv_size || depth >= DT_PRINT_DEPTH) {
> + dt_printf(dva->dv_dtp, dva->dv_fp, "%*s...\n",
> + dva->dv_startindent + depth, "");
> + return -1;
> + }
> + typename = ctf_type_aname(dva->dv_ctfp, type);
> + if (!typename) {
> + dt_dprintf("error retrieving type name for [%ld]: %s\n",
> + type, ctf_errmsg(ctf_errno(dva->dv_ctfp)));
> + return dt_set_errno(dva->dv_dtp, EDT_CTF);
> + }
> + rtype = ctf_type_resolve(dva->dv_ctfp, type);
> + if (rtype == CTF_ERR) {
> + dt_dprintf("error resolving type [%s]: %s\n", typename,
> + ctf_errmsg(ctf_errno(dva->dv_ctfp)));
> + ret = dt_set_errno(dva->dv_dtp, EDT_CTF);
> + goto err;
> + }
> + rkind = ctf_type_kind(dva->dv_ctfp, rtype);
> +
> + /* we may have just shown the last member in a struct/union
> + * at depth + 1 (or deeper than that). If so close the "{".
> + * The final parenthesis close (at depth 0) will be done
> + * by the caller.
> + */
> + if (dva->dv_last_depth > depth)
> + dt_print_close_parens(dva, depth);
> +
> + /* zeroed data? if so, do not display it. */
> + if (depth > 0) {
> + typesize = ctf_type_size(dva->dv_ctfp, rtype);
> + if (typesize < 0) {
> + dt_dprintf("error retrieving type size for [%s]: %s\n",
> + typename, ctf_errmsg(ctf_errno(dva->dv_ctfp)));
> + ret = dt_set_errno(dva->dv_dtp, EDT_CTF);
> + goto err;
> + }
> + if (typesize < sizeof(zeros) && memcmp(data, zeros, typesize) == 0)
> + return 0;
> + }
> +
> + dva->dv_kinds[depth] = rkind;
> + dva->dv_last_depth = depth;
> +
> + /* anon struct/union/enum will be "struct "; remove the space. */
> + typelen = strlen(typename);
> + if (typename[typelen - 1] == ' ')
> + typename[typelen - 1] = '\0';
> +
> + /* print .<member_name> where available */
> + if (depth > 0) {
> + if (name && strlen(name) > 0)
> + membername = name;
> + }
> +
> + switch (rkind) {
> + case CTF_K_STRUCT:
> + case CTF_K_UNION:
> + ret = asprintf(&datastr, "(%s) {", typename);
> + break;
> + case CTF_K_ARRAY:
> + ret = ctf_array_info(dva->dv_ctfp, rtype, &a);
> + if (ret < 0) {
> + dt_dprintf("error retrieving array info for [%s]: %s\n",
> + typename, ctf_errmsg(ctf_errno(dva->dv_ctfp)));
> + ret = dt_set_errno(dva->dv_dtp, EDT_CTF);
> + goto err;
> + }
> + asize = ctf_type_size(dva->dv_ctfp, a.ctr_contents);
> + if (asize < 0) {
> + dt_dprintf("error retrieving type size for array contents in [%s]: %s\n",
> + typename, ctf_errmsg(ctf_errno(dva->dv_ctfp)));
> + ret = dt_set_errno(dva->dv_dtp, EDT_CTF);
> + goto err;
> + }
> + ret = asprintf(&datastr, "(%s) [%s", typename,
> + a.ctr_nelems == 0 ? "]," : "");
> + /* array member processing will be handled below... */
> + break;
> + case CTF_K_INTEGER:
> + ret = ctf_type_encoding(dva->dv_ctfp, rtype, &e);
> + if (ret < 0) {
> + dt_dprintf("error retrieving type encoding for [%s]: %s\n",
> + typename, ctf_errmsg(ctf_errno(dva->dv_ctfp)));
> + ret = dt_set_errno(dva->dv_dtp, EDT_CTF);
> + goto err;
> + }
> + issigned = (e.cte_format & CTF_INT_SIGNED) != 0;
> + switch (e.cte_bits) {
> + case 8:
> + if (issigned)
> + intdata = (int64_t)*(char *)data;
> + else
> + intdata = (int64_t)*(unsigned char *)data;
> + break;
> + case 16:
> + if (issigned)
> + intdata = (int64_t)*(int16_t *)data;
> + else
> + intdata = (int64_t)*(uint16_t *)data;
> + break;
> + case 32:
> + if (issigned)
> + intdata = (int64_t)*(int32_t *)data;
> + else
> + intdata = (int64_t)*(uint32_t *)data;
> + break;
> + case 64:
> + if (issigned) {
> + intdata = (int64_t)*(int64_t *)data;
> + } else {
> + /* cannot use intdata (int64_t) to hold uin64_t;
> + * stringify the cast and data here instead.
> + */
> + ret = asprintf(&datastr, "(%s)%lu,",
> + typename, *(uint64_t *)data);
> + goto doprint;
> + }
> + break;
> + default:
> + if (e.cte_bits <= 64) {
> + unsigned int lshift, rshift;
> + uint64_t bitdata = *(uint64_t *)((char *)dva->dv_data +
> + (offset + e.cte_offset)/NBBY);
> + /* shift bitfield data left then right to
> + * eliminate unneeded bits.
> + */
> + lshift = 63 - ((offset + e.cte_offset) % 64);
> + rshift = 64 - e.cte_bits;
> + bitdata = bitdata << lshift;
> + bitdata = bitdata >> rshift;
> + intdata = (int64_t)bitdata;
> + /* zero-value bitfield; do not display */
> + if (intdata == 0)
> + return 0;
> + } else {
> + /* > 64 bit integer vals not supported. */
> + ret = asprintf(&datastr, "(%s)?(%d bit value),",
> + typename, e.cte_bits);
> + goto doprint;
> + }
> + break;
> + }
> + if ((e.cte_format & CTF_INT_CHAR) != 0 &&
> + strcmp(typename, "char") == 0) {
> + ret = asprintf(&datastr, "(%s)'%c',",
> + typename, (char)intdata);
> + } else {
> + ret = asprintf(&datastr, "(%s)%ld,",
> + typename, intdata);
> + }
> + break;
> + case CTF_K_ENUM:
> + ename = ctf_enum_name(dva->dv_ctfp, rtype, *(int *)data);
> + if (ename != NULL) {
> + /* exception to printing 0-valued data here;
> + * enum values are often meaningful with 0
> + * value so display the enum value string.
> + */
> + ret = asprintf(&datastr, "(%s)%s,",
> + typename, ename);
> + } else {
> + ret = asprintf(&datastr, "(%s)%d,",
> + typename, *(int *)data);
> + }
> + break;
> + case CTF_K_POINTER:
> + ret = asprintf(&datastr, "(%s)0x%lx,",
> + typename, (uintptr_t)*(void **)data);
> + break;
> + default:
> + free(typename);
> + return 0;
> + }
> +doprint:
> + if (ret < 0) {
> + /* asprintf() failed... */
> + ret = dt_set_errno(dva->dv_dtp, EDT_NOMEM);
> + goto err;
> + }
> + free(typename);
> +
> + /* format is
> + * [.<membername> = ](<type>)value,
> + * For example
> + * .foo = (bar)1,
> + */
> + ret = dt_printf(dva->dv_dtp, dva->dv_fp, "%*s%s%s%s%s\n",
> + dva->dv_startindent + depth, "",
> + membername != NULL ? "." : "",
> + membername != NULL ? membername : "",
> + membername != NULL ? " = " : "",
> + datastr);
> + free(datastr);
> + if (ret < 0)
> + return ret;
> +
> + /* type visit does not recurse into array elements... */
> + if (rkind == CTF_K_ARRAY) {
> + struct dt_visit_arg dva2;
> + int ischar = 0;
> +
> + dva2.dv_dtp = dva->dv_dtp;
> + dva2.dv_ctfp = dva->dv_ctfp;
> + dva2.dv_fp = dva->dv_fp;
> + dva2.dv_data = dva->dv_data;
> + dva2.dv_startindent = dva->dv_startindent;
> + if (ctf_type_encoding(dva->dv_ctfp, a.ctr_contents, &e) < 0) {
> + dt_dprintf("error retrieving type encoding for array contents in [%ld]: %s\n",
> + a.ctr_contents,
> + ctf_errmsg(ctf_errno(dva->dv_ctfp)));
> + return dt_set_errno(dva->dv_dtp, EDT_CTF);
> + }
> + /* is array all 0s? Then skip... */
> + if (memcmp(data, zeros, asize * a.ctr_nelems) == 0)
> + return 0;
> + /* we check for early '\0' in char arrays... */
> + if ((e.cte_format & CTF_INT_CHAR) != 0 && e.cte_bits == 8)
> + ischar = 1;
> +
> + for (i = 0; i < a.ctr_nelems; i++) {
> + int aoffset = (asize * 8) * i;
> +
> + if (ischar && *(char *)((char *)data + i) == '\0')
> + break;
> + if (dt_print_visit(NULL, a.ctr_contents,
> + offset + aoffset,
> + depth + 1, &dva2) < 0)
> + return -1;
> + }
> + if (a.ctr_nelems > 0) {
> + if (dt_printf(dva->dv_dtp, dva->dv_fp, "%*s],\n",
> + dva->dv_startindent + depth, "") < 0)
> + return -1;
> + }
> + }
> + return 0;
> +err:
> + free(typename);
> + free(datastr);
> + return ret;
> +}
> +
> +int
> +dt_print_type(dtrace_hdl_t *dtp, FILE *fp, uint64_t printaddr,
> + ctf_id_t type, caddr_t data, size_t size)
> +{
> + struct dt_visit_arg dva;
> +
> + dva.dv_dtp = dtp;
> + dva.dv_fp = fp;
> + dva.dv_ctfp = dtp->dt_ddefs->dm_ctfp;
> + dva.dv_data = data;
> + dva.dv_size = size;
> + dva.dv_last_depth = 0;
> + dva.dv_startindent = DT_PRINT_STARTINDENT;
> +
> + /* print address as * to type; type display will start on the
> + * next line.
> + */
> + if (dt_printf(dtp, fp, "%p = *\n", (void *)printaddr) < 0)
> + return -1;
> +
> + ctf_type_visit(dva.dv_ctfp, type, dt_print_visit, &dva);
> +
> + /* may need to close top-level parenthesis for struct/union. */
> + if (dt_print_close_parens(&dva, 0) < 0)
> + return -1;
> +
> + return 2;
> +}
> diff --git a/libdtrace/dt_printf.h b/libdtrace/dt_printf.h
> index c274066f..9771c4a8 100644
> --- a/libdtrace/dt_printf.h
> +++ b/libdtrace/dt_printf.h
> @@ -108,6 +108,8 @@ extern int dt_print_ustack(dtrace_hdl_t *, FILE *,
> const char *, caddr_t, uint64_t);
> extern int dt_print_mod(dtrace_hdl_t *, FILE *, const char *, caddr_t);
> extern int dt_print_umod(dtrace_hdl_t *, FILE *, const char *, caddr_t);
> +extern int dt_print_type(dtrace_hdl_t *, FILE *, uint64_t, ctf_id_t,
> + caddr_t, size_t);
>
> #ifdef __cplusplus
> }
> --
> 2.39.3
>
>
More information about the DTrace-devel
mailing list