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