Changeset View
Standalone View
cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c
| Show All 21 Lines | |||||
| * Copyright 2009 Sun Microsystems, Inc. All rights reserved. | * Copyright 2009 Sun Microsystems, Inc. All rights reserved. | ||||
| * Use is subject to license terms. | * Use is subject to license terms. | ||||
| */ | */ | ||||
| /* | /* | ||||
| * Copyright (c) 2011 by Delphix. All rights reserved. | * Copyright (c) 2011 by Delphix. All rights reserved. | ||||
| */ | */ | ||||
| /* | /* | ||||
| * Copyright (c) 2013, Joyent, Inc. All rights reserved. | * Copyright (c) 2013, Joyent, Inc. All rights reserved. | ||||
| * Copyright (c) 2023, Domagoj Stolfa. All rights reserved. | |||||
| */ | */ | ||||
| /* | /* | ||||
| * DTrace print() action | * DTrace print() action | ||||
| * | * | ||||
| * This file contains the post-processing logic for the print() action. The | * This file contains the post-processing logic for the print() action. The | ||||
| * print action behaves identically to trace() in that it generates a | * print action behaves identically to trace() in that it generates a | ||||
| * DTRACEACT_DIFEXPR action, but the action argument field refers to a CTF type | * DTRACEACT_DIFEXPR action, but the action argument field refers to a CTF type | ||||
| Show All 39 Lines | |||||
| #include <netdb.h> | #include <netdb.h> | ||||
| #include <netinet/in.h> | #include <netinet/in.h> | ||||
| #include <arpa/inet.h> | #include <arpa/inet.h> | ||||
| #include <dt_module.h> | #include <dt_module.h> | ||||
| #include <dt_printf.h> | #include <dt_printf.h> | ||||
| #include <dt_string.h> | #include <dt_string.h> | ||||
| #include <dt_impl.h> | #include <dt_impl.h> | ||||
| #include <dt_oformat.h> | |||||
| /* determines whether the given integer CTF encoding is a character */ | /* determines whether the given integer CTF encoding is a character */ | ||||
| #define CTF_IS_CHAR(e) \ | #define CTF_IS_CHAR(e) \ | ||||
| (((e).cte_format & (CTF_INT_CHAR | CTF_INT_SIGNED)) == \ | (((e).cte_format & (CTF_INT_CHAR | CTF_INT_SIGNED)) == \ | ||||
| (CTF_INT_CHAR | CTF_INT_SIGNED) && (e).cte_bits == NBBY) | (CTF_INT_CHAR | CTF_INT_SIGNED) && (e).cte_bits == NBBY) | ||||
| /* determines whether the given CTF kind is a struct or union */ | /* determines whether the given CTF kind is a struct or union */ | ||||
| #define CTF_IS_STRUCTLIKE(k) \ | #define CTF_IS_STRUCTLIKE(k) \ | ||||
| ((k) == CTF_K_STRUCT || (k) == CTF_K_UNION) | ((k) == CTF_K_STRUCT || (k) == CTF_K_UNION) | ||||
| /* | /* | ||||
| * Print structure passed down recursively through printing algorithm. | * Print structure passed down recursively through printing algorithm. | ||||
| */ | */ | ||||
| typedef struct dt_printarg { | typedef struct dt_printarg { | ||||
| dtrace_hdl_t *pa_dtp; /* libdtrace handle */ | dtrace_hdl_t *pa_dtp; /* libdtrace handle */ | ||||
| caddr_t pa_addr; /* base address of trace data */ | caddr_t pa_addr; /* base address of trace data */ | ||||
| ctf_file_t *pa_ctfp; /* CTF container */ | ctf_file_t *pa_ctfp; /* CTF container */ | ||||
| int pa_depth; /* member depth */ | int pa_depth; /* member depth */ | ||||
| int pa_nest; /* nested array depth */ | int pa_nest; /* nested array depth */ | ||||
| FILE *pa_file; /* output file */ | FILE *pa_file; /* output file */ | ||||
| const char *pa_object; /* object name */ | |||||
| } dt_printarg_t; | } dt_printarg_t; | ||||
| static int dt_format_member(const char *, ctf_id_t, ulong_t, int, void *); | |||||
| static int dt_print_member(const char *, ctf_id_t, ulong_t, int, void *); | 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 | * Safe version of ctf_type_name() that will fall back to just "<ctfid>" if it | ||||
| * can't resolve the type. | * can't resolve the type. | ||||
| */ | */ | ||||
| static void | static void | ||||
| dt_print_type_name(ctf_file_t *ctfp, ctf_id_t id, char *buf, size_t buflen) | dt_print_type_name(ctf_file_t *ctfp, ctf_id_t id, char *buf, size_t buflen) | ||||
| ▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | #endif | ||||
| /* | /* | ||||
| * If the bits we want do not begin on a byte boundary, shift the data | * 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'. | * right so that the value is in the lowest 'cte_bits' of 'value'. | ||||
| */ | */ | ||||
| if (off % NBBY != 0) | if (off % NBBY != 0) | ||||
| value >>= shift; | value >>= shift; | ||||
| value &= mask; | value &= mask; | ||||
| (void) fprintf(fp, "%#llx", (u_longlong_t)value); | xo_emit("{:value/%#llx}", (u_longlong_t)value); | ||||
| /* Flush in order to ensure output is aligned properly */ | |||||
| xo_flush(); | |||||
| } | } | ||||
| /* | /* | ||||
| * Dump the contents of memory as a fixed-size integer in hex. | * Dump the contents of memory as a fixed-size integer in hex. | ||||
| */ | */ | ||||
| static void | static void | ||||
| dt_print_hex(FILE *fp, caddr_t addr, size_t size) | dt_print_hex(FILE *fp, caddr_t addr, size_t size) | ||||
| { | { | ||||
| switch (size) { | switch (size) { | ||||
| case sizeof (uint8_t): | case sizeof (uint8_t): | ||||
| (void) fprintf(fp, "%#x", *(uint8_t *)addr); | xo_emit("{:value/%#x}", *(uint8_t *)addr); | ||||
| break; | break; | ||||
| case sizeof (uint16_t): | case sizeof (uint16_t): | ||||
| /* LINTED - alignment */ | xo_emit("{:value/%#x}", *(uint16_t *)addr); | ||||
| (void) fprintf(fp, "%#x", *(uint16_t *)addr); | |||||
| break; | break; | ||||
| case sizeof (uint32_t): | case sizeof (uint32_t): | ||||
| /* LINTED - alignment */ | xo_emit("{:value/%#x}", *(uint32_t *)addr); | ||||
| (void) fprintf(fp, "%#x", *(uint32_t *)addr); | |||||
| break; | break; | ||||
| case sizeof (uint64_t): | case sizeof (uint64_t): | ||||
| (void) fprintf(fp, "%#llx", | xo_emit("{:value/%#llx}", | ||||
| /* LINTED - alignment */ | |||||
| (unsigned long long)*(uint64_t *)addr); | (unsigned long long)*(uint64_t *)addr); | ||||
| break; | break; | ||||
| default: | default: | ||||
| (void) fprintf(fp, "<invalid size %u>", (uint_t)size); | xo_emit("<{:warning} {:size/%u}>", "invalid size", | ||||
| (uint_t)size); | |||||
| } | } | ||||
| /* Flush in order to ensure output is aligned properly */ | |||||
| xo_flush(); | |||||
| } | } | ||||
| /* | /* | ||||
| * Print an integer type. Before dumping the contents via dt_print_hex(), we | * 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. | * first check the encoding to see if it's part of a bitfield or a character. | ||||
| */ | */ | ||||
| static void | static void | ||||
| dt_print_int(ctf_id_t base, ulong_t off, dt_printarg_t *pap) | dt_print_int(ctf_id_t base, ulong_t off, dt_printarg_t *pap) | ||||
| { | { | ||||
| FILE *fp = pap->pa_file; | FILE *fp = pap->pa_file; | ||||
| ctf_file_t *ctfp = pap->pa_ctfp; | ctf_file_t *ctfp = pap->pa_ctfp; | ||||
| dtrace_hdl_t *dtp = pap->pa_dtp; | |||||
| ctf_encoding_t e; | ctf_encoding_t e; | ||||
| size_t size; | size_t size; | ||||
| caddr_t addr = pap->pa_addr + off / NBBY; | caddr_t addr = pap->pa_addr + off / NBBY; | ||||
| if (ctf_type_encoding(ctfp, base, &e) == CTF_ERR) { | if (ctf_type_encoding(ctfp, base, &e) == CTF_ERR) { | ||||
| (void) fprintf(fp, "<unknown encoding>"); | xo_emit("<{:warning}>", "unknown encoding"); | ||||
| /* Flush in order to ensure output is aligned properly */ | |||||
| xo_flush(); | |||||
| return; | return; | ||||
phil: This isn't a "value"; it should be an error or something. Maybe something like:
xo_emit… | |||||
Done Inline ActionsI assume this would make sense to also conflate with the fprintf below (same with all the other examples of this pattern)? domagoj.stolfa_gmail.com: I assume this would make sense to also conflate with the `fprintf` below (same with all the… | |||||
Done Inline ActionsYup, like the other chuck I sent with the "<" and ">": xo_emit("<{:message}>", "unknown encoding");Would "{:warning}" be more clear that something's corrupted? phil: Yup, like the other chuck I sent with the "<" and ">":
xo_emit("<{:message}>", "unknown… | |||||
| } | } | ||||
| /* | /* | ||||
| * This comes from MDB - it's not clear under what circumstances this | * This comes from MDB - it's not clear under what circumstances this | ||||
| * would be found. | * would be found. | ||||
| */ | */ | ||||
| if (e.cte_format & CTF_INT_VARARGS) { | if (e.cte_format & CTF_INT_VARARGS) { | ||||
| if (!dtp->dt_oformat) | |||||
| (void) fprintf(fp, "..."); | (void)fprintf(fp, "..."); | ||||
| return; | return; | ||||
| } | } | ||||
| /* | /* | ||||
| * We print this as a bitfield if the bit encoding indicates it's not | * 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. | * an even power of two byte size, or is larger than 8 bytes. | ||||
| */ | */ | ||||
| size = e.cte_bits / NBBY; | size = e.cte_bits / NBBY; | ||||
| if (size > 8 || (e.cte_bits % NBBY) != 0 || (size & (size - 1)) != 0) { | if (size > 8 || (e.cte_bits % NBBY) != 0 || (size & (size - 1)) != 0) { | ||||
| print_bitfield(pap, off, &e); | print_bitfield(pap, off, &e); | ||||
| return; | return; | ||||
| } | } | ||||
| /* | /* | ||||
| * If this is a character, print it out as such. | * If this is a character, print it out as such. | ||||
| */ | */ | ||||
| if (CTF_IS_CHAR(e)) { | if (CTF_IS_CHAR(e)) { | ||||
| char c = *(char *)addr; | char c = *(char *)addr; | ||||
| if (isprint(c)) | if (isprint(c)) | ||||
| (void) fprintf(fp, "'%c'", c); | xo_emit("'{:value/%c}'", c); | ||||
| else if (c == 0) | else if (c == 0) | ||||
| (void) fprintf(fp, "'\\0'"); | xo_emit("'\\{:value/0}'"); | ||||
| else | else | ||||
| (void) fprintf(fp, "'\\%03o'", c); | xo_emit("'\\{:value/%03o}'", c); | ||||
| /* Flush in order to ensure output is aligned properly */ | |||||
| xo_flush(); | |||||
| return; | return; | ||||
| } | } | ||||
Done Inline ActionsThe quotes shouldn't be part of the value; they can be outside it: xo_emit("'{:value/%c}'", c);phil: The quotes shouldn't be part of the value; they can be outside it:
xo_emit("'{… | |||||
| dt_print_hex(fp, addr, size); | dt_print_hex(fp, addr, size); | ||||
| } | } | ||||
| /* | /* | ||||
| * Print a floating point (float, double, long double) value. | * Print a floating point (float, double, long double) value. | ||||
| */ | */ | ||||
| /* ARGSUSED */ | /* ARGSUSED */ | ||||
| static void | static void | ||||
| dt_print_float(ctf_id_t base, ulong_t off, dt_printarg_t *pap) | dt_print_float(ctf_id_t base, ulong_t off, dt_printarg_t *pap) | ||||
| { | { | ||||
| FILE *fp = pap->pa_file; | FILE *fp = pap->pa_file; | ||||
| ctf_file_t *ctfp = pap->pa_ctfp; | ctf_file_t *ctfp = pap->pa_ctfp; | ||||
| ctf_encoding_t e; | ctf_encoding_t e; | ||||
| caddr_t addr = pap->pa_addr + off / NBBY; | caddr_t addr = pap->pa_addr + off / NBBY; | ||||
| dtrace_hdl_t *dtp = pap->pa_dtp; | |||||
| if (ctf_type_encoding(ctfp, base, &e) == 0) { | if (ctf_type_encoding(ctfp, base, &e) == 0) { | ||||
| if (e.cte_format == CTF_FP_SINGLE && | if (e.cte_format == CTF_FP_SINGLE && | ||||
| e.cte_bits == sizeof (float) * NBBY) { | e.cte_bits == sizeof (float) * NBBY) { | ||||
| /* LINTED - alignment */ | xo_emit("{:value/%+.7e}", *((float *)addr)); | ||||
| (void) fprintf(fp, "%+.7e", *((float *)addr)); | |||||
| } else if (e.cte_format == CTF_FP_DOUBLE && | } else if (e.cte_format == CTF_FP_DOUBLE && | ||||
| e.cte_bits == sizeof (double) * NBBY) { | e.cte_bits == sizeof (double) * NBBY) { | ||||
| /* LINTED - alignment */ | xo_emit("{:value/%+.7e}", *((double *)addr)); | ||||
| (void) fprintf(fp, "%+.7e", *((double *)addr)); | |||||
| } else if (e.cte_format == CTF_FP_LDOUBLE && | } else if (e.cte_format == CTF_FP_LDOUBLE && | ||||
Done Inline ActionsJust picking an example at random: as I understand it the intent behind libxo is to replace printf()s with xo_emit()s. I believe libxo will DTRT, so I don't see any purpose in keeping the fprintf() calls around in cases like this. Combining them will shrink the diff a fair bit. markj: Just picking an example at random: as I understand it the intent behind libxo is to replace… | |||||
Done Inline ActionsThe goal behind libxo is to have a single code path, with as little overhead as possible for text output while allowing these other formats to be emitted, so yes, it makes sense to drop the fprintf calls and move them to libxo. phil: The goal behind libxo is to have a single code path, with as little overhead as possible for… | |||||
| e.cte_bits == sizeof (long double) * NBBY) { | e.cte_bits == sizeof (long double) * NBBY) { | ||||
| /* LINTED - alignment */ | xo_emit("{:value/%+.16LE}", *((long double *)addr)); | ||||
| (void) fprintf(fp, "%+.16LE", *((long double *)addr)); | |||||
| } else { | } else { | ||||
| (void) fprintf(fp, "<unknown encoding>"); | xo_emit("<{:warning}>", "unknown encoding"); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* | /* | ||||
| * A pointer is generally printed as a fixed-size integer. If we have a | * A pointer is generally printed as a fixed-size integer. If we have a | ||||
| * function pointer, we try to look up its name. | * function pointer, we try to look up its name. | ||||
| */ | */ | ||||
| Show All 12 Lines | dt_print_ptr(ctf_id_t base, ulong_t off, dt_printarg_t *pap) | ||||
| if (bid == CTF_ERR || ctf_type_kind(ctfp, bid) != CTF_K_FUNCTION) { | if (bid == CTF_ERR || ctf_type_kind(ctfp, bid) != CTF_K_FUNCTION) { | ||||
| dt_print_hex(fp, addr, size); | dt_print_hex(fp, addr, size); | ||||
| } else { | } else { | ||||
| /* LINTED - alignment */ | /* LINTED - alignment */ | ||||
| pc = *((uint64_t *)addr); | pc = *((uint64_t *)addr); | ||||
| if (dtrace_lookup_by_addr(pap->pa_dtp, pc, &sym, &dts) != 0) { | if (dtrace_lookup_by_addr(pap->pa_dtp, pc, &sym, &dts) != 0) { | ||||
| dt_print_hex(fp, addr, size); | dt_print_hex(fp, addr, size); | ||||
| } else { | } else { | ||||
| (void) fprintf(fp, "%s`%s", dts.dts_object, | xo_emit("{:value/%s`%s}", dts.dts_object, dts.dts_name); | ||||
| dts.dts_name); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* | /* | ||||
| * Print out an array. This is somewhat complex, as we must manually visit | * 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 | * each member, and recursively invoke ctf_type_visit() for each member. If | ||||
| * the members are non-structs, then we print them out directly: | * the members are non-structs, then we print them out directly: | ||||
| Show All 20 Lines | |||||
| * [ "string" ] | * [ "string" ] | ||||
| */ | */ | ||||
| static void | static void | ||||
| dt_print_array(ctf_id_t base, ulong_t off, dt_printarg_t *pap) | dt_print_array(ctf_id_t base, ulong_t off, dt_printarg_t *pap) | ||||
| { | { | ||||
| FILE *fp = pap->pa_file; | FILE *fp = pap->pa_file; | ||||
| ctf_file_t *ctfp = pap->pa_ctfp; | ctf_file_t *ctfp = pap->pa_ctfp; | ||||
| caddr_t addr = pap->pa_addr + off / NBBY; | caddr_t addr = pap->pa_addr + off / NBBY; | ||||
| char *str; | |||||
| ctf_arinfo_t car; | ctf_arinfo_t car; | ||||
| ssize_t eltsize; | ssize_t eltsize; | ||||
| ctf_encoding_t e; | ctf_encoding_t e; | ||||
| int i; | int i; | ||||
| boolean_t isstring; | boolean_t isstring; | ||||
| int kind; | int kind; | ||||
| ctf_id_t rtype; | ctf_id_t rtype; | ||||
| dtrace_hdl_t *dtp = pap->pa_dtp; | |||||
| if (ctf_array_info(ctfp, base, &car) == CTF_ERR) { | if (ctf_array_info(ctfp, base, &car) == CTF_ERR) { | ||||
| (void) fprintf(fp, "%p", (void *)addr); | xo_emit("{:value/%p}", (void *)addr); | ||||
| return; | return; | ||||
| } | } | ||||
| if ((eltsize = ctf_type_size(ctfp, car.ctr_contents)) < 0 || | if ((eltsize = ctf_type_size(ctfp, car.ctr_contents)) < 0 || | ||||
| (rtype = ctf_type_resolve(ctfp, car.ctr_contents)) == CTF_ERR || | (rtype = ctf_type_resolve(ctfp, car.ctr_contents)) == CTF_ERR || | ||||
| (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR) { | (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR) { | ||||
| (void) fprintf(fp, "<invalid type %lu>", car.ctr_contents); | xo_emit("<{:warning} {:type-identifier/%lu}>", "invalid type", | ||||
| car.ctr_contents); | |||||
| return; | return; | ||||
| } | } | ||||
| /* see if this looks like a string */ | /* see if this looks like a string */ | ||||
| isstring = B_FALSE; | isstring = B_FALSE; | ||||
| if (kind == CTF_K_INTEGER && | if (kind == CTF_K_INTEGER && | ||||
| ctf_type_encoding(ctfp, rtype, &e) != CTF_ERR && CTF_IS_CHAR(e)) { | ctf_type_encoding(ctfp, rtype, &e) != CTF_ERR && CTF_IS_CHAR(e)) { | ||||
| char c; | char c; | ||||
| Show All 11 Lines | dt_print_array(ctf_id_t base, ulong_t off, dt_printarg_t *pap) | ||||
| * As a slight aesthetic optimization, if we are a top-level type, then | * 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 | * don't bother printing out the brackets. This lets print("foo") look | ||||
| * like: | * like: | ||||
| * | * | ||||
| * string "foo" | * string "foo" | ||||
| * | * | ||||
| * As D will internally represent this as a char[256] array. | * As D will internally represent this as a char[256] array. | ||||
| */ | */ | ||||
| if (dtp->dt_oformat) { | |||||
| if (!isstring) | |||||
| xo_open_list("value"); | |||||
| else { | |||||
| str = malloc(car.ctr_nelems); | |||||
| if (str == NULL) | |||||
| return; | |||||
| *str = 0; | |||||
| } | |||||
| } else { | |||||
| if (!isstring || pap->pa_depth != 0) | if (!isstring || pap->pa_depth != 0) | ||||
| (void) fprintf(fp, "[ "); | (void)fprintf(fp, "[ "); | ||||
| if (isstring) | if (isstring) | ||||
| (void) fprintf(fp, "\""); | (void)fprintf(fp, "\""); | ||||
| } | |||||
| for (i = 0; i < car.ctr_nelems; i++) { | for (i = 0; i < car.ctr_nelems; i++) { | ||||
| if (isstring) { | if (isstring) { | ||||
| char c = *((char *)addr + eltsize * i); | char c = *((char *)addr + eltsize * i); | ||||
| if (c == '\0') | if (c == '\0') { | ||||
| if (dtp->dt_oformat) | |||||
| str[i] = 0; | |||||
| break; | break; | ||||
| } | |||||
| if (dtp->dt_oformat) | |||||
| str[i] = c; | |||||
| else | |||||
| (void) fprintf(fp, "%c", c); | (void)fprintf(fp, "%c", c); | ||||
| } else if (dtp->dt_oformat) { | |||||
| 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_format_member, &pa); | |||||
| } else { | } else { | ||||
| /* | /* | ||||
| * Recursively invoke ctf_type_visit() on each member. | * Recursively invoke ctf_type_visit() on each member. | ||||
| * We setup a new printarg struct with 'pa_nest' set to | * We setup a new printarg struct with 'pa_nest' set to | ||||
| * indicate that we are within a nested array. | * indicate that we are within a nested array. | ||||
| */ | */ | ||||
| dt_printarg_t pa = *pap; | dt_printarg_t pa = *pap; | ||||
| pa.pa_nest += pap->pa_depth + 1; | pa.pa_nest += pap->pa_depth + 1; | ||||
| pa.pa_depth = 0; | pa.pa_depth = 0; | ||||
| pa.pa_addr = addr + eltsize * i; | pa.pa_addr = addr + eltsize * i; | ||||
| (void) ctf_type_visit(ctfp, car.ctr_contents, | (void) ctf_type_visit(ctfp, car.ctr_contents, | ||||
| dt_print_member, &pa); | dt_print_member, &pa); | ||||
| dt_print_trailing_braces(&pa, 0); | dt_print_trailing_braces(&pa, 0); | ||||
| if (i != car.ctr_nelems - 1) | if (i != car.ctr_nelems - 1) | ||||
| (void) fprintf(fp, ", "); | (void) fprintf(fp, ", "); | ||||
| else if (CTF_IS_STRUCTLIKE(kind)) | else if (CTF_IS_STRUCTLIKE(kind)) | ||||
| (void) fprintf(fp, "\n"); | (void) fprintf(fp, "\n"); | ||||
| } | } | ||||
| } | } | ||||
| if (dtp->dt_oformat) { | |||||
| if (!isstring) | |||||
| xo_close_list("value"); | |||||
| else { | |||||
| xo_emit("{:value/%s}", str); | |||||
| free(str); | |||||
| } | |||||
| } else { | |||||
| if (isstring) | if (isstring) | ||||
| (void) fprintf(fp, "\""); | (void)fprintf(fp, "\""); | ||||
| if (!isstring || pap->pa_depth != 0) { | if (!isstring || pap->pa_depth != 0) { | ||||
| if (CTF_IS_STRUCTLIKE(kind)) | if (CTF_IS_STRUCTLIKE(kind)) | ||||
| dt_print_indent(pap); | dt_print_indent(pap); | ||||
| else | else | ||||
| (void) fprintf(fp, " "); | (void)fprintf(fp, " "); | ||||
| (void) fprintf(fp, "]"); | (void)fprintf(fp, "]"); | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| /* | /* | ||||
| * This isued by both structs and unions to print the leading brace. | * This isued by both structs and unions to print the leading brace. | ||||
| */ | */ | ||||
| /* ARGSUSED */ | /* ARGSUSED */ | ||||
| static void | static void | ||||
| dt_print_structlike(ctf_id_t id, ulong_t off, dt_printarg_t *pap) | dt_print_structlike(ctf_id_t id, ulong_t off, dt_printarg_t *pap) | ||||
| { | { | ||||
| if (pap->pa_dtp->dt_oformat == DTRACE_OFORMAT_TEXT) | |||||
| (void) fprintf(pap->pa_file, "{"); | (void)fprintf(pap->pa_file, "{"); | ||||
| } | } | ||||
| /* | /* | ||||
| * For enums, we try to print the enum name, and fall back to the value if it | * 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. | * can't be determined. We do not do any fancy flag processing like mdb. | ||||
| */ | */ | ||||
| /* ARGSUSED */ | /* ARGSUSED */ | ||||
| static void | static void | ||||
| dt_print_enum(ctf_id_t base, ulong_t off, dt_printarg_t *pap) | dt_print_enum(ctf_id_t base, ulong_t off, dt_printarg_t *pap) | ||||
| { | { | ||||
| FILE *fp = pap->pa_file; | FILE *fp = pap->pa_file; | ||||
| ctf_file_t *ctfp = pap->pa_ctfp; | ctf_file_t *ctfp = pap->pa_ctfp; | ||||
| const char *ename; | const char *ename; | ||||
| ssize_t size; | ssize_t size; | ||||
| caddr_t addr = pap->pa_addr + off / NBBY; | caddr_t addr = pap->pa_addr + off / NBBY; | ||||
| int value = 0; | int value = 0; | ||||
| dtrace_hdl_t *dtp = pap->pa_dtp; | |||||
| /* | /* | ||||
| * The C standard says that an enum will be at most the sizeof (int). | * 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 | * But if all the values are less than that, the compiler can use a | ||||
| * smaller size. Thanks standards. | * smaller size. Thanks standards. | ||||
| */ | */ | ||||
| size = ctf_type_size(ctfp, base); | size = ctf_type_size(ctfp, base); | ||||
| switch (size) { | switch (size) { | ||||
| case sizeof (uint8_t): | case sizeof (uint8_t): | ||||
| value = *(uint8_t *)addr; | value = *(uint8_t *)addr; | ||||
| break; | break; | ||||
| case sizeof (uint16_t): | case sizeof (uint16_t): | ||||
| value = *(uint16_t *)addr; | value = *(uint16_t *)addr; | ||||
| break; | break; | ||||
| case sizeof (int32_t): | case sizeof (int32_t): | ||||
| value = *(int32_t *)addr; | value = *(int32_t *)addr; | ||||
| break; | break; | ||||
| default: | default: | ||||
| (void) fprintf(fp, "<invalid enum size %u>", (uint_t)size); | xo_emit("<{:warning} {:size/%u}>", "invalid enum size", | ||||
| (uint_t)size); | |||||
| return; | return; | ||||
Done Inline ActionsThis also isn't a value, but an error message. Also when we use of JSON or XML, we really want to avoid using regex or pattern matching to extract data from a field. Here someone wanting the size would have to work to get it. Instead consider: xo_emit("{<:message} {:size/%u}>", "invalid enum size", (uint_t) size);phil: This also isn't a value, but an error message. Also when we use of JSON or XML, we really… | |||||
| } | } | ||||
| if ((ename = ctf_enum_name(ctfp, base, value)) != NULL) | if ((ename = ctf_enum_name(ctfp, base, value)) != NULL) { | ||||
| (void) fprintf(fp, "%s", ename); | xo_emit("{:value/%s}", ename); | ||||
| else | } else { | ||||
| (void) fprintf(fp, "%d", value); | xo_emit("{:value/%d}", value); | ||||
| } | } | ||||
| /* Flush in order to ensure output is aligned properly */ | |||||
| xo_flush(); | |||||
| } | |||||
| /* | /* | ||||
| * Forward declaration. There's not much to do here without the complete | * Forward declaration. There's not much to do here without the complete | ||||
| * type information, so just print out this fact and drive on. | * type information, so just print out this fact and drive on. | ||||
| */ | */ | ||||
| /* ARGSUSED */ | /* ARGSUSED */ | ||||
| static void | static void | ||||
| dt_print_tag(ctf_id_t base, ulong_t off, dt_printarg_t *pap) | dt_print_tag(ctf_id_t base, ulong_t off, dt_printarg_t *pap) | ||||
| { | { | ||||
| if (pap->pa_dtp->dt_oformat == DTRACE_OFORMAT_TEXT) | |||||
| (void) fprintf(pap->pa_file, "<forward decl>"); | (void)fprintf(pap->pa_file, "<forward decl>"); | ||||
| } | } | ||||
| typedef void dt_printarg_f(ctf_id_t, ulong_t, dt_printarg_t *); | typedef void dt_printarg_f(ctf_id_t, ulong_t, dt_printarg_t *); | ||||
| static dt_printarg_f *const dt_printfuncs[] = { | static dt_printarg_f *const dt_printfuncs[] = { | ||||
| dt_print_int, /* CTF_K_INTEGER */ | dt_print_int, /* CTF_K_INTEGER */ | ||||
| dt_print_float, /* CTF_K_FLOAT */ | dt_print_float, /* CTF_K_FLOAT */ | ||||
| dt_print_ptr, /* CTF_K_POINTER */ | dt_print_ptr, /* CTF_K_POINTER */ | ||||
| dt_print_array, /* CTF_K_ARRAY */ | dt_print_array, /* CTF_K_ARRAY */ | ||||
| dt_print_ptr, /* CTF_K_FUNCTION */ | dt_print_ptr, /* CTF_K_FUNCTION */ | ||||
| dt_print_structlike, /* CTF_K_STRUCT */ | dt_print_structlike, /* CTF_K_STRUCT */ | ||||
| dt_print_structlike, /* CTF_K_UNION */ | dt_print_structlike, /* CTF_K_UNION */ | ||||
| dt_print_enum, /* CTF_K_ENUM */ | dt_print_enum, /* CTF_K_ENUM */ | ||||
| dt_print_tag /* CTF_K_FORWARD */ | dt_print_tag /* CTF_K_FORWARD */ | ||||
| }; | }; | ||||
| static int | |||||
| dt_format_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; | |||||
| 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) { | |||||
| xo_emit("{:name/%s} <{:warning} {:type-identifier/%lu}>" | |||||
| " {:value/0x%llx}", | |||||
| name, "invalid type", id, pap->pa_addr); | |||||
| return (0); | |||||
| } | |||||
| dt_print_type_name(ctfp, id, type, sizeof (type)); | |||||
| xo_open_instance("type"); | |||||
| if (pap->pa_object) { | |||||
| xo_emit("{:object-name/%s}", pap->pa_object); | |||||
| /* Clear the object to avoid duplication */ | |||||
| pap->pa_object = NULL; | |||||
| } | |||||
| if (*name != 0) | |||||
| xo_emit("{:member-name/%s}", name); | |||||
| xo_emit("{:name/%s} {:ctfid/%ld}", type, id); | |||||
| dt_printfuncs[kind - 1](rtype, off, pap); | |||||
| xo_close_instance("type"); | |||||
| return (0); | |||||
| } | |||||
| /* | /* | ||||
| * Print one member of a structure. This callback is invoked from | * Print one member of a structure. This callback is invoked from | ||||
| * ctf_type_visit() recursively. | * ctf_type_visit() recursively. | ||||
| */ | */ | ||||
| static int | static int | ||||
| dt_print_member(const char *name, ctf_id_t id, ulong_t off, int depth, | dt_print_member(const char *name, ctf_id_t id, ulong_t off, int depth, | ||||
| void *data) | void *data) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | dt_print_member(const char *name, ctf_id_t id, ulong_t off, int depth, | ||||
| /* direct simple array members are not separated by newlines */ | /* direct simple array members are not separated by newlines */ | ||||
| if (!brief) | if (!brief) | ||||
| (void) fprintf(fp, "\n"); | (void) fprintf(fp, "\n"); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| /* | static ctf_id_t | ||||
| * Main print function invoked by dt_consume_cpu(). | dt_print_prepare(dtrace_hdl_t *dtp, const char *typename, caddr_t addr, | ||||
| */ | size_t len, dt_printarg_t *pa) | ||||
| int | |||||
| dtrace_print(dtrace_hdl_t *dtp, FILE *fp, const char *typename, | |||||
| caddr_t addr, size_t len) | |||||
| { | { | ||||
| const char *s; | const char *s; | ||||
| char *object; | char *object; | ||||
| dt_printarg_t pa; | |||||
| ctf_id_t id; | ctf_id_t id; | ||||
| dt_module_t *dmp; | dt_module_t *dmp; | ||||
| ctf_file_t *ctfp; | ctf_file_t *ctfp; | ||||
| int libid; | int libid; | ||||
| /* | /* | ||||
| * Split the fully-qualified type ID (module`id). This should | * Split the fully-qualified type ID (module`id). This should | ||||
| * always be the format, but if for some reason we don't find the | * 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() | * expected value, return 0 to fall back to the generic trace() | ||||
| * behavior. In the case of userland CTF modules this will actually be | * 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 | * of the format (module`lib`id). This is due to the fact that those | ||||
| * modules have multiple CTF containers which `lib` identifies. | * modules have multiple CTF containers which `lib` identifies. | ||||
| */ | */ | ||||
| for (s = typename; *s != '\0' && *s != '`'; s++) | for (s = typename; *s != '\0' && *s != '`'; s++) | ||||
| ; | ; | ||||
| if (*s != '`') | if (*s != '`') | ||||
| return (0); | return (CTF_ERR); | ||||
| object = alloca(s - typename + 1); | object = alloca(s - typename + 1); | ||||
| bcopy(typename, object, s - typename); | bcopy(typename, object, s - typename); | ||||
| object[s - typename] = '\0'; | object[s - typename] = '\0'; | ||||
| dmp = dt_module_lookup_by_name(dtp, object); | dmp = dt_module_lookup_by_name(dtp, object); | ||||
| if (dmp == NULL) | if (dmp == NULL) | ||||
| return (0); | return (CTF_ERR); | ||||
| if (dmp->dm_pid != 0) { | if (dmp->dm_pid != 0) { | ||||
| libid = atoi(s + 1); | libid = atoi(s + 1); | ||||
| s = strchr(s + 1, '`'); | s = strchr(s + 1, '`'); | ||||
| if (s == NULL || libid > dmp->dm_nctflibs) | if (s == NULL || libid > dmp->dm_nctflibs) | ||||
| return (0); | return (CTF_ERR); | ||||
| ctfp = dmp->dm_libctfp[libid]; | ctfp = dmp->dm_libctfp[libid]; | ||||
| } else { | } else { | ||||
| ctfp = dt_module_getctf(dtp, dmp); | ctfp = dt_module_getctf(dtp, dmp); | ||||
| } | } | ||||
| id = atoi(s + 1); | id = atoi(s + 1); | ||||
| /* | /* | ||||
| * Try to get the CTF kind for this id. If something has gone horribly | * 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 | * wrong and we can't resolve the ID, bail out and let trace() do the | ||||
| * work. | * work. | ||||
| */ | */ | ||||
| if (ctfp == NULL || ctf_type_kind(ctfp, id) == CTF_ERR) | if (ctfp == NULL || ctf_type_kind(ctfp, id) == CTF_ERR) | ||||
| return (CTF_ERR); | |||||
| pa->pa_dtp = dtp; | |||||
| pa->pa_addr = addr; | |||||
| pa->pa_ctfp = ctfp; | |||||
| pa->pa_nest = 0; | |||||
| pa->pa_depth = 0; | |||||
| pa->pa_object = strdup(object); | |||||
| return (id); | |||||
| } | |||||
| /* | |||||
| * 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) | |||||
| { | |||||
| dt_printarg_t pa; | |||||
| ctf_id_t id; | |||||
| id = dt_print_prepare(dtp, typename, addr, len, &pa); | |||||
| if (id == CTF_ERR) | |||||
| return (0); | 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; | pa.pa_file = fp; | ||||
| (void) ctf_type_visit(pa.pa_ctfp, id, dt_print_member, &pa); | (void) ctf_type_visit(pa.pa_ctfp, id, dt_print_member, &pa); | ||||
| dt_print_trailing_braces(&pa, 0); | dt_print_trailing_braces(&pa, 0); | ||||
| dt_free(dtp, (void *)pa.pa_object); | |||||
| return (len); | return (len); | ||||
| } | } | ||||
| /* | |||||
| * Main format function invoked by dt_consume_cpu(). | |||||
| */ | |||||
| int | |||||
| dtrace_format_print(dtrace_hdl_t *dtp, FILE *fp, const char *typename, | |||||
| caddr_t addr, size_t len) | |||||
| { | |||||
| dt_printarg_t pa; | |||||
| ctf_id_t id; | |||||
| char toplevel[1024]; | |||||
| id = dt_print_prepare(dtp, typename, addr, len, &pa); | |||||
| if (id == CTF_ERR) | |||||
| return (0); | |||||
| if (ctf_type_name(pa.pa_ctfp, id, toplevel, sizeof(toplevel)) < 0) | |||||
| return (0); | |||||
| xo_open_list("type"); | |||||
| (void) ctf_type_visit(pa.pa_ctfp, id, dt_format_member, &pa); | |||||
| xo_close_list("type"); | |||||
| dt_free(dtp, (void *)pa.pa_object); | |||||
| return (len); | |||||
| } | |||||
This isn't a "value"; it should be an error or something. Maybe something like:
xo_emit("{:message}", "unknown encoding");