github.com/dmaizel/tests@v0.0.0-20210728163746-cae6a2d9cee8/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  # This function measures the PSS average
    54  # memory about the child process of each
    55  # docker-containerd-shim instance in the
    56  # system.
    57  get_runc_pss_memory(){
    58          avg=0
    59          docker_shim="docker-containerd-shim"
    60          mem_amount=0
    61          count=0
    62  
    63          shim_instances=$(pgrep  -f "^$docker_shim")
    64          for shim in $shim_instances; do
    65                  child_pid="$(pgrep -P $shim)"
    66                  child_mem=$(sudo "$SMEM_BIN" -c "pid pss" | \
    67                                  awk "/^$child_pid / {print \$2}")
    68  
    69  		# Getting all individual results
    70  		echo $child_mem >> $MEM_TMP_FILE
    71  
    72                  if (( $child_mem > 0 ));then
    73                          mem_amount=$(( $child_mem + $mem_amount ))
    74                          (( count++ ))
    75                  fi
    76          done
    77  
    78          # Calculate average
    79          if (( $count > 0 )); then
    80                  avg=$(bc -l <<< "scale=2; $mem_amount / $count")
    81          fi
    82  
    83          echo "$avg"
    84  }
    85  
    86  get_runc_individual_memory() {
    87  	runc_process_result=$(cat $MEM_TMP_FILE | tr "\n" " " | sed -e 's/\s$//g' | sed 's/ /, /g')
    88  
    89  	# Verify runc process result
    90  	if [ -z "$runc_process_result" ];then
    91  		die "Runc process not found"
    92  	fi
    93  
    94  	read -r -a runc_values <<< "${runc_process_result}"
    95  
    96  	metrics_json_start_array
    97  
    98  	local json="$(cat << EOF
    99  	{
   100  		"runc individual results": [
   101  			$(for ((i=0;i<${NUM_CONTAINERS[@]};++i)); do
   102  				printf '%s\n\t\t\t' "${runc_values[i]}"
   103  			done)
   104  		]
   105  	}
   106  EOF
   107  )"
   108  	metrics_json_add_array_element "$json"
   109  	metrics_json_end_array "Raw results"
   110  }
   111  
   112  # This function measures the PSS average
   113  # memory of a process.
   114  get_pss_memory(){
   115  	ps="$1"
   116  	mem_amount=0
   117  	count=0
   118  	avg=0
   119  
   120  	if [ -z "$ps" ]; then
   121  		die "No argument to get_pss_memory()"
   122  	fi
   123  
   124  	# Save all the processes names
   125  	# This will be help us to retrieve raw information
   126  	echo $ps >> $PS_TMP_FILE
   127  
   128  	data=$(sudo "$SMEM_BIN" --no-header -P "^$ps" -c "pss" | sed 's/[[:space:]]//g')
   129  
   130  	# Save all the smem results
   131  	# This will help us to retrieve raw information
   132  	echo $data >> $MEM_TMP_FILE
   133  
   134  	for i in $data;do
   135  		if (( i > 0 ));then
   136  			mem_amount=$(( i + mem_amount ))
   137  			(( count++ ))
   138  		fi
   139  	done
   140  
   141  	if (( $count > 0 ));then
   142  		avg=$(bc -l <<< "scale=2; $mem_amount / $count")
   143  	fi
   144  
   145  	echo "$avg"
   146  }
   147  
   148  ppid() {
   149  	local pid
   150  	pid=$(ps -p "${1:-nopid}" -o ppid=)
   151  	echo "${pid//[[:blank:]]/}"
   152  }
   153  
   154  # This function measures the PSS average
   155  # memory of virtiofsd.
   156  # It is a special case of get_pss_memory,
   157  # virtiofsd forks itself so, smem sees the process
   158  # two times, this function sum both pss values:
   159  # pss_virtiofsd=pss_fork + pss_parent
   160  get_pss_memory_virtiofsd() {
   161  	mem_amount=0
   162  	count=0
   163  	avg=0
   164  
   165  	virtiofsd_path=${1:-}
   166  	if [ -z "${virtiofsd_path}" ]; then
   167  		die "virtiofsd_path not provided"
   168  	fi
   169  
   170  	echo "${virtiofsd_path}" >> $PS_TMP_FILE
   171  
   172  	virtiofsd_pids=$(ps aux | grep [v]irtiofsd | awk '{print $2}')
   173  	data=$(sudo smem --no-header -P "^${virtiofsd_path}" -c pid -c "pid pss")
   174  
   175  	for p in ${virtiofsd_pids}; do
   176  		parent_pid=$(ppid ${p})
   177  		cmd="$(cat /proc/${p}/cmdline | tr -d '\0')"
   178  		cmd_parent="$(cat /proc/${parent_pid}/cmdline | tr -d '\0')"
   179  		if [ "${cmd}" != "${cmd_parent}" ]; then
   180  			pss_parent=$(printf "%s" "${data}" | grep "\s^${p}" | awk '{print $2}')
   181  
   182  			fork=$(pgrep -P ${p})
   183  
   184  			pss_fork=$(printf "%s" "${data}" | grep "^\s*${fork}" | awk '{print $2}')
   185  			pss_process=$((pss_fork + pss_parent))
   186  
   187  			# Save all the smem results
   188  			# This will help us to retrieve raw information
   189  			echo "${pss_process}" >>$MEM_TMP_FILE
   190  
   191  			if ((pss_process > 0)); then
   192  				mem_amount=$((pss_process + mem_amount))
   193  				((count++))
   194  			fi
   195  		fi
   196  	done
   197  
   198  	if (( $count > 0 ));then
   199  		avg=$(bc -l <<< "scale=2; $mem_amount / $count")
   200  	fi
   201  	echo "${avg}"
   202  }
   203  
   204  get_individual_memory(){
   205  	# Getting all the individual container information
   206  	first_process_name=$(cat $PS_TMP_FILE | awk 'NR==1' | awk -F "/" '{print $NF}' | sed 's/[[:space:]]//g')
   207  	first_process_result=$(cat $MEM_TMP_FILE | awk 'NR==1' | sed 's/ /, /g')
   208  
   209  	second_process_name=$(cat $PS_TMP_FILE | awk 'NR==2' | awk -F "/" '{print $NF}' | sed 's/[[:space:]]//g')
   210  	second_process_result=$(cat $MEM_TMP_FILE | awk 'NR==2' | sed 's/ /, /g')
   211  
   212  	third_process_name=$(cat $PS_TMP_FILE | awk 'NR==3' | awk -F "/" '{print $NF}' | sed 's/[[:space:]]//g')
   213  	third_process_result=$(cat $MEM_TMP_FILE | awk 'NR==3' | sed 's/ /, /g')
   214  
   215  	read -r -a first_values <<< "${first_process_result}"
   216  	read -r -a second_values <<< "${second_process_result}"
   217  	read -r -a third_values <<< "${third_process_result}"
   218  
   219  	metrics_json_start_array
   220  
   221  	local json="$(cat << EOF
   222  	{
   223  		"$first_process_name memory": [
   224  			$(for ((i=0;i<${NUM_CONTAINERS[@]};++i)); do
   225  				[ -n "${first_values[i]}" ] &&
   226  				printf '%s\n\t\t\t' "${first_values[i]}"
   227  			done)
   228  		],
   229  		"$second_process_name memory": [
   230  			$(for ((i=0;i<${NUM_CONTAINERS[@]};++i)); do
   231  				[ -n "${second_values[i]}" ] &&
   232  				printf '%s\n\t\t\t' "${second_values[i]}"
   233  			done)
   234  		],
   235  		"$third_process_name memory": [
   236  			$(for ((i=0;i<${NUM_CONTAINERS[@]};++i)); do
   237  				[ -n "${third_values[i]}" ] &&
   238  				printf '%s\n\t\t\t' "${third_values[i]}"
   239  			done)
   240  		]
   241  	}
   242  EOF
   243  )"
   244  	metrics_json_add_array_element "$json"
   245  	metrics_json_end_array "Raw results"
   246  }
   247  
   248  # Try to work out the 'average memory footprint' of a container.
   249  get_docker_memory_usage(){
   250  	hypervisor_mem=0
   251  	virtiofsd_mem=0
   252  	shim_mem=0
   253  	memory_usage=0
   254  
   255  	containers=()
   256  
   257  	for ((i=1; i<= NUM_CONTAINERS; i++)); do
   258  		containers+=($(random_name))
   259  		${CTR_EXE} run --runtime "${CTR_RUNTIME}" -d ${IMAGE}  ${containers[-1]} ${CMD}
   260  	done
   261  
   262  	if [ "$AUTO_MODE" == "auto" ]; then
   263  		if (( ksm_on != 1 )); then
   264  			die "KSM not enabled, cannot use auto mode"
   265  		fi
   266  
   267  		echo "Entering KSM settle auto detect mode..."
   268  		wait_ksm_settle $WAIT_TIME
   269  	else
   270  		# If KSM is enabled, then you normally want to sleep long enough to
   271  		# let it do its work and for the numbers to 'settle'.
   272  		echo "napping $WAIT_TIME s"
   273  		sleep "$WAIT_TIME"
   274  	fi
   275  
   276  	metrics_json_start_array
   277  	# Check the runtime in order in order to determine which process will
   278  	# be measured about PSS
   279  	if [ "$RUNTIME" == "runc" ]; then
   280  		runc_workload_mem="$(get_runc_pss_memory)"
   281  		memory_usage="$runc_workload_mem"
   282  
   283  	local json="$(cat << EOF
   284  	{
   285  		"average": {
   286  			"Result": $memory_usage,
   287  			"Units" : "KB"
   288  		},
   289  		"runc": {
   290  			"Result": $runc_workload_mem,
   291  			"Units" : "KB"
   292  		}
   293  	}
   294  EOF
   295  )"
   296  
   297  	elif [ "$RUNTIME" == "kata-qemu" ] || [ "$RUNTIME" == "kata-fc" ] || [ "$RUNTIME" == "kata-runtime" ]; then
   298  		# Get PSS memory of VM runtime components.
   299  		# And check that the smem search has found the process - we get a "0"
   300  		#  back if that procedure fails (such as if a process has changed its name
   301  		#  or is not running when expected to be so)
   302  		# As an added bonus - this script must be run as root.
   303  		# Now if you do not have enough rights
   304  		#  the smem failure to read the stats will also be trapped.
   305  
   306  		hypervisor_mem="$(get_pss_memory "$HYPERVISOR_PATH")"
   307  		if [ "$hypervisor_mem" == "0" ]; then
   308  			die "Failed to find PSS for $HYPERVISOR_PATH"
   309  		fi
   310  
   311  		virtiofsd_mem="$(get_pss_memory_virtiofsd "$VIRTIOFSD_PATH")"
   312  		if [ "$virtiofsd_mem" == "0" ]; then
   313  			echo >&2 "WARNING: Failed to find PSS for $VIRTIOFSD_PATH"
   314  		fi
   315  		shim_mem="$(get_pss_memory "$SHIM_PATH")"
   316  		if [ "$shim_mem" == "0" ]; then
   317  			die "Failed to find PSS for $SHIM_PATH"
   318  		fi
   319  
   320  		mem_usage="$(bc -l <<< "scale=2; $hypervisor_mem +$virtiofsd_mem + $shim_mem")"
   321  		memory_usage="$mem_usage"
   322  
   323  	local json="$(cat << EOF
   324  	{
   325  		"average": {
   326  			"Result": $mem_usage,
   327  			"Units" : "KB"
   328  		},
   329  		"qemus": {
   330  			"Result": $hypervisor_mem,
   331  			"Units" : "KB"
   332  		},
   333  		"virtiofsds": {
   334  			"Result": $virtiofsd_mem,
   335  			"Units" : "KB"
   336  		},
   337  		"shims": {
   338  			"Result": $shim_mem,
   339  			"Units" : "KB"
   340  		}
   341  	}
   342  EOF
   343  )"
   344  	else
   345  		die "Unknown runtime: $RUNTIME"
   346  	fi
   347  
   348  	metrics_json_add_array_element "$json"
   349  	metrics_json_end_array "Results"
   350  
   351  	${CTR_EXE} t rm -f ${containers[@]}
   352  	${CTR_EXE} c rm ${containers[@]}
   353  }
   354  
   355  save_config(){
   356  	metrics_json_start_array
   357  
   358  	local json="$(cat << EOF
   359  	{
   360  		"containers": $NUM_CONTAINERS,
   361  		"ksm": $ksm_on,
   362  		"auto": "$AUTO_MODE",
   363  		"waittime": $WAIT_TIME,
   364  		"image": "$IMAGE",
   365  		"command": "$CMD"
   366  	}
   367  EOF
   368  
   369  )"
   370  	metrics_json_add_array_element "$json"
   371  	metrics_json_end_array "Config"
   372  }
   373  
   374  main(){
   375  	# Verify enough arguments
   376  	if [ $# != 2 ] && [ $# != 3 ];then
   377  		echo >&2 "error: Not enough arguments [$@]"
   378  		help
   379  		exit 1
   380  	fi
   381  
   382  	#Check for KSM before reporting test name, as it can modify it
   383  	check_for_ksm
   384  
   385  	init_env
   386  
   387  	check_cmds "${SMEM_BIN}" bc
   388  	check_images "$IMAGE"
   389  
   390  	metrics_json_init
   391  	save_config
   392  	get_docker_memory_usage
   393  
   394  	if [ "$RUNTIME" == "runc" ]; then
   395  		get_runc_individual_memory
   396  	elif [ "$RUNTIME" == "cor" ] || [ "$RUNTIME" == "cc-runtime" ] || [ "$RUNTIME" == "kata-runtime" ]; then
   397  		get_individual_memory
   398  	fi
   399  
   400  	metrics_json_save
   401  }
   402  
   403  main "$@"