Initial commit
This commit is contained in:
52
lib/common.sh
Normal file
52
lib/common.sh
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
# Common utilities for strix-halo-optimizations scripts
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Auto-detect project root
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR" && while [[ ! -f Makefile ]] && [[ "$PWD" != "/" ]]; do cd ..; done; pwd)"
|
||||
if [[ "$PROJECT_ROOT" == "/" ]]; then
|
||||
# Fallback: assume lib/ is one level below project root
|
||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
fi
|
||||
|
||||
# Colors (disabled if not a terminal)
|
||||
if [[ -t 1 ]]; then
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'
|
||||
DIM='\033[2m'; RESET='\033[0m'
|
||||
else
|
||||
RED=''; GREEN=''; YELLOW=''; BLUE=''; CYAN=''; BOLD=''; DIM=''; RESET=''
|
||||
fi
|
||||
|
||||
log_info() { printf "${BLUE}[INFO]${RESET} %s\n" "$*"; }
|
||||
log_success() { printf "${GREEN}[OK]${RESET} %s\n" "$*"; }
|
||||
log_warn() { printf "${YELLOW}[WARN]${RESET} %s\n" "$*"; }
|
||||
log_error() { printf "${RED}[ERR]${RESET} %s\n" "$*" >&2; }
|
||||
log_header() { printf "\n${BOLD}=== %s ===${RESET}\n" "$*"; }
|
||||
|
||||
is_cmd() { command -v "$1" &>/dev/null; }
|
||||
|
||||
require_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "This script requires root privileges. Run with sudo."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
confirm() {
|
||||
local prompt="${1:-Continue?}"
|
||||
printf "${YELLOW}%s [y/N] ${RESET}" "$prompt"
|
||||
read -r reply
|
||||
[[ "$reply" =~ ^[Yy]$ ]]
|
||||
}
|
||||
|
||||
data_dir() {
|
||||
local subdir="${1:-.}"
|
||||
local dir="$PROJECT_ROOT/data/$subdir"
|
||||
mkdir -p "$dir"
|
||||
echo "$dir"
|
||||
}
|
||||
|
||||
timestamp() { date '+%Y%m%d-%H%M%S'; }
|
||||
197
lib/detect.sh
Normal file
197
lib/detect.sh
Normal file
@@ -0,0 +1,197 @@
|
||||
#!/usr/bin/env bash
|
||||
# Hardware and configuration detection for Strix Halo
|
||||
|
||||
# Find the amdgpu DRM card path
|
||||
find_gpu_card() {
|
||||
local card
|
||||
for card in /sys/class/drm/card*/device/vendor; do
|
||||
if [[ -f "$card" ]] && [[ "$(cat "$card")" == "0x1002" ]]; then
|
||||
echo "$(dirname "$card")"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
# Fallback: try any card with mem_info_vram_total (i.e., an amdgpu device)
|
||||
for card in /sys/class/drm/card*/device/mem_info_vram_total; do
|
||||
if [[ -f "$card" ]]; then
|
||||
echo "$(dirname "$card")"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
echo "/sys/class/drm/card1/device" # last resort
|
||||
}
|
||||
|
||||
GPU_SYSFS="$(find_gpu_card)"
|
||||
|
||||
# --- CPU ---
|
||||
detect_cpu_model() { grep -m1 'model name' /proc/cpuinfo | cut -d: -f2 | xargs; }
|
||||
detect_cpu_cores() { grep -c '^processor' /proc/cpuinfo; }
|
||||
detect_cpu_physical() { grep 'cpu cores' /proc/cpuinfo | head -1 | cut -d: -f2 | xargs; }
|
||||
|
||||
# --- GPU ---
|
||||
detect_gpu_name() {
|
||||
lspci | grep -i 'Display\|VGA' | grep -i 'AMD' | head -1 | sed 's/.*: //'
|
||||
}
|
||||
|
||||
detect_gpu_device_id() {
|
||||
cat "$GPU_SYSFS/device" 2>/dev/null | sed 's/^0x//'
|
||||
}
|
||||
|
||||
# --- Memory (bytes) ---
|
||||
detect_vram_total() { cat "$GPU_SYSFS/mem_info_vram_total" 2>/dev/null || echo 0; }
|
||||
detect_vram_used() { cat "$GPU_SYSFS/mem_info_vram_used" 2>/dev/null || echo 0; }
|
||||
detect_gtt_total() { cat "$GPU_SYSFS/mem_info_gtt_total" 2>/dev/null || echo 0; }
|
||||
detect_gtt_used() { cat "$GPU_SYSFS/mem_info_gtt_used" 2>/dev/null || echo 0; }
|
||||
|
||||
detect_system_ram_kb() {
|
||||
local kb
|
||||
kb="$(grep MemTotal /proc/meminfo 2>/dev/null | awk '{print $2}')"
|
||||
echo "${kb:-0}"
|
||||
}
|
||||
detect_system_ram_bytes() { echo $(( $(detect_system_ram_kb) * 1024 )); }
|
||||
|
||||
# --- Kernel ---
|
||||
detect_kernel_version() { uname -r; }
|
||||
|
||||
detect_kernel_param() {
|
||||
# Returns the value of a kernel param, or empty if not present
|
||||
local param="$1"
|
||||
local cmdline
|
||||
cmdline="$(cat /proc/cmdline)"
|
||||
# Escape dots for regex and anchor with word boundary (space or start-of-string)
|
||||
local pattern="${param//./\\.}"
|
||||
if [[ "$cmdline" =~ (^|[[:space:]])${pattern}=([^ ]+) ]]; then
|
||||
echo "${BASH_REMATCH[2]}"
|
||||
elif [[ "$cmdline" =~ (^|[[:space:]])${pattern}([[:space:]]|$) ]]; then
|
||||
echo "present"
|
||||
fi
|
||||
}
|
||||
|
||||
detect_has_iommu_pt() {
|
||||
local val
|
||||
val="$(detect_kernel_param 'iommu')"
|
||||
[[ "$val" == "pt" ]]
|
||||
}
|
||||
|
||||
detect_gttsize_param() { detect_kernel_param 'amdgpu.gttsize'; }
|
||||
detect_pages_limit_param() { detect_kernel_param 'ttm.pages_limit'; }
|
||||
|
||||
# --- Tuned ---
|
||||
detect_tuned_profile() {
|
||||
if is_cmd tuned-adm; then
|
||||
tuned-adm active 2>/dev/null | sed 's/Current active profile: //'
|
||||
else
|
||||
echo "tuned not installed"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Firmware ---
|
||||
detect_firmware_version() {
|
||||
rpm -q linux-firmware 2>/dev/null | sed 's/linux-firmware-//' | sed 's/\.fc.*//' || echo "unknown"
|
||||
}
|
||||
|
||||
detect_firmware_bad() {
|
||||
# Returns 0 (true) if firmware is the known-bad version
|
||||
local fw
|
||||
fw="$(detect_firmware_version)"
|
||||
[[ "$fw" == *"20251125"* ]]
|
||||
}
|
||||
|
||||
# --- ROCm ---
|
||||
detect_rocm_version() {
|
||||
if [[ -f /opt/rocm/.info/version ]]; then
|
||||
cat /opt/rocm/.info/version
|
||||
else
|
||||
rpm -qa 2>/dev/null | grep '^rocm-core-' | head -1 | sed 's/rocm-core-//' | sed 's/-.*//' || echo "not installed"
|
||||
fi
|
||||
}
|
||||
|
||||
detect_rocm_packages() {
|
||||
rpm -qa 2>/dev/null | grep -i rocm | sort
|
||||
}
|
||||
|
||||
# --- Vulkan ---
|
||||
detect_vulkan_driver() {
|
||||
if is_cmd vulkaninfo; then
|
||||
vulkaninfo --summary 2>/dev/null | grep 'driverName' | head -1 | awk '{print $NF}'
|
||||
else
|
||||
echo "vulkaninfo not available"
|
||||
fi
|
||||
}
|
||||
|
||||
detect_vulkan_version() {
|
||||
if is_cmd vulkaninfo; then
|
||||
vulkaninfo --summary 2>/dev/null | grep 'apiVersion' | head -1 | awk '{print $NF}'
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Toolbox containers ---
|
||||
detect_toolboxes() {
|
||||
if is_cmd toolbox; then
|
||||
toolbox list --containers 2>/dev/null | tail -n +2
|
||||
fi
|
||||
}
|
||||
|
||||
detect_toolbox_names() {
|
||||
detect_toolboxes | awk '{print $2}' 2>/dev/null
|
||||
}
|
||||
|
||||
# --- LLM stacks ---
|
||||
detect_stack_ollama() { is_cmd ollama && echo "installed" || echo "missing"; }
|
||||
detect_stack_lmstudio() { is_cmd lms && echo "installed" || echo "missing"; }
|
||||
detect_stack_llamacpp() { (is_cmd llama-cli || is_cmd llama-bench) && echo "installed" || echo "missing"; }
|
||||
detect_stack_opencode() { is_cmd opencode && echo "installed" || echo "missing"; }
|
||||
|
||||
# --- Sensors ---
|
||||
detect_gpu_temp() {
|
||||
# Returns temperature in millidegrees C
|
||||
local hwmon
|
||||
for hwmon in "$GPU_SYSFS"/hwmon/hwmon*/temp1_input; do
|
||||
if [[ -f "$hwmon" ]]; then
|
||||
cat "$hwmon"
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo 0
|
||||
}
|
||||
|
||||
detect_gpu_power() {
|
||||
# Returns power in microwatts
|
||||
local hwmon
|
||||
for hwmon in "$GPU_SYSFS"/hwmon/hwmon*/power1_average; do
|
||||
if [[ -f "$hwmon" ]]; then
|
||||
cat "$hwmon"
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo 0
|
||||
}
|
||||
|
||||
detect_gpu_busy() {
|
||||
cat "$GPU_SYSFS/gpu_busy_percent" 2>/dev/null || echo 0
|
||||
}
|
||||
|
||||
# --- Total physical memory (visible + VRAM dedicated) ---
|
||||
detect_total_physical_ram_kb() {
|
||||
local visible_kb vram_bytes vram_kb
|
||||
visible_kb="$(detect_system_ram_kb)"
|
||||
vram_bytes="$(detect_vram_total)"
|
||||
vram_kb=$(( vram_bytes / 1024 ))
|
||||
echo $(( visible_kb + vram_kb ))
|
||||
}
|
||||
|
||||
# --- Recommended values for this system ---
|
||||
recommended_gttsize_mib() {
|
||||
# Total physical RAM (including VRAM allocation) minus 4 GiB reserve, in MiB
|
||||
local total_kb
|
||||
total_kb="$(detect_total_physical_ram_kb)"
|
||||
local total_gib=$(( total_kb / 1024 / 1024 ))
|
||||
local gtt_gib=$(( total_gib - 4 ))
|
||||
echo $(( gtt_gib * 1024 ))
|
||||
}
|
||||
|
||||
recommended_pages_limit() {
|
||||
# GTT GiB * 1024 MiB/GiB * 256 pages/MiB
|
||||
local gtt_mib
|
||||
gtt_mib="$(recommended_gttsize_mib)"
|
||||
echo $(( gtt_mib * 256 ))
|
||||
}
|
||||
74
lib/format.sh
Normal file
74
lib/format.sh
Normal file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env bash
|
||||
# Formatting utilities
|
||||
# Requires: lib/common.sh must be sourced first (provides color variables)
|
||||
|
||||
# Guard: ensure color variables are defined (sourced from common.sh)
|
||||
: "${GREEN:=}" "${RED:=}" "${YELLOW:=}" "${CYAN:=}" "${BOLD:=}" "${DIM:=}" "${RESET:=}"
|
||||
|
||||
human_bytes() {
|
||||
local bytes="${1:-0}"
|
||||
if (( bytes >= 1073741824 )); then
|
||||
local val
|
||||
val="$(echo "scale=1; $bytes / 1073741824" | bc)"
|
||||
printf "%s GiB" "${val/#./0.}"
|
||||
elif (( bytes >= 1048576 )); then
|
||||
printf "%d MiB" "$(( bytes / 1048576 ))"
|
||||
elif (( bytes >= 1024 )); then
|
||||
printf "%d KiB" "$(( bytes / 1024 ))"
|
||||
else
|
||||
printf "%d B" "$bytes"
|
||||
fi
|
||||
}
|
||||
|
||||
human_mib() {
|
||||
local mib="${1:-0}"
|
||||
if (( mib >= 1024 )); then
|
||||
local val
|
||||
val="$(echo "scale=1; $mib / 1024" | bc)"
|
||||
printf "%s GiB" "${val/#./0.}"
|
||||
else
|
||||
printf "%d MiB" "$mib"
|
||||
fi
|
||||
}
|
||||
|
||||
# Status indicators
|
||||
STATUS_PASS="${GREEN}[OK]${RESET}"
|
||||
STATUS_FAIL="${RED}[!!]${RESET}"
|
||||
STATUS_WARN="${YELLOW}[??]${RESET}"
|
||||
STATUS_INFO="${CYAN}[--]${RESET}"
|
||||
|
||||
print_status() {
|
||||
# Usage: print_status pass|fail|warn|info "label" "detail"
|
||||
local kind="$1" label="$2" detail="${3:-}"
|
||||
local indicator
|
||||
case "$kind" in
|
||||
pass) indicator="$STATUS_PASS" ;;
|
||||
fail) indicator="$STATUS_FAIL" ;;
|
||||
warn) indicator="$STATUS_WARN" ;;
|
||||
*) indicator="$STATUS_INFO" ;;
|
||||
esac
|
||||
printf " %b %-30s %s\n" "$indicator" "$label" "$detail"
|
||||
}
|
||||
|
||||
print_kv() {
|
||||
local key="$1" value="$2"
|
||||
printf " %b%-24s%b %s\n" "$DIM" "$key:" "$RESET" "$value"
|
||||
}
|
||||
|
||||
print_divider() {
|
||||
printf "%b%s%b\n" "$DIM" "$(printf '%.0s─' {1..60})" "$RESET"
|
||||
}
|
||||
|
||||
# Table helpers — format strings are caller-controlled constants, not user input
|
||||
print_table_header() {
|
||||
local fmt="$1"; shift
|
||||
# shellcheck disable=SC2059 — format string is a trusted constant from callers
|
||||
printf "${BOLD}${fmt}${RESET}\n" "$@"
|
||||
print_divider
|
||||
}
|
||||
|
||||
print_table_row() {
|
||||
local fmt="$1"; shift
|
||||
# shellcheck disable=SC2059 — format string is a trusted constant from callers
|
||||
printf "${fmt}\n" "$@"
|
||||
}
|
||||
Reference in New Issue
Block a user