#!/bin/bash

# IBM  "amsstat": Active Memory Sharing statistics gathering tool
#
# Copyright (c) 2009 International Business Machines.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# Authors: 
#      Andrew Theurer <habanero@linux.vnet.ibm.com>
#      Robert Jennings <rcj@linux.vnet.ibm.com>
#
# This script will gather AMS related information on supported Linux systems
# usage:
# amstat <interval in seconds>
#
# If you do not provide an interval, amsstat will display stats only once
#
# For further details on this tool and the fields it displays please
# reference man page amsstat.1

sleep_interval=$1
indent=-4
devstat_data_spacing=-30
lparcfg_data_spacing=-30
lparcfg_file=/proc/ppc64/lparcfg
# Hardcoding pseries_platform path as amstat will be placed in bin
PSERIES_PLATFORM=/usr/sbin/pseries_platform

function print_meminfo_stats {
    echo "System Memory Statistics:"
    OLD_IFS=${IFS}
    IFS="
"
    for stat in `cat /proc/meminfo`; do
    IFS=${OLD_IFS}
        if echo $stat | grep "^MemTotal\|^MemFree\|^Buffers\|^Cached\|^Inactive\|SwapTotal\|SwapFree" >/dev/null; then
            this_stat=`echo $stat | awk -F: '{print $1}'`
            this_value=`echo $stat | awk -F: '{print $2}'`
            printf "%${indent}s %${lparcfg_data_spacing}s %${lparcfg_data_spacing}s\n" " " "$this_stat:" "${this_value##\ }"
        fi
    done

    # Include Desired Memory value from /proc/ppc64/lparcfg
    stat=`grep "^DesMem" $lparcfg_file`
    if [ ! -z "${stat}" ]; then
        this_stat=`echo $stat | awk -F= '{print $1}'`
        this_value=`echo $stat | awk -F= '{print $2}'`
        printf "%${indent}s %${lparcfg_data_spacing}s %${lparcfg_data_spacing}s\n" " " "$this_stat:" "$this_value MB"
    fi
}

function print_entitlement_data {
    echo "Entitlement Information:"
    for stat in `cat $lparcfg_file`; do
        if echo $stat | grep "^entitled_memory\|^mapped_entitled_memory\|^entitled_memory_weight\|entitled_memory_pool_size\|^backing_memory\|^cmo_enabled\|^cmo_faults\|^cmo_fault_time_usec\|cmo_primary_psp\|^cmo_secondary_psp\|^coalesced_bytes\|^pool_coalesced_bytes" >/dev/null; then
            this_stat=`echo $stat | awk -F= '{print $1}'`
            this_value=`echo $stat | awk -F= '{print $2}'`
            printf "%${indent}s %${lparcfg_data_spacing}s %${lparcfg_data_spacing}s\n" " " "$this_stat:" "$this_value"
        fi
    done
}

function print_cmm_stats {
    # CMM kernel parameters
    echo "CMM Statistics:"

    local path=/sys/module/cmm/parameters
    pushd $path >/dev/null 2>&1
    if [ $? -ne 0 ] ; then
        printf "%${indent}s Could not get CMM Statistics.\n" " "
	return
    fi

    for stat in `find . -mindepth 1 -maxdepth 1 -print`; do
        printf "%${indent}s %${devstat_data_spacing}s %${devstat_data_spacing}s\n" " " "${stat#\.\/}:" "`cat $stat`"
    done
    popd >/dev/null

    # CMM statistics
    local path=/sys/devices/system/cmm/cmm0
    pushd $path >/dev/null 2>&1
    if [ $? -ne 0 ] ; then
        return
    fi
    for stat in `find . -mindepth 1 -maxdepth 1 -print`; do
        printf "%${indent}s %${devstat_data_spacing}s %${devstat_data_spacing}s\n" " " "${stat#\.\/}:" "`cat $stat`"
    done
    popd >/dev/null
}

function print_vio_bus_stats {
    echo "VIO Bus Statistics:"
    local found=0
    local path=/sys/bus/vio
    pushd $path >/dev/null 2>&1
    if [ $? -ne 0 ] ; then
        printf "%${indent}s Could not get VIO Bus Statistics.\n" " "
	return
    fi

    for stat in `find . -mindepth 1 -maxdepth 1 -name "cmo*" -print`; do
        found=1
        printf "%${indent}s %${devstat_data_spacing}s %${devstat_data_spacing}s\n" " " "${stat#\.\/}:" "`cat $stat`"
    done
    popd >/dev/null

    if [ "$found" -eq "0" ]; then
        printf "%${indent}s No AMS Busses found.\n" " "
    fi
}


function print_vio_dev_stats {
    echo "VIO Device Statistics:"

    local found=0
    local path=/sys/bus/vio/devices
    pushd $path >/dev/null 2>&1
    if [ $? -ne 0 ] ; then
        printf "%${indent}s Could not get VIO Device Statistics.\n" " "
	return
    fi

    for dir in `find . -mindepth 1 -print`; do
        pushd $dir >/dev/null 2>&1
        if [ $? -ne 0 ] ; then
            break
        fi

	# Skip printing devices that are not using entitlement
        if [ ! -e "cmo_entitled" ]; then
            popd >/dev/null
	    continue
	fi

	value=`cat cmo_entitled`
	if [ ${value} -eq "0" ]; then
		popd >/dev/null
		continue
	fi

	NAME=$(cat devspec)
	echo "   ${NAME##/*/}:"
        for stat in `find . -mindepth 1 -maxdepth 1 -name "cmo*" -print`; do
	    found=1
            printf "%${indent}s %${devstat_data_spacing}s %${devstat_data_spacing}s\n" " " "${stat#\.\/}:" "`cat $stat`"
        done
        popd >/dev/null
    done
    popd >/dev/null

    if [ "$found" -eq "0" ]; then
        printf "%${indent}s No AMS devices found.\n" " "
    fi
}

if [ ! -f $PSERIES_PLATFORM ]; then
	echo "$PSERIES_PLATFORM does not exist"
	echo "amstat: is not supported on the Unknown platform"
	exit 1;
fi

. $PSERIES_PLATFORM
if [[ $platform != $PLATFORM_PSERIES_LPAR ]]; then
     echo "amstat: is not supported on the $platform_name platform"
     exit 1
fi

# Verify CMO is present and enabled
enabled=`cat $lparcfg_file | grep "^cmo_enabled" | awk -F= '{print $2}'`
if [ -z $enabled ]; then
        echo "This system is not capable of Active Memory Sharing."
	exit -1
elif [ "$enabled" -eq "0" ]; then
        echo "Active Memory Sharing is not enabled on this system."
        exit -1
fi

if [ -z $sleep_interval ]; then
    date
    print_meminfo_stats
    print_entitlement_data
    print_cmm_stats
    print_vio_bus_stats
    print_vio_dev_stats
else
    while [ 1 ]; do
        date
        print_meminfo_stats
        print_entitlement_data
        print_cmm_stats
        print_vio_bus_stats
        print_vio_dev_stats
        sleep $sleep_interval
        echo
    done
fi
