269 lines · bash
1#!/bin/bash2# SPDX-License-Identifier: GPL-2.0-only3 4# Sergey Senozhatsky, 20155# sergey.senozhatsky.work@gmail.com6#7 8 9# This program is intended to plot a `slabinfo -X' stats, collected,10# for example, using the following command:11# while [ 1 ]; do slabinfo -X >> stats; sleep 1; done12#13# Use `slabinfo-gnuplot.sh stats' to pre-process collected records14# and generate graphs (totals, slabs sorted by size, slabs sorted15# by size).16#17# Graphs can be [individually] regenerate with different ranges and18# size (-r %d,%d and -s %d,%d options).19#20# To visually compare N `totals' graphs, do21# slabinfo-gnuplot.sh -t FILE1-totals FILE2-totals ... FILEN-totals22#23 24min_slab_name_size=1125xmin=026xmax=027width=150028height=70029mode=preprocess30 31usage()32{33 echo "Usage: [-s W,H] [-r MIN,MAX] [-t|-l] FILE1 [FILE2 ..]"34 echo "FILEs must contain 'slabinfo -X' samples"35 echo "-t - plot totals for FILE(s)"36 echo "-l - plot slabs stats for FILE(s)"37 echo "-s %d,%d - set image width and height"38 echo "-r %d,%d - use data samples from a given range"39}40 41check_file_exist()42{43 if [ ! -f "$1" ]; then44 echo "File '$1' does not exist"45 exit 146 fi47}48 49do_slabs_plotting()50{51 local file=$152 local out_file53 local range="every ::$xmin"54 local xtic=""55 local xtic_rotate="norotate"56 local lines=200000057 local wc_lines58 59 check_file_exist "$file"60 61 out_file=`basename "$file"`62 if [ $xmax -ne 0 ]; then63 range="$range::$xmax"64 lines=$((xmax-xmin))65 fi66 67 wc_lines=`cat "$file" | wc -l`68 if [ $? -ne 0 ] || [ "$wc_lines" -eq 0 ] ; then69 wc_lines=$lines70 fi71 72 if [ "$wc_lines" -lt "$lines" ]; then73 lines=$wc_lines74 fi75 76 if [ $((width / lines)) -gt $min_slab_name_size ]; then77 xtic=":xtic(1)"78 xtic_rotate=9079 fi80 81gnuplot -p << EOF82#!/usr/bin/env gnuplot83 84set terminal png enhanced size $width,$height large85set output '$out_file.png'86set autoscale xy87set xlabel 'samples'88set ylabel 'bytes'89set style histogram columnstacked title textcolor lt -190set style fill solid 0.1591set xtics rotate $xtic_rotate92set key left above Left title reverse93 94plot "$file" $range u 2$xtic title 'SIZE' with boxes,\95 '' $range u 3 title 'LOSS' with boxes96EOF97 98 if [ $? -eq 0 ]; then99 echo "$out_file.png"100 fi101}102 103do_totals_plotting()104{105 local gnuplot_cmd=""106 local range="every ::$xmin"107 local file=""108 109 if [ $xmax -ne 0 ]; then110 range="$range::$xmax"111 fi112 113 for i in "${t_files[@]}"; do114 check_file_exist "$i"115 116 file="$file"`basename "$i"`117 gnuplot_cmd="$gnuplot_cmd '$i' $range using 1 title\118 '$i Memory usage' with lines,"119 gnuplot_cmd="$gnuplot_cmd '' $range using 2 title \120 '$i Loss' with lines,"121 done122 123gnuplot -p << EOF124#!/usr/bin/env gnuplot125 126set terminal png enhanced size $width,$height large127set autoscale xy128set output '$file.png'129set xlabel 'samples'130set ylabel 'bytes'131set key left above Left title reverse132 133plot $gnuplot_cmd134EOF135 136 if [ $? -eq 0 ]; then137 echo "$file.png"138 fi139}140 141do_preprocess()142{143 local out144 local lines145 local in=$1146 147 check_file_exist "$in"148 149 # use only 'TOP' slab (biggest memory usage or loss)150 let lines=3151 out=`basename "$in"`"-slabs-by-loss"152 `cat "$in" | grep -A "$lines" 'Slabs sorted by loss' |\153 grep -E -iv '\-\-|Name|Slabs'\154 | awk '{print $1" "$4+$2*$3" "$4}' > "$out"`155 if [ $? -eq 0 ]; then156 do_slabs_plotting "$out"157 fi158 159 let lines=3160 out=`basename "$in"`"-slabs-by-size"161 `cat "$in" | grep -A "$lines" 'Slabs sorted by size' |\162 grep -E -iv '\-\-|Name|Slabs'\163 | awk '{print $1" "$4" "$4-$2*$3}' > "$out"`164 if [ $? -eq 0 ]; then165 do_slabs_plotting "$out"166 fi167 168 out=`basename "$in"`"-totals"169 `cat "$in" | grep "Memory used" |\170 awk '{print $3" "$7}' > "$out"`171 if [ $? -eq 0 ]; then172 t_files[0]=$out173 do_totals_plotting174 fi175}176 177parse_opts()178{179 local opt180 181 while getopts "tlr::s::h" opt; do182 case $opt in183 t)184 mode=totals185 ;;186 l)187 mode=slabs188 ;;189 s)190 array=(${OPTARG//,/ })191 width=${array[0]}192 height=${array[1]}193 ;;194 r)195 array=(${OPTARG//,/ })196 xmin=${array[0]}197 xmax=${array[1]}198 ;;199 h)200 usage201 exit 0202 ;;203 \?)204 echo "Invalid option: -$OPTARG" >&2205 exit 1206 ;;207 :)208 echo "-$OPTARG requires an argument." >&2209 exit 1210 ;;211 esac212 done213 214 return $OPTIND215}216 217parse_args()218{219 local idx=0220 local p221 222 for p in "$@"; do223 case $mode in224 preprocess)225 files[$idx]=$p226 idx=$idx+1227 ;;228 totals)229 t_files[$idx]=$p230 idx=$idx+1231 ;;232 slabs)233 files[$idx]=$p234 idx=$idx+1235 ;;236 esac237 done238}239 240parse_opts "$@"241argstart=$?242parse_args "${@:$argstart}"243 244if [ ${#files[@]} -eq 0 ] && [ ${#t_files[@]} -eq 0 ]; then245 usage246 exit 1247fi248 249case $mode in250 preprocess)251 for i in "${files[@]}"; do252 do_preprocess "$i"253 done254 ;;255 totals)256 do_totals_plotting257 ;;258 slabs)259 for i in "${files[@]}"; do260 do_slabs_plotting "$i"261 done262 ;;263 *)264 echo "Unknown mode $mode" >&2265 usage266 exit 1267 ;;268esac269