plockstat.c revision 0b38a8bdfd75ac6144f9d462bb38d0c1b3f0ca50
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 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 <libproc.h>
static char *g_pname;
static dtrace_hdl_t *g_dtp;
struct ps_prochandle *g_pr;
#define E_SUCCESS 0
#define E_ERROR 1
#define E_USAGE 2
/*
* 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"
"}\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"
"}\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"
"}\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)] = avg(timestamp - self->rwhold[arg0]);\n"
" self->rwhold[arg0] = 0;\n"
"}\n"
"plockstat$target:::rw-release\n"
"/self->rwhold[arg0]/\n"
"{\n"
" @rw_r_hold[arg0, ustack(5)] = avg(timestamp - self->rwhold[arg0]);\n"
" self->rwhold[arg0] = 0;\n"
"}\n"
"plockstat$target:::mutex-release\n"
"/mtxhold[arg0] && arg1 == 0/\n"
"{\n"
" @mtx_hold[arg0, ustack(5)] = avg(timestamp - mtxhold[arg0]);\n"
" mtxhold[arg0] = 0;\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"
"}\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"
"}\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"
"}\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"
"}\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"
"}\n"
"plockstat$target:::mutex-blocked\n"
"/self->mtxblock[arg0]/\n"
"{\n"
" self->mtxblock[arg0] = 0;\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"
" avg(timestamp - self->rwblock[arg0]);\n"
" self->rwblock[arg0] = 0;\n"
"}\n"
"plockstat$target:::rw-blocked\n"
"/self->rwblock[arg0] && arg2 != 0/\n"
"{\n"
" @rw_r_block[arg0, ustack(5)] =\n"
" avg(timestamp - self->rwblock[arg0]);\n"
" self->rwblock[arg0] = 0;\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"
" avg(timestamp - self->mtxspin[arg0]);\n"
" self->mtxspin[arg0] = 0;\n"
"}\n"
"plockstat$target:::mutex-spun\n"
"/self->mtxspin[arg0]/\n"
"{\n"
" @mtx_vain_spin[arg0, ustack(5)] =\n"
" avg(timestamp - self->mtxspin[arg0]);\n"
" self->mtxspin[arg0] = 0;\n"
"}\n"
"plockstat$target:::mutex-blocked\n"
"/self->mtxblock[arg0] && arg1 != 0/\n"
"{\n"
" @mtx_block[arg0, ustack(5)] =\n"
" avg(timestamp - self->mtxblock[arg0]);\n"
" self->mtxblock[arg0] = 0;\n"
"}\n"
"plockstat$target:::mutex-blocked\n"
"/self->mtxblock[arg0]/\n"
"{\n"
" self->mtxblock[arg0] = 0;\n"
"}\n";
static char g_prog[2048];
static int g_intr;
static dtrace_optval_t g_nframes;
#define PLOCKSTAT_OPTSTR "n:ps:e:vx:ACHV"
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");
}
static void
print_header(const char *aggname)
{
(void) printf("\nMutex hold\n\n");
(void) printf("\nMutex block\n\n");
(void) printf("\nMutex spin\n\n");
(void) printf("\nMutex unsuccessful spin\n\n");
(void) printf("\nR/W reader hold\n\n");
(void) printf("\nR/W writer hold\n\n");
(void) printf("\nR/W reader block\n\n");
(void) printf("\nR/W writer block\n\n");
}
}
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)
{
char name[256];
return (0);
}
}
return (-1);
return (0);
}
/*ARGSUSED*/
static int
{
char buf[256];
struct ps_prochandle *P;
int i, j;
nent = 0;
}
goto out;
nent++;
/*LINTED - alignment*/
/*LINTED - alignment*/
/*LINTED - alignment*/
if (!g_opt_s) {
print_legend();
print_bar();
}
count = a[0];
avg = a[1] / a[0];
} else {
print_bar();
print_legend();
for (i = DTRACE_QUANTIZE_ZEROBUCKET, j = 0;
i < DTRACE_QUANTIZE_NBUCKETS; i++, j++) {
count += a[i];
avg += a[i] << (j - 64);
}
}
for (i = 2; i <= 5; i++) {
break;
}
if (g_opt_s) {
int stack_done = 0;
int quant_done = 0;
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);
out:
return (DTRACE_AGGWALK_NEXT);
}
static int
prochandler(struct ps_prochandle *P)
{
char name[SIG2STR_MAX];
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 {
}
return (1);
case PS_LOST:
return (1);
}
return (0);
}
/*ARGSUSED*/
static void
{
g_intr = 1;
}
int
{
int err;
char c, *p, *end;
int done = 0;
else
/*
* Make sure we have the required dtrace_proc privilege.
*/
const priv_set_t *psp;
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",
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("dtrace_go()");
dfatal("failed to get 'ustackframes'");
if (opt_v)
while (!done) {
(void) sleep(1);
done = 1;
/* Done if the user hits control-C. */
if (g_intr)
done = 1;
done = 1;
if (dtrace_aggregate_snap(g_dtp) != 0)
dfatal("failed to add to aggregate");
}
if (dtrace_aggregate_snap(g_dtp) != 0)
dfatal("failed to add to aggregate");
process_aggregate, NULL) != 0)
dfatal("failed to print aggregations");
return (0);
}