#!/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. duration=0 buckets=(1 8 64 128) secsz=512 trap ':' INT QUIT TERM PIPE HUP function usage { cat <<-END >&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 } function die { echo >&2 "$@" exit 1 } ### process options while getopts b:h opt do case $opt in b) buckets=($OPTARG) ;; h|?) usage ;; esac done shift $(( $OPTIND - 1 )) tpoint=block:block_rq_issue var=nr_sector duration=$1 ### convert buckets (Kbytes) to disk sectors i=0 sectors=(${buckets[*]}) ((max_i = ${#buckets[*]} - 1)) while (( i <= max_i )); do (( sectors[$i] = ${sectors[$i]} * 1024 / $secsz )) # avoid negative array index errors for old version bash if (( i > 0 ));then if (( ${sectors[$i]} <= ${sectors[$i - 1]} )); then die "ERROR: bucket list must increase in size." fi fi (( i++ )) done ### build list of tracepoints and filters for each histogram bucket max_b=${buckets[$max_i]} max_s=${sectors[$max_i]} tpoints="-e $tpoint --filter \"$var < ${sectors[0]}\"" awkarray= i=0 while (( i < max_i )); do tpoints="$tpoints -e $tpoint --filter \"$var >= ${sectors[$i]} && " tpoints="$tpoints $var < ${sectors[$i + 1]}\"" awkarray="$awkarray buckets[$i]=${buckets[$i]};" (( i++ )) done awkarray="$awkarray buckets[$max_i]=${buckets[$max_i]};" tpoints="$tpoints -e $tpoint --filter \"$var >= ${sectors[$max_i]}\"" ### prepare to run if (( duration )); then etext="for $duration seconds" cmd="sleep $duration" else etext="until Ctrl-C" cmd="sleep 999999" fi echo "Tracing block I/O size (bytes), $etext..." ### run perf out="-o /dev/stdout" # a workaround needed in linux 3.2; not by 3.4.15 stat=$(eval perf stat $tpoints -a $out $cmd 2>&1) if (( $? != 0 )); then echo >&2 "ERROR running perf:" echo >&2 "$stat" exit fi ### find max value for ASCII histogram 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++ } '