1235368Sgnn#!/usr/bin/ksh
2235368Sgnn#
3235368Sgnn# iopattern - print disk I/O pattern.
4235368Sgnn#             Written using DTrace (Solaris 10 3/05).
5235368Sgnn#
6235368Sgnn# This prints details on the I/O access pattern for the disks, such as
7235368Sgnn# percentage of events that were of a random or sequential nature.
8235368Sgnn# By default totals for all disks are printed.
9235368Sgnn#
10235368Sgnn# $Id: iopattern 65 2007-10-04 11:09:40Z brendan $
11235368Sgnn#
12235368Sgnn# USAGE:	iopattern [-v] [-d device] [-f filename] [-m mount_point] 
13235368Sgnn#			  [interval [count]]
14235368Sgnn#
15235368Sgnn#		       -v       	# print timestamp, string
16235368Sgnn#		       -d device	# instance name to snoop (eg, dad0)
17235368Sgnn#		       -f filename	# full pathname of file to snoop
18235368Sgnn#		       -m mount_point	# this FS only (will skip raw events)
19235368Sgnn#  eg,
20235368Sgnn#		iopattern   	# default output, 1 second intervals
21235368Sgnn#		iopattern 10  	# 10 second samples
22235368Sgnn#		iopattern 5 12	# print 12 x 5 second samples
23235368Sgnn#	        iopattern -m /  # snoop events on filesystem / only
24235368Sgnn# 	
25235368Sgnn# FIELDS:
26235368Sgnn#		%RAN  		percentage of events of a random nature
27235368Sgnn#		%SEQ 	 	percentage of events of a sequential nature
28235368Sgnn#		COUNT		number of I/O events
29235368Sgnn#		MIN		minimum I/O event size
30235368Sgnn#		MAX		maximum I/O event size
31235368Sgnn#		AVG		average I/O event size
32235368Sgnn#		KR		total kilobytes read during sample
33235368Sgnn#		KW		total kilobytes written during sample
34235368Sgnn#		DEVICE		device name
35235368Sgnn#		MOUNT		mount point
36235368Sgnn#		FILE		filename
37235368Sgnn#		TIME		timestamp, string
38235368Sgnn# 
39235368Sgnn# NOTES:
40235368Sgnn#
41235368Sgnn#  An event is considered random when the heads seek. This program prints
42235368Sgnn#  the percentage of events that are random. The size of the seek is not
43235368Sgnn#  measured - it's either random or not.
44235368Sgnn#
45235368Sgnn# SEE ALSO: iosnoop, iotop
46235368Sgnn# 
47235368Sgnn# IDEA: Ryan Matteson
48235368Sgnn#
49235368Sgnn# COPYRIGHT: Copyright (c) 2005 Brendan Gregg.
50235368Sgnn#
51235368Sgnn# CDDL HEADER START
52235368Sgnn#
53235368Sgnn#  The contents of this file are subject to the terms of the
54235368Sgnn#  Common Development and Distribution License, Version 1.0 only
55235368Sgnn#  (the "License").  You may not use this file except in compliance
56235368Sgnn#  with the License.
57235368Sgnn#
58235368Sgnn#  You can obtain a copy of the license at Docs/cddl1.txt
59235368Sgnn#  or http://www.opensolaris.org/os/licensing.
60235368Sgnn#  See the License for the specific language governing permissions
61235368Sgnn#  and limitations under the License.
62235368Sgnn#
63235368Sgnn# CDDL HEADER END
64235368Sgnn#
65235368Sgnn# Author: Brendan Gregg  [Sydney, Australia]
66235368Sgnn#
67235368Sgnn# 25-Jul-2005	Brendan Gregg	Created this.
68235368Sgnn# 25-Jul-2005	   "      "	Last update.
69235368Sgnn#
70235368Sgnn
71235368Sgnn
72235368Sgnn##############################
73235368Sgnn# --- Process Arguments ---
74235368Sgnn#
75235368Sgnn
76235368Sgnn### default variables
77235368Sgnnopt_device=0; opt_file=0; opt_mount=0; opt_time=0
78235368Sgnnfilter=0; device=.; filename=.; mount=.; interval=1; count=-1
79235368Sgnn
80235368Sgnn### process options
81235368Sgnnwhile getopts d:f:hm:v name
82235368Sgnndo
83235368Sgnn	case $name in
84235368Sgnn	d)	opt_device=1; device=$OPTARG ;;
85235368Sgnn	f)	opt_file=1; filename=$OPTARG ;;
86235368Sgnn	m)	opt_mount=1; mount=$OPTARG ;;
87235368Sgnn	v)	opt_time=1 ;;
88235368Sgnn	h|?)	cat <<-END >&2
89235368Sgnn		USAGE: iopattern [-v] [-d device] [-f filename] [-m mount_point]
90235368Sgnn		                 [interval [count]]
91235368Sgnn 
92235368Sgnn		                -v              # print timestamp
93235368Sgnn		                -d device       # instance name to snoop 
94235368Sgnn		                -f filename     # snoop this file only
95235368Sgnn		                -m mount_point  # this FS only 
96235368Sgnn		   eg,
97235368Sgnn		        iopattern         # default output, 1 second samples
98235368Sgnn		        iopattern 10      # 10 second samples
99235368Sgnn		        iopattern 5 12    # print 12 x 5 second samples
100235368Sgnn		        iopattern -m /    # snoop events on filesystem / only
101235368Sgnn		END
102235368Sgnn		exit 1
103235368Sgnn	esac
104235368Sgnndone
105235368Sgnn
106235368Sgnnshift $(( $OPTIND - 1 ))
107235368Sgnn
108235368Sgnn### option logic
109235368Sgnnif [[ "$1" > 0 ]]; then
110235368Sgnn        interval=$1; shift
111235368Sgnnfi
112235368Sgnnif [[ "$1" > 0 ]]; then
113235368Sgnn        count=$1; shift
114235368Sgnnfi
115235368Sgnnif (( opt_device || opt_mount || opt_file )); then
116235368Sgnn	filter=1
117235368Sgnnfi
118235368Sgnn
119235368Sgnn
120235368Sgnn#################################
121235368Sgnn# --- Main Program, DTrace ---
122235368Sgnn#
123235368Sgnn/usr/sbin/dtrace -n '
124235368Sgnn /*
125235368Sgnn  * Command line arguments
126235368Sgnn  */
127235368Sgnn inline int OPT_time 	= '$opt_time';
128235368Sgnn inline int OPT_device 	= '$opt_device';
129235368Sgnn inline int OPT_mount 	= '$opt_mount';
130235368Sgnn inline int OPT_file 	= '$opt_file';
131235368Sgnn inline int INTERVAL 	= '$interval';
132235368Sgnn inline int COUNTER 	= '$count';
133235368Sgnn inline int FILTER 	= '$filter';
134235368Sgnn inline string DEVICE 	= "'$device'";
135235368Sgnn inline string FILENAME = "'$filename'";
136235368Sgnn inline string MOUNT 	= "'$mount'";
137235368Sgnn 
138235368Sgnn #pragma D option quiet
139235368Sgnn
140235368Sgnn int last_loc[string];
141235368Sgnn
142235368Sgnn /*
143235368Sgnn  * Program start
144235368Sgnn  */
145235368Sgnn dtrace:::BEGIN 
146235368Sgnn {
147235368Sgnn        /* starting values */
148235368Sgnn	diskcnt = 0;
149235368Sgnn	diskmin = 0;
150235368Sgnn	diskmax = 0;
151235368Sgnn	diskran = 0;
152235368Sgnn	diskr = 0;
153235368Sgnn	diskw = 0;
154235368Sgnn        counts = COUNTER;
155235368Sgnn        secs = INTERVAL;
156235368Sgnn	LINES = 20;
157235368Sgnn	line = 0;
158235368Sgnn	last_event[""] = 0;
159235368Sgnn }
160235368Sgnn
161235368Sgnn /*
162235368Sgnn  * Print header
163235368Sgnn  */
164235368Sgnn profile:::tick-1sec
165235368Sgnn /line <= 0 /
166235368Sgnn {
167235368Sgnn	/* print optional headers */
168235368Sgnn	OPT_time   ? printf("%-20s ", "TIME")  : 1;
169235368Sgnn	OPT_device ? printf("%-9s ", "DEVICE") : 1;
170235368Sgnn	OPT_mount  ? printf("%-12s ", "MOUNT") : 1;
171235368Sgnn	OPT_file   ? printf("%-12s ", "FILE") : 1;
172235368Sgnn
173235368Sgnn	/* print header */
174235368Sgnn	printf("%4s %4s %6s %6s %6s %6s %6s %6s\n",
175235368Sgnn	    "%RAN", "%SEQ", "COUNT", "MIN", "MAX", "AVG", "KR", "KW");
176235368Sgnn
177235368Sgnn	line = LINES;
178235368Sgnn }
179235368Sgnn
180235368Sgnn /*
181235368Sgnn  * Check event is being traced
182235368Sgnn  */
183235368Sgnn io:genunix::done
184235368Sgnn { 
185235368Sgnn	/* default is to trace unless filtering */
186235368Sgnn	self->ok = FILTER ? 0 : 1;
187235368Sgnn
188235368Sgnn	/* check each filter */
189235368Sgnn	(OPT_device == 1 && DEVICE == args[1]->dev_statname)? self->ok = 1 : 1;
190235368Sgnn	(OPT_file == 1 && FILENAME == args[2]->fi_pathname) ? self->ok = 1 : 1;
191235368Sgnn	(OPT_mount == 1 && MOUNT == args[2]->fi_mount)  ? self->ok = 1 : 1;
192235368Sgnn }
193235368Sgnn
194235368Sgnn /*
195235368Sgnn  * Process and Print completion
196235368Sgnn  */
197235368Sgnn io:genunix::done
198235368Sgnn /self->ok/
199235368Sgnn {
200235368Sgnn	/*
201235368Sgnn	 * Save details
202235368Sgnn	 */
203235368Sgnn	this->loc = args[0]->b_blkno * 512;
204235368Sgnn	this->pre = last_loc[args[1]->dev_statname];
205235368Sgnn	diskr += args[0]->b_flags & B_READ ? args[0]->b_bcount : 0;
206235368Sgnn	diskw += args[0]->b_flags & B_READ ? 0 : args[0]->b_bcount;
207235368Sgnn	diskran += this->pre == this->loc ? 0 : 1;
208235368Sgnn	diskcnt++;
209235368Sgnn	diskmin = diskmin == 0 ? args[0]->b_bcount :
210235368Sgnn	    (diskmin > args[0]->b_bcount ? args[0]->b_bcount : diskmin);
211235368Sgnn	diskmax = diskmax < args[0]->b_bcount ? args[0]->b_bcount : diskmax;
212235368Sgnn
213235368Sgnn	/* save disk location */
214235368Sgnn	last_loc[args[1]->dev_statname] = this->loc + args[0]->b_bcount;
215235368Sgnn
216235368Sgnn	/* cleanup */
217235368Sgnn	self->ok = 0;
218235368Sgnn }
219235368Sgnn
220235368Sgnn /*
221235368Sgnn  * Timer
222235368Sgnn  */
223235368Sgnn profile:::tick-1sec
224235368Sgnn {
225235368Sgnn	secs--;
226235368Sgnn }
227235368Sgnn
228235368Sgnn /*
229235368Sgnn  * Print Output
230235368Sgnn  */
231235368Sgnn profile:::tick-1sec
232235368Sgnn /secs == 0/
233235368Sgnn {
234235368Sgnn	/* calculate diskavg */
235235368Sgnn	diskavg = diskcnt > 0 ? (diskr + diskw) / diskcnt : 0;
236235368Sgnn
237235368Sgnn	/* convert counters to Kbytes */
238235368Sgnn	diskr /= 1024;
239235368Sgnn	diskw /= 1024;
240235368Sgnn
241235368Sgnn	/* convert to percentages */
242235368Sgnn	diskran = diskcnt == 0 ? 0 : (diskran * 100) / diskcnt;
243235368Sgnn	diskseq = diskcnt == 0 ? 0 : 100 - diskran;
244235368Sgnn
245235368Sgnn	/* print optional fields */
246235368Sgnn	OPT_time   ? printf("%-20Y ", walltimestamp) : 1;
247235368Sgnn	OPT_device ? printf("%-9s ", DEVICE) : 1;
248235368Sgnn	OPT_mount  ? printf("%-12s ", MOUNT) : 1;
249235368Sgnn	OPT_file   ? printf("%-12s ", FILENAME) : 1;
250235368Sgnn
251235368Sgnn	/* print data */
252235368Sgnn	printf("%4d %4d %6d %6d %6d %6d %6d %6d\n",
253235368Sgnn	    diskran, diskseq, diskcnt, diskmin, diskmax, diskavg,
254235368Sgnn	    diskr, diskw);
255235368Sgnn
256235368Sgnn	/* clear data */
257235368Sgnn	diskmin = 0;
258235368Sgnn	diskmax = 0;
259235368Sgnn	diskcnt = 0;
260235368Sgnn	diskran = 0;
261235368Sgnn	diskr = 0;
262235368Sgnn	diskw = 0;
263235368Sgnn
264235368Sgnn	secs = INTERVAL;
265235368Sgnn	counts--;
266235368Sgnn	line--;
267235368Sgnn }
268235368Sgnn
269235368Sgnn /*
270235368Sgnn  * End of program
271235368Sgnn  */
272235368Sgnn profile:::tick-1sec
273235368Sgnn /counts == 0/
274235368Sgnn {
275235368Sgnn	exit(0);
276235368Sgnn }
277235368Sgnn'
278