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