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