#!/bin/sh # # procmem - Show memory breakdown for selected processes # including file memory usage and stack, heap and # anon. Except for per pid details and grand totals, # all memory results shown are average KB. # By default, all processes are shown. # # Arguments: # -h - show usage, plus helpful hints # -v - verbose mode - show detail. # -u username|userid - show all processes # for specified user(s). Multiple # users should be presented as a # single string, space separated # (e.g. "root daemon"). # -p pidlist - show all processes matching # the list of pids (space separated) # searchstring - search for all processes in # 'ps -ef' matching this egrep string. # This argument may be combined with # the -u argument. # Usage Examples: # procmem -p 10784 10759 # procmem -u root # procmem -u "daemon root" # procmem netscape # procmem -u fred netscape # procmem -a # # NOTE: superuser privileges are required when # monitoring processes not owned by the # current user. # # NOTE: If memtool is installed, which means # /opt/RMCmem will exist, pmem is used # rather than pmap. # Download from ftp://playground.sun.com/pub/memtool # # Acknowledgements: # The raw data comes from Richard McDougall's # pmem program if available (pmap is broken in # some older Solaris releases), otherwise from # pmap. The idea of writing a script also came from # Richard, as did many suggestions for the content. # # Author: Allan Packer # Date: May 2000 # Copyright (c) 2000 by Sun Microsystems, Inc. # DIR=`dirname $0` if test -f /opt/RMCmem/bin/pmem; then PMAP="/opt/RMCmem/bin/pmem" else PMAP="/usr/proc/bin/pmap -x" fi PATH="$PATH":$DIR; export PATH ######################################################################### # # Process script arguments # ######################################################################### search=1 verbose=0 while test "`echo $1 | cut -c1`" = "-" do case `echo "$1" | cut -c2` in 'h') cat << EOF usage: $0 [-v] [-h | -p pidlist | [ -u username ] [ searchstring ]] Examples: procmem -p 10784 10759 - show memory usage for processes with pids 10784 10759 procmem -u root - show memory usage for all processes owned by the root user procmem -u "daemon root" - show memory usage for all processes owned by the root & daemon users procmem netscape - show memory usage for process(es) in 'ps -ef' with "netscape" procmem -u fred netscape - show memory usage for "netscape" processes owned by fred procmem - show memory usage for all processes (provided current user has superuser access privileges) Definition of terms 'Kbytes' is the total memory size of the process or file. 'Resident' is that portion currently occupying physical memory. 'Shared' is resident memory capable of being shared. 'Private' is resident memory unique to this process or file. Resident = Shared + Private Sizing For reporting purposes, the 'Shared' component has been counted once only while the 'Private' component has been summed for each process or file. The /usr/lib shared libraries have been reported separately since they tend to be widely used across applications. To be totally accurate, though, the shared component of these shared libraries should only be counted once across all applications, not once for every group of applications. The same logic may apply to other shared libraries also used by multiple applications. EOF exit ;; 'v') verbose=1 shift ;; 'p') if test "$1" = "-p"; then shift pidlist=$* else pidlist=`echo "$1" | cut -c3-99` pidlist="$pidlist $*" fi search=0 shift ;; 'u') if test "$1" = "-u"; then shift username=$1 else username=`echo "$1" | cut -c3-99` fi psstring="/bin/ps -f -u \"$username\"" shift if test -n "$1"; then egrepstring="| grep -v grep | grep -v \"$0\" | egrep \"$1\" | nawk '{print \$2}'" else egrepstring="| grep -v PID | nawk '{print \$2}'" fi search=0 ;; esac done if test $search -eq 1; then if test -n "$1"; then psstring="/bin/ps -ef" egrepstring="| grep -v grep | grep -v \"$0\" | egrep \"$1\" | nawk '{print \$2}'" else psstring="/bin/ps -ef" egrepstring="| grep -v PID | nawk '{print \$2}'" fi fi if test -z "$pidlist"; then pidlist=`eval $psstring $egrepstring` fi ######################################################################### # # Main script # ######################################################################### # Get list of /usr/lib shared libraries. These form the definition of # 'system' libraries ( ls -l1 /usr/lib/*\.so* echo "EOF" for i in $pidlist do $PMAP $i 2> /dev/null done ) | nawk -v verbose="$verbose" 'BEGIN { phase=1 printf("Processes Kbytes Resident Shared Private\n") printf("--------- ------ -------- ------ -------\n") } function fileprint (string1, string2, kbarray, resarray, sharedarray, privarray, countarray, maxsharedarray) { flag=0 valkb=0; valshared=0; valpriv=0; valres=0 for (i in kbarray) { if (!flag) { if (verbose) printf("%s\n", string1) flag=1 } desc=sprintf("%s (%d)", i, countarray[i]) if (verbose) { printf(" %-38s %8d %8d %8d %8d\n", desc, kbarray[i], privarray[i] + maxsharedarray[i], maxsharedarray[i], privarray[i]) | "sort" } valkb+=kbarray[i] valshared+=maxsharedarray[i] valpriv+=privarray[i] valres+=privarray[i]+maxsharedarray[i] } if (verbose) close("sort") if (flag) { printf("%-40s %8d %8d %8d %8d\n", string2 " Totals", valkb, valres, valshared, valpriv) if (verbose) printf("\n") } finalkb+=valkb finalres+=valres finalshared+=valshared finalpriv+=valpriv } { if ($0 == "EOF") {phase=2; getline} if (phase == 1) { if (substr($1,1,1) != "^l") { split($0,arr,"/") usrlibarray[arr[4]]=1 } } if (phase == 2) { if ($1 ~ ":") { pid=$1 el=split($2, arr, "/") progname=arr[el] getline; getline detail=1 } if ($1 == "--------") { getline if (verbose) { printf(" %-38s %8d %8d %8d %8d\n", pid " " progname, $3, $4, $5, $6) } progcount[progname]++ if (progcount[progname] > 1) proccount=1 progkb[progname]+=$3 progres[progname]+=$4 progshared[progname]+=$5 if (maxprogshared[progname] <= $5) maxprogshared[progname] = $5 progpriv[progname]+=$6 totprogcount++ detail=0 lastsys=""; lastoth=""; lastbin="" } if (detail) { kb+=$2 res+=$3 shared+=$4 priv+=$5 if ($7 ~ "shmid") { file=$7 kbshm[file]+=$2 resshm[file]+=$3 if (maxsharedshm[file] <= $4) maxsharedshm[file] = $4 privshm[file]+=$5 countshm[file]++ } else if ($7 ~ "\.so") { if (usrlibarray[$7] == 1) { file=$7 kbsyslib[file]+=$2 ressyslib[file]+=$3 if ($6 == "read/exec") sharedsyslib[file]=$4 if (maxsharedsyslib[file] <= sharedsyslib[file]) \ maxsharedsyslib[file] = sharedsyslib[file] if ($6 ~ "/write/") { max = sharedsyslib[file] + $4 } else max = 0 if (maxsharedsyslib[file] <= max) maxsharedsyslib[file] = max privsyslib[file]+=$5 if (file != lastsys) countsyslib[file]++ lastsys=file } else { file=$7 kbothlib[file]+=$2 resothlib[file]+=$3 if ($6 == "read/exec") sharedothlib[file]=$4 if (maxsharedothlib[file] <= sharedothlib[file]) \ maxsharedothlib[file] = sharedothlib[file] if ($6 ~ "/write/") { max = sharedothlib[file] + $4 } else max = 0 if (maxsharedothlib[file] <= max) maxsharedothlib[file] = max privothlib[file]+=$5 if (file != lastoth) countothlib[file]++ lastoth=file } } else if ($7 ~ "^dev:") { file=$7 " " $8 kbfil[file]+=$2 resfil[file]+=$3 if ($6 == "read/exec") sharedfil[file]=$4 if (maxsharedfil[file] <= sharedfil[file]) \ maxsharedfil[file] = sharedfil[file] if ($6 ~ "/write/") { max = sharedfil[file] + $4 } else max = 0 if (maxsharedfil[file] <= max) maxsharedfil[file] = max privfil[file]+=$5 countfil[file]++ } else if ($7 == "[") { file=$8 kbanon[file]+=$2 resanon[file]+=$3 sharedanon[file]+=$4 privanon[file]+=$5 countanon[file]++ } else { file=$7 kbbin[file]+=$2 resbin[file]+=$3 if ($6 == "read/exec") sharedbin[file]=$4 if (maxsharedbin[file] <= sharedbin[file]) \ maxsharedbin[file] = sharedbin[file] if ($6 ~ "/write/") { max = sharedbin[file] + $4 } else max = 0 if (maxsharedbin[file] <= max) maxsharedbin[file] = max privbin[file]+=$5 if (file != lastbin) countbin[file]++ lastbin=file } } } } END { if (verbose) printf("\n") if (totprogcount > 1 || !verbose) { if (proccount == 1 || !verbose) { printf("Process Summary (Count)\n") for (i in progkb) { hdg=sprintf("%s (%d)", i, progcount[i]) printf(" %-38s %8d %8d %8d %8d\n", hdg , progkb[i], progpriv[i] + maxprogshared[i], maxprogshared[i], progpriv[i]) | "sort" } close("sort") } printf("-----------------------------------------------------------------------------\n") } printf("\nFile (Count) Kbytes Resident Shared Private\n") printf("----------- ------ -------- ------ -------\n") fileprint("/usr/lib Shared Libraries", "/usr/lib Shared Library", kbsyslib, ressyslib, sharedsyslib, privsyslib, countsyslib, maxsharedsyslib) fileprint("Other Shared Libraries", "Other Shared Library", kbothlib, resothlib, sharedothlib, privothlib, countothlib, maxsharedothlib) fileprint("Mapped Files", "Mapped File", kbfil, resfil, sharedfil, privfil, countfil, maxsharedfil) fileprint("Binary Files", "Binary File", kbbin, resbin, sharedbin, privbin, countbin, maxsharedbin) fileprint("Shared Memory", "Shared Memory", kbshm, resshm, sharedshm, privshm, countshm, maxsharedshm) fileprint("Anonymous Memory", "Anonymous Memory", kbanon, resanon, sharedanon, privanon, countanon, 0) printf("-----------------------------------------------------------------------------\n") printf("%-40s %8d %8d %8d %8d\n", "Grand Totals", finalkb, finalres, finalshared, finalpriv) }'