mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
467 lines
12 KiB
Bash
Executable File
467 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# dasdstat - Tool to print DASD statistics data as formatted table
|
|
#
|
|
# Copyright IBM Corp. 2011, 2017
|
|
#
|
|
# s390-tools is free software; you can redistribute it and/or modify
|
|
# it under the terms of the MIT license. See LICENSE for details.
|
|
#
|
|
|
|
function print_usage() {
|
|
cat <<-EOD
|
|
Usage: $CMD <options> [<statistic>]
|
|
|
|
<options> ::=
|
|
-e|--enable
|
|
Enable the statistics.
|
|
-d|--disable
|
|
Disable the statistics.
|
|
-r|--reset
|
|
Reset the statistics.
|
|
-i|--directory <directory>
|
|
Specify the directory in which the statistics can be found.
|
|
-h|--help
|
|
Print this text and exit.
|
|
-l|--long
|
|
Print more detailed information, e.g differentiate between
|
|
read and write requests.
|
|
-c|--columns <number>
|
|
Format the output in a table with the given number of columns.
|
|
-w|--column-width <width>
|
|
Set the minimum width of the columns in the output table.
|
|
-V|--verbose
|
|
Print more verbose information.
|
|
-v|--version
|
|
Show tools and command version.
|
|
|
|
<statistic>
|
|
Limit operation to one or more statistic.
|
|
EOD
|
|
}
|
|
|
|
function print_version()
|
|
{
|
|
echo "$CMD: version %S390_TOOLS_VERSION%"
|
|
echo "Copyright IBM Corp. 2011, 2017"
|
|
}
|
|
|
|
function unset_known_variables() {
|
|
unset start_time
|
|
unset total_requests
|
|
unset total_sectors
|
|
unset total_pav
|
|
unset total_hpf
|
|
unset histogram_sectors
|
|
unset histogram_io_times
|
|
unset histogram_io_times_weighted
|
|
unset histogram_time_build_to_ssch
|
|
unset histogram_time_ssch_to_irq
|
|
unset histogram_time_ssch_to_irq_weighted
|
|
unset histogram_time_irq_to_end
|
|
unset histogram_ccw_queue_length
|
|
unset total_read_requests
|
|
unset total_read_sectors
|
|
unset total_read_pav
|
|
unset total_read_hpf
|
|
unset histogram_read_sectors
|
|
unset histogram_read_times
|
|
unset histogram_read_time_build_to_ssch
|
|
unset histogram_read_time_ssch_to_irq
|
|
unset histogram_read_time_irq_to_end
|
|
unset histogram_read_ccw_queue_length
|
|
}
|
|
|
|
function print_array()
|
|
{
|
|
local width=$1
|
|
shift
|
|
local linebreak=$1
|
|
shift
|
|
local i=1
|
|
for element in $*
|
|
do
|
|
printf "%${width}s" $element
|
|
(( 0 == i++ % linebreak )) && printf "\n"
|
|
done
|
|
# add an extra line break if we do not already have one
|
|
(( 0 != (i - 1) % linebreak )) && printf "\n"
|
|
}
|
|
|
|
function subtract_array()
|
|
{
|
|
local -a a=( $1 )
|
|
local -a b=( $2 )
|
|
local -a c
|
|
local i
|
|
local cnt=${#a[@]}
|
|
for (( i = 0 ; i < cnt ; i++ ))
|
|
do
|
|
(( c[i] = a[i] - b[i] ))
|
|
done
|
|
echo -n ${c[*]}
|
|
}
|
|
|
|
function print_line()
|
|
{
|
|
local i
|
|
for (( i = 0 ; i < $1 ; i++ ))
|
|
do
|
|
echo -n '-'
|
|
done
|
|
echo
|
|
}
|
|
|
|
HEADERSEXP=" __<4 ___8 __16 __32 __64 _128 _256 _512 \
|
|
__1k __2k __4k __8k _16k _32k _64k 128k \
|
|
_256 _512 __1M __2M __4M __8M _16M _32M \
|
|
_64M 128M 256M 512M __1G __2G __4G _>4G"
|
|
|
|
HEADERSLIN=" ___0 ___1 ___2 ___3 ___4 ___5 ___6 ___7 \
|
|
___8 ___9 __10 __11 __12 __13 __14 __15 \
|
|
__16 __17 __18 __19 __20 __21 __22 __23 \
|
|
__24 __25 __26 __27 __28 __29 __30 __31"
|
|
|
|
|
|
function print_format_standard()
|
|
{
|
|
local converted_time=$(date -d @$start_time)
|
|
# all numbers in the histograms below are smaller or equal to the
|
|
# total_requests. So we use that number to determine the column width.
|
|
local width=${#total_requests}
|
|
(( width++ ))
|
|
(( width < 5 )) && width=5
|
|
|
|
if [[ -n $COLUMN_WIDTH ]] && [[ $width -lt $COLUMN_WIDTH ]]
|
|
then
|
|
width=$COLUMN_WIDTH
|
|
fi
|
|
|
|
local linebreak=$NUMBER_COLUMNS
|
|
local statname="$1"
|
|
local tablewidth
|
|
(( tablewidth = width * linebreak ))
|
|
|
|
# Note: This function does not print the histogram_io_times_weighted
|
|
# and histogram_time_ssch_to_irq_weighted because the interpretation
|
|
# of these histograms is not intuitive and may lead to confusion.
|
|
print_line $tablewidth
|
|
printf "statistics data for statistic: %s\n" "$statname"
|
|
printf "start time of data collection: %s\n\n" "$converted_time"
|
|
|
|
printf "%d dasd I/O requests\n" ${total_requests[*]}
|
|
printf "with %u sectors(512B each)\n" ${total_sectors[*]}
|
|
printf "%d requests used a PAV alias device\n" ${total_pav[*]}
|
|
printf "%d requests used HPF\n" ${total_hpf[*]}
|
|
print_array $width $linebreak $HEADERSEXP
|
|
printf "Histogram of sizes (512B secs)\n"
|
|
print_array $width $linebreak ${histogram_sectors[*]}
|
|
printf "Histogram of I/O times (microseconds)\n"
|
|
print_array $width $linebreak ${histogram_io_times[*]}
|
|
printf "Histogram of I/O time till ssch\n"
|
|
print_array $width $linebreak ${histogram_time_build_to_ssch[*]}
|
|
printf "Histogram of I/O time between ssch and irq\n"
|
|
print_array $width $linebreak ${histogram_time_ssch_to_irq[*]}
|
|
printf "Histogram of I/O time between irq and end\n"
|
|
print_array $width $linebreak ${histogram_time_irq_to_end[*]}
|
|
printf "# of req in chanq at enqueuing (0..31) \n"
|
|
print_array $width $linebreak $HEADERSLIN
|
|
print_array $width $linebreak ${histogram_ccw_queue_length[*]}
|
|
|
|
if [[ $OUTPUT == "short" ]]
|
|
then
|
|
print_line $tablewidth
|
|
return
|
|
fi
|
|
|
|
printf "\n%d dasd I/O read requests\n" ${total_read_requests[*]}
|
|
printf "with %u sectors(512B each)\n" ${total_read_sectors[*]}
|
|
printf "%d requests used a PAV alias device\n" ${total_read_pav[*]}
|
|
printf "%d requests used HPF\n" ${total_read_hpf[*]}
|
|
print_array $width $linebreak $HEADERSEXP
|
|
printf "Histogram of sizes (512B secs)\n"
|
|
print_array $width $linebreak ${histogram_read_sectors[*]}
|
|
printf "Histogram of I/O times (microseconds)\n"
|
|
print_array $width $linebreak ${histogram_read_times[*]}
|
|
printf "Histogram of I/O time till ssch\n"
|
|
print_array $width $linebreak ${histogram_read_time_build_to_ssch[*]}
|
|
printf "Histogram of I/O time between ssch and irq\n"
|
|
print_array $width $linebreak ${histogram_read_time_ssch_to_irq[*]}
|
|
printf "Histogram of I/O time between irq and end\n"
|
|
print_array $width $linebreak ${histogram_read_time_irq_to_end[*]}
|
|
printf "# of req in chanq at enqueuing (0..31) \n"
|
|
print_array $width $linebreak $HEADERSLIN
|
|
print_array $width $linebreak ${histogram_read_ccw_queue_length[*]}
|
|
|
|
printf "\n%d dasd I/O write requests\n" $(( total_requests[0] - total_read_requests[0] ))
|
|
printf "with %u sectors(512B each)\n" $(( total_sectors[0] - total_read_sectors[0] ))
|
|
printf "%d requests used a PAV alias device\n" $(( total_pav[0] - total_read_pav[0] ))
|
|
printf "%d requests used HPF\n" $(( total_hpf[0] - total_read_hpf[0] ))
|
|
print_array $width $linebreak $HEADERSEXP
|
|
printf "Histogram of sizes (512B secs)\n"
|
|
print_array $width $linebreak $(subtract_array "${histogram_sectors[*]}" "${histogram_read_sectors[*]}")
|
|
printf "Histogram of I/O times (microseconds)\n"
|
|
print_array $width $linebreak $(subtract_array "${histogram_io_times[*]}" "${histogram_read_times[*]}")
|
|
printf "Histogram of I/O time till ssch\n"
|
|
print_array $width $linebreak $(subtract_array "${histogram_time_build_to_ssch[*]}" "${histogram_read_time_build_to_ssch[*]}")
|
|
printf "Histogram of I/O time between ssch and irq\n"
|
|
print_array $width $linebreak $(subtract_array "${histogram_time_ssch_to_irq[*]}" "${histogram_read_time_ssch_to_irq[*]}")
|
|
printf "Histogram of I/O time between irq and end\n"
|
|
print_array $width $linebreak $(subtract_array "${histogram_time_irq_to_end[*]}" "${histogram_read_time_irq_to_end[*]}")
|
|
printf "# of req in chanq at enqueuing (0..31) \n"
|
|
print_array $width $linebreak $HEADERSLIN
|
|
print_array $width $linebreak $(subtract_array "${histogram_ccw_queue_length[*]}" "${histogram_read_ccw_queue_length[*]}")
|
|
|
|
print_line $tablewidth
|
|
}
|
|
|
|
function read_stat_data() {
|
|
local file="$1"
|
|
|
|
while read -d ' ' token
|
|
do
|
|
read -a $token
|
|
done < <(cat $file) #avoid inconsistent data due to multiple reads/seeks
|
|
|
|
# differentiate I/O error from disabled statistic
|
|
if [[ -n $total_requests ]] || [[ "$token" == "disabled" ]]
|
|
then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
function print_stat() {
|
|
myfile="$1"
|
|
if [[ ! -f "$myfile" ]]
|
|
then
|
|
print_line 80
|
|
echo "$CMD: Statistic \"$myfile\" does not exist" >&2
|
|
print_line 80
|
|
echo
|
|
return
|
|
fi
|
|
unset_known_variables
|
|
if read_stat_data "$myfile"
|
|
then
|
|
if [[ -z "$total_requests" ]]
|
|
then
|
|
if [[ "$VERBOSE" == "true" ]]
|
|
then
|
|
print_line 80
|
|
echo "statistics \"$f\" are disabled"
|
|
print_line 80
|
|
echo
|
|
fi
|
|
else
|
|
print_format_standard $f
|
|
echo
|
|
fi
|
|
else
|
|
print_line 80
|
|
echo "$CMD: Could not read statistic \"$myfile\" " >&2
|
|
print_line 80
|
|
echo
|
|
fi
|
|
}
|
|
|
|
function enable_stat() {
|
|
myfile="$1"
|
|
if [[ ! -f "$myfile" ]]
|
|
then
|
|
echo "$CMD: Statistic \"$myfile\" does not exist" >&2
|
|
return
|
|
fi
|
|
if echo on > "$myfile"
|
|
then
|
|
echo "enable statistic \"$myfile\""
|
|
else
|
|
echo "$CMD: Failed to enable statistic \"$myfile\"" >&2
|
|
fi
|
|
}
|
|
|
|
function disable_stat() {
|
|
myfile="$1"
|
|
if [[ ! -f "$myfile" ]]
|
|
then
|
|
echo "$CMD: Statistic \"$myfile\" does not exist" >&2
|
|
return
|
|
fi
|
|
if echo off > "$myfile"
|
|
then
|
|
echo "disable statistic \"$myfile\""
|
|
else
|
|
echo "$CMD: Failed to disable statistic \"$myfile\""
|
|
fi
|
|
}
|
|
|
|
function reset_stat() {
|
|
myfile="$1"
|
|
if [[ ! -f "$myfile" ]]
|
|
then
|
|
echo "$CMD: Statistic \"$myfile\" does not exist" >&2
|
|
return
|
|
fi
|
|
if echo reset > "$myfile"
|
|
then
|
|
echo "reset statistic \"$myfile\""
|
|
else
|
|
echo "$CMD: Failed to reset statistic \"$myfile\"" >&2
|
|
fi
|
|
}
|
|
|
|
function verbose_msg() {
|
|
if [[ "$VERBOSE" == "true" ]]
|
|
then
|
|
echo "$*"
|
|
fi
|
|
}
|
|
|
|
|
|
# Evaluating command line options
|
|
CMD=$(basename $0)
|
|
DASD_STATISTICS_DIR=""
|
|
ALLFILES=""
|
|
VERBOSE=false
|
|
OUTPUT="short"
|
|
NUMBER_COLUMNS=16
|
|
COLUMN_WIDTH=
|
|
ACTION="print"
|
|
while [ $# -gt 0 ]; do
|
|
case $1 in
|
|
--help|-h)
|
|
print_usage
|
|
exit 0
|
|
;;
|
|
--enable|-e)
|
|
ACTION="enable"
|
|
;;
|
|
--disable|-d)
|
|
ACTION="disable"
|
|
;;
|
|
--reset|-r)
|
|
ACTION="reset"
|
|
;;
|
|
--directory|-i)
|
|
DASD_STATISTICS_DIR="$2"
|
|
shift
|
|
if [[ ! -d $DASD_STATISTICS_DIR ]]
|
|
then
|
|
echo "$CMD: $DASD_STATISTICS_DIR is not a directory" >&2
|
|
exit 1
|
|
fi
|
|
;;
|
|
--long|-l)
|
|
OUTPUT="extended"
|
|
;;
|
|
--columns|-c)
|
|
NUMBER_COLUMNS="$2"
|
|
if [[ ! "$NUMBER_COLUMNS" -gt 0 ]]
|
|
then
|
|
echo "$CMD: $NUMBER_COLUMNS is not a positive integer number" >&2
|
|
exit 1
|
|
fi
|
|
shift
|
|
;;
|
|
--column-width|-w)
|
|
COLUMN_WIDTH="$2"
|
|
if [[ ! "$COLUMN_WIDTH" -gt 0 ]]
|
|
then
|
|
echo "$CMD: $COLUMN_WIDTH is not a positive integer number" >&2
|
|
exit 1
|
|
fi
|
|
shift
|
|
;;
|
|
--verbose|-V)
|
|
VERBOSE=true
|
|
;;
|
|
--version|-v)
|
|
print_version
|
|
exit 0
|
|
;;
|
|
-*)
|
|
echo "$CMD: Invalid option $1" >&2
|
|
echo "Try '$CMD --help' for more information." >&2
|
|
exit 1
|
|
;;
|
|
*)
|
|
ALLFILES="$ALLFILES $1"
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# if no directory is given on command line, find dasd directory in debugfs
|
|
if [[ "$DASD_STATISTICS_DIR" == "" ]]
|
|
then
|
|
while read -a mntentries
|
|
do
|
|
if [[ "${mntentries[2]}" == "debugfs" ]]
|
|
then
|
|
DASD_STATISTICS_DIR="${mntentries[1]}"
|
|
verbose_msg "found debugfs mount point $DASD_STATISTICS_DIR"
|
|
break;
|
|
fi
|
|
done < /etc/mtab
|
|
if [[ "$DASD_STATISTICS_DIR" == "" ]]
|
|
then
|
|
echo "$CMD: No debugfs mount point found" >&2
|
|
exit 1
|
|
fi
|
|
DASD_STATISTICS_DIR="$DASD_STATISTICS_DIR/dasd"
|
|
if [[ ! -d "$DASD_STATISTICS_DIR" ]]
|
|
then
|
|
echo "$CMD: Default DASD debugfs directory $DASD_STATISTICS_DIR does not exist" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# look for directories that contain statistics
|
|
if [[ "$ALLFILES" == "" ]]
|
|
then
|
|
ALLFILES=$(ls -x $DASD_STATISTICS_DIR)
|
|
explicitstats="false"
|
|
else
|
|
explicitstats="true"
|
|
fi
|
|
ALLSTATS=""
|
|
for f in $ALLFILES
|
|
do
|
|
if [[ -f "$DASD_STATISTICS_DIR/$f/statistics" ]]
|
|
then
|
|
ALLSTATS="$ALLSTATS $f"
|
|
elif [[ $explicitstats == "true" ]]
|
|
then
|
|
echo "$CMD: No statistics found for $f" >&2
|
|
fi
|
|
done
|
|
|
|
verbose_msg "found the following statistics in directory $DASD_STATISTICS_DIR:"
|
|
verbose_msg "$ALLSTATS"
|
|
|
|
# execute the required operation
|
|
for f in $ALLSTATS
|
|
do
|
|
case $ACTION in
|
|
"enable")
|
|
enable_stat "$DASD_STATISTICS_DIR/$f/statistics"
|
|
;;
|
|
"disable")
|
|
disable_stat "$DASD_STATISTICS_DIR/$f/statistics"
|
|
;;
|
|
"reset")
|
|
reset_stat "$DASD_STATISTICS_DIR/$f/statistics"
|
|
;;
|
|
"print")
|
|
print_stat "$DASD_STATISTICS_DIR/$f/statistics"
|
|
;;
|
|
*)
|
|
echo "error"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
|