#!/usr/bin/ksh
#
# iopattern - print disk I/O pattern.
# Written using DTrace (Solaris 10 3/05).
#
# This prints details on the I/O access pattern for the disks, such as
# percentage of events that were of a random or sequential nature.
# By default totals for all disks are printed.
#
# $Id: iopattern 65 2007-10-04 11:09:40Z brendan $
#
# USAGE: iopattern [-v] [-d device] [-f filename] [-m mount_point]
# [interval [count]]
#
# -v # print timestamp, string
# -d device # instance name to snoop (eg, dad0)
# -f filename # full pathname of file to snoop
# -m mount_point # this FS only (will skip raw events)
# eg,
# iopattern # default output, 1 second intervals
# iopattern 10 # 10 second samples
# iopattern 5 12 # print 12 x 5 second samples
# iopattern -m / # snoop events on filesystem / only
#
# FIELDS:
# %RAN percentage of events of a random nature
# %SEQ percentage of events of a sequential nature
# COUNT number of I/O events
# MIN minimum I/O event size
# MAX maximum I/O event size
# AVG average I/O event size
# KR total kilobytes read during sample
# KW total kilobytes written during sample
# DEVICE device name
# MOUNT mount point
# FILE filename
# TIME timestamp, string
#
# NOTES:
#
# An event is considered random when the heads seek. This program prints
# the percentage of events that are random. The size of the seek is not
# measured - it's either random or not.
#
# SEE ALSO: iosnoop, iotop
#
# IDEA: Ryan Matteson
#
# 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]
#
# 25-Jul-2005 Brendan Gregg Created this.
# 25-Jul-2005 " " Last update.
#
##############################
# --- Process Arguments ---
#
### default variables
opt_device=0; opt_file=0; opt_mount=0; opt_time=0
filter=0; device=.; filename=.; mount=.; interval=1; count=-1
### process options
while getopts d:f:hm:v name
do
case $name in
d) opt_device=1; device=$OPTARG ;;
f) opt_file=1; filename=$OPTARG ;;
m) opt_mount=1; mount=$OPTARG ;;
v) opt_time=1 ;;
h|?) cat <<-END >&2
USAGE: iopattern [-v] [-d device] [-f filename] [-m mount_point]
[interval [count]]
-v # print timestamp
-d device # instance name to snoop
-f filename # snoop this file only
-m mount_point # this FS only
eg,
iopattern # default output, 1 second samples
iopattern 10 # 10 second samples
iopattern 5 12 # print 12 x 5 second samples
iopattern -m / # snoop events on filesystem / only
END
exit 1
esac
done
shift $(( $OPTIND - 1 ))
### option logic
if [[ "$1" > 0 ]]; then
interval=$1; shift
fi
if [[ "$1" > 0 ]]; then
count=$1; shift
fi
if (( opt_device || opt_mount || opt_file )); then
filter=1
fi
#################################
# --- Main Program, DTrace ---
#
/usr/sbin/dtrace -n '
/*
* Command line arguments
*/
inline int OPT_time = '$opt_time';
inline int OPT_device = '$opt_device';
inline int OPT_mount = '$opt_mount';
inline int OPT_file = '$opt_file';
inline int INTERVAL = '$interval';
inline int COUNTER = '$count';
inline int FILTER = '$filter';
inline string DEVICE = "'$device'";
inline string FILENAME = "'$filename'";
inline string MOUNT = "'$mount'";
#pragma D option quiet
int last_loc[string];
/*
* Program start
*/
dtrace:::BEGIN
{
/* starting values */
diskcnt = 0;
diskmin = 0;
diskmax = 0;
diskran = 0;
diskr = 0;
diskw = 0;
counts = COUNTER;
secs = INTERVAL;
LINES = 20;
line = 0;
last_event[""] = 0;
}
/*
* Print header
*/
profile:::tick-1sec
/line <= 0 /
{
/* print optional headers */
OPT_time ? printf("%-20s ", "TIME") : 1;
OPT_device ? printf("%-9s ", "DEVICE") : 1;
OPT_mount ? printf("%-12s ", "MOUNT") : 1;
OPT_file ? printf("%-12s ", "FILE") : 1;
/* print header */
printf("%4s %4s %6s %6s %6s %6s %6s %6s\n",
"%RAN", "%SEQ", "COUNT", "MIN", "MAX", "AVG", "KR", "KW");
line = LINES;
}
/*
* Check event is being traced
*/
io:genunix::done
{
/* default is to trace unless filtering */
self->ok = FILTER ? 0 : 1;
/* check each filter */
(OPT_device == 1 && DEVICE == args[1]->dev_statname)? self->ok = 1 : 1;
(OPT_file == 1 && FILENAME == args[2]->fi_pathname) ? self->ok = 1 : 1;
(OPT_mount == 1 && MOUNT == args[2]->fi_mount) ? self->ok = 1 : 1;
}
/*
* Process and Print completion
*/
io:genunix::done
/self->ok/
{
/*
* Save details
*/
this->loc = args[0]->b_blkno * 512;
this->pre = last_loc[args[1]->dev_statname];
diskr += args[0]->b_flags & B_READ ? args[0]->b_bcount : 0;
diskw += args[0]->b_flags & B_READ ? 0 : args[0]->b_bcount;
diskran += this->pre == this->loc ? 0 : 1;
diskcnt++;
diskmin = diskmin == 0 ? args[0]->b_bcount :
(diskmin > args[0]->b_bcount ? args[0]->b_bcount : diskmin);
diskmax = diskmax < args[0]->b_bcount ? args[0]->b_bcount : diskmax;
/* save disk location */
last_loc[args[1]->dev_statname] = this->loc + args[0]->b_bcount;
/* cleanup */
self->ok = 0;
}
/*
* Timer
*/
profile:::tick-1sec
{
secs--;
}
/*
* Print Output
*/
profile:::tick-1sec
/secs == 0/
{
/* calculate diskavg */
diskavg = diskcnt > 0 ? (diskr + diskw) / diskcnt : 0;
/* convert counters to Kbytes */
diskr /= 1024;
diskw /= 1024;
/* convert to percentages */
diskran = diskcnt == 0 ? 0 : (diskran * 100) / diskcnt;
diskseq = diskcnt == 0 ? 0 : 100 - diskran;
/* print optional fields */
OPT_time ? printf("%-20Y ", walltimestamp) : 1;
OPT_device ? printf("%-9s ", DEVICE) : 1;
OPT_mount ? printf("%-12s ", MOUNT) : 1;
OPT_file ? printf("%-12s ", FILENAME) : 1;
/* print data */
printf("%4d %4d %6d %6d %6d %6d %6d %6d\n",
diskran, diskseq, diskcnt, diskmin, diskmax, diskavg,
diskr, diskw);
/* clear data */
diskmin = 0;
diskmax = 0;
diskcnt = 0;
diskran = 0;
diskr = 0;
diskw = 0;
secs = INTERVAL;
counts--;
line--;
}
/*
* End of program
*/
profile:::tick-1sec
/counts == 0/
{
exit(0);
}
'