#!/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 [] ::= -e|--enable Enable the statistics. -d|--disable Disable the statistics. -r|--reset Reset the statistics. -i|--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 Format the output in a table with the given number of columns. -w|--column-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. 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