Changeset View
Standalone View
cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c
| Show All 18 Lines | |||||
| * CDDL HEADER END | * CDDL HEADER END | ||||
| */ | */ | ||||
| /* | /* | ||||
| * 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) 2023, Domagoj Stolfa. All rights reserved. | |||||
| * Copyright (c) 2017, Joyent, Inc. All rights reserved. | * Copyright (c) 2017, Joyent, Inc. All rights reserved. | ||||
| * Copyright (c) 2012 by Delphix. All rights reserved. | * Copyright (c) 2012 by Delphix. All rights reserved. | ||||
| */ | */ | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <strings.h> | #include <strings.h> | ||||
| #include <errno.h> | #include <errno.h> | ||||
| #include <unistd.h> | #include <unistd.h> | ||||
| #include <limits.h> | #include <limits.h> | ||||
| #include <assert.h> | #include <assert.h> | ||||
| #include <ctype.h> | #include <ctype.h> | ||||
| #ifdef illumos | #ifdef illumos | ||||
| #include <alloca.h> | #include <alloca.h> | ||||
| #endif | #endif | ||||
| #include <dt_impl.h> | #include <dt_impl.h> | ||||
| #include <dt_pq.h> | #include <dt_pq.h> | ||||
| #include <dt_oformat.h> | |||||
| #ifndef illumos | #ifndef illumos | ||||
| #include <libproc_compat.h> | #include <libproc_compat.h> | ||||
| #endif | #endif | ||||
| #define DT_MASK_LO 0x00000000FFFFFFFFULL | #define DT_MASK_LO 0x00000000FFFFFFFFULL | ||||
| #define dt_format_sym(dtp, addr) dt_print_sym((dtp), NULL, NULL, addr) | |||||
| typedef struct dt_prepare_args { | |||||
| int first_bin; | |||||
| int last_bin; | |||||
| union { | |||||
| struct lquantize_args { | |||||
| #define lquantize_step u.lquantize.step | |||||
| #define lquantize_levels u.lquantize.levels | |||||
| #define lquantize_base u.lquantize.base | |||||
| int base; | |||||
| uint16_t step; | |||||
| uint16_t levels; | |||||
| } lquantize; | |||||
| struct llquantize_args { | |||||
| #define llquantize_next u.llquantize.next | |||||
| #define llquantize_step u.llquantize.step | |||||
| #define llquantize_value u.llquantize.value | |||||
| #define llquantize_levels u.llquantize.levels | |||||
| #define llquantize_order u.llquantize.order | |||||
| #define llquantize_factor u.llquantize.factor | |||||
| #define llquantize_low u.llquantize.low | |||||
| #define llquantize_high u.llquantize.high | |||||
| #define llquantize_nsteps u.llquantize.nsteps | |||||
| int64_t next; | |||||
| int64_t step; | |||||
| int64_t value; | |||||
| int levels; | |||||
| int order; | |||||
| uint16_t factor; | |||||
| uint16_t low; | |||||
| uint16_t high; | |||||
| uint16_t nsteps; | |||||
| } llquantize; | |||||
| } u; | |||||
| } dt_prepare_args_t; | |||||
| /* | /* | ||||
| * We declare this here because (1) we need it and (2) we want to avoid a | * We declare this here because (1) we need it and (2) we want to avoid a | ||||
| * dependency on libm in libdtrace. | * dependency on libm in libdtrace. | ||||
| */ | */ | ||||
| static long double | static long double | ||||
| dt_fabsl(long double x) | dt_fabsl(long double x) | ||||
| { | { | ||||
| if (x < 0) | if (x < 0) | ||||
| ▲ Show 20 Lines • Show All 699 Lines • ▼ Show 20 Lines | return (dt_printf(dtp, fp, "%c%c%c", | ||||
| DTRACE_AGGUTF8_BYTE2(block))); | DTRACE_AGGUTF8_BYTE2(block))); | ||||
| } | } | ||||
| len = strlen(ascii); | len = strlen(ascii); | ||||
| val = (datum * (len - 1)) / total; | val = (datum * (len - 1)) / total; | ||||
| return (dt_printf(dtp, fp, "%c", ascii[(uint_t)(val + 0.5)])); | return (dt_printf(dtp, fp, "%c", ascii[(uint_t)(val + 0.5)])); | ||||
| } | } | ||||
| int | static const int64_t * | ||||
| dt_print_quantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, | dt_format_quantize_prepare(dtrace_hdl_t *dtp, const void *addr, size_t size, | ||||
markj: Let's please call this `dt_format_quantize_prepare()` to make it a bit clearer that this is a… | |||||
| size_t size, uint64_t normal) | dt_prepare_args_t *args) | ||||
| { | { | ||||
| const int64_t *data = addr; | const int64_t *data = addr; | ||||
| int i, first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1; | int first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1; | ||||
| long double total = 0; | |||||
| char positives = 0, negatives = 0; | |||||
| if (size != DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) | if (size != DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) { | ||||
| return (dt_set_errno(dtp, EDT_DMISMATCH)); | (void) dt_set_errno(dtp, EDT_DMISMATCH); | ||||
| return (NULL); | |||||
| } | |||||
| while (first_bin < DTRACE_QUANTIZE_NBUCKETS - 1 && data[first_bin] == 0) | while (first_bin < DTRACE_QUANTIZE_NBUCKETS - 1 && data[first_bin] == 0) | ||||
| first_bin++; | first_bin++; | ||||
| if (first_bin == DTRACE_QUANTIZE_NBUCKETS - 1) { | if (first_bin == DTRACE_QUANTIZE_NBUCKETS - 1) { | ||||
| /* | /* | ||||
| * There isn't any data. This is possible if the aggregation | * There isn't any data. This is possible if the aggregation | ||||
| * has been clear()'d or if negative increment values have been | * has been clear()'d or if negative increment values have been | ||||
| * used. Regardless, we'll print the buckets around 0. | * used. Regardless, we'll print the buckets around 0. | ||||
| */ | */ | ||||
| first_bin = DTRACE_QUANTIZE_ZEROBUCKET - 1; | first_bin = DTRACE_QUANTIZE_ZEROBUCKET - 1; | ||||
| last_bin = DTRACE_QUANTIZE_ZEROBUCKET + 1; | last_bin = DTRACE_QUANTIZE_ZEROBUCKET + 1; | ||||
| } else { | } else { | ||||
| if (first_bin > 0) | if (first_bin > 0) | ||||
| first_bin--; | first_bin--; | ||||
| while (last_bin > 0 && data[last_bin] == 0) | while (last_bin > 0 && data[last_bin] == 0) | ||||
| last_bin--; | last_bin--; | ||||
| if (last_bin < DTRACE_QUANTIZE_NBUCKETS - 1) | if (last_bin < DTRACE_QUANTIZE_NBUCKETS - 1) | ||||
| last_bin++; | last_bin++; | ||||
| } | } | ||||
| args->first_bin = first_bin; | |||||
| args->last_bin = last_bin; | |||||
| return (data); | |||||
| } | |||||
| int | |||||
| dt_format_quantize(dtrace_hdl_t *dtp, const void *addr, size_t size, | |||||
| uint64_t normal) | |||||
| { | |||||
| const int64_t *data; | |||||
| dt_prepare_args_t args = { 0 }; | |||||
| int i, first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1; | |||||
| data = dt_format_quantize_prepare(dtp, addr, size, &args); | |||||
| /* dt_errno is set for us */ | |||||
| if (data == NULL) | |||||
| return (-1); | |||||
| first_bin = args.first_bin; | |||||
| last_bin = args.last_bin; | |||||
| xo_open_list("buckets"); | |||||
| for (i = first_bin; i <= last_bin; i++) { | for (i = first_bin; i <= last_bin; i++) { | ||||
| long long value = (long long)DTRACE_QUANTIZE_BUCKETVAL(i); | |||||
| xo_open_instance("buckets"); | |||||
| xo_emit("{:value/%lld} {:count/%lld}", value, | |||||
| (long long)data[i] / normal); | |||||
| xo_close_instance("buckets"); | |||||
| } | |||||
| xo_close_list("buckets"); | |||||
| return (0); | |||||
| } | |||||
| int | |||||
| dt_print_quantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, | |||||
| size_t size, uint64_t normal) | |||||
| { | |||||
| const int64_t *data; | |||||
| dt_prepare_args_t args = { 0 }; | |||||
| int i, first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1; | |||||
| long double total = 0; | |||||
| char positives = 0, negatives = 0; | |||||
| data = dt_format_quantize_prepare(dtp, addr, size, &args); | |||||
| /* dt_errno is set for us */ | |||||
| if (data == NULL) | |||||
| return (-1); | |||||
| first_bin = args.first_bin; | |||||
| last_bin = args.last_bin; | |||||
| for (i = first_bin; i <= last_bin; i++) { | |||||
| positives |= (data[i] > 0); | positives |= (data[i] > 0); | ||||
| negatives |= (data[i] < 0); | negatives |= (data[i] < 0); | ||||
| dt_quantize_total(dtp, data[i], &total); | dt_quantize_total(dtp, data[i], &total); | ||||
| } | } | ||||
| if (dt_print_quanthdr(dtp, fp, 0) < 0) | if (dt_print_quanthdr(dtp, fp, 0) < 0) | ||||
| return (-1); | return (-1); | ||||
| ▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | dt_print_quantize_packed(dtrace_hdl_t *dtp, FILE *fp, const void *addr, | ||||
| if (dt_printf(dtp, fp, ": %*lld | %lld\n", | if (dt_printf(dtp, fp, ": %*lld | %lld\n", | ||||
| -dt_ndigits(maxval), (long long)maxval, (long long)count) < 0) | -dt_ndigits(maxval), (long long)maxval, (long long)count) < 0) | ||||
| return (-1); | return (-1); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| int | static const int64_t * | ||||
| dt_print_lquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, | dt_format_lquantize_prepare(dtrace_hdl_t *dtp, const void *addr, size_t size, | ||||
| size_t size, uint64_t normal) | dt_prepare_args_t *args) | ||||
| { | { | ||||
| const int64_t *data = addr; | const int64_t *data = addr; | ||||
| int i, first_bin, last_bin, base; | int first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1, base; | ||||
| uint64_t arg; | uint64_t arg; | ||||
| long double total = 0; | |||||
| uint16_t step, levels; | uint16_t step, levels; | ||||
| char positives = 0, negatives = 0; | |||||
| if (size < sizeof (uint64_t)) | if (size < sizeof (uint64_t)) { | ||||
| return (dt_set_errno(dtp, EDT_DMISMATCH)); | (void) dt_set_errno(dtp, EDT_DMISMATCH); | ||||
| return (NULL); | |||||
| } | |||||
| arg = *data++; | arg = *data++; | ||||
| size -= sizeof (uint64_t); | size -= sizeof (uint64_t); | ||||
| base = DTRACE_LQUANTIZE_BASE(arg); | base = DTRACE_LQUANTIZE_BASE(arg); | ||||
| step = DTRACE_LQUANTIZE_STEP(arg); | step = DTRACE_LQUANTIZE_STEP(arg); | ||||
| levels = DTRACE_LQUANTIZE_LEVELS(arg); | levels = DTRACE_LQUANTIZE_LEVELS(arg); | ||||
| first_bin = 0; | first_bin = 0; | ||||
| last_bin = levels + 1; | last_bin = levels + 1; | ||||
| if (size != sizeof (uint64_t) * (levels + 2)) | if (size != sizeof (uint64_t) * (levels + 2)) { | ||||
| return (dt_set_errno(dtp, EDT_DMISMATCH)); | (void) dt_set_errno(dtp, EDT_DMISMATCH); | ||||
| return (NULL); | |||||
| } | |||||
| while (first_bin <= levels + 1 && data[first_bin] == 0) | while (first_bin <= levels + 1 && data[first_bin] == 0) | ||||
| first_bin++; | first_bin++; | ||||
| if (first_bin > levels + 1) { | if (first_bin > levels + 1) { | ||||
| first_bin = 0; | first_bin = 0; | ||||
| last_bin = 2; | last_bin = 2; | ||||
| } else { | } else { | ||||
| if (first_bin > 0) | if (first_bin > 0) | ||||
| first_bin--; | first_bin--; | ||||
| while (last_bin > 0 && data[last_bin] == 0) | while (last_bin > 0 && data[last_bin] == 0) | ||||
| last_bin--; | last_bin--; | ||||
| if (last_bin < levels + 1) | if (last_bin < levels + 1) | ||||
| last_bin++; | last_bin++; | ||||
| } | } | ||||
| args->first_bin = first_bin; | |||||
| args->last_bin = last_bin; | |||||
| args->lquantize_base = base; | |||||
| args->lquantize_step = step; | |||||
| args->lquantize_levels = levels; | |||||
| return (data); | |||||
| } | |||||
| int | |||||
| dt_format_lquantize(dtrace_hdl_t *dtp, const void *addr, size_t size, | |||||
| uint64_t normal) | |||||
| { | |||||
| const int64_t *data; | |||||
| dt_prepare_args_t args = { 0 }; | |||||
| int i, first_bin, last_bin, base; | |||||
| uint16_t step, levels; | |||||
| data = dt_format_lquantize_prepare(dtp, addr, size, &args); | |||||
| /* dt_errno is set for us */ | |||||
| if (data == NULL) | |||||
| return (-1); | |||||
| first_bin = args.first_bin; | |||||
| last_bin = args.last_bin; | |||||
| step = args.lquantize_step; | |||||
| levels = args.lquantize_levels; | |||||
| base = args.lquantize_base; | |||||
| xo_open_list("buckets"); | |||||
| for (i = first_bin; i <= last_bin; i++) { | for (i = first_bin; i <= last_bin; i++) { | ||||
| char c[32]; | |||||
| int err; | |||||
| xo_open_instance("buckets"); | |||||
| if (i == 0) { | |||||
| xo_emit("{:value/%d} {:operator/%s}", base, "<"); | |||||
| } else if (i == levels + 1) { | |||||
| xo_emit("{:value/%d} {:operator/%s}", | |||||
| base + (levels * step), ">="); | |||||
| } else { | |||||
| xo_emit("{:value/%d}", base + (i - 1) * step); | |||||
Not Done Inline ActionsWhy is base an "int", rather than unsigned? phil: Why is base an "int", rather than unsigned? | |||||
Done Inline ActionsThis is how libdtrace represented it before, so I just kept it the same as the original code. It added some duplication unfortunately, but it's consistent with the existing libdtrace code. domagoj.stolfa_gmail.com: This is how libdtrace represented it before, so I just kept it the same as the original code. | |||||
| } | |||||
| xo_emit("{:count/%lld}", (long long)data[i] / normal); | |||||
| xo_close_instance("buckets"); | |||||
| } | |||||
| xo_close_list("buckets"); | |||||
| return (0); | |||||
| } | |||||
| int | |||||
| dt_print_lquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, | |||||
| size_t size, uint64_t normal) | |||||
| { | |||||
| const int64_t *data; | |||||
| dt_prepare_args_t args = { 0 }; | |||||
| int i, first_bin, last_bin, base; | |||||
| uint64_t arg; | |||||
| long double total = 0; | |||||
| uint16_t step, levels; | |||||
| char positives = 0, negatives = 0; | |||||
| data = dt_format_lquantize_prepare(dtp, addr, size, &args); | |||||
| /* dt_errno is set for us */ | |||||
| if (data == NULL) | |||||
| return (-1); | |||||
| first_bin = args.first_bin; | |||||
| last_bin = args.last_bin; | |||||
| step = args.lquantize_step; | |||||
| levels = args.lquantize_levels; | |||||
| base = args.lquantize_base; | |||||
| for (i = first_bin; i <= last_bin; i++) { | |||||
| positives |= (data[i] > 0); | positives |= (data[i] > 0); | ||||
| negatives |= (data[i] < 0); | negatives |= (data[i] < 0); | ||||
| dt_quantize_total(dtp, data[i], &total); | dt_quantize_total(dtp, data[i], &total); | ||||
| } | } | ||||
| if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", | if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", | ||||
| "------------- Distribution -------------", "count") < 0) | "------------- Distribution -------------", "count") < 0) | ||||
Done Inline ActionsWhat happens to these with libxo? phil: What happens to these with libxo? | |||||
Done Inline ActionsThis function never gets called if oformat is specified, so it should never end up in the output. domagoj.stolfa_gmail.com: This function never gets called if `oformat` is specified, so it should never end up in the… | |||||
Not Done Inline ActionsBut since the goal is "one code path", turning these into xo_emit calls makes sense. Also this will allow these headers to show up in "html" style: if (xo_emit("\n{T:/%16s} {T:/%41s} {T:%-9s}\n", "value",
"------------- Distribution -------------", "count") < 0)This is assuming that "fp" points to the same thing that the default handle point to. Or maybe I'm missing something: are you converting all dtrace output or just a subset? phil: But since the goal is "one code path", turning these into xo_emit calls makes sense. Also this… | |||||
Done Inline ActionsI think the original reason I did this was because dt_printf is fairly involved in what it does: https://github.com/freebsd/freebsd-src/blob/main/cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c#L561-L578. The dtrace_sprintf() scenario is not an issue because the only public function that can possibly allocate the the sprintf buffer for DTrace is the dtrace_sprintf() function. As a result, this will never happen from this code path. However, passing the fp as NULL is certainly possible from the consumer end. fp originates from a call to dtrace_consume(). For example, lockstat calls it directly: https://github.com/freebsd/freebsd-src/blob/main/cddl/contrib/opensolaris/cmd/lockstat/lockstat.c#L1090. This unfortunately complicates things a fair bit, so I just opted for a completely separate code path. Perhaps my libxo-foo is lacking and I don't see an obvious way to address this with a single code-path without essentially doing an if (dtp->dt_oformat) {
xo_emit(...);
} else {
dt_printf(...);
}domagoj.stolfa_gmail.com: I think the original reason I did this was because `dt_printf` is fairly involved in what it… | |||||
Not Done Inline ActionsYeah, that makes sense. You could put an xo_handle_t into dtrace_hdl_t and use a FILE * in an xo_handle_t, and I could make a set of xo_set_writer()-friendly write/flush/close functions to that write to a buffer, but then we'd have to look at what's using the dt_sprintf_buf value and what it's doing with it. Not sure where that string goes and what encoding it would want. phil: Yeah, that makes sense.
You could put an xo_handle_t into dtrace_hdl_t and use a FILE * in an… | |||||
Done Inline ActionsThe buffer in question seems to be dt_buffered_buf, which is not really mentioned anywhere throughout the entire codebase other than in dt_printf(). It is somewhat tempting to just remove this code path all together and replace it with xo_emit(), but I'm not sure if anyone is using it either out-of-tree or in some other implementation (macOS, Windows?). We certainly don't lose anything by ignoring it in FreeBSD for the current diff and keep the original behavior for regular non-libxo output. As for the new libxo functions, I don't know if it would be commonly used outside of this? I'm also unsure if it's worth adding to support this use-case alone, considering that libdtrace doesn't expose dt_buffered_buf publicly nor provides any meaningful function to interact with it. The only way this could be used is if someone has their own version of libdtrace with functions that use it -- which is certainly possible and I'd prefer to avoid breaking (at least for this patch). @markj Do you have any insight as to what might make sense to do here? domagoj.stolfa_gmail.com: The buffer in question seems to be `dt_buffered_buf`, which is not really mentioned anywhere… | |||||
Not Done Inline ActionsI was thinking about: So digging in the source, I see that this is really only used by dt_sprintf(), which is only called by dtrace_freopen() and dtrace_system(), and I'm sure neither of those want any sort of libxo formatting. I think as long as you leave the existing dt_printf suitable for dtrace_sprintf calls, you can freely move the rest of the dt_printf calls to either:
I'd probably lean toward the last. So instead of if/else or two distinct "print average" functions, you just have a single call that does The Right Thing, like: static int
dt_print_average(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr,
size_t size, uint64_t normal)
{
/* LINTED - alignment */
int64_t *data = (int64_t *)addr;
return xo_emit(" {:average/%16lld}",
data[0] ? (long long)(data[1] / (int64_t)normal / data[0]) : 0);
}Note that since libxo automatically removes widths from formats,you don't have to give an explicit "encoding" format string (e.g. "{:average/%16ldd/%ldd}". The default encoding format string is the normal one widths removed. phil: I was thinking about:
https://github.com/freebsd/freebsd… | |||||
Done Inline ActionsThe problem wasn't with dtrace_sprintf(), rather the scenario where NULL was passed as the FILE *. Specifically, this code: https://github.com/freebsd/freebsd-src/blob/main/cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c#L619-L693. However, your suggestion would work regardless because dt_emit() could just add this particular check. I'll add dt_emit() in the next diff, keeping around dt_printf() for the other things that use it and then switch to dt_emit() in these cases. Thanks! domagoj.stolfa_gmail.com: The problem wasn't with `dtrace_sprintf()`, rather the scenario where `NULL` was passed as the… | |||||
Not Done Inline ActionsWell, assuming this is just straight-forward buffering, libxo contains its own buffering facility. Hmm... looks like the dt buffering grows unlimited, where xo just tries to make full BUFSIZ contents to reduce i/o overhead. (That's really only mostly true, since we can buffer more, depending on the particular features being used.) I don't really know if this is or isn't important; the buffered flush doesn't seem to be that wide spread: % grep -r dt_buffered_flush . ./common/dt_printf.c: if (dt_buffered_flush(dtp, NULL, NULL, ./common/dt_printf.c: if (dt_buffered_flush(dtp, NULL, ./common/dt_impl.h:extern int dt_buffered_flush(dtrace_hdl_t *, dtrace_probedata_t *, ./common/dt_subr.c:dt_buffered_flush(dtrace_hdl_t *dtp, dtrace_probedata_t *pdata, ./common/dt_consume.c: if (dt_buffered_flush(dtp, NULL, rec, aggdata, ./common/dt_consume.c: if (dt_buffered_flush(dtp, NULL, rec, aggdata, ./common/dt_consume.c: if (dt_buffered_flush(dtp, NULL, NULL, aggdata, ./common/dt_consume.c: if (dt_buffered_flush(dtp, &data, rec, NULL, 0) < 0) but this just a quick look. Ah, it's used for "dtrace -l". Like you said, these details can hide inside dt_emit. And you could make xo_set_writer() functions that copy data into dt_buffered_buf, at the cost of another copy. phil: Well, assuming this is just straight-forward buffering, libxo contains its own buffering… | |||||
| return (-1); | return (-1); | ||||
| for (i = first_bin; i <= last_bin; i++) { | for (i = first_bin; i <= last_bin; i++) { | ||||
| char c[32]; | char c[32]; | ||||
| int err; | int err; | ||||
| if (i == 0) { | if (i == 0) { | ||||
| (void) snprintf(c, sizeof (c), "< %d", base); | (void) snprintf(c, sizeof (c), "< %d", base); | ||||
| ▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | for (i = min; i <= max; i++) { | ||||
| if (dt_print_packed(dtp, fp, data[i], total) < 0) | if (dt_print_packed(dtp, fp, data[i], total) < 0) | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| (void) snprintf(c, sizeof (c), ">= %d", base + (levels * step)); | (void) snprintf(c, sizeof (c), ">= %d", base + (levels * step)); | ||||
| return (dt_printf(dtp, fp, ": %-8s | %lld\n", c, (long long)count)); | return (dt_printf(dtp, fp, ": %-8s | %lld\n", c, (long long)count)); | ||||
| } | } | ||||
| int | static const int64_t * | ||||
| dt_print_llquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, | dt_format_llquantize_prepare(dtrace_hdl_t *dtp, const void *addr, size_t size, | ||||
| size_t size, uint64_t normal) | dt_prepare_args_t *args) | ||||
| { | { | ||||
| int i, first_bin, last_bin, bin = 1, order, levels; | int i, first_bin, last_bin, bin = 1, order, levels; | ||||
| uint16_t factor, low, high, nsteps; | uint16_t factor, low, high, nsteps; | ||||
| const int64_t *data = addr; | const int64_t *data = addr; | ||||
| int64_t value = 1, next, step; | int64_t value = 1, next, step; | ||||
| char positives = 0, negatives = 0; | |||||
| long double total = 0; | |||||
| uint64_t arg; | uint64_t arg; | ||||
| char c[32]; | |||||
| if (size < sizeof (uint64_t)) | if (size < sizeof(uint64_t)) { | ||||
| return (dt_set_errno(dtp, EDT_DMISMATCH)); | (void) dt_set_errno(dtp, EDT_DMISMATCH); | ||||
| return (NULL); | |||||
| } | |||||
| arg = *data++; | arg = *data++; | ||||
| size -= sizeof (uint64_t); | size -= sizeof (uint64_t); | ||||
| factor = DTRACE_LLQUANTIZE_FACTOR(arg); | factor = DTRACE_LLQUANTIZE_FACTOR(arg); | ||||
| low = DTRACE_LLQUANTIZE_LOW(arg); | low = DTRACE_LLQUANTIZE_LOW(arg); | ||||
| high = DTRACE_LLQUANTIZE_HIGH(arg); | high = DTRACE_LLQUANTIZE_HIGH(arg); | ||||
| nsteps = DTRACE_LLQUANTIZE_NSTEP(arg); | nsteps = DTRACE_LLQUANTIZE_NSTEP(arg); | ||||
| /* | /* | ||||
| * We don't expect to be handed invalid llquantize() parameters here, | * We don't expect to be handed invalid llquantize() parameters here, | ||||
| * but sanity check them (to a degree) nonetheless. | * but sanity check them (to a degree) nonetheless. | ||||
| */ | */ | ||||
| if (size > INT32_MAX || factor < 2 || low >= high || | if (size > INT32_MAX || factor < 2 || low >= high || | ||||
| nsteps == 0 || factor > nsteps) | nsteps == 0 || factor > nsteps) { | ||||
| return (dt_set_errno(dtp, EDT_DMISMATCH)); | (void) dt_set_errno(dtp, EDT_DMISMATCH); | ||||
| return (NULL); | |||||
| } | |||||
| levels = (int)size / sizeof (uint64_t); | levels = (int)size / sizeof (uint64_t); | ||||
| first_bin = 0; | first_bin = 0; | ||||
| last_bin = levels - 1; | last_bin = levels - 1; | ||||
| while (first_bin < levels && data[first_bin] == 0) | while (first_bin < levels && data[first_bin] == 0) | ||||
| first_bin++; | first_bin++; | ||||
| if (first_bin == levels) { | if (first_bin == levels) { | ||||
| first_bin = 0; | first_bin = 0; | ||||
| last_bin = 1; | last_bin = 1; | ||||
| } else { | } else { | ||||
| if (first_bin > 0) | if (first_bin > 0) | ||||
| first_bin--; | first_bin--; | ||||
| while (last_bin > 0 && data[last_bin] == 0) | while (last_bin > 0 && data[last_bin] == 0) | ||||
| last_bin--; | last_bin--; | ||||
| if (last_bin < levels - 1) | if (last_bin < levels - 1) | ||||
| last_bin++; | last_bin++; | ||||
| } | } | ||||
| for (order = 0; order < low; order++) | |||||
| value *= factor; | |||||
| next = value * factor; | |||||
| step = next > nsteps ? next / nsteps : 1; | |||||
| args->first_bin = first_bin; | |||||
| args->last_bin = last_bin; | |||||
| args->llquantize_factor = factor; | |||||
| args->llquantize_low = low; | |||||
| args->llquantize_high = high; | |||||
| args->llquantize_nsteps = nsteps; | |||||
| args->llquantize_levels = levels; | |||||
| args->llquantize_order = order; | |||||
| args->llquantize_next = next; | |||||
| args->llquantize_step = step; | |||||
| args->llquantize_value = value; | |||||
| return (data); | |||||
| } | |||||
| int | |||||
| dt_format_llquantize(dtrace_hdl_t *dtp, const void *addr, size_t size, | |||||
| uint64_t normal) | |||||
| { | |||||
| int first_bin, last_bin, bin = 1, order, levels; | |||||
| uint16_t factor, low, high, nsteps; | |||||
| const int64_t *data; | |||||
| dt_prepare_args_t args = { 0 }; | |||||
| int64_t value = 1, next, step; | |||||
| uint64_t arg; | |||||
| char c[32]; | |||||
| data = dt_format_llquantize_prepare(dtp, addr, size, &args); | |||||
| /* dt_errno is set for us */ | |||||
| if (data == NULL) | |||||
| return (-1); | |||||
| first_bin = args.first_bin; | |||||
| last_bin = args.last_bin; | |||||
| factor = args.llquantize_factor; | |||||
| low = args.llquantize_low; | |||||
| high = args.llquantize_high; | |||||
| nsteps = args.llquantize_nsteps; | |||||
| levels = args.llquantize_levels; | |||||
| order = args.llquantize_order; | |||||
| next = args.llquantize_next; | |||||
| step = args.llquantize_step; | |||||
| value = args.llquantize_value; | |||||
| xo_open_list("buckets"); | |||||
| if (first_bin == 0) { | |||||
| /* | |||||
| * We have to represent < value somehow in JSON, so we bundle an | |||||
| * optional "operator" in llquantize buckets. | |||||
| */ | |||||
| xo_open_instance("buckets"); | |||||
| xo_emit("{:value/%lld} {:count/%lld} {:operator/%s}", | |||||
| (long long)value, (long long)data[0] / normal, "<"); | |||||
| xo_close_instance("buckets"); | |||||
| } | |||||
| while (order <= high) { | |||||
| if (bin >= first_bin && bin <= last_bin) { | |||||
| xo_open_instance("buckets"); | |||||
| xo_emit("{:value/%lld} {:count/%lld}", (long long)value, | |||||
| (long long)data[bin] / normal); | |||||
| xo_close_instance("buckets"); | |||||
| } | |||||
| assert(value < next); | |||||
| bin++; | |||||
| if ((value += step) != next) | |||||
| continue; | |||||
| next = value * factor; | |||||
| step = next > nsteps ? next / nsteps : 1; | |||||
| order++; | |||||
| } | |||||
| if (last_bin < bin) { | |||||
| xo_close_list("buckets"); | |||||
| return (0); | |||||
| } | |||||
| assert(last_bin == bin); | |||||
| xo_open_instance("buckets"); | |||||
| xo_emit("{:value/%lld} {:count/%lld} {:operator/%s}", (long long)value, | |||||
| (long long)data[bin] / normal, ">="); | |||||
| xo_close_instance("buckets"); | |||||
| xo_close_list("buckets"); | |||||
| return (0); | |||||
| } | |||||
| int | |||||
| dt_print_llquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, | |||||
| size_t size, uint64_t normal) | |||||
| { | |||||
| int i, first_bin, last_bin, bin = 1, order, levels; | |||||
| uint16_t factor, low, high, nsteps; | |||||
| const int64_t *data; | |||||
| dt_prepare_args_t args = { 0 }; | |||||
| int64_t value = 1, next, step; | |||||
| char positives = 0, negatives = 0; | |||||
| long double total = 0; | |||||
| uint64_t arg; | |||||
| char c[32]; | |||||
| data = dt_format_llquantize_prepare(dtp, addr, size, &args); | |||||
| /* dt_errno is set for us */ | |||||
| if (data == NULL) | |||||
| return (-1); | |||||
| first_bin = args.first_bin; | |||||
| last_bin = args.last_bin; | |||||
| factor = args.llquantize_factor; | |||||
| low = args.llquantize_low; | |||||
| high = args.llquantize_high; | |||||
| nsteps = args.llquantize_nsteps; | |||||
| levels = args.llquantize_levels; | |||||
| order = args.llquantize_order; | |||||
| next = args.llquantize_next; | |||||
| step = args.llquantize_step; | |||||
| value = args.llquantize_value; | |||||
| for (i = first_bin; i <= last_bin; i++) { | for (i = first_bin; i <= last_bin; i++) { | ||||
| positives |= (data[i] > 0); | positives |= (data[i] > 0); | ||||
| negatives |= (data[i] < 0); | negatives |= (data[i] < 0); | ||||
| dt_quantize_total(dtp, data[i], &total); | dt_quantize_total(dtp, data[i], &total); | ||||
| } | } | ||||
| if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", | if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", | ||||
| "------------- Distribution -------------", "count") < 0) | "------------- Distribution -------------", "count") < 0) | ||||
| return (-1); | return (-1); | ||||
| for (order = 0; order < low; order++) | |||||
| value *= factor; | |||||
| next = value * factor; | |||||
| step = next > nsteps ? next / nsteps : 1; | |||||
| if (first_bin == 0) { | if (first_bin == 0) { | ||||
| (void) snprintf(c, sizeof (c), "< %lld", (long long)value); | (void) snprintf(c, sizeof (c), "< %lld", (long long)value); | ||||
| if (dt_printf(dtp, fp, "%16s ", c) < 0) | if (dt_printf(dtp, fp, "%16s ", c) < 0) | ||||
| return (-1); | return (-1); | ||||
Done Inline ActionsIf you're going full-on libxo, these output-generating lines will need to be converted. This particular line will need some thought, since you'd need to snprintf the value, but without the "<", then do something like: xo_emit("{P:/*s}{:direction}{P: }{:value}", 14 - strlen(c), "<", c); or something like that. phil: If you're going full-on libxo, these output-generating lines will need to be converted.
This… | |||||
Done Inline ActionsSimilar to the other one, this doesn't run when oformat is specified so shouldn't end up in the output anywhere. If it does, it's certainly a bug I did not anticipate. domagoj.stolfa_gmail.com: Similar to the other one, this doesn't run when `oformat` is specified so shouldn't end up in… | |||||
| if (dt_print_quantline(dtp, fp, data[0], normal, | if (dt_print_quantline(dtp, fp, data[0], normal, | ||||
| total, positives, negatives) < 0) | total, positives, negatives) < 0) | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| while (order <= high) { | while (order <= high) { | ||||
| if (bin >= first_bin && bin <= last_bin) { | if (bin >= first_bin && bin <= last_bin) { | ||||
| Show All 24 Lines | dt_print_llquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, | ||||
| if (dt_printf(dtp, fp, "%16s ", c) < 0) | if (dt_printf(dtp, fp, "%16s ", c) < 0) | ||||
| return (-1); | return (-1); | ||||
| return (dt_print_quantline(dtp, fp, data[bin], normal, | return (dt_print_quantline(dtp, fp, data[bin], normal, | ||||
| total, positives, negatives)); | total, positives, negatives)); | ||||
| } | } | ||||
| static int | |||||
| dt_format_average(dtrace_hdl_t *dtp, caddr_t addr, size_t size, uint64_t normal) | |||||
| { | |||||
| int64_t *data = (int64_t *)addr; | |||||
| xo_emit("{:average/%lld}", | |||||
| data[0] ? (long long)(data[1] / (int64_t)normal / data[0]) : 0); | |||||
| return (0); | |||||
| } | |||||
| /*ARGSUSED*/ | /*ARGSUSED*/ | ||||
| static int | static int | ||||
| dt_print_average(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, | dt_print_average(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, | ||||
| size_t size, uint64_t normal) | size_t size, uint64_t normal) | ||||
| { | { | ||||
| /* LINTED - alignment */ | /* LINTED - alignment */ | ||||
| int64_t *data = (int64_t *)addr; | int64_t *data = (int64_t *)addr; | ||||
| return (dt_printf(dtp, fp, " %16lld", data[0] ? | return (dt_printf(dtp, fp, " %16lld", data[0] ? | ||||
| (long long)(data[1] / (int64_t)normal / data[0]) : 0)); | (long long)(data[1] / (int64_t)normal / data[0]) : 0)); | ||||
| } | } | ||||
| static int | |||||
| dt_format_stddev(dtrace_hdl_t *dtp, caddr_t addr, size_t size, uint64_t normal) | |||||
| { | |||||
| uint64_t *data = (uint64_t *)addr; | |||||
| xo_emit("{:stddev/%llu}", | |||||
| data[0] ? (unsigned long long)dt_stddev(data, normal) : 0); | |||||
| return (0); | |||||
| } | |||||
| /*ARGSUSED*/ | /*ARGSUSED*/ | ||||
| static int | static int | ||||
| dt_print_stddev(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, | dt_print_stddev(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, | ||||
| size_t size, uint64_t normal) | size_t size, uint64_t normal) | ||||
| { | { | ||||
| /* LINTED - alignment */ | /* LINTED - alignment */ | ||||
| uint64_t *data = (uint64_t *)addr; | uint64_t *data = (uint64_t *)addr; | ||||
| ▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | for (i = 0; i < nbytes; i += 16) { | ||||
| if (dt_printf(dtp, fp, "\n") < 0) | if (dt_printf(dtp, fp, "\n") < 0) | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| int | int | ||||
| dt_format_stack(dtrace_hdl_t *dtp, caddr_t addr, int depth, int size) | |||||
| { | |||||
| dtrace_syminfo_t dts; | |||||
| GElf_Sym sym; | |||||
| int i; | |||||
| uint64_t pc; | |||||
| xo_open_list("stack-frames"); | |||||
| for (i = 0; i < depth; i++) { | |||||
| switch (size) { | |||||
| case sizeof (uint32_t): | |||||
| pc = *((uint32_t *)addr); | |||||
| break; | |||||
| case sizeof (uint64_t): | |||||
| pc = *((uint64_t *)addr); | |||||
| break; | |||||
| default: | |||||
| return (dt_set_errno(dtp, EDT_BADSTACKPC)); | |||||
| } | |||||
| if (pc == 0) | |||||
| break; | |||||
| addr += size; | |||||
| xo_open_instance("stack-frames"); | |||||
| if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) { | |||||
| if (pc > sym.st_value) { | |||||
| xo_emit("{:symbol/%s`%s+0x%llx} {:module/%s} " | |||||
| "{:name/%s} {:offset/0x%llx}", | |||||
| dts.dts_object, dts.dts_name, | |||||
Done Inline ActionsUse full words for tag names. "symbol" instead of "sym". From xo_format(5): Use full words Do not abbreviate especially when the abbreviation is not obvious or not widely used. Use "data-size", not "dsz" or "dsize". Use "interface" in‐ stead of "ifname", "if-name", "iface", "if", or "intf". The goal is to help tag names be as consistent as possible across all FreeBSD. phil: Use full words for tag names. "symbol" instead of "sym". From xo_format(5):
Use full… | |||||
| (u_longlong_t)(pc - sym.st_value), | |||||
| dts.dts_object, dts.dts_name, | |||||
| (u_longlong_t)(pc - sym.st_value)); | |||||
| } else { | |||||
| xo_emit("{:symbol/%s`%s} {:module/%s} " | |||||
| "{:name/%s}", | |||||
| dts.dts_object, dts.dts_name, | |||||
| dts.dts_object, dts.dts_name); | |||||
| } | |||||
| } else { | |||||
| /* | |||||
| * We'll repeat the lookup, but this time we'll specify | |||||
| * a NULL GElf_Sym -- indicating that we're only | |||||
| * interested in the containing module. | |||||
| */ | |||||
| if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { | |||||
| xo_emit("{:symbol/%s`0x%llx} {:module/%s} " | |||||
| "{:offset/0x%llx}", | |||||
| dts.dts_object, (u_longlong_t)pc, | |||||
| dts.dts_object, (u_longlong_t)pc); | |||||
| } else { | |||||
| xo_emit("{:symbol/0x%llx} {:offset/0x%llx}", | |||||
| (u_longlong_t)pc, (u_longlong_t)pc); | |||||
| } | |||||
| } | |||||
| xo_close_instance("stack-frames"); | |||||
| } | |||||
| xo_close_list("stack-frames"); | |||||
| return (0); | |||||
| } | |||||
| int | |||||
| dt_format_ustack(dtrace_hdl_t *dtp, caddr_t addr, uint64_t arg) | |||||
| { | |||||
| uint64_t *pc = (uint64_t *)addr; | |||||
| uint32_t depth = DTRACE_USTACK_NFRAMES(arg); | |||||
| uint32_t strsize = DTRACE_USTACK_STRSIZE(arg); | |||||
| const char *strbase = addr + (depth + 1) * sizeof (uint64_t); | |||||
| const char *str = strsize ? strbase : NULL; | |||||
| int err = 0; | |||||
| char name[PATH_MAX], objname[PATH_MAX], c[PATH_MAX * 2]; | |||||
| struct ps_prochandle *P; | |||||
| GElf_Sym sym; | |||||
| int i, indent; | |||||
| pid_t pid; | |||||
| if (depth == 0) | |||||
| return (0); | |||||
| pid = (pid_t)*pc++; | |||||
| /* | |||||
| * Ultimately, we need to add an entry point in the library vector for | |||||
| * determining <symbol, offset> from <pid, address>. For now, if | |||||
| * this is a vector open, we just print the raw address or string. | |||||
| */ | |||||
| if (dtp->dt_vector == NULL) | |||||
| P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); | |||||
| else | |||||
| P = NULL; | |||||
| if (P != NULL) | |||||
| dt_proc_lock(dtp, P); /* lock handle while we perform lookups */ | |||||
| xo_open_list("ustack-frames"); | |||||
| for (i = 0; i < depth && pc[i] != 0; i++) { | |||||
| const prmap_t *map; | |||||
| xo_open_instance("ustack-frames"); | |||||
Done Inline ActionsI so want to suggest that it make sense to pull the module and offset into distinct tags, but they really make no sense individually, right? Using multiple tags keeps the API user from having to carve the field into the three pieces. Perhaps emitting both styles, like: <symbol>foo`goo+0x55</symbol> <module>foo</module> <name>goo</name> <offset>0x55</offset> Overkill? phil: I so want to suggest that it make sense to pull the module and offset into distinct tags, but… | |||||
Done Inline ActionsI'm not sure. I don't have a strong opinion on this because all of my stack trace usage only ever has to use them combined, so I didn't do this. If someone could point out where this makes sense I'm happy to split it into multiple entries. domagoj.stolfa_gmail.com: I'm not sure. I don't have a strong opinion on this because all of my stack trace usage only… | |||||
Done Inline ActionsI'm not strongly opinionated on this one either, but it' hard to know what your users will need. I can imagine wanting to filter on a particular module or function, but that's just my random imagination..... phil: I'm not strongly opinionated on this one either, but it' hard to know what your users will need. | |||||
Done Inline ActionsI could add both formats -- the "legacy" (perhaps calling it "frame") which is how DTrace currently represents stack frames and the machine-readable one in the format that you've suggested. Does that sound like a reasonable approach? domagoj.stolfa_gmail.com: I could add both formats -- the "legacy" (perhaps calling it "frame") which is how DTrace… | |||||
| if (P != NULL && Plookup_by_addr(P, pc[i], | |||||
| name, sizeof (name), &sym) == 0) { | |||||
| (void) Pobjname(P, pc[i], objname, sizeof (objname)); | |||||
| if (pc[i] > sym.st_value) { | |||||
| xo_emit("{:symbol/%s`%s+0x%llx} {:module/%s} " | |||||
| "{:name/%s} {:offset/0x%llx}", | |||||
| dt_basename(objname), name, | |||||
| (u_longlong_t)(pc[i] - sym.st_value), | |||||
| dt_basename(objname), name, | |||||
| (u_longlong_t)(pc[i] - sym.st_value)); | |||||
| } else { | |||||
| xo_emit("{:symbol/%s`%s} {:module/%s} " | |||||
| "{:name/%s}", | |||||
| dt_basename(objname), name, | |||||
| dt_basename(objname), name); | |||||
| } | |||||
| } else if (str != NULL && str[0] != '\0' && str[0] != '@' && | |||||
| (P != NULL && ((map = Paddr_to_map(P, pc[i])) == NULL || | |||||
| (map->pr_mflags & MA_WRITE)))) { | |||||
| /* | |||||
| * If the current string pointer in the string table | |||||
| * does not point to an empty string _and_ the program | |||||
| * counter falls in a writable region, we'll use the | |||||
| * string from the string table instead of the raw | |||||
| * address. This last condition is necessary because | |||||
| * some (broken) ustack helpers will return a string | |||||
| * even for a program counter that they can't | |||||
| * identify. If we have a string for a program | |||||
| * counter that falls in a segment that isn't | |||||
| * writable, we assume that we have fallen into this | |||||
| * case and we refuse to use the string. | |||||
| */ | |||||
| xo_emit("{:symbol/%s}", str); | |||||
| } else { | |||||
| if (P != NULL && Pobjname(P, pc[i], objname, | |||||
| sizeof (objname)) != 0) { | |||||
| xo_emit("{:symbol/%s`0x%llx} {:module/%s} " | |||||
| "{:offset/0x%llx}", | |||||
| dt_basename(objname), (u_longlong_t)pc[i], | |||||
| dt_basename(objname), (u_longlong_t)pc[i]); | |||||
| } else { | |||||
| xo_emit("{:symbol/0x%llx} {:offset/0x%llx}", | |||||
| (u_longlong_t)pc[i], (u_longlong_t)pc[i]); | |||||
| } | |||||
| } | |||||
| if (str != NULL && str[0] == '@') { | |||||
| /* | |||||
| * If the first character of the string is an "at" sign, | |||||
| * then the string is inferred to be an annotation -- | |||||
| * and it is printed out beneath the frame and offset | |||||
| * with brackets. | |||||
| */ | |||||
| xo_emit("{:annotation/%s}", &str[1]); | |||||
| } | |||||
| if (str != NULL) { | |||||
| str += strlen(str) + 1; | |||||
| if (str - strbase >= strsize) | |||||
| str = NULL; | |||||
| } | |||||
| xo_close_instance("ustack-frames"); | |||||
| } | |||||
| xo_close_list("ustack-frames"); | |||||
| if (P != NULL) { | |||||
| dt_proc_unlock(dtp, P); | |||||
| dt_proc_release(dtp, P); | |||||
| } | |||||
| return (err); | |||||
| } | |||||
| int | |||||
| dt_print_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format, | dt_print_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format, | ||||
| caddr_t addr, int depth, int size) | caddr_t addr, int depth, int size) | ||||
| { | { | ||||
| dtrace_syminfo_t dts; | dtrace_syminfo_t dts; | ||||
| GElf_Sym sym; | GElf_Sym sym; | ||||
| int i, indent; | int i, indent; | ||||
| char c[PATH_MAX * 2]; | char c[PATH_MAX * 2]; | ||||
| uint64_t pc; | uint64_t pc; | ||||
| ▲ Show 20 Lines • Show All 196 Lines • ▼ Show 20 Lines | if (P != NULL) { | ||||
| dt_proc_unlock(dtp, P); | dt_proc_unlock(dtp, P); | ||||
| dt_proc_release(dtp, P); | dt_proc_release(dtp, P); | ||||
| } | } | ||||
| return (err); | return (err); | ||||
| } | } | ||||
| static int | static int | ||||
| dt_format_usym(dtrace_hdl_t *dtp, caddr_t addr, dtrace_actkind_t act) | |||||
| { | |||||
| uint64_t pid = ((uint64_t *)addr)[0]; | |||||
| uint64_t pc = ((uint64_t *)addr)[1]; | |||||
| char *s; | |||||
| int n, len = 256; | |||||
| if (act == DTRACEACT_USYM && dtp->dt_vector == NULL) { | |||||
| struct ps_prochandle *P; | |||||
| if ((P = dt_proc_grab(dtp, pid, | |||||
| PGRAB_RDONLY | PGRAB_FORCE, 0)) != NULL) { | |||||
| GElf_Sym sym; | |||||
| dt_proc_lock(dtp, P); | |||||
| if (Plookup_by_addr(P, pc, NULL, 0, &sym) == 0) | |||||
| pc = sym.st_value; | |||||
| dt_proc_unlock(dtp, P); | |||||
| dt_proc_release(dtp, P); | |||||
| } | |||||
| } | |||||
| do { | |||||
| n = len; | |||||
| s = alloca(n); | |||||
| } while ((len = dtrace_uaddr2str(dtp, pid, pc, s, n)) > n); | |||||
| xo_emit("{:usym/%s}", s); | |||||
| return (0); | |||||
| } | |||||
| static int | |||||
| dt_print_usym(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, dtrace_actkind_t act) | dt_print_usym(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, dtrace_actkind_t act) | ||||
| { | { | ||||
| /* LINTED - alignment */ | /* LINTED - alignment */ | ||||
| uint64_t pid = ((uint64_t *)addr)[0]; | uint64_t pid = ((uint64_t *)addr)[0]; | ||||
| /* LINTED - alignment */ | /* LINTED - alignment */ | ||||
| uint64_t pc = ((uint64_t *)addr)[1]; | uint64_t pc = ((uint64_t *)addr)[1]; | ||||
| const char *format = " %-50s"; | const char *format = " %-50s"; | ||||
| char *s; | char *s; | ||||
| Show All 20 Lines | do { | ||||
| n = len; | n = len; | ||||
| s = alloca(n); | s = alloca(n); | ||||
| } while ((len = dtrace_uaddr2str(dtp, pid, pc, s, n)) > n); | } while ((len = dtrace_uaddr2str(dtp, pid, pc, s, n)) > n); | ||||
| return (dt_printf(dtp, fp, format, s)); | return (dt_printf(dtp, fp, format, s)); | ||||
| } | } | ||||
| int | int | ||||
| dt_format_umod(dtrace_hdl_t *dtp, caddr_t addr) | |||||
| { | |||||
| uint64_t pid = ((uint64_t *)addr)[0]; | |||||
| uint64_t pc = ((uint64_t *)addr)[1]; | |||||
| int err = 0; | |||||
| char objname[PATH_MAX]; | |||||
| struct ps_prochandle *P; | |||||
| /* | |||||
| * See the comment in dt_print_ustack() for the rationale for | |||||
| * printing raw addresses in the vectored case. | |||||
| */ | |||||
| if (dtp->dt_vector == NULL) | |||||
| P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); | |||||
| else | |||||
| P = NULL; | |||||
| if (P != NULL) | |||||
| dt_proc_lock(dtp, P); /* lock handle while we perform lookups */ | |||||
| if (P != NULL && Pobjname(P, pc, objname, sizeof (objname)) != 0) { | |||||
| xo_emit("{:umod/%s}", dt_basename(objname)); | |||||
| } else { | |||||
| xo_emit("{:umod/0x%llx}", (u_longlong_t)pc); | |||||
| } | |||||
| if (P != NULL) { | |||||
| dt_proc_unlock(dtp, P); | |||||
| dt_proc_release(dtp, P); | |||||
| } | |||||
| return (0); | |||||
| } | |||||
| int | |||||
| dt_print_umod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) | dt_print_umod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) | ||||
| { | { | ||||
| /* LINTED - alignment */ | /* LINTED - alignment */ | ||||
| uint64_t pid = ((uint64_t *)addr)[0]; | uint64_t pid = ((uint64_t *)addr)[0]; | ||||
| /* LINTED - alignment */ | /* LINTED - alignment */ | ||||
| uint64_t pc = ((uint64_t *)addr)[1]; | uint64_t pc = ((uint64_t *)addr)[1]; | ||||
| int err = 0; | int err = 0; | ||||
| Show All 39 Lines | dt_print_sym(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) | ||||
| dtrace_syminfo_t dts; | dtrace_syminfo_t dts; | ||||
| GElf_Sym sym; | GElf_Sym sym; | ||||
| char c[PATH_MAX * 2]; | char c[PATH_MAX * 2]; | ||||
| if (format == NULL) | if (format == NULL) | ||||
| format = " %-50s"; | format = " %-50s"; | ||||
| if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) { | if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) { | ||||
| if (dtp->dt_oformat) | |||||
| xo_emit("{:sym/%s`%s} {:object/%s} {:name/%s}", | |||||
| dts.dts_object, dts.dts_name, dts.dts_object, | |||||
| dts.dts_name); | |||||
| else | |||||
| (void) snprintf(c, sizeof (c), "%s`%s", | (void) snprintf(c, sizeof (c), "%s`%s", | ||||
| dts.dts_object, dts.dts_name); | dts.dts_object, dts.dts_name); | ||||
| } else { | } else { | ||||
| /* | /* | ||||
| * We'll repeat the lookup, but this time we'll specify a | * We'll repeat the lookup, but this time we'll specify a | ||||
| * NULL GElf_Sym -- indicating that we're only interested in | * NULL GElf_Sym -- indicating that we're only interested in | ||||
| * the containing module. | * the containing module. | ||||
| */ | */ | ||||
| if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { | if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { | ||||
| if (dtp->dt_oformat) | |||||
| xo_emit("{:sym/%s`0x%llx} {:object/%s} " | |||||
| "{:offset/0x%llx}", | |||||
| dts.dts_object, (u_longlong_t)pc, | |||||
| dts.dts_object, (u_longlong_t)pc); | |||||
| else | |||||
| (void) snprintf(c, sizeof (c), "%s`0x%llx", | (void) snprintf(c, sizeof (c), "%s`0x%llx", | ||||
| dts.dts_object, (u_longlong_t)pc); | dts.dts_object, (u_longlong_t)pc); | ||||
| } else { | } else { | ||||
| if (dtp->dt_oformat) | |||||
| xo_emit("{:sym/0x%llx} {:offset/0x%llx}", | |||||
| (u_longlong_t)pc, (u_longlong_t)pc); | |||||
| else | |||||
| (void) snprintf(c, sizeof (c), "0x%llx", | (void) snprintf(c, sizeof (c), "0x%llx", | ||||
| (u_longlong_t)pc); | (u_longlong_t)pc); | ||||
| } | } | ||||
Done Inline ActionsI rather dislike the code duplication between dt_print_sym() and dt_format_sym(). Why can't both cases be handled with a single function? markj: I rather dislike the code duplication between dt_print_sym() and dt_format_sym(). Why can't… | |||||
| } | } | ||||
| if (dt_printf(dtp, fp, format, c) < 0) | if (dtp->dt_oformat != 0 && dt_printf(dtp, fp, format, c) < 0) | ||||
| return (-1); | return (-1); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| int | int | ||||
| dt_format_mod(dtrace_hdl_t *dtp, caddr_t addr) | |||||
| { | |||||
| /* LINTED - alignment */ | |||||
| uint64_t pc = *((uint64_t *)addr); | |||||
| dtrace_syminfo_t dts; | |||||
| if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { | |||||
| xo_emit("{:mod/%s}", dts.dts_object); | |||||
| } else { | |||||
| xo_emit("{:mod/0x%llx}", (u_longlong_t)pc); | |||||
| } | |||||
| return (0); | |||||
| } | |||||
| int | |||||
| dt_print_mod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) | dt_print_mod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) | ||||
| { | { | ||||
| /* LINTED - alignment */ | /* LINTED - alignment */ | ||||
| uint64_t pc = *((uint64_t *)addr); | uint64_t pc = *((uint64_t *)addr); | ||||
| dtrace_syminfo_t dts; | dtrace_syminfo_t dts; | ||||
| char c[PATH_MAX * 2]; | char c[PATH_MAX * 2]; | ||||
| if (format == NULL) | if (format == NULL) | ||||
| format = " %-50s"; | format = " %-50s"; | ||||
| if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { | if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { | ||||
| (void) snprintf(c, sizeof (c), "%s", dts.dts_object); | (void) snprintf(c, sizeof (c), "%s", dts.dts_object); | ||||
| } else { | } else { | ||||
| (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); | (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); | ||||
| } | } | ||||
| if (dt_printf(dtp, fp, format, c) < 0) | if (dt_printf(dtp, fp, format, c) < 0) | ||||
| return (-1); | return (-1); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static char * | |||||
| dt_format_bytes_get(dtrace_hdl_t *dtp, caddr_t addr, size_t nbytes) | |||||
| { | |||||
| char *s = dt_alloc(dtp, nbytes * 2 + 2 + 1); /* 2 bytes per byte + 0x + '\0' */ | |||||
| char t[6]; | |||||
| char *c = (char *)addr; | |||||
| size_t i, j; | |||||
| if (s == NULL) | |||||
| return (NULL); | |||||
| /* | |||||
| * XXX: Some duplication with dt_print_bytes(). | |||||
| */ | |||||
| for (i = 0; i < nbytes; i++) { | |||||
| if (isprint(c[i]) || isspace(c[i]) || c[i] == '\b' || c[i] == '\a') | |||||
| continue; | |||||
| if (c[i] == '\0' && i > 0) { | |||||
| for (j = i + 1; j < nbytes; j++) { | |||||
| if (c[j] != '\0') | |||||
| break; | |||||
| } | |||||
| if (j != nbytes) | |||||
| break; | |||||
| memcpy(s, c, nbytes); | |||||
| return (s); | |||||
| } | |||||
| break; | |||||
| } | |||||
| if (i == nbytes) { | |||||
| memcpy(s, c, nbytes); | |||||
| s[nbytes] = '\0'; | |||||
| return (s); | |||||
| } | |||||
| s[0] = '0'; | |||||
| s[1] = 'x'; | |||||
| for (i = 0; i < nbytes; i++) { | |||||
| snprintf(t, sizeof(t), "%02x", (uchar_t)c[i]); | |||||
Done Inline ActionsConsider using "snprintf(t, sizeof(t) ..." just so the next developer doesn't have to think about the use of sprintf. phil: Consider using "snprintf(t, sizeof(t) ..." just so the next developer doesn't have to think… | |||||
| memcpy(s + (i * 2) + 2, t, 2); | |||||
| } | |||||
| s[nbytes * 2 + 2] = 0; | |||||
| return (s); | |||||
| } | |||||
| static int | static int | ||||
| dt_format_memory(dtrace_hdl_t *dtp, caddr_t addr) | |||||
| { | |||||
| size_t nbytes = *((uintptr_t *) addr); | |||||
| char *s; | |||||
| s = dt_format_bytes_get(dtp, addr + sizeof(uintptr_t), nbytes); | |||||
| if (s == NULL) | |||||
| return (-1); | |||||
| xo_emit("{:printm/%s}", s); | |||||
| dt_free(dtp, s); | |||||
| return (0); | |||||
| } | |||||
| static int | |||||
| dt_print_memory(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr) | dt_print_memory(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr) | ||||
| { | { | ||||
| int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); | int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); | ||||
| size_t nbytes = *((uintptr_t *) addr); | size_t nbytes = *((uintptr_t *) addr); | ||||
| return (dt_print_bytes(dtp, fp, addr + sizeof(uintptr_t), | return (dt_print_bytes(dtp, fp, addr + sizeof(uintptr_t), | ||||
| nbytes, 50, quiet, 1)); | nbytes, 50, quiet, 1)); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 188 Lines • ▼ Show 20 Lines | dt_trunc(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec) | ||||
| trunc.dttd_remaining = remaining; | trunc.dttd_remaining = remaining; | ||||
| (void) func(dtp, dt_trunc_agg, &trunc); | (void) func(dtp, dt_trunc_agg, &trunc); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static int | static int | ||||
| dt_format_datum(dtrace_hdl_t *dtp, dtrace_recdesc_t *rec, caddr_t addr, | |||||
| size_t size, const dtrace_aggdata_t *aggdata, uint64_t normal, | |||||
| dt_print_aggdata_t *pd) | |||||
| { | |||||
| dtrace_actkind_t act = rec->dtrd_action; | |||||
| boolean_t packed = pd->dtpa_agghist || pd->dtpa_aggpack; | |||||
| dtrace_aggdesc_t *agg = aggdata->dtada_desc; | |||||
| char fmt[512]; | |||||
| char *s; | |||||
| if (packed && pd->dtpa_agghisthdr != agg->dtagd_varid) | |||||
| pd->dtpa_agghisthdr = agg->dtagd_varid; | |||||
| switch (act) { | |||||
| case DTRACEACT_STACK: | |||||
| return (dt_format_stack(dtp, addr, rec->dtrd_arg, | |||||
| rec->dtrd_size / rec->dtrd_arg)); | |||||
| case DTRACEACT_USTACK: | |||||
| case DTRACEACT_JSTACK: | |||||
| return (dt_format_ustack(dtp, addr, rec->dtrd_arg)); | |||||
| case DTRACEACT_USYM: | |||||
| case DTRACEACT_UADDR: | |||||
| return (dt_format_usym(dtp, addr, act)); | |||||
| case DTRACEACT_UMOD: | |||||
| return (dt_format_umod(dtp, addr)); | |||||
| case DTRACEACT_SYM: | |||||
| return (dt_format_sym(dtp, addr)); | |||||
| case DTRACEACT_MOD: | |||||
| return (dt_format_mod(dtp, addr)); | |||||
| case DTRACEAGG_QUANTIZE: | |||||
| return (dt_format_quantize(dtp, addr, size, normal)); | |||||
| case DTRACEAGG_LQUANTIZE: | |||||
| return (dt_format_lquantize(dtp, addr, size, normal)); | |||||
| case DTRACEAGG_LLQUANTIZE: | |||||
| return (dt_format_llquantize(dtp, addr, size, normal)); | |||||
| case DTRACEAGG_AVG: | |||||
| return (dt_format_average(dtp, addr, size, normal)); | |||||
| case DTRACEAGG_STDDEV: | |||||
| return (dt_format_stddev(dtp, addr, size, normal)); | |||||
| default: | |||||
| break; | |||||
| } | |||||
| switch (size) { | |||||
| case sizeof (uint64_t): | |||||
| snprintf(fmt, sizeof(fmt), "{:%s/%%lld}", pd->dtpa_keyname); | |||||
| xo_emit(fmt, (long long)*((uint64_t *)addr) / normal); | |||||
| break; | |||||
| case sizeof (uint32_t): | |||||
| snprintf(fmt, sizeof(fmt), "{:%s/%%d}", pd->dtpa_keyname); | |||||
| xo_emit(fmt, *((uint32_t *)addr) / (uint32_t)normal); | |||||
| break; | |||||
| case sizeof (uint16_t): | |||||
| snprintf(fmt, sizeof(fmt), "{:%s/%%d}", pd->dtpa_keyname); | |||||
| xo_emit(fmt, *((uint16_t *)addr) / (uint32_t)normal); | |||||
| break; | |||||
| case sizeof (uint8_t): | |||||
| snprintf(fmt, sizeof(fmt), "{:%s/%%d}", pd->dtpa_keyname); | |||||
| xo_emit(fmt, *((uint8_t *)addr) / (uint32_t)normal); | |||||
| break; | |||||
| default: | |||||
| s = dt_format_bytes_get(dtp, addr, size); | |||||
| if (s == NULL) | |||||
| return (-1); | |||||
| xo_emit("{:value/%s}", s); | |||||
| dt_free(dtp, s); | |||||
| break; | |||||
| } | |||||
| return (0); | |||||
| } | |||||
| static int | |||||
| dt_print_datum(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec, | dt_print_datum(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec, | ||||
| caddr_t addr, size_t size, const dtrace_aggdata_t *aggdata, | caddr_t addr, size_t size, const dtrace_aggdata_t *aggdata, | ||||
| uint64_t normal, dt_print_aggdata_t *pd) | uint64_t normal, dt_print_aggdata_t *pd) | ||||
| { | { | ||||
| int err, width; | int err, width; | ||||
| dtrace_actkind_t act = rec->dtrd_action; | dtrace_actkind_t act = rec->dtrd_action; | ||||
| boolean_t packed = pd->dtpa_agghist || pd->dtpa_aggpack; | boolean_t packed = pd->dtpa_agghist || pd->dtpa_aggpack; | ||||
| dtrace_aggdesc_t *agg = aggdata->dtada_desc; | dtrace_aggdesc_t *agg = aggdata->dtada_desc; | ||||
| Show All 39 Lines | if (packed && pd->dtpa_agghisthdr != agg->dtagd_varid) { | ||||
| pd->dtpa_agghisthdr = agg->dtagd_varid; | pd->dtpa_agghisthdr = agg->dtagd_varid; | ||||
| } | } | ||||
| if (pd->dtpa_agghist && DTRACEACT_ISAGG(act)) { | if (pd->dtpa_agghist && DTRACEACT_ISAGG(act)) { | ||||
| char positives = aggdata->dtada_flags & DTRACE_A_HASPOSITIVES; | char positives = aggdata->dtada_flags & DTRACE_A_HASPOSITIVES; | ||||
| char negatives = aggdata->dtada_flags & DTRACE_A_HASNEGATIVES; | char negatives = aggdata->dtada_flags & DTRACE_A_HASNEGATIVES; | ||||
| int64_t val; | int64_t val; | ||||
Not Done Inline ActionsWhy not pass fmt as a string literal? markj: Why not pass `fmt` as a string literal? | |||||
Not Done Inline ActionsAssuming it just needs the variable field name. I'm adding a function xo_emit_field() that will support this: xo_ssize_t
xo_emit_field(const char *rolmod, const char *content, const char *fmt,
const char *efmt, ...);
xo_emit_field("V", pd->dtpa_keyname, "%lld", "%lld", (long long)*((uint64_t *)addr) / normal);Again, that will be in the next import of libxo. phil: Assuming it just needs the variable field name. I'm adding a function xo_emit_field() that will… | |||||
Done Inline Actions@phil's comment is correct, this was done because I needed a variable field name. The xo_emit_field function seems neat, thanks! I believe that this is the only function that would make use of it, so it shouldn't be too hard to change if the timing turns out to be off. domagoj.stolfa_gmail.com: @phil's comment is correct, this was done because I needed a variable field name. The… | |||||
| assert(act == DTRACEAGG_SUM || act == DTRACEAGG_COUNT); | assert(act == DTRACEAGG_SUM || act == DTRACEAGG_COUNT); | ||||
| val = (long long)*((uint64_t *)addr); | val = (long long)*((uint64_t *)addr); | ||||
| if (dt_printf(dtp, fp, " ") < 0) | if (dt_printf(dtp, fp, " ") < 0) | ||||
| return (-1); | return (-1); | ||||
| return (dt_print_quantline(dtp, fp, val, normal, | return (dt_print_quantline(dtp, fp, val, normal, | ||||
| aggdata->dtada_total, positives, negatives)); | aggdata->dtada_total, positives, negatives)); | ||||
| ▲ Show 20 Lines • Show All 82 Lines • ▼ Show 20 Lines | default: | ||||
| err = dt_print_bytes(dtp, fp, addr, size, width, 0, 0); | err = dt_print_bytes(dtp, fp, addr, size, width, 0, 0); | ||||
| break; | break; | ||||
| } | } | ||||
| return (err); | return (err); | ||||
| } | } | ||||
| int | int | ||||
| dt_format_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg) | |||||
| { | |||||
| int i, aggact = 0; | |||||
| dt_print_aggdata_t *pd = arg; | |||||
| const dtrace_aggdata_t *aggdata = aggsdata[0]; | |||||
| dtrace_aggdesc_t *agg = aggdata->dtada_desc; | |||||
| dtrace_hdl_t *dtp = pd->dtpa_dtp; | |||||
| dtrace_recdesc_t *rec; | |||||
| dtrace_actkind_t act; | |||||
| caddr_t addr; | |||||
| size_t size; | |||||
| if (pd->dtpa_aggname == NULL) | |||||
| pd->dtpa_aggname = agg->dtagd_name; | |||||
| xo_open_instance("aggregation-data"); | |||||
| strcpy(pd->dtpa_keyname, "value"); | |||||
| xo_open_list("keys"); | |||||
| /* | |||||
| * Iterate over each record description in the key, printing the traced | |||||
| * data, skipping the first datum (the tuple member created by the | |||||
| * compiler). | |||||
| */ | |||||
| for (i = 1; i < agg->dtagd_nrecs; i++) { | |||||
| rec = &agg->dtagd_rec[i]; | |||||
| act = rec->dtrd_action; | |||||
| addr = aggdata->dtada_data + rec->dtrd_offset; | |||||
| size = rec->dtrd_size; | |||||
| if (DTRACEACT_ISAGG(act)) { | |||||
| aggact = i; | |||||
| break; | |||||
| } | |||||
| xo_open_instance("keys"); | |||||
| if (dt_format_datum(dtp, rec, addr, | |||||
| size, aggdata, 1, pd) < 0) { | |||||
| xo_close_instance("keys"); | |||||
| xo_close_instance("aggregation-data"); | |||||
| return (-1); | |||||
| } | |||||
| xo_close_instance("keys"); | |||||
| if (dt_buffered_flush(dtp, NULL, rec, aggdata, | |||||
| DTRACE_BUFDATA_AGGKEY) < 0) { | |||||
| xo_close_instance("aggregation-data"); | |||||
| return (-1); | |||||
| } | |||||
| } | |||||
| xo_close_list("keys"); | |||||
| assert(aggact != 0); | |||||
| for (i = (naggvars == 1 ? 0 : 1); i < naggvars; i++) { | |||||
| uint64_t normal; | |||||
| aggdata = aggsdata[i]; | |||||
| agg = aggdata->dtada_desc; | |||||
| rec = &agg->dtagd_rec[aggact]; | |||||
| act = rec->dtrd_action; | |||||
| addr = aggdata->dtada_data + rec->dtrd_offset; | |||||
| size = rec->dtrd_size; | |||||
| assert(DTRACEACT_ISAGG(act)); | |||||
| switch (act) { | |||||
| case DTRACEAGG_MIN: | |||||
| strcpy(pd->dtpa_keyname, "min"); | |||||
| break; | |||||
| case DTRACEAGG_MAX: | |||||
| strcpy(pd->dtpa_keyname, "max"); | |||||
| break; | |||||
| case DTRACEAGG_COUNT: | |||||
| strcpy(pd->dtpa_keyname, "count"); | |||||
| break; | |||||
| case DTRACEAGG_SUM: | |||||
| strcpy(pd->dtpa_keyname, "sum"); | |||||
| break; | |||||
| default: | |||||
| strcpy(pd->dtpa_keyname, "UNKNOWN"); | |||||
| break; | |||||
| } | |||||
| normal = aggdata->dtada_normal; | |||||
| if (dt_format_datum(dtp, rec, addr, size, | |||||
| aggdata, normal, pd) < 0) { | |||||
| xo_close_instance("aggregation-data"); | |||||
| return (-1); | |||||
| } | |||||
| if (dt_buffered_flush(dtp, NULL, rec, aggdata, | |||||
| DTRACE_BUFDATA_AGGVAL) < 0) { | |||||
| xo_close_instance("aggregation-data"); | |||||
| return (-1); | |||||
| } | |||||
| if (!pd->dtpa_allunprint) | |||||
| agg->dtagd_flags |= DTRACE_AGD_PRINTED; | |||||
| } | |||||
| if (dt_buffered_flush(dtp, NULL, NULL, aggdata, | |||||
| DTRACE_BUFDATA_AGGFORMAT | DTRACE_BUFDATA_AGGLAST) < 0) { | |||||
| xo_close_instance("aggregation-data"); | |||||
| return (-1); | |||||
| } | |||||
| xo_close_instance("aggregation-data"); | |||||
| return (0); | |||||
| } | |||||
| int | |||||
| dt_print_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg) | dt_print_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg) | ||||
| { | { | ||||
| int i, aggact = 0; | int i, aggact = 0; | ||||
| dt_print_aggdata_t *pd = arg; | dt_print_aggdata_t *pd = arg; | ||||
| const dtrace_aggdata_t *aggdata = aggsdata[0]; | const dtrace_aggdata_t *aggdata = aggsdata[0]; | ||||
| dtrace_aggdesc_t *agg = aggdata->dtada_desc; | dtrace_aggdesc_t *agg = aggdata->dtada_desc; | ||||
| FILE *fp = pd->dtpa_fp; | FILE *fp = pd->dtpa_fp; | ||||
| dtrace_hdl_t *dtp = pd->dtpa_dtp; | dtrace_hdl_t *dtp = pd->dtpa_dtp; | ||||
| ▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | dt_print_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg) | ||||
| if (dt_buffered_flush(dtp, NULL, NULL, aggdata, | if (dt_buffered_flush(dtp, NULL, NULL, aggdata, | ||||
| DTRACE_BUFDATA_AGGFORMAT | DTRACE_BUFDATA_AGGLAST) < 0) | DTRACE_BUFDATA_AGGFORMAT | DTRACE_BUFDATA_AGGLAST) < 0) | ||||
| return (-1); | return (-1); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| int | int | ||||
| dt_format_agg(const dtrace_aggdata_t *aggdata, void *arg) | |||||
| { | |||||
| dt_print_aggdata_t *pd = arg; | |||||
| dtrace_aggdesc_t *agg = aggdata->dtada_desc; | |||||
| dtrace_aggvarid_t aggvarid = pd->dtpa_id; | |||||
| if (pd->dtpa_allunprint) { | |||||
| if (agg->dtagd_flags & DTRACE_AGD_PRINTED) | |||||
| return (0); | |||||
| } else { | |||||
| /* | |||||
| * If we're not printing all unprinted aggregations, then the | |||||
| * aggregation variable ID denotes a specific aggregation | |||||
| * variable that we should print -- skip any other aggregations | |||||
| * that we encounter. | |||||
| */ | |||||
| if (agg->dtagd_nrecs == 0) | |||||
| return (0); | |||||
| if (aggvarid != agg->dtagd_varid) | |||||
| return (0); | |||||
| } | |||||
| return (dt_format_aggs(&aggdata, 1, arg)); | |||||
| } | |||||
| int | |||||
| dt_print_agg(const dtrace_aggdata_t *aggdata, void *arg) | dt_print_agg(const dtrace_aggdata_t *aggdata, void *arg) | ||||
| { | { | ||||
| dt_print_aggdata_t *pd = arg; | dt_print_aggdata_t *pd = arg; | ||||
| dtrace_aggdesc_t *agg = aggdata->dtada_desc; | dtrace_aggdesc_t *agg = aggdata->dtada_desc; | ||||
| dtrace_aggvarid_t aggvarid = pd->dtpa_id; | dtrace_aggvarid_t aggvarid = pd->dtpa_id; | ||||
| if (pd->dtpa_allunprint) { | if (pd->dtpa_allunprint) { | ||||
| if (agg->dtagd_flags & DTRACE_AGD_PRINTED) | if (agg->dtagd_flags & DTRACE_AGD_PRINTED) | ||||
| ▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | (void) snprintf(msg, len, "couldn't set option \"%s\" to \"%s\": %s\n", | ||||
| option, value, errstr); | option, value, errstr); | ||||
| if ((rval = dt_handle_liberr(dtp, data, msg)) == 0) | if ((rval = dt_handle_liberr(dtp, data, msg)) == 0) | ||||
| return (0); | return (0); | ||||
| return (rval); | return (rval); | ||||
| } | } | ||||
| /* | |||||
| * Helper functions to help maintain style(9) in dt_consume_cpu(). | |||||
| */ | |||||
| static int | static int | ||||
| dt_oformat_agg_sorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, | |||||
| dt_print_aggdata_t *pd) | |||||
| { | |||||
| int r; | |||||
| r = dtrace_aggregate_walk_sorted(dtp, dt_format_agg, pd); | |||||
| if (r < 0) { | |||||
| xo_close_list("aggregation-data"); | |||||
| xo_emit("{:aggregation-name/%s}", pd->dtpa_aggname); | |||||
| xo_close_instance("output"); | |||||
| } | |||||
| return (r); | |||||
| } | |||||
| static void | |||||
| dt_oformat_agg_name(dt_print_aggdata_t *pd) | |||||
| { | |||||
| xo_close_list("aggregation-data"); | |||||
| xo_emit("{:aggregation-name/%s}", pd->dtpa_aggname); | |||||
| } | |||||
| static int | |||||
| dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, int cpu, | dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, int cpu, | ||||
| dtrace_bufdesc_t *buf, boolean_t just_one, | dtrace_bufdesc_t *buf, boolean_t just_one, | ||||
| dtrace_consume_probe_f *efunc, dtrace_consume_rec_f *rfunc, void *arg) | dtrace_consume_probe_f *efunc, dtrace_consume_rec_f *rfunc, void *arg) | ||||
| { | { | ||||
| dtrace_epid_t id; | dtrace_epid_t id; | ||||
| size_t offs; | size_t offs; | ||||
| int flow = (dtp->dt_options[DTRACEOPT_FLOWINDENT] != DTRACEOPT_UNSET); | int flow = (dtp->dt_options[DTRACEOPT_FLOWINDENT] != DTRACEOPT_UNSET); | ||||
| int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); | int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); | ||||
| int rval, i, n; | int rval, i, n; | ||||
| uint64_t tracememsize = 0; | uint64_t tracememsize = 0; | ||||
| dtrace_probedata_t data; | dtrace_probedata_t data; | ||||
| uint64_t drops; | uint64_t drops; | ||||
| size_t skip_format; | |||||
| bzero(&data, sizeof (data)); | bzero(&data, sizeof (data)); | ||||
| data.dtpda_handle = dtp; | data.dtpda_handle = dtp; | ||||
| data.dtpda_cpu = cpu; | data.dtpda_cpu = cpu; | ||||
| data.dtpda_flow = dtp->dt_flow; | data.dtpda_flow = dtp->dt_flow; | ||||
| data.dtpda_indent = dtp->dt_indent; | data.dtpda_indent = dtp->dt_indent; | ||||
| data.dtpda_prefix = dtp->dt_prefix; | data.dtpda_prefix = dtp->dt_prefix; | ||||
| Show All 15 Lines | for (offs = buf->dtbd_oldest; offs < buf->dtbd_size; ) { | ||||
| } | } | ||||
| if ((rval = dt_epid_lookup(dtp, id, &data.dtpda_edesc, | if ((rval = dt_epid_lookup(dtp, id, &data.dtpda_edesc, | ||||
| &data.dtpda_pdesc)) != 0) | &data.dtpda_pdesc)) != 0) | ||||
| return (rval); | return (rval); | ||||
| epd = data.dtpda_edesc; | epd = data.dtpda_edesc; | ||||
| data.dtpda_data = buf->dtbd_data + offs; | data.dtpda_data = buf->dtbd_data + offs; | ||||
| data.dtpda_timestamp = DTRACE_RECORD_LOAD_TIMESTAMP( | |||||
| (struct dtrace_rechdr *)data.dtpda_data); | |||||
| if (data.dtpda_edesc->dtepd_uarg != DT_ECB_DEFAULT) { | if (data.dtpda_edesc->dtepd_uarg != DT_ECB_DEFAULT) { | ||||
| rval = dt_handle(dtp, &data); | rval = dt_handle(dtp, &data); | ||||
| if (rval == DTRACE_CONSUME_NEXT) | if (rval == DTRACE_CONSUME_NEXT) | ||||
| goto nextepid; | goto nextepid; | ||||
| if (rval == DTRACE_CONSUME_ERROR) | if (rval == DTRACE_CONSUME_ERROR) | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| if (flow) | if (flow) | ||||
| (void) dt_flowindent(dtp, &data, dtp->dt_last_epid, | (void) dt_flowindent(dtp, &data, dtp->dt_last_epid, | ||||
| buf, offs); | buf, offs); | ||||
| if (dtp->dt_oformat) | |||||
| xo_open_instance("probes"); | |||||
| rval = (*efunc)(&data, arg); | rval = (*efunc)(&data, arg); | ||||
| if (flow) { | if (flow) { | ||||
| if (data.dtpda_flow == DTRACEFLOW_ENTRY) | if (data.dtpda_flow == DTRACEFLOW_ENTRY) | ||||
| data.dtpda_indent += 2; | data.dtpda_indent += 2; | ||||
| } | } | ||||
| if (rval == DTRACE_CONSUME_NEXT) | if (rval == DTRACE_CONSUME_NEXT) | ||||
| goto nextepid; | goto nextepid; | ||||
| if (rval == DTRACE_CONSUME_ABORT) | if (rval == DTRACE_CONSUME_ABORT) | ||||
| return (dt_set_errno(dtp, EDT_DIRABORT)); | return (dt_set_errno(dtp, EDT_DIRABORT)); | ||||
| if (rval != DTRACE_CONSUME_THIS) | if (rval != DTRACE_CONSUME_THIS) | ||||
| return (dt_set_errno(dtp, EDT_BADRVAL)); | return (dt_set_errno(dtp, EDT_BADRVAL)); | ||||
| skip_format = 0; | |||||
| if (dtp->dt_oformat) | |||||
| xo_open_list("output"); | |||||
| for (i = 0; i < epd->dtepd_nrecs; i++) { | for (i = 0; i < epd->dtepd_nrecs; i++) { | ||||
| caddr_t addr; | caddr_t addr; | ||||
| dtrace_recdesc_t *rec = &epd->dtepd_rec[i]; | dtrace_recdesc_t *rec = &epd->dtepd_rec[i]; | ||||
| dtrace_actkind_t act = rec->dtrd_action; | dtrace_actkind_t act = rec->dtrd_action; | ||||
| if (skip_format > 0) | |||||
| skip_format--; | |||||
| data.dtpda_data = buf->dtbd_data + offs + | data.dtpda_data = buf->dtbd_data + offs + | ||||
| rec->dtrd_offset; | rec->dtrd_offset; | ||||
| addr = data.dtpda_data; | addr = data.dtpda_data; | ||||
| if (act == DTRACEACT_LIBACT) { | if (act == DTRACEACT_LIBACT) { | ||||
| uint64_t arg = rec->dtrd_arg; | uint64_t arg = rec->dtrd_arg; | ||||
| dtrace_aggvarid_t id; | dtrace_aggvarid_t id; | ||||
| ▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | for (i = 0; i < epd->dtepd_nrecs; i++) { | ||||
| continue; | continue; | ||||
| if (rval == DTRACE_CONSUME_ABORT) | if (rval == DTRACE_CONSUME_ABORT) | ||||
| return (dt_set_errno(dtp, EDT_DIRABORT)); | return (dt_set_errno(dtp, EDT_DIRABORT)); | ||||
| if (rval != DTRACE_CONSUME_THIS) | if (rval != DTRACE_CONSUME_THIS) | ||||
| return (dt_set_errno(dtp, EDT_BADRVAL)); | return (dt_set_errno(dtp, EDT_BADRVAL)); | ||||
| if (dtp->dt_oformat && rec->dtrd_size > 0) | |||||
| xo_open_instance("output"); | |||||
| if (act == DTRACEACT_STACK) { | if (act == DTRACEACT_STACK) { | ||||
| int depth = rec->dtrd_arg; | int depth = rec->dtrd_arg; | ||||
| if (dt_print_stack(dtp, fp, NULL, addr, depth, | if (dtp->dt_oformat) { | ||||
| if (dt_format_stack(dtp, addr, depth, | |||||
| rec->dtrd_size / depth) < 0) { | |||||
| xo_close_instance("output"); | |||||
| return (-1); | |||||
| } | |||||
| } else { | |||||
| if (dt_print_stack(dtp, | |||||
| fp, NULL, addr, depth, | |||||
| rec->dtrd_size / depth) < 0) | rec->dtrd_size / depth) < 0) | ||||
| return (-1); | return (-1); | ||||
| } | |||||
| goto nextrec; | goto nextrec; | ||||
| } | } | ||||
| if (act == DTRACEACT_USTACK || | if (act == DTRACEACT_USTACK || | ||||
| act == DTRACEACT_JSTACK) { | act == DTRACEACT_JSTACK) { | ||||
| if (dtp->dt_oformat) { | |||||
| if (dt_format_ustack(dtp, addr, | |||||
| rec->dtrd_arg) < 0) { | |||||
| xo_close_instance("output"); | |||||
| return (-1); | |||||
| } | |||||
| } else { | |||||
| if (dt_print_ustack(dtp, fp, NULL, | if (dt_print_ustack(dtp, fp, NULL, | ||||
| addr, rec->dtrd_arg) < 0) | addr, rec->dtrd_arg) < 0) | ||||
| return (-1); | return (-1); | ||||
| } | |||||
| goto nextrec; | goto nextrec; | ||||
| } | } | ||||
| if (act == DTRACEACT_SYM) { | if (act == DTRACEACT_SYM) { | ||||
| if (dtp->dt_oformat) { | |||||
| if (dt_format_sym(dtp, addr) < 0) { | |||||
| xo_close_instance("output"); | |||||
| return (-1); | |||||
| } | |||||
| } else { | |||||
| if (dt_print_sym(dtp, fp, NULL, addr) < 0) | if (dt_print_sym(dtp, fp, NULL, addr) < 0) | ||||
| return (-1); | return (-1); | ||||
| } | |||||
| goto nextrec; | goto nextrec; | ||||
| } | } | ||||
| if (act == DTRACEACT_MOD) { | if (act == DTRACEACT_MOD) { | ||||
| if (dtp->dt_oformat) { | |||||
| if (dt_format_mod(dtp, addr) < 0) { | |||||
| xo_close_instance("output"); | |||||
| return (-1); | |||||
| } | |||||
| } else { | |||||
| if (dt_print_mod(dtp, fp, NULL, addr) < 0) | if (dt_print_mod(dtp, fp, NULL, addr) < 0) | ||||
| return (-1); | return (-1); | ||||
| } | |||||
| goto nextrec; | goto nextrec; | ||||
| } | } | ||||
| if (act == DTRACEACT_USYM || act == DTRACEACT_UADDR) { | if (act == DTRACEACT_USYM || act == DTRACEACT_UADDR) { | ||||
| if (dtp->dt_oformat) { | |||||
| if (dt_format_usym(dtp, addr, act) < 0) { | |||||
| xo_close_instance("output"); | |||||
| return (-1); | |||||
| } | |||||
| } else { | |||||
| if (dt_print_usym(dtp, fp, addr, act) < 0) | if (dt_print_usym(dtp, fp, addr, act) < 0) | ||||
| return (-1); | return (-1); | ||||
| } | |||||
| goto nextrec; | goto nextrec; | ||||
| } | } | ||||
| if (act == DTRACEACT_UMOD) { | if (act == DTRACEACT_UMOD) { | ||||
| if (dtp->dt_oformat) { | |||||
| if (dt_format_umod(dtp, addr) < 0) { | |||||
| xo_close_instance("output"); | |||||
| return (-1); | |||||
| } | |||||
| } else { | |||||
| if (dt_print_umod(dtp, fp, NULL, addr) < 0) | if (dt_print_umod(dtp, fp, NULL, addr) < 0) | ||||
| return (-1); | return (-1); | ||||
| } | |||||
| goto nextrec; | goto nextrec; | ||||
| } | } | ||||
| if (act == DTRACEACT_PRINTM) { | if (act == DTRACEACT_PRINTM) { | ||||
| if (dtp->dt_oformat) { | |||||
| if (dt_format_memory(dtp, addr) < 0) { | |||||
| xo_close_instance("output"); | |||||
| return (-1); | |||||
| } | |||||
| } else { | |||||
| if (dt_print_memory(dtp, fp, addr) < 0) | if (dt_print_memory(dtp, fp, addr) < 0) | ||||
| return (-1); | return (-1); | ||||
| } | |||||
| goto nextrec; | goto nextrec; | ||||
| } | } | ||||
| if (DTRACEACT_ISPRINTFLIKE(act)) { | if (dtp->dt_oformat == DTRACE_OFORMAT_TEXT && | ||||
| DTRACEACT_ISPRINTFLIKE(act)) { | |||||
| void *fmtdata; | void *fmtdata; | ||||
| int (*func)(dtrace_hdl_t *, FILE *, void *, | int (*func)(dtrace_hdl_t *, FILE *, void *, | ||||
| const dtrace_probedata_t *, | const dtrace_probedata_t *, | ||||
| const dtrace_recdesc_t *, uint_t, | const dtrace_recdesc_t *, uint_t, | ||||
| const void *buf, size_t); | const void *buf, size_t); | ||||
| if ((fmtdata = dt_format_lookup(dtp, | if ((fmtdata = dt_format_lookup(dtp, | ||||
| rec->dtrd_format)) == NULL) | rec->dtrd_format)) == NULL) | ||||
| Show All 23 Lines | for (i = 0; i < epd->dtepd_nrecs; i++) { | ||||
| return (-1); /* errno is set for us */ | return (-1); /* errno is set for us */ | ||||
| if (n > 0) | if (n > 0) | ||||
| i += n - 1; | i += n - 1; | ||||
| goto nextrec; | goto nextrec; | ||||
| } | } | ||||
| /* | /* | ||||
| * We don't care about a formatted printa, system or | |||||
| * freopen for oformat. | |||||
| */ | |||||
| if (dtp->dt_oformat && act == DTRACEACT_PRINTF && | |||||
| skip_format == 0) { | |||||
| void *fmtdata; | |||||
| if ((fmtdata = dt_format_lookup(dtp, | |||||
| rec->dtrd_format)) == NULL) | |||||
| goto nofmt; | |||||
| n = dtrace_sprintf(dtp, fp, fmtdata, rec, | |||||
| epd->dtepd_nrecs - i, | |||||
| (uchar_t *)buf->dtbd_data + offs, | |||||
| buf->dtbd_size - offs); | |||||
| if (n < 0) { | |||||
| xo_close_instance("output"); | |||||
| return (-1); /* errno is set for us */ | |||||
| } | |||||
| xo_emit("{:message/%s}", dtp->dt_sprintf_buf); | |||||
| skip_format += n; | |||||
| /* | |||||
| * We want the "message" object to be its own | |||||
| * thing, but we still want to process the | |||||
| * current DIFEXPR in case there is a value | |||||
| * attached to it. If there is, we need to | |||||
| * re-open a new output instance, as otherwise | |||||
| * the message ends up bundled with the first | |||||
| * value. | |||||
| * | |||||
| * XXX: There is an edge case where a | |||||
| * printf("hello"); will produce a DIFO that | |||||
| * returns 0 attached to it and we have no good | |||||
| * way to determine if this 0 value is because | |||||
| * there's no real data attached to the printf | |||||
| * as an argument, or it's because the argument | |||||
| * actually returns 0. | |||||
| */ | |||||
| if (skip_format == 0) | |||||
| goto nextrec; | |||||
| xo_close_instance("output"); | |||||
| xo_open_instance("output"); | |||||
| } | |||||
| /* | |||||
| * If this is a DIF expression, and the record has a | * If this is a DIF expression, and the record has a | ||||
| * format set, this indicates we have a CTF type name | * format set, this indicates we have a CTF type name | ||||
| * associated with the data and we should try to print | * associated with the data and we should try to print | ||||
| * it out by type. | * it out by type. | ||||
| */ | */ | ||||
| if (act == DTRACEACT_DIFEXPR) { | if (act == DTRACEACT_DIFEXPR) { | ||||
| const char *strdata = dt_strdata_lookup(dtp, | const char *strdata = dt_strdata_lookup(dtp, | ||||
| rec->dtrd_format); | rec->dtrd_format); | ||||
| if (strdata != NULL) { | if (strdata != NULL) { | ||||
| n = dtrace_print(dtp, fp, strdata, | if (dtp->dt_oformat) | ||||
| addr, rec->dtrd_size); | n = dtrace_format_print(dtp, fp, | ||||
| strdata, addr, | |||||
| rec->dtrd_size); | |||||
| else | |||||
| n = dtrace_print(dtp, fp, | |||||
| strdata, addr, | |||||
| rec->dtrd_size); | |||||
| /* | /* | ||||
| * dtrace_print() will return -1 on | * dtrace_print() will return -1 on | ||||
| * error, or return the number of bytes | * error, or return the number of bytes | ||||
| * consumed. It will return 0 if the | * consumed. It will return 0 if the | ||||
| * type couldn't be determined, and we | * type couldn't be determined, and we | ||||
| * should fall through to the normal | * should fall through to the normal | ||||
| * trace method. | * trace method. | ||||
| */ | */ | ||||
| if (n < 0) | if (n < 0) { | ||||
| if (dtp->dt_oformat) | |||||
| xo_close_instance( | |||||
| "output"); | |||||
| return (-1); | return (-1); | ||||
| } | |||||
| if (n > 0) | if (n > 0) | ||||
| goto nextrec; | goto nextrec; | ||||
| } | } | ||||
| } | } | ||||
| nofmt: | nofmt: | ||||
| if (act == DTRACEACT_PRINTA) { | if (act == DTRACEACT_PRINTA) { | ||||
| dt_print_aggdata_t pd; | dt_print_aggdata_t pd; | ||||
| dtrace_aggvarid_t *aggvars; | dtrace_aggvarid_t *aggvars; | ||||
| int j, naggvars = 0; | int j, naggvars = 0; | ||||
| size_t size = ((epd->dtepd_nrecs - i) * | size_t size = ((epd->dtepd_nrecs - i) * | ||||
| sizeof (dtrace_aggvarid_t)); | sizeof (dtrace_aggvarid_t)); | ||||
| if ((aggvars = dt_alloc(dtp, size)) == NULL) | if ((aggvars = dt_alloc(dtp, size)) == NULL) { | ||||
| if (dtp->dt_oformat) | |||||
| xo_close_instance("output"); | |||||
| return (-1); | return (-1); | ||||
| } | |||||
| /* | /* | ||||
| * This might be a printa() with multiple | * This might be a printa() with multiple | ||||
| * aggregation variables. We need to scan | * aggregation variables. We need to scan | ||||
| * forward through the records until we find | * forward through the records until we find | ||||
| * a record from a different statement. | * a record from a different statement. | ||||
| */ | */ | ||||
| for (j = i; j < epd->dtepd_nrecs; j++) { | for (j = i; j < epd->dtepd_nrecs; j++) { | ||||
| dtrace_recdesc_t *nrec; | dtrace_recdesc_t *nrec; | ||||
| caddr_t naddr; | caddr_t naddr; | ||||
| nrec = &epd->dtepd_rec[j]; | nrec = &epd->dtepd_rec[j]; | ||||
| if (nrec->dtrd_uarg != rec->dtrd_uarg) | if (nrec->dtrd_uarg != rec->dtrd_uarg) | ||||
| break; | break; | ||||
| if (nrec->dtrd_action != act) { | if (nrec->dtrd_action != act) { | ||||
| if (dtp->dt_oformat) | |||||
| xo_close_instance( | |||||
| "output"); | |||||
| return (dt_set_errno(dtp, | return (dt_set_errno(dtp, | ||||
| EDT_BADAGG)); | EDT_BADAGG)); | ||||
| } | } | ||||
| naddr = buf->dtbd_data + offs + | naddr = buf->dtbd_data + offs + | ||||
| nrec->dtrd_offset; | nrec->dtrd_offset; | ||||
| aggvars[naggvars++] = | aggvars[naggvars++] = | ||||
| /* LINTED - alignment */ | /* LINTED - alignment */ | ||||
| *((dtrace_aggvarid_t *)naddr); | *((dtrace_aggvarid_t *)naddr); | ||||
| } | } | ||||
| i = j - 1; | i = j - 1; | ||||
| bzero(&pd, sizeof (pd)); | bzero(&pd, sizeof (pd)); | ||||
| pd.dtpa_dtp = dtp; | pd.dtpa_dtp = dtp; | ||||
| pd.dtpa_fp = fp; | pd.dtpa_fp = fp; | ||||
| assert(naggvars >= 1); | assert(naggvars >= 1); | ||||
| if (dtp->dt_oformat) | |||||
| xo_open_list("aggregation-data"); | |||||
| if (naggvars == 1) { | if (naggvars == 1) { | ||||
Done Inline ActionsUse full words and also use dashes instead of underscores. Users can use the "underscores" option if they need to turn dashes into underscores. Use hyphens, not underscores Use of hyphens is traditional in XML, and the XOF_UNDERSCORES flag can be used to generate underscores in JSON, if desired. But the raw field name should use hyphens. phil: Use full words and also use dashes instead of underscores. Users can use the "underscores"… | |||||
| pd.dtpa_id = aggvars[0]; | pd.dtpa_id = aggvars[0]; | ||||
| dt_free(dtp, aggvars); | dt_free(dtp, aggvars); | ||||
| if (dtp->dt_oformat) { | |||||
| n = dt_oformat_agg_sorted(dtp, | |||||
| dt_format_agg, &pd); | |||||
| if (n < 0) | |||||
| return (-1); | |||||
| } else { | |||||
| if (dt_printf(dtp, fp, "\n") < 0 || | if (dt_printf(dtp, fp, "\n") < 0 || | ||||
| dtrace_aggregate_walk_sorted(dtp, | dtrace_aggregate_walk_sorted(dtp, | ||||
| dt_print_agg, &pd) < 0) | dt_print_agg, &pd) < 0) | ||||
| return (-1); | return (-1); | ||||
| } | |||||
| if (dtp->dt_oformat) | |||||
| dt_oformat_agg_name(&pd); | |||||
| goto nextrec; | goto nextrec; | ||||
| } | } | ||||
| if (dtp->dt_oformat) { | |||||
| if (dtrace_aggregate_walk_joined(dtp, | |||||
| aggvars, naggvars, | |||||
| dt_format_aggs, &pd) < 0) { | |||||
| dt_oformat_agg_name(&pd); | |||||
| xo_close_instance("output"); | |||||
| dt_free(dtp, aggvars); | |||||
| return (-1); | |||||
| } | |||||
| } else { | |||||
| if (dt_printf(dtp, fp, "\n") < 0 || | if (dt_printf(dtp, fp, "\n") < 0 || | ||||
| dtrace_aggregate_walk_joined(dtp, aggvars, | dtrace_aggregate_walk_joined(dtp, | ||||
| naggvars, dt_print_aggs, &pd) < 0) { | aggvars, naggvars, | ||||
| dt_print_aggs, &pd) < 0) { | |||||
| dt_free(dtp, aggvars); | dt_free(dtp, aggvars); | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| } | |||||
| if (dtp->dt_oformat) | |||||
| dt_oformat_agg_name(&pd); | |||||
| dt_free(dtp, aggvars); | dt_free(dtp, aggvars); | ||||
| goto nextrec; | goto nextrec; | ||||
| } | } | ||||
| if (act == DTRACEACT_TRACEMEM) { | if (act == DTRACEACT_TRACEMEM) { | ||||
| if (tracememsize == 0 || | if (tracememsize == 0 || | ||||
| tracememsize > rec->dtrd_size) { | tracememsize > rec->dtrd_size) { | ||||
| tracememsize = rec->dtrd_size; | tracememsize = rec->dtrd_size; | ||||
| } | } | ||||
| if (dtp->dt_oformat) { | |||||
| char *s; | |||||
| s = dt_format_bytes_get(dtp, addr, | |||||
| tracememsize); | |||||
| n = xo_emit("{:tracemem/%s}", s); | |||||
| dt_free(dtp, s); | |||||
| } else { | |||||
Done Inline Actionsxo_emit returns a suitable error here: RETURN CODE xo_emit returns a negative value on error. If the XOF_COLUMNS flag has been turned on for the specific handle using xo_set_flags(3), then the number of display columns consumed by the output will be returned. Since your test is "(n < 0)", you should be able to just say "n = xo_emit(...)". phil: xo_emit returns a suitable error here:
RETURN CODE
xo_emit returns a negative value on… | |||||
| n = dt_print_bytes(dtp, fp, addr, | n = dt_print_bytes(dtp, fp, addr, | ||||
| tracememsize, -33, quiet, 1); | tracememsize, -33, quiet, 1); | ||||
| } | |||||
| tracememsize = 0; | tracememsize = 0; | ||||
| if (n < 0) | if (n < 0) | ||||
| return (-1); | return (-1); | ||||
| goto nextrec; | goto nextrec; | ||||
| } | } | ||||
| switch (rec->dtrd_size) { | switch (rec->dtrd_size) { | ||||
| case sizeof (uint64_t): | case sizeof (uint64_t): | ||||
| if (dtp->dt_oformat) { | |||||
| xo_emit("{:value/%lld}", | |||||
| *((unsigned long long *)addr)); | |||||
| n = 0; | |||||
| } else | |||||
| n = dt_printf(dtp, fp, | n = dt_printf(dtp, fp, | ||||
| quiet ? "%lld" : " %16lld", | quiet ? "%lld" : " %16lld", | ||||
| /* LINTED - alignment */ | /* LINTED - alignment */ | ||||
| *((unsigned long long *)addr)); | *((unsigned long long *)addr)); | ||||
| break; | break; | ||||
| case sizeof (uint32_t): | case sizeof (uint32_t): | ||||
| n = dt_printf(dtp, fp, quiet ? "%d" : " %8d", | if (dtp->dt_oformat) { | ||||
| xo_emit("{:value/%d}", | |||||
| *((uint32_t *)addr)); | |||||
| n = 0; | |||||
| } else | |||||
| n = dt_printf(dtp, fp, | |||||
| quiet ? "%d" : " %8d", | |||||
| /* LINTED - alignment */ | /* LINTED - alignment */ | ||||
| *((uint32_t *)addr)); | *((uint32_t *)addr)); | ||||
| break; | break; | ||||
| case sizeof (uint16_t): | case sizeof (uint16_t): | ||||
| n = dt_printf(dtp, fp, quiet ? "%d" : " %5d", | if (dtp->dt_oformat) { | ||||
| xo_emit("{:value/%d}", | |||||
| *((uint16_t *)addr)); | |||||
| n = 0; | |||||
| } else | |||||
| n = dt_printf(dtp, fp, | |||||
| quiet ? "%d" : " %5d", | |||||
| /* LINTED - alignment */ | /* LINTED - alignment */ | ||||
| *((uint16_t *)addr)); | *((uint16_t *)addr)); | ||||
| break; | break; | ||||
| case sizeof (uint8_t): | case sizeof (uint8_t): | ||||
| n = dt_printf(dtp, fp, quiet ? "%d" : " %3d", | if (dtp->dt_oformat) { | ||||
| xo_emit("{:value/%d}", | |||||
| *((uint8_t *)addr)); | *((uint8_t *)addr)); | ||||
| n = 0; | |||||
| } else | |||||
| n = dt_printf(dtp, fp, | |||||
| quiet ? "%d" : " %3d", | |||||
| *((uint8_t *)addr)); | |||||
| break; | break; | ||||
| default: | default: | ||||
| if (dtp->dt_oformat && rec->dtrd_size > 0) { | |||||
| char *s; | |||||
| s = dt_format_bytes_get(dtp, addr, | |||||
| rec->dtrd_size); | |||||
| xo_emit("{:value/%s}", s); | |||||
| dt_free(dtp, s); | |||||
| n = 0; | |||||
| } else { | |||||
| n = dt_print_bytes(dtp, fp, addr, | n = dt_print_bytes(dtp, fp, addr, | ||||
| rec->dtrd_size, -33, quiet, 0); | rec->dtrd_size, -33, quiet, 0); | ||||
| } | |||||
| break; | break; | ||||
| } | } | ||||
| if (dtp->dt_oformat && rec->dtrd_size > 0) | |||||
| xo_close_instance("output"); | |||||
| if (n < 0) | if (n < 0) | ||||
| return (-1); /* errno is set for us */ | return (-1); /* errno is set for us */ | ||||
| nextrec: | nextrec: | ||||
| if (dt_buffered_flush(dtp, &data, rec, NULL, 0) < 0) | if (dt_buffered_flush(dtp, &data, rec, NULL, 0) < 0) | ||||
| return (-1); /* errno is set for us */ | return (-1); /* errno is set for us */ | ||||
| } | } | ||||
| /* | /* | ||||
| * Call the record callback with a NULL record to indicate | * Call the record callback with a NULL record to indicate | ||||
| * that we're done processing this EPID. | * that we're done processing this EPID. | ||||
| */ | */ | ||||
| rval = (*rfunc)(&data, NULL, arg); | rval = (*rfunc)(&data, NULL, arg); | ||||
| nextepid: | nextepid: | ||||
| offs += epd->dtepd_size; | offs += epd->dtepd_size; | ||||
| dtp->dt_last_epid = id; | dtp->dt_last_epid = id; | ||||
| if (dtp->dt_oformat) { | |||||
| xo_close_list("output"); | |||||
| xo_close_instance("probes"); | |||||
| xo_flush(); | |||||
| } | |||||
| if (just_one) { | if (just_one) { | ||||
| buf->dtbd_oldest = offs; | buf->dtbd_oldest = offs; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| dtp->dt_flow = data.dtpda_flow; | dtp->dt_flow = data.dtpda_flow; | ||||
| dtp->dt_indent = data.dtpda_indent; | dtp->dt_indent = data.dtpda_indent; | ||||
| dtp->dt_prefix = data.dtpda_prefix; | dtp->dt_prefix = data.dtpda_prefix; | ||||
| if ((drops = buf->dtbd_drops) == 0) | if ((drops = buf->dtbd_drops) == 0) | ||||
| return (0); | return (0); | ||||
| /* | /* | ||||
| * Explicitly zero the drops to prevent us from processing them again. | * Explicitly zero the drops to prevent us from processing them again. | ||||
| */ | */ | ||||
| buf->dtbd_drops = 0; | buf->dtbd_drops = 0; | ||||
| return (dt_handle_cpudrop(dtp, cpu, DTRACEDROP_PRINCIPAL, drops)); | xo_open_instance("probes"); | ||||
| dt_oformat_drop(dtp, cpu); | |||||
| rval = dt_handle_cpudrop(dtp, cpu, DTRACEDROP_PRINCIPAL, drops); | |||||
| xo_close_instance("probes"); | |||||
| return (rval); | |||||
| } | } | ||||
| /* | /* | ||||
| * Reduce memory usage by shrinking the buffer if it's no more than half full. | * Reduce memory usage by shrinking the buffer if it's no more than half full. | ||||
| * Note, we need to preserve the alignment of the data at dtbd_oldest, which is | * Note, we need to preserve the alignment of the data at dtbd_oldest, which is | ||||
| * only 4-byte aligned. | * only 4-byte aligned. | ||||
| */ | */ | ||||
| static void | static void | ||||
| ▲ Show 20 Lines • Show All 486 Lines • ▼ Show 20 Lines | for (;;) { | ||||
| buf->dtbd_cpu, buf, B_TRUE, pf, rf, arg)) != 0) | buf->dtbd_cpu, buf, B_TRUE, pf, rf, arg)) != 0) | ||||
| return (rval); | return (rval); | ||||
| dt_pq_insert(dtp->dt_bufq, buf); | dt_pq_insert(dtp->dt_bufq, buf); | ||||
| } | } | ||||
| /* Consume drops. */ | /* Consume drops. */ | ||||
| for (i = 0; i < max_ncpus; i++) { | for (i = 0; i < max_ncpus; i++) { | ||||
| if (drops[i] != 0) { | if (drops[i] != 0) { | ||||
| int error = dt_handle_cpudrop(dtp, i, | int error; | ||||
| xo_open_instance("probes"); | |||||
| dt_oformat_drop(dtp, i); | |||||
| error = dt_handle_cpudrop(dtp, i, | |||||
| DTRACEDROP_PRINCIPAL, drops[i]); | DTRACEDROP_PRINCIPAL, drops[i]); | ||||
| xo_close_instance("probes"); | |||||
| if (error != 0) | if (error != 0) | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| } | } | ||||
| /* | /* | ||||
| * Reduce memory usage by re-allocating smaller buffers | * Reduce memory usage by re-allocating smaller buffers | ||||
| * for the "remnants". | * for the "remnants". | ||||
| */ | */ | ||||
| while (buf = dt_pq_walk(dtp->dt_bufq, &cookie)) | while (buf = dt_pq_walk(dtp->dt_bufq, &cookie)) | ||||
| dt_realloc_buf(dtp, buf, buf->dtbd_size); | dt_realloc_buf(dtp, buf, buf->dtbd_size); | ||||
| } | } | ||||
| return (0); | return (0); | ||||
| } | |||||
| void | |||||
| dtrace_oformat_probe(dtrace_hdl_t *dtp __unused, const dtrace_probedata_t *data, | |||||
| processorid_t cpu, dtrace_probedesc_t *pd) | |||||
| { | |||||
| xo_emit("{:timestamp/%llu} {:cpu/%d} {:id/%d} {:provider/%s} " | |||||
| "{:module/%s} {:function/%s} {:name/%s}", | |||||
| (unsigned long long)data->dtpda_timestamp, cpu, pd->dtpd_id, | |||||
| pd->dtpd_provider, pd->dtpd_mod, pd->dtpd_func, pd->dtpd_name); | |||||
| } | |||||
| void | |||||
| dt_oformat_drop(dtrace_hdl_t *dtp, processorid_t cpu) | |||||
| { | |||||
| xo_emit("{:cpu/%d} {:id/%d} {:provider/%s} " | |||||
| "{:module/%s} {:function/%s} {:name/%s}", | |||||
| cpu, -1, "dtrace", "INTERNAL", "INTERNAL", "DROP"); | |||||
| } | } | ||||
Let's please call this dt_format_quantize_prepare() to make it a bit clearer that this is a helper for dt_format_quantize(). Same for lquantize.