Commit | Line | Data |
---|---|---|
0b265c3b | 1 | #!/bin/bash |
9c92ab61 | 2 | # SPDX-License-Identifier: GPL-2.0-only |
4a981abd SS |
3 | |
4 | # Sergey Senozhatsky, 2015 | |
5 | # sergey.senozhatsky.work@gmail.com | |
6 | # | |
4a981abd SS |
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 | ||
24 | min_slab_name_size=11 | |
25 | xmin=0 | |
26 | xmax=0 | |
27 | width=1500 | |
28 | height=700 | |
29 | mode=preprocess | |
30 | ||
31 | usage() | |
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 | ||
41 | check_file_exist() | |
42 | { | |
43 | if [ ! -f "$1" ]; then | |
44 | echo "File '$1' does not exist" | |
45 | exit 1 | |
46 | fi | |
47 | } | |
48 | ||
49 | do_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 | ||
81 | gnuplot -p << EOF | |
82 | #!/usr/bin/env gnuplot | |
83 | ||
84 | set terminal png enhanced size $width,$height large | |
85 | set output '$out_file.png' | |
86 | set autoscale xy | |
87 | set xlabel 'samples' | |
88 | set ylabel 'bytes' | |
89 | set style histogram columnstacked title textcolor lt -1 | |
90 | set style fill solid 0.15 | |
91 | set xtics rotate $xtic_rotate | |
92 | set key left above Left title reverse | |
93 | ||
94 | plot "$file" $range u 2$xtic title 'SIZE' with boxes,\ | |
95 | '' $range u 3 title 'LOSS' with boxes | |
96 | EOF | |
97 | ||
98 | if [ $? -eq 0 ]; then | |
99 | echo "$out_file.png" | |
100 | fi | |
101 | } | |
102 | ||
103 | do_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 | ||
123 | gnuplot -p << EOF | |
124 | #!/usr/bin/env gnuplot | |
125 | ||
126 | set terminal png enhanced size $width,$height large | |
127 | set autoscale xy | |
128 | set output '$file.png' | |
129 | set xlabel 'samples' | |
130 | set ylabel 'bytes' | |
131 | set key left above Left title reverse | |
132 | ||
133 | plot $gnuplot_cmd | |
134 | EOF | |
135 | ||
136 | if [ $? -eq 0 ]; then | |
137 | echo "$file.png" | |
138 | fi | |
139 | } | |
140 | ||
141 | do_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' |\ | |
a435874b | 153 | grep -E -iv '\-\-|Name|Slabs'\ |
4a981abd SS |
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' |\ | |
a435874b | 162 | grep -E -iv '\-\-|Name|Slabs'\ |
4a981abd SS |
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 | ||
177 | parse_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 | ||
217 | parse_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 | ||
240 | parse_opts "$@" | |
241 | argstart=$? | |
242 | parse_args "${@:$argstart}" | |
243 | ||
244 | if [ ${#files[@]} -eq 0 ] && [ ${#t_files[@]} -eq 0 ]; then | |
245 | usage | |
246 | exit 1 | |
247 | fi | |
248 | ||
249 | case $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 | ;; | |
268 | esac |