/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
*/
#include <sys/sysmacros.h>
#include <strings.h>
#include <stdlib.h>
#include <alloca.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <netdb.h>
#include <dt_printf.h>
#include <dt_string.h>
#include <dt_impl.h>
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
if (dt_node_is_usymaddr(dnp))
return (1);
return (0);
}
/*ARGSUSED*/
static int
{
return (dt_node_is_stack(dnp));
}
/*ARGSUSED*/
static int
{
return (dt_node_is_integer(dnp) &&
}
/*ARGSUSED*/
static int
{
ctf_arinfo_t r;
if (dt_node_is_string(dnp))
return (1);
}
/*ARGSUSED*/
static int
{
ctf_arinfo_t r;
}
/*ARGSUSED*/
static int
{
return (dt_node_is_integer(dnp) &&
dt_node_type_size(dnp) <= sizeof (int));
}
/*ARGSUSED*/
static int
{
return (dt_node_is_float(dnp));
}
/*ARGSUSED*/
static int
{
return (dt_node_is_integer(dnp));
}
/*ARGSUSED*/
static int
{
else
return (dt_node_is_integer(dnp));
}
/*ARGSUSED*/
static int
{
char n[DT_TYPE_NAMELEN];
strcmp(n, "unsigned short") == 0));
}
/*ARGSUSED*/
static int
{
char n[DT_TYPE_NAMELEN];
strcmp(n, "unsigned long") == 0));
}
/*ARGSUSED*/
static int
{
char n[DT_TYPE_NAMELEN];
strcmp(n, "signed long long") == 0 ||
strcmp(n, "unsigned long long") == 0))
return (1);
/*
* If the type used for %llx or %llX is not an [unsigned] long long, we
* also permit it to be a [u]int64_t or any typedef thereof. We know
* that these typedefs are guaranteed to work with %ll[xX] in either
* compilation environment even though they alias to "long" in LP64.
*/
return (1);
}
return (0);
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
switch (size) {
case sizeof (int8_t):
case sizeof (int16_t):
case sizeof (int32_t):
case sizeof (int64_t):
default:
}
}
/*ARGSUSED*/
static int
{
switch (size) {
case sizeof (uint8_t):
case sizeof (uint16_t):
case sizeof (uint32_t):
case sizeof (uint64_t):
default:
}
}
static int
{
else
}
/*ARGSUSED*/
static int
{
double n = (double)normal;
switch (size) {
case sizeof (float):
(double)*((float *)addr) / n));
case sizeof (double):
*((double *)addr) / n));
case sizeof (long double):
default:
}
}
/*ARGSUSED*/
static int
{
char *s;
switch (size) {
case sizeof (uint32_t):
break;
case sizeof (uint64_t):
break;
default:
}
do {
n = len;
s = alloca(n);
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
char *s;
switch (size) {
case sizeof (uint32_t):
break;
case sizeof (uint64_t):
break;
case sizeof (uint64_t) * 2:
break;
default:
}
do {
n = len;
s = alloca(n);
}
/*ARGSUSED*/
static int
{
int width;
int err = 0;
/*
* We have stashed the value of the STACKINDENT option, and we will
* now override it for the purposes of formatting the stack. If the
* field has been specified as left-aligned (i.e. (%-#), we set the
* indentation to be the width. This is a slightly odd semantic, but
* it's useful functionality -- and it's slightly odd to begin with to
* be using a single format specifier to be formatting multiple lines
* of text...
*/
if (pfd->pfd_dynwidth < 0) {
} else {
width = 0;
}
switch (rec->dtrd_action) {
case DTRACEACT_USTACK:
case DTRACEACT_JSTACK:
break;
case DTRACEACT_STACK:
break;
default:
assert(0);
}
return (err);
}
/*ARGSUSED*/
static int
{
int i;
/*
* ctime(3C) returns a string of the form "Dec 3 17:20:00 1973\n\0".
* "1973 Dec 3 17:20:00".
*/
/*
* Place the 4-digit year at the head of the string...
*/
for (i = 20; i < 24; i++)
/*
* ...and follow it with the remainder (month, day, hh:mm:ss).
*/
for (i = 3; i < 19; i++)
*dst = '\0';
}
/*
* This prints the time in RFC 822 standard form. This is useful for emitting
* notions of time that are consumed by standard tools (e.g., as part of an
* RSS feed).
*/
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
int e;
s[size] = '\0';
}
}
/*ARGSUSED*/
static int
{
s[size] = '\0';
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
char *s;
int n;
free(s);
return (n);
}
static int
{
char c;
switch (size) {
case sizeof (int8_t):
break;
case sizeof (int16_t):
break;
case sizeof (int32_t):
break;
default:
}
}
/*ARGSUSED*/
static int
{
}
static const char pfproto_uaddr[] =
"pointer or integer (with -p/-c) or _usymaddr (without -p/-c)";
/*
* Printf format conversion dictionary. This table should match the set of
* conversions offered by printf(3C), as well as some additional extensions.
* The second parameter is an ASCII string which is either an actual type
* name we should look up (if pfcheck_type is specified), or just a descriptive
* string of the types expected for use in error messages.
*/
};
int
{
uint_t n = _dtrace_strbuckets;
}
pdi->pdi_nbuckets = n;
uint_t h;
}
/*
* The "D" container or its parent must contain a definition of
* any type referenced by a printf conversion. If none can be
* found, we fail to initialize the printf dictionary.
*/
}
/*
* The "C" container may contain an alternate definition of an
* explicit conversion type. If it does, use it; otherwise
* just set pfc_ctype to pfc_dtype so it is always valid.
*/
} else {
}
}
}
return (0);
}
void
{
uint_t i;
return;
for (i = 0; i < pdi->pdi_nbuckets; i++) {
}
}
}
static const dt_pfconv_t *
{
break;
}
return (pfc);
}
static dt_pfargv_t *
{
return (NULL);
}
{
const char *p, *q;
char *format;
}
int digits = 0;
int dot = 0;
char c;
int n;
}
else
if (p > q) {
pfd->pfd_prefix = q;
}
switch (c = *++p) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
goto fmt_switch;
}
for (n = 0; isdigit(c); c = *++p)
n = n * 10 + c - '0';
if (dot)
else
p--;
digits++;
goto fmt_switch;
case '#':
goto fmt_switch;
case '*':
yywarn("format conversion #%u has more than "
"one '*' specified for the output %s\n",
}
goto fmt_switch;
case '+':
goto fmt_switch;
case '-':
goto fmt_switch;
case '.':
if (dot++ != 0) {
yywarn("format conversion #%u has more than "
}
digits = 0;
goto fmt_switch;
case '?':
else
goto fmt_switch;
case '@':
goto fmt_switch;
case '\'':
goto fmt_switch;
case ' ':
goto fmt_switch;
case '$':
yywarn("format conversion #%u uses unsupported "
case '%':
if (p[-1] == '%')
goto default_lbl; /* if %% then use "%" conv */
yywarn("format conversion #%u cannot be combined "
case '\0':
yywarn("format conversion #%u name expected before "
case 'h':
case 'l':
case 'L':
case 'w':
goto fmt_switch;
default:
}
yywarn("format conversion #%u is undefined: %%%s\n",
}
}
}
else
pfd->pfd_prefix = q;
}
return (pfv);
}
void
{
}
}
void
{
char n[DT_TYPE_NAMELEN];
const char *aggtype;
int i, j;
"%s( ) format string is empty\n", func);
}
/*
* We fake up a parse node representing the type that can be used with
* an aggregation result conversion, which -- for all but count() --
* is a signed quantity.
*/
if (kind != DTRACEAGG_COUNT)
aggtype = "int64_t";
else
aggtype = "uint64_t";
int dync = 0;
continue; /* no checking if argd is just a prefix */
continue;
}
"%s( ) prototype mismatch: conversion "
"#%d (%%%s) is missing a corresponding "
}
if (dt_node_is_integer(dnp) == 0) {
"%s( ) argument #%d is incompatible "
"with conversion #%d prototype:\n"
"\tconversion: %% %s %s\n"
"\t prototype: int\n\t argument: %s\n",
dt_node_type_name(dnp, n, sizeof (n)));
}
j++;
}
/*
* If this conversion is consuming the aggregation data, set
* the value node pointer (vnp) to a fake node based on the
* aggregating function result type. Otherwise assign vnp to
* the next parse node in the argument list, if there is one.
*/
if (!(flags & DT_PRINTF_AGGREGATION)) {
"%%@ conversion requires an aggregation"
" and is not for use with %s( )\n", func);
}
sizeof (vname));
"%s( ) prototype mismatch: conversion #%d (%%"
"%s) is missing a corresponding value argument\n",
} else {
j++;
}
/*
* Fill in the proposed final format string by prepending any
* size-related prefixes to the pfconv's format string. The
* pfc_check() function below may optionally modify the format
* as part of validating the type of the input argument.
*/
if (dt_node_type_size(vnp) == sizeof (long double))
}
/*
* Validate the format conversion against the value node type.
* If the conversion is good, create the descriptor format
* string by concatenating together any required printf(3C)
* size prefixes with the conversion's native format string.
*/
"%s( ) %s is incompatible with "
"conversion #%d prototype:\n\tconversion: %%%s\n"
"\t prototype: %s\n\t argument: %s\n", func,
dt_node_type_name(vnp, n, sizeof (n)));
}
}
"%s( ) prototype mismatch: only %d arguments "
"required by this format string\n", func, j);
}
}
void
{
/*
* First, get an argument count on each side. These must match.
*/
largc++;
rargc++;
"matching key signatures: @%s has %d key%s, @%s has %d "
}
/*
* Now iterate over the keys to verify that each type matches.
*/
continue;
"incompatible with @%s:\n%9s key #%d: %s\n"
"%9s key #%d: %s\n",
}
}
static int
{
if (nrecs == 0)
case sizeof (int8_t):
break;
case sizeof (int16_t):
break;
case sizeof (int32_t):
break;
case sizeof (int64_t):
break;
default:
}
return (0);
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
}
static int
{
/*
* If we are formatting an aggregation, set 'aggrec' to the index of
* the final record description (the aggregation result) so we can use
* this record index with any conversion where DT_PFCONV_AGG is set.
* (The actual aggregation used will vary as we increment through the
* aggregation variables that we have been passed.) Finally, we
* decrement nrecs to prevent this record from being used with any
* other conversion.
*/
if (nrecs == 0)
nrecs--;
}
int rval;
if (pfd->pfd_preflen != 0) {
return (rval);
/*
* For printa(), we flush the buffer after each
* prefix, setting the flags to indicate that
* this is part of the printa() format string.
*/
return (-1);
}
}
return (nrecs != 0);
continue;
}
/*
* If the conversion is %%, just invoke the print callback
* with no data record and continue; it consumes no record.
*/
continue;
return (-1); /* errno is set for us */
}
return (-1); /* errno is set for us */
} else {
pfd->pfd_dynwidth = 0;
}
return (-1); /* errno is set for us */
/*
* This should be impossible -- the compiler shouldn't
* create a DT_PFCONV_AGG conversion without an
* aggregation present. Still, we'd rather fail
* gracefully than blow up...
*/
/*
* We increment the current aggregation variable, but
* not beyond the number of aggregation variables that
* we're printing. This has the (desired) effect that
* DT_PFCONV_AGG conversions beyond the number of
* aggregation variables (re-)convert the aggregation
* value of the last aggregation variable.
*/
curagg++;
} else {
if (nrecs == 0)
/*
* When printing aggregation keys, we always
* set the aggdata to be the representative
* (zeroth) aggregation. The aggdata isn't
* actually used here in this case, but it is
* passed to the buffer handler and must
* therefore still be correct.
*/
}
nrecs--;
normal = 1;
}
dt_dprintf("bad size: addr=%p size=0x%x lim=%p\n",
}
if (rec->dtrd_alignment != 0 &&
dt_dprintf("bad align: addr=%p size=0x%x align=0x%x\n",
}
switch (rec->dtrd_action) {
case DTRACEAGG_AVG:
break;
case DTRACEAGG_STDDEV:
break;
case DTRACEAGG_QUANTIZE:
break;
case DTRACEAGG_LQUANTIZE:
break;
case DTRACEAGG_LLQUANTIZE:
break;
case DTRACEACT_MOD:
func = pfprint_mod;
break;
case DTRACEACT_UMOD:
func = pfprint_umod;
break;
default:
break;
}
*f++ = '#';
*f++ = '0';
*f++ = '-';
*f++ = '+';
*f++ = '\'';
*f++ = ' ';
/*
* If we're printing a stack and DT_PFCONV_LEFT is set, we
* don't add the width to the format string. See the block
* comment in pfprint_stack() for a description of the
* behavior in this case.
*/
width = 0;
if (width != 0)
if (prec > 0)
return (-1); /* errno is set for us */
/*
* For printa(), we flush the buffer after each tuple
* element, inidicating that this is the last record
* as appropriate.
*/
return (-1);
}
}
}
int
{
int rval;
NULL, 0);
dtp->dt_sprintf_buflen = 0;
if (rval == -1)
return (rval);
}
/*ARGSUSED*/
int
{
if (rval == -1)
return (rval);
/*
* Before we execute the specified command, flush fp to assure that
* any prior dt_printf()'s appear before the output of the command
* not after it.
*/
return (rval);
}
int
{
return (rval);
if (pfd->pfd_preflen != 0 &&
/*
* The only way to have the format string set to the value
* DT_FREOPEN_RESTORE is via the empty freopen() string --
* denoting that we should restore the old stdout.
*/
/*
* We could complain here by generating an error,
* but it seems like overkill: it seems that calling
* freopen() to restore stdout when freopen() has
* never before been called should just be a no-op,
* so we just return in this case.
*/
return (rval);
}
} else {
}
/*
* freopen(3C) will always close the specified stream and underlying
* file descriptor -- even if the specified file can't be opened.
* Even for the semantic cesspool that is standard I/O, this is
* surprisingly brain-dead behavior: it means that any failure to
* open the specified file destroys the specified stream in the
* process -- which is particularly relevant when the specified stream
* happens (or rather, happened) to be stdout. This could be resolved
* were there an "fdreopen()" equivalent of freopen() that allowed one
* to pass a file descriptor instead of the name of a file, but there
* is no such thing. However, we can effect this ourselves by first
* fopen()'ing the desired file, and then (assuming that that works),
* file descriptor for the fopen()'d file. This way, if the fopen()
* fails, we can fail the operation without destroying stdout.
*/
return (rval);
return (errval);
}
/*
* If this is the first time that we're calling freopen(),
* we're going to stash away the file descriptor for stdout.
* We don't expect the dup(2) to fail, so if it does we must
* return failure.
*/
}
}
}
return (rval);
}
/*ARGSUSED*/
int
{
}
void *
{
int i;
return (NULL); /* errno has been set for us */
continue;
/*
* If the output format is not %s then we assume that we have
* been given a correctly-sized format string, so we copy the
* true format name including the size modifier. If the output
* format is %s, then either the input format is %s as well or
* it is one of our custom formats (e.g. pfprint_addr), so we
* must set pfd_fmt to be the output format conversion "s".
*/
else
}
return (pfv);
}
void *
{
return (NULL); /* errno has been set for us */
return (pfv);
}
/*ARGSUSED*/
{
/*
* An upper bound on the string length is the length of the original
* format string, plus three times the number of conversions (each
* in the case of converting %? to %16) plus one for a terminating \0.
*/
char *f = format;
int i, j;
const char *str;
if (pfd->pfd_preflen != 0) {
for (j = 0; j < pfd->pfd_preflen; j++)
*f++ = pfd->pfd_prefix[j];
}
continue;
*f++ = '%';
*f++ = '#';
*f++ = '0';
*f++ = '-';
*f++ = '+';
*f++ = '*';
*f++ = '.';
*f++ = '*';
}
*f++ = '\'';
*f++ = ' ';
*f++ = '@';
if (width != 0)
if (prec != 0)
/*
* If the output format is %s, then either %s is the underlying
* conversion or the conversion is one of our customized ones,
* e.g. pfprint_addr. In these cases, put the original string
* name of the conversion (pfc_name) into the pickled format
* string rather than the derived conversion (pfd_fmt).
*/
else
for (j = 0; str[j] != '\0'; j++)
*f++ = str[j];
}
*f = '\0'; /* insert nul byte; do not count in return value */
}
static int
{
int id;
return (0); /* no aggregation id or id does not match */
/*
* Cast away the const to set the bit indicating that this aggregation
* has been printed.
*/
return (0);
}
static int
{
int i;
/*
* For each aggregation, indicate that it has been printed, casting
* away the const as necessary.
*/
for (i = 1; i < naggvars; i++) {
}
return (0);
}
/*ARGSUSED*/
int
{
int i, naggvars = 0;
/*
* This might be a printa() with multiple aggregation variables. We
* need to scan forward through the records until we find a record from
* a different statement.
*/
for (i = 0; i < nrecs; i++) {
break;
/* LINTED - alignment */
}
if (naggvars == 0)
if (naggvars == 1) {
return (-1); /* errno is set for us */
} else {
return (-1); /* errno is set for us */
}
return (i);
}