1235368Sgnn#!/usr/bin/ksh
2235368Sgnn#
3235368Sgnn# rwtop - display top read/write bytes by process.
4235368Sgnn#         Written using DTrace (Solaris 10 3/05).
5235368Sgnn#
6235368Sgnn# This is measuring reads and writes at the application level. This matches
7235368Sgnn# read and write system calls.
8235368Sgnn#
9235368Sgnn# $Id: rwtop 3 2007-08-01 10:50:08Z brendan $
10235368Sgnn#
11235368Sgnn# USAGE:	rwtop [-cC] [-j|-Z] [-n name] [-p pid]
12235368Sgnn#		      [-t top] [interval [count]]
13235368Sgnn# 
14235368Sgnn#		rwtop		# default output, 5 second samples
15235368Sgnn#
16235368Sgnn#		-C		# don't clear the screen
17235368Sgnn#		-c		# print counts
18235368Sgnn#		-j		# print project ID
19235368Sgnn#		-Z		# print zone ID
20235368Sgnn#		-n name		# this process name only
21235368Sgnn#		-p PID		# this PID only
22235368Sgnn#		-t top		# print top number only
23235368Sgnn#	eg,
24235368Sgnn#		rwtop 1		# 1 second samples
25235368Sgnn#		rwtop -t 10	# print top 10 only
26235368Sgnn#		rwtop -n bash	# monitor processes named "bash"
27235368Sgnn#		rwtop -C 5 12	# print 12 x 5 second samples
28235368Sgnn#
29235368Sgnn# FIELDS:
30235368Sgnn#		ZONE		Zone ID
31235368Sgnn#		PROJ		Project ID
32235368Sgnn#		UID		User ID
33235368Sgnn#		PID		Process ID
34235368Sgnn#		PPID		Parent Process ID
35235368Sgnn#		CMD		Process name
36235368Sgnn#		D		Direction, Read or Write
37235368Sgnn#		BYTES		Total bytes during sample
38235368Sgnn#		app_r		total reads during sample, Kbytes
39235368Sgnn#		app_w		total writes during sample, Kbytes
40235368Sgnn#
41235368Sgnn# SEE ALSO:	iotop
42235368Sgnn#
43235368Sgnn# INSPIRATION:  top(1) by William LeFebvre
44235368Sgnn#
45235368Sgnn# COPYRIGHT: Copyright (c) 2005, 2006 Brendan Gregg.
46235368Sgnn#
47235368Sgnn# CDDL HEADER START
48235368Sgnn#
49235368Sgnn#  The contents of this file are subject to the terms of the
50235368Sgnn#  Common Development and Distribution License, Version 1.0 only
51235368Sgnn#  (the "License").  You may not use this file except in compliance
52235368Sgnn#  with the License.
53235368Sgnn#
54235368Sgnn#  You can obtain a copy of the license at Docs/cddl1.txt
55235368Sgnn#  or http://www.opensolaris.org/os/licensing.
56235368Sgnn#  See the License for the specific language governing permissions
57235368Sgnn#  and limitations under the License.
58235368Sgnn#
59235368Sgnn# CDDL HEADER END
60235368Sgnn#
61235368Sgnn# Author: Brendan Gregg  [Sydney, Australia]
62235368Sgnn#
63235368Sgnn# 24-Jul-2005   Brendan Gregg   Created this.
64235368Sgnn# 20-Apr-2006	   "      "	Last update.
65235368Sgnn
66235368Sgnn
67235368Sgnn##############################
68235368Sgnn# --- Process Arguments ---
69235368Sgnn#
70235368Sgnn
71235368Sgnn### default variables
72235368Sgnnopt_name=0; opt_pid=0; opt_clear=1; opt_proj=0; opt_zone=0
73235368Sgnnopt_def=1; opt_bytes=1; filter=0; pname=.; pid=0
74235368Sgnnopt_top=0; opt_count=0; interval=5; count=-1; top=0
75235368Sgnn
76235368Sgnn### process options
77235368Sgnnwhile getopts Cchn:p:jt:Z name
78235368Sgnndo
79235368Sgnn	case $name in
80235368Sgnn	C)	opt_clear=0 ;;
81235368Sgnn	c)	opt_count=1; opt_bytes=0 ;;
82235368Sgnn	n)	opt_name=1; pname=$OPTARG ;;
83235368Sgnn	p)	opt_pid=1; pid=$OPTARG ;;
84235368Sgnn	j)	opt_proj=1; opt_def=0 ;;
85235368Sgnn	t)	opt_top=1; top=$OPTARG ;;
86235368Sgnn	Z)	opt_zone=1; opt_def=0 ;;
87235368Sgnn	h|?)	cat <<-END >&2
88235368Sgnn		USAGE: rwtop [-cC] [-j|-Z] [-n name] [-p pid]
89235368Sgnn		             [-t top] [interval [count]]
90235368Sgnn 
91235368Sgnn		                -C        # don't clear the screen
92235368Sgnn		                -c        # print counts
93235368Sgnn		                -j        # print project ID
94235368Sgnn		                -Z        # print zone ID
95235368Sgnn		                -n name   # this process name only
96235368Sgnn		                -p PID    # this PID only
97235368Sgnn		                -t top    # print top number only
98235368Sgnn		   eg,
99235368Sgnn		        rwtop          # default output, 5 second samples
100235368Sgnn		        rwtop 1        # 1 second samples
101235368Sgnn		        rwtop -t 10    # print top 10 only
102235368Sgnn		        rwtop -n bash  # monitor processes named "bash"
103235368Sgnn		        rwtop -C 5 12  # print 12 x 5 second samples
104235368Sgnn		END
105235368Sgnn		exit 1
106235368Sgnn	esac
107235368Sgnndone
108235368Sgnn
109235368Sgnnshift $(( $OPTIND - 1 ))
110235368Sgnn
111235368Sgnn### option logic
112235368Sgnnif [[ "$1" > 0 ]]; then
113235368Sgnn        interval=$1; shift
114235368Sgnnfi
115235368Sgnnif [[ "$1" > 0 ]]; then
116235368Sgnn        count=$1; shift
117235368Sgnnfi
118235368Sgnnif (( opt_proj && opt_zone )); then
119235368Sgnn        opt_proj=0
120235368Sgnnfi
121235368Sgnnif (( opt_name || opt_pid )); then
122235368Sgnn	filter=1
123235368Sgnnfi
124235368Sgnnif (( opt_clear )); then
125235368Sgnn        clearstr=`clear`
126235368Sgnnelse
127235368Sgnn        clearstr=.
128235368Sgnnfi
129235368Sgnn
130235368Sgnn
131235368Sgnn
132235368Sgnn#################################
133235368Sgnn# --- Main Program, DTrace ---
134235368Sgnn#
135235368Sgnn/usr/sbin/dtrace -n '
136235368Sgnn /*
137235368Sgnn  * Command line arguments
138235368Sgnn  */
139235368Sgnn inline int OPT_def 	= '$opt_def';
140235368Sgnn inline int OPT_proj 	= '$opt_proj';
141235368Sgnn inline int OPT_zone 	= '$opt_zone';
142235368Sgnn inline int OPT_clear 	= '$opt_clear';
143235368Sgnn inline int OPT_bytes 	= '$opt_bytes';
144235368Sgnn inline int OPT_count	= '$opt_count';
145235368Sgnn inline int OPT_name 	= '$opt_name';
146235368Sgnn inline int OPT_pid 	= '$opt_pid';
147235368Sgnn inline int OPT_top 	= '$opt_top';
148235368Sgnn inline int INTERVAL 	= '$interval';
149235368Sgnn inline int COUNTER 	= '$count';
150235368Sgnn inline int FILTER 	= '$filter';
151235368Sgnn inline int TOP 	= '$top';
152235368Sgnn inline int PID		= '$pid';
153235368Sgnn inline string NAME 	= "'$pname'";
154235368Sgnn inline string CLEAR 	= "'$clearstr'";
155235368Sgnn 
156235368Sgnn #pragma D option quiet
157235368Sgnn
158235368Sgnn /*
159235368Sgnn  * Print header
160235368Sgnn  */
161235368Sgnn dtrace:::BEGIN 
162235368Sgnn {
163235368Sgnn        /* starting values */
164235368Sgnn        counts = COUNTER;
165235368Sgnn        secs = INTERVAL;
166235368Sgnn        app_r = 0;
167235368Sgnn        app_w = 0;
168235368Sgnn
169235368Sgnn        printf("Tracing... Please wait.\n");
170235368Sgnn }
171235368Sgnn
172235368Sgnn /*
173235368Sgnn  * Check event is being traced
174235368Sgnn  */
175235368Sgnn sysinfo:::readch,
176235368Sgnn sysinfo:::writech
177235368Sgnn /pid != $pid/
178235368Sgnn { 
179235368Sgnn	/* default is to trace unless filtering, */
180235368Sgnn	this->ok = FILTER ? 0 : 1;
181235368Sgnn
182235368Sgnn	/* check each filter, */
183235368Sgnn	(OPT_name == 1 && NAME == execname)? this->ok = 1 : 1;
184235368Sgnn	(OPT_pid == 1 && PID == pid) ? this->ok = 1 : 1;
185235368Sgnn }
186235368Sgnn
187235368Sgnn /*
188235368Sgnn  * Increment tallys
189235368Sgnn  */
190235368Sgnn sysinfo:::readch
191235368Sgnn /this->ok/
192235368Sgnn {
193235368Sgnn	app_r += arg0;
194235368Sgnn }
195235368Sgnn sysinfo:::writech
196235368Sgnn /this->ok/
197235368Sgnn {
198235368Sgnn	app_w += arg0;
199235368Sgnn }
200235368Sgnn
201235368Sgnn /*
202235368Sgnn  * Process event
203235368Sgnn  */
204235368Sgnn sysinfo:::readch,
205235368Sgnn sysinfo:::writech
206235368Sgnn /this->ok/
207235368Sgnn {
208235368Sgnn	/* choose statistic to track */
209235368Sgnn	this->value = OPT_bytes ? arg0 : 1;
210235368Sgnn	
211235368Sgnn	/*
212235368Sgnn	 * Save details
213235368Sgnn	 */
214235368Sgnn	OPT_def ? @out[uid, pid, ppid, execname,
215235368Sgnn	    probename == "readch" ? "R" : "W"] = sum(this->value) : 1;
216235368Sgnn	OPT_proj ? @out[curpsinfo->pr_projid, pid, ppid, execname,
217235368Sgnn	    probename == "readch" ? "R" : "W"] = sum(this->value) : 1;
218235368Sgnn	OPT_zone ? @out[curpsinfo->pr_zoneid, pid, ppid, execname,
219235368Sgnn	    probename == "readch" ? "R" : "W"] = sum(this->value) : 1;
220235368Sgnn
221235368Sgnn	this->ok = 0;
222235368Sgnn }
223235368Sgnn
224235368Sgnn /*
225235368Sgnn  * Timer
226235368Sgnn  */
227235368Sgnn profile:::tick-1sec
228235368Sgnn {
229235368Sgnn	secs--;
230235368Sgnn }
231235368Sgnn
232235368Sgnn /*
233235368Sgnn  * Print Report
234235368Sgnn  */
235235368Sgnn profile:::tick-1sec
236235368Sgnn /secs == 0/
237235368Sgnn {
238235368Sgnn	/* fetch 1 min load average */
239235368Sgnn	this->load1a  = `hp_avenrun[0] / 65536;
240235368Sgnn	this->load1b  = ((`hp_avenrun[0] % 65536) * 100) / 65536;
241235368Sgnn
242235368Sgnn	/* convert counters to Kbytes */
243235368Sgnn	app_r /= 1024;
244235368Sgnn	app_w /= 1024;
245235368Sgnn
246235368Sgnn	/* print status */
247235368Sgnn	OPT_clear ? printf("%s", CLEAR) : 1;
248235368Sgnn	printf("%Y,  load: %d.%02d,  app_r: %6d KB,  app_w: %6d KB\n\n",
249235368Sgnn	    walltimestamp, this->load1a, this->load1b, app_r, app_w);
250235368Sgnn
251235368Sgnn	/* print headers */
252235368Sgnn	OPT_def  ? printf("  UID ") : 1;
253235368Sgnn	OPT_proj ? printf(" PROJ ") : 1;
254235368Sgnn	OPT_zone ? printf(" ZONE ") : 1;
255235368Sgnn	printf("%6s %6s %-16s %1s",
256235368Sgnn	    "PID", "PPID", "CMD", "D");
257235368Sgnn	OPT_bytes ? printf(" %16s\n", "BYTES") : 1;
258235368Sgnn	OPT_count ? printf(" %16s\n", "COUNT") : 1;
259235368Sgnn
260235368Sgnn	/* truncate to top lines if needed */
261235368Sgnn	OPT_top ? trunc(@out, TOP) : 1;
262235368Sgnn
263235368Sgnn	/* print data */
264235368Sgnn	printa("%5d %6d %6d %-16s %1s %16@d\n", @out);
265235368Sgnn	printf("\n");
266235368Sgnn
267235368Sgnn	/* clear data */
268235368Sgnn	trunc(@out);
269235368Sgnn	app_r = 0;
270235368Sgnn	app_w = 0;
271235368Sgnn	secs = INTERVAL;
272235368Sgnn	counts--;
273235368Sgnn }
274235368Sgnn
275235368Sgnn /*
276235368Sgnn  * End of program
277235368Sgnn  */
278235368Sgnn profile:::tick-1sec
279235368Sgnn /counts == 0/
280235368Sgnn {
281235368Sgnn	exit(0);
282235368Sgnn }
283235368Sgnn
284235368Sgnn /*
285235368Sgnn  * Cleanup for Ctrl-C
286235368Sgnn  */
287235368Sgnn dtrace:::END
288235368Sgnn {
289235368Sgnn	trunc(@out);
290235368Sgnn }
291235368Sgnn'
292235368Sgnn
293