Skip to content
Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 390 lines (362 sloc) 9.01 KB
#!/bin/sh
############################################################ IDENT(1)
#
# $Title: Script to show memory usage on FreeBSD $
# $Copyright: 2018 Devin Teske. All rights reserved. $
# $FrauBSD: meminfo/meminfo 2018-08-03 09:37:33 -0700 freebsdfrau $
#
############################################################ GLOBALS
VERSION='$Version: 5.1 $'
pgm="${0##*/}" # Program basename
#
# Global exit status
#
SUCCESS=0
FAILURE=1
#
# Command-line options
#
HUMANIZE=1 # -n
SHOW_ALL_PROCS= # -a
SHOW_ALL_STATS=1 # -A
SHOW_DMESGBOOTINFO= # -B
SHOW_PSINFO= # -P
SHOW_SNMP_STAT= # -o name
SHOW_TOPINFO= # -T
SNMP_STAT= # -o name
#
# Miscellaneous
#
FMT="\t%22s: %s\n"
############################################################ FUNCTIONS
usage()
{
local optfmt="\t%-10s %s\n"
exec >&2
printf "Usage: %s [-AaBnPTV] [-o name]\n" "$pgm"
printf "Options:\n"
printf "$optfmt" "-A" "Show all stats (default)."
printf "$optfmt" "-a" "Show all processes."
printf "$optfmt" "-B" "Show dmesg.boot info."
printf "$optfmt" "-n" "Do not humanize numbers."
printf "$optfmt" "-P" "Show ps info."
printf "$optfmt" "-o name" "Show SNMP OID value by name."
printf "$optfmt" "-T" "Show top info."
printf "$optfmt" "-V" "Print version on stdout and exit."
printf "SNMP OIDs:\n"
printf "$optfmt" "memAvail" "Available memory in bytes."
printf "$optfmt" "memTotal" "Total memory in bytes."
exit $FAILURE
}
humanize()
{
awk -v size="$1" 'BEGIN {
suffix = ""
if (size >= 1024) { size /= 1024; suffix = "K" }
if (size >= 1024) { size /= 1024; suffix = "M" }
if (size >= 1024) { size /= 1024; suffix = "G" }
if (size >= 1024) { size /= 1024; suffix = "T" }
if (size >= 1024) { size /= 1024; suffix = "P" }
if (suffix) {
size = sprintf("%.2f", size)
if (size ~ /\.[0-9]*0+$/) {
sub(/0+$/, "", size)
sub(/\.$/, "", size)
}
}
print size suffix
}'
}
snmp_type()
{
local type="$1"
[ "$SNMP_OID" ] || return $SUCCESS
echo "$SNMP_OID"
echo "$type"
}
dehumanize()
{
awk -v size="$1" 'BEGIN {
if (sub(/P$/, "", size)) size = size * 1024 "T"
if (sub(/T$/, "", size)) size = size * 1024 "G"
if (sub(/G$/, "", size)) size = size * 1024 "M"
if (sub(/M$/, "", size)) size = size * 1024 "K"
if (sub(/K$/, "", size)) size = size * 1024
print int(size)
}'
}
percent()
{
awk -v sum="$1" -v total="$2" 'BEGIN {
percent = sprintf("%.2f", sum * 100 / total)
if (percent ~ /\.[0-9]*0+$/) {
sub(/0+$/, "", percent)
sub(/\.$/, "", percent)
}
print percent "%"
}'
}
# dmesg_boot_collect
#
# Collect information from /var/run/dmesg.boot
#
dmesg_boot_collect()
{
eval "$( awk '
/real memory/ { print "real=" $4 }
/avail memory/ { print "avail=" $4 }
' /var/run/dmesg.boot )"
if [ "$HUMANIZE" ]; then
local var
for var in real avail; do
eval ${var}_human=\$\( humanize \$$var \)
done
fi
}
# sysctl_collect
#
# Collect information from sysctl(8)
#
sysctl_collect()
{
local vars="
vfs.zfs.arc_min vfs.zfs.arc_max
hw.physmem vm.stats.vm.v_wire_count vm.stats.vm.v_page_size
kstat.zfs.misc.arcstats.mfu_evictable_data
kstat.zfs.misc.arcstats.mfu_evictable_metadata
kstat.zfs.misc.arcstats.mru_evictable_data
kstat.zfs.misc.arcstats.mru_evictable_metadata
kstat.zfs.misc.arcstats.size
vfs.bufspace
vm.stats.vm.v_free_count
vm.stats.vm.v_inactive_count
vm.stats.vm.v_laundry_count
" # END-QUOTE
eval "$( sysctl -e $vars 2> /dev/null | awk 'sub(/.*\./, "")' )"
local var
for var in $vars; do
var=${var##*.}
eval : \${$var:=0} 2>&1
[ "$HUMANIZE" ] && eval ${var}_human=\$\( humanize \$$var \)
done
}
# dmesg_boot_info
#
# Print information from /var/run/dmesg.boot
#
dmesg_boot_info()
{
echo "dmesg.boot info:"
if [ "$HUMANIZE" ]; then
printf "$FMT" "Total RAM (avail/real)" \
"$avail_human/$real_human"
else
printf "$FMT" "Total RAM (avail/real)" "$avail/$real"
fi
}
# top_info
#
# Print top(1)/ZFS information
#
top_info()
{
local wired wired_human
local arc arc_human
#
# Collect information from top(1)
#
echo "top info:"
wired=$(( $v_wire_count * $v_page_size ))
arc=$size # kstat.zfs.misc.arcstats.size
if [ "$HUMANIZE" ]; then
local var
for var in wired arc; do
eval ${var}_human=\$\( humanize \$$var \)
done
fi
local free=$(( ($v_free_count + $v_inactive_count + $v_laundry_count)
* $v_page_size ))
local cache=$(( $mfu_evictable_data + $mfu_evictable_metadata
+ $mru_evictable_data + $mru_evictable_metadata + $bufspace ))
local free_plus=$(( $free + $cache ))
local wired_minus=$(( $wired - $cache ))
if [ "$HUMANIZE" ]; then
local free_plus_human="$( humanize $free_plus )"
local wired_minus_human="$( humanize $wired_minus )"
printf "$FMT" "Used Memory (Wired)" \
"$wired_human ($( percent $wired $real ))"
printf "$FMT" "Used w/o Buf/Cache" \
"$wired_minus_human ($( percent $wired_minus $real ))"
printf "$FMT" "Avail (inc. Buf/Cache)" \
"$free_plus_human ($(
percent $free_plus $real
))"
printf "$FMT" "ZFS ARC (min/used/max)" \
"$arc_min_human/$arc_human/$arc_max_human"
else
printf "$FMT" "Used Memory (Wired)" "$wired ($(
percent $wired $real
))"
printf "$FMT" "Used w/o Buf/Cache" \
"$wired_minus ($( percent $wired_minus $real ))"
printf "$FMT" "Avail (inc. Buf/Cache)" \
"$free_plus ($( percent $free_plus $real))"
printf "$FMT" "ZFS ARC (min/used/max)" "$arc_min/$arc/$arc_max"
fi
}
# ps_info
#
# Collect information from ps(1)
#
ps_info()
{
echo "ps info:"
ps axHwwwo rss,pmem,ucomm |
awk -v humanize=$HUMANIZE -v show_all=$SHOW_ALL_PROCS '
BEGIN { getline hdr }
function _asort(src, dest)
{
k = nitems = 0
for (i in src) dest[++nitems] = src[i]
for (i = 1; i <= nitems; k = i++) {
val = dest[i]
while ((k > 0) && (dest[k] > val)) {
dest[k+1] = dest[k]; k--
}
dest[k+1] = val
}
return nitems
}
function format(size, unit) {
unit = "K"
if (humanize) {
if (size >= 1024) { size /= 1024; unit = "M" }
if (size >= 1024) { size /= 1024; unit = "G" }
if (size >= 1024) { size /= 1024; unit = "T" }
if (size >= 1024) { size /= 1024; unit = "P" }
}
if (unit != "K") {
if ((size = sprintf("%.2f", size)) ~ \
/\.[0-9]*0+$/) {
sub(/0+$/, "", size)
sub(/\.$/, "", size)
}
}
return size unit
}
(_rss = $1) (_pmem = $2) {
# _rss
sub(/^[[:space:]]*[^[:space:]]*[[:space:]]*/, "")
# _pmem
sub(/^[[:space:]]*[^[:space:]]*[[:space:]]*/, "")
sub(/ .*/, "", rss[$0])
rss[$0] += _rss
rss[$0] = sprintf("%12u %s", rss[$0], $0)
trss += _rss
pmem[$0] += _pmem
tmem += _pmem
}
END {
n = _asort(rss, sorted_rss)
if (!show_all) {
other_fmt = "<%u others; use -a to see all>"
p = 0
stop = (n <= 10 ? 0 : n - 10)
orss = omem = 0
for (i = 1; i <= stop; i++) {
_rss = x = sorted_rss[i]
sub(/^[[:space:]]*[^[:space:]]+ /,
"", x)
sub(/^[[:space:]]*/, "", _rss)
sub(/ .*/, "", _rss)
orss += _rss
omem += pmem[x]
p++
}
printf "\t%6.2f%% %s %s\n", omem, format(orss),
sprintf(other_fmt, p)
start = n >= 10 ? n - 9 : 1
} else
start = 1
for (i = start; i <= n; i++) {
_rss = x = sorted_rss[i]
sub(/^[[:space:]]*[^[:space:]]+ /, "", x)
sub(/^[[:space:]]*/, "", _rss)
sub(/ .*/, "", _rss)
_rss = int(_rss)
printf "\t%6.2f%% %s %s\n", pmem[x],
format(_rss), x
}
_rss = trss
printf "\t%6.2f%% %s %s\n", tmem, format(_rss), "TOTAL"
}
' # END-QUOTE
}
############################################################ MAIN
#
# Process command-line options
#
while getopts AaBg:hno:PTV flag; do
case "$flag" in
A) SHOW_ALL_STATS=1 ;;
a) SHOW_ALL_PROCS=1 ;;
B) SHOW_DMESGBOOTINFO=1 SHOW_ALL_STATS= ;;
g) SHOW_SNMP_STAT=1 SNMP_OID="$OPTARG" ;;
n) HUMANIZE= ;;
o) SHOW_SNMP_STAT=1 SNMP_STAT="$OPTARG" ;;
P) SHOW_PSINFO=1 SHOW_ALL_STATS= ;;
T) SHOW_TOPINFO=1 SHOW_ALL_STATS= ;;
V) vers="${VERSION#*: }"
echo "${vers% $}"
exit $SUCCESS ;;
*) usage # NOTREACHED
esac
done
shift $(( $OPTIND - 1 ))
#
# Process SNMP oid and exit if given
#
[ "$SHOW_SNMP_STAT" ] && case "$SNMP_STAT" in
memAvail) # w/o buf/cache
snmp_type COUNTER64
HUMANIZE=
sysctl_collect
free=$(( ($v_free_count + $v_inactive_count + $v_laundry_count)
* $v_page_size ))
cache=$(( $mfu_evictable_data + $mfu_evictable_metadata
+ $mru_evictable_data + $mru_evictable_metadata + $bufspace ))
avail=$(( $free + $cache ))
echo $avail
exit $SUCCESS ;;
memTotal)
snmp_type COUNTER64
HUMANIZE=
sysctl_collect
echo $physmem
exit $SUCCESS ;;
*)
echo "$pgm: Unknown SNMP stat \`$SNMP_STAT'" >&2
exit $FAILURE
esac
#
# Select which info to display for `-A' (the default)
#
if [ "$SHOW_ALL_STATS" ]; then
SHOW_DMESGBOOTINFO=1
SHOW_PSINFO=1
SHOW_TOPINFO=1
fi
#
# Display requested info and exit
#
[ "$SHOW_DMESGBOOTINFO" -o "$SHOW_TOPINFO" ] && dmesg_boot_collect
[ "$SHOW_DMESGBOOTINFO" ] && dmesg_boot_info
if [ "$SHOW_TOPINFO" ]; then
sysctl_collect
top_info
fi
[ "$SHOW_PSINFO" ] && ps_info
exit $SUCCESS
################################################################################
# END
################################################################################
You can’t perform that action at this time.