dtrace.c revision b8fac8e162eda7e98db13dfa3e439e43f90f41d9
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* explicitly define DTRACE_ERRDEBUG to pull in definition of dtrace_errhash_t
*/
#define DTRACE_ERRDEBUG
#define _STDARG_H
#include <mdb/mdb_param.h>
#include <mdb/mdb_modapi.h>
#include <sys/dtrace_impl.h>
#include <sys/vmem_impl.h>
#include <sys/ddi_impldefs.h>
#include <sys/sysmacros.h>
#include <dtrace.h>
#include <alloca.h>
#include <ctype.h>
#include <errno.h>
#include <math.h>
/*ARGSUSED*/
int
{
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
goto out;
mdb_warn("failed to read 'dtrace_probes'");
return (DCMD_ERR);
}
return (DCMD_ERR);
}
out:
return (DCMD_OK);
}
void
dtrace_help(void)
{
mdb_printf("Given a dtrace_state_t structure that represents a "
"DTrace consumer, prints\n"
"dtrace(1M)-like output for in-kernel DTrace data. (The "
"dtrace_state_t\n"
"structures for all DTrace consumers may be obtained by running "
"the \n"
"::dtrace_state dcmd.) When data is present on multiple CPUs, "
"data are\n"
"presented in CPU order, with records within each CPU ordered "
"oldest to \n"
"youngest. Options:\n\n"
"-c cpu Only provide output for specified CPU.\n");
}
static int
{
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
/*
* This is a little painful: in order to find the number of actions,
* we need to first walk through them.
*/
mdb_warn("failed to read action %p on ecb %p",
return (-1);
}
nactions++;
}
mdb_warn("failed to read action %p on ecb %p",
return (-1);
}
if (nrecs-- == 0)
break;
}
}
return (0);
}
/*ARGSUSED*/
static int
{
int nprobes, i;
mdb_warn("failed to read 'dtrace_probes'");
return (-1);
}
mdb_warn("failed to read 'dtrace_nprobes'");
return (-1);
}
return (-1);
}
break;
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (0);
}
/*ARGSUSED*/
static int
{
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
/*
* This is a little painful: in order to find the number of actions,
* we need to first walk through them.
*/
nactions = 0;
for (;;) {
mdb_warn("failed to read action %p on aggregation %p",
return (-1);
}
nactions++;
break;
}
nactions = 0;
for (;;) {
mdb_warn("failed to read action %p on aggregation %p",
return (-1);
}
if (nrecs-- == 0)
break;
break;
}
return (0);
}
static int
{
mdb_warn("failed to read 'max_cpuid'");
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
} else {
}
return (-1);
}
return (-1);
}
mdb_warn("ringbuffer for CPU %d has corrupt "
"wrapped offset\n", cpu);
return (-1);
}
/*
* If the ring buffer has wrapped, it needs to be polished.
* See the comment in dtrace_buffer_polish() for details.
*/
}
}
} else {
desc->dtbd_oldest = 0;
}
return (0);
}
/*
* This is essentially identical to its cousin in the kernel.
*/
static dof_hdr_t *
{
sizeof (dof_optdesc_t) * DTRACEOPT_MAX;
dof->dofh_flags = 0;
/*
* Fill in the option section header...
*/
for (i = 0; i < DTRACEOPT_MAX; i++) {
opt[i].dofo_option = i;
}
return (dof);
}
static int
{
char c;
int len = 0;
return (-1);
}
return (-1);
}
do {
return (-1);
}
} while (c != '\0');
return (0);
}
return (-1);
}
return (0);
}
static int
{
int i, j;
int ncpu;
mdb_warn("failed to read '_ncpu'");
return (DCMD_ERR);
}
return (-1);
}
/*
* For the MDB backend, we never set dtst_exiting or dtst_filled. This
* is by design: we don't want the library to try to stop tracing,
* because it doesn't particularly mean anything.
*/
for (i = 0; i < ncpu; i++) {
return (-1);
}
return (-1);
}
for (j = 0; j < state->dts_nspeculations; j++) {
mdb_warn("failed to read "
"speculation at %p", addr);
return (-1);
}
mdb_warn("failed to read "
"speculative buffer at %p", addr);
return (-1);
}
}
}
return (0);
}
typedef struct dtracemdb_data {
char *dtmd_symstr;
char *dtmd_modstr;
static int
{
switch (cmd) {
case DTRACEIOC_CONF: {
return (0);
}
case DTRACEIOC_DOFGET: {
return (0);
}
case DTRACEIOC_BUFSNAP:
case DTRACEIOC_AGGSNAP:
case DTRACEIOC_AGGDESC:
case DTRACEIOC_EPROBE:
case DTRACEIOC_PROBES:
case DTRACEIOC_FORMAT:
case DTRACEIOC_STATUS:
case DTRACEIOC_GO:
return (0);
case DTRACEIOC_ENABLE:
return (-1);
case DTRACEIOC_PROVIDER:
case DTRACEIOC_PROBEMATCH:
return (-1);
default:
"???");
return (-1);
}
}
static int
{
return (WALK_NEXT);
return (WALK_NEXT);
}
return (WALK_NEXT);
return (WALK_NEXT);
return (WALK_ERR);
return (WALK_DONE);
}
static int
{
}
}
return (-1);
}
if (mdb_walk("modctl",
mdb_warn("couldn't walk 'modctl'");
return (-1);
}
}
return (0);
}
/*ARGSUSED*/
static int
{
cpu_t c;
mdb_warn("failed to find symbol for 'cpu'");
return (-1);
}
return (-1);
return (-1);
}
return (-1);
return (-1);
}
if (c.cpu_flags & CPU_POWEROFF) {
return (P_POWEROFF);
return (P_SPARE);
} else if (c.cpu_flags & CPU_FAULTED) {
return (P_FAULTED);
return (P_OFFLINE);
} else if (c.cpu_flags & CPU_ENABLE) {
return (P_ONLINE);
} else {
return (P_NOINTR);
}
}
/*ARGSUSED*/
static long
{
int max_ncpus;
switch (name) {
case _SC_CPUID_MAX:
mdb_warn("failed to read 'max_cpuid'");
return (-1);
}
return (max_cpuid);
case _SC_NPROCESSORS_MAX:
mdb_warn("failed to read 'max_ncpus'");
return (-1);
}
return (max_ncpus);
default:
return (-1);
}
}
const dtrace_vector_t dtrace_mdbops = {
};
typedef struct dtrace_dcmddata {
int dtdd_cpu;
int dtdd_quiet;
int dtdd_flowindent;
int dtdd_heading;
/*ARGSUSED*/
static int
{
/*
* We have processed the final record; output the newline if
* we're not in quiet mode.
*/
if (!dd->dtdd_quiet)
mdb_printf("\n");
return (DTRACE_CONSUME_NEXT);
}
return (DTRACE_CONSUME_THIS);
}
/*ARGSUSED*/
static int
{
return (DTRACE_CONSUME_NEXT);
if (dd->dtdd_heading == 0) {
if (!dd->dtdd_flowindent) {
if (!dd->dtdd_quiet) {
mdb_printf("%3s %6s %32s\n",
"CPU", "ID", "FUNCTION:NAME");
}
} else {
}
}
if (!dd->dtdd_flowindent) {
if (!dd->dtdd_quiet) {
}
} else {
} else {
}
}
return (DTRACE_CONSUME_THIS);
}
/*ARGSUSED*/
static int
{
return (DTRACE_HANDLE_OK);
}
/*ARGSUSED*/
static int
{
return (DTRACE_HANDLE_OK);
}
/*ARGSUSED*/
static int
{
return (DTRACE_HANDLE_OK);
}
/*ARGSUSED*/
int
{
uintptr_t c = -1UL;
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
return (DCMD_USAGE);
mdb_warn("failed to read '_ncpu'");
return (DCMD_ERR);
}
return (DCMD_ERR);
}
mdb_warn("failed to initialize dtrace: %s\n",
return (DCMD_ERR);
}
mdb_warn("failed to initialize dtrace: %s\n",
goto err;
}
mdb_warn("couldn't get 'flowindent' option: %s\n",
goto err;
}
mdb_warn("couldn't get 'quiet' option: %s\n",
goto err;
}
mdb_warn("couldn't add err handler: %s\n",
goto err;
}
mdb_warn("couldn't add drop handler: %s\n",
goto err;
}
mdb_warn("couldn't add buffered handler: %s\n",
goto err;
}
mdb_warn("couldn't get status: %s\n",
goto err;
}
mdb_warn("couldn't snapshot aggregation: %s\n",
goto err;
}
mdb_warn("couldn't consume DTrace buffers: %s\n",
}
mdb_warn("couldn't print aggregation: %s\n",
goto err;
}
err:
return (rval);
}
static int
dtrace_errhash_cmp(const void *l, const void *r)
{
return (-1);
return (1);
}
int
{
int i;
mdb_warn("dtrace_errhash walk only supports global walks\n");
return (WALK_ERR);
}
mdb_warn("couldn't find 'dtrace_errhash' (non-DEBUG kernel?)");
return (WALK_ERR);
}
for (i = 0; i < DTRACE_ERRHASHSZ; i++)
return (WALK_NEXT);
}
int
{
if (ndx >= DTRACE_ERRHASHSZ)
return (WALK_DONE);
return (WALK_DONE);
}
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
char msg[256];
if (!(flags & DCMD_ADDRSPEC)) {
mdb_warn("can't walk 'dtrace_errhash'");
return (DCMD_ERR);
}
return (DCMD_OK);
}
if (DCMD_HDRSPEC(flags))
return (DCMD_ERR);
}
return (DCMD_ERR);
}
/*
* Some error messages include a newline -- only print the newline
* if the message doesn't have one.
*/
mdb_printf("\n");
return (DCMD_OK);
}
int
{
int enabled;
mdb_warn("dtrace_helptrace only supports global walks\n");
return (WALK_ERR);
}
mdb_warn("couldn't read 'dtrace_helptrace_enabled'");
return (WALK_ERR);
}
if (!enabled) {
mdb_warn("helper tracing is not enabled\n");
return (WALK_ERR);
}
mdb_warn("couldn't read 'dtrace_helptrace_next'");
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
int rval;
mdb_warn("couldn't read 'dtrace_helptrace_next'");
return (WALK_ERR);
}
mdb_warn("couldn't read 'dtrace_helptrace_bufsize'");
return (WALK_ERR);
}
mdb_warn("couldn't read 'dtrace_helptrace_buffer'");
return (WALK_ERR);
}
mdb_warn("couldn't read 'dtrace_helptrace_nlocals'");
return (WALK_ERR);
}
size = sizeof (dtrace_helptrace_t) +
if (next == 0)
return (WALK_DONE);
}
return (WALK_ERR);
}
return (rval);
}
return (WALK_DONE);
return (WALK_NEXT);
}
int
{
char where[30];
if (!(flags & DCMD_ADDRSPEC)) {
mdb_warn("can't walk 'dtrace_helptrace'");
return (DCMD_ERR);
}
return (DCMD_OK);
}
return (DCMD_USAGE);
if (DCMD_HDRSPEC(flags)) {
mdb_printf(" %?s %?s %12s %s\n",
"ADDR", "HELPER", "WHERE", "DIFO");
}
return (DCMD_ERR);
}
switch (help.dtht_where) {
case 0:
break;
case DTRACE_HELPTRACE_NEXT:
break;
case DTRACE_HELPTRACE_DONE:
break;
case DTRACE_HELPTRACE_ERR:
break;
default:
break;
}
/*
* We're not going to warn in this case -- we're just not going
* to print anything exciting.
*/
mdb_printf("???\n");
} else {
switch (help.dtht_where) {
case 0:
break;
case DTRACE_HELPTRACE_NEXT:
case DTRACE_HELPTRACE_DONE:
case DTRACE_HELPTRACE_ERR:
mdb_printf("-\n");
break;
default:
mdb_printf("???\n");
} else {
}
}
}
if (opt_v) {
int i;
int f = help.dtht_fault;
f == DTRACEFLT_BADADDR ? "BADADDR" :
f == DTRACEFLT_BADALIGN ? "BADALIGN" :
f == DTRACEFLT_ILLOP ? "ILLOP" :
f == DTRACEFLT_DIVZERO ? "DIVZERO" :
f == DTRACEFLT_NOSCRATCH ? "NOSCRATCH" :
f == DTRACEFLT_KPRIV ? "KPRIV" :
f == DTRACEFLT_UPRIV ? "UPRIV" :
f == DTRACEFLT_TUPOFLOW ? "TUPOFLOW" :
f == DTRACEFLT_BADSTACK ? "BADSTACK" :
"DTRACEFLT_UNKNOWN");
}
"ADDR", "NDX", "VALUE");
for (i = 0; i < help.dtht_nlocals; i++) {
continue;
}
}
mdb_printf("\n");
}
return (DCMD_OK);
}
/*ARGSUSED*/
static int
{
return (WALK_NEXT);
}
typedef struct dtrace_state_walk {
int
{
mdb_warn("dtrace_state only supports global walks\n");
return (WALK_ERR);
}
/*
* Find the dtrace_minor vmem arena and walk it to get the maximum
* minor number.
*/
mdb_warn("failed to read 'dtrace_minor'");
return (WALK_ERR);
}
mdb_warn("couldn't walk 'vmem_alloc'");
return (WALK_ERR);
}
dw->dtsw_current = 0;
mdb_warn("failed to read 'dtrace_softstate'");
return (DCMD_ERR);
}
return (WALK_NEXT);
}
int
{
int rval;
return (WALK_DONE);
dw->dtsw_current++;
}
return (WALK_NEXT);
}
dw->dtsw_current++;
return (rval);
}
typedef struct dtrace_state_data {
int dtsd_major;
static int
{
return (WALK_NEXT);
}
return (WALK_NEXT);
return (WALK_NEXT);
}
return (WALK_NEXT);
}
return (WALK_NEXT);
return (WALK_NEXT);
}
/*ARGSUSED*/
static int
{
if (mdb_pwalk("file",
return (WALK_ERR);
}
return (WALK_NEXT);
}
void
dtrace_state_help(void)
{
mdb_printf("Given a dtrace_state_t structure, displays all "
/*CSTYLED*/
"consumers, or \"<anonymous>\"\nif the consumer is anonymous. If "
"no state structure is provided, iterates\nover all state "
"structures.\n\n"
"Addresses in ADDR column may be provided to ::dtrace to obtain\n"
"dtrace(1M)-like output for in-kernel DTrace data.\n");
}
int
{
if (!(flags & DCMD_ADDRSPEC)) {
if (mdb_walk_dcmd("dtrace_state",
mdb_warn("can't walk dtrace_state");
return (DCMD_ERR);
}
return (DCMD_OK);
}
if (DCMD_HDRSPEC(flags)) {
}
/*
* First determine if this is anonymous state.
*/
mdb_warn("failed to read 'dtrace_anon'");
return (DCMD_ERR);
}
return (DCMD_ERR);
}
"<anonymous>", "-");
return (DCMD_OK);
}
mdb_warn("failed to read 'dtrace_devi'");
return (DCMD_ERR);
}
mdb_warn("failed to read 'dev_info'");
return (DCMD_ERR);
}
mdb_warn("failed to read 'dtrace_softstate'");
return (DCMD_ERR);
}
/*
* Walk through all processes and all open files looking for this
* state. It must be open somewhere...
*/
mdb_warn("couldn't walk 'proc'");
return (DCMD_ERR);
}
return (DCMD_OK);
}
typedef struct dtrace_aggkey_data {
int
{
mdb_warn("dtrace_aggkey walk needs aggregation buffer\n");
return (WALK_ERR);
}
return (WALK_ERR);
}
return (WALK_ERR);
}
mdb_warn("failed to read hash at %p",
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
return (WALK_DONE);
}
return (WALK_ERR);
}
}
void
{
}
typedef struct dtrace_dynvar_data {
int
{
mdb_warn("dtrace_dynvar walk needs dtrace_dstate_t\n");
return (WALK_ERR);
}
return (WALK_ERR);
}
mdb_warn("failed to read hash at %p",
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
int nkeys;
return (WALK_DONE);
data->dtdvd_next =
}
return (WALK_ERR);
}
/*
* Now we need to allocate the correct size.
*/
return (WALK_ERR);
}
}
void
{
}
typedef struct dtrace_hashstat_data {
char *dthsd_data;
int dthsd_header;
typedef void (*dtrace_hashstat_func_t)(dtrace_hashstat_data_t *);
static void
{
int i;
int hval = 0;
for (i = 0; i < data->dthsd_size; i++)
}
static void
{
int i;
return;
}
/* LINTED - alignment */
}
}
static void
{
int i;
for (i = 0; i < data->dthsd_size; i++)
}
static void
{
int i;
for (i = 0; i < data->dthsd_size; i++) {
}
}
static void
{
int i;
for (i = 0; i < data->dthsd_size; i++) {
}
}
static void
{
int longest = 0;
double sum = 0.0;
double avg;
if (!data->dthsd_header) {
"HASHSIZE", "%UTIL", "LONGEST", "AVERAGE", "STDDEV");
}
for (i = 0; i < data->dthsd_hashsize; i++) {
if (data->dthsd_counts[i] != 0) {
nz++;
}
}
if (nz == 0) {
return;
}
for (i = 0; i < data->dthsd_hashsize; i++) {
if (data->dthsd_counts[i] == 0)
continue;
}
}
static struct dtrace_hashstat {
char *dths_name;
} _dtrace_hashstat[] = {
{ "<actual>", NULL },
{ "additive", dtrace_hashstat_additive },
{ "shifty", dtrace_hashstat_shifty },
{ "knuth", dtrace_hashstat_knuth },
{ "one-at-a-time", dtrace_hashstat_oneatatime },
{ "fnv", dtrace_hashstat_fnv },
{ NULL, 0 }
};
typedef struct dtrace_aggstat_data {
static int
{
return (WALK_NEXT);
}
/*
* We need to read the data.
*/
return (WALK_ERR);
}
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
return (DCMD_ERR);
}
return (DCMD_ERR);
}
/*
* Now pick the largest prime smaller than the hash size. (If the
* existing size is prime, we'll pick a smaller prime just for the
* hell of it.)
*/
for (i = 2; i < limit; i++) {
if ((prime % i) == 0)
break;
}
if (i == limit)
break;
}
/*
* And now we want to pick the largest power of two smaller than the
* hashsize.
*/
continue;
if (mdb_pwalk("dtrace_aggkey",
return (DCMD_ERR);
}
/*
* If we were just printing the actual value, we won't try
* any of the sizing experiments.
*/
continue;
if (mdb_pwalk("dtrace_aggkey",
return (DCMD_ERR);
}
if (mdb_pwalk("dtrace_aggkey",
return (DCMD_ERR);
}
}
return (DCMD_OK);
}
/*ARGSUSED*/
static int
{
char *buf;
return (WALK_NEXT);
}
/*
* We want to hand the hashing algorithm a contiguous buffer. First
* run through the tuple and determine the size.
*/
for (i = 0; i < nkeys; i++) {
} else {
}
}
/*
* Now go back through the tuple and copy the data into the buffer.
*/
for (i = 0; i < nkeys; i++) {
sizeof (uint64_t));
} else {
mdb_warn("couldn't read tuple data at %p",
key[i].dttk_value);
return (WALK_ERR);
}
}
}
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
return (DCMD_ERR);
}
/*
* Now pick the largest prime smaller than the hash size. (If the
* existing size is prime, we'll pick a smaller prime just for the
* hell of it.)
*/
for (i = 2; i < limit; i++) {
if ((prime % i) == 0)
break;
}
if (i == limit)
break;
}
if (mdb_pwalk("dtrace_dynvar",
return (DCMD_ERR);
}
/*
* If we were just printing the actual value, we won't try
* any of the sizing experiments.
*/
continue;
if (mdb_pwalk("dtrace_dynvar",
return (DCMD_ERR);
}
}
return (DCMD_OK);
}
typedef struct dtrace_ecb_walk {
int dtew_necbs;
int dtew_curecb;
static int
{
mdb_warn("dtrace_ecb walk needs dtrace_state_t\n");
return (WALK_ERR);
}
return (WALK_ERR);
}
ecbwp->dtew_curecb = 0;
return (WALK_NEXT);
}
static int
{
return (WALK_DONE);
mdb_warn("failed to read ecb at entry %d\n",
ecbwp->dtew_curecb);
return (WALK_ERR);
}
return (WALK_NEXT);
}
const mdb_dcmd_t kernel_dcmds[] = {
{ "id2probe", ":", "translate a dtrace_id_t to a dtrace_probe_t",
id2probe },
{ "dtrace", ":[-c cpu]", "print dtrace(1M)-like output",
dtrace, dtrace_help },
{ "dtrace_helptrace", ":", "print DTrace helper trace",
{ "dtrace_aggstat", ":",
"print DTrace aggregation hash statistics", dtrace_aggstat },
{ "dtrace_dynstat", ":",
"print DTrace dynamic variable hash statistics", dtrace_dynstat },
{ NULL }
};
const mdb_walker_t kernel_walkers[] = {
{ "dtrace_errhash", "walk hash of DTrace error messasges",
{ "dtrace_helptrace", "walk DTrace helper trace entries",
{ "dtrace_state", "walk DTrace per-consumer softstate",
{ "dtrace_aggkey", "walk DTrace aggregation keys",
{ "dtrace_dynvar", "walk DTrace dynamic variables",
{ "dtrace_ecb", "walk a DTrace consumer's enabling control blocks",
{ NULL }
};