diff options
Diffstat (limited to 'Apps')
-rw-r--r-- | Apps/Readme | 5 | ||||
-rwxr-xr-x | Apps/httpdstat.d | 132 | ||||
-rwxr-xr-x | Apps/nfswizard.d | 102 | ||||
-rwxr-xr-x | Apps/shellsnoop | 268 | ||||
-rwxr-xr-x | Apps/weblatency.d | 186 |
5 files changed, 693 insertions, 0 deletions
diff --git a/Apps/Readme b/Apps/Readme new file mode 100644 index 000000000000..3a6812f4bfa2 --- /dev/null +++ b/Apps/Readme @@ -0,0 +1,5 @@ +Apps - Specific Application based analysis + + These are DTrace scripts that are written to analyse a particular + application or applictaion layer protocol. For example, Apache or NFS + scripts would appear here. diff --git a/Apps/httpdstat.d b/Apps/httpdstat.d new file mode 100755 index 000000000000..a053482a6c15 --- /dev/null +++ b/Apps/httpdstat.d @@ -0,0 +1,132 @@ +#!/usr/sbin/dtrace -s +/* + * httpdstat.d - realtime httpd statistics. Uses DTrace. + * + * $Id: httpdstat.d 2 2007-08-01 10:01:43Z brendan $ + * + * USAGE: httpdstat.d [interval [count]] + * + * interval seconds + * count number of samples + * + * FIELDS: + * TIME Time, string + * NUM Number of connections + * GET Number of "GET"s + * POST Number of "POST"s + * HEAD Number of "HEAD"s + * TRACE Number of "TRACE"s + * + * All of the statistics are printed as a value per interval (not per second). + * + * NOTE: This version does not process subsequent operations on keepalives. + * + * IDEA: Ryan Matteson (who first wrote a solution to this). + * + * COPYRIGHT: Copyright (c) 2005 Brendan Gregg. + * + * 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 Docs/cddl1.txt + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * CDDL HEADER END + * + * 20-Nov-2005 Brendan Gregg Created this. + */ + +#pragma D option quiet +#pragma D option defaultargs + +inline int SCREEN = 21; + +/* + * Program Start + */ +dtrace:::BEGIN +{ + num = 0; get = 0; head = 0; post = 0; trac = 0; + lines = SCREEN + 1; + secs = $1 ? $1 : 1; + counts = $2 ? $2 : -1; + first = 1; +} + +profile:::tick-1sec +{ + secs--; +} + +/* + * Print Header + */ +dtrace:::BEGIN, +profile:::tick-1sec +/first || (secs == 0 && lines > SCREEN)/ +{ + printf("%-20s %6s %6s %5s %5s %5s\n", "TIME", + "NUM", "GET", "POST", "HEAD", "TRACE"); + lines = 0; + first = 0; +} + +/* + * Track Accept Events + */ +syscall::accept:return +/execname == "httpd"/ +{ + self->buf = 1; +} + +syscall::read:entry +/self->buf/ +{ + self->buf = arg1; +} + +/* + * Tally Data + */ +syscall::read:return +/self->buf && arg0/ +{ + this->str = (char *)copyin(self->buf, arg0); + this->str[4] = '\0'; + get += stringof(this->str) == "GET " ? 1 : 0; + post += stringof(this->str) == "POST" ? 1 : 0; + head += stringof(this->str) == "HEAD" ? 1 : 0; + trac += stringof(this->str) == "TRAC" ? 1 : 0; + num++; + self->buf = 0; +} + +/* + * Print Output + */ +profile:::tick-1sec +/secs == 0/ +{ + printf("%-20Y %6d %6d %5d %5d %5d\n", walltimestamp, + num, get, post, head, trac); + num = 0; get = 0; head = 0; post = 0; trac = 0; + secs = $1 ? $1 : 1; + lines++; + counts--; +} + +/* + * End + */ +profile:::tick-1sec +/counts == 0/ +{ + exit(0); +} diff --git a/Apps/nfswizard.d b/Apps/nfswizard.d new file mode 100755 index 000000000000..c63bc33dfa4e --- /dev/null +++ b/Apps/nfswizard.d @@ -0,0 +1,102 @@ +#!/usr/sbin/dtrace -s +/* + * nfswizard.d - nfs client activity wizard. + * Written using DTrace (Solaris 10 3/05). + * + * This examines activity caused by NFS client processes on the same server + * that you are running this script on. A detailed report is generated + * to explain various details of NFS client activity, including response + * times and file access. + * + * $Id: nfswizard.d 3 2007-08-01 10:50:08Z brendan $ + * + * USAGE: nfswizard.d # hit Ctrl-C to end sample + * + * COPYRIGHT: Copyright (c) 2005, 2006 Brendan Gregg. + * + * 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 Docs/cddl1.txt + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * CDDL HEADER END + * + * 02-Dec-2005 Brendan Gregg Created this. + * 20-Apr-2006 " " Last update. + */ + +#pragma D option quiet + +dtrace:::BEGIN +{ + printf("Tracing... Hit Ctrl-C to end.\n"); + scriptstart = walltimestamp; + timestart = timestamp; +} + +io:nfs::start +{ + /* tally file sizes */ + @file[args[2]->fi_pathname] = sum(args[0]->b_bcount); + + /* time response */ + start[args[0]->b_addr] = timestamp; + + /* overall stats */ + @rbytes = sum(args[0]->b_flags & B_READ ? args[0]->b_bcount : 0); + @wbytes = sum(args[0]->b_flags & B_READ ? 0 : args[0]->b_bcount); + @events = count(); +} + +io:nfs::done +/start[args[0]->b_addr]/ +{ + /* calculate and save response time stats */ + this->elapsed = timestamp - start[args[0]->b_addr]; + @maxtime = max(this->elapsed); + @avgtime = avg(this->elapsed); + @qnztime = quantize(this->elapsed / 1000); +} + +dtrace:::END +{ + /* print header */ + printf("NFS Client Wizard. %Y -> %Y\n\n", scriptstart, walltimestamp); + + /* print read/write stats */ + printa("Read: %@d bytes ", @rbytes); + normalize(@rbytes, 1000000); + printa("(%@d Mb)\n", @rbytes); + printa("Write: %@d bytes ", @wbytes); + normalize(@wbytes, 1000000); + printa("(%@d Mb)\n\n", @wbytes); + + /* print throughput stats */ + denormalize(@rbytes); + normalize(@rbytes, (timestamp - timestart) / 1000000); + printa("Read: %@d Kb/sec\n", @rbytes); + denormalize(@wbytes); + normalize(@wbytes, (timestamp - timestart) / 1000000); + printa("Write: %@d Kb/sec\n\n", @wbytes); + + /* print time stats */ + printa("NFS I/O events: %@d\n", @events); + normalize(@avgtime, 1000000); + printa("Avg response time: %@d ms\n", @avgtime); + normalize(@maxtime, 1000000); + printa("Max response time: %@d ms\n\n", @maxtime); + printa("Response times (us):%@d\n", @qnztime); + + /* print file stats */ + printf("Top 25 files accessed (bytes):\n"); + printf(" %-64s %s\n", "PATHNAME", "BYTES"); + trunc(@file, 25); + printa(" %-64s %@d\n", @file); +} diff --git a/Apps/shellsnoop b/Apps/shellsnoop new file mode 100755 index 000000000000..95f42c046529 --- /dev/null +++ b/Apps/shellsnoop @@ -0,0 +1,268 @@ +#!/usr/bin/sh +# +# shellsnoop - A program to print read/write details from shells, +# such as keystrokes and command outputs. +# Written using DTrace (Solaris 10 3/05). +# +# This program sounds somewhat dangerous (snooping keystrokes), but is +# no more so than /usr/bin/truss, and both need root or dtrace privileges to +# run. In fact, less dangerous, as we only print visible text (not password +# text, for example). Having said that, it goes without saying that this +# program shouldn't be used for breeching privacy of other users. +# +# This was written as a tool to demonstrate the capabilities of DTrace. +# +# $Id: shellsnoop 19 2007-09-12 07:47:59Z brendan $ +# +# USAGE: shellsnoop [-hqsv] [-p PID] [-u UID] +# +# -q # quiet, only print data +# -s # include start time, us +# -v # include start time, string +# -p PID # process ID to snoop +# -u UID # user ID to snoop +# eg, +# shellsnoop # default output +# shellsnoop -v # human readable timestamps +# shellsnoop -p 1892 # snoop this PID only +# shellsnoop -qp 1892 # watch this PID data only +# +# FIELDS: +# UID User ID +# PID process ID +# PPID parent process ID +# COMM command name +# DIR direction (R read, W write) +# TEXT text contained in the read/write +# TIME timestamp for the command, us +# STRTIME timestamp for the command, string +# +# SEE ALSO: ttywatcher +# +# COPYRIGHT: Copyright (c) 2005 Brendan Gregg. +# +# 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 Docs/cddl1.txt +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# CDDL HEADER END +# +# Author: Brendan Gregg [Sydney, Australia] +# +# 28-Mar-2004 Brendan Gregg Created this. +# 21-Jan-2005 " " Wrapped in sh to provide options. +# 30-Nov-2005 " " Fixed trailing buffer text bug. +# 30-Nov-2005 " " Fixed sh no keystroke text in quiet bug. +# 30-Nov-2005 " " Last update. +# + + +############################## +# --- Process Arguments --- +# +opt_pid=0; opt_uid=0; opt_time=0; opt_timestr=0; opt_quiet=0; opt_debug=0 +filter=0; pid=0; uid=0 + +while getopts dhp:qsu:v name +do + case $name in + d) opt_debug=1 ;; + p) opt_pid=1; pid=$OPTARG ;; + q) opt_quiet=1 ;; + s) opt_time=1 ;; + u) opt_uid=1; uid=$OPTARG ;; + v) opt_timestr=1 ;; + h|?) cat <<-END >&2 + USAGE: shellsnoop [-hqsv] [-p PID] [-u UID] + shellsnoop # default output + -q # quiet, only print data + -s # include start time, us + -v # include start time, string + -p PID # process ID to snoop + -u UID # user ID to snoop + END + exit 1 + esac +done + +if [ $opt_quiet -eq 1 ]; then + opt_time=0; opt_timestr=0 +fi +if [ $opt_pid -eq 1 -o $opt_uid -eq 1 ]; then + filter=1 +fi + + +################################# +# --- Main Program, DTrace --- +# +dtrace -n ' + /* + * Command line arguments + */ + inline int OPT_debug = '$opt_debug'; + inline int OPT_quiet = '$opt_quiet'; + inline int OPT_pid = '$opt_pid'; + inline int OPT_uid = '$opt_uid'; + inline int OPT_time = '$opt_time'; + inline int OPT_timestr = '$opt_timestr'; + inline int FILTER = '$filter'; + inline int PID = '$pid'; + inline int UID = '$uid'; + + #pragma D option quiet + #pragma D option switchrate=20hz + + /* + * Print header + */ + dtrace:::BEGIN /OPT_time == 1/ + { + printf("%-14s ","TIME"); + } + dtrace:::BEGIN /OPT_timestr == 1/ + { + printf("%-20s ","STRTIME"); + } + dtrace:::BEGIN /OPT_quiet == 0/ + { + printf("%5s %5s %8s %3s %s\n", "PID", "PPID", "CMD", "DIR", "TEXT"); + } + + /* + * Remember this PID is a shell child + */ + syscall::exec:entry, syscall::exece:entry + /execname == "sh" || execname == "ksh" || execname == "csh" || + execname == "tcsh" || execname == "zsh" || execname == "bash"/ + { + child[pid] = 1; + + /* debug */ + this->parent = (char *)curthread->t_procp->p_parent->p_user.u_comm; + OPT_debug == 1 ? printf("PID %d CMD %s started. (%s)\n", + pid, execname, stringof(this->parent)) : 1; + } + syscall::exec:entry, syscall::exece:entry + /(OPT_pid == 1 && PID != ppid) || (OPT_uid == 1 && UID != uid)/ + { + /* forget if filtered */ + child[pid] = 0; + } + + /* + * Print shell keystrokes + */ + syscall::write:entry, syscall::read:entry + /(execname == "sh" || execname == "ksh" || execname == "csh" || + execname == "tcsh" || execname == "zsh" || execname == "bash") + && (arg0 >= 0 && arg0 <= 2)/ + { + self->buf = arg1; + } + syscall::write:entry, syscall::read:entry + /(OPT_pid == 1 && PID != pid) || (OPT_uid == 1 && UID != uid)/ + { + self->buf = 0; + } + syscall::write:return, syscall::read:return + /self->buf && child[pid] == 0 && OPT_time == 1/ + { + printf("%-14d ", timestamp/1000); + } + syscall::write:return, syscall::read:return + /self->buf && child[pid] == 0 && OPT_timestr == 1/ + { + printf("%-20Y ", walltimestamp); + } + syscall::write:return, syscall::read:return + /self->buf && child[pid] == 0 && OPT_quiet == 0/ + { + this->text = (char *)copyin(self->buf, arg0); + this->text[arg0] = '\'\\0\''; + + printf("%5d %5d %8s %3s %s\n", pid, curpsinfo->pr_ppid, execname, + probefunc == "read" ? "R" : "W", stringof(this->text)); + } + syscall::write:return + /self->buf && child[pid] == 0 && OPT_quiet == 1/ + { + this->text = (char *)copyin(self->buf, arg0); + this->text[arg0] = '\'\\0\''; + printf("%s", stringof(this->text)); + } + syscall::read:return + /self->buf && execname == "sh" && child[pid] == 0 && OPT_quiet == 1/ + { + this->text = (char *)copyin(self->buf, arg0); + this->text[arg0] = '\'\\0\''; + printf("%s", stringof(this->text)); + } + syscall::write:return, syscall::read:return + /self->buf && child[pid] == 0/ + { + self->buf = 0; + } + + /* + * Print command output + */ + syscall::write:entry, syscall::read:entry + /child[pid] == 1 && (arg0 == 1 || arg0 == 2)/ + { + self->buf = arg1; + } + syscall::write:return, syscall::read:return + /self->buf && OPT_time == 1/ + { + printf("%-14d ", timestamp/1000); + } + syscall::write:return, syscall::read:return + /self->buf && OPT_timestr == 1/ + { + printf("%-20Y ", walltimestamp); + } + syscall::write:return, syscall::read:return + /self->buf && OPT_quiet == 0/ + { + this->text = (char *)copyin(self->buf, arg0); + this->text[arg0] = '\'\\0\''; + + printf("%5d %5d %8s %3s %s", pid, curpsinfo->pr_ppid, execname, + probefunc == "read" ? "R" : "W", stringof(this->text)); + + /* here we check if a newline is needed */ + this->length = strlen(this->text); + printf("%s", this->text[this->length - 1] == '\'\\n\'' ? "" : "\n"); + self->buf = 0; + } + syscall::write:return, syscall::read:return + /self->buf && OPT_quiet == 1/ + { + this->text = (char *)copyin(self->buf, arg0); + this->text[arg0] = '\'\\0\''; + printf("%s", stringof(this->text)); + self->buf = 0; + } + + /* + * Cleanup + */ + syscall::rexit:entry + { + child[pid] = 0; + + /* debug */ + this->parent = (char *)curthread->t_procp->p_parent->p_user.u_comm; + OPT_debug == 1 ? printf("PID %d CMD %s exited. (%s)\n", + pid, execname, stringof(this->parent)) : 1; + } +' diff --git a/Apps/weblatency.d b/Apps/weblatency.d new file mode 100755 index 000000000000..8d96d5cdd88b --- /dev/null +++ b/Apps/weblatency.d @@ -0,0 +1,186 @@ +#!/usr/sbin/dtrace -s +/* + * weblatency.d - website latency statistics. + * Written using DTrace (Solaris 10 3/05). + * + * $Id: weblatency.d 3 2007-08-01 10:50:08Z brendan $ + * + * USAGE: weblatency.d # hit Ctrl-C to end sample + * + * See the code below for the "BROWSER" variable, which sets the browser + * to trace (currently set to "mozilla-bin"). + * + * This is written as an experimental tool, and may not work at all with + * your browser. + * + * FIELDS: + * HOST Hostname from URL + * NUM Number of GETs + * AVGTIME(ms) Average time for response, ms + * MAXTIME(ms) Maximum time for response, ms + * + * NOTE: + * + * The latency measured here is from the browser sending the GET + * request to when the browser begins to recieve the response. It + * is an overall response time for the client, and encompasses + * connection speed delays, DNS lookups, proxy delays, and web server + * response time. + * + * IDEA: Bryan Cantrill (who wrote an elegant version for Sol 10 update 1) + * + * COPYRIGHT: Copyright (c) 2005, 2006 Brendan Gregg. + * + * 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 Docs/cddl1.txt + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * CDDL HEADER END + * + * ToDo: + * Check write fd for socket, not file. + * + * 30-Nov-2005 Brendan Gregg Created this. + * 20-Apr-2006 " " Last update. + */ + +#pragma D option quiet + +/* browser's execname */ +inline string BROWSER = "mozilla-bin"; + +/* maximum expected hostname length + "GET http://" */ +inline int MAX_REQ = 64; + +dtrace:::BEGIN +{ + printf("Tracing... Hit Ctrl-C to end.\n"); +} + +/* + * Trace brower request + * + * This is achieved by matching writes for the browser's execname that + * start with "GET", and then timing from the return of the write to + * the return of the next read in the same thread. Various stateful flags + * are used: self->fd, self->read. + * + * For performance reasons, I'd like to only process writes that follow a + * connect(), however this approach fails to process keepalives. + */ +syscall::write:entry +/execname == BROWSER/ +{ + self->buf = arg1; + self->fd = arg0 + 1; + self->nam = ""; +} + +syscall::write:return +/self->fd/ +{ + this->str = (char *)copyin(self->buf, MAX_REQ); + this->str[4] = '\0'; + self->fd = stringof(this->str) == "GET " ? self->fd : 0; +} + +syscall::write:return +/self->fd/ +{ + /* fetch browser request */ + this->str = (char *)copyin(self->buf, MAX_REQ); + this->str[MAX_REQ] = '\0'; + + /* + * This unrolled loop strips down a URL to it's hostname. + * We ought to use strtok(), but it's not available on Sol 10 3/05, + * so instead I used dirname(). It's not pretty - it's done so that + * this works on all Sol 10 versions. + */ + self->req = stringof(this->str); + self->nam = strlen(self->req) > 15 ? self->req : self->nam; + self->req = dirname(self->req); + self->nam = strlen(self->req) > 15 ? self->req : self->nam; + self->req = dirname(self->req); + self->nam = strlen(self->req) > 15 ? self->req : self->nam; + self->req = dirname(self->req); + self->nam = strlen(self->req) > 15 ? self->req : self->nam; + self->req = dirname(self->req); + self->nam = strlen(self->req) > 15 ? self->req : self->nam; + self->req = dirname(self->req); + self->nam = strlen(self->req) > 15 ? self->req : self->nam; + self->req = dirname(self->req); + self->nam = strlen(self->req) > 15 ? self->req : self->nam; + self->req = dirname(self->req); + self->nam = strlen(self->req) > 15 ? self->req : self->nam; + self->req = dirname(self->req); + self->nam = strlen(self->req) > 15 ? self->req : self->nam; + self->nam = basename(self->nam); + + /* start the timer */ + start[pid, self->fd - 1] = timestamp; + host[pid, self->fd - 1] = self->nam; + self->buf = 0; + self->fd = 0; + self->req = 0; + self->nam = 0; +} + +/* this one wasn't a GET */ +syscall::write:return +/self->buf/ +{ + self->buf = 0; + self->fd = 0; +} + +syscall::read:entry +/execname == BROWSER && start[pid, arg0]/ +{ + self->fd = arg0 + 1; +} + +/* + * Record host details + */ +syscall::read:return +/self->fd/ +{ + /* fetch details */ + self->host = stringof(host[pid, self->fd - 1]); + this->start = start[pid, self->fd - 1]; + + /* save details */ + @Avg[self->host] = avg((timestamp - this->start)/1000000); + @Max[self->host] = max((timestamp - this->start)/1000000); + @Num[self->host] = count(); + + /* clear vars */ + start[pid, self->fd - 1] = 0; + host[pid, self->fd - 1] = 0; + self->host = 0; + self->fd = 0; +} + +/* + * Output report + */ +dtrace:::END +{ + printf("%-32s %11s\n", "HOST", "NUM"); + printa("%-32s %@11d\n", @Num); + + printf("\n%-32s %11s\n", "HOST", "AVGTIME(ms)"); + printa("%-32s %@11d\n", @Avg); + + printf("\n%-32s %11s\n", "HOST", "MAXTIME(ms)"); + printa("%-32s %@11d\n", @Max); +} |