xref: /OK3568_Linux_fs/kernel/tools/vm/slabinfo-gnuplot.sh (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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