xref: /linux/tools/mm/slabinfo-gnuplot.sh (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
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