xref: /spdk/scripts/perf/pm/collect-vmstat (revision 3fa77bf24625a476f3376bb780a23bf6a6d76a1e)
1#!/usr/bin/env bash
2#  SPDX-License-Identifier: BSD-3-Clause
3#  Copyright (C) 2023 Intel Corporation
4#  All rights reserved.
5
6shopt -s nullglob extglob
7
8pmdir=$(readlink -f "$(dirname "$0")")
9rootdir=$(readlink -f "$pmdir/../../../")
10source "$pmdir/common"
11source "$rootdir/test/scheduler/common.sh"
12
13help() {
14	cat <<- HELP
15
16		Usage: $0 [-h] [-c count] [-d dir] [-l] [-p prefix] [-r reprint_header_count]
17
18		-h - Print this message.
19		-c - Execute count times. 0 is the default and it means to run
20		     indefinitely.
21		-d - Directory where results should be saved. Default is /tmp.
22		-l - Save output of the script to a log file (dir/${0##*/}.pm.log).
23		-p - Add prefix to saved files.
24		-r - Stat count after which header should be re-printed. Default is 20.
25		-t - How long to wait before each stat dump. Default is 1s.
26
27		When started, ${0##*/} will enter loop to continuously dump
28		vmstat. Each iteration will be logged to stderr and a log file
29		(dir/${0##*/}.pm.log).
30
31	HELP
32}
33
34get_extra_info() {
35	local match="" data node hp hps
36
37	case "$PM_OS" in
38		Linux)
39			match+="|MemAvailable"
40			match+="|Shmem"
41			match+="|HugePages_(Total|Free)"
42			match+="|Hugepagesize"
43
44			data=($(grep -E "^($match):" /proc/meminfo | awk '{print $2}'))
45			if is_numa; then
46				for node in "${nodes[@]}"; do
47					for hp in "/sys/devices/system/node/node$node/hugepages/"*; do
48						hps=${hps:+$hps,}N$node-${hp#*hugepages-}_f=$(< "$hp/free_hugepages")
49						hps=${hps:+$hps,}N$node-${hp#*hugepages-}_t=$(< "$hp/nr_hugepages")
50					done
51				done
52				data+=("$hps")
53			fi
54			;&
55		*) data+=("$TEST_TAG") ;;
56	esac
57	echo "${data[*]}"
58}
59
60set_extra_info_header() {
61	local -n header_ref=$1
62	local _header
63
64	# Keep the header ordered as in get_extra_info()
65	case "$PM_OS" in
66		Linux)
67			_header="avail shmem hp_total hp_free hp_size"
68			if is_numa; then
69				_header+=" hp_per_node"
70			fi
71			_header+=" test"
72			;;
73		FreeBSD) _header="test" ;;
74	esac
75
76	header_ref[0]="${header_ref[0]} ----extra info----"
77	header_ref[1]="${header_ref[1]} $_header"
78}
79
80_vmstat() {
81	local count=$1 interval=$2 reprint_header=${3:-20} _count
82	local vmstat vmstat_cmdline=() _vmstat stat_idx header=()
83
84	[[ $PM_OS == Linux ]] && vmstat_cmdline+=(--timestamp)
85
86	_count=$count
87	while ((stat_idx = stat_idx == reprint_header ? 1 : ++stat_idx, count <= 0 ? 1 : _count--)); do
88		mapfile -t vmstat < <(vmstat "${vmstat_cmdline[@]}")
89		# Enhance output to include stuff we are most interested in
90		vmstat[2]="${vmstat[2]} $(get_extra_info)"
91		if ((stat_idx == 1)); then
92			header=("${vmstat[@]::2}")
93			set_extra_info_header header
94			_vmstat=("${header[@]}" "${vmstat[2]}")
95		else
96			_vmstat=("${vmstat[2]}")
97		fi
98		printf '%s\n' "${_vmstat[@]}"
99		sleep "${interval}"
100	done
101}
102
103cleanup() {
104	rm_pm_pid
105}
106
107count=0
108interval=1
109log_to_file=no
110prefix=""
111reprint_header=20
112
113while getopts c:d:hlp:r:t: opt; do
114	case "$opt" in
115		c) count=$OPTARG ;;
116		d) PM_OUTPUTDIR=$OPTARG ;;
117		h)
118			help
119			exit 0
120			;;
121		l) log_to_file=yes ;;
122		p) prefix=$OPTARG ;;
123		r) reprint_header=$OPTARG ;;
124		t) interval=$OPTARG ;;
125		*) ;;
126	esac
127done
128
129declare -r log_file=${prefix:+${prefix}_}${0##*/}.pm.log
130
131mkdir -p "$PM_OUTPUTDIR"
132if [[ $log_to_file == yes ]]; then
133	printf 'Redirecting to %s\n' "$PM_OUTPUTDIR/$log_file" >&2
134	exec > "$PM_OUTPUTDIR/$log_file" 2>&1
135fi
136
137save_pm_pid
138trap 'cleanup' EXIT
139trap 'retag' USR1
140
141map_cpus
142
143_vmstat "$count" "$interval" "$reprint_header"
144