/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <assert.h>
#include <dtrace.h>
#include <limits.h>
#include <link.h>
#include <priv.h>
#include <signal.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <libgen.h>
#include <libproc.h>
static char *g_pname;
#define E_SUCCESS 0
/*
* For hold times we use a global associative array since for mutexes, in
* user-land, it's not invalid to release a sychonization primitive that
* another thread acquired; rwlocks require a thread-local associative array
* since multiple thread can hold the same lock for reading. Note that we
* ignore recursive mutex acquisitions and releases as they don't truly
* affect lock contention.
*/
static const char *g_hold_init =
"plockstat$target:::rw-acquire\n"
"{\n"
" self->rwhold[arg0] = timestamp;\n"
"}\n"
"plockstat$target:::mutex-acquire\n"
"/arg1 == 0/\n"
"{\n"
" mtxhold[arg0] = timestamp;\n"
"}\n";
static const char *g_hold_histogram =
"plockstat$target:::rw-release\n"
"/self->rwhold[arg0] && arg1 == 1/\n"
"{\n"
" @rw_w_hold[arg0, ustack()] =\n"
" quantize(timestamp - self->rwhold[arg0]);\n"
" self->rwhold[arg0] = 0;\n"
" rw_w_hold_found = 1;\n"
"}\n"
"plockstat$target:::rw-release\n"
"/self->rwhold[arg0]/\n"
"{\n"
" @rw_r_hold[arg0, ustack()] =\n"
" quantize(timestamp - self->rwhold[arg0]);\n"
" self->rwhold[arg0] = 0;\n"
" rw_r_hold_found = 1;\n"
"}\n"
"plockstat$target:::mutex-release\n"
"/mtxhold[arg0] && arg1 == 0/\n"
"{\n"
" @mtx_hold[arg0, ustack()] = quantize(timestamp - mtxhold[arg0]);\n"
" mtxhold[arg0] = 0;\n"
" mtx_hold_found = 1;\n"
"}\n"
"\n"
"END\n"
"/mtx_hold_found/\n"
"{\n"
" trace(\"Mutex hold\");\n"
" printa(@mtx_hold);\n"
"}\n"
"END\n"
"/rw_r_hold_found/\n"
"{\n"
" trace(\"R/W reader hold\");\n"
" printa(@rw_r_hold);\n"
"}\n"
"END\n"
"/rw_w_hold_found/\n"
"{\n"
" trace(\"R/W writer hold\");\n"
" printa(@rw_w_hold);\n"
"}\n";
static const char *g_hold_times =
"plockstat$target:::rw-release\n"
"/self->rwhold[arg0] && arg1 == 1/\n"
"{\n"
" @rw_w_hold[arg0, ustack(5)] = sum(timestamp - self->rwhold[arg0]);\n"
" @rw_w_hold_count[arg0, ustack(5)] = count();\n"
" self->rwhold[arg0] = 0;\n"
" rw_w_hold_found = 1;\n"
"}\n"
"plockstat$target:::rw-release\n"
"/self->rwhold[arg0]/\n"
"{\n"
" @rw_r_hold[arg0, ustack(5)] = sum(timestamp - self->rwhold[arg0]);\n"
" @rw_r_hold_count[arg0, ustack(5)] = count();\n"
" self->rwhold[arg0] = 0;\n"
" rw_r_hold_found = 1;\n"
"}\n"
"plockstat$target:::mutex-release\n"
"/mtxhold[arg0] && arg1 == 0/\n"
"{\n"
" @mtx_hold[arg0, ustack(5)] = sum(timestamp - mtxhold[arg0]);\n"
" @mtx_hold_count[arg0, ustack(5)] = count();\n"
" mtxhold[arg0] = 0;\n"
" mtx_hold_found = 1;\n"
"}\n"
"\n"
"END\n"
"/mtx_hold_found/\n"
"{\n"
" trace(\"Mutex hold\");\n"
" printa(@mtx_hold, @mtx_hold_count);\n"
"}\n"
"END\n"
"/rw_r_hold_found/\n"
"{\n"
" trace(\"R/W reader hold\");\n"
" printa(@rw_r_hold, @rw_r_hold_count);\n"
"}\n"
"END\n"
"/rw_w_hold_found/\n"
"{\n"
" trace(\"R/W writer hold\");\n"
" printa(@rw_w_hold, @rw_w_hold_count);\n"
"}\n";
/*
* For contention, we use thread-local associative arrays since we're tracing
* a single thread's activity in libc and multiple threads can be blocking or
* spinning on the same sychonization primitive.
*/
static const char *g_ctnd_init =
"plockstat$target:::rw-block\n"
"{\n"
" self->rwblock[arg0] = timestamp;\n"
"}\n"
"plockstat$target:::mutex-block\n"
"{\n"
" self->mtxblock[arg0] = timestamp;\n"
"}\n"
"plockstat$target:::mutex-spin\n"
"{\n"
" self->mtxspin[arg0] = timestamp;\n"
"}\n";
static const char *g_ctnd_histogram =
"plockstat$target:::rw-blocked\n"
"/self->rwblock[arg0] && arg1 == 1 && arg2 != 0/\n"
"{\n"
" @rw_w_block[arg0, ustack()] =\n"
" quantize(timestamp - self->rwblock[arg0]);\n"
" self->rwblock[arg0] = 0;\n"
" rw_w_block_found = 1;\n"
"}\n"
"plockstat$target:::rw-blocked\n"
"/self->rwblock[arg0] && arg2 != 0/\n"
"{\n"
" @rw_r_block[arg0, ustack()] =\n"
" quantize(timestamp - self->rwblock[arg0]);\n"
" self->rwblock[arg0] = 0;\n"
" rw_r_block_found = 1;\n"
"}\n"
"plockstat$target:::rw-blocked\n"
"/self->rwblock[arg0]/\n"
"{\n"
" self->rwblock[arg0] = 0;\n"
"}\n"
"plockstat$target:::mutex-spun\n"
"/self->mtxspin[arg0] && arg1 != 0/\n"
"{\n"
" @mtx_spin[arg0, ustack()] =\n"
" quantize(timestamp - self->mtxspin[arg0]);\n"
" self->mtxspin[arg0] = 0;\n"
" mtx_spin_found = 1;\n"
"}\n"
"plockstat$target:::mutex-spun\n"
"/self->mtxspin[arg0]/\n"
"{\n"
" @mtx_vain_spin[arg0, ustack()] =\n"
" quantize(timestamp - self->mtxspin[arg0]);\n"
" self->mtxspin[arg0] = 0;\n"
" mtx_vain_spin_found = 1;\n"
"}\n"
"plockstat$target:::mutex-blocked\n"
"/self->mtxblock[arg0] && arg1 != 0/\n"
"{\n"
" @mtx_block[arg0, ustack()] =\n"
" quantize(timestamp - self->mtxblock[arg0]);\n"
" self->mtxblock[arg0] = 0;\n"
" mtx_block_found = 1;\n"
"}\n"
"plockstat$target:::mutex-blocked\n"
"/self->mtxblock[arg0]/\n"
"{\n"
" self->mtxblock[arg0] = 0;\n"
"}\n"
"\n"
"END\n"
"/mtx_block_found/\n"
"{\n"
" trace(\"Mutex block\");\n"
" printa(@mtx_block);\n"
"}\n"
"END\n"
"/mtx_spin_found/\n"
"{\n"
" trace(\"Mutex spin\");\n"
" printa(@mtx_spin);\n"
"}\n"
"END\n"
"/mtx_vain_spin_found/\n"
"{\n"
" trace(\"Mutex unsuccessful spin\");\n"
" printa(@mtx_vain_spin);\n"
"}\n"
"END\n"
"/rw_r_block_found/\n"
"{\n"
" trace(\"R/W reader block\");\n"
" printa(@rw_r_block);\n"
"}\n"
"END\n"
"/rw_w_block_found/\n"
"{\n"
" trace(\"R/W writer block\");\n"
" printa(@rw_w_block);\n"
"}\n";
static const char *g_ctnd_times =
"plockstat$target:::rw-blocked\n"
"/self->rwblock[arg0] && arg1 == 1 && arg2 != 0/\n"
"{\n"
" @rw_w_block[arg0, ustack(5)] =\n"
" sum(timestamp - self->rwblock[arg0]);\n"
" @rw_w_block_count[arg0, ustack(5)] = count();\n"
" self->rwblock[arg0] = 0;\n"
" rw_w_block_found = 1;\n"
"}\n"
"plockstat$target:::rw-blocked\n"
"/self->rwblock[arg0] && arg2 != 0/\n"
"{\n"
" @rw_r_block[arg0, ustack(5)] =\n"
" sum(timestamp - self->rwblock[arg0]);\n"
" @rw_r_block_count[arg0, ustack(5)] = count();\n"
" self->rwblock[arg0] = 0;\n"
" rw_r_block_found = 1;\n"
"}\n"
"plockstat$target:::rw-blocked\n"
"/self->rwblock[arg0]/\n"
"{\n"
" self->rwblock[arg0] = 0;\n"
"}\n"
"plockstat$target:::mutex-spun\n"
"/self->mtxspin[arg0] && arg1 != 0/\n"
"{\n"
" @mtx_spin[arg0, ustack(5)] =\n"
" sum(timestamp - self->mtxspin[arg0]);\n"
" @mtx_spin_count[arg0, ustack(5)] = count();\n"
" self->mtxspin[arg0] = 0;\n"
" mtx_spin_found = 1;\n"
"}\n"
"plockstat$target:::mutex-spun\n"
"/self->mtxspin[arg0]/\n"
"{\n"
" @mtx_vain_spin[arg0, ustack(5)] =\n"
" sum(timestamp - self->mtxspin[arg0]);\n"
" @mtx_vain_spin_count[arg0, ustack(5)] = count();\n"
" self->mtxspin[arg0] = 0;\n"
" mtx_vain_spin_found = 1;\n"
"}\n"
"plockstat$target:::mutex-blocked\n"
"/self->mtxblock[arg0] && arg1 != 0/\n"
"{\n"
" @mtx_block[arg0, ustack(5)] =\n"
" sum(timestamp - self->mtxblock[arg0]);\n"
" @mtx_block_count[arg0, ustack(5)] = count();\n"
" self->mtxblock[arg0] = 0;\n"
" mtx_block_found = 1;\n"
"}\n"
"plockstat$target:::mutex-blocked\n"
"/self->mtxblock[arg0]/\n"
"{\n"
" self->mtxblock[arg0] = 0;\n"
"}\n"
"\n"
"END\n"
"/mtx_block_found/\n"
"{\n"
" trace(\"Mutex block\");\n"
" printa(@mtx_block, @mtx_block_count);\n"
"}\n"
"END\n"
"/mtx_spin_found/\n"
"{\n"
" trace(\"Mutex spin\");\n"
" printa(@mtx_spin, @mtx_spin_count);\n"
"}\n"
"END\n"
"/mtx_vain_spin_found/\n"
"{\n"
" trace(\"Mutex unsuccessful spin\");\n"
" printa(@mtx_vain_spin, @mtx_vain_spin_count);\n"
"}\n"
"END\n"
"/rw_r_block_found/\n"
"{\n"
" trace(\"R/W reader block\");\n"
" printa(@rw_r_block, @rw_r_block_count);\n"
"}\n"
"END\n"
"/rw_w_block_found/\n"
"{\n"
" trace(\"R/W writer block\");\n"
" printa(@rw_w_block, @rw_w_block_count);\n"
"}\n";
static int g_intr;
static int g_exited;
static void
usage(void)
{
"\t%s [-vACHV] [-n count] [-s depth] [-e secs] [-x opt[=val]]\n"
"\t command [arg...]\n"
"\t%s [-vACHV] [-n count] [-s depth] [-e secs] [-x opt[=val]]\n"
}
static void
{
}
/*PRINTFLIKE1*/
static void
{
}
/*PRINTFLIKE1*/
static void
{
}
}
}
/*PRINTFLIKE1*/
static void
{
}
static void
{
}
static void
dprog_compile(void)
{
if (g_opt_V) {
}
dfatal("failed to compile program");
dfatal("failed to enable probes");
}
void
print_legend(void)
{
}
void
print_bar(void)
{
(void) printf("---------------------------------------"
"----------------------------------------\n");
}
void
print_histogram_header(void)
{
(void) printf("\n%10s ---- Time Distribution --- %5s %s\n",
"nsec", "count", "Stack");
}
/*
* Convert an address to a symbolic string or a numeric string. If nolocks
* is set, we return an error code if this symbol appears to be a mutex- or
* rwlock-related symbol in libc so the caller has a chance to find a more
* helpful symbol.
*/
static int
int nolocks)
{
return (0);
}
}
return (-1);
return (0);
}
/*ARGSUSED*/
static int
{
struct ps_prochandle *P;
int i, j;
return (DTRACE_AGGWALK_NEXT);
/*LINTED - alignment*/
/*LINTED - alignment*/
if (!g_opt_s) {
/*LINTED - alignment*/
/*LINTED - alignment*/
} else {
uint64_t *a;
/*LINTED - alignment*/
print_bar();
print_legend();
i < DTRACE_QUANTIZE_NBUCKETS; i++, j++) {
count += a[i];
sum += a[i] << (j - 64);
}
}
for (i = 2; i <= 5; i++) {
break;
}
if (g_opt_s) {
int stack_done = 0;
int quant_done = 0;
/*LINTED - alignment*/
for (first_bin = DTRACE_QUANTIZE_ZEROBUCKET;
continue;
continue;
for (i = 0; !stack_done || !quant_done; i++) {
if (!stack_done) {
sizeof (buf), 0);
} else {
buf[0] = '\0';
}
if (!quant_done) {
(void) printf("%10llu |%-24.*s| %5llu %s\n",
1ULL <<
"@@@@@@@@@@@@@@@@@@@@@@@@@@",
} else {
}
stack_done = 1;
quant_done = 1;
}
}
dtrace_proc_release(g_dtp, P);
return (DTRACE_AGGWALK_NEXT);
}
/*ARGSUSED*/
static void
{
return;
}
switch (Pstate(P)) {
case PS_UNDEAD:
/*
* Ideally we would like to always report pr_wstat here, but it
* isn't possible given current /proc semantics. If we grabbed
* the process, Ppsinfo() will either fail or return a zeroed
* psinfo_t depending on how far the parent is in reaping it.
* When /proc provides a stable pr_wstat in the status file,
* this code can be improved by examining this new pr_wstat.
*/
notice("pid %d exited with status %d\n",
} else {
}
g_exited = 1;
break;
case PS_LOST:
g_exited = 1;
break;
}
}
/*ARGSUSED*/
static int
{
const void *buf;
int i, nagv;
/*
* A NULL rec indicates that we've processed the last record.
*/
return (DTRACE_CONSUME_NEXT);
switch (rec->dtrd_action) {
case DTRACEACT_DIFEXPR:
if (!g_opt_s) {
print_legend();
print_bar();
}
return (DTRACE_CONSUME_NEXT);
case DTRACEACT_PRINTA:
break;
/*LINTED - alignment*/
nrec->dtrd_offset);
}
process_aggregate, &nent) != 0)
dfatal("failed to walk aggregate");
}
return (DTRACE_CONSUME_NEXT);
}
return (DTRACE_CONSUME_THIS);
}
/*ARGSUSED*/
static void
{
g_intr = 1;
}
int
{
int err;
char c, *p, *end;
int done = 0;
/*
* Make sure we have the required dtrace_proc privilege.
*/
fatal("dtrace_proc privilege required\n");
}
}
switch (c) {
case 'n':
errno = 0;
usage();
}
break;
case 'p':
opt_p = 1;
break;
case 'v':
opt_v = 1;
break;
case 'A':
break;
case 'C':
opt_C = 1;
break;
case 'H':
opt_H = 1;
break;
case 'V':
g_opt_V = 1;
break;
default:
usage();
}
}
/*
* We need a command or at least one pid.
*/
usage();
opt_C = 1;
fatal("failed to initialize dtrace: %s\n",
/*
* The longest string we trace is 23 bytes long -- so 32 is plenty.
*/
dfatal("failed to set 'strsize'");
/*
* 1k should be more than enough for all trace() and printa() actions.
*/
dfatal("failed to set 'bufsize'");
/*
* The table we produce has the hottest locks at the top.
*/
dfatal("failed to set 'aggsortrev'");
/*
* These are two reasonable defaults which should suffice.
*/
dfatal("failed to set 'aggsize'");
dfatal("failed to set 'aggrate'");
/*
* Take a second pass through to look for options that set options now
* that we have an open dtrace handle.
*/
optind = 1;
switch (c) {
case 's':
g_opt_s = 1;
dfatal("failed to set 'ustackframes'");
break;
case 'x':
*p++ = '\0';
break;
case 'e':
errno = 0;
usage();
}
/*
* Construct a DTrace enabling that will exit after
* the specified number of seconds.
*/
dprog_add("BEGIN\n{\n\tend = timestamp + ");
dprog_add(" * 1000000000;\n}\n");
dprog_add("{\n\texit(0);\n}\n");
break;
}
}
if (opt_H) {
else
}
if (opt_C) {
else
}
if (opt_p) {
if (argc > 1) {
g_pname);
usage();
}
errno = 0;
usage();
}
} else {
}
dfatal("failed to establish proc handler");
dfatal("dtrace_go()");
dfatal("failed to get 'ustackframes'");
if (opt_v)
do {
done = 1;
dfatal("couldn't stop tracing");
}
case DTRACE_WORKSTATUS_DONE:
done = 1;
break;
case DTRACE_WORKSTATUS_OKAY:
break;
default:
dfatal("processing aborted");
}
} while (!done);
return (0);
}