github.com/kata-containers/tests@v0.0.0-20240307153542-772105b56064/metrics/density/footprint_data.sh (about) 1 #!/bin/bash 2 # Copyright (c) 2017-2018, 2021 Intel Corporation 3 # 4 # SPDX-License-Identifier: Apache-2.0 5 # 6 # A script to gather memory 'footprint' information as we launch more 7 # and more containers 8 # 9 # The script gathers information about both user and kernel space consumption 10 # Output is into a .json file, named using some of the config component names 11 # (such as footprint-busybox.json) 12 13 # Pull in some common, useful, items 14 SCRIPT_PATH=$(dirname "$(readlink -f "$0")") 15 source "${SCRIPT_PATH}/../lib/common.bash" 16 17 KSM_ENABLE_FILE="/sys/kernel/mm/ksm/run" 18 19 # Note that all vars that can be set from outside the script (that is, 20 # passed in the ENV), use the ':-' setting to allow being over-ridden 21 22 # Default sleep for 10s to let containers come up and finish their 23 # initialisation before we take the measures. Some of the larger 24 # containers can take a number of seconds to get running. 25 PAYLOAD_SLEEP="${PAYLOAD_SLEEP:-10}" 26 27 ### The default config - run a small busybox image 28 # Define what we will be running (app under test) 29 # Default is we run busybox, as a 'small' workload 30 PAYLOAD="${PAYLOAD:-quay.io/prometheus/busybox:latest}" 31 PAYLOAD_ARGS="${PAYLOAD_ARGS:-tail -f /dev/null}" 32 33 ### 34 # Define the cutoff checks for when we stop running the test 35 # Run up to this many containers 36 MAX_NUM_CONTAINERS="${MAX_NUM_CONTAINERS:-10}" 37 # Run until we have consumed this much memory (from MemFree) 38 MAX_MEMORY_CONSUMED="${MAX_MEMORY_CONSUMED:-6*1024*1024*1024}" 39 # Run until we have this much MemFree left 40 MIN_MEMORY_FREE="${MIN_MEMORY_FREE:-2*1024*1024*1024}" 41 42 # Tools we need to have installed in order to operate 43 REQUIRED_COMMANDS="smem awk" 44 45 # If we 'dump' the system caches before we measure then we get less 46 # noise in the results - they show more what our un-reclaimable footprint is 47 DUMP_CACHES="${DUMP_CACHES:-1}" 48 49 # Affects the name of the file to store the results in 50 TEST_NAME="${TEST_NAME:-footprint-busybox}" 51 52 ############# end of configurable items ################### 53 54 # vars to remember where we started so we can calc diffs 55 base_mem_avail=0 56 base_mem_free=0 57 58 # dump the kernel caches, so we get a more precise (or just different) 59 # view of what our footprint really is. 60 function dump_caches() { 61 sudo bash -c "echo 3 > /proc/sys/vm/drop_caches" 62 } 63 64 function init() { 65 restart_containerd_service 66 67 check_cmds $REQUIRED_COMMANDS 68 sudo -E "${CTR_EXE}" image pull "$PAYLOAD" 69 70 # Modify the test name if running with KSM enabled 71 check_for_ksm 72 73 # Use the common init func to get to a known state 74 init_env 75 76 # Prepare to start storing results 77 metrics_json_init 78 79 # Store up baseline measures 80 base_mem_avail=$(free -b | head -2 | tail -1 | awk '{print $7}') 81 base_mem_free=$(get_memfree) 82 83 # Store our configuration for this run 84 save_config 85 } 86 87 save_config(){ 88 metrics_json_start_array 89 90 local json="$(cat << EOF 91 { 92 "testname": "${TEST_NAME}", 93 "payload": "${PAYLOAD}", 94 "payload_args": "${PAYLOAD_ARGS}", 95 "payload_sleep": ${PAYLOAD_SLEEP}, 96 "max_containers": ${MAX_NUM_CONTAINERS}, 97 "max_memory_consumed": "${MAX_MEMORY_CONSUMED}", 98 "min_memory_free": "${MIN_MEMORY_FREE}", 99 "dump_caches": "${DUMP_CACHES}" 100 } 101 EOF 102 )" 103 metrics_json_add_array_element "$json" 104 metrics_json_end_array "Config" 105 } 106 107 function cleanup() { 108 # Finish storing the results 109 metrics_json_save 110 111 clean_env_ctr 112 } 113 114 # helper function to get USS of process in arg1 115 function get_proc_uss() { 116 item=$(sudo smem -t -P "^$1" | tail -1 | awk '{print $4}') 117 ((item*=1024)) 118 echo $item 119 } 120 121 # helper function to get PSS of process in arg1 122 function get_proc_pss() { 123 item=$(sudo smem -t -P "^$1" | tail -1 | awk '{print $5}') 124 ((item*=1024)) 125 echo $item 126 } 127 128 # Get the PSS for the whole of userspace (all processes) 129 # This allows us to see if we had any impact on the rest of the system, for instance 130 # containerd grows as we launch containers, so we should account for that in our total 131 # memory breakdown 132 function grab_all_pss() { 133 item=$(sudo smem -t | tail -1 | awk '{print $5}') 134 ((item*=1024)) 135 136 local json="$(cat << EOF 137 "all_pss": { 138 "pss": $item, 139 "Units": "KB" 140 } 141 EOF 142 )" 143 144 metrics_json_add_array_fragment "$json" 145 } 146 147 function grab_user_smem() { 148 # userspace 149 item=$(sudo smem -w | head -5 | tail -1 | awk '{print $3}') 150 ((item*=1024)) 151 152 local json="$(cat << EOF 153 "user_smem": { 154 "userspace": $item, 155 "Units": "KB" 156 } 157 EOF 158 )" 159 160 metrics_json_add_array_fragment "$json" 161 } 162 163 function grab_slab() { 164 # Grabbing slab total from meminfo is easier than doing the math 165 # on slabinfo 166 item=$(fgrep "Slab:" /proc/meminfo | awk '{print $2}') 167 ((item*=1024)) 168 169 local json="$(cat << EOF 170 "slab": { 171 "slab": $item, 172 "Units": "KB" 173 } 174 EOF 175 )" 176 177 metrics_json_add_array_fragment "$json" 178 } 179 180 function get_memfree() { 181 mem_free=$(sudo smem -w | head -6 | tail -1 | awk '{print $4}') 182 ((mem_free*=1024)) 183 echo $mem_free 184 } 185 186 function grab_system() { 187 # avail memory, from 'free' 188 local avail=$(free -b | head -2 | tail -1 | awk '{print $7}') 189 local avail_decr=$((base_mem_avail-avail)) 190 191 # cached memory, from 'free' 192 local cached=$(free -b | head -2 | tail -1 | awk '{print $6}') 193 194 # free memory from smem 195 local smem_free=$(get_memfree) 196 local free_decr=$((base_mem_free-item)) 197 198 # Anon pages 199 local anon=$(fgrep "AnonPages:" /proc/meminfo | awk '{print $2}') 200 ((anon*=1024)) 201 202 # Mapped pages 203 local mapped=$(egrep "^Mapped:" /proc/meminfo | awk '{print $2}') 204 ((mapped*=1024)) 205 206 # Cached 207 local meminfo_cached=$(grep "^Cached:" /proc/meminfo | awk '{print $2}') 208 ((meminfo_cached*=1024)) 209 210 local json="$(cat << EOF 211 "system": { 212 "avail": $avail, 213 "avail_decr": $avail_decr, 214 "cached": $cached, 215 "smem_free": $smem_free, 216 "free_decr": $free_decr, 217 "anon": $anon, 218 "mapped": $mapped, 219 "meminfo_cached": $meminfo_cached, 220 "Units": "KB" 221 } 222 EOF 223 )" 224 225 metrics_json_add_array_fragment "$json" 226 } 227 228 function grab_stats() { 229 # If configured, dump the caches so we get a more stable 230 # view of what our static footprint really is 231 if [[ "$DUMP_CACHES" ]] ; then 232 dump_caches 233 fi 234 235 # user space data 236 # PSS taken all userspace 237 grab_all_pss 238 # user as reported by smem 239 grab_user_smem 240 241 # System overview data 242 # System free and cached 243 grab_system 244 245 # kernel data 246 # The 'total kernel space taken' we can work out as: 247 # ktotal = ((free-avail)-user) 248 # So, we don't grab that number from smem, as that is what it does 249 # internally anyhow. 250 # Still try to grab any finer kernel details that we can though 251 252 # totals from slabinfo 253 grab_slab 254 255 metrics_json_close_array_element 256 } 257 258 function check_limits() { 259 mem_free=$(get_memfree) 260 if ((mem_free <= MIN_MEMORY_FREE)); then 261 echo 1 262 return 263 fi 264 265 mem_consumed=$((base_mem_avail-mem_free)) 266 if ((mem_consumed >= MAX_MEMORY_CONSUMED)); then 267 echo 1 268 return 269 fi 270 271 echo 0 272 } 273 274 function go() { 275 # Init the json cycle for this save 276 metrics_json_start_array 277 278 containers=() 279 280 for i in $(seq 1 $MAX_NUM_CONTAINERS); do 281 containers+=($(random_name)) 282 sudo -E "${CTR_EXE}" run --rm --runtime=$CTR_RUNTIME $PAYLOAD ${containers[-1]} sh -c $PAYLOAD_ARGS 283 284 if [[ $PAYLOAD_SLEEP ]]; then 285 sleep $PAYLOAD_SLEEP 286 fi 287 288 grab_stats 289 290 # check if we have hit one of our limits and need to wrap up the tests 291 if (($(check_limits))); then 292 # Wrap up the results array 293 metrics_json_end_array "Results" 294 return 295 fi 296 done 297 298 # Wrap up the results array 299 metrics_json_end_array "Results" 300 } 301 302 303 function show_vars() 304 { 305 echo -e "\nEvironment variables:" 306 echo -e "\tName (default)" 307 echo -e "\t\tDescription" 308 echo -e "\tPAYLOAD (${PAYLOAD})" 309 echo -e "\t\tThe ctr image to run" 310 echo -e "\tPAYLOAD_ARGS (${PAYLOAD_ARGS})" 311 echo -e "\t\tAny extra arguments passed into the ctr 'run' command" 312 echo -e "\tPAYLOAD_SLEEP (${PAYLOAD_SLEEP})" 313 echo -e "\t\tSeconds to sleep between launch and measurement, to allow settling" 314 echo -e "\tMAX_NUM_CONTAINERS (${MAX_NUM_CONTAINERS})" 315 echo -e "\t\tThe maximum number of containers to run before terminating" 316 echo -e "\tMAX_MEMORY_CONSUMED (${MAX_MEMORY_CONSUMED})" 317 echo -e "\t\tThe maximum amount of memory to be consumed before terminating" 318 echo -e "\tMIN_MEMORY_FREE (${MIN_MEMORY_FREE})" 319 echo -e "\t\tThe path to the ctr binary (for 'smem' measurements)" 320 echo -e "\tDUMP_CACHES (${DUMP_CACHES})" 321 echo -e "\t\tA flag to note if the system caches should be dumped before capturing stats" 322 echo -e "\tTEST_NAME (${TEST_NAME})" 323 echo -e "\t\tCan be set to over-ride the default JSON results filename" 324 325 } 326 327 function help() 328 { 329 usage=$(cat << EOF 330 Usage: $0 [-h] [options] 331 Description: 332 Launch a series of workloads and take memory metric measurements after 333 each launch. 334 Options: 335 -h, Help page. 336 EOF 337 ) 338 echo "$usage" 339 show_vars 340 } 341 342 function main() { 343 344 local OPTIND 345 while getopts "h" opt;do 346 case ${opt} in 347 h) 348 help 349 exit 0; 350 ;; 351 esac 352 done 353 shift $((OPTIND-1)) 354 355 init 356 go 357 cleanup 358 } 359 360 main "$@"