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