1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0-only 3 4# Sergey Senozhatsky, 2015 5# sergey.senozhatsky.work@gmail.com 6# 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; done 12# 13# Use `slabinfo-gnuplot.sh stats' to pre-process collected records 14# and generate graphs (totals, slabs sorted by size, slabs sorted 15# by size). 16# 17# Graphs can be [individually] regenerate with different ranges and 18# size (-r %d,%d and -s %d,%d options). 19# 20# To visually compare N `totals' graphs, do 21# slabinfo-gnuplot.sh -t FILE1-totals FILE2-totals ... FILEN-totals 22# 23 24min_slab_name_size=11 25xmin=0 26xmax=0 27width=1500 28height=700 29mode=preprocess 30 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" ]; then 44 echo "File '$1' does not exist" 45 exit 1 46 fi 47} 48 49do_slabs_plotting() 50{ 51 local file=$1 52 local out_file 53 local range="every ::$xmin" 54 local xtic="" 55 local xtic_rotate="norotate" 56 local lines=2000000 57 local wc_lines 58 59 check_file_exist "$file" 60 61 out_file=`basename "$file"` 62 if [ $xmax -ne 0 ]; then 63 range="$range::$xmax" 64 lines=$((xmax-xmin)) 65 fi 66 67 wc_lines=`cat "$file" | wc -l` 68 if [ $? -ne 0 ] || [ "$wc_lines" -eq 0 ] ; then 69 wc_lines=$lines 70 fi 71 72 if [ "$wc_lines" -lt "$lines" ]; then 73 lines=$wc_lines 74 fi 75 76 if [ $((width / lines)) -gt $min_slab_name_size ]; then 77 xtic=":xtic(1)" 78 xtic_rotate=90 79 fi 80 81gnuplot -p << EOF 82#!/usr/bin/env gnuplot 83 84set terminal png enhanced size $width,$height large 85set output '$out_file.png' 86set autoscale xy 87set xlabel 'samples' 88set ylabel 'bytes' 89set style histogram columnstacked title textcolor lt -1 90set style fill solid 0.15 91set xtics rotate $xtic_rotate 92set key left above Left title reverse 93 94plot "$file" $range u 2$xtic title 'SIZE' with boxes,\ 95 '' $range u 3 title 'LOSS' with boxes 96EOF 97 98 if [ $? -eq 0 ]; then 99 echo "$out_file.png" 100 fi 101} 102 103do_totals_plotting() 104{ 105 local gnuplot_cmd="" 106 local range="every ::$xmin" 107 local file="" 108 109 if [ $xmax -ne 0 ]; then 110 range="$range::$xmax" 111 fi 112 113 for i in "${t_files[@]}"; do 114 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 done 122 123gnuplot -p << EOF 124#!/usr/bin/env gnuplot 125 126set terminal png enhanced size $width,$height large 127set autoscale xy 128set output '$file.png' 129set xlabel 'samples' 130set ylabel 'bytes' 131set key left above Left title reverse 132 133plot $gnuplot_cmd 134EOF 135 136 if [ $? -eq 0 ]; then 137 echo "$file.png" 138 fi 139} 140 141do_preprocess() 142{ 143 local out 144 local lines 145 local in=$1 146 147 check_file_exist "$in" 148 149 # use only 'TOP' slab (biggest memory usage or loss) 150 let lines=3 151 out=`basename "$in"`"-slabs-by-loss" 152 `cat "$in" | grep -A "$lines" 'Slabs sorted by loss' |\ 153 egrep -iv '\-\-|Name|Slabs'\ 154 | awk '{print $1" "$4+$2*$3" "$4}' > "$out"` 155 if [ $? -eq 0 ]; then 156 do_slabs_plotting "$out" 157 fi 158 159 let lines=3 160 out=`basename "$in"`"-slabs-by-size" 161 `cat "$in" | grep -A "$lines" 'Slabs sorted by size' |\ 162 egrep -iv '\-\-|Name|Slabs'\ 163 | awk '{print $1" "$4" "$4-$2*$3}' > "$out"` 164 if [ $? -eq 0 ]; then 165 do_slabs_plotting "$out" 166 fi 167 168 out=`basename "$in"`"-totals" 169 `cat "$in" | grep "Memory used" |\ 170 awk '{print $3" "$7}' > "$out"` 171 if [ $? -eq 0 ]; then 172 t_files[0]=$out 173 do_totals_plotting 174 fi 175} 176 177parse_opts() 178{ 179 local opt 180 181 while getopts "tlr::s::h" opt; do 182 case $opt in 183 t) 184 mode=totals 185 ;; 186 l) 187 mode=slabs 188 ;; 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 usage 201 exit 0 202 ;; 203 \?) 204 echo "Invalid option: -$OPTARG" >&2 205 exit 1 206 ;; 207 :) 208 echo "-$OPTARG requires an argument." >&2 209 exit 1 210 ;; 211 esac 212 done 213 214 return $OPTIND 215} 216 217parse_args() 218{ 219 local idx=0 220 local p 221 222 for p in "$@"; do 223 case $mode in 224 preprocess) 225 files[$idx]=$p 226 idx=$idx+1 227 ;; 228 totals) 229 t_files[$idx]=$p 230 idx=$idx+1 231 ;; 232 slabs) 233 files[$idx]=$p 234 idx=$idx+1 235 ;; 236 esac 237 done 238} 239 240parse_opts "$@" 241argstart=$? 242parse_args "${@:$argstart}" 243 244if [ ${#files[@]} -eq 0 ] && [ ${#t_files[@]} -eq 0 ]; then 245 usage 246 exit 1 247fi 248 249case $mode in 250 preprocess) 251 for i in "${files[@]}"; do 252 do_preprocess "$i" 253 done 254 ;; 255 totals) 256 do_totals_plotting 257 ;; 258 slabs) 259 for i in "${files[@]}"; do 260 do_slabs_plotting "$i" 261 done 262 ;; 263 *) 264 echo "Unknown mode $mode" >&2 265 usage 266 exit 1 267 ;; 268esac 269