dt_print.c revision 2974b68dd4245e041051c854ef990e47c8271e71
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2011 by Delphix. All rights reserved.
*/
/*
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
*/
/*
* DTrace print() action
*
* This file contains the post-processing logic for the print() action. The
* print action behaves identically to trace() in that it generates a
* DTRACEACT_DIFEXPR action, but the action argument field refers to a CTF type
* string stored in the DOF string table (similar to printf formats). We
* take the result of the trace action and post-process it in the fashion of
* MDB's ::print dcmd.
*
* This implementation differs from MDB's in the following ways:
*
* - We do not expose any options or flags. The behavior of print() is
* equivalent to "::print -tn".
*
* - MDB will display "holes" in structures (unused padding between
* members).
*
* - When printing arrays of structures, MDB will leave a trailing ','
* after the last element.
*
* - MDB will print time_t types as date and time.
*
* - MDB will detect when an enum is actually the OR of several flags,
* and print it out with the constituent flags separated.
*
* - For large arrays, MDB will print the first few members and then
* print a "..." continuation line.
*
* - MDB will break and wrap arrays at 80 columns.
*
* - MDB prints out floats and doubles by hand, as it must run in kmdb
* context. We're able to leverage the printf() format strings,
* but the result is a slightly different format.
*/
#include <sys/sysmacros.h>
#include <strings.h>
#include <stdlib.h>
#include <alloca.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <netdb.h>
#include <dt_module.h>
#include <dt_printf.h>
#include <dt_string.h>
#include <dt_impl.h>
/* determines whether the given integer CTF encoding is a character */
#define CTF_IS_CHAR(e) \
/* determines whether the given CTF kind is a struct or union */
#define CTF_IS_STRUCTLIKE(k) \
((k) == CTF_K_STRUCT || (k) == CTF_K_UNION)
/*
* Print structure passed down recursively through printing algorithm.
*/
typedef struct dt_printarg {
int pa_depth; /* member depth */
int pa_nest; /* nested array depth */
/*
* Safe version of ctf_type_name() that will fall back to just "<ctfid>" if it
* can't resolve the type.
*/
static void
{
}
/*
* Print any necessary trailing braces for structures or unions. We don't get
* invoked when a struct or union ends, so we infer the need to print braces
* based on the depth the last time we printed something and the new depth.
*/
static void
{
int d;
}
}
/*
* Print the appropriate amount of indentation given the current depth and
* array nesting.
*/
static void
{
}
/*
* Print a bitfield. It's worth noting that the D compiler support for
* bitfields is currently broken; printing "D`user_desc_t" (pulled in by the
* various D provider files) will produce incorrect results compared to
* "genunix`user_desc_t".
*/
static void
{
/*
* On big-endian machines, we need to adjust the buf pointer to refer
* to the lowest 'size' bytes in 'value', and we need to shift based on
* the offset from the end of the data, not the offset of the start.
*/
#ifdef _BIG_ENDIAN
#endif
/*
* Offsets are counted from opposite ends on little- and
* big-endian machines.
*/
#ifdef _BIG_ENDIAN
#endif
/*
* If the bits we want do not begin on a byte boundary, shift the data
* right so that the value is in the lowest 'cte_bits' of 'value'.
*/
}
/*
* Dump the contents of memory as a fixed-size integer in hex.
*/
static void
{
switch (size) {
case sizeof (uint8_t):
break;
case sizeof (uint16_t):
/* LINTED - alignment */
break;
case sizeof (uint32_t):
/* LINTED - alignment */
break;
case sizeof (uint64_t):
/* LINTED - alignment */
break;
default:
}
}
/*
* Print an integer type. Before dumping the contents via dt_print_hex(), we
* first check the encoding to see if it's part of a bitfield or a character.
*/
static void
{
return;
}
/*
* This comes from MDB - it's not clear under what circumstances this
* would be found.
*/
if (e.cte_format & CTF_INT_VARARGS) {
return;
}
/*
* We print this as a bitfield if the bit encoding indicates it's not
* an even power of two byte size, or is larger than 8 bytes.
*/
return;
}
/*
* If this is a character, print it out as such.
*/
if (CTF_IS_CHAR(e)) {
char c = *(char *)addr;
if (isprint(c))
else if (c == 0)
else
return;
}
}
/*
* Print a floating point (float, double, long double) value.
*/
/* ARGSUSED */
static void
{
if (e.cte_format == CTF_FP_SINGLE &&
/* LINTED - alignment */
} else if (e.cte_format == CTF_FP_DOUBLE &&
/* LINTED - alignment */
} else if (e.cte_format == CTF_FP_LDOUBLE &&
/* LINTED - alignment */
} else {
}
}
}
/*
* A pointer is generally printed as a fixed-size integer. If we have a
* function pointer, we try to look up its name.
*/
static void
{
} else {
/* LINTED - alignment */
} else {
}
}
}
/*
* Print out an array. This is somewhat complex, as we must manually visit
* each member, and recursively invoke ctf_type_visit() for each member. If
* the members are non-structs, then we print them out directly:
*
* [ 0x14, 0x2e, 0 ]
*
* If they are structs, then we print out the necessary leading and trailing
* braces, to end up with:
*
* [
* type {
* ...
* },
* type {
* ...
* }
* ]
*
* We also use a heuristic to detect whether the array looks like a character
* array. If the encoding indicates it's a character, and we have all
* printable characters followed by a null byte, then we display it as a
* string:
*
* [ "string" ]
*/
static void
{
int i;
int kind;
return;
}
return;
}
/* see if this looks like a string */
if (kind == CTF_K_INTEGER &&
char c;
for (i = 0; i < car.ctr_nelems; i++) {
if (!isprint(c) || c == '\0')
break;
}
}
/*
* As a slight aesthetic optimization, if we are a top-level type, then
* don't bother printing out the brackets. This lets print("foo") look
* like:
*
* string "foo"
*
* As D will internally represent this as a char[256] array.
*/
if (isstring)
for (i = 0; i < car.ctr_nelems; i++) {
if (isstring) {
if (c == '\0')
break;
} else {
/*
* Recursively invoke ctf_type_visit() on each member.
* We setup a new printarg struct with 'pa_nest' set to
* indicate that we are within a nested array.
*/
dt_print_member, &pa);
dt_print_trailing_braces(&pa, 0);
else if (CTF_IS_STRUCTLIKE(kind))
}
}
if (isstring)
if (CTF_IS_STRUCTLIKE(kind))
else
}
}
/*
* This isued by both structs and unions to print the leading brace.
*/
/* ARGSUSED */
static void
{
}
/*
* For enums, we try to print the enum name, and fall back to the value if it
* can't be determined. We do not do any fancy flag processing like mdb.
*/
/* ARGSUSED */
static void
{
const char *ename;
int value = 0;
/*
* The C standard says that an enum will be at most the sizeof (int).
* But if all the values are less than that, the compiler can use a
* smaller size. Thanks standards.
*/
switch (size) {
case sizeof (uint8_t):
break;
case sizeof (uint16_t):
/* LINTED - alignment */
break;
case sizeof (int32_t):
/* LINTED - alignment */
break;
default:
return;
}
else
}
/*
* Forward declaration. There's not much to do here without the complete
* type information, so just print out this fact and drive on.
*/
/* ARGSUSED */
static void
{
}
static dt_printarg_f *const dt_printfuncs[] = {
dt_print_int, /* CTF_K_INTEGER */
dt_print_float, /* CTF_K_FLOAT */
dt_print_ptr, /* CTF_K_POINTER */
dt_print_array, /* CTF_K_ARRAY */
dt_print_ptr, /* CTF_K_FUNCTION */
dt_print_structlike, /* CTF_K_STRUCT */
dt_print_structlike, /* CTF_K_UNION */
dt_print_enum, /* CTF_K_ENUM */
dt_print_tag /* CTF_K_FORWARD */
};
/*
* Print one member of a structure. This callback is invoked from
* ctf_type_visit() recursively.
*/
static int
void *data)
{
char type[DT_TYPE_NAMELEN];
int kind;
/*
* dt_print_trailing_braces() doesn't include the trailing newline; add
* it here if necessary.
*/
return (0);
}
if (!brief) {
/*
* If this is a direct array member and a struct (otherwise
* brief would be true), then print a trailing newline, as the
* array printing code doesn't include it because it might be a
* simple type.
*/
if (arraymember)
/* always print the type */
if (name[0] != '\0') {
/*
* For aesthetics, we don't include a space between the
* type name and member name if the type is a pointer.
* This will give us "void *foo =" instead of "void *
* foo =". Unions also have the odd behavior that the
* type name is returned as "union ", with a trailing
* space, so we also avoid printing a space if the type
* name already ends with a space.
*/
}
/*
* If this looks like a bitfield, or is an integer not
* aligned on a byte boundary, print the number of
* bits after the name.
*/
if (kind == CTF_K_INTEGER &&
size > 8 ||
}
}
}
}
/* direct simple array members are not separated by newlines */
if (!brief)
return (0);
}
/*
* Main print function invoked by dt_consume_cpu().
*/
int
{
const char *s;
char *object;
/*
* Split the fully-qualified type ID (module`id). This should
* always be the format, but if for some reason we don't find the
* expected value, return 0 to fall back to the generic trace()
* behavior.
*/
;
if (*s != '`')
return (0);
/*
* Try to get the CTF kind for this id. If something has gone horribly
* wrong and we can't resolve the ID, bail out and let trace() do the
* work.
*/
return (0);
}
/* setup the print structure and kick off the main print routine */
dt_print_trailing_braces(&pa, 0);
return (len);
}