#!/bin/bash # # bitesize - show disk I/O size as a histogram. # Written using Linux perf_events (aka "perf"). # # This can be used to characterize the distribution of block device I/O # sizes. To study I/O in more detail, see iosnoop(8). # # USAGE: bitesize [-h] [-b buckets] [seconds] # eg, # ./bitesize 10 # # Run "bitesize -h" for full usage. # # REQUIREMENTS: perf_events and block:block_rq_issue tracepoint, which you may # already have on recent kernels. # # This uses multiple counting tracepoints with different filters, one for each # histogram bucket. While this is summarized in-kernel, the use of multiple # tracepoints does add addiitonal overhead, which is more evident if you add # more buckets. In the future this functionality will be available in an # efficient way in the kernel, and this tool can be rewritten. # # From perf-tools: https://github.com/brendangregg/perf-tools # # COPYRIGHT: Copyright (c) 2014 Brendan Gregg. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # (http://www.gnu.org/copyleft/gpl.html) # # 22-Jul-2014 Brendan Gregg Created this. setglobal duration = '0' setglobal buckets = '('1 8 64 128) setglobal secsz = '512' trap ':' INT QUIT TERM PIPE HUP proc usage { cat << """ >&2 USAGE: bitesize [-h] [-b buckets] [seconds] -b buckets # specify histogram buckets (Kbytes) -h # this usage message eg, bitesize # trace I/O size until Ctrl-C bitesize 10 # trace I/O size for 10 seconds bitesize -b "8 16 32" # specify custom bucket points """ > !2 USAGE: bitesize [-h] [-b buckets] [seconds] -b buckets # specify histogram buckets (Kbytes) -h # this usage message eg, bitesize # trace I/O size until Ctrl-C bitesize 10 # trace I/O size for 10 seconds bitesize -b "8 16 32" # specify custom bucket points END exit } proc die { echo >&2 @Argv> !2 "$@" exit 1 } ### process options while getopts b:h opt { match $opt { with b setglobal buckets = '('$OPTARG) with h|? usage } } shift $shExpr(' $OPTIND - 1 ') setglobal tpoint = 'block:block_rq_issue' setglobal var = 'nr_sector' setglobal duration = $1 ### convert buckets (Kbytes) to disk sectors setglobal i = '0' setglobal sectors = '('${buckets[*]}) sh-expr 'max_i = ${#buckets[*]} - 1' while sh-expr ' i <= max_i ' { sh-expr ' sectors[$i] = ${sectors[$i]} * 1024 / $secsz ' # avoid negative array index errors for old version bash if sh-expr ' i > 0 '{ if sh-expr ' ${sectors[$i]} <= ${sectors[$i - 1]} ' { die "ERROR: bucket list must increase in size." } } sh-expr ' i++ ' } ### build list of tracepoints and filters for each histogram bucket setglobal max_b = $(buckets[$max_i]) setglobal max_s = $(sectors[$max_i]) setglobal tpoints = ""-e $tpoint --filter \"$var < $(sectors[0])"\"" setglobal awkarray = '' setglobal i = '0' while sh-expr ' i < max_i ' { setglobal tpoints = ""$tpoints -e $tpoint --filter \"$var >= $(sectors[$i]) && "" setglobal tpoints = ""$tpoints $var < $(sectors[$i + 1])"\"" setglobal awkarray = ""$awkarray buckets[$i]=$(buckets[$i]);"" sh-expr ' i++ ' } setglobal awkarray = ""$awkarray buckets[$max_i]=$(buckets[$max_i]);"" setglobal tpoints = ""$tpoints -e $tpoint --filter \"$var >= $(sectors[$max_i])"\"" ### prepare to run if sh-expr ' duration ' { setglobal etext = ""for $duration seconds"" setglobal cmd = ""sleep $duration"" } else { setglobal etext = '"until Ctrl-C'" setglobal cmd = '"sleep 999999'" } echo "Tracing block I/O size (bytes), $etext..." ### run perf setglobal out = '"-o /dev/stdout'" # a workaround needed in linux 3.2; not by 3.4.15 setglobal stat = $[eval perf stat $tpoints -a $out $cmd !2 > !1] if sh-expr ' $? != 0 ' { echo >&2 "ERROR running perf:> !2 "ERROR running perf:" echo >&2 $stat> !2 "$stat" exit } ### find max value for ASCII histogram setglobal most = $[echo $stat | awk -v tpoint=$tpoint ' $2 == tpoint { gsub(/,/, ""); if ($1 > m) { m = $1 } } END { print m }] ### process output echo echo $stat | awk -v tpoint=$tpoint -v max_i=$max_i -v most=$most ' function star(sval, smax, swidth) { stars = "" # using int could avoid error on gawk if (int(smax) == 0) return "" for (si = 0; si < (swidth * sval / smax); si++) { stars = stars "#" } return stars } BEGIN { '"$awkarray"' printf(" %-15s: %-8s %s\n", "Kbytes", "I/O", "Distribution") } /Performance counter stats/ { i = -1 } # reverse order of rule set is important { ok = 0 } $2 == tpoint { num = $1; gsub(/,/, "", num); ok = 1 } ok && i >= max_i { printf(" %10.1f -> %-10s: %-8s |%-38s|\n", buckets[i], "", num, star(num, most, 38)) next } ok && i >= 0 && i < max_i { printf(" %10.1f -> %-10.1f: %-8s |%-38s|\n", buckets[i], buckets[i+1] - 0.1, num, star(num, most, 38)) i++ next } ok && i == -1 { printf(" %10s -> %-10.1f: %-8s |%-38s|\n", "", buckets[0] - 0.1, num, star(num, most, 38)) i++ } '