#!/bin/bash
#
# cs_show_memory
#
# (c) 2011-2017 SUSE Linux GmbH, Germany.
# (c) 2018-2019 SUSE LLC
# Author: L.Pinne.
# GNU General Public License v2. No warranty.
# http://www.gnu.org/licenses/gpl.html
#
# Version: 2019-12-05
#
# shellcheck disable=SC1090,SC2128
#
# TODO systemd cgroup
# systemctl show machine.slice | grep -e MemoryLimit -e FragmentPath
# systemctl show system.slice | grep -e MemoryLimit -e FragmentPath
# systemctl show user.slice | grep -e MemoryLimit -e FragmentPath
# 

EXE="$0"
CFG="/etc/ClusterTools2/cs_show_memory"
test -s $CFG && source $CFG
RDM=$RANDOM
JCTL=""
if test -x "$(type -p journalctl)"; then
	JCTL="/tmp/journalctl.$RDM"
	test -z "${SYSTEM_LOG}" &&\
		SYSTEM_LOG="/var/log/messages"
fi
SYSTEM_LOG="${SYSTEM_LOG} ${JCTL}"
test -z "${ZIPPED_LOG}" &&\
        ZIPPED_LOG="/var/log/messages*bz2"
# TODO mcelog

VM_CONFIG_MEMINFO="
MemTotal:
SwapTotal:
HugePages_Total:
CommitLimit:
VmallocTotal:
Hugepagesize:
"

VM_CONFIG_SYSCTL="
vm.dirty_background_bytes
vm.dirty_background_ratio
vm.dirty_bytes
vm.dirty_ratio
vm.pagecache_limit_mb
vm.zone_reclaim_mode
vm.lowmem_reserve_ratio
vm.min_free_kbytes
vm.nr_hugepages
vm.nr_overcommit_hugepages
vm.overcommit_memory
vm.swappiness
kernel.shmmax
kernel.shmall
"

VM_UTILISE_MEMINFO="
Buffers:
Cached:
SwapCached:
Active.anon.:
Inactive.anon.:
Active.file.:
Inactive.file.:
Mlocked:
Dirty:
Shmem:
Slab:
SReclaimable:
Committed_AS:
VmallocUsed:
AnonHugePages:
HardwareCorrupted:
"

#### not used at the moment - why?
# shellcheck disable=SC2034
VM_UTILISE_MEMINFO_CALC="
MemFree:
Available:
SwapFree:
HugePages_Free:
"

MEMLOG_PATTERN="
Error.DRAM
kernel.*not.have.kernel.memory.protection
kernel.*page.allocation.failure
kernel.*segfault.at
Out.of.memory
invoked.oom-killer
pagecache.limit.set
"

CFGVAR="
VM_CONFIG_MEMINFO
VM_CONFIG_SYSCTL
VM_UTILISE_MEMINFO
VM_UTILISE_MEMINFO_CALC
MEMLOG_PATTERN
"

function help() {
	echo "usage:	$(basename "$EXE")"
	echo "	$(basename "$EXE") [OPTION]"
	echo
	echo " --help		show help"
	echo " --version	show version"
	echo " --writecfg	show some internal variables"
	echo " --all		show memory configuration and utilisation"
	echo " --configured	show memory configuration"
	echo " --logs		show memory related log messages"
	echo " --utilised	show memory utilisation"
	echo " --payload	estimate memory needed for payload"
	echo
}


function writecfg(){
	echo -e "# $CFG \n# For SLES11,12.\n#"
	for c in $CFGVAR; do
		echo "${c}=\""
		# shellcheck disable=SC2086
		echo ${!c} | tr " " "\n"
		echo "\""
		echo "#"
	done
}


function show_configured() {
	# systemd, cgroup
	# ulimit
	# hugepages, transparent hugepages
	# nfs?
	# tcp bucket, ...?

	# TODO formatting
	echo "### $FUNCNAME"
	echo
	numactl -H | grep -e "^node.*cpus:" -e "^node.*size:"
	echo
	for s in $VM_CONFIG_MEMINFO; do
		grep "^$s" /proc/meminfo
	done
	echo
	for s in $VM_CONFIG_SYSCTL; do
		# TODO bash for .->/
		f=$(echo "$s" | tr "." "/")
		echo -n "$s: "
		cat /proc/sys/"$f"
	done
	echo
	F="/sys/kernel/mm/transparent_hugepage/enabled"
	echo -n "$F: "
	cat $F
	echo
	# TODO formatting
	F="/proc/cgroups" 
	echo -n "$F: "
	awk '$1=="memory" {print $1,$4}' $F
	echo
	df -h | grep tmpfs
	echo

}


function show_utilised() {
	# ulimit
	# cgroups memory
	# shmem, shmax, shmall
	# tmpfs
	# hugepages, transparent hugepages
	# available (12)
	# ...?

	echo "### $FUNCNAME"
	echo
	numactl -H | grep -e "^node.*free:"
	# TODO numa_used = size - free
	numastat | grep -e "node[0-9]" -e "^numa_miss" | tr -s " "

	echo
	for s in $VM_UTILISE_MEMINFO; do
		grep "^$s" /proc/meminfo
	done
	# TODO HugePages_Used = HugePages_Total: - HugePages_Free:
	# TODO Swap_Used = SwapTotal: - SwapFree:
	#
	echo
	df -h | grep tmpfs
	echo
}


function show_logs() {
	echo "### $FUNCNAME"

	LOG="$SYSTEM_LOG $ZIPPED_LOG"
	echo "logs: $LOG" >/dev/stderr
	command -v journalctl 2>/dev/null && journalctl --no-pager >$JCTL
	# TODO: more efficient loop
        for e in ${MEMLOG_PATTERN}; do
		echo -n "$e = "
		for f in ${LOG}; do
			test -r "$f" && cat "$f"
		done | zgrep -ic "$e"
	done
	rm -f $JCTL
}


function show_payload() {
	# Commited_AS + KernelStack + SUnreclaim + Shmem + tmpfs ?

	echo "### $FUNCNAME"
	echo
	/usr/bin/systemd-cgtop -m -b -P --iterations=1 |\
		awk -F" " '{print $1" "$2" "$4}'
	echo
	/bin/systemctl --no-pager show |\
		grep -e DefaultLimit -e DefaultTasksMax
	#TODO MemoryLimit ?
}


# main()
case $1 in
	 -v|--version)
		echo -n "$(basename "$EXE") "
		head -11 "$EXE" | grep "^# Version: "
		exit
	;;
	-h|--help)
		help
		exit
	;;
	-w|--writecfg)
		writecfg
		exit
	;;
	-c|--configured)
		show_configured
		exit
	;;
	-u|--utilised)
		show_utilised
		exit
	;;
	-p|--payload)
		show_payload
		exit
	;;
	-l|--logs)
		show_logs
		exit
	;;
	-a|--all|*)
		show_configured
		show_utilised
		show_payload
		show_logs
		exit
	;;
esac
#
