1235368Sgnn#!/usr/bin/ksh
2235368Sgnn#
3235368Sgnn# rwsnoop - snoop read/write events.
4235368Sgnn#           Written using DTrace (Solaris 10 3/05).
5235368Sgnn#
6235368Sgnn# This is measuring reads and writes at the application level. This matches
7235368Sgnn# the syscalls read, write, pread and pwrite.
8235368Sgnn#
9235368Sgnn# $Id: rwsnoop 3 2007-08-01 10:50:08Z brendan $
10235368Sgnn#
11235368Sgnn# USAGE:	rwsnoop [-jPtvZ] [-n name] [-p pid]
12235368Sgnn# 
13235368Sgnn#		rwsnoop		# default output
14235368Sgnn#
15235368Sgnn#		-j		# print project ID
16235368Sgnn#		-P		# print parent process ID
17235368Sgnn#		-t		# print timestamp, us
18235368Sgnn#		-v		# print time, string
19235368Sgnn#		-Z		# print zone ID
20235368Sgnn#		-n name		# this process name only
21235368Sgnn#		-p PID		# this PID only
22235368Sgnn#	eg,
23235368Sgnn#		rwsnoop -Z		# print zone ID
24235368Sgnn#		rwsnoop -n bash 	# monitor processes named "bash"
25235368Sgnn#		rwsnoop > out.txt	# recommended
26235368Sgnn#
27235368Sgnn# NOTE:
28235368Sgnn# 	rwsnoop usually prints plenty of output, which itself will cause
29235368Sgnn#	more output. It can be better to redirect the output of rwsnoop
30235368Sgnn#	to a file to prevent this.
31235368Sgnn#
32235368Sgnn# FIELDS:
33235368Sgnn#		TIME		Timestamp, us
34235368Sgnn#		TIMESTR		Time, string
35235368Sgnn#		ZONE		Zone ID
36235368Sgnn#		PROJ		Project ID
37235368Sgnn#		UID		User ID
38235368Sgnn#		PID		Process ID
39235368Sgnn#		PPID		Parent Process ID
40235368Sgnn#		CMD		Process name
41235368Sgnn#		D		Direction, Read or Write
42235368Sgnn#		BYTES		Total bytes during sample, -1 for error
43235368Sgnn#		FILE		Filename, if file based
44235368Sgnn#
45235368Sgnn# Reads and writes that are not file based, for example with sockets, will
46235368Sgnn# print "<unknown>" as the filename.
47235368Sgnn#
48235368Sgnn# SEE ALSO:	rwtop
49235368Sgnn#
50235368Sgnn# COPYRIGHT: Copyright (c) 2005 Brendan Gregg.
51235368Sgnn#
52235368Sgnn# CDDL HEADER START
53235368Sgnn#
54235368Sgnn#  The contents of this file are subject to the terms of the
55235368Sgnn#  Common Development and Distribution License, Version 1.0 only
56235368Sgnn#  (the "License").  You may not use this file except in compliance
57235368Sgnn#  with the License.
58235368Sgnn#
59235368Sgnn#  You can obtain a copy of the license at Docs/cddl1.txt
60235368Sgnn#  or http://www.opensolaris.org/os/licensing.
61235368Sgnn#  See the License for the specific language governing permissions
62235368Sgnn#  and limitations under the License.
63235368Sgnn#
64235368Sgnn# CDDL HEADER END
65235368Sgnn#
66235368Sgnn# TODO:
67235368Sgnn#  Track readv and writev.
68235368Sgnn#
69235368Sgnn# Author: Brendan Gregg  [Sydney, Australia]
70235368Sgnn#
71235368Sgnn# 24-Jul-2005   Brendan Gregg   Created this.
72235368Sgnn# 17-Sep-2005	   "      "	Increased switchrate.
73235368Sgnn# 17-Sep-2005	   "      "	Last update.
74235368Sgnn#
75235368Sgnn
76235368Sgnn
77235368Sgnn##############################
78235368Sgnn# --- Process Arguments ---
79235368Sgnn#
80235368Sgnn
81235368Sgnn### default variables
82235368Sgnnopt_name=0; opt_pid=0; opt_proj=0; opt_zone=0; opt_time=0; opt_timestr=0
83235368Sgnnopt_bytes=1; filter=0; pname=.; pid=0; opt_ppid=0
84235368Sgnn
85235368Sgnn### process options
86235368Sgnnwhile getopts n:Pp:jtvZ name
87235368Sgnndo
88235368Sgnn	case $name in
89235368Sgnn	n)	opt_name=1; pname=$OPTARG ;;
90235368Sgnn	p)	opt_pid=1; pid=$OPTARG ;;
91235368Sgnn	P)	opt_ppid=1 ;;
92235368Sgnn	j)	opt_proj=1 ;;
93235368Sgnn	t)	opt_time=1 ;;
94235368Sgnn	v)	opt_timestr=1 ;;
95235368Sgnn	Z)	opt_zone=1 ;;
96235368Sgnn	h|?)	cat <<-END >&2
97235368Sgnn		USAGE: rwsnoop [-jPtvZ] [-n name] [-p pid]
98235368Sgnn 
99235368Sgnn		                -j       # print project ID
100235368Sgnn		                -P       # print parent process ID
101235368Sgnn		                -t       # print timestamp, us
102235368Sgnn		                -v       # print time, string
103235368Sgnn		                -Z       # print zone ID
104235368Sgnn		                -n name  # this process name only
105235368Sgnn		                -p PID   # this PID only
106235368Sgnn		   eg,
107235368Sgnn		        rwsnoop          # default output
108235368Sgnn		        rwsnoop -Z       # print zone ID
109235368Sgnn		        rwsnoop -n bash  # monitor processes named "bash"
110235368Sgnn		END
111235368Sgnn		exit 1
112235368Sgnn	esac
113235368Sgnndone
114235368Sgnn
115235368Sgnnshift $(( $OPTIND - 1 ))
116235368Sgnn
117235368Sgnn### option logic
118235368Sgnnif (( opt_name || opt_pid )); then
119235368Sgnn	filter=1
120235368Sgnnfi
121235368Sgnn
122235368Sgnn
123235368Sgnn
124235368Sgnn#################################
125235368Sgnn# --- Main Program, DTrace ---
126235368Sgnn#
127235368Sgnn/usr/sbin/dtrace -n '
128235368Sgnn /*
129235368Sgnn  * Command line arguments
130235368Sgnn  */
131235368Sgnn inline int OPT_proj 	= '$opt_proj';
132235368Sgnn inline int OPT_zone 	= '$opt_zone';
133235368Sgnn inline int OPT_bytes 	= '$opt_bytes';
134235368Sgnn inline int OPT_name 	= '$opt_name';
135235368Sgnn inline int OPT_ppid 	= '$opt_ppid';
136235368Sgnn inline int OPT_pid 	= '$opt_pid';
137235368Sgnn inline int OPT_time 	= '$opt_time';
138235368Sgnn inline int OPT_timestr	= '$opt_timestr';
139235368Sgnn inline int FILTER 	= '$filter';
140235368Sgnn inline int PID		= '$pid';
141235368Sgnn inline string NAME 	= "'$pname'";
142235368Sgnn 
143235368Sgnn #pragma D option quiet
144235368Sgnn #pragma D option switchrate=10hz
145235368Sgnn
146235368Sgnn /*
147235368Sgnn  * Print header
148235368Sgnn  */
149235368Sgnn dtrace:::BEGIN 
150235368Sgnn {
151235368Sgnn	/* print header */
152235368Sgnn	OPT_time    ? printf("%-14s ", "TIME") : 1;
153235368Sgnn	OPT_timestr ? printf("%-20s ", "TIMESTR") : 1;
154235368Sgnn	OPT_proj    ? printf("%5s ", "PROJ") : 1;
155235368Sgnn	OPT_zone    ? printf("%5s ", "ZONE") : 1;
156235368Sgnn	OPT_ppid    ? printf("%6s ", "PPID") : 1;
157235368Sgnn	printf("%5s %6s %-12s %1s %7s %s\n",
158235368Sgnn	    "UID", "PID", "CMD", "D", "BYTES", "FILE");
159235368Sgnn }
160235368Sgnn
161235368Sgnn /*
162235368Sgnn  * Check event is being traced
163235368Sgnn  */
164235368Sgnn syscall::*read:entry,
165235368Sgnn syscall::*write:entry
166235368Sgnn /pid != $pid/
167235368Sgnn { 
168235368Sgnn	/* default is to trace unless filtering, */
169235368Sgnn	self->ok = FILTER ? 0 : 1;
170235368Sgnn
171235368Sgnn	/* check each filter, */
172235368Sgnn	(OPT_name == 1 && NAME == execname)? self->ok = 1 : 1;
173235368Sgnn	(OPT_pid == 1 && PID == pid) ? self->ok = 1 : 1;
174235368Sgnn
175235368Sgnn	/* save file descriptor */
176235368Sgnn	self->fd = self->ok ? arg0 : 0;
177235368Sgnn }
178235368Sgnn
179235368Sgnn /*
180235368Sgnn  * Save read details
181235368Sgnn  */
182235368Sgnn syscall::*read:return
183235368Sgnn /self->ok/
184235368Sgnn {
185235368Sgnn	self->rw = "R";
186235368Sgnn	self->size = arg0;
187235368Sgnn }
188235368Sgnn
189235368Sgnn /*
190235368Sgnn  * Save write details
191235368Sgnn  */
192235368Sgnn syscall::*write:entry
193235368Sgnn /self->ok/
194235368Sgnn {
195235368Sgnn	self->rw = "W";
196235368Sgnn	self->size = arg2;
197235368Sgnn }
198235368Sgnn
199235368Sgnn /*
200235368Sgnn  * Process event
201235368Sgnn  */
202235368Sgnn syscall::*read:return,
203235368Sgnn syscall::*write:entry
204235368Sgnn /self->ok/
205235368Sgnn {
206235368Sgnn	/*
207235368Sgnn	 * Fetch filename
208235368Sgnn	 */
209235368Sgnn	this->filistp = curthread->t_procp->p_user.u_finfo.fi_list;
210235368Sgnn	this->ufentryp = (uf_entry_t *)((uint64_t)this->filistp +
211235368Sgnn	    (uint64_t)self->fd * (uint64_t)sizeof(uf_entry_t));
212235368Sgnn	this->filep = this->ufentryp->uf_file;
213235368Sgnn	this->vnodep = this->filep != 0 ? this->filep->f_vnode : 0;
214235368Sgnn	self->vpath = this->vnodep ? (this->vnodep->v_path != 0 ? 
215235368Sgnn	    cleanpath(this->vnodep->v_path) : "<unknown>") : "<unknown>";
216235368Sgnn
217235368Sgnn	/*
218235368Sgnn	 * Print details
219235368Sgnn	 */
220235368Sgnn	OPT_time    ? printf("%-14d ", timestamp / 1000) : 1;
221235368Sgnn	OPT_timestr ? printf("%-20Y ", walltimestamp) : 1;
222235368Sgnn	OPT_proj    ? printf("%5d ", curpsinfo->pr_projid) : 1;
223235368Sgnn	OPT_zone    ? printf("%5d ", curpsinfo->pr_zoneid) : 1;
224235368Sgnn	OPT_ppid    ? printf("%6d ", ppid) : 1;
225235368Sgnn	printf("%5d %6d %-12.12s %1s %7d %s\n",
226235368Sgnn	    uid, pid, execname, self->rw, (int)self->size, self->vpath);
227235368Sgnn	
228235368Sgnn	self->ok = 0;
229235368Sgnn	self->fd = 0;
230235368Sgnn	self->rw = 0;
231235368Sgnn	self->size = 0;
232235368Sgnn	self->vpath = 0;
233235368Sgnn }
234235368Sgnn'
235