From f1c88395531ce9666113f59235eb6860f5774b46 Mon Sep 17 00:00:00 2001 From: David Schroeder Date: Tue, 26 Nov 2024 20:37:33 -0600 Subject: [PATCH] Add storage-test.sh --- storage-test.sh | 961 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 961 insertions(+) create mode 100644 storage-test.sh diff --git a/storage-test.sh b/storage-test.sh new file mode 100644 index 0000000..8b7779a --- /dev/null +++ b/storage-test.sh @@ -0,0 +1,961 @@ +#!/bin/bash +# Storage benchmark by James A. Chambers (https://jamesachambers.com/) +# Benchmarks your storage and anonymously submits result to https://pibenchmarks.com +# Results and discussion available at https://jamesachambers.com/2020s-fastest-raspberry-pi-4-storage-sd-ssd-benchmarks/ +# +# To run the benchmark use the following command: +# sudo curl https://raw.githubusercontent.com/TheRemote/PiBenchmarks/master/Storage.sh | sudo bash + +# Fix language settings for utf8 +export LC_ALL=C + +# Terminal colors +BLACK=$(tput setaf 0) +RED=$(tput setaf 1) +GREEN=$(tput setaf 2) +YELLOW=$(tput setaf 3) +LIME_YELLOW=$(tput setaf 190) +POWDER_BLUE=$(tput setaf 153) +BLUE=$(tput setaf 4) +MAGENTA=$(tput setaf 5) +CYAN=$(tput setaf 6) +WHITE=$(tput setaf 7) +BRIGHT=$(tput bold) +NORMAL=$(tput sgr0) +BLINK=$(tput blink) +REVERSE=$(tput smso) +UNDERLINE=$(tput smul) + +# Whether apt update has ran +AptUpdated="0" + +# Prints a line with color using terminal codes +Print_Style() { + printf "%s\n" "${2}$1${NORMAL}" +} + +# Install apt package +Install_Apt_Package() { + echo "Install $1" + if [ "$AptUpdated" -ne "1" ]; then + export AptUpdated="1" + apt-get update + apt-get install lshw pciutils usbutils lsscsi bc curl hwinfo hdparm nvme-cli dmidecode smartmontools fio sdparm xxd libxml-dumper-perl --no-install-recommends -y + fi + apt-get install --no-install-recommends "$1" -y +} + +# Get binary from string +Get_Binary() { + local BinaryStr + BinaryStr=$(printf "$1" | xxd -r -p | xxd -b | cut -d: -f 2 | sed 's/ .*//; s/ //g' | sed ':a;N;$!ba;s/\n//g') + echo "$BinaryStr" +} + +# Get specific bits from binary string +Get_Bits() { + # $1 - Binary String + # $2 - BitsStart + # $3 - BitsCount + # $4 - Structure size in bits + local BitsStart=$(($4 - $2 - $3)) + local BinaryStr=$(printf "$1") + echo "${BinaryStr:BitsStart:$3}" +} + +# Get decimal from binary +Get_Decimal() { + echo "$((2#$1))" 2>/dev/null +} + +# Get hex from binary +Get_Hex() { + printf '%x\n' "$((2#$1))" +} + +# Get string of text from binary +Get_Text() { + for a in $1; do printf "%x\n" $((2#$a)); done | xxd -r -p +} + +# Check if script is running as root first +if [[ "$(whoami)" != "root" ]]; then + Print_Style "Benchmarks must be ran as root! Example: sudo ./Storage.sh" "$RED" + exit 1 +fi + +# Trim drives for more accurate benchmarking +Print_Style "Trimming and syncing drives ..." "$YELLOW" +fstrim -av +sync +sync + +# Initialize variables +Score=0 +DDWriteResult=0 +fio4kRandReadIOPS=0 +fio4kRandWriteIOPS=0 +IO4kRead=0 +IO4kWrite=0 +IO4kRandRead=0 +IO4kRandWrite=0 + +# Did the user give a folder ? +ChosenPartition="" +if [ "$1" = "" ]; then + # User did not provide a partition/folder, change directory to rootfs + cd / +else + if [ ! -d "$1" ]; then + Print_Style "Your chosen partition (folder) does not exist! Provide a good one or run without parameters to check the rootfs" "$RED" + exit 1 + else + ChosenPartition="$1" + cd "$ChosenPartition" + fi +fi + +# Get host board information +HostArchitecture=$(uname -m) +HostOSInfo=$(cat /etc/os-release | sed 's/;/!/g') +HostOS=$(echo "$HostOSInfo" | grep "PRETTY_NAME" | cut -d= -f2 | xargs) + +if [[ "$HostArchitecture" == *"x86"* || "$HostArchitecture" == *"amd64"* ]]; then + # X86 or X86_64 system -- use dmidecode + HostModel=$(dmidecode -t1 | grep 'Product Name' -m 1 | cut -d: -f2 | xargs) + HostManufacturer=$(dmidecode -t1 | grep 'Manufacturer' -m 1 | cut -d: -f2 | xargs) +else + # ARM system + if [ -e /proc/device-tree/model ]; then + HostModel=$(tr -d '\0' &1 | grep Status | grep installed) + if [ -z "$DpkgTest" ]; then Install_Apt_Package "libxml-dumper-perl"; fi + +# Next test for Pac-Man (Arch Linux) +elif [ -n "$(which pacman)" ]; then + pacman -Syy + pacman --needed --noconfirm -S \ + base-devel \ + bc \ + curl \ + dmidecode \ + fio \ + hdparm \ + lshw \ + lsscsi \ + pciutils \ + usbutils \ + nvme-cli \ + sdparm \ + vim + + # Install iozone + if ! command -v iozone; then + echo "Please install iozone via the AUR for this script to work" >&2 + echo "https://aur.archlinux.org/packages/iozone/" >&2 + exit 3 + fi + + # Check if running on a Raspberry Pi + if [[ $HostModel == *"Raspberry Pi"* ]]; then + if [ -z "$(which vcgencmd)" ]; then + # Create soft link for vcgencmd + ln -s /opt/vc/bin/vcgencmd /usr/local/bin + fi + fi +else + Print_Style "No package manager found!" "$RED" +fi + +if [ -z "$(which lsblk)" ]; then + Print_Style "Unable to locate the utility lsblk. Please install lsblk for your distro!" "$RED" + # Return to home directory + exit 3 +fi + +if [ -z "$(which fio)" ]; then + Print_Style "Unable to locate the utility fio. Please install fio for your distro!" "$RED" + # Return to home directory + exit 3 +fi + +# Get clock speeds +if [[ "$HostArchitecture" == *"x86"* || "$HostArchitecture" == *"amd64"* ]]; then + # X86 or X86_64 system -- use dmidecode + HostCPUClock=$(dmidecode -t4 | grep -m 1 'Max Speed' | cut -d: -f2 | cut -d' ' -f2 | xargs) + HostCoreClock="" + HostRAMClock=$(dmidecode -t17 | grep -m 1 "Speed: " | cut -d' ' -f2 | xargs) +else + # Check for vcgencmd + if [ -n "$(which vcgencmd)" ]; then + HostConfig=$(vcgencmd get_config int) + HostCPUClock=$(echo "$HostConfig" | grep -m 1 arm_freq= | cut -d= -f2 | xargs) + if [ -z "$HostCPUClock" ]; then + HostCPUClock=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq | xargs) + HostCPUClock=$(echo "scale=0; $HostCPUClock / 1000" | bc) + fi + HostCoreClock=$(echo "$HostConfig" | grep -m 1 core_freq= | cut -d= -f2 | xargs) + if [ -z "$HostCoreClock" ]; then + HostCoreClock=$(echo "$HostConfig" | grep -m 1 gpu_freq= | cut -d= -f2 | xargs) + fi + if [ -z "$HostCoreClock" ]; then + HostCoreClock=$(vcgencmd measure_clock core | cut -d= -f2 | xargs) + HostCoreClock=$(echo "scale=0; $HostCoreClock / 1000000" | bc) + fi + HostRAMClock=$(echo "$HostConfig" | grep -m 1 sdram_freq= | cut -d= -f2 | xargs) + if [ -z "$HostRAMClock" ]; then + HostRAMClock="" + fi + HostConfig+=$(echo " ") + HostConfig+=$(vcgencmd get_config str) + else + HostConfig+=$(echo " ") + HostCPUClock=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq) + HostCPUClock=$(echo "scale=0; $HostCPUClock / 1000" | bc) + HostCoreClock="" + HostRAMClock="" + fi +fi +if [ -z "$HostRAMClock" ]; then + Print_Style "Clock speeds: CPU: $HostCPUClock - Core: $HostCoreClock" +else + Print_Style "Clock speeds: CPU: $HostCPUClock - Core: $HostCoreClock - RAM: $HostRAMClock" "$YELLOW" +fi + +# Retrieve and build iozone +if [ -z "$(which iozone)" ]; then + if [ ! -f iozone/src/current/iozone ]; then + Print_Style "Building iozone ..." "$YELLOW" + DownloadURL=$(curl -N iozone.org | grep -m 1 -o 'src/current/iozone3_[^"]*') + curl -s -o iozone.tar "http://www.iozone.org/$DownloadURL" + tar -xf iozone.tar + rm iozone.tar + mv iozone3_* iozone + cd iozone/src/current + make --quiet linux-arm + cd ../../.. + fi +fi + +# Run sync to make sure all changes have been written to disk +sync + +if [ -z "$ChosenPartition" ]; then + # User did not provide a partition/folder, continue with rootfs + # --Get system boot drive information-- + # Find from mountpoint first + BootDrive=$(findmnt -n -o SOURCE --target /) + + # Find by matching device IDs to / next + if [ -z "$BootDrive" ]; then + RDEV=$(mountpoint -d /) + + for file in /dev/*; do + DeviceIDP1=$(stat --printf="0x%t" "$file") + DeviceIDP2=$(stat --printf="0x%T" "$file") + DeviceID=$(printf "%d:%d" "$DeviceIDP1" "$DeviceIDP2") + if [ "$DeviceID" = "$RDEV" ]; then + BootDrive=$file + break + fi + done + fi + + # Fall back to finding from lsblk + if [ -z "$BootDrive" ]; then + BootDrive=$(lsblk -l | grep -v "0 part /boot" | grep -m 1 "0 part /" | awk 'NR==1{ print $1 }' | sed -e 's/\[\/.*\]//g') + if [ -n "$BootDrive" ]; then + BootDrive="/dev/"$BootDrive + fi + fi + + # Fall back to finding from df + if [ -z "$BootDrive" ]; then + BootDrive=$(df -H | grep -m 1 boot | awk 'NR==1{ print $1 }' | sed -e 's/\[\/.*\]//g') + fi +else + BootDrive=$(findmnt -n -o SOURCE --target "$ChosenPartition" | sed -e 's/\[\/.*\]//g') +fi + +# Detect BootDrive suffix +BootDriveSuffix=$(echo "$BootDrive" | cut -d"/" -f3) +if [ -z "$BootDriveSuffix" ]; then + BootDriveSuffix=$(echo "$BootDrive" | cut -d"/" -f2) +fi +if [ -z "$BootDriveSuffix" ]; then + BootDriveSuffix=$(echo "$BootDrive" | cut -d"/" -f1) +fi +if [ -z "$BootDriveSuffix" ]; then + BootDriveSuffix="$BootDrive" +fi + +if [ -z "$ChosenPartition" ]; then + Print_Style "System rootfs drive (/) has been detected as $BootDrive ($BootDriveSuffix)" "$YELLOW" +else + Print_Style "Chosen partition ($ChosenPartition) has been detected as $BootDrive ($BootDriveSuffix)" "$YELLOW" +fi + +Print_Style "Starting INXI hardware identification..." "$YELLOW" +# Retrieve inxi hardware identification utility (https://github.com/smxi/inxi for more info) +curl -s -o inxi https://raw.githubusercontent.com/smxi/inxi/master/inxi +chmod +x inxi +Test_inxi=$(./inxi -F -v8 -c0 -M -m -d -f -i -l -m -o -p -r -t -u -xxx 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +./inxi -v4 -d -c0 2>&1 +rm -f inxi + +Print_Style "Running additional hardware identification tests..." "$YELLOW" +Test_udevadm=$(udevadm info -w20 -a -n "$BootDrive" 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d' | grep -v 'info: invalid option') +if [ -z "$Test_udevadm" ]; then + echo "udevadm does not have wait option -- retrying..." + Test_udevadm=$(udevadm info -a -n "$BootDrive" 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +fi +Test_lsblk=$(lsblk -l -o NAME,FSTYPE,LABEL,MOUNTPOINT,SIZE,MODEL 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +Test_lshw=$(lshw 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +Test_lsusb=$(lsusb 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +Test_lsscsi=$(lsscsi -Lv 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +Test_lscpu=$(lscpu 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +Test_lspci=$(lspci -v 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +Test_findmnt=$(findmnt -n 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +Test_diskbyid=$(ls /dev/disk/by-id 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +Test_df=$(df -h 2>&1 | sed 's/;/!/g') +Test_cpuinfo=$(cat /proc/cpuinfo 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +Test_dmesg=$(dmesg -Lnever 2>&1 | grep usb | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +Test_fstab=$(cat /etc/fstab 2>&1 | sed 's/;/!/g') +Test_dmidecode=$(dmidecode 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +Test_hwinfo=$(hwinfo --arch --bios --block --bridge --disk --framebuffer --gfxcard --hub --ide --isapnp --listmd --memory --mmc-ctrl --monitor --netcard --partition --pci --pcmcia --pcmcia-ctrl --redasd --scsi --sound --storage-ctrl --sys --tape --usb --usb-ctrl 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +Test_nvme=$(nvme list -o json 2>&1 | sed 's/\x0//g') +#Test_nvme+=$(nvme show-regs "$BootDrive" -H 2>&1 | sed 's/;/!/g' | sed 's/\x0//g') +Test_smartctl=$(smartctl -x "$BootDrive" 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +Test_sdparm=$(sudo sdparm --long --verbose "$BootDrive" 2>&1 | sed 's/;/!/g' | sed '/^[[:space:]]*$/d') +Capacity=$(lsblk -l 2>&1 | grep "$BootDriveSuffix" -m 1 | awk 'NR==1{ print $4 }' | sed 's/,/./g') +Print_Style "Additional hardware identification tests completed." "$YELLOW" + +# Check for Micro SD / MMC card +if [[ "$BootDrive" == *"mmcblk"* ]]; then + # MicroSD hardware identification + Print_Style "Starting MMC/SD identification..." "$YELLOW" + + # Determine if MMC or Micro SD + RootDrive=$(echo "$BootDrive" | cut -dp -f1 | cut -d/ -f3) + MMCType=$(cat /sys/block/"$RootDrive"/device/type) + + # Get card information + Manufacturer=$(echo "$Test_udevadm" | grep -m 1 "manfid" | cut -d= -f3 | cut -d\" -f2 | xargs) + if [ -z "$Manufacturer" ]; then + Manufacturer=$(cat /sys/block/"$RootDrive"/device/manfid) + Product=$(cat /sys/block/"$RootDrive"/device/type) + Firmware=$(cat /sys/block/"$RootDrive"/device/fwrev) + DateManufactured=$(cat /sys/block/"$RootDrive"/device/date) + Model=$(cat /sys/block/"$RootDrive"/device/name) + Version=$(cat /sys/block/"$RootDrive"/device/hwrev) + #Vendor=$(cat /sys/block/"$RootDrive"/device/oemid) + SSR=$(cat /sys/block/"$RootDrive"/device/ssr) + SCR=$(cat /sys/block/"$RootDrive"/device/scr) + CID=$(cat /sys/block/"$RootDrive"/device/cid) + CSD=$(cat /sys/block/"$RootDrive"/device/csd) + OCR=$(cat /sys/block/"$RootDrive"/device/ocr) + else + Product=$(echo "$Test_udevadm" | grep -m 1 "{type}" | cut -d= -f3 | cut -d\" -f2 | xargs) + Firmware=$(echo "$Test_udevadm" | grep -m 1 "{fwrev}" | cut -d= -f3 | cut -d\" -f2 | xargs) + DateManufactured=$(echo "$Test_udevadm" | grep -m 1 "date" | cut -d= -f3 | cut -d\" -f2 | xargs) + Model=$(echo "$Test_udevadm" | grep -m 1 "{name}" | cut -d= -f3 | cut -d\" -f2 | xargs) + Version=$(echo "$Test_udevadm" | grep -m 1 "{hwrev}" | cut -d= -f3 | cut -d\" -f2 | xargs) + #Vendor=$(echo "$Test_udevadm" | grep -m 1 "oemid" | cut -d= -f3 | cut -d\" -f2 | xargs | xxd -r) + SSR=$(echo "$Test_udevadm" | grep -m 1 "{ssr}" | cut -d= -f3 | cut -d\" -f2 | xargs) + SCR=$(echo "$Test_udevadm" | grep -m 1 "{scr}" | cut -d= -f3 | cut -d\" -f2 | xargs) + CID=$(echo "$Test_udevadm" | grep -m 1 "{cid}" | cut -d= -f3 | cut -d\" -f2 | xargs) + CSD=$(echo "$Test_udevadm" | grep -m 1 "{csd}" | cut -d= -f3 | cut -d\" -f2 | xargs) + OCR=$(echo "$Test_udevadm" | grep -m 1 "{ocr}" | cut -d= -f3 | cut -d\" -f2 | xargs) + fi + + if [[ "$MMCType" == *"SD"* ]]; then + # MicroSD hardware identification + Print_Style "Starting SD card identification..." "$YELLOW" + + HostSDClock=$(grep "actual clock" /sys/kernel/debug/mmc0/ios 2>/dev/null | awk '{printf("%0.1f", $3/1000000)}') + + # Parse SSR status register + SSRBinary=$(Get_Binary "$SSR") + SSRAppClass=$(Get_Decimal "$(Get_Bits "$SSRBinary" 336 4 512)") + SSRVideoClass=$(Get_Decimal "$(Get_Bits "$SSRBinary" 384 8 512)") + SSRUHSClass=$(Get_Decimal "$(Get_Bits "$SSRBinary" 396 4 512)") + SSRSpeedClass=$(Get_Decimal "$(Get_Bits "$SSRBinary" 440 8 512)") + + # Parse CID status register + CIDBinary=$(Get_Binary "$CID") + CIDMID=$(Get_Hex "$(Get_Bits "$CIDBinary" 120 8 128)") + CIDMDateM=$(Get_Decimal "$(Get_Bits "$CIDBinary" 8 4 128)") + CIDMDateY=$(Get_Decimal "$(Get_Bits "$CIDBinary" 12 8 128)") + CIDMDate="$CIDMDateM/20$CIDMDateY" + CIDOID=$(Get_Text "$(Get_Bits "$CIDBinary" 104 16 128)") + CIDPNM=$(Get_Text "$(Get_Bits "$CIDBinary" 64 40 128)") + CIDPRVHW=$(Get_Hex "$(Get_Bits "$CIDBinary" 60 4 128)") + CIDPRVFW=$(Get_Hex "$(Get_Bits "$CIDBinary" 56 4 128)") + CIDPRV="$CIDPRVHW.$CIDPRVFW" + + # Parse CSD status register + CSDBinary=$(Get_Binary "$CSD") + CSDMID=$(Get_Hex "$(Get_Bits "$CSDBinary" 120 8 128)") + Print_Style "Card CSD status register: MID: $CIDMID OID: $CIDOID PNM: $CIDPNM PRV: $CIDPRV MDATE: $CIDMDate" "$YELLOW" + + # Parse SCR status register + SCRBinary=$(Get_Binary "$SCR") + SCRSDSpec=$(Get_Decimal "$(Get_Bits "$SCRBinary" 56 4 64)") + SCRSDSpec3=$(Get_Decimal "$(Get_Bits "$SCRBinary" 47 1 64)") + SCRSDSpec4=$(Get_Decimal "$(Get_Bits "$SCRBinary" 42 1 64)") + SCRSDSpecX=$(Get_Decimal "$(Get_Bits "$SCRBinary" 38 4 64)") + + # Get SD physical layer specification version + if [ "$SCRSDSpecX" == "2" ]; then # 6.XX + SCRSDSpecVer="6" + elif [ "$SCRSDSpecX" == "1" ]; then # 5.XX + SCRSDSpecVer="5" + elif [ "$SCRSDSpec4" == "1" ]; then # 4.XX + SCRSDSpecVer="4" + elif [ "$SCRSDSpec3" == "1" ]; then # 3.XX + SCRSDSpecVer="3" + elif [ "$SCRSDSpec" == "2" ]; then # 2.00 + SCRSDSpecVer="2" + elif [ "$SCRSDSpec" == "1" ]; then # 1.10 + SCRSDSpecVer="1.1" + elif [ "$SCRSDSpec" == "0" ]; then # 1.0 + SCRSDSpecVer="1" + fi + Print_Style "Card SCR status register: SD Physical Version Specification: $SCRSDSpecVer" "$YELLOW" + + # Check for known manufacturers + case "$Manufacturer" in + 0x000001) + Manufacturer="Panasonic" + ;; + 0x000002) + Manufacturer="Toshiba" + ;; + 0x000003) + Manufacturer="SanDisk" + ;; + 0x000008) + Manufacturer="Silicon Power" + ;; + 0x000018) + Manufacturer="Infineon" + ;; + 0x00001b) + Manufacturer="Samsung" + ;; + 0x00001d) + Manufacturer="Corsair/AData" + ;; + 0x000027) + Manufacturer="Phison" + ;; + 0x000028) + Manufacturer="Lexar" + ;; + 0x000030) + Manufacturer="SanDisk" + ;; + 0x000031) + Manufacturer="Silicon Power" + ;; + 0x000033) + Manufacturer="STMicroelectronics" + ;; + 0x000041) + Manufacturer="Kingston" + ;; + 0x000045) + Manufacturer="Team Group" + ;; + 0x00006f) + Manufacturer="STMicroelectronics" + ;; + 0x000073) + Manufacturer="Hama" + ;; + 0x000074) + Manufacturer="Transcend" + ;; + 0x000076) + Manufacturer="Patriot" + ;; + 0x000082) + Manufacturer="Sony" + ;; + 0x000092) + Manufacturer="Sony" + ;; + 0x00009c) + Manufacturer="Sony" + ;; + 0x00009e) + Manufacturer="Lexar" + ;; + 0x00009f) + Manufacturer="Texas Instruments" + ;; + 0x0000ff) + Manufacturer="Netac" + ;; + *) ;; + + esac + + # Identify card classifications + Class="" + if [[ $SSRAppClass -gt 0 ]]; then + Class="A$SSRAppClass" + fi + case "$SSRSpeedClass" in + 0) + Class="$Class Class 0" + ;; + 1) + Class="$Class Class 2" + ;; + 2) + Class="$Class Class 4" + ;; + 3) + Class="$Class Class 6" + ;; + 4) + Class="$Class Class 10" + ;; + *) + Class="$Class ?" + ;; + esac + if [[ $SSRVideoClass -gt 0 ]]; then + Class="$Class V$SSRVideoClass" + fi + if [[ $SSRUHSClass -gt 0 ]]; then + Class="$Class U$SSRUHSClass" + fi + Class=$(echo "$Class" | xargs) + Print_Style "MicroSD information: Clock Speed: $HostSDClock - Manufacturer: $Manufacturer - Model: $Model - Vendor: $Vendor - Product: $Product - HW Version: $Version - FW Version: $Firmware - Date Manufactured: $DateManufactured - Class: $Class" "$YELLOW" + elif [[ "$MMCType" == *"MMC"* ]]; then + # Attempt to identify MMC device + + Print_Style "MMC Detected. Identifying..." "$YELLOW" + + HostSDClock="" + + case "$Manufacturer" in + 0x000000) + Manufacturer="SanDisk" + ;; + 0x000002) + Manufacturer="Kingston/SanDisk" + ;; + 0x000003) + Manufacturer="Toshiba" + ;; + 0x000011) + Manufacturer="Toshiba" + ;; + 0x000015) + Manufacturer="Samsung/SanDisk/LG" + ;; + 0x000037) + Manufacturer="KingMax" + ;; + 0x000044) + Manufacturer="SanDisk" + ;; + 0x000090) + Manufacturer="SK Hynix" + ;; + 0x00002c) + Manufacturer="Kingston" + ;; + 0x000070) + Manufacturer="Kingston" + ;; + *) ;; + + esac + + # Get capacity + DriveCapacity=$(cat /sys/block/"$RootDrive"/device/emmc_total_size) + if [ -n "$DriveCapacity" ]; then + if [ "$DriveCapacity" -eq "$DriveCapacity" ] 2>/dev/null; then + Capacity=$DriveCapacity"G" + fi + fi + + # Parse CSD register + CSDBinary=$(Get_Binary "$CSD") + CSDSpecVersion=$(Get_Decimal "$(Get_Bits "$CSDBinary" 122 4 128)") + + # Parse CID register + CIDBinary=$(Get_Binary "$CID") + CIDCBX=$(Get_Decimal "$(Get_Bits "$CIDBinary" 112 1 128)") + + # Check CBX value to see if MMC is embedded or removable + case "$CIDCBX" in + 0) + Product="MMC" + Class="MMC v$CSDSpecVersion (Card)" + ;; + 1) + Product="eMMC" + Class="eMMC v$CSDSpecVersion (Embedded)" + ;; + 10) + Product="MMC" + Class="MMC v$CSDSpecVersion (POP)" + ;; + *) ;; + + esac + + Print_Style "MMC Type: $Class - Manufacturer: $Manufacturer - Model: $Model - Size: $Capacity" "$YELLOW" + + fi +else + # Not a MicroSD card + Print_Style "Starting mass storage device identification..." "$YELLOW" + + HDParmInfo=$(hdparm -Ii "$BootDrive" 2>/dev/null | sed '/^[[:space:]]*$/d') + if [ -z "$HDParmInfo" ]; then + HDParmInfo=$(hdparm -I "$BootDrive" 2>/dev/null | sed '/^[[:space:]]*$/d') + fi + Test_hdparm=$(echo "$HDParmInfo") + + HostSDClock="" + DateManufactured="" + + # Attempt to identify drive model + Model=$(echo "$Test_udevadm" | grep -m 1 "{model}" | cut -d= -f3 | cut -d\" -f2 | xargs) + #Vendor=$(echo "$Test_udevadm" | grep -m 1 "{vendor}" | cut -d= -f3 | cut -d\" -f2 | xargs) + Manufacturer=$(echo "$Test_udevadm" | grep -m 1 "{manufacturer}" | cut -d= -f3 | cut -d\" -f2 | xargs) + + case "$Model" in + "ASM105x") + # This is the ASMedia USB TO 2.5" SATA adapter chipset + Product="SSD" + FormFactor="2.5" + Class="SSD (2.5\" SATA)" + Adapter="ASMedia ASM105x" + Model= + Manufacturer= + ;; + "SABRENT") + # This is the Sabrent USB TO 2.5" SATA adapter chipset + Product="SSD" + FormFactor="2.5" + Class="SSD (2.5\" SATA)" + Adapter="Sabrent" + Model= + Manufacturer= + ;; + "2105") + # ASMedia adapter 2105 + Product="SSD" + FormFactor="2.5" + Class="SSD (2.5\" SATA)" + Adapter="ASMedia 2105" + Model= + Manufacturer= + ;; + "2115") + # ASMedia adapter 2115 + Product="SSD" + FormFactor="2.5" + Class="SSD (2.5\" SATA)" + Adapter="ASMedia 2115" + Model= + Manufacturer= + ;; + "USB 3.0 Device") + # ASMedia USB to SATA adapter (generic) + Product="SSD" + FormFactor="2.5" + Class="SSD (2.5\" SATA)" + Adapter="ASMedia 3.0 Generic" + Model= + Manufacturer= + ;; + "AQ3120") + # Geekworm x855 + Product="SSD" + FormFactor="2.5" + Class="SSD (mSATA)" + Adapter="Geekworm x855" + Model= + Manufacturer= + ;; + *) ;; + + esac + if [ -z "$Model" ]; then + Model=$(echo "$Test_hdparm" | grep -m 1 "Model Number:" | awk 'NR==1{ print $3 }' | grep "_" | cut -d_ -f2 | xargs) + fi + if [ -z "$Model" ]; then + Model=$(echo "$Test_hdparm" | grep -m 1 "Model Number:" | awk 'NR==1{ print $3 }' | grep " " | cut -d" " -f2 | xargs) + fi + if [ -z "$Model" ]; then + Model=$(echo "$Test_hdparm" | grep -m 1 "Model Number:" | awk 'NR==1{ print $3$4$5$6$7$8$9 }' | xargs) + fi + if [ -z "$Model" ]; then + Model=$(echo "$Test_hdparm" | grep -m 1 "Model Number:" | awk 'NR==1{ print $3$4$5$6$7$8$9 }' | xargs) + fi + + # Attempt to identify drive manufacturer + if [ -z "$Manufacturer" ]; then + Manufacturer=$(echo "$Test_hdparm" | grep -m 1 "Model Number:" | grep "_" | awk 'NR==1{ print $3 }' | cut -d_ -f1 | xargs) + fi + if [ -z "$Manufacturer" ]; then + Manufacturer=$(echo "$Test_hdparm" | grep -m 1 "Model Number:" | grep " " | awk 'NR==1{ print $3 }' | cut -d" " -f1 | xargs) + fi + if [ -z "$Manufacturer" ]; then + Manufacturer=$(echo "$Test_hdparm" | grep -m 1 "Model Number:" | awk 'NR==1{ print $3 }' | xargs) + fi + + # Identify drive type, form factor + if [ -z "$FormFactor" ]; then + FormFactor=$(echo "$Test_hdparm" | grep -m 1 "Form Factor:" | cut -d: -f2 | cut -d' ' -f1 | xargs) + if [ -z "$FormFactor" ]; then + FormFactor="2.5" + fi + fi + + # Attempt to get drive capacity + DriveCapacity=$(echo "$Test_hdparm" | grep -m 1 "device size with M = 1000*" | cut -d\( -f2 | cut -d' ' -f1 | xargs) + if [ -n "$DriveCapacity" ]; then + if [ "$DriveCapacity" -eq "$DriveCapacity" ] 2>/dev/null; then + Capacity=$DriveCapacity"G" + fi + fi + + # Attempt to identify drive type + DriveType=$(echo "$Test_hdparm" | grep -m 1 "Nominal Media Rotation Rate:" | cut -d: -f2 | xargs) + case "$DriveType" in + 5400 | 7200 | 10000) + Product="HDD" + Class="HDD ($FormFactor\" SATA)" + ;; + "Solid State Device") + Product="SSD" + Class="SSD ($FormFactor\" SATA)" + ;; + *) + Product="USB Flash" + Class="USB Flash Drive" + ;; + esac + + # Identify hardware and firmware versions of drive + Version=$(echo "$Test_udevadm" | grep -m 1 "{version}" | cut -d= -f3 | cut -d\" -f2 | xargs) + Firmware=$(echo "$Test_hdparm" | grep -m 1 "Firmware Revision:" | awk 'NR==1{ print $3$4$5$6 }') + if [ -n "$Firmware" ]; then + Firmware=$(echo "$Test_hdparm" | grep -m 1 "Firmware Revision:" | awk 'NR==1{ print $3$4$5$6 }') + fi +fi + +# Run HDParm tests +Print_Style "Running HDParm tests ..." "$YELLOW" +sync +sync +HDParm=$(hdparm -Tt --direct "$BootDrive" 2>/dev/null | sed '/^[[:space:]]*$/d') +if [ -z "$HDParm" ]; then + HDParm=$(hdparm -Tt "$BootDrive" 2>/dev/null | sed '/^[[:space:]]*$/d') +fi +if [ -z "$HDParm" ]; then + HDParm=$(hdparm -T --direct "$BootDrive" 2>/dev/null | sed '/^[[:space:]]*$/d') +fi +if [ -z "$HDParm" ]; then + HDParm=$(hdparm -T "$BootDrive" 2>/dev/null | sed '/^[[:space:]]*$/d') +fi +if [ -z "$HDParm" ]; then + HDParm=$(hdparm -t --direct "$BootDrive" 2>/dev/null | sed '/^[[:space:]]*$/d') +fi +if [ -z "$HDParm" ]; then + HDParm=$(hdparm -t "$BootDrive" 2>/dev/null | sed '/^[[:space:]]*$/d') +fi +Print_Style "$HDParm" "$NORMAL" +HDParmDisk=$(echo "$HDParm" | grep "disk reads:" | awk 'NR==1{ print $11 }' | sed 's/;/!/g') +HDParmDiskUnit=$(echo "$HDParm" | grep "disk reads:" | awk 'NR==1{ print $12 }' | sed 's/;/!/g') +HDParmCached=$(echo "$HDParm" | grep "cached reads:" | awk 'NR==1{ print $11 }' | sed 's/;/!/g') +HDParmCachedUnit=$(echo "$HDParm" | grep "disk reads:" | awk 'NR==1{ print $12 }' | sed 's/;/!/g') +Print_Style "HDParm: $HDParmDisk $HDParmDiskUnit - HDParmCached: $HDParmCached $HDParmCachedUnit" "$YELLOW" + +# Run DD tests +Print_Style "Running dd tests ..." "$YELLOW" +sync +sync +DDWrite=$(dd if=/dev/zero of=test bs=4k count=130k conv=fsync 2>&1 | sed '/^[[:space:]]*$/d') +DDWriteResult=$(echo "$DDWrite" | tail -n 1 | awk 'NR==1{ print $(NF-1) }' | sed 's/,/./g' | sed 's/s,//g') +DDWriteUnit=$(echo "$DDWrite" | tail -n 1 | awk 'NR==1{ print $(NF) }' | sed 's/,/./g' | sed 's/s,//g') + +echo "$DDWrite" +Print_Style "DD Write Speed: $DDWriteResult $DDWriteUnit" "$YELLOW" +rm -f test + +# Run fio tests +Print_Style "Running fio write test ..." "$YELLOW" +sync +sync +fio4kRandWrite=$(fio --minimal --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=test --filename=test --bs=4k --iodepth=64 --size=80M --readwrite=randwrite | sed 's/;/!/g') +fio4kRandWriteIOPS=$(echo "$fio4kRandWrite" | awk -F '!' '{print $49}') +fio4kRandWriteSpeed=$(echo "$fio4kRandWrite" | awk -F '!' '{print $48}') +rm -f test +Print_Style "Running fio read test ..." "$YELLOW" +sync +sync +fio4kRandRead=$(fio --minimal --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=test --filename=test --bs=4k --iodepth=64 --size=80M --readwrite=randread | sed 's/;/!/g') +fio4kRandReadIOPS=$(echo "$fio4kRandRead" | awk -F '!' '{print $8}') +fio4kRandReadSpeed=$(echo "$fio4kRandRead" | awk -F '!' '{print $7}') +Print_Style "FIO results - 4k RandWrite: $fio4kRandWriteIOPS IOPS ($fio4kRandWriteSpeed KB/s) - 4k RandRead: $fio4kRandReadIOPS IOPS ($fio4kRandReadSpeed KB/s)" "$YELLOW" +rm -f test + +# Run iozone tests +Print_Style "Running iozone test ..." "$YELLOW" +sync +sync +if [ -z "$(which iozone)" ]; then + IOZone=$(iozone/src/current/./iozone -a -e -I -i 0 -i 1 -i 2 -s 80M -r 4k) +else + IOZone=$(iozone -a -e -I -i 0 -i 1 -i 2 -s 80M -r 4k) +fi +IO4kRandRead=$(echo "$IOZone" | tail -n 3 | awk 'NR==1{ print $7 }') +IO4kRandWrite=$(echo "$IOZone" | tail -n 3 | awk 'NR==1{ print $8 }') +IO4kRead=$(echo "$IOZone" | tail -n 3 | awk 'NR==1{ print $5 }') +IO4kWrite=$(echo "$IOZone" | tail -n 3 | awk 'NR==1{ print $3 }') +IOZone=$(echo "$IOZone" | sed '/^[[:space:]]*$/d' | grep -v 'Contrib\|Rhine\|Landherr\|Dunlap\|Blomberg\|Strecker\|Xue,\|Vikentsi\|Alexey') +echo "$IOZone" +Print_Style "RandRead: $IO4kRandRead - RandWrite: $IO4kRandWrite - Read: $IO4kRead - Write: $IO4kWrite" "$YELLOW" + +# Get brand information +Print_Style "Enter a description of your storage and setup (Example: Kingston A400 SSD on Pi 4 using StarTech SATA to USB adapter)" $GREEN +read -r -t 0.001 tmpudev) +# tmpInxi=$(echo -e "$Test_inxi" >tmpinxi) +# tmpLshw=$(echo -e "$Test_lshw" >tmplshw) +# tmpHwinfo=$(echo -e "$Test_hwinfo" >tmphwinfo) +# tmpDmi=$(echo -e "$Test_dmidecode" >tmpdmi) +# tmpDmesg=$(echo -e "$Test_dmesg" >tmpdmesg) +# tmpSmart=$(echo -e "$Test_smartctl" >tmpsmart) +# tmpParm=$(echo -e "$Test_sdparm" >tmpparm) +# Submit=$(curl -s -k -L --form "form_tools_form_id=1" --form "DDTest=$DDWrite" --form "DDWriteSpeed=$DDWriteResult" --form "HDParmDisk=$HDParmDisk" --form "HDParmCached=$HDParmCached" --form "HDParm=$HDParm" --form "fio4kRandRead=$fio4kRandRead" --form "fio4kRandWrite=$fio4kRandWrite" --form "fio4kRandWriteIOPS=$fio4kRandWriteIOPS" --form "fio4kRandReadIOPS=$fio4kRandReadIOPS" --form "fio4kRandWriteSpeed=$fio4kRandWriteSpeed" --form "fio4kRandReadSpeed=$fio4kRandReadSpeed" --form "IOZone=$IOZone" --form "IO4kRandRead=$IO4kRandRead" --form "IO4kRandWrite=$IO4kRandWrite" --form "IO4kRead=$IO4kRead" --form "IO4kWrite=$IO4kWrite" --form "Drive=$BootDrive" --form "Test_hdparm=$Test_hdparm" --form "Test_lsblk=$Test_lsblk" --form "Test_findmnt=$Test_findmnt" --form "Test_lsusb=$Test_lsusb" --form "Test_lshw=