#!/bin/bash # mfree --- show linux memory statistics # Author: Noah Friedman # Created: 2009-07-17 # Public domain # $Id: mfree,v 1.5 2018/11/11 21:39:55 friedman Exp $ # Commentary: # This script prints a report on memory statistics similar to the # procps-ng "free" command. The main differences are: # # * Default output is "human readable", with unit suffixes. # * Try to provide up to 4 significant digits, including decimal fractions. # # For the older-style output format from util-linux-ng: # * It gets rid of the useless "shared" column that was always 0 # * Unlike the busybox variant, it shows the -/+ buffers/cache line # # The only external command this script depends on is the coreutils/busybox # "printf" command, for formatting. I can't stand the idea of inlining that. # It will use dc or bc if they are around, otherwise it will approximate inline. # # The busybox sh and mksh on older versions of android may overflow on # some of the multiplication operations. # Code: getmeminfo() { # Don't redirect the while loop directly, because that may put the loop # in a subshell in some bourne implementations. exec < /proc/meminfo while read key val ign ; do case $key in *'('* ) key=`filtername "$key" '(' ')'` ;; esac key=${key%:} # Scale only on output, see mprintf. # This improves accuracy of computed values #val=$((val $op factor)) eval $key=$val done # low==main except with large-memory support case ${LowTotal:-0} in 0 ) LowTotal=$MemTotal LowFree=$MemFree ;; esac HighTotal=${HighTotal:-0} HighFree=${HighFree:-0} # Derived values MemCached=$((Cached + SReclaimable)) # Slab_reclaimable MemUsed=$((MemTotal - MemFree - MemCached - Buffers)) SwapUsed=$((SwapTotal - SwapFree)) LowUsed=$((LowTotal - LowFree)) HighUsed=$((HighTotal - HighFree)) Total=$((MemTotal + SwapTotal)) TotalUsed=$((MemUsed + SwapUsed)) TotalFree=$((MemFree + SwapFree)) # if $MemAvailable is greater than $MemTotal or our calculation of # mem_used overflows, that's symptomatic of running within a lxc container # where such values will be dramatically distorted over those of the host. if [ ${MemAvailable:-0} -gt ${MemTotal:-0} ]; then MemAvailable=$MemFree fi case ${MemAvailable:-0} in 0 ) minfile=/proc/sys/vm/min_free_kbytes if [ -f "$minfile" ]; then read MinFree < $minfile # Should be equal to sum of all 'low' fields in /proc/zoneinfo watermark_low=$((MinFree * 5 / 16)) MemAvailable=$((MemFree - watermark_low + Inactive_file_ + Active_file_ - `min $(( (Inactive_file_ + Active_file_) / 2 )) $watermark_low` + SReclaimable - `min $(( SReclaimable / 2 )) $watermark_low` )) else MemAvailable=$MemFree # kernel 2.6.26 and earlier fi ;; esac } print_old() { MemUsed=$((MemTotal - MemFree)) # Not accurate, but historical TotalUsed=$((MemUsed + SwapUsed)) MemNCFree=$((MemFree + Buffers + Cached)) MemUNC=$((MemUsed - Buffers - Cached)) fmtw="%-6s$w$w$w$w$w\n" fmt="%-6s$w$w$w\n" fmtbc="%-6s$w$w\n" mprintf "$fmtw" "" total used free buffers cached mprintf "$fmtw" Mem: $MemTotal $MemUsed $MemFree $Buffers $Cached case $show_lohi in t ) mprintf "$fmt" Low: $LowTotal $LowUsed $LowFree mprintf "$fmt" High: $HighTotal $HighUsed $HighFree ;; esac mprintf "$fmtbc" "-/+ buffers/cache:" $MemUNC $MemNCFree mprintf "$fmt" Swap: $SwapTotal $SwapUsed $SwapFree case $show_total in t ) mprintf "$fmt" Total: $Total $TotalUsed $TotalFree ;; esac } print_new() { fmtw="%-7s$w$w$w$w$w$w$w\n" fmt="%-7s$w$w$w\n" mprintf "$fmtw" "" total used free shared buffers cache avail mprintf "$fmtw" Mem: $MemTotal $MemUsed $MemFree $Shmem $Buffers $MemCached $MemAvailable case $show_lohi in t ) mprintf "$fmt" Low: $LowTotal $LowUsed $LowFree mprintf "$fmt" High: $HighTotal $HighUsed $HighFree ;; esac case ${SwapTotal:-0}:$show_total in *:t ) mprintf "$fmt" Swap: $SwapTotal $SwapUsed $SwapFree mprintf "$fmt" Total: $Total $TotalUsed $TotalFree ;; 0:* ) : ;; * ) mprintf "$fmt" Swap: $SwapTotal $SwapUsed $SwapFree ;; esac } mprintf() { printf "$1" "$2" `shift; shift; scale_args "$@"` } fscale() { if type -p bc > /dev/null; then nval=`echo "scale=$sigfig; $1 $2 $3" | bc` elif type -p dc > /dev/null; then if [ -f /system/build.prop ]; then # Android: busybox's dc doesn't support precision nval=`echo "$1 $3 $2 p" | dc` else nval=`echo "$sigfig k $1 $3 $2 p" | dc` fi else exp=$(( 10 ** sigfig )) nval=$(($1 * exp $2 $3)) while [ ${#nval} -lt $sigfig ]; do nval=0$nval done int=${nval::-$sigfig} frac=${nval#$int} nval=$int.$frac fi int=${nval%.*} frac=${nval#*.} if [ .$int = . ]; then int=0 else while [ ${#int} -gt 1 ] && [ ${int:0:1} = '0' ]; do int=${int:1} done fi if [ ${#int} -ge $sigfig ] || [ .$frac = . ]; then nval=$int else nval=$int.$frac nval=${nval:0:$((sigfig + 1))} while :; do case $nval in *.*0 ) nval=${nval::-1} ;; *. ) nval=${nval::-1}; break ; ;; * ) break ;; esac done fi echo $nval$4 } scale_args() { for val in "$@"; do case $unit:$val in h:[0-9]* ) if [ $val -eq 0 ]; then echo ${val}B; continue; fi val=$((val * 1024)) # starts out as kibytes; expand set B K M G T P E local f=1 while [ $((val / f)) -gt $minprecis ]; do f=$((f * mult)) shift done fscale $val / $f $1 ;; *:[0-9]* ) if [ $val -eq 0 ]; then echo $val; continue; fi val=$(( val * 1024 / mult )) # possible SI conversion local nval=$(( val $op factor )) if [ $nval -lt $minprecis ]; then fscale $val "$op" $factor else echo $nval fi ;; *:* ) echo "$val" ;; # text; preserve whitespace esac done } min() { low=$1 shift for arg in "$@"; do if [ "$arg" -lt "$low" ]; then low=$arg fi done echo "$low" } filtername() { key=$1 shift for c in "$@"; do while :; do case $key in *${c}* ) key=${key%$c*}_${key#*$c} ;; * ) break ;; esac done done echo "$key" } splitopt() { local arg for arg in "$@"; do local i=0 while [ $i -lt ${#arg} ]; do echo " -${arg:$i:1}" i=$((i + 1)) done done } main() { # For android busybox; should be harmless elsewhere PATH=/system/xbin/bb:$PATH output=new unit= factor=1 mult=1024 w= sigfig= while :; do case $1 in -o | --old ) output=old ; : ${unit:=k} ${sigfig:=1} ${w:=%12s} ;; -n | --new ) output=new ; : ${unit:=h} ;; --pow2 ) mult=1024 ;; --si ) mult=1000 ;; -P | --precis ) sigfig=$2 ; shift ;; -h | --human ) unit=h ;; -b | --bytes ) unit=b ;; -k | --kilo ) unit=k ;; -m | --mega ) unit=m ;; -g | --giga ) unit=g ;; -p | --peta ) unit=p ;; -l | --lohi ) show_lohi=t ;; -t | --total ) show_total=t ;; -s | --sec* ) repeat_interval=$2 ; shift ;; -c | --count ) repeat_count=$2 ; shift ;; -w | --wide ) w=%12s case $2 in -[0-9]* | [0-9]* ) w=%${2}s ; shift ;; esac ;; -- | --* ) break ;; -??* ) local arg=$1; shift set . `splitopt "${arg#-}"` "$@" shift; continue ;; * ) break ;; esac shift done case ${unit:=h} in h ) : ;; b ) op=* factor='$mult' mult=1024 w=${w:-%12s} ;; k ) op=* factor=1 ;; m ) op=/ factor='$mult' ;; g ) op=/ factor='$((mult ** 2))' ;; p ) op=/ factor='$((mult ** 3))' ;; esac : ${w:=%9s} : ${sigfig:=3} minprecis=$(( 10 ** sigfig - 1 )) # computed value of factor was delayed in case mult is switched to SI factor=`eval echo "$factor"` case $repeat_interval:$repeat_count in [0-9]*: ) while :; do getmeminfo print_$output echo sleep $repeat_interval done ;; * ) : ${repeat_interval:=1} ${repeat_count:=1} while [ $repeat_count -gt 1 ]; do repeat_count=$((repeat_count - 1)) getmeminfo print_$output echo sleep $repeat_interval done getmeminfo print_$output ;; esac } main "$@" # eof