#!/usr/bin/ksh93

########################################################################
#                                                                      #
#               This software is part of the ast package               #
#                 Copyright (c) 2010-2012 Roland Mainz                 #
#                      and is licensed under the                       #
#                 Eclipse Public License, Version 1.0                  #
#                    by AT&T Intellectual Property                     #
#                                                                      #
#                A copy of the License is available at                 #
#          http://www.eclipse.org/org/documents/epl-v10.html           #
#         (with md5 checksum b35adb5213ca9657e911e9befb180842)         #
#                                                                      #
#                                                                      #
#                 Roland Mainz <roland.mainz@nrubsig.org>              #
#                                                                      #
########################################################################

#
# Copyright (c) 2010, 2013, Roland Mainz. All rights reserved.
#

#
# cpvvmstat - convert vmstat output into a stream of compound variables
#

# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
export PATH='/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin'

# Make sure all math stuff runs in the "C" locale to avoid problems
# with alternative # radix point representations (e.g. ',' instead of
# '.' in de_DE.*-locales). This needs to be set _before_ any
# floating-point constants are defined in this script).
if [[ "${LC_ALL-}" != '' ]] ; then
	export \
		LC_MONETARY="${LC_ALL}" \
		LC_MESSAGES="${LC_ALL}" \
		LC_COLLATE="${LC_ALL}" \
		LC_CTYPE="${LC_ALL}"
		unset LC_ALL
fi
export LC_NUMERIC='C'


function fatal_error
{
	print -u2 "${progname}: $*"
	exit 1
}


function main_loop
{
	compound flags=(
		bool p=false
		bool s=true
		bool Tu=false
		bool Td=true
	)
	
	vmstat -T d -S 1 |
	{	
		integer i
		typeset s
		typeset line
		typeset dummy
		compound fieldpos=(
			integer start
			integer end
		)
		typeset timestamp='' # date for -Tu/Td
		typeset -a cpvfields
		
		if (( flags.Tu || flags.Td )) ; then
			bool flags.T=true
		else
			bool flags.T=false
		fi
		
		# read field information from "vmstat"
		(( flags.T )) && read dummy
		read dummy 		# ignore "kthr memory page disk faults cpu"
		read -A vmfields	# load fields	
		read dummy		# ignore first line of vmstat data
	
		# dynamically build a list of fields
		if (( flags.p )) ; then
			# fields:
	        	# memory           page          executable      anonymous      filesystem
	   		# swap  free  re  mf  fr  de  sr  epi  epo  epf  api  apo  apf  fpi  fpo  fpf
			cpvfields+=( 'rec.memory.swap' )
			cpvfields+=( 'rec.memory.free' )
			cpvfields+=( 'rec.page.re' )
			cpvfields+=( 'rec.page.mf' )
			cpvfields+=( 'rec.page.fr' )
			cpvfields+=( 'rec.page.de' )
			cpvfields+=( 'rec.page.sr' )
	
			cpvfields+=( 'rec.executable.epi' )
			cpvfields+=( 'rec.executable.epo' )
			cpvfields+=( 'rec.executable.epf' )
	
			cpvfields+=( 'rec.anonymous.api' )
			cpvfields+=( 'rec.anonymous.apo' )
			cpvfields+=( 'rec.anonymous.apf' )
	
			cpvfields+=( 'rec.filesystem.fpi' )
			cpvfields+=( 'rec.filesystem.fpo' )
			cpvfields+=( 'rec.filesystem.fpf' )
	
			# declare sub-variables
			compound rec=(
				compound memory=( integer swap free )
				compound page=( integer re mf fr de sr )
				compound executable=( integer epi epo epf )
				compound anonymous=( integer api apo apf )
				compound filesystem=( integer fpi fpo fpf )
			)
		else
			# fields:
	                # kthr      memory            page            disk          faults      cpu
	                # r b w   swap  free  re  mf pi po fr de sr s0 s2 -- --   in   sy   cs us sy id
			cpvfields+=( 'rec.kthr.r' )
			cpvfields+=( 'rec.kthr.b' )
			cpvfields+=( 'rec.kthr.w' )
			cpvfields+=( 'rec.memory.swap' )
			cpvfields+=( 'rec.memory.free' )
			if (( flags.s )) ; then
				cpvfields+=( 'rec.page.si' )
				cpvfields+=( 'rec.page.so' )
			else
				cpvfields+=( 'rec.page.re' )
				cpvfields+=( 'rec.page.mf' )
			fi
			cpvfields+=( 'rec.page.pi' )
			cpvfields+=( 'rec.page.po' )
			cpvfields+=( 'rec.page.fr' )
			cpvfields+=( 'rec.page.de' )
		
			# list of disks is dynamic (per machine)
			(( fieldpos.start=${#cpvfields[@]} , fieldpos.end=${#vmfields[@]}-6 ))
				for (( i=fieldpos.start ; i <= fieldpos.end ; i++ )) ; do
				s="${vmfields[i]}"
				[[ "${s}" == 'in' && "${vmfields[i+1]}" == 'sy' ]] && break
				[[ "${s}" == -*   ]] && s="disk_$(( i-fieldpos.start ))"
				cpvfields+=( "rec.disk[${s}]" )
			done
			[[ "${s}" == 'in' ]] || fatal_error "Unexpected field '${s}'" # assert
	
			cpvfields+=( 'rec.faults.in' )
			cpvfields+=( 'rec.faults.sy' )
			cpvfields+=( 'rec.cpu.cs' )
			cpvfields+=( 'rec.cpu.us' )
			cpvfields+=( 'rec.cpu.sy' )
			cpvfields+=( 'rec.cpu.id' )
		
			# declare sub-variables
			compound rec=(
				compound kthr
				compound memory
				compound page
				integer -A disk
				compound faults
				compound cpu
			)
	
			for s in "${cpvfields[@]}" ; do
				[[ "${s}" == rec.disk* ]] && continue
				integer "${s}"
			done
		fi
	
		if (( flags.Tu )) ; then
			integer rec.utimestamp
		elif (( flags.Td )) ; then
			typeset rec.timestamp
		fi
			
		# read data
		while read -r line ; do
			if [[ "${line}" == ~(E)(kthr.*memory.*page|memory.*page.*executable) ]] ; then
				read dummy
				continue
			fi
	
			# debug only
			# printf "# %s\n# %s" "${vmfields[*]}" "${line}"
			
			if (( flags.T )) && [[ "${timestamp}" == '' ]] ; then
				timestamp="${line}"
				continue
			fi
	
			read "${cpvfields[@]}" dummy <<<"${line} 3.14159" || break
			[[ "${dummy}" == '3.14159' ]] || fatal_error "Internal error, '${dummy}' does not match '3.14159'" # assert
	
			if (( flags.Tu )) ; then
				(( rec.utimestamp=timestamp ))
			elif (( flags.Td )) ; then
				rec.timestamp="${timestamp}"
			fi
			
			# print records
			print -v rec
			
			timestamp=''
		done
	}

	return 0
}


function main
{
	main_loop

	return 0
}


# main
builtin basename

set -o noglob
set -o nounset

typeset progname="${ basename "${0}" ; }"

main "$@"
exit $?

# EOF.

