1*c29d5175Schristos#!/bin/ksh 2*c29d5175Schristos# 3*c29d5175Schristos# iotop - display top disk I/O events by process. 4*c29d5175Schristos# Written using DTrace (Solaris 10 3/05). 5*c29d5175Schristos# 6*c29d5175Schristos# This is measuring disk events that have made it past system caches. 7*c29d5175Schristos# 8*c29d5175Schristos# $Id: iotop,v 1.1.1.1 2015/09/30 22:01:06 christos Exp $ 9*c29d5175Schristos# 10*c29d5175Schristos# USAGE: iotop [-C] [-D|-o|-P] [-j|-Z] [-d device] [-f filename] 11*c29d5175Schristos# [-m mount_point] [-t top] [interval [count]] 12*c29d5175Schristos# 13*c29d5175Schristos# iotop # default output, 5 second intervals 14*c29d5175Schristos# 15*c29d5175Schristos# -C # don't clear the screen 16*c29d5175Schristos# -D # print delta times, elapsed, us 17*c29d5175Schristos# -j # print project ID 18*c29d5175Schristos# -o # print disk delta times, us 19*c29d5175Schristos# -P # print %I/O (disk delta times) 20*c29d5175Schristos# -Z # print zone ID 21*c29d5175Schristos# -d device # instance name to snoop (eg, dad0) 22*c29d5175Schristos# -f filename # full pathname of file to snoop 23*c29d5175Schristos# -m mount_point # this FS only (will skip raw events) 24*c29d5175Schristos# -t top # print top number only 25*c29d5175Schristos# eg, 26*c29d5175Schristos# iotop 1 # 1 second samples 27*c29d5175Schristos# iotop -C # don't clear the screen 28*c29d5175Schristos# iotop -P # print %I/O (time based) 29*c29d5175Schristos# iotop -j # print project IDs 30*c29d5175Schristos# iotop -Z # print zone IDs 31*c29d5175Schristos# iotop -t 20 # print top 20 lines only 32*c29d5175Schristos# iotop -C 5 12 # print 12 x 5 second samples 33*c29d5175Schristos# 34*c29d5175Schristos# FIELDS: 35*c29d5175Schristos# UID user ID 36*c29d5175Schristos# PID process ID 37*c29d5175Schristos# PPID parent process ID 38*c29d5175Schristos# PROJ project ID 39*c29d5175Schristos# ZONE zone ID 40*c29d5175Schristos# CMD process command name 41*c29d5175Schristos# DEVICE device name 42*c29d5175Schristos# MAJ device major number 43*c29d5175Schristos# MIN device minor number 44*c29d5175Schristos# D direction, Read or Write 45*c29d5175Schristos# BYTES total size of operations, bytes 46*c29d5175Schristos# ELAPSED total elapsed from request to completion, us 47*c29d5175Schristos# DISKTIME total time for disk to complete request, us 48*c29d5175Schristos# %I/O percent disk I/O, based on time (DISKTIME) 49*c29d5175Schristos# load 1 min load average 50*c29d5175Schristos# disk_r total disk read Kbytes for sample 51*c29d5175Schristos# disk_w total disk write Kbytes for sample 52*c29d5175Schristos# 53*c29d5175Schristos# NOTE: 54*c29d5175Schristos# * There are two different delta times reported. -D prints the 55*c29d5175Schristos# elapsed time from the disk request (strategy) to the disk completion 56*c29d5175Schristos# (iodone); -o prints the time for the disk to complete that event 57*c29d5175Schristos# since it's last event (time between iodones), or, the time to the 58*c29d5175Schristos# strategy if the disk had been idle. 59*c29d5175Schristos# * The %I/O value can exceed 100%. It represents how busy a process is 60*c29d5175Schristos# making the disks, in terms of a single disk. A value of 200% could 61*c29d5175Schristos# mean 2 disks are busy at 100%, or 4 disks at 50%... 62*c29d5175Schristos# 63*c29d5175Schristos# SEE ALSO: iosnoop 64*c29d5175Schristos# BigAdmin: DTrace, http://www.sun.com/bigadmin/content/dtrace 65*c29d5175Schristos# Solaris Dynamic Tracing Guide, http://docs.sun.com 66*c29d5175Schristos# DTrace Tools, http://www.brendangregg.com/dtrace.html 67*c29d5175Schristos# 68*c29d5175Schristos# INSPIRATION: top(1) by William LeFebvre 69*c29d5175Schristos# 70*c29d5175Schristos# COPYRIGHT: Copyright (c) 2005, 2006 Brendan Gregg. 71*c29d5175Schristos# 72*c29d5175Schristos# CDDL HEADER START 73*c29d5175Schristos# 74*c29d5175Schristos# The contents of this file are subject to the terms of the 75*c29d5175Schristos# Common Development and Distribution License, Version 1.0 only 76*c29d5175Schristos# (the "License"). You may not use this file except in compliance 77*c29d5175Schristos# with the License. 78*c29d5175Schristos# 79*c29d5175Schristos# You can obtain a copy of the license at Docs/cddl1.txt 80*c29d5175Schristos# or http://www.opensolaris.org/os/licensing. 81*c29d5175Schristos# See the License for the specific language governing permissions 82*c29d5175Schristos# and limitations under the License. 83*c29d5175Schristos# 84*c29d5175Schristos# CDDL HEADER END 85*c29d5175Schristos# 86*c29d5175Schristos# KNOWN BUGS: 87*c29d5175Schristos# - This can print errors while running on servers with Veritas volumes. 88*c29d5175Schristos# 89*c29d5175Schristos# Author: Brendan Gregg [Sydney, Australia] 90*c29d5175Schristos# 91*c29d5175Schristos# 15-Jul-2005 Brendan Gregg Created this. 92*c29d5175Schristos# 20-Apr-2006 " " Last update. 93*c29d5175Schristos# 94*c29d5175Schristos 95*c29d5175Schristos 96*c29d5175Schristos############################## 97*c29d5175Schristos# --- Process Arguments --- 98*c29d5175Schristos# 99*c29d5175Schristos 100*c29d5175Schristos### default variables 101*c29d5175Schristosopt_device=0; opt_file=0; opt_mount=0; opt_clear=1; opt_proj=0; opt_zone=0 102*c29d5175Schristosopt_percent=0; opt_def=1; opt_bytes=1; filter=0; device=.; filename=.; mount=. 103*c29d5175Schristosopt_top=0; opt_elapsed=0; opt_dtime=0; interval=5; count=-1; top=0 104*c29d5175Schristos 105*c29d5175Schristos### process options 106*c29d5175Schristoswhile getopts CDd:f:hjm:oPt:Z name 107*c29d5175Schristosdo 108*c29d5175Schristos case $name in 109*c29d5175Schristos C) opt_clear=0 ;; 110*c29d5175Schristos D) opt_elapsed=1; opt_bytes=0 ;; 111*c29d5175Schristos d) opt_device=1; device=$OPTARG ;; 112*c29d5175Schristos f) opt_file=1; filename=$OPTARG ;; 113*c29d5175Schristos j) opt_proj=1; opt_def=0 ;; 114*c29d5175Schristos m) opt_mount=1; mount=$OPTARG ;; 115*c29d5175Schristos o) opt_dtime=1; opt_bytes=0 ;; 116*c29d5175Schristos P) opt_percent=1; opt_dtime=1; opt_bytes=0 ;; 117*c29d5175Schristos t) opt_top=1; top=$OPTARG ;; 118*c29d5175Schristos Z) opt_zone=1; opt_def=0 ;; 119*c29d5175Schristos h|?) cat <<-END >&2 120*c29d5175Schristos USAGE: iotop [-C] [-D|-o|-P] [-j|-Z] [-d device] [-f filename] 121*c29d5175Schristos [-m mount_point] [-t top] [interval [count]] 122*c29d5175Schristos 123*c29d5175Schristos -C # don't clear the screen 124*c29d5175Schristos -D # print delta times, elapsed, us 125*c29d5175Schristos -j # print project ID 126*c29d5175Schristos -o # print disk delta times, us 127*c29d5175Schristos -P # print %I/O (disk delta times) 128*c29d5175Schristos -Z # print zone ID 129*c29d5175Schristos -d device # instance name to snoop 130*c29d5175Schristos -f filename # snoop this file only 131*c29d5175Schristos -m mount_point # this FS only 132*c29d5175Schristos -t top # print top number only 133*c29d5175Schristos eg, 134*c29d5175Schristos iotop # default output, 5 second samples 135*c29d5175Schristos iotop 1 # 1 second samples 136*c29d5175Schristos iotop -P # print %I/O (time based) 137*c29d5175Schristos iotop -m / # snoop events on filesystem / only 138*c29d5175Schristos iotop -t 20 # print top 20 lines only 139*c29d5175Schristos iotop -C 5 12 # print 12 x 5 second samples 140*c29d5175Schristos END 141*c29d5175Schristos exit 1 142*c29d5175Schristos esac 143*c29d5175Schristosdone 144*c29d5175Schristos 145*c29d5175Schristosshift $(( $OPTIND - 1 )) 146*c29d5175Schristos 147*c29d5175Schristos### option logic 148*c29d5175Schristosif [[ "$1" > 0 ]]; then 149*c29d5175Schristos interval=$1; shift 150*c29d5175Schristosfi 151*c29d5175Schristosif [[ "$1" > 0 ]]; then 152*c29d5175Schristos count=$1; shift 153*c29d5175Schristosfi 154*c29d5175Schristosif (( opt_proj && opt_zone )); then 155*c29d5175Schristos opt_proj=0 156*c29d5175Schristosfi 157*c29d5175Schristosif (( opt_elapsed && opt_dtime )); then 158*c29d5175Schristos opt_elapsed=0 159*c29d5175Schristosfi 160*c29d5175Schristosif (( opt_device || opt_mount || opt_file )); then 161*c29d5175Schristos filter=1 162*c29d5175Schristosfi 163*c29d5175Schristosif (( opt_clear )); then 164*c29d5175Schristos clearstr=`clear` 165*c29d5175Schristoselse 166*c29d5175Schristos clearstr=. 167*c29d5175Schristosfi 168*c29d5175Schristos 169*c29d5175Schristos 170*c29d5175Schristos 171*c29d5175Schristos################################# 172*c29d5175Schristos# --- Main Program, DTrace --- 173*c29d5175Schristos# 174*c29d5175Schristos/usr/sbin/dtrace -n ' 175*c29d5175Schristos /* 176*c29d5175Schristos * Command line arguments 177*c29d5175Schristos */ 178*c29d5175Schristos inline int OPT_def = '$opt_def'; 179*c29d5175Schristos inline int OPT_proj = '$opt_proj'; 180*c29d5175Schristos inline int OPT_zone = '$opt_zone'; 181*c29d5175Schristos inline int OPT_clear = '$opt_clear'; 182*c29d5175Schristos inline int OPT_bytes = '$opt_bytes'; 183*c29d5175Schristos inline int OPT_elapsed = '$opt_elapsed'; 184*c29d5175Schristos inline int OPT_dtime = '$opt_dtime'; 185*c29d5175Schristos inline int OPT_percent = '$opt_percent'; 186*c29d5175Schristos inline int OPT_device = '$opt_device'; 187*c29d5175Schristos inline int OPT_mount = '$opt_mount'; 188*c29d5175Schristos inline int OPT_file = '$opt_file'; 189*c29d5175Schristos inline int OPT_top = '$opt_top'; 190*c29d5175Schristos inline int INTERVAL = '$interval'; 191*c29d5175Schristos inline int COUNTER = '$count'; 192*c29d5175Schristos inline int FILTER = '$filter'; 193*c29d5175Schristos inline int TOP = '$top'; 194*c29d5175Schristos inline string DEVICE = "'$device'"; 195*c29d5175Schristos inline string FILENAME = "'$filename'"; 196*c29d5175Schristos inline string MOUNT = "'$mount'"; 197*c29d5175Schristos inline string CLEAR = "'$clearstr'"; 198*c29d5175Schristos 199*c29d5175Schristos #pragma D option quiet 200*c29d5175Schristos 201*c29d5175Schristos /* boost the following if you get "dynamic variable drops" */ 202*c29d5175Schristos #pragma D option dynvarsize=8m 203*c29d5175Schristos 204*c29d5175Schristos /* 205*c29d5175Schristos * Print header 206*c29d5175Schristos */ 207*c29d5175Schristos dtrace:::BEGIN 208*c29d5175Schristos { 209*c29d5175Schristos last_event[""] = 0; 210*c29d5175Schristos 211*c29d5175Schristos /* starting values */ 212*c29d5175Schristos counts = COUNTER; 213*c29d5175Schristos secs = INTERVAL; 214*c29d5175Schristos disk_r = 0; 215*c29d5175Schristos disk_w = 0; 216*c29d5175Schristos 217*c29d5175Schristos printf("Tracing... Please wait.\n"); 218*c29d5175Schristos } 219*c29d5175Schristos 220*c29d5175Schristos /* 221*c29d5175Schristos * Check event is being traced 222*c29d5175Schristos */ 223*c29d5175Schristos io:genunix::start, 224*c29d5175Schristos io:genunix::done 225*c29d5175Schristos { 226*c29d5175Schristos /* default is to trace unless filtering, */ 227*c29d5175Schristos this->ok = FILTER ? 0 : 1; 228*c29d5175Schristos 229*c29d5175Schristos /* check each filter, */ 230*c29d5175Schristos (OPT_device == 1 && DEVICE == args[1]->dev_statname)? this->ok = 1 : 1; 231*c29d5175Schristos (OPT_file == 1 && FILENAME == args[2]->fi_pathname) ? this->ok = 1 : 1; 232*c29d5175Schristos (OPT_mount == 1 && MOUNT == args[2]->fi_mount) ? this->ok = 1 : 1; 233*c29d5175Schristos } 234*c29d5175Schristos 235*c29d5175Schristos /* 236*c29d5175Schristos * Reset last_event for disk idle -> start 237*c29d5175Schristos * this prevents idle time being counted as disk time. 238*c29d5175Schristos */ 239*c29d5175Schristos io:genunix::start 240*c29d5175Schristos /! pending[args[1]->dev_statname]/ 241*c29d5175Schristos { 242*c29d5175Schristos /* save last disk event */ 243*c29d5175Schristos last_event[args[1]->dev_statname] = timestamp; 244*c29d5175Schristos } 245*c29d5175Schristos 246*c29d5175Schristos /* 247*c29d5175Schristos * Store entry details 248*c29d5175Schristos */ 249*c29d5175Schristos io:genunix::start 250*c29d5175Schristos /this->ok/ 251*c29d5175Schristos { 252*c29d5175Schristos /* these are used as a unique disk event key, */ 253*c29d5175Schristos this->dev = args[0]->b_edev; 254*c29d5175Schristos this->blk = args[0]->b_blkno; 255*c29d5175Schristos 256*c29d5175Schristos /* save disk event details, */ 257*c29d5175Schristos start_uid[this->dev, this->blk] = uid; 258*c29d5175Schristos start_pid[this->dev, this->blk] = pid; 259*c29d5175Schristos start_ppid[this->dev, this->blk] = ppid; 260*c29d5175Schristos start_comm[this->dev, this->blk] = execname; 261*c29d5175Schristos start_time[this->dev, this->blk] = timestamp; 262*c29d5175Schristos start_proj[this->dev, this->blk] = curpsinfo->pr_projid; 263*c29d5175Schristos start_zone[this->dev, this->blk] = curpsinfo->pr_zoneid; 264*c29d5175Schristos start_rw[this->dev, this->blk] = args[0]->b_flags & B_READ ? "R" : "W"; 265*c29d5175Schristos disk_r += args[0]->b_flags & B_READ ? args[0]->b_bcount : 0; 266*c29d5175Schristos disk_w += args[0]->b_flags & B_READ ? 0 : args[0]->b_bcount; 267*c29d5175Schristos 268*c29d5175Schristos /* increase disk event pending count */ 269*c29d5175Schristos pending[args[1]->dev_statname]++; 270*c29d5175Schristos } 271*c29d5175Schristos 272*c29d5175Schristos /* 273*c29d5175Schristos * Process and Print completion 274*c29d5175Schristos */ 275*c29d5175Schristos io:genunix::done 276*c29d5175Schristos /this->ok/ 277*c29d5175Schristos { 278*c29d5175Schristos /* decrease disk event pending count */ 279*c29d5175Schristos pending[args[1]->dev_statname]--; 280*c29d5175Schristos 281*c29d5175Schristos /* 282*c29d5175Schristos * Process details 283*c29d5175Schristos */ 284*c29d5175Schristos 285*c29d5175Schristos /* fetch entry values */ 286*c29d5175Schristos this->dev = args[0]->b_edev; 287*c29d5175Schristos this->blk = args[0]->b_blkno; 288*c29d5175Schristos this->suid = start_uid[this->dev, this->blk]; 289*c29d5175Schristos this->spid = start_pid[this->dev, this->blk]; 290*c29d5175Schristos this->sppid = start_ppid[this->dev, this->blk]; 291*c29d5175Schristos this->sproj = start_proj[this->dev, this->blk]; 292*c29d5175Schristos this->szone = start_zone[this->dev, this->blk]; 293*c29d5175Schristos self->scomm = start_comm[this->dev, this->blk]; 294*c29d5175Schristos this->stime = start_time[this->dev, this->blk]; 295*c29d5175Schristos this->etime = timestamp; /* endtime */ 296*c29d5175Schristos this->elapsed = this->etime - this->stime; 297*c29d5175Schristos self->rw = start_rw[this->dev, this->blk]; 298*c29d5175Schristos this->dtime = last_event[args[1]->dev_statname] == 0 ? 0 : 299*c29d5175Schristos timestamp - last_event[args[1]->dev_statname]; 300*c29d5175Schristos 301*c29d5175Schristos /* memory cleanup */ 302*c29d5175Schristos start_uid[this->dev, this->blk] = 0; 303*c29d5175Schristos start_pid[this->dev, this->blk] = 0; 304*c29d5175Schristos start_ppid[this->dev, this->blk] = 0; 305*c29d5175Schristos start_time[this->dev, this->blk] = 0; 306*c29d5175Schristos start_comm[this->dev, this->blk] = 0; 307*c29d5175Schristos start_zone[this->dev, this->blk] = 0; 308*c29d5175Schristos start_proj[this->dev, this->blk] = 0; 309*c29d5175Schristos start_rw[this->dev, this->blk] = 0; 310*c29d5175Schristos 311*c29d5175Schristos /* 312*c29d5175Schristos * Choose statistic to track 313*c29d5175Schristos */ 314*c29d5175Schristos OPT_bytes ? this->value = args[0]->b_bcount : 1; 315*c29d5175Schristos OPT_elapsed ? this->value = this->elapsed / 1000 : 1; 316*c29d5175Schristos OPT_dtime ? this->value = this->dtime / 1000 : 1; 317*c29d5175Schristos 318*c29d5175Schristos /* 319*c29d5175Schristos * Save details 320*c29d5175Schristos */ 321*c29d5175Schristos OPT_def ? @out[this->suid, this->spid, this->sppid, self->scomm, 322*c29d5175Schristos args[1]->dev_statname, args[1]->dev_major, args[1]->dev_minor, 323*c29d5175Schristos self->rw] = sum(this->value) : 1; 324*c29d5175Schristos OPT_proj ? @out[this->sproj, this->spid, this->sppid, self->scomm, 325*c29d5175Schristos args[1]->dev_statname, args[1]->dev_major, args[1]->dev_minor, 326*c29d5175Schristos self->rw] = sum(this->value) : 1; 327*c29d5175Schristos OPT_zone ? @out[this->szone, this->spid, this->sppid, self->scomm, 328*c29d5175Schristos args[1]->dev_statname, args[1]->dev_major, args[1]->dev_minor, 329*c29d5175Schristos self->rw] = sum(this->value) : 1; 330*c29d5175Schristos 331*c29d5175Schristos /* save last disk event */ 332*c29d5175Schristos last_event[args[1]->dev_statname] = timestamp; 333*c29d5175Schristos 334*c29d5175Schristos self->scomm = 0; 335*c29d5175Schristos self->rw = 0; 336*c29d5175Schristos } 337*c29d5175Schristos 338*c29d5175Schristos /* 339*c29d5175Schristos * Prevent pending from underflowing 340*c29d5175Schristos * this can happen if this program is started during disk events. 341*c29d5175Schristos */ 342*c29d5175Schristos io:genunix::done 343*c29d5175Schristos /pending[args[1]->dev_statname] < 0/ 344*c29d5175Schristos { 345*c29d5175Schristos pending[args[1]->dev_statname] = 0; 346*c29d5175Schristos } 347*c29d5175Schristos 348*c29d5175Schristos /* 349*c29d5175Schristos * Timer 350*c29d5175Schristos */ 351*c29d5175Schristos profile:::tick-1sec 352*c29d5175Schristos { 353*c29d5175Schristos secs--; 354*c29d5175Schristos } 355*c29d5175Schristos 356*c29d5175Schristos /* 357*c29d5175Schristos * Print Report 358*c29d5175Schristos */ 359*c29d5175Schristos profile:::tick-1sec 360*c29d5175Schristos /secs == 0/ 361*c29d5175Schristos { 362*c29d5175Schristos /* fetch 1 min load average */ 363*c29d5175Schristos this->load1a = `hp_avenrun[0] / 65536; 364*c29d5175Schristos this->load1b = ((`hp_avenrun[0] % 65536) * 100) / 65536; 365*c29d5175Schristos 366*c29d5175Schristos /* convert counters to Kbytes */ 367*c29d5175Schristos disk_r /= 1024; 368*c29d5175Schristos disk_w /= 1024; 369*c29d5175Schristos 370*c29d5175Schristos /* print status */ 371*c29d5175Schristos OPT_clear ? printf("%s", CLEAR) : 1; 372*c29d5175Schristos printf("%Y, load: %d.%02d, disk_r: %6d KB, disk_w: %6d KB\n\n", 373*c29d5175Schristos walltimestamp, this->load1a, this->load1b, disk_r, disk_w); 374*c29d5175Schristos 375*c29d5175Schristos /* print headers */ 376*c29d5175Schristos OPT_def ? printf(" UID ") : 1; 377*c29d5175Schristos OPT_proj ? printf(" PROJ ") : 1; 378*c29d5175Schristos OPT_zone ? printf(" ZONE ") : 1; 379*c29d5175Schristos printf("%6s %6s %-16s %-7s %3s %3s %1s", 380*c29d5175Schristos "PID", "PPID", "CMD", "DEVICE", "MAJ", "MIN", "D"); 381*c29d5175Schristos OPT_bytes ? printf(" %16s\n", "BYTES") : 1; 382*c29d5175Schristos OPT_elapsed ? printf(" %16s\n", "ELAPSED") : 1; 383*c29d5175Schristos OPT_dtime && ! OPT_percent ? printf(" %16s\n", "DISKTIME") : 1; 384*c29d5175Schristos OPT_dtime && OPT_percent ? printf(" %6s\n", "%I/O") : 1; 385*c29d5175Schristos 386*c29d5175Schristos /* truncate to top lines if needed */ 387*c29d5175Schristos OPT_top ? trunc(@out, TOP) : 1; 388*c29d5175Schristos 389*c29d5175Schristos /* normalise to percentage if needed */ 390*c29d5175Schristos OPT_percent ? normalize(@out, INTERVAL * 10000) : 1; 391*c29d5175Schristos 392*c29d5175Schristos /* print data */ 393*c29d5175Schristos ! OPT_percent ? 394*c29d5175Schristos printa("%5d %6d %6d %-16s %-7s %3d %3d %1s %16@d\n", @out) : 395*c29d5175Schristos printa("%5d %6d %6d %-16s %-7s %3d %3d %1s %6@d\n", @out); 396*c29d5175Schristos printf("\n"); 397*c29d5175Schristos 398*c29d5175Schristos /* clear data */ 399*c29d5175Schristos trunc(@out); 400*c29d5175Schristos disk_r = 0; 401*c29d5175Schristos disk_w = 0; 402*c29d5175Schristos secs = INTERVAL; 403*c29d5175Schristos counts--; 404*c29d5175Schristos } 405*c29d5175Schristos 406*c29d5175Schristos /* 407*c29d5175Schristos * End of program 408*c29d5175Schristos */ 409*c29d5175Schristos profile:::tick-1sec 410*c29d5175Schristos /counts == 0/ 411*c29d5175Schristos { 412*c29d5175Schristos exit(0); 413*c29d5175Schristos } 414*c29d5175Schristos 415*c29d5175Schristos /* 416*c29d5175Schristos * Cleanup for Ctrl-C 417*c29d5175Schristos */ 418*c29d5175Schristos dtrace:::END 419*c29d5175Schristos { 420*c29d5175Schristos trunc(@out); 421*c29d5175Schristos } 422*c29d5175Schristos' 423