#!/bin/bash # # Copyright (c) 2024 Red Hat. # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # Pretty-print sosreport information using Performance Co-Pilot metrics # from local host or an archive. # . $PCP_DIR/etc/pcp.env sts=2 tmp=`mktemp -d "$PCP_TMPFILE_DIR/pcp-xsos.XXXXXXXXX"` || exit 1 trap "rm -rf $tmp; exit \$sts" 0 1 2 3 15 progname=`basename $0` check_gawk() { echo >&2 "$progname: this script needs gawk $@" exit } which gawk >/dev/null 2>&1 || check_gawk "but it is not installed" gawk '@include "/dev/null"' {} 2>/dev/null || check_gawk "with @include syntax" base_metrics=( kernel.all.boottime mem.physmem ) ps_metrics=( proc.psinfo.utime proc.psinfo.stime proc.psinfo.sname proc.psinfo.psargs proc.psinfo.start_time proc.psinfo.threads proc.psinfo.rss proc.psinfo.vsize proc.id.uid_nm ) disk_metrics=( disk.dev.capacity filesys.capacity filesys.used filesys.avail filesys.full filesys.mountdir ) mem_metrics=( mem.util.used mem.util.dirty mem.util.shmem mem.util.bufmem mem.util.cached mem.util.swapFree mem.util.swapTotal mem.util.slab mem.util.pageTables mem.util.percpu mem.util.lowFree mem.util.lowTotal mem.util.anonhugepages mem.util.available mem.util.hugepagesFreeBytes mem.util.hugepagesTotalBytes mem.vmmemctl.current mem.vmmemctl.target ) netdev_metrics=( network.interface.in.bytes network.interface.in.packets network.interface.in.errors network.interface.in.drops network.interface.in.fifo network.interface.in.frame network.interface.in.compressed network.interface.in.mcasts network.interface.out.bytes network.interface.out.packets network.interface.out.errors network.interface.out.drops network.interface.out.fifo network.interface.collisions network.interface.out.compressed network.interface.out.carrier network.sockstat.total network.sockstat.tcp.mem network.sockstat.tcp.inuse network.sockstat.tcp.orphan network.sockstat.tcp.tw network.sockstat.tcp.alloc network.sockstat.udp.inuse network.sockstat.frag.inuse network.sockstat.udp.mem network.sockstat.udplite.inuse network.sockstat.raw.inuse network.sockstat.raw6.inuse network.sockstat.frag.memory network.sockstat.tcp6.inuse network.sockstat.udp6.inuse network.sockstat.udplite6.inuse network.sockstat.frag6.inuse network.sockstat.frag6.memory ) netstat_metrics=( network.icmp.inerrors network.icmp6.inerrors network.tcp.attemptfails network.tcp.estabresets network.tcp.inerrs network.tcp.outrsts network.tcp.delayedacklocked network.tcp.delayedacklost network.tcp.delayedacks network.tcp.pawsestabrejected network.tcp.abortontimeout network.tcp.lossproberecovery network.tcp.lossprobes network.tcp.timeouts network.tcp.tcptimeoutrehash network.ip.inaddrerrors network.ip6.inaddrerrors ) os_metrics=( hinv.ncpu hinv.pagesize pmcd.hostname pmcd.timezone pmcd.zoneinfo kernel.uname.release kernel.uname.version kernel.uname.sysname kernel.uname.machine kernel.uname.nodename kernel.uname.distro kernel.all.boottime kernel.all.uptime kernel.all.nusers kernel.all.load kernel.all.hz kernel.all.runnable kernel.all.running kernel.all.blocked kernel.all.nprocs kernel.all.cpu.user kernel.all.cpu.nice kernel.all.cpu.sys kernel.all.cpu.idle kernel.all.cpu.wait.total kernel.all.cpu.steal kernel.all.cpu.irq.soft kernel.all.cpu.irq.hard ) _usage() { [ ! -z "$@" ] && echo $@ 1>&2 pmgetopt --progname=$progname --usage --config=$tmp/usage exit } # usage spec for pmgetopt, note posix flag (commands mean no reordering) cat > $tmp/usage << EOF # getopts: a:h:O:S:?domnNpu:x --archive --host --origin --start --help --all show everything -o,--os show hostname, distro, kernel info, uptime, etc -d,--disks show info from /proc/partitions, df -m,--mem display memory summary -n,--netdev display network interface summary -N,--netstat display network statistics -p,--ps inspect running processes, ps -u=P, --units=P change byte display where P is "b" for byte, "k", "m", "g", or "t" -x,--nocolor disable output colorization # end EOF color=true osflag=false memflag=false diskflag=false netdevflag=false netstatflag=false psflag=false netunits='M' # options: B (byte), K, M, G or T memunits='G' # options: B (byte), K, M, G or T batch='' args=`pmgetopt --progname=$progname --config=$tmp/usage -- "$@"` [ $? != 0 ] && exit 1 eval set -- "$args" while [ $# -gt 0 ] do case "$1" in # pcp options -a) export PCP_ARCHIVE="$2" batch="-b 1" shift ;; -h) export PCP_HOST="$2" shift ;; -O) export PCP_ORIGIN="$2" shift ;; -S) export PCP_START_TIME="$2" shift ;; # pcp-xsos options --all) osflag=true memflag=true diskflag=true netdevflag=true netstatflag=true psflag=true ;; -d) diskflag=true ;; -m) memflag=true ;; -n) netdevflag=true ;; -N) netstatflag=true ;; -o) osflag=true ;; -p) psflag=true ;; -u) memunits=`echo "$2" | tr '[:lower:]' '[:upper:]' | cut -c 1` netunits=${memunits} ;; -x) color=false ;; -\?) sts=0 _usage "" ;; --) # end of options, start of arguments sts=1 _usage "Unknown argument: $2" ;; esac shift # finished with this option, move to next done # accumulate an array of all metrics to be fetched metrics=( ${base_metrics[*]} ) $osflag && metrics+=( ${os_metrics[*]} ) $memflag && metrics+=( ${mem_metrics[*]} ) $diskflag && metrics+=( ${disk_metrics[*]} ) $netdevflag && metrics+=( ${netdev_metrics[*]} ) $netstatflag && metrics+=( ${netstat_metrics[*]} ) $psflag && metrics+=( ${ps_metrics[*]} ) # default to OS metrics if nothing specified if test ${#metrics[@]} -eq ${#base_metrics[@]}; then metrics+=( ${os_metrics[*]} ) osflag=true fi # convert to printable form of unit string unitstr() { case "$1" in T) echo "TiB";; G) echo "GiB";; M) echo "MiB";; K) echo "KiB";; *) echo "B";; esac } memunits=`unitstr $memunits` netunits=`unitstr $netunits` if [ ! -z "$PCP_ARCHIVE" ] then # extract pmcd values from log label in case metrics missing eval `pmdumplog -Ll 2>/dev/null | gawk ' /^Performance metrics from host/ { printf "pmcd_hostname_value=\"%s\"\n", $5 } /^ commencing / { $1 = ""; printf "sampletime=\"%s\"\n", $0 } /^Archive timezone: / { printf "pmcd_timezone_value=\"%s\"\n", $3 } /^Archive zoneinfo: / { printf "pmcd_zoneinfo_value=\"%s\"\n", $3 } '` else sampletime=$( date --iso-8601=ns ) fi if [ ! -z "$PCP_START_TIME" ] then sampletime=`echo ${PCP_START_TIME} | sed -e 's/^@//g'` elif [ ! -z "$PCP_ORIGIN" ] then sampletime=`echo ${PCP_ORIGIN} | sed -e 's/^@//g'` fi # Timestamp for sample in seconds since the epoch timestamp=$( date --date "${sampletime}" +%s.%N 2>$tmp/error) if test -s $tmp/error then $PCP_ECHO_PROG $PCP_ECHO_N "$progname: ""$PCP_ECHO_C" sed < $tmp/error -e 's/^date: //g' sts=1 exit fi # Extract values for all metrics at once. Fetch using pminfo then # cater for 3 cases: single-valued metrics, set-valued metrics and # errors fetching individual metrics (see pminfo example below). # It translates the pminfo output into a series of bash variables, # including arrays (for set-valued metrics inst names and values). # Note that this is both bash and awk syntax being generated here. # # Input: # pminfo -f kernel.all.pswitch kernel.all.load kernel.cpu.util.user # # kernel.all.pswitch # value 730564942 # # kernel.all.load # inst [1 or "1 minute"] value 0.02 # inst [5 or "5 minute"] value 0.05 # inst [15 or "15 minute"] value 0 # # kernel.cpu.util.user # No value(s) available! # Output: # kernel_all_pswitch_value=730564942 # kernel_all_load_inst[1]="1 minute" # kernel_all_load_value[1]=0.19 # kernel_all_load_inst[5]="5 minute" # kernel_all_load_value[5]=0.12 # kernel_all_load_inst[15]="15 minute" # kernel_all_load_value[15]=0.06 # kernel_cpu_util_user_error="No value(s) available!" export PCP_SQUASH_NEWLINES=1 if ! pminfo $batch --fetch ${metrics[*]} > $tmp/metrics 2>$tmp/error then if grep "^pminfo:" $tmp/error > /dev/null 2>&1 then $PCP_ECHO_PROG $PCP_ECHO_N "$progname: ""$PCP_ECHO_C" sed < $tmp/error -e 's/^pminfo: //g' sts=1 exit fi fi [ -s $tmp/error ] && sed -e '/Unknown metric name/d' <$tmp/error >&2 gawk < $tmp/metrics > $tmp/variables ' function filter(string) { gsub(/"/, "\\\"", string) # escape double quotes gsub(/\\u/, "\\\\u", string) # escape backslash-u # replace any characters with special shell meaning gsub("/\\(|\\$|\\*|)|\\{|\\}\\?|`|;|!/", "-", string) gsub(/%/, "%%", string) # percent sign in printf gsub(/^\\"|\\"$/, "\"", string) # except on ends return string } BEGIN { error = 0; count = 0; value = 0; metric = "" } { if (NF == 0) { # end previous metric (if any) metric = "" } else if ($1 == "Note:") { # timezone message metric = "" } else if (metric == "") { # new metric, name gsub("\\.", "_", $1) if (gsub(":$", "", $1) > 0) { printf("%s=%c%s%c\n", $1, "\"", $0, "\"") metric = "" error++ } else { metric = $1 } count++ } else if ($1 == "value") { # singleton metric printf("%s_value=%s\n", metric, substr($0,11)) value++ } else if ($1 == "inst") { # set-valued metric sub("\\[", "") instid = $2 instoff = index($0, " or \"") + 4 instend = index($0, "\"]") instname = substr($0, instoff, instend-instoff+1) instname = filter(instname) # escape special chars valuestr = substr($0, index($0, "] value ") + 8) valuestr = filter(valuestr) # escape special chars printf("%s_inst[%s]=%s\n", metric, instid, instname) printf("%s_value[%s]=%s\n", metric, instid, valuestr) value++ } else { # set an error string for the metric printf("%s=%c%s%c\n", metric, "\"", $0, "\"") metric = "" error++ } } END { printf "metrics=%d\nvalues=%d\nerrors=%d\n", count, value, error }' eval `cat $tmp/variables` #cat $tmp/variables # check for a catastrophic failure (an error for every metric) if test $errors -eq $metrics then $PCP_ECHO_PROG $PCP_ECHO_N "$progname: ""$PCP_ECHO_C" $PCP_ECHO_PROG "failed to retrieve all $metrics metric values" sts=1 exit fi # prepare an awk import with metric values and helper function(s) cat > $tmp/metrics << EOF function round(number, places) { places = 10 ^ places return int(number * places + .5) / places } BEGIN { EOF cat $tmp/variables >> $tmp/metrics echo "}" >> $tmp/metrics #cat $tmp/metrics if $color; then RESET='\033[0m' # use defaults BLACK='\033[0;30m' RED='\033[0;31m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' WHITE='\033[0;37m' BACKBLUE='\033[44m' # background in blue BOLDWHITE='\033[1;37m' BOLDPURPLE='\033[1;35m' BOLDBLUE='\033[1;34m' BOLDRED='\033[1;31m' fi H0="${BOLDRED}" # heading color H1=" ${BOLDPURPLE}" # next level down H2=" ${BOLDBLUE}" # second level heading put_value() { # $1 - mandatory metric name, e.g. kernel.all.pswitch # $2 - optional fallback value, used to override error string # $3 - optional formatting string for printf (allows alignment, etc) eval __value=`echo ${1} | sed -e 's/^/$/' -e 's/\./_/g' -e 's/$/_value/'` eval __error=`echo ${1} | sed -e 's/^/$/' -e 's/\./_/g' -e 's/$/_error/'` __format="%s" __fallback="${2}" test -n "${3}" && __format=${3} test -n "${__value}" && printf "${__format}" "${__value}" && return 0 test -n "${__error}" -a -z "${__fallback}" && \ printf "${__format}" "${RED}(missing)" && return 3 test -n "${__fallback}" && printf "${__format}" "${ORANGE}${__fallback}" && echo 2 printf "${__format}" "${ORANGE}unknown" return 1 } get_value() { # $1 - mandatory metric name, e.g. kernel.all.pswitch # $2 - mandatory fallback value, used when missing metric value eval __value=`echo ${1} | sed -e 's/^/$/' -e 's/\./_/g' -e 's/$/_value/'` test -n "${__value}" && echo "${__value}" && return 0 echo "${2}" return 1 } get_inst_value() { # $1 - mandatory metric name, e.g. kernel.all.pswitch # $2 - mandatory numeric instance identifier, e.g. 1 eval __value=`echo ${1} | sed -e 's/^/${/' -e 's/\./_/g' -e 's/$/_value['$2']}/'` test -n "${__value}" && echo "${__value}" && return 0 echo "unknown" return 1 } print_uptime() { # Report on system up-time in days, hours and minutes # and number of users (given booted seconds + nusers) seconds=`echo "$1" | sed -e 's/\..*$//g'` users="$2" days=$((seconds / (60 * 60 * 24))) minutes=$((seconds / 60)) hours=$((minutes / 60)) hours=$((hours % 24)) minutes=$((minutes % 60)) if test $days -gt 1; then printf "$days days," elif test $days -ne 0; then printf "1 day," fi if test $hours -ne 0; then printf ' %2d:%02d,' $hours $minutes else printf ' %d min,' $minutes fi if test $users -eq 1; then printf ' 1 user\n' else printf ' %2d users\n' $users fi } pcp_xsos_os() { printf "${H0}OS\n" printf "${H1}Hostname:${RESET} " put_value pmcd.hostname; echo printf "${H1}Distro:${RESET} " banner=`put_value kernel.uname.distro` printf "${BACKBLUE}${banner}${RESET}\n" printf "${H1}Arch:${RESET} " platform=`put_value kernel.uname.machine` printf "platform=$platform\n" printf "${H1}Kernel:${RESET}\n" printf "${H2}Hertz:${RESET} " put_value kernel.all.hz; echo printf "${H2}Pagesize:${RESET} " put_value hinv.pagesize; echo printf "${H2}Build version:${RESET}\n" release=`put_value kernel.uname.release` release="version $release" sysname=`put_value kernel.uname.sysname` sysname="$sysname $release" version=`put_value kernel.uname.version "unknown build version"` printf " ${ORANGE}${sysname}${RESET}\n" printf " ${ORANGE}${version}${RESET}\n" printf " - - - - - - - - - - - - - - - - - - -\n" printf "${H1}Boot time: ${RESET}" boottime=`get_value kernel.all.boottime 0` date --date="@$boottime" +"%a %b %d %I:%M:%S %P %Z %Y" printf "${H1}Time Zone: ${RESET}" timezone=`get_value pmcd.timezone unknown` zoneinfo=`get_value pmcd.zoneinfo unknown | sed -e 's/^://'` printf "${zoneinfo} [${timezone}]\n" printf "${H1}Uptime: ${RESET} " uptime=`get_value kernel.all.uptime 0` nusers=`get_value kernel.all.nusers 0` print_uptime $uptime $nusers printf "${H1}LoadAvg:${RESET} " ncpus=`get_value hinv.ncpu 0` printf "${BOLDWHITE}[$ncpus CPU]${RESET}" test $ncpus -lt 1 && ncpus=1 # safe division later for inst in 1 5 15; do load=`get_inst_value kernel.all.load $inst` percent=`gawk "BEGIN {print int(${load}*${ncpus}+.5)}"` test $inst -eq 1 || printf "," printf " %.2f (${GREEN}%d%%${RESET})" $load $percent test $inst -eq 15 && printf "\n" done printf "${H1}Processes: ${RESET}\n" put_value kernel.all.running "" "${H2}running:${RESET} %s" put_value kernel.all.runnable "" "${H2}runnable:${RESET} %s" put_value kernel.all.blocked "" "${H2}blocked:${RESET} %s" put_value kernel.all.nprocs "" "${H2}count:${RESET} %s\n" printf "${H1}Processors: ${RESET}\n" printf "${H2}cpu [Utilization since boot]: ${RESET}\n " us=`get_value kernel.all.cpu.user 0` ni=`get_value kernel.all.cpu.nice 0` sy=`get_value kernel.all.cpu.sys 0` id=`get_value kernel.all.cpu.idle 0` wt=`get_value kernel.all.cpu.wait.total 0` ih=`get_value kernel.all.cpu.irq.hard 0` is=`get_value kernel.all.cpu.irq.soft 0` st=`get_value kernel.all.cpu.steal 0` gawk "BEGIN { tot=$us+$ni+$sy+$id+$wt+$ih+$is+$st; printf \"us %d%%, \", int($us/tot*100+.5) printf \"ni %d%%, \", int($ni/tot*100+.5) printf \"sys %d%%, \", int($sy/tot*100+.5) printf \"idle %d%%, \", int($id/tot*100+.5) printf \"iowait %d%%, \", int($wt/tot*100+.5) printf \"irq %d%%, \", int($ih/tot*100+.5) printf \"sftirq %d%%, \", int($is/tot*100+.5) printf \"steal %d%%\n\", int($st/tot*100+.5) }" echo # additional space for next session (with --all) } pcp_xsos_disk() { gawk " @include \"$tmp/metrics\" BEGIN { for (i in disk_dev_capacity_value) { nKiB += disk_dev_capacity_value[i] ndisks++ } nGiB = nKiB / 1024 / 1024 nTiB = round(nGiB / 1024, 2) printf \"${H0}STORAGE${RESET}\n\" printf \"${H1}Whole Disks from /proc/partitions:${RESET}\n\" printf \" ${BOLDWHITE}\" printf \"%s disks, totalling %s GiB (%s TiB)\n\", ndisks, nGiB, nTiB printf \" - - - - - - - - - - - - - - - - - - - - -\n\" printf \"${H2}Disk \tSize in GiB${RESET}\n\" printf \"${H2}---- \t-----------${RESET}\n\" for (i in disk_dev_capacity_value) { capacity = round(disk_dev_capacity_value[i] / 1024 / 1024, 2) printf \" %s \t%-s\n\", disk_dev_capacity_inst[i], capacity } printf \"\n\" printf \"${H1}Filesystem usage from df:${RESET}\n\" printf \" ${BOLDWHITE}\" printf \"Filesystem 1K-blocks Used Available Use%% Mounted on\" printf \"${RESET}\n\" for (i in filesys_capacity_value) { printf \" %s %14s %8s %9s %3s%% %s\n\", filesys_capacity_inst[i], filesys_capacity_value[i], filesys_used_value[i], filesys_avail_value[i], round(filesys_full_value[i], 0), filesys_mountdir_value[i] } }" echo # additional space for next session (with --all) } pcp_xsos_mem() { gawk -v u=${memunits} " @include \"$tmp/metrics\" function put_hbar(title, color, value, total) { width = 50 ratio = value/total hbars = int(ratio * width + .5) printf \" %s%-10s \", color, title for (i=1; i < hbars+1; i++) printf \"▊\" printf \"${RESET}\" for (i=hbars; i < width; i++) printf \".\" printf \" %s%5.1f%%${RESET}\n\", color, ratio * 100 + .005 } BEGIN { total = (mem_physmem_value > 0) ? mem_physmem_value : 1 other = mem_util_used_value - mem_util_cached_value - mem_util_bufmem_value huge = mem_util_hugepagesTotalBytes_value / 1024 hugefree = mem_util_hugepagesFreeBytes_value / 1024 hugeused = huge - (mem_util_hugepagesFreeBytes_value / 1024) printf \"${H0}MEMORY\n\" printf \"${H1}Stats graphed as percent of MemTotal:${RESET}\n\" put_hbar(\"MemUsed\", \"${GREEN}\", mem_util_used_value, total) put_hbar(\"Buffers\", \"${PURPLE}\", mem_util_bufmem_value, total) put_hbar(\"Cached\", \"${BLUE}\", mem_util_cached_value, total) put_hbar(\"HugePages\", \"${CYAN}\", huge, total) put_hbar(\"Dirty\", \"${RED}\", mem_util_dirty_value, total) put_hbar(\"Available\", \"${WHITE}\", mem_util_available_value, total) precision_pct = 1 precision_low = 0 precision_high = 0 if (u == \"TiB\") { kbytes_divisor = 1024 ^ 3 precision_pct = 0 precision_low = 2 precision_high = 3 } else if (u == \"GiB\") { kbytes_divisor = 1024 ^ 2 precision_pct = 0 precision_low = 1 precision_high = 2 } else if (u == \"MiB\") { kbytes_divisor = 1024 } else if (u == \"KiB\") { kbytes_divisor = 1 } else { kbytes_divisor = 1 / 1024 } printf \"${H1}RAM:${RESET}\n\" printf \" ${BOLDWHITE}%s %s total ram${CLEAR}\n\", round(total/kbytes_divisor, precision_low), u printf \" ${WHITE}%s %s (%s%%) used\n\", round(mem_util_used_value/kbytes_divisor, precision_low), u, round((mem_util_used_value/total)*100, precision_pct) printf \" ${BOLDWHITE}%s %s (%s%%) used excluding Buffers/Cached\n\", round(other/kbytes_divisor, precision_low), u, round((other/total)*100, precision_pct) printf \" ${WHITE}%s %s (%s%%) dirty\n\", round(mem_util_dirty_value/kbytes_divisor, precision_low), u, round((mem_util_dirty_value/total)*100, precision_pct) printf \" ${BOLDWHITE}%s %s (%s%%) available\n\", round(mem_util_available_value/kbytes_divisor, precision_low), u, round((mem_util_available_value/total)*100, precision_pct) printf \"${H1}HugePages:${RESET}\n\" if (huge == 0) printf \" No ram pre-allocated to HugePages\n\" else { printf \" %s %s pre-allocated to HugePages (%s%% of total ram)\n\", round(huge/kbytes_divisor, precision_low), u, round((huge/total)*100, precision_pct) printf \" %s %s of HugePages (%s%%) in-use by applications\n\", round(hugeused/kbytes_divisor, precision_low), u, round((hugeused/hugefree)*100, precision_pct) } printf \"${H1}TransparentHugePages:${RESET}\n\" if (mem_util_anonhugepages_value == 0) printf \" No ram allocated to THP\n\" else { anonhugepages = mem_util_anonhugepages_value / kbytes_divisor printf \" %s %s allocated to THP\n\", round(anonhugepages, precision_high), u } printf \"${H1}LowMem/Slab/PageTables/Shmem:${RESET}\n\" if (mem_util_lowTotal_value == 0) { } else { lowused = mem_util_lowTotal_value - mem_util_lowFree_value lowtotal = mem_util_lowTotal_value > 0 ? mem_util_lowTotal_value : 1 printf \" %s %s (%s%%) of LowMem in-use\n\", round(lowused/kbytes_divisor, precision_low), u, round((lowused/lowtotal)*100, precision_pct) } printf \" %s %s (%s%%) of total ram used for Slab\n\", round(mem_util_slab_value/kbytes_divisor, precision_high), u, round((mem_util_slab_value/total)*100, precision_low) printf \" %s %s (%s%%) of total ram used for PageTables\n\", round(mem_util_pageTables_value/kbytes_divisor, precision_high), u, round((mem_util_pageTables_value/total)*100, precision_pct) printf \" %s %s (%s%%) of total ram used for Shmem\n\", round(mem_util_shmem_value/kbytes_divisor, precision_high), u, round((mem_util_shmem_value/total)*100, precision_pct) printf \" %s %s (%s%%) of total ram used for Percpu\n\", round(mem_util_percpu_value/kbytes_divisor, precision_high), u, round((mem_util_percpu_value/total)*100, precision_pct) printf \"${H1}Virtual Machine Balloon:${RESET}\n\" if (mem_vmmemctl_target_value == 0) printf \" No VM balloon memory target\n\" else { vmcurrent = mem_vmmemctl_current_value / 1024 vmtarget = mem_vmmemctl_target_value / 1024 printf \" %s %s (%s%%) of %s %s balloon in-use\n\", round(vmcurrent/kbytes_divisor, precision_low), u, round((vmcurrent/vmtarget)*100, precision_pct), u, round(vmtarget/kbytes_divisor, precision_low) } printf \"${H1}Swap:${RESET}\n\" if (mem_util_swapTotal_value == 0) printf \" ${ORANGE}No system swap space configured${RESET}\n\" else { swaptotal = mem_util_swapTotal_value swapused = swaptotal - mem_util_swapFree_value printf \" %s %s (%s%%) used of %s %s total\n\", round(swapused/kbytes_divisor, precision_low), u, round((swapused/swaptotal)*100, precision_pct), round(swaptotal/kbytes_divisor, precision_high), u } }" echo # additional space for next session (with --all) } pcp_xsos_netdev() { gawk -v u=${netunits} " @include \"$tmp/metrics\" BEGIN { printf \"${H0}NETDEV\n\" # Figure out what to divide by to end up with KiB, MiB, GiB, or TiB. # Also figure out decimal precision (conditional below) :- # - For T, round Bytes field to nearest hundredth (.nn) # - For G, round Bytes field to nearest tenth (.n) # - For KiB/MiB, keep Bytes as whole numbers # Finally, never show decimal for Packets precision_bytes = 0 precision_pckts = 0 packets_divisor = 1 bytes_divisor = 1 if (u == \"KiB\") { bytes_divisor = 1024 } else if (u == \"MiB\") { bytes_divisor = 1024 ^ 2 packets_divisor = 1000 packets_unit = \" k\" } else if (u == \"GiB\") { bytes_divisor = 1024 ^ 3 packets_divisor = 1000 ^ 2 packets_unit = \" M\" precision_bytes = 1 } else if (u == \"TiB\") { bytes_divisor = 1024 ^ 4 packets_divisor = 1000 ^ 2 packets_unit = \" M\" precision_bytes = 2 } printf \" ${BOLDPURPLE}Interface Rx%sytes RxPackets RxErrs RxDrop RxFifo RxComp RxFrame RxMultCast\n\", u printf \" ========= ========= ========= ====== ====== ====== ====== ======= ==========${RESET}\n\" ignored = \"^lo\" for (i in network_interface_in_bytes_inst) { if (match(network_interface_in_bytes_inst[i], ignored) != 0) continue rxbytes = network_interface_in_bytes_value[i] rxpckts = network_interface_in_packets_value[i] rxerror = network_interface_in_errors_value[i] rxdrops = network_interface_in_drops_value[i] rxfifos = network_interface_in_fifo_value[i] rxframe = network_interface_in_frame_value[i] rxmcast = network_interface_in_mcasts_value[i] rxcomps = network_interface_in_compressed_value[i] rxtotal = rxpckts+rxerror+rxdrops+rxfifos+rxframe+rxmcast+rxcomps if (rxtotal > 0) { if (rxerror > 0) rxerrorpct = \"(\" round(rxerror * 100 / rxtotal, 0) \"%)\" if (rxdrops > 0) rxdropspct = \"(\" round(rxdrops * 100 / rxtotal, 0) \"%)\" if (rxfifos > 0) rxfifospct = \"(\" round(rxfifos * 100 / rxtotal, 0) \"%)\" if (rxframe > 0) rxframepct = \"(\" round(rxframe * 100 / rxtotal, 0) \"%)\" if (rxmcast > 0) rxmcastpct = \"(\" round(rxmcast * 100 / rxtotal, 0) \"%)\" if (rxcomps > 0) rxcompspct = \"(\" round(rxcomps * 100 / rxtotal, 0) \"%)\" } # If unit is anything but bytes, perform the necessary division if (u == \"KiB\" || u == \"MiB\" || u == \"GiB\" || u == \"TiB\") rxbytes /= bytes_divisor # If unit is MiB, GiB, or TiB, perform division on packets as well if (u == \"MiB\" || u == \"GiB\" || u == \"TiB\") rxpckts /= packets_divisor rxbytes = round(rxbytes, precision_bytes) rxpckts = round(rxpckts, precision_pckts)\"\" packets_unit printf \" ${BLUE}\" printf \"%-9s${RESET} %-9s %-9s %-6s %-6s %-6s %-6s %-7s %-10s\n\", network_interface_in_bytes_inst[i], rxbytes, rxpckts, rxerror\"\" rxerrorpct, rxdrops\"\" rxdropspct, rxfifos\"\" rxfifospct, rxcomps\"\" rxcompspct, rxframe\"\" rxframepct, rxmcast\"\" rxmcastpct } printf \" - - - - - - - - - - - - - - - - -\n\" printf \" ${BOLDPURPLE}Interface Tx%sytes TxPackets TxErrs TxDrop TxFifo TxComp TxColls TxCarrier\n\", u printf \" ========= ========= ========= ====== ====== ====== ====== ======= ==========${RESET}\n\" for (i in network_interface_out_bytes_inst) { if (match(network_interface_out_bytes_inst[i], ignored) != 0) continue txbytes = network_interface_out_bytes_value[i] txpckts = network_interface_out_packets_value[i] txerror = network_interface_out_errors_value[i] txdrops = network_interface_out_drops_value[i] txfifos = network_interface_out_fifo_value[i] txcolls = network_interface_collisions_value[i] txcarrs = network_interface_out_carrier_value[i] txcomps = network_interface_out_compressed_value[i] txtotal = txpckts+txerror+txdrops+txfifos+txcolls+txcarrs+txcomps if (txtotal > 0) { if (txerror > 0) txerrorpct = \"(\" round(txerror * 100 / txtotal, 0) \"%)\" if (txdrops > 0) txdropspct = \"(\" round(txdrops * 100 / txtotal, 0) \"%)\" if (txfifos > 0) txfifospct = \"(\" round(txfifos * 100 / txtotal, 0) \"%)\" if (txcolls > 0) txcollspct = \"(\" round(txcolls * 100 / txtotal, 0) \"%)\" if (txcarrs > 0) txcarrspct = \"(\" round(txcarrs * 100 / txtotal, 0) \"%)\" if (txcomps > 0) txcompspct = \"(\" round(txcomps * 100 / txtotal, 0) \"%)\" } # If unit is anything but bytes, perform the necessary division if (u == \"KiB\" || u == \"MiB\" || u == \"GiB\" || u == \"TiB\") txbytes /= bytes_divisor # If unit is MiB, GiB, or TiB, perform division on packets as well if (u == \"MiB\" || u == \"GiB\" || u == \"TiB\") txpckts /= packets_divisor txbytes = round(txbytes, precision_bytes) txpckts = round(txpckts, precision_pckts)\"\" packets_unit printf \" ${BLUE}\" printf \"%-9s${RESET} %-9s %-9s %-6s %-6s %-6s %-6s %-7s %-10s\n\", network_interface_out_bytes_inst[i], txbytes, txpckts, txerror\"\" txerrorpct, txdrops\"\" rxdropspct, txfifos\"\" txfifospct, txcomps\"\" txcompspct, txcolls\"\" txcollspct, txcarrs\"\" txcarrspct } }" echo printf "${H0}SOCKSTAT${RESET}\n" printf " ${H2}sockets:${RESET} used " put_value network.sockstat.total; echo printf " ${H2}TCP:${RESET} inuse " put_value network.sockstat.tcp.inuse printf " orphan " put_value network.sockstat.tcp.orphan printf " tw " put_value network.sockstat.tcp.tw printf " alloc " put_value network.sockstat.tcp.alloc printf " mem " put_value network.sockstat.tcp.mem; echo printf " ${H2}UDP:${RESET} inuse " put_value network.sockstat.udp.inuse printf " mem " put_value network.sockstat.udp.mem; echo printf " ${H2}UDPLITE:${RESET} inuse " put_value network.sockstat.udplite.inuse; echo printf " ${H2}RAW:${RESET} inuse " put_value network.sockstat.raw.inuse; echo printf " ${H2}FRAG:${RESET} inuse " put_value network.sockstat.frag.inuse printf " memory " put_value network.sockstat.frag.memory; echo printf " ${H2}TCP6:${RESET} inuse " put_value network.sockstat.tcp6.inuse; echo printf " ${H2}UDP6:${RESET} inuse " put_value network.sockstat.udp6.inuse; echo printf " ${H2}UDPLITE6:${RESET} inuse " put_value network.sockstat.udplite6.inuse; echo printf " ${H2}RAW6:${RESET} inuse " put_value network.sockstat.raw6.inuse; echo printf " ${H2}FRAG6:${RESET} inuse " put_value network.sockstat.frag6.inuse printf " memory " put_value network.sockstat.frag6.memory; echo echo # additional space for next session (with --all) } pcp_xsos_netstat() { printf "${H0}NET STATS${RESET}\n" put_value network.icmp.inerrors "" " Icmp.InErrors: %30s\n" put_value network.icmp6.inerrors "" " Icmp6.InErrors: %29s\n" put_value network.tcp.attemptfails "" " Tcp.AttemptFails: %27s\n" put_value network.tcp.estabresets "" " Tcp.EstabResets: %28s\n" put_value network.tcp.inerrs "" " Tcp.InErrs: %33s\n" put_value network.tcp.outrsts "" " Tcp.OutRsts: %32s\n" put_value network.tcp.delayedacklocked "" " TcpExt.DelayedACKLocked: %20s\n" put_value network.tcp.delayedacklost "" " TcpExt.DelayedACKLost: %22s\n" put_value network.tcp.delayedacks "" " TcpExt.DelayedACKs: %25s\n" put_value network.tcp.pawsestabrejected "" " TcpExt.PAWSEstab: %27s\n" put_value network.tcp.abortontimeout "" " TcpExt.TCPAbortOnTimeout: %19s\n" put_value network.tcp.lossproberecovery "" " TcpExt.TCPLossProbeRecovery: %16s\n" put_value network.tcp.lossprobes "" " TcpExt.TCPLossProbes: %23s\n" put_value network.tcp.timeouts "" " TcpExt.TCPTimeouts: %25s\n" put_value network.tcp.tcptimeoutrehash "" " TcpExt.TcpTimeoutRehash: %20s\n" put_value network.ip.inaddrerrors "" " Ip.InAddrErrors: %28s\n" put_value network.ip6.inaddrerrors "" " Ip6.InAddrErrors: %27s\n" echo # additional space for next session (with --all) } pcp_xsos_ps() { gawk -v u=${memunits} " @include \"$tmp/metrics\" function max(a, b) { return a > b ? a : b } function min(a, b) { return a < b ? a : b } function get_username(pid) { username = proc_id_uid_nm_value[pid] if (length(username) > 7) username = substr(username, 0, 7) \"+\" return username } function put_headers() { printf \"%-9s %-8s %-6s %-6s %-8s %-8s %-s${RESET}\n\", \"USER\", \"PID\", \"%CPU\", \"%MEM\", \"VSZ-MiB\", \"RSS-MiB\", \"COMMAND\" } function get_cputime(pid) { cpu = (proc_psinfo_utime_value[pid] + proc_psinfo_stime_value[pid]) / 1000 time = ${timestamp} time -= proc_psinfo_start_time_value[pid] / 1000 time -= kernel_all_boottime_value return cpu / time } function put_process(pid, prefix) { user = get_username(pid) cpu = get_cputime(pid) mem = (proc_psinfo_rss_value[pid] / mem_physmem_value) printf \"%s%-9s %-8s %-6s %-6s %-8s %-8s %-s\n\", prefix, user, pid, round(cpu * 100, 1) \"%\", round(mem * 100, 1) \"%\", round(proc_psinfo_vsize_value[pid] / 1024, 0), round(proc_psinfo_rss_value[pid] / 1024, 0), proc_psinfo_psargs_value[pid] } BEGIN { printf \"${H0}PROCESSES\n\" num_top_procs = 10 num_top_users = 10 kbytes_divisor = 1 if (u == \"MiB\") { kbytes_divisor = 1024 } else if (u == \"GiB\") { kbytes_divisor = 1024 ^ 2 } else if (u == \"TiB\") { kbytes_divisor = 1024 ^ 3 } else if (u == \"B\") { kbytes_divisor = 1 / 1024 } printf \"${H1}Total number of threads/processes:\n\" for (i in proc_psinfo_threads_value) { threads += proc_psinfo_threads_value[i] + 1 processes += 1 } printf \" ${BOLDWHITE}%s/%s\n\", threads, processes printf \"${H1}Top users of CPU & MEM:\n\" for (i in proc_psinfo_utime_value) { proc_cpu = proc_psinfo_cutime_value[i] + proc_psinfo_cstime_value[i] proc_cpu += proc_psinfo_utime_value[i] + proc_psinfo_stime_value[i] proc_cpu /= 1000 # milliseconds to seconds proc_mem = proc_psinfo_rss_value[i] if (proc_cpu == 0 && proc_mem == 0) continue proctime = proc_psinfo_start_time_value[i] / 1000 username = get_username(i) if (length(username) > 0) { if (starttime[username] == 0) starttime[username] = proctime else starttime[username] = min(proctime, starttime[username]) users_cpu[username] += proc_cpu users_mem[username] += proc_mem } } # start_time (msec) is relative to the boottime # boottime (sec) is relative to the epoch # sample timestamp (sec) is relative to the epoch for (username in starttime) { time_span = max(1, $timestamp - \ kernel_all_boottime_value - starttime[username]) users_cpu[username] /= time_span } printf \"${H2}%-9s %7s %6s %5s${RESET}\n\", \"USER\", \"%CPU\", \"%MEM\", \"RSS\" asorti(users_cpu, top_cpu, \"@val_num_desc\") topmost = num_top_users for (i in top_cpu) { user = top_cpu[i] pct_cpu = round(users_cpu[user] * 100, 1) pct_mem = round(users_mem[user] / mem_physmem_value * 100, 1) if (pct_cpu < 0.1 && pct_mem < 0.1) break printf \" %-9s %5s%% %5s%% %6s %s\n\", user, pct_cpu, pct_mem, round(users_mem[user] / kbytes_divisor, 2), u if ((topmost -= 1) == 0) break } # process state accounting for (i in proc_psinfo_sname_value) { state = proc_psinfo_sname_value[i] if (state == \"Z\") { zombies[i] = proc_psinfo_threads_value[i] + 1 nzombies++ } else if (state == \"D\") { sleepers[i] = proc_psinfo_threads_value[i] + 1 nsleepers++ } } printf \"${H1}Uninterruptible sleep threads/processes \" printf \"(%d/%d)\n\", sleepers[i], length(sleeper) if (nsleepers == 0) printf \" ${RESET}[None]\n\" else { printf \"${H2}\" put_headers() for (i in sleepers) put_process(sleepers[i], \" \") } printf \"${H1}Defunct zombie threads/processes \" printf \"(%d/%d)\n\", zombies[i], length(zombies) if (nzombies == 0) printf \" ${RESET}[None]\n\" else { printf \"${H2}\" put_headers() for (i in zombies) put_process(zombies[i], \" \") } printf \"${H1}Top CPU-using processes:\n${H2}\" put_headers() for (pid in proc_psinfo_utime_value) cputimes[pid] = get_cputime(pid) asorti(cputimes, topcpu, \"@val_num_desc\") topmost = num_top_procs for (i in topcpu) { put_process(topcpu[i], \" \") if ((topmost -= 1) == 0) break } printf \"${H1}Top MEM-using processes:\n${H2}\" put_headers() asorti(proc_psinfo_rss_value, topmem, \"@val_num_desc\") topmost = num_top_procs for (i in topmem) { put_process(topmem[i], \" \") if ((topmost -= 1) == 0) break } printf \"${H1}Top thread-spawning processes:\n\" printf \"${H2}%-3s \", \"#\" put_headers() asorti(proc_psinfo_threads_value, topthr, \"@val_num_desc\") topmost = num_top_procs for (i in topthr) { pid = topthr[i] if (proc_psinfo_threads_value[pid] < 2) break prefix = sprintf(\" %-3s \", proc_psinfo_threads_value[pid]) put_process(pid, prefix) if ((topmost -= 1) == 0) break } }" echo # additional space for next session (with --all) } $osflag && pcp_xsos_os $memflag && pcp_xsos_mem $diskflag && pcp_xsos_disk $netdevflag && pcp_xsos_netdev $netstatflag && pcp_xsos_netstat $psflag && pcp_xsos_ps sts=0 exit