YABS/yabs.sh
Michael Hohl 9b9c8b7ac4
Use Telia for Helsinki, FI instead of Hostkey
HOSTKEY's iperf3 server seems to be limited to 1G. Another option in Helsinki would be Telia and they seem to be able to reach 10G without issues.
2024-03-18 09:40:43 +01:00

1033 lines
44 KiB
Bash

#!/bin/bash
# Yet Another Bench Script by Mason Rowe
# Initial Oct 2019; Last update Mar 2024
# Disclaimer: This project is a work in progress. Any errors or suggestions should be
# relayed to me via the GitHub project page linked below.
#
# Purpose: The purpose of this script is to quickly gauge the performance of a Linux-
# based server by benchmarking network performance via iperf3, CPU and
# overall system performance via Geekbench 4/5, and random disk
# performance via fio. The script is designed to not require any dependencies
# - either compiled or installed - nor admin privileges to run.
YABS_VERSION="v2024-03-05"
echo -e '# ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #'
echo -e '# Yet-Another-Bench-Script #'
echo -e '# '$YABS_VERSION' #'
echo -e '# https://github.com/masonr/yet-another-bench-script #'
echo -e '# ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #'
echo -e
date
TIME_START=$(date '+%Y%m%d-%H%M%S')
YABS_START_TIME=$(date +%s)
# override locale to eliminate parsing errors (i.e. using commas as delimiters rather than periods)
if locale -a 2>/dev/null | grep ^C$ > /dev/null; then
# locale "C" installed
export LC_ALL=C
else
# locale "C" not installed, display warning
echo -e "\nWarning: locale 'C' not detected. Test outputs may not be parsed correctly."
fi
# determine architecture of host
ARCH=$(uname -m)
if [[ $ARCH = *x86_64* ]]; then
# host is running a 64-bit kernel
ARCH="x64"
elif [[ $ARCH = *i?86* ]]; then
# host is running a 32-bit kernel
ARCH="x86"
elif [[ $ARCH = *aarch* || $ARCH = *arm* ]]; then
KERNEL_BIT=$(getconf LONG_BIT)
if [[ $KERNEL_BIT = *64* ]]; then
# host is running an ARM 64-bit kernel
ARCH="aarch64"
else
# host is running an ARM 32-bit kernel
ARCH="arm"
fi
echo -e "\nARM compatibility is considered *experimental*"
else
# host is running a non-supported kernel
echo -e "Architecture not supported by YABS."
exit 1
fi
# flags to skip certain performance tests
unset PREFER_BIN SKIP_FIO SKIP_IPERF SKIP_GEEKBENCH SKIP_NET PRINT_HELP REDUCE_NET GEEKBENCH_4 GEEKBENCH_5 GEEKBENCH_6 DD_FALLBACK IPERF_DL_FAIL JSON JSON_SEND JSON_RESULT JSON_FILE
GEEKBENCH_6="True" # gb6 test enabled by default
# get any arguments that were passed to the script and set the associated skip flags (if applicable)
while getopts 'bfdignhr4596jw:s:' flag; do
case "${flag}" in
b) PREFER_BIN="True" ;;
f) SKIP_FIO="True" ;;
d) SKIP_FIO="True" ;;
i) SKIP_IPERF="True" ;;
g) SKIP_GEEKBENCH="True" ;;
n) SKIP_NET="True" ;;
h) PRINT_HELP="True" ;;
r) REDUCE_NET="True" ;;
4) GEEKBENCH_4="True" && unset GEEKBENCH_6 ;;
5) GEEKBENCH_5="True" && unset GEEKBENCH_6 ;;
9) GEEKBENCH_4="True" && GEEKBENCH_5="True" && unset GEEKBENCH_6 ;;
6) GEEKBENCH_6="True" ;;
j) JSON+="j" ;;
w) JSON+="w" && JSON_FILE=${OPTARG} ;;
s) JSON+="s" && JSON_SEND=${OPTARG} ;;
*) exit 1 ;;
esac
done
# check for local fio/iperf installs
command -v fio >/dev/null 2>&1 && LOCAL_FIO=true || unset LOCAL_FIO
command -v iperf3 >/dev/null 2>&1 && LOCAL_IPERF=true || unset LOCAL_IPERF
# check for ping
command -v ping >/dev/null 2>&1 && LOCAL_PING=true || unset LOCAL_PING
# check for curl/wget
command -v curl >/dev/null 2>&1 && LOCAL_CURL=true || unset LOCAL_CURL
# test if the host has IPv4/IPv6 connectivity
[[ ! -z $LOCAL_CURL ]] && IP_CHECK_CMD="curl -s -m 4" || IP_CHECK_CMD="wget -qO- -T 4"
IPV4_CHECK=$( (ping -4 -c 1 -W 4 ipv4.google.com >/dev/null 2>&1 && echo true) || $IP_CHECK_CMD -4 icanhazip.com 2> /dev/null)
IPV6_CHECK=$( (ping -6 -c 1 -W 4 ipv6.google.com >/dev/null 2>&1 && echo true) || $IP_CHECK_CMD -6 icanhazip.com 2> /dev/null)
if [[ -z "$IPV4_CHECK" && -z "$IPV6_CHECK" ]]; then
echo -e
echo -e "Warning: Both IPv4 AND IPv6 connectivity were not detected. Check for DNS issues..."
fi
# print help and exit script, if help flag was passed
if [ ! -z "$PRINT_HELP" ]; then
echo -e
echo -e "Usage: ./yabs.sh [-flags]"
echo -e " curl -sL yabs.sh | bash"
echo -e " curl -sL yabs.sh | bash -s -- -flags"
echo -e " wget -qO- yabs.sh | bash"
echo -e " wget -qO- yabs.sh | bash -s -- -flags"
echo -e
echo -e "Flags:"
echo -e " -b : prefer pre-compiled binaries from repo over local packages"
echo -e " -f/d : skips the fio disk benchmark test"
echo -e " -i : skips the iperf network test"
echo -e " -g : skips the geekbench performance test"
echo -e " -n : skips the network information lookup and print out"
echo -e " -h : prints this lovely message, shows any flags you passed,"
echo -e " shows if fio/iperf3 local packages have been detected,"
echo -e " then exits"
echo -e " -r : reduce number of iperf3 network locations (to only three)"
echo -e " to lessen bandwidth usage"
echo -e " -4 : use geekbench 4 instead of geekbench 6"
echo -e " -5 : use geekbench 5 instead of geekbench 6"
echo -e " -9 : use both geekbench 4 AND geekbench 5 instead of geekbench 6"
echo -e " -6 : user geekbench 6 in addition to 4 and/or 5 (only needed if -4, -5, or -9 are set; -6 must come last)"
echo -e " -j : print jsonified YABS results at conclusion of test"
echo -e " -w <filename> : write jsonified YABS results to disk using file name provided"
echo -e " -s <url> : send jsonified YABS results to URL"
echo -e
echo -e "Detected Arch: $ARCH"
echo -e
echo -e "Detected Flags:"
[[ ! -z $PREFER_BIN ]] && echo -e " -b, force using precompiled binaries from repo"
[[ ! -z $SKIP_FIO ]] && echo -e " -f/d, skipping fio disk benchmark test"
[[ ! -z $SKIP_IPERF ]] && echo -e " -i, skipping iperf network test"
[[ ! -z $SKIP_GEEKBENCH ]] && echo -e " -g, skipping geekbench test"
[[ ! -z $SKIP_NET ]] && echo -e " -n, skipping network info lookup and print out"
[[ ! -z $REDUCE_NET ]] && echo -e " -r, using reduced (3) iperf3 locations"
[[ ! -z $GEEKBENCH_4 ]] && echo -e " running geekbench 4"
[[ ! -z $GEEKBENCH_5 ]] && echo -e " running geekbench 5"
[[ ! -z $GEEKBENCH_6 ]] && echo -e " running geekbench 6"
echo -e
echo -e "Local Binary Check:"
[[ -z $LOCAL_FIO ]] && echo -e " fio not detected, will download precompiled binary" ||
[[ -z $PREFER_BIN ]] && echo -e " fio detected, using local package" ||
echo -e " fio detected, but using precompiled binary instead"
[[ -z $LOCAL_IPERF ]] && echo -e " iperf3 not detected, will download precompiled binary" ||
[[ -z $PREFER_BIN ]] && echo -e " iperf3 detected, using local package" ||
echo -e " iperf3 detected, but using precompiled binary instead"
echo -e
echo -e "Detected Connectivity:"
[[ ! -z $IPV4_CHECK ]] && echo -e " IPv4 connected" ||
echo -e " IPv4 not connected"
[[ ! -z $IPV6_CHECK ]] && echo -e " IPv6 connected" ||
echo -e " IPv6 not connected"
echo -e
echo -e "JSON Options:"
[[ -z $JSON ]] && echo -e " none"
[[ $JSON = *j* ]] && echo -e " printing json to screen after test"
[[ $JSON = *w* ]] && echo -e " writing json to file ($JSON_FILE) after test"
[[ $JSON = *s* ]] && echo -e " sharing json YABS results to $JSON_SEND"
echo -e
echo -e "Exiting..."
exit 0
fi
# format_size
# Purpose: Formats raw disk and memory sizes from kibibytes (KiB) to largest unit
# Parameters:
# 1. RAW - the raw memory size (RAM/Swap) in kibibytes
# Returns:
# Formatted memory size in KiB, MiB, GiB, or TiB
function format_size {
RAW=$1 # mem size in KiB
RESULT=$RAW
local DENOM=1
local UNIT="KiB"
# ensure the raw value is a number, otherwise return blank
re='^[0-9]+$'
if ! [[ $RAW =~ $re ]] ; then
echo ""
return 0
fi
if [ "$RAW" -ge 1073741824 ]; then
DENOM=1073741824
UNIT="TiB"
elif [ "$RAW" -ge 1048576 ]; then
DENOM=1048576
UNIT="GiB"
elif [ "$RAW" -ge 1024 ]; then
DENOM=1024
UNIT="MiB"
fi
# divide the raw result to get the corresponding formatted result (based on determined unit)
RESULT=$(awk -v a="$RESULT" -v b="$DENOM" 'BEGIN { print a / b }')
# shorten the formatted result to two decimal places (i.e. x.x)
RESULT=$(echo $RESULT | awk -F. '{ printf "%0.1f",$1"."substr($2,1,2) }')
# concat formatted result value with units and return result
RESULT="$RESULT $UNIT"
echo $RESULT
}
# gather basic system information (inc. CPU, AES-NI/virt status, RAM + swap + disk size)
echo -e
echo -e "Basic System Information:"
echo -e "---------------------------------"
UPTIME=$(uptime | awk -F'( |,|:)+' '{d=h=m=0; if ($7=="min") m=$6; else {if ($7~/^day/) {d=$6;h=$8;m=$9} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours,",m+0,"minutes"}')
echo -e "Uptime : $UPTIME"
# check for local lscpu installs
command -v lscpu >/dev/null 2>&1 && LOCAL_LSCPU=true || unset LOCAL_LSCPU
if [[ $ARCH = *aarch64* || $ARCH = *arm* ]] && [[ ! -z $LOCAL_LSCPU ]]; then
CPU_PROC=$(lscpu | grep "Model name" | sed 's/Model name: *//g')
else
CPU_PROC=$(awk -F: '/model name/ {name=$2} END {print name}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//')
fi
echo -e "Processor : $CPU_PROC"
if [[ $ARCH = *aarch64* || $ARCH = *arm* ]] && [[ ! -z $LOCAL_LSCPU ]]; then
CPU_CORES=$(lscpu | grep "^[[:blank:]]*CPU(s):" | sed 's/CPU(s): *//g')
CPU_FREQ=$(lscpu | grep "CPU max MHz" | sed 's/CPU max MHz: *//g')
[[ -z "$CPU_FREQ" ]] && CPU_FREQ="???"
CPU_FREQ="${CPU_FREQ} MHz"
else
CPU_CORES=$(awk -F: '/model name/ {core++} END {print core}' /proc/cpuinfo)
CPU_FREQ=$(awk -F: ' /cpu MHz/ {freq=$2} END {print freq " MHz"}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//')
fi
echo -e "CPU cores : $CPU_CORES @ $CPU_FREQ"
CPU_AES=$(cat /proc/cpuinfo | grep aes)
[[ -z "$CPU_AES" ]] && CPU_AES="\xE2\x9D\x8C Disabled" || CPU_AES="\xE2\x9C\x94 Enabled"
echo -e "AES-NI : $CPU_AES"
CPU_VIRT=$(cat /proc/cpuinfo | grep 'vmx\|svm')
[[ -z "$CPU_VIRT" ]] && CPU_VIRT="\xE2\x9D\x8C Disabled" || CPU_VIRT="\xE2\x9C\x94 Enabled"
echo -e "VM-x/AMD-V : $CPU_VIRT"
TOTAL_RAM_RAW=$(free | awk 'NR==2 {print $2}')
TOTAL_RAM=$(format_size $TOTAL_RAM_RAW)
echo -e "RAM : $TOTAL_RAM"
TOTAL_SWAP_RAW=$(free | grep Swap | awk '{ print $2 }')
TOTAL_SWAP=$(format_size $TOTAL_SWAP_RAW)
echo -e "Swap : $TOTAL_SWAP"
# total disk size is calculated by adding all partitions of the types listed below (after the -t flags)
TOTAL_DISK_RAW=$(df -t simfs -t ext2 -t ext3 -t ext4 -t btrfs -t xfs -t vfat -t ntfs -t swap --total 2>/dev/null | grep total | awk '{ print $2 }')
TOTAL_DISK=$(format_size $TOTAL_DISK_RAW)
echo -e "Disk : $TOTAL_DISK"
DISTRO=$(grep 'PRETTY_NAME' /etc/os-release | cut -d '"' -f 2 )
echo -e "Distro : $DISTRO"
KERNEL=$(uname -r)
echo -e "Kernel : $KERNEL"
VIRT=$(systemd-detect-virt 2>/dev/null)
VIRT=${VIRT^^} || VIRT="UNKNOWN"
echo -e "VM Type : $VIRT"
[[ -z "$IPV4_CHECK" ]] && ONLINE="\xE2\x9D\x8C Offline / " || ONLINE="\xE2\x9C\x94 Online / "
[[ -z "$IPV6_CHECK" ]] && ONLINE+="\xE2\x9D\x8C Offline" || ONLINE+="\xE2\x9C\x94 Online"
echo -e "IPv4/IPv6 : $ONLINE"
# Function to get information from IP Address using ip-api.com free API
function ip_info() {
# check for curl vs wget
[[ ! -z $LOCAL_CURL ]] && DL_CMD="curl -s" || DL_CMD="wget -qO-"
local ip6me_resp="$($DL_CMD http://ip6.me/api/)"
local net_type="$(echo $ip6me_resp | cut -d, -f1)"
local net_ip="$(echo $ip6me_resp | cut -d, -f2)"
local response=$($DL_CMD http://ip-api.com/json/$net_ip)
# if no response, skip output
if [[ -z $response ]]; then
return
fi
local country=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^country/ {print $2}' | head -1 | sed 's/^"\(.*\)"$/\1/')
local region=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^regionName/ {print $2}' | sed 's/^"\(.*\)"$/\1/')
local region_code=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^region/ {print $2}' | head -1 | sed 's/^"\(.*\)"$/\1/')
local city=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^city/ {print $2}' | sed 's/^"\(.*\)"$/\1/')
local isp=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^isp/ {print $2}' | sed 's/^"\(.*\)"$/\1/')
local org=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^org/ {print $2}' | sed 's/^"\(.*\)"$/\1/')
local as=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^as/ {print $2}' | sed 's/^"\(.*\)"$/\1/')
echo
echo "$net_type Network Information:"
echo "---------------------------------"
if [[ -n "$isp" ]]; then
echo "ISP : $isp"
else
echo "ISP : Unknown"
fi
if [[ -n "$as" ]]; then
echo "ASN : $as"
else
echo "ASN : Unknown"
fi
if [[ -n "$org" ]]; then
echo "Host : $org"
fi
if [[ -n "$city" && -n "$region" ]]; then
echo "Location : $city, $region ($region_code)"
fi
if [[ -n "$country" ]]; then
echo "Country : $country"
fi
[[ ! -z $JSON ]] && JSON_RESULT+=',"ip_info":{"protocol":"'$net_type'","isp":"'$isp'","asn":"'$as'","org":"'$org'","city":"'$city'","region":"'$region'","region_code":"'$region_code'","country":"'$country'"}'
}
if [ ! -z $JSON ]; then
UPTIME_S=$(awk '{print $1}' /proc/uptime)
IPV4=$([ ! -z $IPV4_CHECK ] && echo "true" || echo "false")
IPV6=$([ ! -z $IPV6_CHECK ] && echo "true" || echo "false")
AES=$([[ "$CPU_AES" = *Enabled* ]] && echo "true" || echo "false")
CPU_VIRT_BOOL=$([[ "$CPU_VIRT" = *Enabled* ]] && echo "true" || echo "false")
JSON_RESULT='{"version":"'$YABS_VERSION'","time":"'$TIME_START'","os":{"arch":"'$ARCH'","distro":"'$DISTRO'","kernel":"'$KERNEL'",'
JSON_RESULT+='"uptime":'$UPTIME_S',"vm":"'$VIRT'"},"net":{"ipv4":'$IPV4',"ipv6":'$IPV6'},"cpu":{"model":"'$CPU_PROC'","cores":'$CPU_CORES','
JSON_RESULT+='"freq":"'$CPU_FREQ'","aes":'$AES',"virt":'$CPU_VIRT_BOOL'},"mem":{"ram":'$TOTAL_RAM_RAW',"ram_units":"KiB","swap":'$TOTAL_SWAP_RAW',"swap_units":"KiB","disk":'$TOTAL_DISK_RAW',"disk_units":"KB"}'
fi
if [ -z $SKIP_NET ]; then
ip_info
fi
# create a directory in the same location that the script is being run to temporarily store YABS-related files
DATE=$(date -Iseconds | sed -e "s/:/_/g")
YABS_PATH=./$DATE
touch "$DATE.test" 2> /dev/null
# test if the user has write permissions in the current directory and exit if not
if [ ! -f "$DATE.test" ]; then
echo -e
echo -e "You do not have write permission in this directory. Switch to an owned directory and re-run the script.\nExiting..."
exit 1
fi
rm "$DATE.test"
mkdir -p "$YABS_PATH"
# trap CTRL+C signals to exit script cleanly
trap catch_abort INT
# catch_abort
# Purpose: This method will catch CTRL+C signals in order to exit the script cleanly and remove
# yabs-related files.
function catch_abort() {
echo -e "\n** Aborting YABS. Cleaning up files...\n"
rm -rf "$YABS_PATH"
unset LC_ALL
exit 0
}
# format_speed
# Purpose: This method is a convenience function to format the output of the fio disk tests which
# always returns a result in KB/s. If result is >= 1 GB/s, use GB/s. If result is < 1 GB/s
# and >= 1 MB/s, then use MB/s. Otherwise, use KB/s.
# Parameters:
# 1. RAW - the raw disk speed result (in KB/s)
# Returns:
# Formatted disk speed in GB/s, MB/s, or KB/s
function format_speed {
RAW=$1 # disk speed in KB/s
RESULT=$RAW
local DENOM=1
local UNIT="KB/s"
# ensure raw value is not null, if it is, return blank
if [ -z "$RAW" ]; then
echo ""
return 0
fi
# check if disk speed >= 1 GB/s
if [ "$RAW" -ge 1000000 ]; then
DENOM=1000000
UNIT="GB/s"
# check if disk speed < 1 GB/s && >= 1 MB/s
elif [ "$RAW" -ge 1000 ]; then
DENOM=1000
UNIT="MB/s"
fi
# divide the raw result to get the corresponding formatted result (based on determined unit)
RESULT=$(awk -v a="$RESULT" -v b="$DENOM" 'BEGIN { print a / b }')
# shorten the formatted result to two decimal places (i.e. x.xx)
RESULT=$(echo $RESULT | awk -F. '{ printf "%0.2f",$1"."substr($2,1,2) }')
# concat formatted result value with units and return result
RESULT="$RESULT $UNIT"
echo $RESULT
}
# format_iops
# Purpose: This method is a convenience function to format the output of the raw IOPS result
# Parameters:
# 1. RAW - the raw IOPS result
# Returns:
# Formatted IOPS (i.e. 8, 123, 1.7k, 275.9k, etc.)
function format_iops {
RAW=$1 # iops
RESULT=$RAW
# ensure raw value is not null, if it is, return blank
if [ -z "$RAW" ]; then
echo ""
return 0
fi
# check if IOPS speed > 1k
if [ "$RAW" -ge 1000 ]; then
# divide the raw result by 1k
RESULT=$(awk -v a="$RESULT" 'BEGIN { print a / 1000 }')
# shorten the formatted result to one decimal place (i.e. x.x)
RESULT=$(echo $RESULT | awk -F. '{ printf "%0.1f",$1"."substr($2,1,1) }')
RESULT="$RESULT"k
fi
echo $RESULT
}
# disk_test
# Purpose: This method is designed to test the disk performance of the host using the partition that the
# script is being run from using fio random read/write speed tests.
# Parameters:
# - (none)
function disk_test {
if [[ "$ARCH" = "aarch64" || "$ARCH" = "arm" ]]; then
FIO_SIZE=512M
else
FIO_SIZE=2G
fi
# run a quick test to generate the fio test file to be used by the actual tests
echo -en "Generating fio test file..."
$FIO_CMD --name=setup --ioengine=libaio --rw=read --bs=64k --iodepth=64 --numjobs=2 --size=$FIO_SIZE --runtime=1 --gtod_reduce=1 --filename="$DISK_PATH/test.fio" --direct=1 --minimal &> /dev/null
echo -en "\r\033[0K"
# get array of block sizes to evaluate
BLOCK_SIZES=("$@")
for BS in "${BLOCK_SIZES[@]}"; do
# run rand read/write mixed fio test with block size = $BS
echo -en "Running fio random mixed R+W disk test with $BS block size..."
DISK_TEST=$(timeout 35 $FIO_CMD --name=rand_rw_$BS --ioengine=libaio --rw=randrw --rwmixread=50 --bs=$BS --iodepth=64 --numjobs=2 --size=$FIO_SIZE --runtime=30 --gtod_reduce=1 --direct=1 --filename="$DISK_PATH/test.fio" --group_reporting --minimal 2> /dev/null | grep rand_rw_$BS)
DISK_IOPS_R=$(echo $DISK_TEST | awk -F';' '{print $8}')
DISK_IOPS_W=$(echo $DISK_TEST | awk -F';' '{print $49}')
DISK_IOPS=$(awk -v a="$DISK_IOPS_R" -v b="$DISK_IOPS_W" 'BEGIN { print a + b }')
DISK_TEST_R=$(echo $DISK_TEST | awk -F';' '{print $7}')
DISK_TEST_W=$(echo $DISK_TEST | awk -F';' '{print $48}')
DISK_TEST=$(awk -v a="$DISK_TEST_R" -v b="$DISK_TEST_W" 'BEGIN { print a + b }')
DISK_RESULTS_RAW+=( "$DISK_TEST" "$DISK_TEST_R" "$DISK_TEST_W" "$DISK_IOPS" "$DISK_IOPS_R" "$DISK_IOPS_W" )
DISK_IOPS=$(format_iops $DISK_IOPS)
DISK_IOPS_R=$(format_iops $DISK_IOPS_R)
DISK_IOPS_W=$(format_iops $DISK_IOPS_W)
DISK_TEST=$(format_speed $DISK_TEST)
DISK_TEST_R=$(format_speed $DISK_TEST_R)
DISK_TEST_W=$(format_speed $DISK_TEST_W)
DISK_RESULTS+=( "$DISK_TEST" "$DISK_TEST_R" "$DISK_TEST_W" "$DISK_IOPS" "$DISK_IOPS_R" "$DISK_IOPS_W" )
echo -en "\r\033[0K"
done
}
# dd_test
# Purpose: This method is invoked if the fio disk test failed. dd sequential speed tests are
# not indiciative or real-world results, however, some form of disk speed measure
# is better than nothing.
# Parameters:
# - (none)
function dd_test {
I=0
DISK_WRITE_TEST_RES=()
DISK_READ_TEST_RES=()
DISK_WRITE_TEST_AVG=0
DISK_READ_TEST_AVG=0
# run the disk speed tests (write and read) thrice over
while [ $I -lt 3 ]
do
# write test using dd, "direct" flag is used to test direct I/O for data being stored to disk
DISK_WRITE_TEST=$(dd if=/dev/zero of="$DISK_PATH/$DATE.test" bs=64k count=16k oflag=direct |& grep copied | awk '{ print $(NF-1) " " $(NF)}')
VAL=$(echo $DISK_WRITE_TEST | cut -d " " -f 1)
[[ "$DISK_WRITE_TEST" == *"GB"* ]] && VAL=$(awk -v a="$VAL" 'BEGIN { print a * 1000 }')
DISK_WRITE_TEST_RES+=( "$DISK_WRITE_TEST" )
DISK_WRITE_TEST_AVG=$(awk -v a="$DISK_WRITE_TEST_AVG" -v b="$VAL" 'BEGIN { print a + b }')
# read test using dd using the 1G file written during the write test
DISK_READ_TEST=$(dd if="$DISK_PATH/$DATE.test" of=/dev/null bs=8k |& grep copied | awk '{ print $(NF-1) " " $(NF)}')
VAL=$(echo $DISK_READ_TEST | cut -d " " -f 1)
[[ "$DISK_READ_TEST" == *"GB"* ]] && VAL=$(awk -v a="$VAL" 'BEGIN { print a * 1000 }')
DISK_READ_TEST_RES+=( "$DISK_READ_TEST" )
DISK_READ_TEST_AVG=$(awk -v a="$DISK_READ_TEST_AVG" -v b="$VAL" 'BEGIN { print a + b }')
I=$(( $I + 1 ))
done
# calculate the write and read speed averages using the results from the three runs
DISK_WRITE_TEST_AVG=$(awk -v a="$DISK_WRITE_TEST_AVG" 'BEGIN { print a / 3 }')
DISK_READ_TEST_AVG=$(awk -v a="$DISK_READ_TEST_AVG" 'BEGIN { print a / 3 }')
}
# check if disk performance is being tested and the host has required space (2G)
AVAIL_SPACE=$(df -k . | awk 'NR==2{print $4}')
if [[ -z "$SKIP_FIO" && "$AVAIL_SPACE" -lt 2097152 && "$ARCH" != "aarch64" && "$ARCH" != "arm" ]]; then # 2GB = 2097152KB
echo -e "\nLess than 2GB of space available. Skipping disk test..."
elif [[ -z "$SKIP_FIO" && "$AVAIL_SPACE" -lt 524288 && ("$ARCH" = "aarch64" || "$ARCH" = "arm") ]]; then # 512MB = 524288KB
echo -e "\nLess than 512MB of space available. Skipping disk test..."
# if the skip disk flag was set, skip the disk performance test, otherwise test disk performance
elif [ -z "$SKIP_FIO" ]; then
# Perform ZFS filesystem detection and determine if we have enough free space according to spa_asize_inflation
ZFSCHECK="/sys/module/zfs/parameters/spa_asize_inflation"
if [[ -f "$ZFSCHECK" ]];then
mul_spa=$((($(cat /sys/module/zfs/parameters/spa_asize_inflation)*2)))
warning=0
poss=()
for pathls in $(df -Th | awk '{print $7}' | tail -n +2)
do
if [[ "${PWD##$pathls}" != "${PWD}" ]]; then
poss+=("$pathls")
fi
done
long=""
m=-1
for x in ${poss[@]}
do
if [ ${#x} -gt $m ];then
m=${#x}
long=$x
fi
done
size_b=$(df -Th | grep -w $long | grep -i zfs | awk '{print $5}' | tail -c -2 | head -c 1)
free_space=$(df -Th | grep -w $long | grep -i zfs | awk '{print $5}' | head -c -2)
if [[ $size_b == 'T' ]]; then
free_space=$(awk "BEGIN {print int($free_space * 1024)}")
size_b='G'
fi
if [[ $(df -Th | grep -w $long) == *"zfs"* ]];then
if [[ $size_b == 'G' ]]; then
if ((free_space < mul_spa)); then
warning=1
fi
else
warning=1
fi
fi
if [[ $warning -eq 1 ]];then
echo -en "\nWarning! You are running YABS on a ZFS Filesystem and your disk space is too low for the fio test. Your test results will be inaccurate. You need at least $mul_spa GB free in order to complete this test accurately. For more information, please see https://github.com/masonr/yet-another-bench-script/issues/13\n"
fi
fi
echo -en "\nPreparing system for disk tests..."
# create temp directory to store disk write/read test files
DISK_PATH=$YABS_PATH/disk
mkdir -p "$DISK_PATH"
if [[ -z "$PREFER_BIN" && ! -z "$LOCAL_FIO" ]]; then # local fio has been detected, use instead of pre-compiled binary
FIO_CMD=fio
else
# download fio binary
if [[ ! -z $LOCAL_CURL ]]; then
curl -s --connect-timeout 5 --retry 5 --retry-delay 0 https://raw.githubusercontent.com/masonr/yet-another-bench-script/master/bin/fio/fio_$ARCH -o "$DISK_PATH/fio"
else
wget -q -T 5 -t 5 -w 0 https://raw.githubusercontent.com/masonr/yet-another-bench-script/master/bin/fio/fio_$ARCH -O "$DISK_PATH/fio"
fi
if [ ! -f "$DISK_PATH/fio" ]; then # ensure fio binary download successfully
echo -en "\r\033[0K"
echo -e "Fio binary download failed. Running dd test as fallback...."
DD_FALLBACK=True
else
chmod +x "$DISK_PATH/fio"
FIO_CMD=$DISK_PATH/fio
fi
fi
if [ -z "$DD_FALLBACK" ]; then # if not falling back on dd tests, run fio test
echo -en "\r\033[0K"
# init global array to store disk performance values
declare -a DISK_RESULTS DISK_RESULTS_RAW
# disk block sizes to evaluate
BLOCK_SIZES=( "4k" "64k" "512k" "1m" )
# execute disk performance test
disk_test "${BLOCK_SIZES[@]}"
fi
if [[ ! -z "$DD_FALLBACK" || ${#DISK_RESULTS[@]} -eq 0 ]]; then # fio download failed or test was killed or returned an error, run dd test instead
if [ -z "$DD_FALLBACK" ]; then # print error notice if ended up here due to fio error
echo -e "fio disk speed tests failed. Run manually to determine cause.\nRunning dd test as fallback..."
fi
dd_test
# format the speed averages by converting to GB/s if > 1000 MB/s
if [ $(echo $DISK_WRITE_TEST_AVG | cut -d "." -f 1) -ge 1000 ]; then
DISK_WRITE_TEST_AVG=$(awk -v a="$DISK_WRITE_TEST_AVG" 'BEGIN { print a / 1000 }')
DISK_WRITE_TEST_UNIT="GB/s"
else
DISK_WRITE_TEST_UNIT="MB/s"
fi
if [ $(echo $DISK_READ_TEST_AVG | cut -d "." -f 1) -ge 1000 ]; then
DISK_READ_TEST_AVG=$(awk -v a="$DISK_READ_TEST_AVG" 'BEGIN { print a / 1000 }')
DISK_READ_TEST_UNIT="GB/s"
else
DISK_READ_TEST_UNIT="MB/s"
fi
# print dd sequential disk speed test results
echo -e
echo -e "dd Sequential Disk Speed Tests:"
echo -e "---------------------------------"
printf "%-6s | %-6s %-4s | %-6s %-4s | %-6s %-4s | %-6s %-4s\n" "" "Test 1" "" "Test 2" "" "Test 3" "" "Avg" ""
printf "%-6s | %-6s %-4s | %-6s %-4s | %-6s %-4s | %-6s %-4s\n"
printf "%-6s | %-11s | %-11s | %-11s | %-6.2f %-4s\n" "Write" "${DISK_WRITE_TEST_RES[0]}" "${DISK_WRITE_TEST_RES[1]}" "${DISK_WRITE_TEST_RES[2]}" "${DISK_WRITE_TEST_AVG}" "${DISK_WRITE_TEST_UNIT}"
printf "%-6s | %-11s | %-11s | %-11s | %-6.2f %-4s\n" "Read" "${DISK_READ_TEST_RES[0]}" "${DISK_READ_TEST_RES[1]}" "${DISK_READ_TEST_RES[2]}" "${DISK_READ_TEST_AVG}" "${DISK_READ_TEST_UNIT}"
else # fio tests completed successfully, print results
CURRENT_PARTITION=$(df -P . 2>/dev/null | tail -1 | cut -d' ' -f 1)
[[ ! -z $JSON ]] && JSON_RESULT+=',"partition":"'$CURRENT_PARTITION'","fio":['
DISK_RESULTS_NUM=$(expr ${#DISK_RESULTS[@]} / 6)
DISK_COUNT=0
# print disk speed test results
echo -e "fio Disk Speed Tests (Mixed R/W 50/50) (Partition $CURRENT_PARTITION):"
echo -e "---------------------------------"
while [ $DISK_COUNT -lt $DISK_RESULTS_NUM ] ; do
if [ $DISK_COUNT -gt 0 ]; then printf "%-10s | %-20s | %-20s\n"; fi
printf "%-10s | %-11s %8s | %-11s %8s\n" "Block Size" "${BLOCK_SIZES[DISK_COUNT]}" "(IOPS)" "${BLOCK_SIZES[DISK_COUNT+1]}" "(IOPS)"
printf "%-10s | %-11s %8s | %-11s %8s\n" " ------" "---" "---- " "----" "---- "
printf "%-10s | %-11s %8s | %-11s %8s\n" "Read" "${DISK_RESULTS[DISK_COUNT*6+1]}" "(${DISK_RESULTS[DISK_COUNT*6+4]})" "${DISK_RESULTS[(DISK_COUNT+1)*6+1]}" "(${DISK_RESULTS[(DISK_COUNT+1)*6+4]})"
printf "%-10s | %-11s %8s | %-11s %8s\n" "Write" "${DISK_RESULTS[DISK_COUNT*6+2]}" "(${DISK_RESULTS[DISK_COUNT*6+5]})" "${DISK_RESULTS[(DISK_COUNT+1)*6+2]}" "(${DISK_RESULTS[(DISK_COUNT+1)*6+5]})"
printf "%-10s | %-11s %8s | %-11s %8s\n" "Total" "${DISK_RESULTS[DISK_COUNT*6]}" "(${DISK_RESULTS[DISK_COUNT*6+3]})" "${DISK_RESULTS[(DISK_COUNT+1)*6]}" "(${DISK_RESULTS[(DISK_COUNT+1)*6+3]})"
if [ ! -z $JSON ]; then
JSON_RESULT+='{"bs":"'${BLOCK_SIZES[DISK_COUNT]}'","speed_r":'${DISK_RESULTS_RAW[DISK_COUNT*6+1]}',"iops_r":'${DISK_RESULTS_RAW[DISK_COUNT*6+4]}
JSON_RESULT+=',"speed_w":'${DISK_RESULTS_RAW[DISK_COUNT*6+2]}',"iops_w":'${DISK_RESULTS_RAW[DISK_COUNT*6+5]}',"speed_rw":'${DISK_RESULTS_RAW[DISK_COUNT*6]}
JSON_RESULT+=',"iops_rw":'${DISK_RESULTS_RAW[DISK_COUNT*6+3]}',"speed_units":"KBps"},'
JSON_RESULT+='{"bs":"'${BLOCK_SIZES[DISK_COUNT+1]}'","speed_r":'${DISK_RESULTS_RAW[(DISK_COUNT+1)*6+1]}',"iops_r":'${DISK_RESULTS_RAW[(DISK_COUNT+1)*6+4]}
JSON_RESULT+=',"speed_w":'${DISK_RESULTS_RAW[(DISK_COUNT+1)*6+2]}',"iops_w":'${DISK_RESULTS_RAW[(DISK_COUNT+1)*6+5]}',"speed_rw":'${DISK_RESULTS_RAW[(DISK_COUNT+1)*6]}
JSON_RESULT+=',"iops_rw":'${DISK_RESULTS_RAW[(DISK_COUNT+1)*6+3]}',"speed_units":"KBps"},'
fi
DISK_COUNT=$(expr $DISK_COUNT + 2)
done
[[ ! -z $JSON ]] && JSON_RESULT=${JSON_RESULT::${#JSON_RESULT}-1} && JSON_RESULT+=']'
fi
fi
# iperf_test
# Purpose: This method is designed to test the network performance of the host by executing an
# iperf3 test to/from the public iperf server passed to the function. Both directions
# (send and receive) are tested.
# Parameters:
# 1. URL - URL/domain name of the iperf server
# 2. PORTS - the range of ports on which the iperf server operates
# 3. HOST - the friendly name of the iperf server host/owner
# 4. FLAGS - any flags that should be passed to the iperf command
function iperf_test {
URL=$1
PORTS=$2
HOST=$3
FLAGS=$4
# attempt the iperf send test 3 times, allowing for a slot to become available on the
# server or to throw out any bad/error results
I=1
while [ $I -le 3 ]
do
echo -en "Performing $MODE iperf3 send test to $HOST (Attempt #$I of 3)..."
# select a random iperf port from the range provided
PORT=$(shuf -i $PORTS -n 1)
# run the iperf test sending data from the host to the iperf server; includes
# a timeout of 15s in case the iperf server is not responding; uses 8 parallel
# threads for the network test
IPERF_RUN_SEND="$(timeout 15 $IPERF_CMD $FLAGS -c "$URL" -p $PORT -P 8 2> /dev/null)"
# check if iperf exited cleanly and did not return an error
if [[ "$IPERF_RUN_SEND" == *"receiver"* && "$IPERF_RUN_SEND" != *"error"* ]]; then
# test did not result in an error, parse speed result
SPEED=$(echo "${IPERF_RUN_SEND}" | grep SUM | grep receiver | awk '{ print $6 }')
# if speed result is blank or bad (0.00), rerun, otherwise set counter to exit loop
[[ -z $SPEED || "$SPEED" == "0.00" ]] && I=$(( $I + 1 )) || I=11
else
# if iperf server is not responding, set counter to exit, otherwise increment, sleep, and rerun
[[ "$IPERF_RUN_SEND" == *"unable to connect"* ]] && I=11 || I=$(( $I + 1 )) && sleep 2
fi
echo -en "\r\033[0K"
done
# small sleep necessary to give iperf server a breather to get ready for a new test
sleep 1
# attempt the iperf receive test 3 times, allowing for a slot to become available on
# the server or to throw out any bad/error results
J=1
while [ $J -le 3 ]
do
echo -n "Performing $MODE iperf3 recv test from $HOST (Attempt #$J of 3)..."
# select a random iperf port from the range provided
PORT=$(shuf -i $PORTS -n 1)
# run the iperf test receiving data from the iperf server to the host; includes
# a timeout of 15s in case the iperf server is not responding; uses 8 parallel
# threads for the network test
IPERF_RUN_RECV="$(timeout 15 $IPERF_CMD $FLAGS -c "$URL" -p $PORT -P 8 -R 2> /dev/null)"
# check if iperf exited cleanly and did not return an error
if [[ "$IPERF_RUN_RECV" == *"receiver"* && "$IPERF_RUN_RECV" != *"error"* ]]; then
# test did not result in an error, parse speed result
SPEED=$(echo "${IPERF_RUN_RECV}" | grep SUM | grep receiver | awk '{ print $6 }')
# if speed result is blank or bad (0.00), rerun, otherwise set counter to exit loop
[[ -z $SPEED || "$SPEED" == "0.00" ]] && J=$(( $J + 1 )) || J=11
else
# if iperf server is not responding, set counter to exit, otherwise increment, sleep, and rerun
[[ "$IPERF_RUN_RECV" == *"unable to connect"* ]] && J=11 || J=$(( $J + 1 )) && sleep 2
fi
echo -en "\r\033[0K"
done
# Run a latency test via ping -c1 command -> will return "xx.x ms"
[[ ! -z $LOCAL_PING ]] && LATENCY_RUN="$(ping -c1 $URL 2>/dev/null | grep -o 'time=.*' | sed s/'time='//)"
[[ -z $LATENCY_RUN ]] && LATENCY_RUN="--"
# parse the resulting send and receive speed results
IPERF_SENDRESULT="$(echo "${IPERF_RUN_SEND}" | grep SUM | grep receiver)"
IPERF_RECVRESULT="$(echo "${IPERF_RUN_RECV}" | grep SUM | grep receiver)"
LATENCY_RESULT="$(echo "${LATENCY_RUN}")"
}
# launch_iperf
# Purpose: This method is designed to facilitate the execution of iperf network speed tests to
# each public iperf server in the iperf server locations array.
# Parameters:
# 1. MODE - indicates the type of iperf tests to run (IPv4 or IPv6)
function launch_iperf {
MODE=$1
[[ "$MODE" == *"IPv6"* ]] && IPERF_FLAGS="-6" || IPERF_FLAGS="-4"
# print iperf3 network speed results as they are completed
echo -e
echo -e "iperf3 Network Speed Tests ($MODE):"
echo -e "---------------------------------"
printf "%-15s | %-25s | %-15s | %-15s | %-15s\n" "Provider" "Location (Link)" "Send Speed" "Recv Speed" "Ping"
printf "%-15s | %-25s | %-15s | %-15s | %-15s\n" "-----" "-----" "----" "----" "----"
# loop through iperf locations array to run iperf test using each public iperf server
for (( i = 0; i < IPERF_LOCS_NUM; i++ )); do
# test if the current iperf location supports the network mode being tested (IPv4/IPv6)
if [[ "${IPERF_LOCS[i*5+4]}" == *"$MODE"* ]]; then
# call the iperf_test function passing the required parameters
iperf_test "${IPERF_LOCS[i*5]}" "${IPERF_LOCS[i*5+1]}" "${IPERF_LOCS[i*5+2]}" "$IPERF_FLAGS"
# parse the send and receive speed results
IPERF_SENDRESULT_VAL=$(echo $IPERF_SENDRESULT | awk '{ print $6 }')
IPERF_SENDRESULT_UNIT=$(echo $IPERF_SENDRESULT | awk '{ print $7 }')
IPERF_RECVRESULT_VAL=$(echo $IPERF_RECVRESULT | awk '{ print $6 }')
IPERF_RECVRESULT_UNIT=$(echo $IPERF_RECVRESULT | awk '{ print $7 }')
LATENCY_VAL=$(echo $LATENCY_RESULT)
# if the results are blank, then the server is "busy" and being overutilized
[[ -z $IPERF_SENDRESULT_VAL || "$IPERF_SENDRESULT_VAL" == *"0.00"* ]] && IPERF_SENDRESULT_VAL="busy" && IPERF_SENDRESULT_UNIT=""
[[ -z $IPERF_RECVRESULT_VAL || "$IPERF_RECVRESULT_VAL" == *"0.00"* ]] && IPERF_RECVRESULT_VAL="busy" && IPERF_RECVRESULT_UNIT=""
# print the speed results for the iperf location currently being evaluated
printf "%-15s | %-25s | %-15s | %-15s | %-15s\n" "${IPERF_LOCS[i*5+2]}" "${IPERF_LOCS[i*5+3]}" "$IPERF_SENDRESULT_VAL $IPERF_SENDRESULT_UNIT" "$IPERF_RECVRESULT_VAL $IPERF_RECVRESULT_UNIT" "$LATENCY_VAL"
if [ ! -z $JSON ]; then
JSON_RESULT+='{"mode":"'$MODE'","provider":"'${IPERF_LOCS[i*5+2]}'","loc":"'${IPERF_LOCS[i*5+3]}
JSON_RESULT+='","send":"'$IPERF_SENDRESULT_VAL' '$IPERF_SENDRESULT_UNIT'","recv":"'$IPERF_RECVRESULT_VAL' '$IPERF_RECVRESULT_UNIT'","latency":"'$LATENCY_VAL'"},'
fi
fi
done
}
# if the skip iperf flag was set, skip the network performance test, otherwise test network performance
if [ -z "$SKIP_IPERF" ]; then
if [[ -z "$PREFER_BIN" && ! -z "$LOCAL_IPERF" ]]; then # local iperf has been detected, use instead of pre-compiled binary
IPERF_CMD=iperf3
else
# create a temp directory to house the required iperf binary and library
IPERF_PATH=$YABS_PATH/iperf
mkdir -p "$IPERF_PATH"
# download iperf3 binary
if [[ ! -z $LOCAL_CURL ]]; then
curl -s --connect-timeout 5 --retry 5 --retry-delay 0 https://raw.githubusercontent.com/masonr/yet-another-bench-script/master/bin/iperf/iperf3_$ARCH -o "$IPERF_PATH/iperf3"
else
wget -q -T 5 -t 5 -w 0 https://raw.githubusercontent.com/masonr/yet-another-bench-script/master/bin/iperf/iperf3_$ARCH -O "$IPERF_PATH/iperf3"
fi
if [ ! -f "$IPERF_PATH/iperf3" ]; then # ensure iperf3 binary downloaded successfully
IPERF_DL_FAIL=True
else
chmod +x "$IPERF_PATH/iperf3"
IPERF_CMD=$IPERF_PATH/iperf3
fi
fi
# array containing all currently available iperf3 public servers to use for the network test
# format: "1" "2" "3" "4" "5" \
# 1. domain name of the iperf server
# 2. range of ports that the iperf server is running on (lowest-highest)
# 3. friendly name of the host/owner of the iperf server
# 4. location and advertised speed link of the iperf server
# 5. network modes supported by the iperf server (IPv4 = IPv4-only, IPv4|IPv6 = IPv4 + IPv6, etc.)
IPERF_LOCS=( \
"lon.speedtest.clouvider.net" "5200-5209" "Clouvider" "London, UK (10G)" "IPv4|IPv6" \
"iperf-ams-nl.eranium.net" "5201-5210" "Eranium" "Amsterdam, NL (10G)" "IPv4|IPv6" \
"speedtest.extra.telia.fi" "5201-5208" "Telia" "Helsinki, FI (10G)" "IPv4" \
# AFR placeholder
"speedtest.uztelecom.uz" "5200-5209" "Uztelecom" "Tashkent, UZ (10G)" "IPv4|IPv6" \
"speedtest.sin1.sg.leaseweb.net" "5201-5210" "Leaseweb" "Singapore, SG (10G)" "IPv4|IPv6" \
"la.speedtest.clouvider.net" "5200-5209" "Clouvider" "Los Angeles, CA, US (10G)" "IPv4|IPv6" \
"speedtest.nyc1.us.leaseweb.net" "5201-5210" "Leaseweb" "NYC, NY, US (10G)" "IPv4|IPv6" \
"speedtest.sao1.edgoo.net" "9204-9240" "Edgoo" "Sao Paulo, BR (1G)" "IPv4|IPv6"
)
# if the "REDUCE_NET" flag is activated, then do a shorter iperf test with only three locations
# (Clouvider London, Clouvider NYC, and Online.net France)
if [ ! -z "$REDUCE_NET" ]; then
IPERF_LOCS=( \
"lon.speedtest.clouvider.net" "5200-5209" "Clouvider" "London, UK (10G)" "IPv4|IPv6" \
"speedtest.sin1.sg.leaseweb.net" "5201-5210" "Leaseweb" "Singapore, SG (10G)" "IPv4|IPv6" \
"speedtest.nyc1.us.leaseweb.net" "5201-5210" "Leaseweb" "NYC, NY, US (10G)" "IPv4|IPv6" \
)
fi
# get the total number of iperf locations (total array size divided by 5 since each location has 5 elements)
IPERF_LOCS_NUM=${#IPERF_LOCS[@]}
IPERF_LOCS_NUM=$((IPERF_LOCS_NUM / 5))
if [ -z "$IPERF_DL_FAIL" ]; then
[[ ! -z $JSON ]] && JSON_RESULT+=',"iperf":['
# check if the host has IPv4 connectivity, if so, run iperf3 IPv4 tests
[ ! -z "$IPV4_CHECK" ] && launch_iperf "IPv4"
# check if the host has IPv6 connectivity, if so, run iperf3 IPv6 tests
[ ! -z "$IPV6_CHECK" ] && launch_iperf "IPv6"
[[ ! -z $JSON ]] && JSON_RESULT=${JSON_RESULT::${#JSON_RESULT}-1} && JSON_RESULT+=']'
else
echo -e "\niperf3 binary download failed. Skipping iperf network tests..."
fi
fi
# launch_geekbench
# Purpose: This method is designed to run the Primate Labs' Geekbench 4/5 Cross-Platform Benchmark utility
# Parameters:
# 1. VERSION - indicates which Geekbench version to run
function launch_geekbench {
VERSION=$1
# create a temp directory to house all geekbench files
GEEKBENCH_PATH=$YABS_PATH/geekbench_$VERSION
mkdir -p "$GEEKBENCH_PATH"
GB_URL=""
GB_CMD=""
GB_RUN=""
# check for curl vs wget
[[ ! -z $LOCAL_CURL ]] && DL_CMD="curl -s" || DL_CMD="wget -qO-"
if [[ $VERSION == *4* && ($ARCH = *aarch64* || $ARCH = *arm*) ]]; then
echo -e "\nARM architecture not supported by Geekbench 4, use Geekbench 5 or 6."
elif [[ $VERSION == *4* && $ARCH != *aarch64* && $ARCH != *arm* ]]; then # Geekbench v4
GB_URL="https://cdn.geekbench.com/Geekbench-4.4.4-Linux.tar.gz"
[[ "$ARCH" == *"x86"* ]] && GB_CMD="geekbench_x86_32" || GB_CMD="geekbench4"
GB_RUN="True"
elif [[ $VERSION == *5* || $VERSION == *6* ]]; then # Geekbench v5/6
if [[ $ARCH = *x86* && $GEEKBENCH_4 == *False* ]]; then # don't run Geekbench 5 if on 32-bit arch
echo -e "\nGeekbench $VERSION cannot run on 32-bit architectures. Re-run with -4 flag to use"
echo -e "Geekbench 4, which can support 32-bit architectures. Skipping Geekbench $VERSION."
elif [[ $ARCH = *x86* && $GEEKBENCH_4 == *True* ]]; then
echo -e "\nGeekbench $VERSION cannot run on 32-bit architectures. Skipping test."
else
if [[ $VERSION == *5* ]]; then # Geekbench v5
[[ $ARCH = *aarch64* || $ARCH = *arm* ]] && GB_URL="https://cdn.geekbench.com/Geekbench-5.5.1-LinuxARMPreview.tar.gz" \
|| GB_URL="https://cdn.geekbench.com/Geekbench-5.5.1-Linux.tar.gz"
GB_CMD="geekbench5"
else # Geekbench v6
[[ $ARCH = *aarch64* || $ARCH = *arm* ]] && GB_URL="https://cdn.geekbench.com/Geekbench-6.2.2-LinuxARMPreview.tar.gz" \
|| GB_URL="https://cdn.geekbench.com/Geekbench-6.2.2-Linux.tar.gz"
GB_CMD="geekbench6"
fi
GB_RUN="True"
fi
fi
if [[ $GB_RUN == *True* ]]; then # run GB test
echo -en "\nRunning GB$VERSION benchmark test... *cue elevator music*"
# check for local geekbench installed
if command -v "$GB_CMD" &>/dev/null; then
GEEKBENCH_PATH=$(dirname "$(command -v "$GB_CMD")")
else
# download the desired Geekbench tarball and extract to geekbench temp directory
$DL_CMD $GB_URL | tar xz --strip-components=1 -C "$GEEKBENCH_PATH" &>/dev/null
fi
# unlock if license file detected
test -f "geekbench.license" && "$GEEKBENCH_PATH/$GB_CMD" --unlock $(cat geekbench.license) > /dev/null 2>&1
# run the Geekbench test and grep the test results URL given at the end of the test
GEEKBENCH_TEST=$("$GEEKBENCH_PATH/$GB_CMD" --upload 2>/dev/null | grep "https://browser")
# ensure the test ran successfully
if [ -z "$GEEKBENCH_TEST" ]; then
# detect if CentOS 7 and print a more helpful error message
if grep -q "CentOS Linux 7" /etc/os-release; then
echo -e "\r\033[0K CentOS 7 and Geekbench have known issues relating to glibc (see issue #71 for details)"
fi
if [[ -z "$IPV4_CHECK" ]]; then
# Geekbench test failed to download because host lacks IPv4 (cdn.geekbench.com = IPv4 only)
echo -e "\r\033[0KGeekbench releases can only be downloaded over IPv4. FTP the Geekbench files and run manually."
elif [[ $VERSION != *4* && $TOTAL_RAM_RAW -le 1048576 ]]; then
# Geekbench 5/6 test failed with low memory (<=1GB)
echo -e "\r\033[0KGeekbench test failed and low memory was detected. Add at least 1GB of SWAP or use GB4 instead (higher compatibility with low memory systems)."
elif [[ $ARCH != *x86* ]]; then
# if the Geekbench test failed for any other reason, exit cleanly and print error message
echo -e "\r\033[0KGeekbench $VERSION test failed. Run manually to determine cause."
fi
else
# if the Geekbench test succeeded, parse the test results URL
GEEKBENCH_URL=$(echo -e $GEEKBENCH_TEST | head -1)
GEEKBENCH_URL_CLAIM=$(echo $GEEKBENCH_URL | awk '{ print $2 }')
GEEKBENCH_URL=$(echo $GEEKBENCH_URL | awk '{ print $1 }')
# sleep a bit to wait for results to be made available on the geekbench website
sleep 10
# parse the public results page for the single and multi core geekbench scores
[[ $VERSION == *4* ]] && GEEKBENCH_SCORES=$($DL_CMD $GEEKBENCH_URL | grep "span class='score'") || \
GEEKBENCH_SCORES=$($DL_CMD $GEEKBENCH_URL | grep "div class='score'")
GEEKBENCH_SCORES_SINGLE=$(echo $GEEKBENCH_SCORES | awk -v FS="(>|<)" '{ print $3 }')
GEEKBENCH_SCORES_MULTI=$(echo $GEEKBENCH_SCORES | awk -v FS="(>|<)" '{ print $7 }')
# print the Geekbench results
echo -en "\r\033[0K"
echo -e "Geekbench $VERSION Benchmark Test:"
echo -e "---------------------------------"
printf "%-15s | %-30s\n" "Test" "Value"
printf "%-15s | %-30s\n"
printf "%-15s | %-30s\n" "Single Core" "$GEEKBENCH_SCORES_SINGLE"
printf "%-15s | %-30s\n" "Multi Core" "$GEEKBENCH_SCORES_MULTI"
printf "%-15s | %-30s\n" "Full Test" "$GEEKBENCH_URL"
if [ ! -z $JSON ]; then
JSON_RESULT+='{"version":'$VERSION',"single":'$GEEKBENCH_SCORES_SINGLE',"multi":'$GEEKBENCH_SCORES_MULTI
JSON_RESULT+=',"url":"'$GEEKBENCH_URL'"},'
fi
# write the geekbench claim URL to a file so the user can add the results to their profile (if desired)
[ ! -z "$GEEKBENCH_URL_CLAIM" ] && echo -e "$GEEKBENCH_URL_CLAIM" >> geekbench_claim.url 2> /dev/null
fi
fi
}
# if the skip geekbench flag was set, skip the system performance test, otherwise test system performance
if [ -z "$SKIP_GEEKBENCH" ]; then
[[ ! -z $JSON ]] && JSON_RESULT+=',"geekbench":['
if [[ $GEEKBENCH_4 == *True* ]]; then
launch_geekbench 4
fi
if [[ $GEEKBENCH_5 == *True* ]]; then
launch_geekbench 5
fi
if [[ $GEEKBENCH_6 == *True* ]]; then
launch_geekbench 6
fi
[[ ! -z $JSON ]] && [[ $(echo -n $JSON_RESULT | tail -c 1) == ',' ]] && JSON_RESULT=${JSON_RESULT::${#JSON_RESULT}-1}
[[ ! -z $JSON ]] && JSON_RESULT+=']'
fi
# finished all tests, clean up all YABS files and exit
echo -e
rm -rf "$YABS_PATH"
YABS_END_TIME=$(date +%s)
# calculate_time_taken
# Purpose: This method is designed to find the time taken for the completion of a YABS run.
# Parameters:
# 1. YABS_END_TIME - time when GB has completed and all files are removed
# 2. YABS_START_TIME - time when YABS is started
function calculate_time_taken() {
end_time=$1
start_time=$2
time_taken=$(( ${end_time} - ${start_time} ))
if [ ${time_taken} -gt 60 ]; then
min=$(expr $time_taken / 60)
sec=$(expr $time_taken % 60)
echo "YABS completed in ${min} min ${sec} sec"
else
echo "YABS completed in ${time_taken} sec"
fi
[[ ! -z $JSON ]] && JSON_RESULT+=',"runtime":{"start":'$start_time',"end":'$end_time',"elapsed":'$time_taken'}'
}
calculate_time_taken $YABS_END_TIME $YABS_START_TIME
if [[ ! -z $JSON ]]; then
JSON_RESULT+='}'
# write json results to file
if [[ $JSON = *w* ]]; then
echo $JSON_RESULT > "$JSON_FILE"
fi
# send json results
if [[ $JSON = *s* ]]; then
IFS=',' read -r -a JSON_SITES <<< "$JSON_SEND"
for JSON_SITE in "${JSON_SITES[@]}"
do
if [[ ! -z $LOCAL_CURL ]]; then
curl -s -H "Content-Type:application/json" -X POST --data ''"$JSON_RESULT"'' $JSON_SITE
else
wget -qO- --post-data=''"$JSON_RESULT"'' --header='Content-Type:application/json' $JSON_SITE
fi
done
fi
# print json result to screen
if [[ $JSON = *j* ]]; then
echo -e
echo $JSON_RESULT
fi
fi
# reset locale settings
unset LC_ALL