#!/usr/bin/env bash
#
# test-drive-speed
#
# DESCRIPTION
#
#     This performs a very simple drive speed test.
#     It measures sustained write and read speed to a 256 MB file.
#
# AUTHOR
#
#     Noah Spurrier <noah@noah.org>
#
# LICENSE
#
#     This license is OSI and FSF approved as GPL-compatible.
#     This license identical to the ISC License and is registered with and
#     approved by the Open Source Initiative. For more information vist:
#         http://opensource.org/licenses/isc-license.txt
#
#     Copyright (c) 2013, Noah Spurrier <noah@noah.org>
#
#     Permission to use, copy, modify, and/or distribute this software for any
#     purpose with or without fee is hereby granted, provided that the above
#     copyright notice and this permission notice appear in all copies.
#
#     THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
#     WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
#     MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
#     ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
#     WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
#     ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
#     OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# VERSION
#
#     Version 2
#

# BLOCK_COUNT selects number of megabytes because BLOCK_SIZE is 1 MB.
BLOCK_COUNT=256
BLOCK_SIZE=1048576
OUTPUT_FILE="junk.bin"

#
# Upgrade for next version: Use openssl encryption to generate most of
# the data instead of using urandom for it all. It's about 10 times faster,
# which will be significant for very large sized junk.bin test file.
# dd if=/dev/zero bs=${BLOCK_SIZE} count=${BLOCK_COUNT} |
# openssl enc -aes-256-ctr -pass "pass:$(dd if=/dev/urandom bs=128 count=1 2>/dev/null | base64)" -nosalt > ${OUTPUT_FILE}
#

if [ -d /dev/shm ]; then
    echo "= Create source data file in /dev/shm/random.bin ="
    SOURCE_DATA=/dev/shm/random.bin
    rm -f ${SOURCE_DATA}
    dd if=/dev/urandom of=${SOURCE_DATA} bs=${BLOCK_SIZE} count=${BLOCK_COUNT} iflag=fullblock oflag=dsync conv=fdatasync &
    PIDDD=$!
    # Give kernel time to update the process table; otherwise 'kill' sometimes fails the first time.
    sleep 1
    while kill -USR1 ${PIDDD} 2>/dev/null; do
        sleep 1
        # See man page on terminfo(5) for list of terminal capabilities.
        #     tput cuu1; tput cuu1; tput cuu1; tput ed
        # Move cursor up three lines and clear to end of screen.
        /bin/echo -n -e "\e[A\e[A\e[A\e[J"
    done
    # Move cursor down three lines.
    /bin/echo -n -e "\e[A\e[A\e[A"
else
    echo "= WARNING: Using /dev/zero for data. Results may skewed by cache or compression. ="
    SOURCE_DATA=/dev/zero
fi

sync
echo "uptime: $(uptime)"
PWD=$(pwd)
echo "current directory: ${PWD}"
CURRENT_DEV=$(df -hT "${PWD}" \
 | sed -n -e 's/^\(\/dev\/[^[:space:]]\+\).*/\1/p')

if [ "${CURRENT_DEV}" = "" ]; then
    echo "WARNING: Did not find device associated with current directory."
else
    echo "mount device: ${CURRENT_DEV}"
    echo "drive mount options: $(grep ${CURRENT_DEV} /etc/mtab)"
    if grep ${CURRENT_DEV} /etc/mtab | grep -iq sync; then
        echo "    WARNING: sync option will effect write speed tests."
    fi
    if echo ${CURRENT_DEV} | grep -q /dev/md; then
        echo "    This device is a sofware RAID configured as follows:"
        echo -en "        "
        egrep 'md[0-9]+' /proc/mdstat
        CURRENT_DEV=$(egrep 'md[0-9]+' /proc/mdstat \
            |sed -n -e 's/.*[[:space:]]\([a-z]*[0-9]*\)\[.\]$/\1/p')
        CURRENT_DEV="/dev/${CURRENT_DEV}"
        echo "    Drive info will be taken from last device in the array."
    fi
    # Try `hdparm` otherwise try `lsusb`.
    if ! DRIVE_INFO=$(hdparm -I ${CURRENT_DEV} | sed -n -e '/^$/d' \
        -e '0,/Standards:/p' | sed -n -e '$!p'); then
    DRIVE_INFO=$(lsusb)
fi
DRIVE_INFO=$(echo ${DRIVE_INFO})
echo "drive info: ${DRIVE_INFO}"
fi

echo
echo "Run test three times..."

for n in 1 2 3; do
    echo -n "test ${n}: "
    sync
    dd if=${SOURCE_DATA} of=${OUTPUT_FILE} oflag=dsync conv=fdatasync \
        bs=${BLOCK_SIZE} count=${BLOCK_COUNT} 2>&1 \
        |grep "copied"|cut -f1,6 -d" " \
        |awk '{printf("write: %7.2f MB/s, ",$1/$2/(1024*1024))}'
    sync
    if [ ! -e /dev/zero ]; then
        rm ${SOURCE_DATA}
    fi
    dd if=${OUTPUT_FILE} iflag=direct conv=fdatasync of=${SOURCE_DATA} \
        bs=${BLOCK_SIZE} count=${BLOCK_COUNT} 2>&1 \
        |grep "copied"|cut -f1,6 -d" " \
        |awk '{printf("read:  %7.2f MB/s\n",$1/$2/(1024*1024))}'
    rm junk.bin
done

if [ -e /dev/shm/random.bin ]; then
    rm ${SOURCE_DATA}
fi

# vim:set ft=sh sr et ts=4 sw=4: // See Vim, :help 'modeline'
