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 "$@"