linux/tools/vm/slabinfo-gnuplot.sh
<<
>>
Prefs
   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