github.com/kata-containers/tests@v0.0.0-20240307153542-772105b56064/metrics/density/memory_usage.sh (about)

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