Path: blob/main/cddl/contrib/opensolaris/cmd/plockstat/plockstat.c
39488 views
/*1* CDDL HEADER START2*3* The contents of this file are subject to the terms of the4* Common Development and Distribution License (the "License").5* You may not use this file except in compliance with the License.6*7* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE8* or http://www.opensolaris.org/os/licensing.9* See the License for the specific language governing permissions10* and limitations under the License.11*12* When distributing Covered Code, include this CDDL HEADER in each13* file and include the License file at usr/src/OPENSOLARIS.LICENSE.14* If applicable, add the following below this CDDL HEADER, with the15* fields enclosed by brackets "[]" replaced with your own identifying16* information: Portions Copyright [yyyy] [name of copyright owner]17*18* CDDL HEADER END19*/2021/*22* Copyright 2006 Sun Microsystems, Inc. All rights reserved.23* Use is subject to license terms.24*/2526#ifdef illumos27#pragma ident "%Z%%M% %I% %E% SMI"28#endif2930#include <assert.h>31#include <dtrace.h>32#include <limits.h>33#include <link.h>34#include <priv.h>35#include <signal.h>36#include <stdlib.h>37#include <stdarg.h>38#include <stdio.h>39#include <string.h>40#include <strings.h>41#include <errno.h>42#include <sys/wait.h>43#include <libgen.h>44#include <libproc.h>45#include <libproc_compat.h>4647static char *g_pname;48static dtrace_hdl_t *g_dtp;49struct ps_prochandle *g_pr;5051#define E_SUCCESS 052#define E_ERROR 153#define E_USAGE 25455/*56* For hold times we use a global associative array since for mutexes, in57* user-land, it's not invalid to release a sychonization primitive that58* another thread acquired; rwlocks require a thread-local associative array59* since multiple thread can hold the same lock for reading. Note that we60* ignore recursive mutex acquisitions and releases as they don't truly61* affect lock contention.62*/63static const char *g_hold_init =64"plockstat$target:::rw-acquire\n"65"{\n"66" self->rwhold[arg0] = timestamp;\n"67"}\n"68"plockstat$target:::mutex-acquire\n"69"/arg1 == 0/\n"70"{\n"71" mtxhold[arg0] = timestamp;\n"72"}\n";7374static const char *g_hold_histogram =75"plockstat$target:::rw-release\n"76"/self->rwhold[arg0] && arg1 == 1/\n"77"{\n"78" @rw_w_hold[arg0, ustack()] =\n"79" quantize(timestamp - self->rwhold[arg0]);\n"80" self->rwhold[arg0] = 0;\n"81" rw_w_hold_found = 1;\n"82"}\n"83"plockstat$target:::rw-release\n"84"/self->rwhold[arg0]/\n"85"{\n"86" @rw_r_hold[arg0, ustack()] =\n"87" quantize(timestamp - self->rwhold[arg0]);\n"88" self->rwhold[arg0] = 0;\n"89" rw_r_hold_found = 1;\n"90"}\n"91"plockstat$target:::mutex-release\n"92"/mtxhold[arg0] && arg1 == 0/\n"93"{\n"94" @mtx_hold[arg0, ustack()] = quantize(timestamp - mtxhold[arg0]);\n"95" mtxhold[arg0] = 0;\n"96" mtx_hold_found = 1;\n"97"}\n"98"\n"99"END\n"100"/mtx_hold_found/\n"101"{\n"102" trace(\"Mutex hold\");\n"103" printa(@mtx_hold);\n"104"}\n"105"END\n"106"/rw_r_hold_found/\n"107"{\n"108" trace(\"R/W reader hold\");\n"109" printa(@rw_r_hold);\n"110"}\n"111"END\n"112"/rw_w_hold_found/\n"113"{\n"114" trace(\"R/W writer hold\");\n"115" printa(@rw_w_hold);\n"116"}\n";117118static const char *g_hold_times =119"plockstat$target:::rw-release\n"120"/self->rwhold[arg0] && arg1 == 1/\n"121"{\n"122" @rw_w_hold[arg0, ustack(5)] = sum(timestamp - self->rwhold[arg0]);\n"123" @rw_w_hold_count[arg0, ustack(5)] = count();\n"124" self->rwhold[arg0] = 0;\n"125" rw_w_hold_found = 1;\n"126"}\n"127"plockstat$target:::rw-release\n"128"/self->rwhold[arg0]/\n"129"{\n"130" @rw_r_hold[arg0, ustack(5)] = sum(timestamp - self->rwhold[arg0]);\n"131" @rw_r_hold_count[arg0, ustack(5)] = count();\n"132" self->rwhold[arg0] = 0;\n"133" rw_r_hold_found = 1;\n"134"}\n"135"plockstat$target:::mutex-release\n"136"/mtxhold[arg0] && arg1 == 0/\n"137"{\n"138" @mtx_hold[arg0, ustack(5)] = sum(timestamp - mtxhold[arg0]);\n"139" @mtx_hold_count[arg0, ustack(5)] = count();\n"140" mtxhold[arg0] = 0;\n"141" mtx_hold_found = 1;\n"142"}\n"143"\n"144"END\n"145"/mtx_hold_found/\n"146"{\n"147" trace(\"Mutex hold\");\n"148" printa(@mtx_hold, @mtx_hold_count);\n"149"}\n"150"END\n"151"/rw_r_hold_found/\n"152"{\n"153" trace(\"R/W reader hold\");\n"154" printa(@rw_r_hold, @rw_r_hold_count);\n"155"}\n"156"END\n"157"/rw_w_hold_found/\n"158"{\n"159" trace(\"R/W writer hold\");\n"160" printa(@rw_w_hold, @rw_w_hold_count);\n"161"}\n";162163164/*165* For contention, we use thread-local associative arrays since we're tracing166* a single thread's activity in libc and multiple threads can be blocking or167* spinning on the same sychonization primitive.168*/169static const char *g_ctnd_init =170"plockstat$target:::rw-block\n"171"{\n"172" self->rwblock[arg0] = timestamp;\n"173"}\n"174"plockstat$target:::mutex-block\n"175"{\n"176" self->mtxblock[arg0] = timestamp;\n"177"}\n"178"plockstat$target:::mutex-spin\n"179"{\n"180" self->mtxspin[arg0] = timestamp;\n"181"}\n";182183static const char *g_ctnd_histogram =184"plockstat$target:::rw-blocked\n"185"/self->rwblock[arg0] && arg1 == 1 && arg2 != 0/\n"186"{\n"187" @rw_w_block[arg0, ustack()] =\n"188" quantize(timestamp - self->rwblock[arg0]);\n"189" self->rwblock[arg0] = 0;\n"190" rw_w_block_found = 1;\n"191"}\n"192"plockstat$target:::rw-blocked\n"193"/self->rwblock[arg0] && arg2 != 0/\n"194"{\n"195" @rw_r_block[arg0, ustack()] =\n"196" quantize(timestamp - self->rwblock[arg0]);\n"197" self->rwblock[arg0] = 0;\n"198" rw_r_block_found = 1;\n"199"}\n"200"plockstat$target:::rw-blocked\n"201"/self->rwblock[arg0]/\n"202"{\n"203" self->rwblock[arg0] = 0;\n"204"}\n"205"plockstat$target:::mutex-spun\n"206"/self->mtxspin[arg0] && arg1 != 0/\n"207"{\n"208" @mtx_spin[arg0, ustack()] =\n"209" quantize(timestamp - self->mtxspin[arg0]);\n"210" self->mtxspin[arg0] = 0;\n"211" mtx_spin_found = 1;\n"212"}\n"213"plockstat$target:::mutex-spun\n"214"/self->mtxspin[arg0]/\n"215"{\n"216" @mtx_vain_spin[arg0, ustack()] =\n"217" quantize(timestamp - self->mtxspin[arg0]);\n"218" self->mtxspin[arg0] = 0;\n"219" mtx_vain_spin_found = 1;\n"220"}\n"221"plockstat$target:::mutex-blocked\n"222"/self->mtxblock[arg0] && arg1 != 0/\n"223"{\n"224" @mtx_block[arg0, ustack()] =\n"225" quantize(timestamp - self->mtxblock[arg0]);\n"226" self->mtxblock[arg0] = 0;\n"227" mtx_block_found = 1;\n"228"}\n"229"plockstat$target:::mutex-blocked\n"230"/self->mtxblock[arg0]/\n"231"{\n"232" self->mtxblock[arg0] = 0;\n"233"}\n"234"\n"235"END\n"236"/mtx_block_found/\n"237"{\n"238" trace(\"Mutex block\");\n"239" printa(@mtx_block);\n"240"}\n"241"END\n"242"/mtx_spin_found/\n"243"{\n"244" trace(\"Mutex spin\");\n"245" printa(@mtx_spin);\n"246"}\n"247"END\n"248"/mtx_vain_spin_found/\n"249"{\n"250" trace(\"Mutex unsuccessful spin\");\n"251" printa(@mtx_vain_spin);\n"252"}\n"253"END\n"254"/rw_r_block_found/\n"255"{\n"256" trace(\"R/W reader block\");\n"257" printa(@rw_r_block);\n"258"}\n"259"END\n"260"/rw_w_block_found/\n"261"{\n"262" trace(\"R/W writer block\");\n"263" printa(@rw_w_block);\n"264"}\n";265266267static const char *g_ctnd_times =268"plockstat$target:::rw-blocked\n"269"/self->rwblock[arg0] && arg1 == 1 && arg2 != 0/\n"270"{\n"271" @rw_w_block[arg0, ustack(5)] =\n"272" sum(timestamp - self->rwblock[arg0]);\n"273" @rw_w_block_count[arg0, ustack(5)] = count();\n"274" self->rwblock[arg0] = 0;\n"275" rw_w_block_found = 1;\n"276"}\n"277"plockstat$target:::rw-blocked\n"278"/self->rwblock[arg0] && arg2 != 0/\n"279"{\n"280" @rw_r_block[arg0, ustack(5)] =\n"281" sum(timestamp - self->rwblock[arg0]);\n"282" @rw_r_block_count[arg0, ustack(5)] = count();\n"283" self->rwblock[arg0] = 0;\n"284" rw_r_block_found = 1;\n"285"}\n"286"plockstat$target:::rw-blocked\n"287"/self->rwblock[arg0]/\n"288"{\n"289" self->rwblock[arg0] = 0;\n"290"}\n"291"plockstat$target:::mutex-spun\n"292"/self->mtxspin[arg0] && arg1 != 0/\n"293"{\n"294" @mtx_spin[arg0, ustack(5)] =\n"295" sum(timestamp - self->mtxspin[arg0]);\n"296" @mtx_spin_count[arg0, ustack(5)] = count();\n"297" self->mtxspin[arg0] = 0;\n"298" mtx_spin_found = 1;\n"299"}\n"300"plockstat$target:::mutex-spun\n"301"/self->mtxspin[arg0]/\n"302"{\n"303" @mtx_vain_spin[arg0, ustack(5)] =\n"304" sum(timestamp - self->mtxspin[arg0]);\n"305" @mtx_vain_spin_count[arg0, ustack(5)] = count();\n"306" self->mtxspin[arg0] = 0;\n"307" mtx_vain_spin_found = 1;\n"308"}\n"309"plockstat$target:::mutex-blocked\n"310"/self->mtxblock[arg0] && arg1 != 0/\n"311"{\n"312" @mtx_block[arg0, ustack(5)] =\n"313" sum(timestamp - self->mtxblock[arg0]);\n"314" @mtx_block_count[arg0, ustack(5)] = count();\n"315" self->mtxblock[arg0] = 0;\n"316" mtx_block_found = 1;\n"317"}\n"318"plockstat$target:::mutex-blocked\n"319"/self->mtxblock[arg0]/\n"320"{\n"321" self->mtxblock[arg0] = 0;\n"322"}\n"323"\n"324"END\n"325"/mtx_block_found/\n"326"{\n"327" trace(\"Mutex block\");\n"328" printa(@mtx_block, @mtx_block_count);\n"329"}\n"330"END\n"331"/mtx_spin_found/\n"332"{\n"333" trace(\"Mutex spin\");\n"334" printa(@mtx_spin, @mtx_spin_count);\n"335"}\n"336"END\n"337"/mtx_vain_spin_found/\n"338"{\n"339" trace(\"Mutex unsuccessful spin\");\n"340" printa(@mtx_vain_spin, @mtx_vain_spin_count);\n"341"}\n"342"END\n"343"/rw_r_block_found/\n"344"{\n"345" trace(\"R/W reader block\");\n"346" printa(@rw_r_block, @rw_r_block_count);\n"347"}\n"348"END\n"349"/rw_w_block_found/\n"350"{\n"351" trace(\"R/W writer block\");\n"352" printa(@rw_w_block, @rw_w_block_count);\n"353"}\n";354355static char g_prog[4096];356static size_t g_proglen;357static int g_opt_V, g_opt_s;358static int g_intr;359static int g_exited;360static dtrace_optval_t g_nframes;361static ulong_t g_nent = ULONG_MAX;362363#define PLOCKSTAT_OPTSTR "n:ps:e:vx:ACHV"364365static void366usage(void)367{368(void) fprintf(stderr, "Usage:\n"369"\t%s [-vACHV] [-n count] [-s depth] [-e secs] [-x opt[=val]]\n"370"\t command [arg...]\n"371"\t%s [-vACHV] [-n count] [-s depth] [-e secs] [-x opt[=val]]\n"372"\t -p pid\n", g_pname, g_pname);373374exit(E_USAGE);375}376377static void378verror(const char *fmt, va_list ap)379{380int error = errno;381382(void) fprintf(stderr, "%s: ", g_pname);383(void) vfprintf(stderr, fmt, ap);384385if (fmt[strlen(fmt) - 1] != '\n')386(void) fprintf(stderr, ": %s\n", strerror(error));387}388389/*PRINTFLIKE1*/390static void391fatal(const char *fmt, ...)392{393va_list ap;394395va_start(ap, fmt);396verror(fmt, ap);397va_end(ap);398399if (g_pr != NULL && g_dtp != NULL)400dtrace_proc_release(g_dtp, g_pr);401402exit(E_ERROR);403}404405/*PRINTFLIKE1*/406static void407dfatal(const char *fmt, ...)408{409va_list ap;410411va_start(ap, fmt);412413(void) fprintf(stderr, "%s: ", g_pname);414if (fmt != NULL)415(void) vfprintf(stderr, fmt, ap);416417va_end(ap);418419if (fmt != NULL && fmt[strlen(fmt) - 1] != '\n') {420(void) fprintf(stderr, ": %s\n",421dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));422} else if (fmt == NULL) {423(void) fprintf(stderr, "%s\n",424dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));425}426427if (g_pr != NULL) {428dtrace_proc_continue(g_dtp, g_pr);429dtrace_proc_release(g_dtp, g_pr);430}431432exit(E_ERROR);433}434435/*PRINTFLIKE1*/436static void437notice(const char *fmt, ...)438{439va_list ap;440441va_start(ap, fmt);442verror(fmt, ap);443va_end(ap);444}445446static void447dprog_add(const char *prog)448{449size_t len = strlen(prog);450bcopy(prog, g_prog + g_proglen, len + 1);451g_proglen += len;452assert(g_proglen < sizeof (g_prog));453}454455static void456dprog_compile(void)457{458dtrace_prog_t *prog;459dtrace_proginfo_t info;460461if (g_opt_V) {462(void) fprintf(stderr, "%s: vvvv D program vvvv\n", g_pname);463(void) fputs(g_prog, stderr);464(void) fprintf(stderr, "%s: ^^^^ D program ^^^^\n", g_pname);465}466467if ((prog = dtrace_program_strcompile(g_dtp, g_prog,468DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL)469dfatal("failed to compile program");470471if (dtrace_program_exec(g_dtp, prog, &info) == -1)472dfatal("failed to enable probes");473}474475void476print_legend(void)477{478(void) printf("%5s %8s %-28s %s\n", "Count", "nsec", "Lock", "Caller");479}480481void482print_bar(void)483{484(void) printf("---------------------------------------"485"----------------------------------------\n");486}487488void489print_histogram_header(void)490{491(void) printf("\n%10s ---- Time Distribution --- %5s %s\n",492"nsec", "count", "Stack");493}494495/*496* Convert an address to a symbolic string or a numeric string. If nolocks497* is set, we return an error code if this symbol appears to be a mutex- or498* rwlock-related symbol in libc so the caller has a chance to find a more499* helpful symbol.500*/501static int502getsym(struct ps_prochandle *P, uintptr_t addr, char *buf, size_t size,503int nolocks)504{505char name[256];506GElf_Sym sym;507#ifdef illumos508prsyminfo_t info;509#else510prmap_t *map;511int info; /* XXX unused */512#endif513size_t len;514515if (P == NULL || Pxlookup_by_addr(P, addr, name, sizeof (name),516&sym, &info) != 0) {517(void) snprintf(buf, size, "%#lx", (unsigned long)addr);518return (0);519}520#ifdef illumos521if (info.prs_object == NULL)522info.prs_object = "<unknown>";523524if (info.prs_lmid != LM_ID_BASE) {525len = snprintf(buf, size, "LM%lu`", info.prs_lmid);526buf += len;527size -= len;528}529530len = snprintf(buf, size, "%s`%s", info.prs_object, info.prs_name);531#else532map = proc_addr2map(P, addr);533len = snprintf(buf, size, "%s`%s", map->pr_mapname, name);534#endif535buf += len;536size -= len;537538if (sym.st_value != addr)539len = snprintf(buf, size, "+%#lx", (unsigned long)(addr - sym.st_value));540541if (nolocks && strcmp("libc.so.1", map->pr_mapname) == 0 &&542(strstr("mutex", name) == 0 ||543strstr("rw", name) == 0))544return (-1);545546return (0);547}548549/*ARGSUSED*/550static int551process_aggregate(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg)552{553const dtrace_recdesc_t *rec;554uintptr_t lock;555uint64_t *stack;556caddr_t data;557pid_t pid;558struct ps_prochandle *P;559char buf[256];560int i, j;561uint64_t sum, count, avg;562563if ((*(uint_t *)arg)++ >= g_nent)564return (DTRACE_AGGWALK_NEXT);565566rec = aggsdata[0]->dtada_desc->dtagd_rec;567data = aggsdata[0]->dtada_data;568569/*LINTED - alignment*/570lock = (uintptr_t)*(uint64_t *)(data + rec[1].dtrd_offset);571/*LINTED - alignment*/572stack = (uint64_t *)(data + rec[2].dtrd_offset);573574if (!g_opt_s) {575/*LINTED - alignment*/576sum = *(uint64_t *)(aggsdata[1]->dtada_data +577aggsdata[1]->dtada_desc->dtagd_rec[3].dtrd_offset);578/*LINTED - alignment*/579count = *(uint64_t *)(aggsdata[2]->dtada_data +580aggsdata[2]->dtada_desc->dtagd_rec[3].dtrd_offset);581} else {582uint64_t *a;583584/*LINTED - alignment*/585a = (uint64_t *)(aggsdata[1]->dtada_data +586aggsdata[1]->dtada_desc->dtagd_rec[3].dtrd_offset);587588print_bar();589print_legend();590591for (count = sum = 0, i = DTRACE_QUANTIZE_ZEROBUCKET, j = 0;592i < DTRACE_QUANTIZE_NBUCKETS; i++, j++) {593count += a[i];594sum += a[i] << (j - 64);595}596}597598avg = sum / count;599(void) printf("%5llu %8llu ", (u_longlong_t)count, (u_longlong_t)avg);600601pid = stack[0];602P = dtrace_proc_grab(g_dtp, pid, PGRAB_RDONLY);603604(void) getsym(P, lock, buf, sizeof (buf), 0);605(void) printf("%-28s ", buf);606607for (i = 2; i <= 5; i++) {608if (getsym(P, stack[i], buf, sizeof (buf), 1) == 0)609break;610}611(void) printf("%s\n", buf);612613if (g_opt_s) {614int stack_done = 0;615int quant_done = 0;616int first_bin, last_bin;617uint64_t bin_size, *a;618619/*LINTED - alignment*/620a = (uint64_t *)(aggsdata[1]->dtada_data +621aggsdata[1]->dtada_desc->dtagd_rec[3].dtrd_offset);622623print_histogram_header();624625for (first_bin = DTRACE_QUANTIZE_ZEROBUCKET;626a[first_bin] == 0; first_bin++)627continue;628for (last_bin = DTRACE_QUANTIZE_ZEROBUCKET + 63;629a[last_bin] == 0; last_bin--)630continue;631632for (i = 0; !stack_done || !quant_done; i++) {633if (!stack_done) {634(void) getsym(P, stack[i + 2], buf,635sizeof (buf), 0);636} else {637buf[0] = '\0';638}639640if (!quant_done) {641bin_size = a[first_bin];642643(void) printf("%10llu |%-24.*s| %5llu %s\n",6441ULL <<645(first_bin - DTRACE_QUANTIZE_ZEROBUCKET),646(int)(24.0 * bin_size / count),647"@@@@@@@@@@@@@@@@@@@@@@@@@@",648(u_longlong_t)bin_size, buf);649} else {650(void) printf("%43s %s\n", "", buf);651}652653if (i + 1 >= g_nframes || stack[i + 3] == 0)654stack_done = 1;655656if (first_bin++ == last_bin)657quant_done = 1;658}659}660661dtrace_proc_release(g_dtp, P);662663return (DTRACE_AGGWALK_NEXT);664}665666/*ARGSUSED*/667static void668prochandler(struct ps_prochandle *P, const char *msg, void *arg)669{670#ifdef illumos671const psinfo_t *prp = Ppsinfo(P);672int pid = Pstatus(P)->pr_pid;673#else674int pid = proc_getpid(P);675int wstat = proc_getwstat(P);676#endif677char name[SIG2STR_MAX];678679if (msg != NULL) {680notice("pid %d: %s\n", pid, msg);681return;682}683684switch (Pstate(P)) {685case PS_UNDEAD:686/*687* Ideally we would like to always report pr_wstat here, but it688* isn't possible given current /proc semantics. If we grabbed689* the process, Ppsinfo() will either fail or return a zeroed690* psinfo_t depending on how far the parent is in reaping it.691* When /proc provides a stable pr_wstat in the status file,692* this code can be improved by examining this new pr_wstat.693*/694if (WIFSIGNALED(wstat)) {695notice("pid %d terminated by %s\n", pid,696proc_signame(WTERMSIG(wstat),697name, sizeof (name)));698} else if (WEXITSTATUS(wstat) != 0) {699notice("pid %d exited with status %d\n",700pid, WEXITSTATUS(wstat));701} else {702notice("pid %d has exited\n", pid);703}704g_exited = 1;705break;706707case PS_LOST:708notice("pid %d exec'd a set-id or unobservable program\n", pid);709g_exited = 1;710break;711}712}713714/*ARGSUSED*/715static int716chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg)717{718dtrace_eprobedesc_t *epd = data->dtpda_edesc;719dtrace_aggvarid_t aggvars[2];720const void *buf;721int i, nagv;722723/*724* A NULL rec indicates that we've processed the last record.725*/726if (rec == NULL)727return (DTRACE_CONSUME_NEXT);728729buf = data->dtpda_data - rec->dtrd_offset;730731switch (rec->dtrd_action) {732case DTRACEACT_DIFEXPR:733(void) printf("\n%s\n\n", (char *)buf + rec->dtrd_offset);734if (!g_opt_s) {735print_legend();736print_bar();737}738return (DTRACE_CONSUME_NEXT);739740case DTRACEACT_PRINTA:741for (nagv = 0, i = 0; i < epd->dtepd_nrecs - 1; i++) {742const dtrace_recdesc_t *nrec = &rec[i];743744if (nrec->dtrd_uarg != rec->dtrd_uarg)745break;746747/*LINTED - alignment*/748aggvars[nagv++] = *(dtrace_aggvarid_t *)((caddr_t)buf +749nrec->dtrd_offset);750}751752if (nagv == (g_opt_s ? 1 : 2)) {753uint_t nent = 0;754if (dtrace_aggregate_walk_joined(g_dtp, aggvars, nagv,755process_aggregate, &nent) != 0)756dfatal("failed to walk aggregate");757}758759return (DTRACE_CONSUME_NEXT);760}761762return (DTRACE_CONSUME_THIS);763}764765/*ARGSUSED*/766static void767intr(int signo)768{769g_intr = 1;770}771772int773main(int argc, char **argv)774{775#ifdef illumos776ucred_t *ucp;777#endif778int err;779int opt_C = 0, opt_H = 0, opt_p = 0, opt_v = 0;780int c;781char *p, *end;782struct sigaction act;783int done = 0;784785g_pname = basename(argv[0]);786argv[0] = g_pname; /* rewrite argv[0] for getopt errors */787#ifdef illumos788/*789* Make sure we have the required dtrace_proc privilege.790*/791if ((ucp = ucred_get(getpid())) != NULL) {792const priv_set_t *psp;793if ((psp = ucred_getprivset(ucp, PRIV_EFFECTIVE)) != NULL &&794!priv_ismember(psp, PRIV_DTRACE_PROC)) {795fatal("dtrace_proc privilege required\n");796}797798ucred_free(ucp);799}800#endif801802while ((c = getopt(argc, argv, PLOCKSTAT_OPTSTR)) != EOF) {803switch (c) {804case 'n':805errno = 0;806g_nent = strtoul(optarg, &end, 10);807if (*end != '\0' || errno != 0) {808(void) fprintf(stderr, "%s: invalid count "809"'%s'\n", g_pname, optarg);810usage();811}812break;813814case 'p':815opt_p = 1;816break;817818case 'v':819opt_v = 1;820break;821822case 'A':823opt_C = opt_H = 1;824break;825826case 'C':827opt_C = 1;828break;829830case 'H':831opt_H = 1;832break;833834case 'V':835g_opt_V = 1;836break;837838default:839if (strchr(PLOCKSTAT_OPTSTR, c) == NULL)840usage();841}842}843844/*845* We need a command or at least one pid.846*/847if (argc == optind)848usage();849850if (opt_C == 0 && opt_H == 0)851opt_C = 1;852853if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL)854fatal("failed to initialize dtrace: %s\n",855dtrace_errmsg(NULL, err));856857/*858* The longest string we trace is 23 bytes long -- so 32 is plenty.859*/860if (dtrace_setopt(g_dtp, "strsize", "32") == -1)861dfatal("failed to set 'strsize'");862863/*864* 1k should be more than enough for all trace() and printa() actions.865*/866if (dtrace_setopt(g_dtp, "bufsize", "1k") == -1)867dfatal("failed to set 'bufsize'");868869/*870* The table we produce has the hottest locks at the top.871*/872if (dtrace_setopt(g_dtp, "aggsortrev", NULL) == -1)873dfatal("failed to set 'aggsortrev'");874875/*876* These are two reasonable defaults which should suffice.877*/878if (dtrace_setopt(g_dtp, "aggsize", "256k") == -1)879dfatal("failed to set 'aggsize'");880if (dtrace_setopt(g_dtp, "aggrate", "1sec") == -1)881dfatal("failed to set 'aggrate'");882883/*884* Take a second pass through to look for options that set options now885* that we have an open dtrace handle.886*/887optind = 1;888while ((c = getopt(argc, argv, PLOCKSTAT_OPTSTR)) != EOF) {889switch (c) {890case 's':891g_opt_s = 1;892if (dtrace_setopt(g_dtp, "ustackframes", optarg) == -1)893dfatal("failed to set 'ustackframes'");894break;895896case 'x':897if ((p = strchr(optarg, '=')) != NULL)898*p++ = '\0';899900if (dtrace_setopt(g_dtp, optarg, p) != 0)901dfatal("failed to set -x %s", optarg);902break;903904case 'e':905errno = 0;906(void) strtoul(optarg, &end, 10);907if (*optarg == '-' || *end != '\0' || errno != 0) {908(void) fprintf(stderr, "%s: invalid timeout "909"'%s'\n", g_pname, optarg);910usage();911}912913/*914* Construct a DTrace enabling that will exit after915* the specified number of seconds.916*/917dprog_add("BEGIN\n{\n\tend = timestamp + ");918dprog_add(optarg);919dprog_add(" * 1000000000;\n}\n");920dprog_add("tick-10hz\n/timestamp >= end/\n");921dprog_add("{\n\texit(0);\n}\n");922break;923}924}925926argc -= optind;927argv += optind;928929if (opt_H) {930dprog_add(g_hold_init);931if (!g_opt_s)932dprog_add(g_hold_times);933else934dprog_add(g_hold_histogram);935}936937if (opt_C) {938dprog_add(g_ctnd_init);939if (!g_opt_s)940dprog_add(g_ctnd_times);941else942dprog_add(g_ctnd_histogram);943}944945if (opt_p) {946ulong_t pid;947948if (argc > 1) {949(void) fprintf(stderr, "%s: only one pid is allowed\n",950g_pname);951usage();952}953954errno = 0;955pid = strtoul(argv[0], &end, 10);956if (*end != '\0' || errno != 0 || (pid_t)pid != pid) {957(void) fprintf(stderr, "%s: invalid pid '%s'\n",958g_pname, argv[0]);959usage();960}961962if ((g_pr = dtrace_proc_grab(g_dtp, (pid_t)pid, 0)) == NULL)963dfatal(NULL);964} else {965if ((g_pr = dtrace_proc_create(g_dtp, argv[0], argv, NULL, NULL)) == NULL)966dfatal(NULL);967}968969dprog_compile();970971if (dtrace_handle_proc(g_dtp, &prochandler, NULL) == -1)972dfatal("failed to establish proc handler");973974(void) sigemptyset(&act.sa_mask);975act.sa_flags = 0;976act.sa_handler = intr;977(void) sigaction(SIGINT, &act, NULL);978(void) sigaction(SIGTERM, &act, NULL);979980if (dtrace_go(g_dtp) != 0)981dfatal("dtrace_go()");982983if (dtrace_getopt(g_dtp, "ustackframes", &g_nframes) != 0)984dfatal("failed to get 'ustackframes'");985986dtrace_proc_continue(g_dtp, g_pr);987988if (opt_v)989(void) printf("%s: tracing enabled for pid %d\n", g_pname,990#ifdef illumos991(int)Pstatus(g_pr)->pr_pid);992#else993(int)proc_getpid(g_pr));994#endif995996do {997if (!g_intr && !done)998dtrace_sleep(g_dtp);9991000if (done || g_intr || g_exited) {1001done = 1;1002if (dtrace_stop(g_dtp) == -1)1003dfatal("couldn't stop tracing");1004}10051006switch (dtrace_work(g_dtp, stdout, NULL, chewrec, NULL)) {1007case DTRACE_WORKSTATUS_DONE:1008done = 1;1009break;1010case DTRACE_WORKSTATUS_OKAY:1011break;1012default:1013dfatal("processing aborted");1014}10151016} while (!done);10171018dtrace_close(g_dtp);10191020return (0);1021}102210231024