diff --git a/cachestats.sh b/cachestats.sh index 09378e9..c4aafe0 100644 --- a/cachestats.sh +++ b/cachestats.sh @@ -5,50 +5,63 @@ # SPDX-License-Identifier: GPL-3.0-or-later # Copyright 2023 Forza -device_name="$1" -# Declare an associative array to store key-value pairs -declare -A data -declare -i sizess +# cacheatats usage +usage() { + echo "Usage: $(basename "${0}") [DEVICE_NAME or PATH] [DEVICE_NAME or PATH] ..." + echo " -h, --help Display this help message" +} + +# Check for help option or no arguments +if [ "$#" -eq 0 ]; then + usage + exit 1 +fi +if [ "${1:0:1}" == "-" ]; then + case "$1" in + -h|--help) + usage + exit 1 + ;; + *) + echo "Invalid option: $1" + usage + exit 1 + ;; + esac +fi + +# Declare used variables +declare -A data # associative array +declare -a dmstatus # indexed array +declare -i sizess # integer variable declare status_output -declare -a dmstatus +declare device_name_or_path +declare device_name # Function to convert bytes to IEC units to_iec() { - local bytes kib mib gib tib + local -i bytes kib mib gib tib # integers bytes=$1 kib=$(( (bytes + 512) / 1024 )) - mib=$(( (kib + 512) / 1024 )) - gib=$(( (mib + 512) / 1024 )) - tib=$(( (gib + 512) / 1024 )) + mib=$(( (kib + 512) / 1024 )) + gib=$(( (mib + 512) / 1024 )) + tib=$(( (gib + 512) / 1024 )) if [ $tib -gt 0 ]; then - echo "${tib} TiB" + echo "$tib TiB" elif [ $gib -gt 0 ]; then - echo "${gib} GiB" + echo "$gib GiB" elif [ $mib -gt 0 ]; then - echo "${mib} MiB" + echo "$mib MiB" elif [ $kib -gt 0 ]; then - echo "${kib} KiB" + echo "$kib KiB" else - echo "${bytes} bytes" + echo "$bytes bytes" fi } +debug_output(){ + # DEBUG info -# Get the status output using dmsetup -status_output=$(dmsetup status "$device_name" 2>/dev/null) - -# Check if the device exists and has valid output -if [ -n "$status_output" ]; then - sizess=$(blockdev --getss "/dev/mapper/${device_name}") - - # Parse the status information into the associative array - IFS='/: ' read -r -a dmstatus <<< "$status_output" - - ## Ouput `dmsetup status` raw data: - # for ((i = 0; i < ${#dmstatus[@]}; i += 1)); do - # echo \# $i: ${dmstatus["$i"]} - # done - # Field order of `dmsetup status `: # https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/cache.html # @@ -59,77 +72,112 @@ if [ -n "$status_output" ]; then # <#core args> * <#policy args> * # - data["name"]="$device_name" - data["origin_start"]=$(( ${dmstatus[0]} * $sizess )) # in sectors - data["origin_length"]=$(( ${dmstatus[1]} * $sizess )) # in sectors - data["table_type"]="${dmstatus[2]}" - data["metadata_block_size"]=$(( ${dmstatus[3]} * $sizess )) # in sectors - data["used_metadata_blocks"]="${dmstatus[4]}" - data["total_metadata_blocks"]="${dmstatus[5]}" - data["cache_block_size"]=$(( ${dmstatus[6]} * $sizess )) # in sectors - data["used_cache_blocks"]="${dmstatus[7]}" - data["total_cache_blocks"]="${dmstatus[8]}" - data["read_hits"]="${dmstatus[9]}" - data["read_misses"]="${dmstatus[10]}" - data["write_hits"]="${dmstatus[11]}" - data["write_misses"]="${dmstatus[12]}" - data["demotions"]="${dmstatus[13]}" - data["promotions"]="${dmstatus[14]}" - data["dirty_cache"]=$(( ${dmstatus[15]} * ${data[cache_block_size]} )) - if [ "${dmstatus[16]}" = 2 ]; then - data["cache_type"]="${dmstatus[17]}" - data["discard_passdown"]="${dmstatus[18]}" - data["migration_threshold"]=$(( ${dmstatus[21]} * $sizess )) - data["cache_policy"]="${dmstatus[22]}" - data["smq_count"]="${dmstatus[23]}" - data["cache_rw"]="${dmstatus[24]}" - data["status"]="${dmstatus[25]//-/OK}" - else - data["cache_type"]="${dmstatus[17]}" - data["discard_passdown"]="true" - data["migration_threshold"]=$(( ${dmstatus[20]} * $sizess )) - data["cache_policy"]="${dmstatus[21]}" - data["smq_count"]="${dmstatus[22]}" - data["cache_rw"]="${dmstatus[23]}" - data["status"]="${dmstatus[24]//-/OK}" + # Loop through throuh all fields to list and print their values + if (( DEBUG > 0 )) ; then + printf "\nDEBUG INFO\n" + printf "========\n" + printf "dmstatus index: value\n" + ## Ouput `dmsetup status` raw data: + for ((i = 0; i < ${#dmstatus[@]}; i += 1)); do + echo "# $i: ${dmstatus[$i]}" + done + printf "\nidentified values:\n" + printf "========\n" + for field in "${!data[@]}"; do + value="${data[$field]}" + if [ -n "$value" ]; then + echo "$field : $value" + else + echo "Field '$field' not found." + fi + done | sort + printf "========\n" fi +} -# Loop through throuh all fields to list and print their values -# for field in "${!data[@]}"; do -# value="${data[$field]}" -# if [ -n "$value" ]; then -# echo "$field : $value" -# else -# echo "Field '$field' not found." -# fi -# done |sort +# Loop through all devices and print their info +for device_name_or_path in "${@}"; do - # Output defined fields - printf "DEVICE\n" - printf "========\n" - printf "%-*s%s\n" "26" "Device-mapper name: " "/dev/mapper/${data[name]}" - printf "%-*s%s\n" "26" "Origin size: " "$(to_iec $(( ${data[origin_length]} - ${data[origin_start]} )) )" - printf "%-*s%s\n" "26" "Discards: " "${data[discard_passdown]}" + # Strip path part from device name + device_name=$(basename "${device_name_or_path}") - printf "\n" - printf "CACHE\n" - printf "========\n" - printf "%-*s%s\n" "26" "Size / Usage: " "$(to_iec $((${data[total_cache_blocks]} * ${data[cache_block_size]}))) / $(to_iec $(( ${data[used_cache_blocks]} * ${data[cache_block_size]} ))) ($(( 100 * ${data[used_cache_blocks]} / ${data[total_cache_blocks]} )) %)" - printf "%-*s%s\n" "26" "Read Hit Rate: " "${data[read_hits]} / $((${data[read_misses]} + ${data[read_hits]})) ($(( 100 * ${data[read_hits]} / (${data[read_hits]} + ${data[read_misses]}) )) %)" - printf "%-*s%s\n" "26" "Write Hit Rate: " "${data[write_hits]} / $((${data[write_misses]} + ${data[write_hits]})) ($(( 100 * ${data[write_hits]} / (${data[write_hits]} + ${data[write_misses]}) )) %)" - printf "%-*s%s\n" "26" "Dirty: " "$(to_iec ${data[dirty_cache]})" - printf "%-*s%s\n" "26" "Block Size: " "$(to_iec ${data[cache_block_size]})" - printf "%-*s%s\n" "26" "Promotions / Demotions: " "${data[promotions]} / ${data[demotions]}" - printf "%-*s%s\n" "26" "Migration Threshold: " "$(to_iec ${data[migration_threshold]})" - printf "%-*s%s\n" "26" "Read-Write mode: " "${data[cache_rw]}" - printf "%-*s%s\n" "26" "Type: " "${data[cache_type]}" - printf "%-*s%s\n" "26" "Policy: " "${data[cache_policy]}" - printf "%-*s%s\n" "26" "Status: " "${data[status]}" + # Get the status output using dmsetup + status_output=$(dmsetup status "${device_name}" 2>/dev/null) - printf "\n" - printf "METADATA\n" - printf "========\n" - printf "%-*s%s\n" "26" "Size / Usage: " "$(to_iec $(( ${data[total_metadata_blocks]} * ${data[metadata_block_size]} ))) / $(to_iec $(( ${data[used_metadata_blocks]} * ${data[metadata_block_size]} ))) ($(( 100 * ${data[used_metadata_blocks]} / ${data[total_metadata_blocks]} )) %)" -else - echo "Device not found or no valid status output." -fi + # Check if the device exists and has valid output + if [ -n "$status_output" ]; then + # Check device block size + sizess=$(blockdev --getss "/dev/mapper/${device_name}") + + # Parse the dmstatus information into the associative array + IFS='/: ' read -r -a dmstatus <<< "${status_output}" + + # Populate the data array + data["name"]="$device_name" + data["origin_start"]=$(( dmstatus[0] * sizess )) # in sectors + data["origin_length"]=$(( dmstatus[1] * sizess )) # in sectors + data["table_type"]="${dmstatus[2]}" + data["metadata_block_size"]=$(( dmstatus[3] * sizess )) # in sectors + data["used_metadata_blocks"]="${dmstatus[4]}" + data["total_metadata_blocks"]="${dmstatus[5]}" + data["cache_block_size"]=$(( dmstatus[6] * sizess )) # in sectors + data["used_cache_blocks"]="${dmstatus[7]}" + data["total_cache_blocks"]="${dmstatus[8]}" + data["read_hits"]="${dmstatus[9]}" + data["read_misses"]="${dmstatus[10]}" + data["write_hits"]="${dmstatus[11]}" + data["write_misses"]="${dmstatus[12]}" + data["demotions"]="${dmstatus[13]}" + data["promotions"]="${dmstatus[14]}" + data["dirty_cache"]=$(( dmstatus[15] * data[cache_block_size] )) + if [ "${dmstatus[16]}" = 2 ]; then + data["cache_type"]="${dmstatus[17]}" + data["discard_passdown"]="${dmstatus[18]}" + data["migration_threshold"]=$(( dmstatus[21] * sizess )) + data["cache_policy"]="${dmstatus[22]}" + data["smq_count"]="${dmstatus[23]}" + data["cache_rw"]="${dmstatus[24]}" + data["status"]="${dmstatus[25]//-/OK}" + else + data["cache_type"]="${dmstatus[17]}" + data["discard_passdown"]="true" + data["migration_threshold"]=$(( dmstatus[20] * sizess )) + data["cache_policy"]="${dmstatus[21]}" + data["smq_count"]="${dmstatus[22]}" + data["cache_rw"]="${dmstatus[23]}" + data["status"]="${dmstatus[24]//-/OK}" + fi + + # Print debug information + debug_output + + # Print dm-cache data + printf "\nDEVICE\n" + printf "========\n" + printf "%-*s%s\n" "26" "Device-mapper name: " "/dev/mapper/${data[name]}" + printf "%-*s%s\n" "26" "Origin size: " "$(to_iec $(( data[origin_length] - data[origin_start] )) )" + printf "%-*s%s\n" "26" "Discards: " "${data[discard_passdown]}" + + printf "\n" + printf "CACHE\n" + printf "========\n" + printf "%-*s%s\n" "26" "Size / Usage: " "$(to_iec $(( data[total_cache_blocks] * data[cache_block_size] ))) / $(to_iec $(( data[used_cache_blocks] * data[cache_block_size] ))) ($(( 100 * data[used_cache_blocks] / data[total_cache_blocks] )) %)" + printf "%-*s%s\n" "26" "Read Hit Rate: " "${data[read_hits]} / $(( data[read_misses] + data[read_hits] )) ($(( 100 * data[read_hits] / (data[read_hits] + data[read_misses] ) )) %)" + printf "%-*s%s\n" "26" "Write Hit Rate: " "${data[write_hits]} / $(( data[write_misses] + data[write_hits] )) ($(( 100 * data[write_hits] / ( data[write_hits] + data[write_misses] ) )) %)" + printf "%-*s%s\n" "26" "Dirty: " "$(to_iec "${data[dirty_cache]}")" + printf "%-*s%s\n" "26" "Block Size: " "$(to_iec "${data[cache_block_size]}")" + printf "%-*s%s\n" "26" "Promotions / Demotions: " "${data[promotions]} / ${data[demotions]}" + printf "%-*s%s\n" "26" "Migration Threshold: " "$(to_iec "${data[migration_threshold]}")" + printf "%-*s%s\n" "26" "Read-Write mode: " "${data[cache_rw]}" + printf "%-*s%s\n" "26" "Type: " "${data[cache_type]}" + printf "%-*s%s\n" "26" "Policy: " "${data[cache_policy]}" + printf "%-*s%s\n" "26" "Status: " "${data[status]}" + + printf "\n" + printf "METADATA\n" + printf "========\n" + printf "%-*s%s\n" "26" "Size / Usage: " "$(to_iec $(( data[total_metadata_blocks] * data[metadata_block_size] ))) / $(to_iec $(( data[used_metadata_blocks] * data[metadata_block_size] ))) ($(( 100 * data[used_metadata_blocks] / data[total_metadata_blocks] )) %)" + else + echo "Device not found or no valid status output." + fi +done