305 lines
8.6 KiB
Bash
Executable File
305 lines
8.6 KiB
Bash
Executable File
#!/usr/bin/sh
|
|
#
|
|
# vopstat - Trace the vnode interface.
|
|
# Written using DTrace (Solaris 10 3/05)
|
|
#
|
|
# Author: Richard McDougall
|
|
#
|
|
# $Id: vopstat 3 2007-08-01 10:50:08Z brendan $
|
|
#
|
|
# USAGE: vopstat [-t] [/mountname]
|
|
#
|
|
# vopstat # default output, summary each 5 secs
|
|
# -t # trace activity as it occurs
|
|
#
|
|
# Example:
|
|
#
|
|
# ./vopstat
|
|
#
|
|
# VOP Physical IO Count
|
|
# fop_fsync 236
|
|
#
|
|
# VOP Count Count
|
|
# fop_create 1
|
|
# fop_fid 1
|
|
# fop_lookup 2
|
|
# fop_access 3
|
|
# fop_read 3
|
|
# fop_poll 11
|
|
# fop_fsync 31
|
|
# fop_putpage 32
|
|
# fop_ioctl 115
|
|
# fop_write 517
|
|
# fop_rwlock 520
|
|
# fop_rwunlock 520
|
|
# fop_inactive 529
|
|
# fop_getattr 1057
|
|
#
|
|
# VOP Wall Time mSeconds
|
|
# fop_fid 0
|
|
# fop_access 0
|
|
# fop_read 0
|
|
# fop_poll 0
|
|
# fop_lookup 0
|
|
# fop_create 0
|
|
# fop_ioctl 0
|
|
# fop_putpage 1
|
|
# fop_rwunlock 1
|
|
# fop_rwlock 1
|
|
# fop_inactive 1
|
|
# fop_getattr 2
|
|
# fop_write 22
|
|
# fop_fsync 504
|
|
#
|
|
# COPYRIGHT: Copyright (c) 2006 Richard McDougall
|
|
#
|
|
# 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
|
|
#
|
|
# Shell Wrapper Concept by Brendan Gregg
|
|
#
|
|
# 08-Jan-2006 Richard McDougall Created this.
|
|
# 23-Apr-2006 Brendan Gregg Minor style tweaks.
|
|
# 23-Apr-2006 " " Last update.
|
|
|
|
|
|
##############################
|
|
# --- Process Arguments ---
|
|
#
|
|
|
|
### default variables
|
|
opt_trace=0; opt_fs=0; opt_stats=1; opt_all=0
|
|
|
|
### process options
|
|
while getopts t name
|
|
do
|
|
case $name in
|
|
t) opt_trace=1 ;;
|
|
h|?) cat <<-END >&2
|
|
USAGE: voptrace [-t] [/mountpoint]
|
|
voptrace # default output
|
|
-t # trace
|
|
eg,
|
|
voptrace -t # trace all file systems
|
|
voptrace -t /tmp # trace /tmp
|
|
voptrace /tmp # summary stats for /tmp
|
|
END
|
|
exit 1
|
|
esac
|
|
done
|
|
shift `expr $OPTIND - 1`
|
|
filesys="$1"
|
|
|
|
### option logic
|
|
if [ $opt_trace -eq 1 ]; then
|
|
opt_stats=0
|
|
fi
|
|
if [ -z "$filesys" ]; then
|
|
opt_all=1
|
|
fi
|
|
|
|
#################################
|
|
# --- Main Program, DTrace ---
|
|
#
|
|
/usr/sbin/dtrace -n '
|
|
/*
|
|
* Command line arguments
|
|
*/
|
|
inline int OPT_fs = '$opt_fs';
|
|
inline int OPT_all = '$opt_all';
|
|
inline int OPT_trace = '$opt_trace';
|
|
inline int OPT_stats = '$opt_stats';
|
|
inline string FILESYS = "'$filesys'";
|
|
|
|
#pragma D option quiet
|
|
|
|
/*
|
|
* Print header
|
|
*/
|
|
dtrace:::BEGIN
|
|
{
|
|
last_event[""] = 0;
|
|
|
|
/* print main headers */
|
|
OPT_stats == 1 ?
|
|
printf("\033[H\033[2J") : 1;
|
|
|
|
OPT_trace == 1 ?
|
|
printf("%2s %-15s %-10s %51s %2s %8s %8s\n",
|
|
"", "Event", "Device", "Path", "RW", "Size", "Offset") : 1;
|
|
self->path = "";
|
|
self->trace = 0;
|
|
}
|
|
|
|
dtrace:::BEGIN
|
|
/OPT_trace == 1/
|
|
{
|
|
/* make D compiler happy */
|
|
@vop_iocnt[""] = count();
|
|
@vop_cnt[""] = count();
|
|
@vop_time[""] = sum(0);
|
|
trunc(@vop_iocnt);
|
|
trunc(@vop_cnt);
|
|
trunc(@vop_time);
|
|
}
|
|
|
|
fbt::fop_*:entry
|
|
{
|
|
self->trace = 0;
|
|
|
|
/* Get vp: fop_open has a pointer to vp */
|
|
this->vpp = (vnode_t **)arg0;
|
|
self->vp = (vnode_t *)arg0;
|
|
self->vp = probefunc == "fop_open" ? (vnode_t *)*this->vpp : self->vp;
|
|
|
|
/* And the containing vfs */
|
|
this->vfsp = self->vp ? self->vp->v_vfsp : 0;
|
|
|
|
/* And the paths for the vp and containing vfs */
|
|
this->vfsvp = this->vfsp ?
|
|
(struct vnode *)((vfs_t *)this->vfsp)->vfs_vnodecovered : 0;
|
|
self->vfspath = this->vfsvp ? stringof(this->vfsvp->v_path) : "unknown";
|
|
|
|
/* Check if we should trace the root fs */
|
|
(OPT_all ||
|
|
(FILESYS == "/" && this->vfsp &&
|
|
(this->vfsp == `rootvfs))) ? self->trace = 1 : self->trace;
|
|
|
|
/* Check if we should trace the fs */
|
|
(OPT_all || (self->vfspath == FILESYS)) ? self->trace = 1 : self->trace;
|
|
|
|
self->vfspath = 0;
|
|
}
|
|
|
|
/*
|
|
* Trace the entry point to each fop
|
|
*/
|
|
fbt::fop_*:entry
|
|
/self->trace/
|
|
{
|
|
self->path = (self->vp != NULL && self->vp->v_path) ?
|
|
stringof(self->vp->v_path) : "unknown";
|
|
|
|
/* Some fops has the len in arg2 */
|
|
(probefunc == "fop_getpage" ||
|
|
probefunc == "fop_putpage" ||
|
|
probefunc == "fop_none") ? self->len = arg2 : 1;
|
|
|
|
/* Some fops has the len in arg3 */
|
|
(probefunc == "fop_pageio" ||
|
|
probefunc == "fop_none") ? self->len = arg3 : 1;
|
|
|
|
/* Some fops has the len in arg4 */
|
|
(probefunc == "fop_addmap" ||
|
|
probefunc == "fop_map" ||
|
|
probefunc == "fop_delmap") ? self->len = arg4 : 1;
|
|
|
|
/* Some fops has the offset in arg1 */
|
|
(probefunc == "fop_addmap" ||
|
|
probefunc == "fop_map" ||
|
|
probefunc == "fop_getpage" ||
|
|
probefunc == "fop_putpage" ||
|
|
probefunc == "fop_seek" ||
|
|
probefunc == "fop_delmap") ? self->off = arg1 : 1;
|
|
|
|
/* Some fops has the offset in arg3 */
|
|
(probefunc == "fop_close" ||
|
|
probefunc == "fop_pageio") ? self->off = arg3 : 1;
|
|
|
|
/* Some fops has the offset in arg4 */
|
|
probefunc == "fop_frlock" ? self->off = arg4 : 1;
|
|
|
|
/* Some fops has the pathname in arg1 */
|
|
self->path = (probefunc == "fop_create" ||
|
|
probefunc == "fop_mkdir" ||
|
|
probefunc == "fop_rmdir" ||
|
|
probefunc == "fop_remove" ||
|
|
probefunc == "fop_lookup") ?
|
|
strjoin(self->path, strjoin("/", stringof(arg1))) : self->path;
|
|
|
|
OPT_trace ?
|
|
printf("%2s %-15s %-10s %51s %2s %8d %8d\n",
|
|
"->", probefunc, "-", self->path, "-",
|
|
self->len, self->off) : 1;
|
|
|
|
self->type = probefunc;
|
|
self->vop_entry[probefunc] = timestamp;
|
|
}
|
|
|
|
fbt::fop_*:return
|
|
/self->trace == 1/
|
|
{
|
|
OPT_trace ?
|
|
printf("%2s %-15s %-10s %51s %2s %8d %8d\n",
|
|
"<-", probefunc, "-", self->path, "-",
|
|
self->len, self->off) : 1;
|
|
|
|
OPT_stats == 1 ?
|
|
@vop_time[probefunc] =
|
|
sum(timestamp - self->vop_entry[probefunc]) : 1;
|
|
OPT_stats == 1 ?
|
|
@vop_cnt[probefunc] = count() : 1;
|
|
|
|
self->path = 0;
|
|
self->len = 0;
|
|
self->off = 0;
|
|
}
|
|
|
|
fbt::fop_*:return
|
|
{
|
|
self->trace = 0;
|
|
self->type = 0;
|
|
self->vp = 0;
|
|
}
|
|
|
|
/* Capture any I/O within this fop */
|
|
io:::start
|
|
/self->trace/
|
|
{
|
|
OPT_stats == 1 ?
|
|
@vop_iocnt[self->type] = count() : 1;
|
|
|
|
OPT_trace == 1?
|
|
printf("%2s %-15s %-10s %51s %2s %8d %8u\n",
|
|
"--", self->type, args[1]->dev_statname,
|
|
self->path, args[0]->b_flags & B_READ ? "R" : "W",
|
|
args[0]->b_bcount, args[0]->b_blkno) : 1;
|
|
}
|
|
|
|
profile:::tick-5s
|
|
/OPT_stats == 1/
|
|
{
|
|
/* Print top 20 only */
|
|
trunc(@vop_iocnt, 20);
|
|
trunc(@vop_time, 20);
|
|
|
|
/* Display microseconds */
|
|
normalize(@vop_time, 1000000);
|
|
printf("\033[H\033[2J");
|
|
printf("%-60s %10s\n", "VOP Physical IO", "Count");
|
|
printa("%-60s %10@d\n", @vop_iocnt);
|
|
printf("\n");
|
|
printf("%-60s %10s\n", "VOP Count", "Count");
|
|
printa("%-60s %10@d\n", @vop_cnt);
|
|
printf("\n");
|
|
printf("%-60s %10s\n", "VOP Wall Time", "mSeconds");
|
|
printa("%-60s %10@d\n", @vop_time);
|
|
|
|
/* Clear data */
|
|
trunc(@vop_iocnt);
|
|
trunc(@vop_cnt);
|
|
trunc(@vop_time);
|
|
}
|
|
'
|