1235368Sgnn#!/usr/bin/sh
2235368Sgnn#
3235368Sgnn# pfilestat
4235368Sgnn#
5235368Sgnn# This prints I/O statistics for each file descriptor within a process.
6235368Sgnn# In particular, the time break down during read() and write() events is
7235368Sgnn# measured.
8235368Sgnn#
9235368Sgnn# $Id: pfilestat 4 2007-08-01 11:01:38Z brendan $
10235368Sgnn#
11235368Sgnn# USAGE:        pfilestat [-r|-w] pid
12235368Sgnn#
13235368Sgnn# FIELDS:
14235368Sgnn#               STATE      microstate: running, sleeping, waitcpu, read, write
15235368Sgnn#               FDUM       File Descriptor ID
16235368Sgnn#               Time       Percentage of wallclock time in each STATE
17235368Sgnn#               File       Name of file, if known
18235368Sgnn# 
19235368Sgnn# COPYRIGHT: Copyright (c) 2006 Richard McDougall.
20235368Sgnn#
21235368Sgnn# CDDL HEADER START
22235368Sgnn#
23235368Sgnn#  The contents of this file are subject to the terms of the
24235368Sgnn#  Common Development and Distribution License, Version 1.0 only
25235368Sgnn#  (the "License").  You may not use this file except in compliance
26235368Sgnn#  with the License.
27235368Sgnn#
28235368Sgnn#  You can obtain a copy of the license at Docs/cddl1.txt
29235368Sgnn#  or http://www.opensolaris.org/os/licensing.
30235368Sgnn#  See the License for the specific language governing permissions
31235368Sgnn#  and limitations under the License.
32235368Sgnn#
33235368Sgnn# CDDL HEADER END
34235368Sgnn#
35235368Sgnn# ToDo:
36235368Sgnn#	Trace readv() and writev().
37235368Sgnn#
38235368Sgnn# 20-Feb-2006   Richard McDougall	created this.
39235368Sgnn# 24-Feb-2006   Brendan Gregg		tweaked code.
40235368Sgnn# 20-Mar-2006      "      " 		tweaked code.
41235368Sgnn# 20-Mar-2006      "      " 		last update.
42235368Sgnn
43235368Sgnn
44235368Sgnn##############################
45235368Sgnn# --- Process Arguments ---
46235368Sgnn#
47235368Sgnn
48235368Sgnn### Default variables
49235368Sgnnopt_read=0; opt_write=0
50235368Sgnn
51235368Sgnn### Process options
52235368Sgnnwhile getopts hrw name
53235368Sgnndo
54235368Sgnn	case $name in
55235368Sgnn	r)      opt_read=1 ;;
56235368Sgnn	w)      opt_write=1 ;;
57235368Sgnn	h|?)    cat <<-END >&2
58235368Sgnn		USAGE: pfilestat [-r|-w] pid
59235368Sgnn		              -r           # reads only
60235368Sgnn		              -w           # writes only
61235368Sgnn		  eg,
62235368Sgnn		       pfilestat pid       # default, r+w counts
63235368Sgnn		       pfilestat -r pid    # read count only
64235368Sgnn		END
65235368Sgnn		exit 1
66235368Sgnn	esac
67235368Sgnndone
68235368Sgnnshift `expr $OPTIND - 1`
69235368Sgnn
70235368SgnnPID=$1
71235368Sgnnclearstr=`clear`
72235368Sgnn
73235368Sgnnif [ -z "$PID" ]
74235368Sgnnthen
75235368Sgnn	echo "Must supply pid"
76235368Sgnn	exit 1
77235368Sgnnfi
78235368Sgnn
79235368Sgnn### Option logic
80235368Sgnnif [ $opt_read -eq 0 -a $opt_write -eq 0 ]; then
81235368Sgnn	opt_read=1; opt_write=1
82235368Sgnnfi
83235368Sgnn
84235368Sgnn
85235368Sgnn#################################
86235368Sgnn# --- Main Program, DTrace ---
87235368Sgnn#
88235368Sgnn/usr/sbin/dtrace -n '
89235368Sgnn #pragma D option quiet
90235368Sgnn
91235368Sgnn inline string CLEAR   = "'$clearstr'";
92235368Sgnn inline int OPT_read   = '$opt_read';
93235368Sgnn inline int OPT_write  = '$opt_write';
94235368Sgnn inline int PID        = '$PID';
95235368Sgnn
96235368Sgnn unsigned long long totaltime;
97235368Sgnn unsigned long long totalbytes;
98235368Sgnn
99235368Sgnn enum runstate {
100235368Sgnn	READ,
101235368Sgnn	WRITE,
102235368Sgnn	OTHER
103235368Sgnn };
104235368Sgnn
105235368Sgnn /* print header */
106235368Sgnn dtrace:::BEGIN
107235368Sgnn {
108235368Sgnn	printf("Tracing ");
109235368Sgnn	OPT_read && OPT_write ? printf("reads and writes") : 1;
110235368Sgnn	OPT_read && ! OPT_write ? printf("reads") : 1;
111235368Sgnn	! OPT_read && OPT_write ? printf("writes") : 1;
112235368Sgnn	printf("...");
113235368Sgnn	totaltime = 0;
114235368Sgnn	totalbytes = 0;
115235368Sgnn	last = timestamp;
116235368Sgnn	stamp = timestamp;
117235368Sgnn }
118235368Sgnn
119235368Sgnn /* sample reads */
120235368Sgnn syscall::read:entry,
121235368Sgnn syscall::pread*:entry
122235368Sgnn /pid == PID && OPT_read/
123235368Sgnn {
124235368Sgnn	runstate = READ;
125235368Sgnn	@logical["running", (uint64_t)0, ""] = sum(timestamp - last);
126235368Sgnn	totaltime += timestamp - last;
127235368Sgnn	last = timestamp;
128235368Sgnn
129235368Sgnn	self->fd = arg0 + 1;
130235368Sgnn }
131235368Sgnn
132235368Sgnn fbt::fop_read:entry,
133235368Sgnn fbt::fop_write:entry
134235368Sgnn /self->fd/
135235368Sgnn {
136235368Sgnn	self->path = args[0]->v_path == 0 ? "<none>" : 
137235368Sgnn	    cleanpath(args[0]->v_path);
138235368Sgnn }
139235368Sgnn
140235368Sgnn syscall::read:return,
141235368Sgnn syscall::pread*:return
142235368Sgnn /pid == PID && OPT_read/
143235368Sgnn {
144235368Sgnn	runstate = OTHER;
145235368Sgnn	this->bytes = (int)arg0 > 0 ? (int)arg0 : 0;
146235368Sgnn	@logical["read", self->fd - 1, self->path] = sum(timestamp - last);
147235368Sgnn	@bytes["read", self->fd - 1, self->path] = sum(this->bytes);
148235368Sgnn	totalbytes += this->bytes;
149235368Sgnn	totaltime += timestamp - last;
150235368Sgnn	last = timestamp;
151235368Sgnn	self->path = 0;
152235368Sgnn	self->fd = 0;
153235368Sgnn }
154235368Sgnn
155235368Sgnn
156235368Sgnn /* sample writes */
157235368Sgnn syscall::write:entry,
158235368Sgnn syscall::pwrite*:entry
159235368Sgnn /pid == PID && OPT_write/
160235368Sgnn {
161235368Sgnn	runstate = WRITE;
162235368Sgnn	@logical["running", (uint64_t)0, ""] = sum(timestamp - last);
163235368Sgnn	totaltime += timestamp - last;
164235368Sgnn	last = timestamp;
165235368Sgnn
166235368Sgnn	self->fd = (int)arg0 + 1;
167235368Sgnn }
168235368Sgnn
169235368Sgnn syscall::write:return,
170235368Sgnn syscall::pwrite*:return
171235368Sgnn /pid == PID && OPT_write/
172235368Sgnn {
173235368Sgnn	runstate = OTHER;
174235368Sgnn	this->bytes = (int)arg0 > 0 ? (int)arg0 : 0;
175235368Sgnn	@logical["write", self->fd - 1, self->path] = sum(timestamp - last);
176235368Sgnn	@bytes["write", self->fd - 1, self->path] = sum(this->bytes);
177235368Sgnn	totalbytes += this->bytes;
178235368Sgnn	totaltime += timestamp - last;
179235368Sgnn	last = timestamp;
180235368Sgnn	self->path = 0;
181235368Sgnn	self->fd = 0;
182235368Sgnn }
183235368Sgnn
184235368Sgnn sched:::on-cpu
185235368Sgnn /pid == PID/
186235368Sgnn {
187235368Sgnn	@logical["waitcpu", (uint64_t)0, ""] = sum(timestamp - last);
188235368Sgnn	totaltime += timestamp - last;
189235368Sgnn	last = timestamp;
190235368Sgnn }
191235368Sgnn
192235368Sgnn
193235368Sgnn sched:::off-cpu
194235368Sgnn /pid == PID/
195235368Sgnn {
196235368Sgnn	@logical["running", (uint64_t)0, ""] = sum(timestamp - last);
197235368Sgnn	totaltime += timestamp - last;
198235368Sgnn	last = timestamp;
199235368Sgnn }
200235368Sgnn
201235368Sgnn sched:::sleep
202235368Sgnn /pid == PID/
203235368Sgnn {
204235368Sgnn	@logical["running", (uint64_t)0, ""] = sum(timestamp - last);
205235368Sgnn	totaltime += timestamp - last;
206235368Sgnn	last = timestamp;
207235368Sgnn }
208235368Sgnn
209235368Sgnn sched:::wakeup
210235368Sgnn /args[1]->pr_pid == PID && runstate == OTHER/
211235368Sgnn {
212235368Sgnn	@logical["sleep", (uint64_t)0, ""] = sum(timestamp - last);
213235368Sgnn	totaltime += timestamp - last;
214235368Sgnn	last = timestamp;
215235368Sgnn }
216235368Sgnn
217235368Sgnn sched:::wakeup
218235368Sgnn /args[1]->pr_pid == PID && runstate == READ/
219235368Sgnn {
220235368Sgnn	@logical["sleep-r", (uint64_t)0, ""] = sum(timestamp - last);
221235368Sgnn	totaltime += timestamp - last;
222235368Sgnn	last = timestamp;
223235368Sgnn }
224235368Sgnn
225235368Sgnn sched:::wakeup
226235368Sgnn /args[1]->pr_pid == PID && runstate == WRITE/
227235368Sgnn {
228235368Sgnn	@logical["sleep-w", (uint64_t)0, ""] = sum(timestamp - last);
229235368Sgnn	totaltime += timestamp - last;
230235368Sgnn	last = timestamp;
231235368Sgnn }
232235368Sgnn
233235368Sgnn sched:::enqueue
234235368Sgnn /args[1]->pr_pid == PID/
235235368Sgnn {
236235368Sgnn	@logical["waitcpu", (uint64_t)0, ""] = sum(timestamp - last);
237235368Sgnn	totaltime += timestamp - last;
238235368Sgnn	last = timestamp;
239235368Sgnn }
240235368Sgnn
241235368Sgnn sched:::dequeue
242235368Sgnn /args[1]->pr_pid == PID/
243235368Sgnn {
244235368Sgnn	@logical["waitcpu", (uint64_t)0, ""] = sum(timestamp - last);
245235368Sgnn	totaltime += timestamp - last;
246235368Sgnn	last = timestamp;
247235368Sgnn }
248235368Sgnn
249235368Sgnn /* print report */
250235368Sgnn profile:::tick-5s
251235368Sgnn {
252235368Sgnn	printf("%s", CLEAR);
253235368Sgnn	normalize(@logical, totaltime / 100);
254235368Sgnn	trunc(@logical, 10);
255235368Sgnn	printf("%10s %7s %9s %-44s\n", "STATE", "FDNUM", "Time", "Filename");
256235368Sgnn	printa("%10s %7d %@8d%% %-44.44s\n", @logical);
257235368Sgnn	trunc(@logical);
258235368Sgnn
259235368Sgnn	delta = timestamp - stamp;
260235368Sgnn	stamp = timestamp;
261235368Sgnn	normalize(@bytes, (1024 * delta) / 1000000000);
262235368Sgnn	trunc(@bytes, 10);
263235368Sgnn	printf("\n%10s %7s %9s %-44s\n", "STATE", "FDNUM", "KB/s",
264235368Sgnn	    "Filename");
265235368Sgnn	printa("%10s %7d %@9d %-44.44s\n", @bytes);
266235368Sgnn	trunc(@bytes);
267235368Sgnn
268235368Sgnn	printf("\nTotal event time (ms): %d   Total Mbytes/sec: %d\n",
269235368Sgnn	    totaltime / 1000000,
270235368Sgnn	    (totalbytes * 1000000000) / (delta * 1048576));
271235368Sgnn
272235368Sgnn	totaltime = 0;
273235368Sgnn	totalbytes = 0;
274235368Sgnn	last = timestamp;
275235368Sgnn }
276235368Sgnn
277235368Sgnn dtrace:::END
278235368Sgnn {
279235368Sgnn	trunc(@logical);
280235368Sgnn	trunc(@bytes);
281235368Sgnn }
282235368Sgnn'
283