Permalink
| #!/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++ | |
| } | |
| ' |