1#!/bin/sh 2 3# Sergey Senozhatsky, 2015 4# sergey.senozhatsky.work@gmail.com 5# 6# This software is licensed under the terms of the GNU General Public 7# License version 2, as published by the Free Software Foundation, and 8# may be copied, distributed, and modified under those terms. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14 15 16# This program is intended to plot a `slabinfo -X' stats, collected, 17# for example, using the following command: 18# while [ 1 ]; do slabinfo -X >> stats; sleep 1; done 19# 20# Use `slabinfo-gnuplot.sh stats' to pre-process collected records 21# and generate graphs (totals, slabs sorted by size, slabs sorted 22# by size). 23# 24# Graphs can be [individually] regenerate with different ranges and 25# size (-r %d,%d and -s %d,%d options). 26# 27# To visually compare N `totals' graphs, do 28# slabinfo-gnuplot.sh -t FILE1-totals FILE2-totals ... FILEN-totals 29# 30 31min_slab_name_size=11 32xmin=0 33xmax=0 34width=1500 35height=700 36mode=preprocess 37 38usage() 39{ 40 echo "Usage: [-s W,H] [-r MIN,MAX] [-t|-l] FILE1 [FILE2 ..]" 41 echo "FILEs must contain 'slabinfo -X' samples" 42 echo "-t - plot totals for FILE(s)" 43 echo "-l - plot slabs stats for FILE(s)" 44 echo "-s %d,%d - set image width and height" 45 echo "-r %d,%d - use data samples from a given range" 46} 47 48check_file_exist() 49{ 50 if [ ! -f "$1" ]; then 51 echo "File '$1' does not exist" 52 exit 1 53 fi 54} 55 56do_slabs_plotting() 57{ 58 local file=$1 59 local out_file 60 local range="every ::$xmin" 61 local xtic="" 62 local xtic_rotate="norotate" 63 local lines=2000000 64 local wc_lines 65 66 check_file_exist "$file" 67 68 out_file=`basename "$file"` 69 if [ $xmax -ne 0 ]; then 70 range="$range::$xmax" 71 lines=$((xmax-xmin)) 72 fi 73 74 wc_lines=`cat "$file" | wc -l` 75 if [ $? -ne 0 ] || [ "$wc_lines" -eq 0 ] ; then 76 wc_lines=$lines 77 fi 78 79 if [ "$wc_lines" -lt "$lines" ]; then 80 lines=$wc_lines 81 fi 82 83 if [ $((width / lines)) -gt $min_slab_name_size ]; then 84 xtic=":xtic(1)" 85 xtic_rotate=90 86 fi 87 88gnuplot -p << EOF 89#!/usr/bin/env gnuplot 90 91set terminal png enhanced size $width,$height large 92set output '$out_file.png' 93set autoscale xy 94set xlabel 'samples' 95set ylabel 'bytes' 96set style histogram columnstacked title textcolor lt -1 97set style fill solid 0.15 98set xtics rotate $xtic_rotate 99set key left above Left title reverse 100 101plot "$file" $range u 2$xtic title 'SIZE' with boxes,\ 102 '' $range u 3 title 'LOSS' with boxes 103EOF 104 105 if [ $? -eq 0 ]; then 106 echo "$out_file.png" 107 fi 108} 109 110do_totals_plotting() 111{ 112 local gnuplot_cmd="" 113 local range="every ::$xmin" 114 local file="" 115 116 if [ $xmax -ne 0 ]; then 117 range="$range::$xmax" 118 fi 119 120 for i in "${t_files[@]}"; do 121 check_file_exist "$i" 122 123 file="$file"`basename "$i"` 124 gnuplot_cmd="$gnuplot_cmd '$i' $range using 1 title\ 125 '$i Memory usage' with lines," 126 gnuplot_cmd="$gnuplot_cmd '' $range using 2 title \ 127 '$i Loss' with lines," 128 done 129 130gnuplot -p << EOF 131#!/usr/bin/env gnuplot 132 133set terminal png enhanced size $width,$height large 134set autoscale xy 135set output '$file.png' 136set xlabel 'samples' 137set ylabel 'bytes' 138set key left above Left title reverse 139 140plot $gnuplot_cmd 141EOF 142 143 if [ $? -eq 0 ]; then 144 echo "$file.png" 145 fi 146} 147 148do_preprocess() 149{ 150 local out 151 local lines 152 local in=$1 153 154 check_file_exist "$in" 155 156 # use only 'TOP' slab (biggest memory usage or loss) 157 let lines=3 158 out=`basename "$in"`"-slabs-by-loss" 159 `cat "$in" | grep -A "$lines" 'Slabs sorted by loss' |\ 160 egrep -iv '\-\-|Name|Slabs'\ 161 | awk '{print $1" "$4+$2*$3" "$4}' > "$out"` 162 if [ $? -eq 0 ]; then 163 do_slabs_plotting "$out" 164 fi 165 166 let lines=3 167 out=`basename "$in"`"-slabs-by-size" 168 `cat "$in" | grep -A "$lines" 'Slabs sorted by size' |\ 169 egrep -iv '\-\-|Name|Slabs'\ 170 | awk '{print $1" "$4" "$4-$2*$3}' > "$out"` 171 if [ $? -eq 0 ]; then 172 do_slabs_plotting "$out" 173 fi 174 175 out=`basename "$in"`"-totals" 176 `cat "$in" | grep "Memory used" |\ 177 awk '{print $3" "$7}' > "$out"` 178 if [ $? -eq 0 ]; then 179 t_files[0]=$out 180 do_totals_plotting 181 fi 182} 183 184parse_opts() 185{ 186 local opt 187 188 while getopts "tlr::s::h" opt; do 189 case $opt in 190 t) 191 mode=totals 192 ;; 193 l) 194 mode=slabs 195 ;; 196 s) 197 array=(${OPTARG//,/ }) 198 width=${array[0]} 199 height=${array[1]} 200 ;; 201 r) 202 array=(${OPTARG//,/ }) 203 xmin=${array[0]} 204 xmax=${array[1]} 205 ;; 206 h) 207 usage 208 exit 0 209 ;; 210 \?) 211 echo "Invalid option: -$OPTARG" >&2 212 exit 1 213 ;; 214 :) 215 echo "-$OPTARG requires an argument." >&2 216 exit 1 217 ;; 218 esac 219 done 220 221 return $OPTIND 222} 223 224parse_args() 225{ 226 local idx=0 227 local p 228 229 for p in "$@"; do 230 case $mode in 231 preprocess) 232 files[$idx]=$p 233 idx=$idx+1 234 ;; 235 totals) 236 t_files[$idx]=$p 237 idx=$idx+1 238 ;; 239 slabs) 240 files[$idx]=$p 241 idx=$idx+1 242 ;; 243 esac 244 done 245} 246 247parse_opts "$@" 248argstart=$? 249parse_args "${@:$argstart}" 250 251if [ ${#files[@]} -eq 0 ] && [ ${#t_files[@]} -eq 0 ]; then 252 usage 253 exit 1 254fi 255 256case $mode in 257 preprocess) 258 for i in "${files[@]}"; do 259 do_preprocess "$i" 260 done 261 ;; 262 totals) 263 do_totals_plotting 264 ;; 265 slabs) 266 for i in "${files[@]}"; do 267 do_slabs_plotting "$i" 268 done 269 ;; 270 *) 271 echo "Unknown mode $mode" >&2 272 usage 273 exit 1 274 ;; 275esac 276