forked from kata-containers/kata-containers
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmemory_usage.sh
executable file
·407 lines (332 loc) · 10.4 KB
/
memory_usage.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
#!/bin/bash
# Copyright (c) 2017-2023 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
# Description of the test:
# This test launches a number of containers in idle mode,
# It will then sleep for a configurable period of time to allow
# any memory optimisations to 'settle, and then checks the
# amount of memory used by all the containers to come up with
# an average (using the PSS measurements)
# This test uses smem tool to get the memory used.
set -e
SCRIPT_PATH=$(dirname "$(readlink -f "$0")")
source "${SCRIPT_PATH}/../lib/common.bash"
# Busybox image: Choose a small workload image, this is
# in order to measure the runtime footprint, not the workload
# footprint.
IMAGE="quay.io/prometheus/busybox:latest"
CMD='tail -f /dev/null'
NUM_CONTAINERS="$1"
WAIT_TIME="$2"
AUTO_MODE="$3"
TEST_NAME="memory footprint"
SMEM_BIN="smem"
KSM_ENABLE_FILE="/sys/kernel/mm/ksm/run"
MEM_TMP_FILE=$(mktemp meminfo.XXXXXXXXXX)
PS_TMP_FILE=$(mktemp psinfo.XXXXXXXXXX)
SKIP_VIRTIO_FS=0
# Variables used to collect memory footprint
global_hypervisor_mem=0
global_virtiofsd_mem=0
global_shim_mem=0
function remove_tmp_file() {
rm -rf "${MEM_TMP_FILE}" "${PS_TMP_FILE}"
clean_env_ctr
}
trap remove_tmp_file EXIT
# Show help about this script
function help(){
cat << EOF
Usage: $0 <count> <wait_time> [auto]
Description:
<count> : Number of containers to run.
<wait_time> : Time in seconds to wait before taking
metrics.
[auto] : Optional 'auto KSM settle' mode
waits for ksm pages_shared to settle down
EOF
}
function get_runc_pss_memory(){
ctr_runc_shim_path="/usr/local/bin/containerd-shim-runc-v2"
get_pss_memory "${ctr_runc_shim_path}"
}
function get_runc_individual_memory() {
runc_process_result=$(cat "${MEM_TMP_FILE}" | tr "\n" " " | sed -e 's/\s$//g' | sed 's/ /, /g')
# Verify runc process result
if [ -z "${runc_process_result}" ];then
die "Runc process not found"
fi
read -r -a runc_values <<< "${runc_process_result}"
metrics_json_start_array
local json="$(cat << EOF
{
"runc individual results": [
$(for ((i=0;i<"${NUM_CONTAINERS[@]}";++i)); do
printf '%s\n\t\t\t' "${runc_values[i]}"
done)
]
}
EOF
)"
metrics_json_add_array_element "$json"
metrics_json_end_array "Raw results"
}
# This function measures the PSS average
# memory of a process.
function get_pss_memory(){
local ps="${1}"
local shim_result_on="${2:-0}"
local mem_amount=0
local count=0
local avg=0
[ -z "${ps}" ] && die "No argument to get_pss_memory()"
ps="$(readlink -f ${ps})"
# Save all the processes names
# This will be help us to retrieve raw information
echo "${ps}" >> "${PS_TMP_FILE}"
data="$(sudo "${SMEM_BIN}" --no-header -P "^${ps}" -c "pss" | sed 's/[[:space:]]//g' | tr '\n' ' ' | sed 's/[[:blank:]]*$//')"
# Save all the smem results
# This will help us to retrieve raw information
echo "${data}" >> "${MEM_TMP_FILE}"
arrData=(${data})
for i in "${arrData[@]}"; do
if [ "${i}" -gt 0 ]; then
let "mem_amount+=i"
let "count+=1"
[ "${count}" -eq "${NUM_CONTAINERS}" ] && break
fi
done
[ "${count}" -eq 0 ] && die "No pss memory was measured for PID: ${ps}"
avg=$(bc -l <<< "scale=2; ${mem_amount} / ${count}")
if [ "${shim_result_on}" -eq "1" ]; then
global_shim_mem="${avg}"
else
global_hypervisor_mem="${avg}"
fi
}
function ppid() {
local pid
pid=$(ps -p "${1:-nopid}" -o ppid=)
echo "${pid//[[:blank:]]/}"
}
# This function measures the PSS average
# memory of virtiofsd.
# It is a special case of get_pss_memory,
# virtiofsd forks itself so, smem sees the process
# two times, this function sum both pss values:
# pss_virtiofsd=pss_fork + pss_parent
function get_pss_memory_virtiofsd() {
mem_amount=0
count=0
avg=0
virtiofsd_path="${1:-}"
if [ $(ps aux | grep -c '[v]irtiofsd') -eq 0 ]; then
SKIP_VIRTIO_FS=1
return
fi
[ -z "${virtiofsd_path}" ] && die "virtiofs path not provided"
echo "${virtiofsd_path}" >> "${PS_TMP_FILE}"
virtiofsd_pids="$(ps aux | grep '[v]irtiofsd' | awk '{print $2}' | head -1)"
data="$(sudo smem --no-header -P "^${virtiofsd_path}" -c "pid pss")"
for p in "${virtiofsd_pids}"; do
echo "get_pss_memory_virtiofsd: p=${p}"
parent_pid=$(ppid "${p}")
cmd="$(cat /proc/${p}/cmdline | tr -d '\0')"
cmd_parent="$(cat /proc/${parent_pid}/cmdline | tr -d '\0')"
if [ "${cmd}" != "${cmd_parent}" ]; then
pss_parent=$(printf "%s" "${data}" | grep "\s^${p}" | awk '{print $2}')
fork=$(pgrep -P "${p}")
pss_fork=$(printf "%s" "${data}" | grep "^\s*${fork}" | awk '{print $2}')
pss_process=$((pss_fork + pss_parent))
# Save all the smem results
# This will help us to retrieve raw information
echo "${pss_process}" >>"${MEM_TMP_FILE}"
if ((pss_process > 0)); then
mem_amount=$((pss_process + mem_amount))
let "count+=1"
fi
fi
done
[ "${count}" -gt 0 ] && global_virtiofsd_mem=$(bc -l <<< "scale=2; ${mem_amount} / ${count}")
}
function get_individual_memory(){
# Getting all the individual container information
first_process_name=$(cat "${PS_TMP_FILE}" | awk 'NR==1' | awk -F "/" '{print $NF}' | sed 's/[[:space:]]//g')
first_process_result=$(cat "${MEM_TMP_FILE}" | awk 'NR==1' | sed 's/ /, /g')
second_process_name=$(cat "${PS_TMP_FILE}" | awk 'NR==2' | awk -F "/" '{print $NF}' | sed 's/[[:space:]]//g')
second_process_result=$(cat "${MEM_TMP_FILE}" | awk 'NR==2' | sed 's/ /, /g')
third_process_name=$(cat "${PS_TMP_FILE}" | awk 'NR==3' | awk -F "/" '{print $NF}' | sed 's/[[:space:]]//g')
third_process_result=$(cat "${MEM_TMP_FILE}" | awk 'NR==3' | sed 's/ /, /g')
read -r -a first_values <<< "${first_process_result}"
read -r -a second_values <<< "${second_process_result}"
read -r -a third_values <<< "${third_process_result}"
declare -a fv_array
declare -a sv_array
declare -a tv_array
# remove null values from arrays of results
for ((i=0;i<"${NUM_CONTAINERS}";++i)); do
[ -n "${first_values[i]}" ] && fv_array+=( "${first_values[i]}" )
[ -n "${second_values[i]}" ] && sv_array+=( "${second_values[i]}" )
[ -n "${third_values[i]}" ] && tv_array+=( "${third_values[i]}" )
done
# remove trailing commas
fv_array[-1]="$(sed -r 's/,*$//g' <<< "${fv_array[-1]}")"
sv_array[-1]="$(sed -r 's/,*$//g' <<< "${sv_array[-1]}")"
[ "${SKIP_VIRTIO_FS}" -eq 0 ] && tv_array[-1]="$(sed -r 's/,*$//g' <<< "${tv_array[-1]}")"
metrics_json_start_array
local json="$(cat << EOF
{
"${first_process_name} memory": [
$(for i in "${fv_array[@]}"; do
printf '\n\t\t\t%s' "${i}"
done)
],
"${second_process_name} memory": [
$(for i in "${sv_array[@]}"; do
printf '\n\t\t\t%s' "${i}"
done)
],
"${third_process_name} memory": [
$(for i in "${tv_array[@]}"; do
printf '\n\t\t\t%s' "${i}"
done)
]
}
EOF
)"
metrics_json_add_array_element "${json}"
metrics_json_end_array "Raw results"
}
# Try to work out the 'average memory footprint' of a container.
function get_memory_usage(){
hypervisor_mem=0
virtiofsd_mem=0
shim_mem=0
memory_usage=0
containers=()
info "Creating ${NUM_CONTAINERS} containers"
for ((i=1; i<="${NUM_CONTAINERS}"; i++)); do
containers+=($(random_name))
sudo "${CTR_EXE}" run --runtime "${CTR_RUNTIME}" -d "${IMAGE}" "${containers[-1]}" sh -c "${CMD}"
done
if [ "${AUTO_MODE}" == "auto" ]; then
if (( ksm_on != 1 )); then
die "KSM not enabled, cannot use auto mode"
fi
echo "Entering KSM settle auto detect mode..."
wait_ksm_settle "${WAIT_TIME}"
else
# If KSM is enabled, then you normally want to sleep long enough to
# let it do its work and for the numbers to 'settle'.
echo "napping ${WAIT_TIME} s"
sleep "${WAIT_TIME}"
fi
metrics_json_start_array
# Check the runtime in order in order to determine which process will
# be measured about PSS
if [ "${CTR_RUNTIME}" == "io.containerd.runc.v2" ]; then
runc_workload_mem="$(get_runc_pss_memory)"
memory_usage="${runc_workload_mem}"
local json="$(cat << EOF
{
"average": {
"Result": ${memory_usage},
"Units" : "KB"
},
"runc": {
"Result": ${runc_workload_mem},
"Units" : "KB"
}
}
EOF
)"
else [ "${CTR_RUNTIME}" == "io.containerd.kata.v2" ]
# Get PSS memory of VM runtime components.
# And check that the smem search has found the process - we get a "0"
# back if that procedure fails (such as if a process has changed its name
# or is not running when expected to be so)
# As an added bonus - this script must be run as root.
# Now if you do not have enough rights
# the smem failure to read the stats will also be trapped.
get_pss_memory "${HYPERVISOR_PATH}"
if [ "${global_hypervisor_mem}" == "0" ]; then
die "Failed to find PSS for ${HYPERVISOR_PATH}"
fi
get_pss_memory_virtiofsd "${VIRTIOFSD_PATH}"
if [ "${global_virtiofsd_mem}" == "0" ]; then
echo >&2 "WARNING: Failed to find PSS for ${VIRTIOFSD_PATH}"
fi
get_pss_memory "${SHIM_PATH}" 1
if [ "${global_shim_mem}" == "0" ]; then
die "Failed to find PSS for ${SHIM_PATH}"
fi
mem_usage="$(bc -l <<< "scale=2; ${global_hypervisor_mem} + ${global_virtiofsd_mem} + ${global_shim_mem}")"
local json="$(cat << EOF
{
"average": {
"Result": ${mem_usage},
"Units" : "KB"
},
"qemus": {
"Result": ${global_hypervisor_mem},
"Units" : "KB"
},
"virtiofsds": {
"Result": ${global_virtiofsd_mem},
"Units" : "KB"
},
"shims": {
"Result": ${global_shim_mem},
"Units" : "KB"
}
}
EOF
)"
fi
metrics_json_add_array_element "$json"
metrics_json_end_array "Results"
}
function save_config(){
metrics_json_start_array
local json="$(cat << EOF
{
"containers": "${NUM_CONTAINERS}",
"ksm": "${ksm_on}",
"auto": "${AUTO_MODE}",
"waittime": "${WAIT_TIME}",
"image": "${IMAGE}",
"command": "${CMD}"
}
EOF
)"
metrics_json_add_array_element "$json"
metrics_json_end_array "Config"
}
function main(){
# Verify enough arguments
if [ $# != 2 ] && [ $# != 3 ];then
help
die "Not enough arguments [$@]"
fi
if [ "${CTR_RUNTIME}" != "io.containerd.runc.v2" ] && [ "${CTR_RUNTIME}" != "io.containerd.kata.v2" ]; then
die "Unknown runtime: ${CTR_RUNTIME}."
fi
#Check for KSM before reporting test name, as it can modify it
check_for_ksm
check_cmds "${SMEM_BIN}" bc
init_env
check_images "${IMAGE}"
metrics_json_init
save_config
get_memory_usage
if [ "${CTR_RUNTIME}" == "io.containerd.kata.v2" ]; then
get_individual_memory
elif [ "${CTR_RUNTIME}" == "io.containerd.runc.v2" ]; then
get_runc_individual_memory
fi
info "memory usage test completed"
metrics_json_save
}
main "$@"