LC_ALL=C platform=$(uname -s 2>/dev/null || echo unknown) kernel=$(uname -r 2>/dev/null || echo unknown) host=$(hostname 2>/dev/null || echo unknown) cpu_arch=$(uname -m 2>/dev/null || echo unknown) cpu_usage=0 stat_second= cpu_logical=0 cpu_physical=0 cpu_sockets=0 cpu_model=unknown cpu_core_types= network_default= if [ -r /proc/uptime ]; then uptime_s=$(awk '{print int($1)}' /proc/uptime) else boot_epoch=$(sysctl -n kern.boottime 2>/dev/null | awk -F'[=,]' '{gsub(/[^0-9]/,"",$2); print $2}') now_epoch=$(date +%s 2>/dev/null || echo 0) if [ -n "$boot_epoch" ] && [ "$now_epoch" -ge "$boot_epoch" ] 2>/dev/null; then uptime_s=$((now_epoch-boot_epoch)); else uptime_s=0; fi fi if [ -r /proc/loadavg ]; then load1=$(awk '{print $1}' /proc/loadavg); else load1=$(uptime | sed -E 's/.*load averages?:[[:space:]]*([0-9.]+).*/\1/'); fi if [ -r /proc/meminfo ]; then mem_total=$(awk '/^MemTotal:/{print $2*1024}' /proc/meminfo) mem_avail=$(awk '/^MemAvailable:/{print $2*1024}' /proc/meminfo) swap_total=$(awk '/^SwapTotal:/{print $2*1024}' /proc/meminfo) swap_free=$(awk '/^SwapFree:/{print $2*1024}' /proc/meminfo) cpu_first=$(awk '/^cpu /{idle=$5+$6; total=0; for(i=2;i<=9 && i<=NF;i++)total+=$i; printf "%.0f:%.0f",idle,total; exit}' /proc/stat) stat_first=$(grep '^cpu[0-9]' /proc/stat) sleep 0.2 stat_second=$(grep '^cpu[0-9]' /proc/stat) cpu_second=$(awk '/^cpu /{idle=$5+$6; total=0; for(i=2;i<=9 && i<=NF;i++)total+=$i; printf "%.0f:%.0f",idle,total; exit}' /proc/stat) cpu_usage=$(awk -v first="$cpu_first" -v second="$cpu_second" 'BEGIN{split(first,a,":");split(second,b,":");total=b[2]-a[2];idle=b[1]-a[1];if(total>0)printf "%.2f",100*(total-idle)/total;else print 0}') cpu_logical=$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc 2>/dev/null || awk '/^processor[[:space:]]*:/{n++}END{print n+0}' /proc/cpuinfo) cpu_model=$(awk -F: '/^(model name|Processor|Hardware)[[:space:]]*:/{gsub(/^[[:space:]]+|[[:space:]]+$/,"",$2);if($2!=""){print $2;exit}}' /proc/cpuinfo) if command -v lscpu >/dev/null 2>&1; then cpu_physical=$(lscpu -p=Core,Socket 2>/dev/null | awk -F, '!/^#/&&$1~/^[0-9]+$/{seen[$1":"$2]=1}END{for(k in seen)n++;print n+0}') cpu_sockets=$(lscpu -p=Socket 2>/dev/null | awk -F, '!/^#/&&$1~/^[0-9]+$/{seen[$1]=1}END{for(k in seen)n++;print n+0}') fi [ "$cpu_physical" -gt 0 ] 2>/dev/null || cpu_physical=$cpu_logical [ "$cpu_sockets" -gt 0 ] 2>/dev/null || cpu_sockets=1 if ls /sys/devices/system/cpu/cpu*/topology/core_type >/dev/null 2>&1; then core_type_records=$(for type_file in /sys/devices/system/cpu/cpu*/topology/core_type; do topology=$(dirname "$type_file"); type=$(cat "$type_file" 2>/dev/null); core=$(cat "$topology/core_id" 2>/dev/null); package=$(cat "$topology/physical_package_id" 2>/dev/null); printf '%s:%s:%s\n' "$type" "$package" "$core"; done | sort -u) efficiency_cores=$(printf '%s\n' "$core_type_records" | awk -F: '$1==1{n++}END{print n+0}') performance_cores=$(printf '%s\n' "$core_type_records" | awk -F: '$1==2{n++}END{print n+0}') [ "$performance_cores" -gt 0 ] && cpu_core_types="P-core:$performance_cores" if [ "$efficiency_cores" -gt 0 ]; then [ -n "$cpu_core_types" ] && separator=, || separator=; cpu_core_types=$(printf '%s%sE-core:%s' "$cpu_core_types" "$separator" "$efficiency_cores"); fi fi if command -v ip >/dev/null 2>&1; then network_default=$(ip route show default 2>/dev/null | awk '{for(i=1;i<=NF;i++)if($i=="dev"){print $(i+1);exit}}'); fi else mem_total=$(sysctl -n hw.memsize 2>/dev/null || echo 0) page_size=$(vm_stat 2>/dev/null | awk '/page size of/{print $8}') [ -n "$page_size" ] || page_size=4096 mem_avail=$(vm_stat 2>/dev/null | awk -v page="$page_size" '/Pages (free|inactive|speculative):/{gsub(/\./,"",$3);pages+=$3}END{printf "%.0f",pages*page}') swap_total=0 swap_free=0 cpu_logical=$(sysctl -n hw.logicalcpu 2>/dev/null || echo 0) cpu_physical=$(sysctl -n hw.physicalcpu 2>/dev/null || echo "$cpu_logical") cpu_sockets=1 cpu_model=$(sysctl -n machdep.cpu.brand_string 2>/dev/null || sysctl -n hw.model 2>/dev/null || echo unknown) cpu_usage=$(top -l 1 -n 0 2>/dev/null | awk '/CPU usage:/{idle=$7;gsub(/%/,"",idle)}END{if(idle!="")printf "%.2f",100-idle;else print 0}') for level in 0 1 2; do count=$(sysctl -n "hw.perflevel$level.physicalcpu" 2>/dev/null || echo 0) if [ "$count" -gt 0 ] 2>/dev/null; then name=$(sysctl -n "hw.perflevel$level.name" 2>/dev/null || echo "Cluster $level") [ -n "$cpu_core_types" ] && separator=, || separator= cpu_core_types=$(printf '%s%s%s:%s' "$cpu_core_types" "$separator" "$name" "$count") fi done network_default=$(route -n get default 2>/dev/null | awk '/interface:/{print $2;exit}') fi [ -n "$cpu_model" ] || cpu_model=unknown cpu_model=$(printf '%s' "$cpu_model" | tr '\r\n|\036' ' ') cpu_core_types=$(printf '%s' "$cpu_core_types" | tr '\r\n|\036' ' ') disk=$(df -Pk / 2>/dev/null | awk 'NR==2{print $2*1024":"$3*1024":"$4*1024}') printf 'platform=%s\nkernel=%s\nhostname=%s\nuptimeSeconds=%s\nload1=%s\nmemoryTotal=%s\nmemoryAvailable=%s\nswapTotal=%s\nswapFree=%s\ndisk=%s\ncpuUsage=%s\ncpuArchitecture=%s\ncpuModel=%s\ncpuLogical=%s\ncpuPhysical=%s\ncpuSockets=%s\ncpuCoreTypes=%s\nnetworkDefault=%s\n' "$platform" "$kernel" "$host" "$uptime_s" "$load1" "$mem_total" "$mem_avail" "$swap_total" "$swap_free" "$disk" "$cpu_usage" "$cpu_arch" "$cpu_model" "$cpu_logical" "$cpu_physical" "$cpu_sockets" "$cpu_core_types" "$network_default" # Detailed memory and swap for the "Memory and virtual memory" section (kB fields converted to bytes). if [ -r /proc/meminfo ]; then awk '$1 ~ /^(MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapCached|Active|Inactive|Slab|SReclaimable|SUnreclaim|Shmem|KernelStack|PageTables|VmallocUsed|Percpu|Dirty|Writeback|SwapTotal|SwapFree|Zswap|Zswapped|CommitLimit|Committed_AS):$/ { key=$1; sub(/:$/, "", key); value=($3=="kB") ? $2*1024 : $2; printf "meminfo=%s|%.0f\n", key, value }' /proc/meminfo fi if [ -r /proc/swaps ]; then awk 'NR>1 { printf "swapArea=%s|%s|%.0f|%.0f|%s\n", $1, $2, $3*1024, $4*1024, $5 }' /proc/swaps fi if [ -r /proc/vmstat ]; then awk '$1=="pswpin" || $1=="pswpout" || $1=="pgmajfault" { printf "vmstat=%s|%s\n", $1, $2 }' /proc/vmstat fi for zram in /sys/block/zram*; do [ -r "$zram/mm_stat" ] || continue printf 'zram=%s|%s|%s|%s\n' "$(basename "$zram")" "$(cat "$zram/disksize" 2>/dev/null)" "$(cat "$zram/mm_stat" 2>/dev/null | tr -s ' ' ',')" "$(cat "$zram/comp_algorithm" 2>/dev/null | sed -n 's/.*\[\([^]]*\)\].*/\1/p')" done if [ -r /sys/module/zswap/parameters/enabled ]; then printf 'zswap=%s|%s|%s\n' "$(cat /sys/module/zswap/parameters/enabled)" "$(cat /sys/module/zswap/parameters/compressor 2>/dev/null)" "$(cat /sys/module/zswap/parameters/max_pool_percent 2>/dev/null)" fi printf 'pageSize=%s\n' "$(getconf PAGESIZE 2>/dev/null || echo 4096)" if [ -d /sys/class/net ]; then for iface_path in /sys/class/net/*; do [ -e "$iface_path" ] || continue iface=$(basename "$iface_path") [ "$iface" = lo ] && continue state=$(cat "$iface_path/operstate" 2>/dev/null || echo unknown) mac=$(cat "$iface_path/address" 2>/dev/null || true) rx=$(cat "$iface_path/statistics/rx_bytes" 2>/dev/null || echo 0) tx=$(cat "$iface_path/statistics/tx_bytes" 2>/dev/null || echo 0) if command -v ip >/dev/null 2>&1; then addresses=$(ip -o addr show dev "$iface" scope global 2>/dev/null | awk '{print $4}' | paste -sd, -); else addresses=; fi printf 'networkInterface=%s|%s|%s|%s|%s|%s\n' "$iface" "$state" "$mac" "$rx" "$tx" "$addresses" done elif command -v ifconfig >/dev/null 2>&1; then for iface in $(ifconfig -l 2>/dev/null); do [ "$iface" = lo0 ] && continue state=$(ifconfig "$iface" 2>/dev/null | awk '/status:/{print $2;exit}') mac=$(ifconfig "$iface" 2>/dev/null | awk '/ether /{print $2;exit}') addresses=$(ifconfig "$iface" 2>/dev/null | awk '/^[[:space:]]+inet /{print $2}/^[[:space:]]+inet6 /{print $2}' | paste -sd, -) counters=$(netstat -ibn -I "$iface" 2>/dev/null | awk 'NR==2{print $7":"$10;exit}') rx=$(printf '%s' "$counters" | cut -d: -f1); tx=$(printf '%s' "$counters" | cut -d: -f2) [ -n "$state" ] || state=unknown; [ -n "$rx" ] || rx=0; [ -n "$tx" ] || tx=0 printf 'networkInterface=%s|%s|%s|%s|%s|%s\n' "$iface" "$state" "$mac" "$rx" "$tx" "$addresses" done fi # Every CPU package: model, cores, usage from the two /proc/stat samples, frequency and temperature. if [ -n "$stat_second" ]; then { printf '%s\n' "$stat_first" | sed 's/^/A /' printf '%s\n' "$stat_second" | sed 's/^/B /' awk 'FNR==1{ n=FILENAME; kind=(n ~ /core_id$/) ? "C" : "P"; sub(/\/topology.*/,"",n); sub(/.*\/cpu/,"",n); print kind, n, $1 }' /sys/devices/system/cpu/cpu[0-9]*/topology/physical_package_id /sys/devices/system/cpu/cpu[0-9]*/topology/core_id 2>/dev/null for hw in /sys/class/hwmon/hwmon*; do [ "$(cat "$hw/name" 2>/dev/null)" = coretemp ] || continue for label in "$hw"/temp*_label; do case "$(cat "$label" 2>/dev/null)" in "Package id "*) printf 'H %s %s\n' "$(sed 's/[^0-9]//g' "$label")" "$(cat "${label%_label}_input" 2>/dev/null)";; esac done done for hw in /sys/class/hwmon/hwmon*; do case "$(cat "$hw/name" 2>/dev/null)" in k10temp|zenpower) printf '%s %s\n' "$(readlink -f "$hw/device" 2>/dev/null)" "$(cat "$hw/temp1_input" 2>/dev/null)";; esac done | sort | awk '{ print "H", NR-1, $2 }' } | awk ' FILENAME == "/proc/cpuinfo" { i = index($0, ":"); if (!i) next k = $0; sub(/[ \t]*:.*/, "", k); v = substr($0, i + 1); sub(/^[ \t]+/, "", v) if (k == "processor") cur = v; else if (k == "model name" || k == "Processor") model[cur] = v; else if (k == "cpu MHz") mhz[cur] = v next } $1 == "A" || $1 == "B" { id = $2; sub(/^cpu/, "", id); idle = $6 + $7; tot = 0 for (i = 3; i <= 10 && i <= NF; i++) tot += $i if ($1 == "A") { ia[id] = idle; ta[id] = tot } else { ib[id] = idle; tb[id] = tot; seen[id] = 1 } next } $1 == "P" { pkg[$2] = $3; next } $1 == "C" { core[$2] = $3; next } $1 == "H" { temp[$2] = $3; next } END { for (id in seen) { p = (id in pkg) ? pkg[id] : 0; logical[p]++ if (!((p, core[id]) in cores)) { cores[p, core[id]] = 1; physical[p]++ } dt[p] += tb[id] - ta[id]; di[p] += ib[id] - ia[id] if (model[id] != "") name[p] = model[id] if (mhz[id] != "") { ms[p] += mhz[id]; mc[p]++ } } for (p in logical) { n = name[p]; gsub(/\|/, " ", n) printf "cpuPackage=%s|%s|%d|%d|%s|%s|%s\n", p, n, logical[p], physical[p], (dt[p] > 0 ? sprintf("%.2f", 100 * (dt[p] - di[p]) / dt[p]) : ""), (mc[p] ? sprintf("%.0f", ms[p] / mc[p]) : ""), ((p in temp) ? temp[p] : "") } }' - /proc/cpuinfo 2>/dev/null fi # Every display or accelerator device on the PCI bus, whatever its driver, plus SoC GPUs. # sysfs needs no root and no extra tools; nvidia-smi, if installed, adds what only the NVIDIA driver knows. gpu_first() { for gpu_file in "$@"; do if [ -r "$gpu_file" ]; then cat "$gpu_file" 2>/dev/null; return; fi; done; } if [ -d /sys/bus/pci/devices ]; then for gpu_dev in $(awk '/^0x(03|12)/{ n=FILENAME; sub(/\/class$/,"",n); print n }' /sys/bus/pci/devices/*/class 2>/dev/null); do gpu_id=$(basename "$gpu_dev") gpu_driver=$(basename "$(readlink "$gpu_dev/driver" 2>/dev/null)" 2>/dev/null) gpu_name= if command -v lspci >/dev/null 2>&1; then gpu_name=$(lspci -vmm -s "$gpu_id" 2>/dev/null | awk -F'\t' '/^Vendor:/{v=$2}/^Device:/{d=$2}END{print v" "d}'); fi gpu_clock=$(gpu_first "$gpu_dev"/hwmon/hwmon*/freq1_input) if [ -z "$gpu_clock" ]; then gpu_mhz=$(gpu_first "$gpu_dev"/drm/card[0-9]*/gt_cur_freq_mhz "$gpu_dev"/tile0/gt0/freq0/act_freq) [ -n "$gpu_mhz" ] && gpu_clock=$((gpu_mhz * 1000000)) fi printf 'gpu=%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s\n' "$gpu_id" "$(gpu_first "$gpu_dev/vendor")" "$(gpu_first "$gpu_dev/device")" "$gpu_driver" "$gpu_name" \ "$(gpu_first "$gpu_dev/gpu_busy_percent")" "$(gpu_first "$gpu_dev/mem_busy_percent")" "$(gpu_first "$gpu_dev/mem_info_vram_total")" "$(gpu_first "$gpu_dev/mem_info_vram_used")" \ "$(gpu_first "$gpu_dev"/hwmon/hwmon*/temp1_input)" "$(gpu_first "$gpu_dev"/hwmon/hwmon*/power1_average "$gpu_dev"/hwmon/hwmon*/power1_input)" "$(gpu_first "$gpu_dev"/hwmon/hwmon*/power1_cap)" "$gpu_clock" "$(gpu_first "$gpu_dev"/hwmon/hwmon*/pwm1)" done fi for gpu_card in /sys/class/drm/card[0-9]*; do case "$gpu_card" in *-*) continue;; esac [ -e "$gpu_card/device" ] || continue case "$(readlink -f "$gpu_card/device" 2>/dev/null)" in */pci*) continue;; esac gpu_driver=$(basename "$(readlink "$gpu_card/device/driver" 2>/dev/null)" 2>/dev/null) printf 'gpu=%s|||%s|%s|||||%s|||%s|\n' "$(basename "$gpu_card")" "$gpu_driver" "$gpu_driver" "$(gpu_first "$gpu_card"/device/hwmon/hwmon*/temp1_input)" "$(gpu_first "$gpu_card/gt_cur_freq_mhz")" done if command -v nvidia-smi >/dev/null 2>&1; then if command -v timeout >/dev/null 2>&1; then gpu_limit="timeout 4"; else gpu_limit=; fi $gpu_limit nvidia-smi --query-gpu=pci.bus_id,name,utilization.gpu,utilization.memory,memory.total,memory.used,temperature.gpu,power.draw,power.limit,clocks.current.graphics,fan.speed,driver_version --format=csv,noheader,nounits 2>/dev/null | tr -d '\r' | sed 's/, */|/g; s/^/nvidiaGpu=/' fi