1b43a0112SMichal Berger#!/usr/bin/env bash 2eb53c232Spaul luse# SPDX-License-Identifier: BSD-3-Clause 3eb53c232Spaul luse# Copyright (C) 2022 Intel Corporation 4eb53c232Spaul luse# All rights reserved. 5eb53c232Spaul luse 6b43a0112SMichal Bergerset -e 7b43a0112SMichal Berger 8d5fe62b2SMichal Bergerpmdir=$(readlink -f "$(dirname "$0")") 9d5fe62b2SMichal Bergerrootdir=$(readlink -f "$pmdir/../../../") 10d5fe62b2SMichal Bergersource "$pmdir/common" 11d5fe62b2SMichal Berger 12b43a0112SMichal Bergerhex() { printf '0x%02x\n' "$@"; } 13b43a0112SMichal Berger 147ee11c56SMichal Bergercalc() { bc <<< "scale=2; $*"; } 157ee11c56SMichal Berger 16b43a0112SMichal Bergeris_root() { 17b43a0112SMichal Berger # Talking to local BMC device requires root privileges 18b43a0112SMichal Berger if ((UID)); then 19b43a0112SMichal Berger printf '%s, you need to be root to run this script\n' "$USER" >&2 20b43a0112SMichal Berger return 1 21b43a0112SMichal Berger fi 22b43a0112SMichal Berger 23b43a0112SMichal Berger} 24b43a0112SMichal Berger 25b43a0112SMichal Bergeris_ipmitool() { 26b43a0112SMichal Berger if ! type -P ipmitool; then 27b43a0112SMichal Berger printf 'ipmitool not detected, cannot run commands against the BMC\n' >&2 28b43a0112SMichal Berger return 1 29b43a0112SMichal Berger fi 30b43a0112SMichal Berger} 31b43a0112SMichal Berger 32b43a0112SMichal Bergeripmi_load() { 33b43a0112SMichal Berger # Silently attempt to load core ipmi drivers - we will pick up the device later on. 34b43a0112SMichal Berger modprobe -qa ipmi_si ipmi_devintf ipmi_msghandler || return 0 35b43a0112SMichal Berger} 36b43a0112SMichal Berger 37b43a0112SMichal Bergeripmi_supported() { 38b43a0112SMichal Berger # Verify if kernel detected and registered at least one BMC under 39b43a0112SMichal Berger # the ipmi platform. Look for KCS specifically as this the type 40b43a0112SMichal Berger # of the interface the script was tested against. 41b43a0112SMichal Berger 42b43a0112SMichal Berger local ipmi=/sys/class/ipmi/ipmi0 43b43a0112SMichal Berger 44b43a0112SMichal Berger # Keep these details global for easy access if needed. 45b43a0112SMichal Berger local -g man_id prod_id dev_id ipmi_ver platform board ipmitool 46b43a0112SMichal Berger 47b43a0112SMichal Berger ipmi_load 48b43a0112SMichal Berger 49b43a0112SMichal Berger if [[ ! -e $ipmi ]]; then 50b43a0112SMichal Berger printf 'BMC not detected. Please, make sure your platform is IPMI-compatible\n' 51b43a0112SMichal Berger return 1 52b43a0112SMichal Berger fi >&2 53b43a0112SMichal Berger 54b43a0112SMichal Berger type=$(< "$ipmi/device/type") 55b43a0112SMichal Berger 56b43a0112SMichal Berger if [[ $type != kcs ]]; then 57b43a0112SMichal Berger printf 'No supported BMC interface detected (%s) - only KCS is supported\n' "$type" 58b43a0112SMichal Berger return 1 59b43a0112SMichal Berger fi >&2 60b43a0112SMichal Berger 61b43a0112SMichal Berger man_id=$(< "$ipmi/device/bmc/manufacturer_id") 62b43a0112SMichal Berger prod_id=$(< "$ipmi/device/bmc/product_id") 63b43a0112SMichal Berger dev_id=$(hex "$(< "$ipmi/device/bmc/device_id")") 64b43a0112SMichal Berger ipmi_ver=$(< "$ipmi/device/bmc/ipmi_version") 65b43a0112SMichal Berger 66b43a0112SMichal Berger if [[ -e /sys/class/dmi/id/board_vendor ]]; then 67b43a0112SMichal Berger platform=$(< /sys/class/dmi/id/board_vendor) 68b43a0112SMichal Berger fi 69b43a0112SMichal Berger 70b43a0112SMichal Berger if [[ -e /sys/class/dmi/id/board_name ]]; then 71b43a0112SMichal Berger board=$(< /sys/class/dmi/id/board_name) 72b43a0112SMichal Berger fi 73b43a0112SMichal Berger 74b43a0112SMichal Berger # Keep output similar to ipmi_si's 75b43a0112SMichal Berger cat <<- BMC_DEV >&2 76b43a0112SMichal Berger 77b43a0112SMichal Berger BMC detected, details below: 78b43a0112SMichal Berger Manufacturer ID: $man_id 79b43a0112SMichal Berger Product ID: $prod_id 80b43a0112SMichal Berger Device ID: $dev_id 81b43a0112SMichal Berger IPMI Version: $ipmi_ver 82b43a0112SMichal Berger Platform: ${platform:-unknown} 83b43a0112SMichal Berger Board: ${board:-unknown} 84b43a0112SMichal Berger 85b43a0112SMichal Berger BMC_DEV 86b43a0112SMichal Berger 87b43a0112SMichal Berger # Verify if we have proper tools to work with 88b43a0112SMichal Berger ipmitool=$(is_ipmitool) 89b43a0112SMichal Berger} 90b43a0112SMichal Berger 91b43a0112SMichal Bergeripmiraw() { 92b43a0112SMichal Berger # For the majority of commands we use raw payload to not depend on specific ipmitool version 93b43a0112SMichal Berger # and the way how it interprets/parses the returned data. This also allows us to inspect the 94b43a0112SMichal Berger # integrity of data more closely to make sure we don't report nonsensical values to the user. 95b43a0112SMichal Berger 96b43a0112SMichal Berger local rsp 97b43a0112SMichal Berger 98b43a0112SMichal Berger rsp=($("$ipmitool" raw "$@" 2> /dev/null)) 99b43a0112SMichal Berger # Slap hex prefix to work with proper base 100b43a0112SMichal Berger rsp=("${rsp[@]/#/0x}") 101b43a0112SMichal Berger 102b43a0112SMichal Berger hex "${rsp[@]}" 103b43a0112SMichal Berger} 104b43a0112SMichal Berger 105b43a0112SMichal Bergerdcmiraw() { 106b43a0112SMichal Berger local cmd=$1 data=("${@:2}") 107b43a0112SMichal Berger 108b43a0112SMichal Berger ipmiraw 0x2c "$cmd" 0xdc "${data[@]}" 109b43a0112SMichal Berger} 110b43a0112SMichal Berger 111b43a0112SMichal Bergerprint_dcmi_available_time_periods() { 112b43a0112SMichal Berger local time_periods=${enhanced_power_attr[4]} 113b43a0112SMichal Berger local -g available_time_periods=() 114b43a0112SMichal Berger local -g available_time_periods_in_seconds=() 115b43a0112SMichal Berger 116b43a0112SMichal Berger available_time_periods[0]="NOW" 117b43a0112SMichal Berger 118b43a0112SMichal Berger if ((time_periods > 0)); then 119b43a0112SMichal Berger local time_idx=5 120b43a0112SMichal Berger local offset=$time_idx 121b43a0112SMichal Berger local units unit time time_s units_mask=0xc0 to_sec 122b43a0112SMichal Berger 123b43a0112SMichal Berger units[0x0]=seconds 124b43a0112SMichal Berger units[0x1]=minutes 125b43a0112SMichal Berger units[0x2]=hours 126b43a0112SMichal Berger units[0x3]=days 127b43a0112SMichal Berger 128b43a0112SMichal Berger to_sec[0x0]=1 129b43a0112SMichal Berger to_sec[0x1]=60 130b43a0112SMichal Berger to_sec[0x2]=3600 131b43a0112SMichal Berger to_sec[0x3]=86400 132b43a0112SMichal Berger 133b43a0112SMichal Berger while ((offset < time_idx + time_periods)); do 134b43a0112SMichal Berger time=$((enhanced_power_attr[offset] & ~units_mask)) 135b43a0112SMichal Berger unit=${units[enhanced_power_attr[offset] >> 6]:-unknown} 136b43a0112SMichal Berger time_s=$((time * to_sec[enhanced_power_attr[offset] >> 6])) 137b43a0112SMichal Berger if ((time != 0)); then 138b43a0112SMichal Berger available_time_periods[offset]="$time $unit" 139b43a0112SMichal Berger available_time_periods_in_seconds[time_s]=${enhanced_power_attr[offset]} 140b43a0112SMichal Berger fi 141b43a0112SMichal Berger ((++offset)) 142b43a0112SMichal Berger done 143b43a0112SMichal Berger fi 144b43a0112SMichal Berger cat <<- TIME_PERIODS >&2 145b43a0112SMichal Berger 146b43a0112SMichal Berger Available averaging time periods to request: 147b43a0112SMichal Berger $(printf ' - %s\n' "${available_time_periods[@]}") 148b43a0112SMichal Berger 149b43a0112SMichal Berger TIME_PERIODS 150b43a0112SMichal Berger} 151b43a0112SMichal Berger 152b43a0112SMichal Bergerdcmi_power_support() { 153b43a0112SMichal Berger # Verify if the BMC conforms to the DCMI spec 154b43a0112SMichal Berger local rsp 155b43a0112SMichal Berger 156b43a0112SMichal Berger # Table 6-2, Get DCMI Capabilities Command Format 157b43a0112SMichal Berger if ! rsp=($(dcmiraw 0x1 0x1)); then 158b43a0112SMichal Berger printf 'Cannot determine if BMC supports DCMI Power Management capability\n' >&2 159b43a0112SMichal Berger return 1 160b43a0112SMichal Berger fi 161b43a0112SMichal Berger 162b43a0112SMichal Berger # Table 6-3, DCMI Capabilities Parameters: 163b43a0112SMichal Berger # - Supported DCMI Capabilities: 164b43a0112SMichal Berger # - Byte 2 Platform capabilities: [0] Power management 165b43a0112SMichal Berger if ((!(rsp[5] & (1 << 0)))); then 166b43a0112SMichal Berger printf 'BMC does not provide DCMI Power Mangament capability\n' >&2 167b43a0112SMichal Berger return 1 168b43a0112SMichal Berger fi 169b43a0112SMichal Berger 170b43a0112SMichal Berger # Check if BMC provides Enhanced System Power Statistics attributes - this allows to issue 171b43a0112SMichal Berger # requests for power readings at averaging time period, .e.g. from last 5 seconds, 30 minutes, 172b43a0112SMichal Berger # 1 hour and so on. With this we can provide more detailed view on power usage within a 173b43a0112SMichal Berger # specific period of time. Without it, we need to depend only on current reading that should 174b43a0112SMichal Berger # be always available (the "NOW" reading). 175b43a0112SMichal Berger 176b43a0112SMichal Berger local -g enhanced_power_attr=() 177b43a0112SMichal Berger 178b43a0112SMichal Berger # Table 6-3, DCMI Capabilities Parameters: 179b43a0112SMichal Berger # - Enhanced System Power Statistics attributes 180b43a0112SMichal Berger if enhanced_power_attr=($(dcmiraw 0x1 0x5)); then 181b43a0112SMichal Berger print_dcmi_available_time_periods 182b43a0112SMichal Berger fi 183b43a0112SMichal Berger 184b43a0112SMichal Berger printf 'Using DCMI Power Management\n' >&2 185b43a0112SMichal Berger} 186b43a0112SMichal Berger 187b43a0112SMichal Bergersdr_power_support() { 188b43a0112SMichal Berger # This is a fallback which only some platforms may provide (confirmed PowerEdge and CYP). 189b43a0112SMichal Berger # We are looking for a full, threshold sensor which reports overall power usage in Watts. 190b43a0112SMichal Berger # Different BMCs may have SDRs which describe such sensor(s) differently so this is not 191b43a0112SMichal Berger # 100% reliable. To make sure we pick up a proper sensor we also narrow it down to a 192b43a0112SMichal Berger # specific entity (System Board or Power Supply). Readings from the sensor should be 193b43a0112SMichal Berger # considered as "NOW" readings (without access to min, max readings). 194b43a0112SMichal Berger 195b43a0112SMichal Berger local -g power_sensors=() 196b43a0112SMichal Berger local sensor entity unit status 197b43a0112SMichal Berger 198b43a0112SMichal Berger # Cache SDR to speed up sensor readings 199b43a0112SMichal Berger if [[ ! -f $sdr_cache ]]; then 200b43a0112SMichal Berger printf 'Saving SDR cache at %s\n' "$sdr_cache" >&2 201b43a0112SMichal Berger "$ipmitool" sdr dump "$sdr_cache" > /dev/null 202b43a0112SMichal Berger fi 203b43a0112SMichal Berger 204b43a0112SMichal Berger if ((${#extra_power_sensors[@]} > 0)); then 205b43a0112SMichal Berger power_sensors+=("${extra_power_sensors[@]}") 206b43a0112SMichal Berger fi 207b43a0112SMichal Berger 208b43a0112SMichal Berger while IFS="," read -r sensor _ unit status _ entity _; do 209b43a0112SMichal Berger [[ $unit == Watts && $status == ok ]] || continue 210b43a0112SMichal Berger [[ $entity == "System Board" || $entity == "Power Supply" ]] || continue 211b43a0112SMichal Berger power_sensors+=("$sensor") 212b43a0112SMichal Berger done < <("$ipmitool" -S "$sdr_cache" -vc sdr list full 2>&1) 213b43a0112SMichal Berger 214b43a0112SMichal Berger if ((${#power_sensors[@]} > 0)); then 215b43a0112SMichal Berger printf 'Using SDR (Power sensors: %s)\n' "${power_sensors[*]}" 216b43a0112SMichal Berger else 217b43a0112SMichal Berger printf 'Cannot locate power sensors\n' 218b43a0112SMichal Berger return 1 219b43a0112SMichal Berger fi >&2 220b43a0112SMichal Berger} 221b43a0112SMichal Berger 222b43a0112SMichal Bergerpower_support() { 2236fb5eae6SMichal Berger local -g support cpu_support=0 2246fb5eae6SMichal Berger 2256fb5eae6SMichal Berger if ((include_cpu == 1)) && rapl_supported; then 2266fb5eae6SMichal Berger cpu_support=1 2276fb5eae6SMichal Berger fi 228b43a0112SMichal Berger 229b43a0112SMichal Berger if [[ $interface == dcmi || $interface == sdr ]]; then 230b43a0112SMichal Berger # override 231b43a0112SMichal Berger "${interface}_power_support" 232b43a0112SMichal Berger support=$interface 233b43a0112SMichal Berger elif dcmi_power_support; then 234b43a0112SMichal Berger support=dcmi 235b43a0112SMichal Berger elif sdr_power_support; then 236b43a0112SMichal Berger support=sdr 237b43a0112SMichal Berger else 2386fb5eae6SMichal Berger printf 'BMC does not provide Power Management support, cannot gather system-wide power measurements\n' >&2 2396fb5eae6SMichal Berger if ((cpu_support)); then 2406fb5eae6SMichal Berger printf 'Only CPU measurements will be provided\n' >&2 2416fb5eae6SMichal Berger return 0 2426fb5eae6SMichal Berger fi 243b43a0112SMichal Berger return 1 244b43a0112SMichal Berger fi 245b43a0112SMichal Berger} 246b43a0112SMichal Berger 247b43a0112SMichal Bergerget_dcmi_now_reading() { 248b43a0112SMichal Berger local rsp reading=0 max min avg ts timeframe mode=01h 249b43a0112SMichal Berger local get_cmd get_avg=0 print 250b43a0112SMichal Berger 251b43a0112SMichal Berger # Table 6-16, Get Power Reading Command: 252b43a0112SMichal Berger get_cmd=(0x2 0x1 0x0 0x0) 253b43a0112SMichal Berger 254f83a2d3aSMichal Berger if [[ $interval =~ ^[0-9]+$ && -n ${available_time_periods_in_seconds[interval]} ]]; then 255b43a0112SMichal Berger get_cmd=(0x2 0x2 "${available_time_periods_in_seconds[interval]}" 0x0) 256b43a0112SMichal Berger get_avg=1 257b43a0112SMichal Berger mode=02h 258b43a0112SMichal Berger fi 259b43a0112SMichal Berger 260b43a0112SMichal Berger # We use System Power Statistics mode to get the "NOW" reading by default. In case 261b43a0112SMichal Berger # interval matches one supported by Enhanced System Power Statistics we use that 262b43a0112SMichal Berger # mode to obtain extra min, max, avg statistics. 263b43a0112SMichal Berger 264b43a0112SMichal Berger if ! rsp=($(dcmiraw "${get_cmd[@]}")); then 265b43a0112SMichal Berger printf 'DCMI reading: error\n' 266b43a0112SMichal Berger else 267b43a0112SMichal Berger # Note that the BMC timestamp depends on the hwclock setup which we then attempt 268b43a0112SMichal Berger # to represent in UTC. 269b43a0112SMichal Berger ts=$((rsp[12] << 24 | rsp[11] << 16 | rsp[10] << 8 | rsp[9])) 270b43a0112SMichal Berger # This is interpreted differently by different BMCs so for now we make a note of 271b43a0112SMichal Berger # it but don't present it to the user. 272b43a0112SMichal Berger timeframe=$((rsp[16] << 24 | rsp[15] << 16 | rsp[14] << 8 | rsp[13])) 273b43a0112SMichal Berger reading=$((rsp[2] << 8 | rsp[1])) 274b43a0112SMichal Berger if ((get_avg == 1)); then 275b43a0112SMichal Berger min=$((rsp[4] << 8 | rsp[3])) 276b43a0112SMichal Berger max=$((rsp[6] << 8 | rsp[5])) 277b43a0112SMichal Berger avg=$((rsp[8] << 8 | rsp[7])) 278b43a0112SMichal Berger _DCMI_min+=("$min") 279b43a0112SMichal Berger _DCMI_max+=("$max") 280b43a0112SMichal Berger _DCMI_avg+=("$avg") 281b43a0112SMichal Berger power_readings["DCMI_MIN"]="_DCMI_min[@]" 282b43a0112SMichal Berger power_readings["DCMI_MAX"]="_DCMI_max[@]" 283b43a0112SMichal Berger power_readings["DCMI_AVG"]="_DCMI_avg[@]" 284b43a0112SMichal Berger fi 285b43a0112SMichal Berger _DCMI+=("$reading") 286b43a0112SMichal Berger power_readings["DCMI"]="_DCMI[@]" 287b43a0112SMichal Berger 288b43a0112SMichal Berger for print in min max avg reading; do 289b43a0112SMichal Berger [[ -n ${!print} ]] || continue 290*fdea5c6dSMichal Berger printf '(%s) DCMI %s (mode: %s): %u Watts (interval: %ss, test: %s)\n' \ 291b43a0112SMichal Berger "$(utc "$ts")" \ 292b43a0112SMichal Berger "$print" \ 293b43a0112SMichal Berger "$mode" \ 294b43a0112SMichal Berger "${!print}" \ 295*fdea5c6dSMichal Berger "$interval" \ 296*fdea5c6dSMichal Berger "$TEST_TAG" >&2 297b43a0112SMichal Berger done 298b43a0112SMichal Berger fi >&2 299b43a0112SMichal Berger} 300b43a0112SMichal Berger 301b43a0112SMichal Bergerget_sdr_now_reading() { 302b43a0112SMichal Berger local sensor reading=0 ts unit 303b43a0112SMichal Berger 304b43a0112SMichal Berger if ((${#power_sensors[@]} == 0)); then 305b43a0112SMichal Berger printf 'No power sensors were provided\n' >&2 306b43a0112SMichal Berger return 1 307b43a0112SMichal Berger fi 308b43a0112SMichal Berger 309b43a0112SMichal Berger for sensor in "${!power_sensors[@]}"; do 310b43a0112SMichal Berger ts=$(utc) 311b43a0112SMichal Berger if ! IFS="," read -r _ reading unit _; then 312b43a0112SMichal Berger reading=error 313b43a0112SMichal Berger else 314b43a0112SMichal Berger eval "_sensor${sensor}_readings+=($reading)" 315b43a0112SMichal Berger power_readings["${power_sensors[sensor]}"]="_sensor${sensor}_readings[@]" 316b43a0112SMichal Berger reading+=" $unit" 317b43a0112SMichal Berger fi < <("$ipmitool" -c -S "$sdr_cache" sdr get "${power_sensors[sensor]}") 2> /dev/null 318*fdea5c6dSMichal Berger printf '(%s) Sensor %s reading: %s (interval: %ss, test: %s)\n' \ 319b43a0112SMichal Berger "$ts" \ 320b43a0112SMichal Berger "${power_sensors[sensor]}" \ 321b43a0112SMichal Berger "$reading" \ 322*fdea5c6dSMichal Berger "$interval" \ 323*fdea5c6dSMichal Berger "$TEST_TAG" >&2 324b43a0112SMichal Berger done 325b43a0112SMichal Berger} 326b43a0112SMichal Berger 3276fb5eae6SMichal Bergerrapl_supported() { 3286fb5eae6SMichal Berger [[ -e /sys/class/powercap/intel-rapl ]] 3296fb5eae6SMichal Berger} 3306fb5eae6SMichal Berger 3316fb5eae6SMichal Bergerget_cpu_socket_reading() { 3326fb5eae6SMichal Berger local rapl=/sys/class/powercap 3336fb5eae6SMichal Berger local socket socket_idx _socket_idx socket_name 3346fb5eae6SMichal Berger local ts reading 3356fb5eae6SMichal Berger 3366fb5eae6SMichal Berger # power_uw is usually not available so we need to relay on energy_uj. It's also rarely 3376fb5eae6SMichal Berger # rw so we can't zero it out, hence we need to keep track of the initial counter. For 3386fb5eae6SMichal Berger # details see kernel documentation (powercap.rst). 3396fb5eae6SMichal Berger ts=$(utc) 3406fb5eae6SMichal Berger for socket in /sys/class/powercap/intel-rapl:*; do 3416fb5eae6SMichal Berger [[ -e $socket ]] || continue 3426fb5eae6SMichal Berger 3436fb5eae6SMichal Berger socket_idx=${socket#*:} socket_name=$(< "$socket/name") 3446fb5eae6SMichal Berger # Adjust for different domains, see linux/intel_rapl.h 3456fb5eae6SMichal Berger case "$socket_name" in 3466fb5eae6SMichal Berger dram | core | uncore) _socket_idx=${socket_idx//:/_} socket_idx=${socket_idx%:*} ;; 3476fb5eae6SMichal Berger package-*) _socket_idx=$socket_idx socket_name=socket ;; 3486fb5eae6SMichal Berger psys*) _socket_idx=$socket_idx socket_name=platform ;; 3496fb5eae6SMichal Berger esac 3506fb5eae6SMichal Berger 3516fb5eae6SMichal Berger local -n socket_uj=socket_${_socket_idx}_uj 3526fb5eae6SMichal Berger socket_uj+=("$(< "$socket/energy_uj")") 3536fb5eae6SMichal Berger # We need at least two readings for comparison 3546fb5eae6SMichal Berger ((${#socket_uj[@]} > 1)) || continue 3556fb5eae6SMichal Berger 3566fb5eae6SMichal Berger # Convert to Watts - use bc since $interval can be an actual float 3577ee11c56SMichal Berger reading=$(calc "(${socket_uj[-1]} - ${socket_uj[-2]}) / 1000000 / $interval") 358df2283c5SMichal Berger if [[ $reading == "-"* ]]; then 35966ed26e0SMichal Berger # Somehow this may happen, probably when the counter wraps over. Consider 36066ed26e0SMichal Berger # this as a faulty reading and don't include it since it may impact overall 36166ed26e0SMichal Berger # avg. 362*fdea5c6dSMichal Berger printf '(%s) CPU %s %s reading: error(%s) (interval: %ss, test: %s)\n' \ 36366ed26e0SMichal Berger "$ts" \ 36466ed26e0SMichal Berger "$socket_name" \ 36566ed26e0SMichal Berger "$socket_idx" \ 36666ed26e0SMichal Berger "$reading" \ 367*fdea5c6dSMichal Berger "$interval" \ 368*fdea5c6dSMichal Berger "$TEST_TAG" >&2 36966ed26e0SMichal Berger return 0 37066ed26e0SMichal Berger fi 3716fb5eae6SMichal Berger eval "_socket${_socket_idx}_readings+=($reading)" 3726fb5eae6SMichal Berger power_readings["$socket_name-$socket_idx"]="_socket${_socket_idx}_readings[@]" 3736fb5eae6SMichal Berger 374*fdea5c6dSMichal Berger printf '(%s) CPU %s %s reading: %s Watts (interval: %ss, test: %s)\n' \ 3756fb5eae6SMichal Berger "$ts" \ 3766fb5eae6SMichal Berger "$socket_name" \ 3776fb5eae6SMichal Berger "$socket_idx" \ 3786fb5eae6SMichal Berger "$reading" \ 379*fdea5c6dSMichal Berger "$interval" \ 380*fdea5c6dSMichal Berger "$TEST_TAG" >&2 3816fb5eae6SMichal Berger done 3826fb5eae6SMichal Berger} 3836fb5eae6SMichal Berger 384b43a0112SMichal Bergerget_now_reading() { 385b43a0112SMichal Berger case "$support" in 386b43a0112SMichal Berger dcmi) get_dcmi_now_reading ;; 387b43a0112SMichal Berger sdr) get_sdr_now_reading ;; 388b43a0112SMichal Berger *) ;; 389b43a0112SMichal Berger esac 390b43a0112SMichal Berger} 391b43a0112SMichal Berger 392b43a0112SMichal Bergerdump_readings() { 393b43a0112SMichal Berger local sensor reading readings avg total 394b43a0112SMichal Berger 395b43a0112SMichal Berger ((${#power_readings[@]} > 0)) || return 1 396b43a0112SMichal Berger printf 'Dumping average sensors reading from %s\n' "${!power_readings[*]}" >&2 397b43a0112SMichal Berger 398b43a0112SMichal Berger for sensor in "${!power_readings[@]}"; do 399b43a0112SMichal Berger readings=("${!power_readings["$sensor"]}") 400b43a0112SMichal Berger if ((${#readings[@]} == 0)); then 401b43a0112SMichal Berger printf 'No readings available for %s sensor\n' "$sensor" >&2 402b43a0112SMichal Berger continue 403b43a0112SMichal Berger fi 404b43a0112SMichal Berger total=0 405b43a0112SMichal Berger for reading in "${readings[@]}"; do 4067ee11c56SMichal Berger total=$(calc "$total + $reading") 407b43a0112SMichal Berger done 4087ee11c56SMichal Berger avg=$(calc "$total / ${#readings[@]}") 409daeadb17SMichal Berger 410daeadb17SMichal Berger readings+=("Total: ${#readings[@]}") 411a86fa6d1SKarol Latecki sensor="${sensor//[[:space:]]/_}" 412d5fe62b2SMichal Berger printf '%s\n' "$avg" > "$PM_OUTPUTDIR/${prefix:+${prefix}_}avg_${sensor}.bmc.pm.txt" 413d5fe62b2SMichal Berger printf '%s\n' "${readings[@]}" > "$PM_OUTPUTDIR/${prefix:+${prefix}_}all_${sensor}.bmc.pm.txt" 414d5fe62b2SMichal Berger printf 'Dumped avg to %s\n' "$PM_OUTPUTDIR/${prefix:+${prefix}_}avg_${sensor}.bmc.pm.txt" >&2 415d5fe62b2SMichal Berger printf 'Dumped all to %s\n' "$PM_OUTPUTDIR/${prefix:+${prefix}_}all_${sensor}.bmc.pm.txt" >&2 416b43a0112SMichal Berger done 417b43a0112SMichal Berger} 418b43a0112SMichal Berger 419b43a0112SMichal Bergerutc() { 420b43a0112SMichal Berger date --utc ${1:+-"d@$1"} 421b43a0112SMichal Berger} 422b43a0112SMichal Berger 423b43a0112SMichal Bergercleanup() { 424d5fe62b2SMichal Berger rm_pm_pid 425b43a0112SMichal Berger [[ -f $sdr_cache && $remove_sdr_cache == yes ]] && rm "$sdr_cache" 426b43a0112SMichal Berger dump_readings 427b43a0112SMichal Berger} 428b43a0112SMichal Berger 429b43a0112SMichal Bergercollect_readings() { 430b43a0112SMichal Berger local _count=$count 4316fb5eae6SMichal Berger if ((_count == 1 && cpu_support)); then 4326fb5eae6SMichal Berger # We need at least two readings to get a meaningful data 4336fb5eae6SMichal Berger ((_count += 1)) 4346fb5eae6SMichal Berger fi 435b43a0112SMichal Berger while ((count <= 0 ? 1 : _count--)); do 436b43a0112SMichal Berger get_now_reading 4376fb5eae6SMichal Berger ((cpu_support)) && get_cpu_socket_reading 438b43a0112SMichal Berger sleep "${interval}s" 439b43a0112SMichal Berger done 440b43a0112SMichal Berger} 441b43a0112SMichal Berger 442b43a0112SMichal Bergerhelp() { 443b43a0112SMichal Berger cat <<- HELP 444b43a0112SMichal Berger 445ad80b46eSMichal Berger Usage: $0 [-h] [-d dir] [-i sdr|dcmi] [-s SENSOR_NAME] [-t interval] [-l] [-p prefix] [-c count] [-r] 446b43a0112SMichal Berger 447b43a0112SMichal Berger -h - Print this message. 448b43a0112SMichal Berger -d - Directory where the results should be saved. Default is /tmp. 449b43a0112SMichal Berger -i - Type of interface to use for requesting power usage. "sdr" or "dcmi". 450b43a0112SMichal Berger If not set, available interface is used ("dcmi" has priority). 451b43a0112SMichal Berger -t - How long to wait before each get power command in seconds. In case 452b43a0112SMichal Berger this value matches one of supported averaging time periods special 453b43a0112SMichal Berger variant of the command will be used to obtain the reading - this 454b43a0112SMichal Berger variant is used only with the "dcmi" interface. Default is 1s. 455b43a0112SMichal Berger -s - In case "sdr" interface is in use, try to read data from SENSOR_NAME. 456b43a0112SMichal Berger -x - In case "sdr" interface is in use, don't remove SDR cache. This can 457b43a0112SMichal Berger speed up subsequent runs of the script. 458b43a0112SMichal Berger -l - Save output of the script to a log file (dir/${0##*/}.bmc.pm.log). 459b43a0112SMichal Berger -p - Add prefix to saved files. 460b43a0112SMichal Berger -c - Read power usage count times. 0 is the default and it means to run 461b43a0112SMichal Berger indefinitely. 4626fb5eae6SMichal Berger -r - Include readings from CPU sockets (RAPL-dependent) 463b43a0112SMichal Berger 464b43a0112SMichal Berger When started, ${0##*/} will enter loop to continuously read power usage from either 465b43a0112SMichal Berger DCMI interface or dedicated Watts sensors every interval. Each reading will be 466b43a0112SMichal Berger logged to stderr. Upon termination, average power usage will be dumped to /tmp or 467b43a0112SMichal Berger directory set by -d. 468b43a0112SMichal Berger 469b43a0112SMichal Berger HELP 470b43a0112SMichal Berger} 471b43a0112SMichal Berger 472b43a0112SMichal Bergeris_root 473b43a0112SMichal Berger 474b43a0112SMichal Bergerinterval=1 475b43a0112SMichal Bergerremove_sdr_cache=yes 476b43a0112SMichal Bergerlog_to_file=no 477b43a0112SMichal Bergerprefix="" 478b43a0112SMichal Bergercount=0 4796fb5eae6SMichal Bergerinclude_cpu=0 480b43a0112SMichal Berger 481b43a0112SMichal Bergerdeclare -A power_readings=() 482b43a0112SMichal Bergerdeclare -a extra_power_sensors=() 483b43a0112SMichal Berger 4846fb5eae6SMichal Bergerwhile getopts :hi:s:d:t:xlp:c:r arg; do 485b43a0112SMichal Berger case "$arg" in 486b43a0112SMichal Berger h) 487b43a0112SMichal Berger help 488b43a0112SMichal Berger exit 0 489b43a0112SMichal Berger ;; 490d5fe62b2SMichal Berger d) PM_OUTPUTDIR=$OPTARG ;; 491b43a0112SMichal Berger s) extra_power_sensors+=("$OPTARG") ;; 492b43a0112SMichal Berger i) interface=${OPTARG,,} ;; 493b43a0112SMichal Berger t) interval=$OPTARG ;; 494b43a0112SMichal Berger x) remove_sdr_cache=no ;; 495b43a0112SMichal Berger l) log_to_file=yes ;; 496b43a0112SMichal Berger p) prefix=$OPTARG ;; 497b43a0112SMichal Berger c) count=$OPTARG ;; 4986fb5eae6SMichal Berger r) include_cpu=1 ;; 499b43a0112SMichal Berger *) ;; 500b43a0112SMichal Berger esac 501b43a0112SMichal Bergerdone 502b43a0112SMichal Berger 503d5fe62b2SMichal Bergerdeclare -r sdr_cache=$PM_OUTPUTDIR/sdr.cache 504b43a0112SMichal Bergerdeclare -r log_file=${prefix:+${prefix}_}${0##*/}.bmc.pm.log 505b43a0112SMichal Berger 506d5fe62b2SMichal Bergermkdir -p "$PM_OUTPUTDIR" 507b43a0112SMichal Bergerif [[ $log_to_file == yes ]]; then 508d5fe62b2SMichal Berger printf 'Redirecting to %s\n' "$PM_OUTPUTDIR/$log_file" >&2 509d5fe62b2SMichal Berger exec > "$PM_OUTPUTDIR/$log_file" 2>&1 510b43a0112SMichal Bergerfi 511b43a0112SMichal Berger 512d5fe62b2SMichal Bergersave_pm_pid 513b43a0112SMichal Bergertrap 'cleanup' EXIT 514d5fe62b2SMichal Bergertrap 'retag' USR1 515b43a0112SMichal Berger 516b43a0112SMichal Bergeripmi_supported 517b43a0112SMichal Bergerpower_support 518b43a0112SMichal Berger 519b43a0112SMichal Bergercollect_readings 520