Files
percona-toolkit/lib/bash/report_system_info.sh
Sveta Smirnova ea6bd77501 PT-2340 - Support MySQL 8.4
- Moved data collection for THP from lib/bash/report_system_info.sh to lib/bash/collect_system_info.sh, so pt-summary reports THP status on the machine where samples were collected, not on the machine where an engineer examines samples.
2024-09-06 13:08:45 +03:00

1163 lines
45 KiB
Bash

# This program is copyright 2011 Percona Inc.
# Feedback and improvements are welcome.
#
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# 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, version 2; OR the Perl Artistic License. On UNIX and similar
# systems, you can issue `man perlgpl' or `man perlartistic' to read these
# licenses.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307 USA.
# ###########################################################################
# report_system_info package
# ###########################################################################
# Package: report_system_info
#
set -u
# ##############################################################################
# Functions for parsing specific files and getting desired info from them.
# These are called from within report_system_summary() and are separated so
# they can be tested easily.
# ##############################################################################
# ##############################################################################
# Parse Linux's /proc/cpuinfo.
# ##############################################################################
parse_proc_cpuinfo () { local PTFUNCNAME=parse_proc_cpuinfo;
local file="$1"
# Physical processors are indicated by distinct 'physical id'. Virtual CPUs
# are indicated by paragraphs -- one per paragraph. We assume that all
# processors are identical, i.e. that there are not some processors with dual
# cores and some with quad cores.
local virtual="$(grep -c ^processor "${file}")";
local physical="$(grep 'physical id' "${file}" | sort -u | wc -l)";
local cores="$(grep 'cpu cores' "${file}" | head -n 1 | cut -d: -f2)";
# Older kernel won't have 'physical id' or 'cpu cores'.
[ "${physical}" = "0" ] && physical="${virtual}"
[ -z "${cores}" ] && cores=0
# Test for HTT; cannot trust the 'ht' flag. If physical * cores < virtual,
# then hyperthreading is in use.
cores=$((${cores} * ${physical}));
local htt=""
if [ ${cores} -gt 0 -a $cores -lt $virtual ]; then htt=yes; else htt=no; fi
name_val "Processors" "physical = ${physical}, cores = ${cores}, virtual = ${virtual}, hyperthreading = ${htt}"
awk -F: '/cpu MHz/{print $2}' "${file}" \
| sort | uniq -c > "$PT_TMPDIR/parse_proc_cpuinfo_cpu.unq"
name_val "Speeds" "$(group_concat "$PT_TMPDIR/parse_proc_cpuinfo_cpu.unq")"
awk -F: '/model name/{print $2}' "${file}" \
| sort | uniq -c > "$PT_TMPDIR/parse_proc_cpuinfo_model.unq"
name_val "Models" "$(group_concat "$PT_TMPDIR/parse_proc_cpuinfo_model.unq")"
awk -F: '/cache size/{print $2}' "${file}" \
| sort | uniq -c > "$PT_TMPDIR/parse_proc_cpuinfo_cache.unq"
name_val "Caches" "$(group_concat "$PT_TMPDIR/parse_proc_cpuinfo_cache.unq")"
}
# ##############################################################################
# Parse sysctl -a output on FreeBSD, and format it as CPU info. The file is the
# first argument.
# ##############################################################################
parse_sysctl_cpu_freebsd() { local PTFUNCNAME=parse_sysctl_cpu_freebsd;
local file="$1"
[ -e "$file" ] || return;
local virtual="$(awk '/hw.ncpu/{print $2}' "$file")"
name_val "Processors" "virtual = ${virtual}"
name_val "Speeds" "$(awk '/hw.clockrate/{print $2}' "$file")"
name_val "Models" "$(awk -F: '/hw.model/{print substr($2, 2)}' "$file")"
}
# ##############################################################################
# Parse sysctl -a output on NetBSD.
# ##############################################################################
parse_sysctl_cpu_netbsd() { local PTFUNCNAME=parse_sysctl_cpu_netbsd;
local file="$1"
# return early if they didn't pass in a file
[ -e "$file" ] || return
local virtual="$(awk '/hw.ncpu /{print $NF}' "$file")"
name_val "Processors" "virtual = ${virtual}"
#name_val "Speeds" # TODO: No clue
name_val "Models" "$(awk -F: '/hw.model/{print $3}' "$file")"
}
# ##############################################################################
# Detect cpu info on OpenBSD, and format it as CPU info
# ##############################################################################
parse_sysctl_cpu_openbsd() { local PTFUNCNAME=parse_sysctl_cpu_openbsd;
local file="$1"
[ -e "$file" ] || return
name_val "Processors" "$(awk -F= '/hw.ncpu=/{print $2}' "$file")"
name_val "Speeds" "$(awk -F= '/hw.cpuspeed/{print $2}' "$file")"
name_val "Models" "$(awk -F= '/hw.model/{print substr($2, 1, index($2, " "))}' "$file")"
}
# ##############################################################################
# Parse CPU info from psrinfo -v
# ##############################################################################
parse_psrinfo_cpus() { local PTFUNCNAME=parse_psrinfo_cpus;
local file="$1"
[ -e "$file" ] || return
name_val "Processors" "$(grep -c 'Status of .* processor' "$file")"
awk '/operates at/ {
start = index($0, " at ") + 4;
end = length($0) - start - 4
print substr($0, start, end);
}' "$file" | sort | uniq -c > "$PT_TMPDIR/parse_psrinfo_cpus.tmp"
name_val "Speeds" "$(group_concat "$PT_TMPDIR/parse_psrinfo_cpus.tmp")"
}
# ##############################################################################
# Parse the output of 'free -b' plus the contents of /proc/meminfo
# ##############################################################################
parse_free_minus_b () { local PTFUNCNAME=parse_free_minus_b;
local file="$1"
[ -e "$file" ] || return
local physical=$(awk '/Mem:/{print $3}' "${file}")
local swap_alloc=$(awk '/Swap:/{print $2}' "${file}")
local swap_used=$(awk '/Swap:/{print $3}' "${file}")
local virtual=$(shorten $(($physical + $swap_used)) 1)
name_val "Total" $(shorten $(awk '/Mem:/{print $2}' "${file}") 1)
name_val "Free" $(shorten $(awk '/Mem:/{print $4}' "${file}") 1)
name_val "Used" "physical = $(shorten ${physical} 1), swap allocated = $(shorten ${swap_alloc} 1), swap used = $(shorten ${swap_used} 1), virtual = ${virtual}"
name_val "Shared" $(shorten $(awk '/Mem:/{print $5}' "${file}") 1)
name_val "Buffers" $(shorten $(awk '/Mem:/{print $6}' "${file}") 1)
name_val "Caches" $(shorten $(awk '/Mem:/{print $7}' "${file}") 1)
name_val "Dirty" "$(awk '/Dirty:/ {print $2, $3}' "${file}")"
}
# ##############################################################################
# Parse FreeBSD memory info from sysctl output.
# ##############################################################################
parse_memory_sysctl_freebsd() { local PTFUNCNAME=parse_memory_sysctl_freebsd;
local file="$1"
[ -e "$file" ] || return
local physical=$(awk '/hw.realmem:/{print $2}' "${file}")
local mem_hw=$(awk '/hw.physmem:/{print $2}' "${file}")
local mem_used=$(awk '
/hw.physmem/ { mem_hw = $2; }
/vm.stats.vm.v_inactive_count/ { mem_inactive = $2; }
/vm.stats.vm.v_cache_count/ { mem_cache = $2; }
/vm.stats.vm.v_free_count/ { mem_free = $2; }
/hw.pagesize/ { pagesize = $2; }
END {
mem_inactive *= pagesize;
mem_cache *= pagesize;
mem_free *= pagesize;
print mem_hw - mem_inactive - mem_cache - mem_free;
}
' "$file");
name_val "Total" $(shorten ${mem_hw} 1)
name_val "Virtual" $(shorten ${physical} 1)
name_val "Used" $(shorten ${mem_used} 1)
}
# ##############################################################################
# Parse NetBSD memory info from sysctl output.
# ##############################################################################
parse_memory_sysctl_netbsd() { local PTFUNCNAME=parse_memory_sysctl_netbsd;
local file="$1"
local swapctl_file="$2"
[ -e "$file" -a -e "$swapctl_file" ] || return
local swap_mem="$(awk '{print $2*512}' "$swapctl_file")"
name_val "Total" $(shorten "$(awk '/hw.physmem /{print $NF}' "$file")" 1)
name_val "User" $(shorten "$(awk '/hw.usermem /{print $NF}' "$file")" 1)
name_val "Swap" $(shorten ${swap_mem} 1)
}
# ##############################################################################
# Parse OpenBSD memory info from sysctl output.
# ##############################################################################
parse_memory_sysctl_openbsd() { local PTFUNCNAME=parse_memory_sysctl_openbsd;
local file="$1"
local swapctl_file="$2"
[ -e "$file" -a -e "$swapctl_file" ] || return
local swap_mem="$(awk '{print $2*512}' "$swapctl_file")"
name_val "Total" $(shorten "$(awk -F= '/hw.physmem/{print $2}' "$file")" 1)
name_val "User" $(shorten "$(awk -F= '/hw.usermem/{print $2}' "$file")" 1)
name_val "Swap" $(shorten ${swap_mem} 1)
}
# ##############################################################################
# Parse memory devices from the output of 'dmidecode'.
# ##############################################################################
parse_dmidecode_mem_devices () { local PTFUNCNAME=parse_dmidecode_mem_devices;
local file="$1"
[ -e "$file" ] || return
echo " Locator Size Speed Form Factor Type Type Detail"
echo " ========= ======== ================= ============= ============= ==========="
# Print paragraphs containing 'Memory Device\n', extract the desired bits,
# concatenate them into one long line, then format as a table. The data
# comes out in this order for each paragraph:
# $2 Size 2048 MB
# $3 Form Factor <OUT OF SPEC>
# $4 Locator DIMM1
# $5 Type <OUT OF SPEC>
# $6 Type Detail Synchronous
# $7 Speed 667 MHz (1.5 ns)
sed -e '/./{H;$!d;}' \
-e 'x;/Memory Device\n/!d;' \
-e 's/: /:/g' \
-e 's/</{/g' \
-e 's/>/}/g' \
-e 's/[ \t]*\n/\n/g' \
"${file}" \
| awk -F: '/Size|Type|Form.Factor|Type.Detail|^[\t ]+Locator|^[\t ]+Speed/{printf("|%s", $2)}/^$/{print}' \
| sed '/^$/d' \
| sed -e 's/No Module Installed/{EMPTY}/' \
| sort \
| awk -F'|' '{printf(" %-9s %-8s %-17s %-13s %-13s %-8s\n", $4, $2, $7, $3, $5, $6);}'
}
# ##############################################################################
# Parse CPU cache from the output of 'dmidecode'.
# ##############################################################################
parse_dmidecode_cache_info () { local PTFUNCNAME=parse_dmidecode_cache_info;
local file="$1"
[ -e "$file" ] || return
echo " Designation Configuration Size Associativity"
echo " ========================= ============================== ======== ======================"
sed -e '/./{H;$!d;}' \
-e 'x;/Cache Information\n/!d;' \
-e 's/: /:/g' \
-e 's/</{/g' \
-e 's/>/}/g' \
-e 's/[ \t]*\n/\n/g' \
"${file}" \
| awk -F: '/Socket Designation|Configuration|Installed Size/{printf("|%s", $2)}/^[\t ]+Associativity/{print "|" $2}' \
| awk -F'|' '{printf(" %-25s %-30s %-8s %-22s\n", $2, $3, $4, $5);}'
}
# ##############################################################################
# Parse the output of 'numactl'.
# ##############################################################################
parse_numactl () { local PTFUNCNAME=parse_numactl;
local file="$1"
[ -e "$file" ] || return
# Print info about NUMA nodes
echo " Node Size Free CPUs"
echo " ==== ==== ==== ===="
sed -n -e 's/node /node/g' \
-e '/node[[:digit:]]/p' \
"${file}" \
| sort -r \
| awk '$1 == cnode {
if (NF > 4) { for(i=3;i<=NF;i++){printf("%s ", $i)} printf "\n" }
else { printf("%-12s", $3" "$4); }
}
$1 != cnode { cnode = $1; printf(" %-8s", $1); printf("%-12s", $3" "$4); }'
echo
}
# ##############################################################################
# Parse the output of 'ip -s link'
# ##############################################################################
parse_ip_s_link () { local PTFUNCNAME=parse_ip_s_link;
local file="$1"
[ -e "$file" ] || return
echo " interface rx_bytes rx_packets rx_errors tx_bytes tx_packets tx_errors"
echo " ========= ========= ========== ========== ========== ========== =========="
awk "/^[1-9][0-9]*:/ {
save[\"iface\"] = substr(\$2, 1, index(\$2, \":\") - 1);
new = 1;
}
\$0 !~ /[^0-9 ]/ {
if ( new == 1 ) {
new = 0;
fuzzy_var = \$1; ${fuzzy_formula} save[\"bytes\"] = fuzzy_var;
fuzzy_var = \$2; ${fuzzy_formula} save[\"packs\"] = fuzzy_var;
fuzzy_var = \$3; ${fuzzy_formula} save[\"errs\"] = fuzzy_var;
}
else {
fuzzy_var = \$1; ${fuzzy_formula} tx_bytes = fuzzy_var;
fuzzy_var = \$2; ${fuzzy_formula} tx_packets = fuzzy_var;
fuzzy_var = \$3; ${fuzzy_formula} tx_errors = fuzzy_var;
printf \" %-8s %10.0f %10.0f %10.0f %10.0f %10.0f %10.0f\\n\", save[\"iface\"], save[\"bytes\"], save[\"packs\"], save[\"errs\"], tx_bytes, tx_packets, tx_errors;
}
}" "$file"
}
# ##############################################################################
# Parse the output of 'ethtool DEVICE'
# ##############################################################################
parse_ethtool () {
local file="$1"
[ -e "$file" ] || return
echo " Device Speed Duplex"
echo " ========= ========= ========="
awk '
/^Settings for / {
device = substr($3, 1, index($3, ":") ? index($3, ":")-1 : length($3));
device_names[device] = device;
}
/Speed:/ { devices[device ",speed"] = $2 }
/Duplex:/ { devices[device ",duplex"] = $2 }
END {
for ( device in device_names ) {
printf(" %-10s %-10s %-10s\n",
device,
devices[device ",speed"],
devices[device ",duplex"]);
}
}
' "$file"
}
# ##############################################################################
# Parse the output of 'netstat -antp'
# ##############################################################################
parse_netstat () { local PTFUNCNAME=parse_netstat;
local file="$1"
[ -e "$file" ] || return
echo " Connections from remote IP addresses"
awk '$1 ~ /^tcp/ && $5 ~ /^[1-9]/ {
print substr($5, 1, index($5, ":") - 1);
}' "${file}" | sort | uniq -c \
| awk "{
fuzzy_var=\$1;
${fuzzy_formula}
printf \" %-15s %5d\\n\", \$2, fuzzy_var;
}" \
| sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4
echo " Connections to local IP addresses"
awk '$1 ~ /^tcp/ && $5 ~ /^[1-9]/ {
print substr($4, 1, index($4, ":") - 1);
}' "${file}" | sort | uniq -c \
| awk "{
fuzzy_var=\$1;
${fuzzy_formula}
printf \" %-15s %5d\\n\", \$2, fuzzy_var;
}" \
| sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4
echo " Connections to top 10 local ports"
awk '$1 ~ /^tcp/ && $5 ~ /^[1-9]/ {
print substr($4, index($4, ":") + 1);
}' "${file}" | sort | uniq -c | sort -rn | head -n10 \
| awk "{
fuzzy_var=\$1;
${fuzzy_formula}
printf \" %-15s %5d\\n\", \$2, fuzzy_var;
}" | sort
echo " States of connections"
awk '$1 ~ /^tcp/ {
print $6;
}' "${file}" | sort | uniq -c | sort -rn \
| awk "{
fuzzy_var=\$1;
${fuzzy_formula}
printf \" %-15s %5d\\n\", \$2, fuzzy_var;
}" | sort
}
# ##############################################################################
# Parse the joined output of 'mount' and 'df -hP'. $1 = file; $2 = ostype.
# ##############################################################################
parse_filesystems () { local PTFUNCNAME=parse_filesystems;
# Filesystem names and mountpoints can be very long. We try to align things
# as nicely as possible by making columns only as wide as needed. This
# requires two passes through the file. The first pass finds the max size of
# these columns and prints out a printf spec, and the second prints out the
# file nicely aligned.
local file="$1"
local platform="$2"
[ -e "$file" ] || return
local spec="$(awk "
BEGIN {
device = 10;
fstype = 4;
options = 4;
}
/./ {
f_device = \$1;
f_fstype = \$10;
f_options = substr(\$11, 2, length(\$11) - 2);
if ( \"$2\" ~ /(Free|Open|Net)BSD/ ) {
f_fstype = substr(\$9, 2, length(\$9) - 2);
f_options = substr(\$0, index(\$0, \",\") + 2);
f_options = substr(f_options, 1, length(f_options) - 1);
}
if ( length(f_device) > device ) {
device=length(f_device);
}
if ( length(f_fstype) > fstype ) {
fstype=length(f_fstype);
}
if ( length(f_options) > options ) {
options=length(f_options);
}
}
END{
print \"%-\" device \"s %5s %4s %-\" fstype \"s %-\" options \"s %s\";
}
" "${file}")"
awk "
BEGIN {
spec=\" ${spec}\\n\";
printf spec, \"Filesystem\", \"Size\", \"Used\", \"Type\", \"Opts\", \"Mountpoint\";
}
{
f_fstype = \$10;
f_options = substr(\$11, 2, length(\$11) - 2);
if ( \"$2\" ~ /(Free|Open|Net)BSD/ ) {
f_fstype = substr(\$9, 2, length(\$9) - 2);
f_options = substr(\$0, index(\$0, \",\") + 2);
f_options = substr(f_options, 1, length(f_options) - 1);
}
printf spec, \$1, \$2, \$5, f_fstype, f_options, \$6;
}
" "${file}"
}
# ##############################################################################
# Parse the output of fdisk -l, which should be in $PT_TMPDIR/percona-toolkit; there might be
# multiple fdisk -l outputs in the file.
# ##############################################################################
parse_fdisk () { local PTFUNCNAME=parse_fdisk;
local file="$1"
[ -e "$file" -a -s "$file" ] || return
awk '
BEGIN {
format="%-12s %4s %10s %10s %18s\n";
printf(format, "Device", "Type", "Start", "End", "Size");
printf(format, "============", "====", "==========", "==========", "==================");
}
/Disk.*bytes/ {
disk = substr($2, 1, length($2) - 1);
size = $5;
printf(format, disk, "Disk", "", "", size);
}
/Units/ {
units = $9;
}
/^\/dev/ {
if ( $2 == "*" ) {
start = $3;
end = $4;
}
else {
start = $2;
end = $3;
}
printf(format, $1, "Part", start, end, sprintf("%.0f", (end - start) * units));
}
' "${file}"
}
# ##############################################################################
# Parse the output of lspci, and detect ethernet cards.
# ##############################################################################
parse_ethernet_controller_lspci () { local PTFUNCNAME=parse_ethernet_controller_lspci;
local file="$1"
[ -e "$file" ] || return
grep -i ethernet "${file}" | cut -d: -f3 | while read line; do
name_val "Controller" "${line}"
done
}
# ##############################################################################
# Parse the output of "hpacucli ctrl all show config", which should be stored in
# $PT_TMPDIR/percona-toolkit
# ##############################################################################
parse_hpacucli () { local PTFUNCNAME=parse_hpacucli;
local file="$1"
[ -e "$file" ] || return
grep 'logicaldrive\|physicaldrive' "${file}"
}
# ##############################################################################
# Parse the output of arcconf, which should be stored in $PT_TMPDIR/percona-toolkit
# ##############################################################################
parse_arcconf () { local PTFUNCNAME=parse_arcconf;
local file="$1"
[ -e "$file" ] || return
local model="$(awk -F: '/Controller Model/{print $2}' "${file}")"
local chan="$(awk -F: '/Channel description/{print $2}' "${file}")"
local cache="$(awk -F: '/Installed memory/{print $2}' "${file}")"
local status="$(awk -F: '/Controller Status/{print $2}' "${file}")"
name_val "Specs" "$(echo "$model" | sed -e 's/ //'),${chan},${cache} cache,${status}"
local battery=""
if grep -q "ZMM" "$file"; then
battery="$(grep -A2 'Controller ZMM Information' "$file" \
| awk '/Status/ {s=$4}
END {printf "ZMM %s", s}')"
else
battery="$(grep -A5 'Controller Battery Info' "${file}" \
| awk '/Capacity remaining/ {c=$4}
/Status/ {s=$3}
/Time remaining/ {t=sprintf("%dd%dh%dm", $7, $9, $11)}
END {printf("%d%%, %s remaining, %s", c, t, s)}')"
fi
name_val "Battery" "${battery}"
# ###########################################################################
# Logical devices
# ###########################################################################
echo
echo " LogicalDev Size RAID Disks Stripe Status Cache"
echo " ========== ========= ==== ===== ====== ======= ======="
for dev in $(awk '/Logical device number/{print $4}' "${file}"); do
sed -n -e "/^Logical device .* ${dev}$/,/^$\|^Logical device number/p" "${file}" \
| awk '
/Logical device name/ {d=$5}
/Size/ {z=$3 " " $4}
/RAID level/ {r=$4}
/Group [0-9]/ {g++}
/Stripe-unit size/ {p=$4 " " $5}
/Status of logical/ {s=$6}
/Write-cache mode.*Ena.*write-back/ {c="On (WB)"}
/Write-cache mode.*Ena.*write-thro/ {c="On (WT)"}
/Write-cache mode.*Disabled/ {c="Off"}
END {
printf(" %-10s %-9s %4d %5d %-6s %-7s %-7s\n",
d, z, r, g, p, s, c);
}'
done
# ###########################################################################
# Physical devices
# ###########################################################################
echo
echo " PhysiclDev State Speed Vendor Model Size Cache"
echo " ========== ======= ============= ======= ============ =========== ======="
# Find the paragraph with physical devices, tabularize with assoc arrays.
local tempresult=""
sed -n -e '/Physical Device information/,/^$/p' "${file}" \
| awk -F: '
/Device #[0-9]/ {
device=substr($0, index($0, "#"));
devicenames[device]=device;
}
/Device is a/ {
devices[device ",isa"] = substr($0, index($0, "is a") + 5);
}
/State/ {
devices[device ",state"] = substr($2, 2);
}
/Transfer Speed/ {
devices[device ",speed"] = substr($2, 2);
}
/Vendor/ {
devices[device ",vendor"] = substr($2, 2);
}
/Model/ {
devices[device ",model"] = substr($2, 2);
}
/Size/ {
devices[device ",size"] = substr($2, 2);
}
/Write Cache/ {
if ( $2 ~ /Enabled .write-back./ )
devices[device ",cache"] = "On (WB)";
else
if ( $2 ~ /Enabled .write-th/ )
devices[device ",cache"] = "On (WT)";
else
devices[device ",cache"] = "Off";
}
END {
for ( device in devicenames ) {
if ( devices[device ",isa"] ~ /Hard drive/ ) {
printf(" %-10s %-7s %-13s %-7s %-12s %-11s %-7s\n",
devices[device ",isa"],
devices[device ",state"],
devices[device ",speed"],
devices[device ",vendor"],
devices[device ",model"],
devices[device ",size"],
devices[device ",cache"]);
}
}
}'
}
# ##############################################################################
# Parse the output of "lsiutil -i -s".
# ##############################################################################
# TODO This isn't used anywhere
parse_fusionmpt_lsiutil () { local PTFUNCNAME=parse_fusionmpt_lsiutil;
local file="$1"
echo
awk '/LSI.*Firmware/ { print " ", $0 }' "${file}"
grep . "${file}" | sed -n -e '/B___T___L/,$ {s/^/ /; p}'
}
# ##############################################################################
# Parse the output of MegaCli64 -AdpAllInfo -aALL
# ##############################################################################
# TODO why aren't we printing the latter half?
parse_lsi_megaraid_adapter_info () { local PTFUNCNAME=parse_lsi_megaraid_adapter_info;
local file="$1"
[ -e "$file" ] || return
local name="$(awk -F: '/Product Name/{print substr($2, 2)}' "${file}")";
local int=$(awk '/Host Interface/{print $4}' "${file}");
local prt=$(awk '/Number of Backend Port/{print $5}' "${file}");
local bbu=$(awk '/^BBU :/{print $3}' "${file}");
local mem=$(awk '/Memory Size/{print $4}' "${file}");
local vdr=$(awk '/Virtual Drives/{print $4}' "${file}");
local dvd=$(awk '/Degraded/{print $3}' "${file}");
local phy=$(awk '/^ Disks/{print $3}' "${file}");
local crd=$(awk '/Critical Disks/{print $4}' "${file}");
local fad=$(awk '/Failed Disks/{print $4}' "${file}");
name_val "Model" "${name}, ${int} interface, ${prt} ports"
name_val "Cache" "${mem} Memory, BBU ${bbu}"
}
# ##############################################################################
# Parse the output of
# /opt/MegaRAID/MegaCli/MegaCli64 -AdpBbuCmd -GetBbuStatus -aALL
# ##############################################################################
parse_lsi_megaraid_bbu_status () { local PTFUNCNAME=parse_lsi_megaraid_bbu_status;
local file="$1"
[ -e "$file" ] || return
local charge=$(awk '/Relative State/{print $5}' "${file}");
local temp=$(awk '/^Temperature/{print $2}' "${file}");
local soh=$(awk '/isSOHGood:/{print $2}' "${file}");
name_val "BBU" "${charge}% Charged, Temperature ${temp}C, isSOHGood=${soh}"
}
# ##############################################################################
# Reports the output of lvs. Additionally, if the second argument is a file
# that contains the output of 'vgs -o vg_name,vg_size,vg_free', appends the
# total and free space available to each volume.
# ##############################################################################
format_lvs () { local PTFUNCNAME=format_lvs;
local file="$1"
if [ -e "$file" ]; then
grep -v "open failed" "$file"
else
echo "Unable to collect information";
fi
}
# ##############################################################################
# Parse physical devices from the output of
# /opt/MegaRAID/MegaCli/MegaCli64 -LdPdInfo -aALL
# OR, it will also work with the output of
# /opt/MegaRAID/MegaCli/MegaCli64 -PDList -aALL
# ##############################################################################
parse_lsi_megaraid_devices () { local PTFUNCNAME=parse_lsi_megaraid_devices;
local file="$1"
[ -e "$file" ] || return
echo
echo " PhysiclDev Type State Errors Vendor Model Size"
echo " ========== ==== ======= ====== ======= ============ ==========="
for dev in $(awk '/Device Id/{print $3}' "${file}"); do
sed -e '/./{H;$!d;}' -e "x;/Device Id: ${dev}/!d;" "${file}" \
| awk '
/Media Type/ {d=substr($0, index($0, ":") + 2)}
/PD Type/ {t=$3}
/Firmware state/ {s=$3}
/Media Error Count/ {me=$4}
/Other Error Count/ {oe=$4}
/Predictive Failure Count/ {pe=$4}
/Inquiry Data/ {v=$3; m=$4;}
/Raw Size/ {z=$3}
END {
printf(" %-10s %-4s %-7s %6s %-7s %-12s %-7s\n",
substr(d, 1, 10), t, s, me "/" oe "/" pe, v, m, z);
}'
done
}
# ##############################################################################
# Parse virtual devices from the output of
# /opt/MegaRAID/MegaCli/MegaCli64 -LdPdInfo -aALL
# OR, it will also work with the output of
# /opt/MegaRAID/MegaCli/MegaCli64 -LDInfo -Lall -aAll
# ##############################################################################
parse_lsi_megaraid_virtual_devices () { local PTFUNCNAME=parse_lsi_megaraid_virtual_devices;
local file="$1"
[ -e "$file" ] || return
# Somewhere on the Internet, I found the following guide to understanding the
# RAID level, but I don't know the source anymore.
# Primary-0, Secondary-0, RAID Level Qualifier-0 = 0
# Primary-1, Secondary-0, RAID Level Qualifier-0 = 1
# Primary-5, Secondary-0, RAID Level Qualifier-3 = 5
# Primary-1, Secondary-3, RAID Level Qualifier-0 = 10
# I am not sure if this is always correct or not (it seems correct). The
# terminology MegaRAID uses is not clear to me, and isn't documented that I
# am aware of. Anyone who can clarify the above, please contact me.
echo
echo " VirtualDev Size RAID Level Disks SpnDpth Stripe Status Cache"
echo " ========== ========= ========== ===== ======= ====== ======= ========="
awk '
/^Virtual (Drive|Disk):/ {
device = $3;
devicenames[device] = device;
}
/Number Of Drives/ {
devices[device ",numdisks"] = substr($0, index($0, ":") + 1);
}
/^Name/ {
devices[device ",name"] = substr($0, index($0, ":") + 1) > "" ? substr($0, index($0, ":") + 1) : "(no name)";
}
/RAID Level/ {
devices[device ",primary"] = substr($3, index($3, "-") + 1, 1);
devices[device ",secondary"] = substr($4, index($4, "-") + 1, 1);
devices[device ",qualifier"] = substr($NF, index($NF, "-") + 1, 1);
}
/Span Depth/ {
devices[device ",spandepth"] = substr($2, index($2, ":") + 1);
}
/Number of Spans/ {
devices[device ",numspans"] = $4;
}
/^Size/ {
devices[device ",size"] = substr($0, index($0, ":") + 1);
}
/^State/ {
devices[device ",state"] = substr($0, index($0, ":") + 2);
}
/^Stripe? Size/ {
devices[device ",stripe"] = substr($0, index($0, ":") + 1);
}
/^Current Cache Policy/ {
devices[device ",wpolicy"] = $4 ~ /WriteBack/ ? "WB" : "WT";
devices[device ",rpolicy"] = $5 ~ /ReadAheadNone/ ? "no RA" : "RA";
}
END {
for ( device in devicenames ) {
raid = 0;
if ( devices[device ",primary"] == 1 ) {
raid = 1;
if ( devices[device ",secondary"] == 3 ) {
raid = 10;
}
}
else {
if ( devices[device ",primary"] == 5 ) {
raid = 5;
}
}
printf(" %-10s %-9s %-10s %5d %7s %6s %-7s %s\n",
device devices[device ",name"],
devices[device ",size"],
raid " (" devices[device ",primary"] "-" devices[device ",secondary"] "-" devices[device ",qualifier"] ")",
devices[device ",numdisks"],
devices[device ",spandepth"] "-" devices[device ",numspans"],
devices[device ",stripe"], devices[device ",state"],
devices[device ",wpolicy"] ", " devices[device ",rpolicy"]);
}
}' "${file}"
}
# ##############################################################################
# Simplifies vmstat and aligns it nicely. We don't need the memory stats, the
# system activity is enough.
# ##############################################################################
format_vmstat () { local PTFUNCNAME=format_vmstat;
local file="$1"
[ -e "$file" ] || return
awk "
BEGIN {
format = \" %2s %2s %4s %4s %5s %5s %6s %6s %3s %3s %3s %3s %3s\n\";
}
/procs/ {
print \" procs ---swap-- -----io---- ---system---- --------cpu--------\";
}
/bo/ {
printf format, \"r\", \"b\", \"si\", \"so\", \"bi\", \"bo\", \"ir\", \"cs\", \"us\", \"sy\", \"il\", \"wa\", \"st\";
}
\$0 !~ /r/ {
fuzzy_var = \$1; ${fuzzy_formula} r = fuzzy_var;
fuzzy_var = \$2; ${fuzzy_formula} b = fuzzy_var;
fuzzy_var = \$7; ${fuzzy_formula} si = fuzzy_var;
fuzzy_var = \$8; ${fuzzy_formula} so = fuzzy_var;
fuzzy_var = \$9; ${fuzzy_formula} bi = fuzzy_var;
fuzzy_var = \$10; ${fuzzy_formula} bo = fuzzy_var;
fuzzy_var = \$11; ${fuzzy_formula} ir = fuzzy_var;
fuzzy_var = \$12; ${fuzzy_formula} cs = fuzzy_var;
fuzzy_var = \$13; us = fuzzy_var;
fuzzy_var = \$14; sy = fuzzy_var;
fuzzy_var = \$15; il = fuzzy_var;
fuzzy_var = \$16; wa = fuzzy_var;
fuzzy_var = \$17; st = fuzzy_var;
printf format, r, b, si, so, bi, bo, ir, cs, us, sy, il, wa, st;
}
" "${file}"
}
processes_section () { local PTFUNCNAME=processes_section;
local top_process_file="$1"
local notable_procs_file="$2"
local vmstat_file="$3"
local platform="$4"
section "Top Processes"
cat "$top_process_file"
section "Notable Processes"
cat "$notable_procs_file"
if [ -e "$vmstat_file" ]; then
section "Simplified and fuzzy rounded vmstat (wait please)"
wait # For the process we forked that was gathering vmstat samples
if [ "${platform}" = "Linux" ]; then
format_vmstat "$vmstat_file"
else
# TODO: simplify/format for other platforms
cat "$vmstat_file"
fi
fi
}
section_Processor () {
local platform="$1"
local data_dir="$2"
section "Processor"
if [ -e "$data_dir/proc_cpuinfo_copy" ]; then
parse_proc_cpuinfo "$data_dir/proc_cpuinfo_copy"
elif [ "${platform}" = "FreeBSD" ]; then
parse_sysctl_cpu_freebsd "$data_dir/sysctl"
elif [ "${platform}" = "NetBSD" ]; then
parse_sysctl_cpu_netbsd "$data_dir/sysctl"
elif [ "${platform}" = "OpenBSD" ]; then
parse_sysctl_cpu_openbsd "$data_dir/sysctl"
elif [ "${platform}" = "SunOS" ]; then
parse_psrinfo_cpus "$data_dir/psrinfo_minus_v"
# TODO: prtconf -v actually prints the CPU model name etc.
fi
if [ -s "$data_dir/dmidecode" ]; then
parse_dmidecode_cache_info "$data_dir/dmidecode"
fi
}
section_Memory () {
local platform="$1"
local data_dir="$2"
local name_val_len_orig=$NAME_VAL_LEN;
local NAME_VAL_LEN=14
section "Memory"
if [ "${platform}" = "Linux" ]; then
parse_free_minus_b "$data_dir/memory"
elif [ "${platform}" = "FreeBSD" ]; then
parse_memory_sysctl_freebsd "$data_dir/sysctl"
elif [ "${platform}" = "NetBSD" ]; then
parse_memory_sysctl_netbsd "$data_dir/sysctl" "$data_dir/swapctl"
elif [ "${platform}" = "OpenBSD" ]; then
parse_memory_sysctl_openbsd "$data_dir/sysctl" "$data_dir/swapctl"
elif [ "${platform}" = "SunOS" ]; then
name_val "Memory" "$(cat "$data_dir/memory")"
fi
local rss=$( get_var "rss" "$data_dir/summary" )
name_val "UsedRSS" "$(shorten ${rss} 1)"
if [ "${platform}" = "Linux" ]; then
name_val "Swappiness" "$(get_var "swappiness" "$data_dir/summary")"
name_val "DirtyPolicy" "$(get_var "dirtypolicy" "$data_dir/summary")"
local dirty_status="$(get_var "dirtystatus" "$data_dir/summary")"
if [ -n "$dirty_status" ]; then
name_val "DirtyStatus" "$dirty_status"
fi
fi
if [ -s "$data_dir/numactl" ]; then
name_val "Numa Nodes" "$(get_var "numa-available" "$data_dir/summary")"
name_val "Numa Policy" "$(get_var "numa-policy" "$data_dir/summary")"
name_val "Preferred Node" "$(get_var "numa-preferred-node" "$data_dir/summary")"
parse_numactl "$data_dir/numactl"
fi
local NAME_VAL_LEN=$name_val_len_orig;
if [ -s "$data_dir/dmidecode" ]; then
parse_dmidecode_mem_devices "$data_dir/dmidecode"
fi
}
parse_uptime () {
local file="$1"
awk ' / up / {
printf substr($0, index($0, " up ")+4 );
}
!/ up / {
printf $0;
}
' "$file"
}
report_fio_minus_a () {
local file="$1"
name_val "fio Driver" "$(get_var driver_version "$file")"
local adapters="$( get_var "adapters" "$file" )"
for adapter in $( echo $adapters | awk '{for (i=1; i<=NF; i++) print $i;}' ); do
local adapter_for_output="$(echo "$adapter" | sed 's/::[0-9]*$//' | tr ':' ' ')"
name_val "$adapter_for_output" "$(get_var "${adapter}_general" "$file")"
local modules="$(get_var "${adapter}_modules" "$file")"
for module in $( echo $modules | awk '{for (i=1; i<=NF; i++) print $i;}' ); do
local name_val_len_orig=$NAME_VAL_LEN;
local NAME_VAL_LEN=16
name_val "$module" "$(get_var "${adapter}_${module}_attached_as" "$file")"
name_val "" "$(get_var "${adapter}_${module}_general" "$file")"
name_val "" "$(get_var "${adapter}_${module}_firmware" "$file")"
name_val "" "$(get_var "${adapter}_${module}_temperature" "$file")"
name_val "" "$(get_var "${adapter}_${module}_media_status" "$file")"
if [ "$(get_var "${adapter}_${module}_rated_pbw" "$file")" ]; then
name_val "" "$(get_var "${adapter}_${module}_rated_pbw" "$file")"
fi
local NAME_VAL_LEN=$name_val_len_orig;
done
done
}
# The sum of all of the above
report_system_summary () { local PTFUNCNAME=report_system_summary;
local data_dir="$1"
section "Percona Toolkit System Summary Report"
# ########################################################################
# General date, time, load, etc
# ########################################################################
[ -e "$data_dir/summary" ] \
|| die "The data directory doesn't have a summary file, exiting."
local platform="$(get_var "platform" "$data_dir/summary")"
name_val "Date" "`date -u +'%F %T UTC'` (local TZ: `date +'%Z %z'`)"
name_val "Hostname" "$(get_var hostname "$data_dir/summary")"
name_val "Uptime" "$(parse_uptime "$data_dir/uptime")"
if [ "$(get_var "vendor" "$data_dir/summary")" ]; then
name_val "System" "$(get_var "system" "$data_dir/summary")";
name_val "Service Tag" "$(get_var "servicetag" "$data_dir/summary")";
fi
name_val "Platform" "${platform}"
local zonename="$(get_var zonename "$data_dir/summary")";
[ -n "${zonename}" ] && name_val "Zonename" "$zonename"
name_val "Release" "$(get_var "release" "$data_dir/summary")"
name_val "Kernel" "$(get_var "kernel" "$data_dir/summary")"
name_val "Architecture" "CPU = $(get_var "CPU_ARCH" "$data_dir/summary"), OS = $(get_var "OS_ARCH" "$data_dir/summary")"
local threading="$(get_var threading "$data_dir/summary")"
local compiler="$(get_var compiler "$data_dir/summary")"
[ -n "$threading" ] && name_val "Threading" "$threading"
[ -n "$compiler" ] && name_val "Compiler" "$compiler"
local getenforce="$(get_var getenforce "$data_dir/summary")"
[ -n "$getenforce" ] && name_val "SELinux" "${getenforce}";
name_val "Virtualized" "$(get_var "virt" "$data_dir/summary")"
# ########################################################################
# Processor/CPU, Memory, Swappiness, dmidecode
# ########################################################################
section_Processor "$platform" "$data_dir"
section_Memory "$platform" "$data_dir"
# ########################################################################
# Disks, RAID, Filesystems
# ########################################################################
# TODO: Add info about software RAID
if [ -s "$data_dir/fusion-io_card" ]; then
section "Fusion-io Card"
report_fio_minus_a "$data_dir/fusion-io_card"
fi
if [ -s "$data_dir/mounted_fs" ]; then
section "Mounted Filesystems"
parse_filesystems "$data_dir/mounted_fs" "${platform}"
fi
if [ "${platform}" = "Linux" ]; then
section "Disk Schedulers And Queue Size"
local disks="$( get_var "internal::disks" "$data_dir/summary" )"
for disk in $disks; do
local scheduler="$( get_var "internal::${disk}" "$data_dir/summary" )"
name_val "${disk}" "${scheduler:-"UNREADABLE"}"
done
section "Disk Partitioning"
parse_fdisk "$data_dir/partitioning"
section "Kernel Inode State"
for file in dentry-state file-nr inode-nr; do
name_val "${file}" "$(get_var "${file}" "$data_dir/summary")"
done
section "LVM Volumes"
format_lvs "$data_dir/lvs"
section "LVM Volume Groups"
format_lvs "$data_dir/vgs"
fi
section "RAID Controller"
local controller="$(get_var "raid_controller" "$data_dir/summary")"
name_val "Controller" "$controller"
local key="$(get_var "internal::raid_opt" "$data_dir/summary")"
case "$key" in
0)
# Not found
cat "$data_dir/raid-controller"
;;
1)
parse_arcconf "$data_dir/raid-controller"
;;
2)
parse_hpacucli "$data_dir/raid-controller"
;;
3)
# TODO: This is pretty bad form, but seeing how the three forms
# aren't mutually exclusive, I can't come up with a better way.
[ -e "$data_dir/lsi_megaraid_adapter_info.tmp" ] && \
parse_lsi_megaraid_adapter_info "$data_dir/lsi_megaraid_adapter_info.tmp"
[ -e "$data_dir/lsi_megaraid_bbu_status.tmp" ] && \
parse_lsi_megaraid_bbu_status "$data_dir/lsi_megaraid_bbu_status.tmp"
if [ -e "$data_dir/lsi_megaraid_devices.tmp" ]; then
parse_lsi_megaraid_virtual_devices "$data_dir/lsi_megaraid_devices.tmp"
parse_lsi_megaraid_devices "$data_dir/lsi_megaraid_devices.tmp"
fi
;;
esac
if [ "${OPT_SUMMARIZE_NETWORK}" ]; then
# #####################################################################
# Network stuff
# #####################################################################
if [ "${platform}" = "Linux" ]; then
section "Network Config"
if [ -s "$data_dir/lspci_file" ]; then
parse_ethernet_controller_lspci "$data_dir/lspci_file"
fi
if grep "net.ipv4.tcp_fin_timeout" "$data_dir/sysctl" > /dev/null 2>&1; then
name_val "FIN Timeout" "$(awk '/net.ipv4.tcp_fin_timeout/{print $NF}' "$data_dir/sysctl")"
name_val "Port Range" "$(awk '/net.ipv4.ip_local_port_range/{print $NF}' "$data_dir/sysctl")"
fi
fi
# TODO cat /proc/sys/net/ipv4/ip_conntrack_max ; it might be
# /proc/sys/net/netfilter/nf_conntrack_max or /proc/sys/net/nf_conntrack_max
# in new kernels like Fedora 12?
if [ -s "$data_dir/ip" ]; then
section "Interface Statistics"
parse_ip_s_link "$data_dir/ip"
fi
if [ -s "$data_dir/network_devices" ]; then
section "Network Devices"
parse_ethtool "$data_dir/network_devices"
fi
if [ "${platform}" = "Linux" -a -e "$data_dir/netstat" ]; then
section "Network Connections"
parse_netstat "$data_dir/netstat"
fi
fi
# ########################################################################
# Processes, load, etc
# ########################################################################
[ "$OPT_SUMMARIZE_PROCESSES" ] && processes_section \
"$data_dir/processes" \
"$data_dir/notable_procs" \
"$data_dir/vmstat" \
"$platform"
section "Memory management"
report_transparent_huge_pages "$data_dir/transparent_hugepage"
# ########################################################################
# All done. Signal the end so it's explicit.
# ########################################################################
section "The End"
}
report_transparent_huge_pages () {
local file="$1"
[ -e "$file" ] || return
if [ $(grep -cv '\[never\]' $file) = 0 ]; then
echo "Transparent huge pages are currently disabled on the system."
else
echo "Transparent huge pages are enabled."
fi
}
# ###########################################################################
# End report_system_info package
# ###########################################################################