Compare commits

...

2 commits

Author SHA1 Message Date
d2f70296f5 Correct the Shift+F2 claim: the keybind does not work in Game Mode
The in-game keys table told users Shift+F2 toggles logging mid-game. That
holds on the desktop, where the in-game Vulkan layer is the logger, but not
in SteamOS Game Mode: there the logger is mangoapp, which ignores the
keybind. Verified under nested gamescope — the keypress produced no log at
all, while mangohudctl in the same harness started one immediately.

Document mangohudctl as the way to start/stop a log by hand in Game Mode.
It drives mangoapp over a SysV message queue, so it needs no display, no
Wayland socket and no window focus.
2026-07-31 19:09:52 +01:00
551471f0b3 Auto-select the gaming GPU + cross-vendor CPU-power tooling
MangoHud logs one GPU. On a hybrid box (discrete NVIDIA/AMD beside an
AMD/Intel iGPU) its default often latches onto the idle iGPU, so every
gpu_* column reads ~0 while the discrete card does the rendering. Detect
the actual gaming GPU — any vendor, single- or multi-GPU — and pin it via
gpu_list. Selection: single GPU -> that one; discrete NVIDIA preferred;
otherwise the card with the most VRAM. Override with MANGOHUD_LOGGER_GPU.
Verified against MangoHud's own enumeration (gpu_list=0 -> renderD128,
RTX 5060 Ti) and 7 synthetic layouts incl. NVIDIA as the second node.

Also make CPU telemetry work across AMD and Intel. cpu_power was logging
0 because the RAPL energy counters are root-only since CVE-2020-8694
(same intel-rapl powercap path on both vendors). Add:
  - doctor: read-only GPU/CPU telemetry readiness report
  - fix-cpu-power: install a udev rule making RAPL counters user-readable
  - enable-time warnings pointing at the fix
CPU temp already works via hwmon (k10temp/zenpower/coretemp).

Default no-arg behavior (toggle on/off) is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 19:31:03 +01:00
2 changed files with 400 additions and 13 deletions

View file

@ -21,8 +21,9 @@ won't trample anything you wrote by hand):
1. **`~/.config/MangoHud/MangoHud.conf`** — appends a marker-bracketed block 1. **`~/.config/MangoHud/MangoHud.conf`** — appends a marker-bracketed block
with `output_folder`, `autostart_log=1`, `log_duration=0` (= until you with `output_folder`, `autostart_log=1`, `log_duration=0` (= until you
quit), `log_interval=100`, `toggle_logging=Shift_L+F2`, and a quit), `log_interval=100`, `toggle_logging=Shift_L+F2`, a `no_display=0`
`no_display=0` override. (Also serves as the on/off state marker.) override, and a `gpu_list=<index>` pin (see [GPU selection](#gpu-selection)).
(Also serves as the on/off state marker.)
2. **`~/.config/environment.d/95-mangohud-logger.conf`** — contains: 2. **`~/.config/environment.d/95-mangohud-logger.conf`** — contains:
- `MANGOHUD=1` so the Vulkan loader auto-injects MangoHud into every game. - `MANGOHUD=1` so the Vulkan loader auto-injects MangoHud into every game.
@ -39,6 +40,40 @@ won't trample anything you wrote by hand):
When you toggle it off, the script removes everything it wrote. When you toggle it off, the script removes everything it wrote.
## GPU selection
MangoHud logs stats for **one** GPU. On a hybrid machine — a discrete
NVIDIA/AMD card alongside an AMD/Intel iGPU (common on modern AMD desktops,
laptops, and handhelds) — MangoHud's default frequently latches onto the *idle*
iGPU, so every `gpu_*` column in the CSV reads ~0 (0 % load, idle clocks, idle
power) even while the discrete card does all the rendering.
On enable, the script auto-detects the GPU that's actually rendering games and
pins MangoHud to it with `gpu_list=<index>`. It's **vendor-agnostic**:
- **Single GPU** (any vendor) → that GPU.
- **Discrete NVIDIA** present → the NVIDIA card.
- **Two AMD/Intel GPUs** → the one with the most VRAM (the discrete card;
iGPUs report little or none).
It works by mapping DRM render nodes (`renderD128`, `renderD129`, …) to
MangoHud's `gpu_list` index order. The chosen GPU is printed on enable:
```
:: gpu logged: gpu_list=0 → renderD128 (NVIDIA), 2 GPU(s) detected
```
If the guess is wrong, override it:
```bash
MANGOHUD_LOGGER_GPU=amd ./mangohud-logger.sh # or: nvidia | intel
MANGOHUD_LOGGER_GPU=1 ./mangohud-logger.sh # or an explicit gpu_list index
```
> **NVIDIA note:** MangoHud reads NVIDIA load/power/clocks via NVML. If NVML
> (`libnvidia-ml`, shipped with `nvidia-utils`) is missing, the script warns you
> — without it the NVIDIA `gpu_*` columns stay at 0.
## Session-flavor detection ## Session-flavor detection
Game Mode is launched differently on different distros, and the fix differs Game Mode is launched differently on different distros, and the fix differs
@ -140,23 +175,74 @@ mangohud-logger
## Usage ## Usage
```bash ```bash
./mangohud-logger.sh ./mangohud-logger.sh # toggle logging on/off (default)
./mangohud-logger.sh doctor # report GPU/CPU telemetry readiness (no changes)
./mangohud-logger.sh fix-cpu-power # make RAPL CPU-power counters readable (sudo)
./mangohud-logger.sh help # usage
``` ```
No flags, no subcommands. First run enables; second run disables. After the Run with no argument to toggle: first run enables, second disables. After the
first enable, **log out and back in** (Game Mode users: fully restart into Game first enable, **log out and back in** (Game Mode users: fully restart into Game
Mode) so the env vars become effective. You only need to do that once. Mode) so the env vars become effective. You only need to do that once.
Before your first benchmark, run `doctor` — it tells you exactly which columns
will hold real data and what (if anything) needs fixing:
```
:: GPUs (DRM render nodes):
renderD128 (NVIDIA)
renderD129 (AMD)
✓ will log: gpu_list=0 → renderD128 (NVIDIA), 2 GPU(s) detected
✓ NVML present → NVIDIA load/power/clocks will log
:: CPU: AMD
✓ temp source: hwmon 'k10temp' → cpu_temp will log
! power (RAPL): root-only → cpu_power = 0. Fix: mangohud-logger.sh fix-cpu-power
```
In-game keys (set by the script's MangoHud block): In-game keys (set by the script's MangoHud block):
| Key | Action | | Key | Action |
|---|---| |---|---|
| `Shift+F2` | Toggle logging on/off mid-game | | `Shift+F2` | Toggle logging on/off mid-game**desktop only, see below** |
| Your existing `toggle_hud` key | Hide the HUD overlay (logging keeps running) | | Your existing `toggle_hud` key | Hide the HUD overlay (logging keeps running) |
> **`Shift+F2` does not work in SteamOS Game Mode.** There the logger is
> mangoapp, not the in-game Vulkan layer, and mangoapp ignores the keybind.
> Verified under nested gamescope: the keypress produced no log at all. To
> start/stop a log by hand in Game Mode, use `mangohudctl` (ships with MangoHud),
> which drives mangoapp over a message queue and needs no display or focus:
>
> ```bash
> mangohudctl set log_session true # start
> mangohudctl set log_session false # stop (also writes a _summary.csv)
> ```
CSV files appear in `~/Downloads/mango-logs/`. Drop them into CSV files appear in `~/Downloads/mango-logs/`. Drop them into
<https://flightlessmango.com/> for graphs, or open in a spreadsheet. <https://flightlessmango.com/> for graphs, or open in a spreadsheet.
## CPU power (`cpu_power` column reads 0?)
MangoHud reads CPU package power from the RAPL powercap energy counters. Since
CVE-2020-8694 those counters are **root-only** (mode `0400`), so user-mode
MangoHud can't read them and `cpu_power` logs as `0`. This is the same on **AMD
and Intel** — the powercap interface is named `intel-rapl` on both (it's the
framework name; on AMD `intel_rapl_msr` feeds it from the Zen RAPL MSRs).
`fix-cpu-power` installs a udev rule
(`/etc/udev/rules.d/60-mangohud-logger-rapl.rules`) that makes `energy_uj`
user-readable, and applies it immediately:
```bash
./mangohud-logger.sh fix-cpu-power
```
> **Security note:** this re-exposes the low-severity PLATYPUS power
> side-channel. Fine for a personal gaming/benchmark box; skip it on shared or
> multi-user machines. Remove the rule file to revert.
CPU **temperature** needs no fix — it comes from hwmon (`k10temp`/`zenpower` on
AMD, `coretemp` on Intel) and works out of the box.
## Compatibility ## Compatibility
- **SteamOS Game Mode** (`/usr/lib/steamos/gamescope-session`) — logs via - **SteamOS Game Mode** (`/usr/lib/steamos/gamescope-session`) — logs via

View file

@ -10,11 +10,20 @@
# → appends a marker block with output_folder, autostart_log, etc. # → appends a marker block with output_folder, autostart_log, etc.
# → keeps your existing keys; overrides `no_display` while logging # → keeps your existing keys; overrides `no_display` while logging
# (autostart_log rides on the render hook that no_display disables). # (autostart_log rides on the render hook that no_display disables).
# → pins gpu_list to the GPU actually rendering games (see below), so the
# gpu_* columns log real data instead of an idle second GPU.
# 2. ~/.config/environment.d/95-mangohud-logger.conf # 2. ~/.config/environment.d/95-mangohud-logger.conf
# → contains MANGOHUD=1 so the Vulkan layer auto-loads into every # → contains MANGOHUD=1 so the Vulkan layer auto-loads into every
# game (gamescope sessions, Steam, native Vulkan apps). # game (gamescope sessions, Steam, native Vulkan apps).
# Picked up by systemd-user at next session login. # Picked up by systemd-user at next session login.
# #
# GPU selection (vendor-agnostic):
# MangoHud logs stats for ONE GPU. On a hybrid box (an AMD/Intel iGPU beside a
# discrete NVIDIA/AMD card) its default often picks the idle iGPU, so every
# gpu_* column reads ~0. We auto-detect the render node that's the actual
# gaming GPU — any vendor — and pin `gpu_list` to its index. Override with
# MANGOHUD_LOGGER_GPU=<index> or =nvidia|amd|intel if the guess is wrong.
#
# Logs land in the user's Downloads folder under "mango-logs" # Logs land in the user's Downloads folder under "mango-logs"
# (honours XDG_DOWNLOAD_DIR from ~/.config/user-dirs.dirs, else ~/Downloads). # (honours XDG_DOWNLOAD_DIR from ~/.config/user-dirs.dirs, else ~/Downloads).
@ -75,15 +84,269 @@ info() { printf '\033[36m::\033[0m %s\n' "$*"; }
ok() { printf '\033[32m✓\033[0m %s\n' "$*"; } ok() { printf '\033[32m✓\033[0m %s\n' "$*"; }
warn() { printf '\033[33m!\033[0m %s\n' "$*"; } warn() { printf '\033[33m!\033[0m %s\n' "$*"; }
if ! command -v mangohud >/dev/null 2>&1; then # PCI vendor IDs (as reported by /sys/class/drm/*/device/vendor).
readonly VENDOR_NVIDIA=0x10de
readonly VENDOR_AMD=0x1002
readonly VENDOR_INTEL=0x8086
# select_gpu — figure out which GPU MangoHud should log, vendor-agnostically.
#
# MangoHud enumerates DRM render nodes (renderD128, renderD129, …) in ascending
# order and `gpu_list=<n>` selects the n-th of them (verified: gpu_list=0 maps to
# renderD128). We pick the node that's the real gaming GPU and return its index.
#
# Selection order:
# 1. MANGOHUD_LOGGER_GPU override — a numeric index, or nvidia|amd|intel.
# 2. Single GPU → index 0 (covers every single-card machine, any vendor).
# 3. Multi-GPU → prefer a discrete NVIDIA; else the card with the most VRAM
# (discrete AMD/Intel report mem_info_vram_total; iGPUs report far less/none).
#
# Sets globals: GPU_INDEX (may be ""), GPU_VENDOR, GPU_DESC.
select_gpu() {
GPU_INDEX=""; GPU_VENDOR=""; GPU_DESC=""
local nodes=()
mapfile -t nodes < <(
for n in /sys/class/drm/renderD*; do
[[ -e "$n/device/vendor" ]] && basename "$n"
done | sort # ascending == MangoHud's gpu_list index order
)
local count=${#nodes[@]}
if (( count == 0 )); then
GPU_DESC="no DRM render node found (software renderer?) — gpu_list left unset"
return
fi
# Index-aligned vendor + VRAM for each node.
local vendors=() vrams=() i
for i in "${!nodes[@]}"; do
vendors[i]=$(cat "/sys/class/drm/${nodes[i]}/device/vendor" 2>/dev/null || echo unknown)
vrams[i]=$(cat "/sys/class/drm/${nodes[i]}/device/mem_info_vram_total" 2>/dev/null || echo 0)
done
if [[ -n "${MANGOHUD_LOGGER_GPU:-}" ]]; then
local ov="${MANGOHUD_LOGGER_GPU,,}"
if [[ "$ov" =~ ^[0-9]+$ ]]; then
GPU_INDEX="$ov"
else
local want=""
case "$ov" in
nvidia) want=$VENDOR_NVIDIA ;;
amd) want=$VENDOR_AMD ;;
intel) want=$VENDOR_INTEL ;;
esac
for i in "${!nodes[@]}"; do
[[ "${vendors[i]}" == "$want" ]] && { GPU_INDEX="$i"; break; }
done
[[ -z "$GPU_INDEX" ]] && GPU_INDEX=0 # requested vendor absent → first GPU
fi
elif (( count == 1 )); then
GPU_INDEX=0
else
# Prefer discrete NVIDIA (NVIDIA never populates mem_info_vram_total via DRM
# sysfs, so the VRAM check below can't see it — hence this explicit pass).
for i in "${!nodes[@]}"; do
[[ "${vendors[i]}" == "$VENDOR_NVIDIA" ]] && { GPU_INDEX="$i"; break; }
done
if [[ -z "$GPU_INDEX" ]]; then
local best=0 bestvram=-1
for i in "${!nodes[@]}"; do
if (( ${vrams[i]:-0} > bestvram )); then bestvram=${vrams[i]:-0}; best=$i; fi
done
GPU_INDEX="$best"
fi
fi
# Describe the choice (guard against an out-of-range manual override).
local node="${nodes[$GPU_INDEX]:-?}" vsel="${vendors[$GPU_INDEX]:-unknown}" name
case "$vsel" in
"$VENDOR_NVIDIA") name=NVIDIA ;;
"$VENDOR_AMD") name=AMD ;;
"$VENDOR_INTEL") name=Intel ;;
*) name="vendor $vsel" ;;
esac
GPU_VENDOR="$vsel"
GPU_DESC="gpu_list=$GPU_INDEX$node ($name), $count GPU(s) detected"
}
# libnvidia-ml (NVML) is what lets MangoHud read NVIDIA load/power/clocks.
have_nvml() {
ldconfig -p 2>/dev/null | grep -q 'libnvidia-ml' \
|| [[ -e /usr/lib/libnvidia-ml.so.1 || -e /usr/lib64/libnvidia-ml.so.1 ]]
}
# ── CPU telemetry (vendor-neutral: works on AMD *and* Intel) ────────────────
#
# MangoHud reads CPU temperature from hwmon and CPU package power from the RAPL
# powercap energy counters. Both paths are the same regardless of CPU vendor:
# • temp → hwmon driver: k10temp/zenpower (AMD), coretemp (Intel)
# • power → /sys/class/powercap/intel-rapl:* — despite the "intel" name this is
# the powercap *framework*; on AMD it's fed by intel_rapl_msr from the Zen
# RAPL MSRs. Since CVE-2020-8694 the energy_uj files are root-only (0400),
# so user-mode MangoHud can't read them and cpu_power logs as 0.
readonly RAPL_UDEV_FILE=/etc/udev/rules.d/60-mangohud-logger-rapl.rules
# cpu_vendor → AMD | Intel | <raw string>
cpu_vendor() {
local v; v=$(awk -F': ' '/^vendor_id/{print $2; exit}' /proc/cpuinfo 2>/dev/null)
case "$v" in
AuthenticAMD) echo AMD ;;
GenuineIntel) echo Intel ;;
*) echo "${v:-unknown}" ;;
esac
}
# First RAPL package energy counter (enough to gate on), else empty.
rapl_energy_file() { compgen -G '/sys/class/powercap/*rapl*/energy_uj' 2>/dev/null | head -1; }
# rapl_state → readable | denied | absent
rapl_state() {
local f; f=$(rapl_energy_file)
[[ -z "$f" ]] && { echo absent; return; }
if cat "$f" >/dev/null 2>&1; then echo readable; else echo denied; fi
}
# cpu_temp_source → hwmon 'name' providing CPU temp (any vendor), else empty.
cpu_temp_source() {
local h n
for h in /sys/class/hwmon/hwmon*; do
n=$(cat "$h/name" 2>/dev/null) || continue
case "$n" in
k10temp|zenpower|zenpower3|coretemp|k8temp) echo "$n"; return ;;
esac
done
echo ""
}
# do_fix_cpu_power — install a udev rule making RAPL energy counters readable so
# MangoHud can log cpu_power. Vendor-neutral (same RAPL path on AMD and Intel).
do_fix_cpu_power() {
local vend; vend=$(cpu_vendor)
info "CPU power ($vend) comes from RAPL energy counters, root-only since CVE-2020-8694."
info "This installs a udev rule so they're user-readable and MangoHud can log cpu_power."
warn "Security note: re-exposes the low-severity PLATYPUS power side-channel."
warn "Fine for a personal gaming/benchmark box; skip on shared/multi-user machines."
echo
case "$(rapl_state)" in
absent)
err "no RAPL powercap interface found."
info "try: sudo modprobe intel_rapl_msr (then re-run)"
return 1 ;;
readable)
ok "RAPL energy counters are already user-readable — nothing to do."
return 0 ;;
esac
local chmod_bin; chmod_bin=$(command -v chmod || echo /usr/bin/chmod)
# KERNEL match covers the package (intel-rapl:0) and its subzones (intel-rapl:0:0).
# %S%p resolves to the node's /sys path; we chmod its energy_uj attribute.
local rule="SUBSYSTEM==\"powercap\", KERNEL==\"intel-rapl:*\", RUN+=\"$chmod_bin 0444 %S%p/energy_uj\""
info "writing $RAPL_UDEV_FILE (sudo)…"
if ! printf '%s\n' \
"# Installed by mangohud-logger: make RAPL energy_uj user-readable for CPU-power logging." \
"# Remove this file to revert. See: mangohud-logger help" \
"$rule" | sudo tee "$RAPL_UDEV_FILE" >/dev/null; then
err "failed to write $RAPL_UDEV_FILE"; return 1
fi
sudo udevadm control --reload-rules 2>/dev/null || true
sudo udevadm trigger --subsystem-match=powercap 2>/dev/null || true
# Apply to already-present nodes now (udev 'add' won't re-fire for existing ones).
sudo "$chmod_bin" -f 0444 /sys/class/powercap/*rapl*/energy_uj 2>/dev/null || true
if [[ "$(rapl_state)" == readable ]]; then
ok "done — cpu_power will now be logged (persists across reboots)."
else
warn "rule installed but counters still not readable; a reboot should apply it."
fi
}
# do_doctor — report cross-vendor telemetry readiness; makes no changes.
do_doctor() {
info "mangohud-logger doctor — telemetry readiness (read-only)"
echo
if command -v mangohud >/dev/null 2>&1; then
ok "mangohud: $(mangohud --version 2>/dev/null | head -1)"
else
err "mangohud: NOT installed"
fi
info "session flavor: $GS_FLAVOR"
echo
# GPUs — list every render node, then the one that will be logged.
info "GPUs (DRM render nodes):"
local n vend name
for n in $(compgen -G '/sys/class/drm/renderD*' 2>/dev/null | sort); do
[[ -e "$n/device/vendor" ]] || continue
vend=$(cat "$n/device/vendor" 2>/dev/null)
case "$vend" in
"$VENDOR_NVIDIA") name=NVIDIA ;; "$VENDOR_AMD") name=AMD ;;
"$VENDOR_INTEL") name=Intel ;; *) name="vendor $vend" ;;
esac
printf ' %s (%s)\n' "$(basename "$n")" "$name"
done
select_gpu
if [[ -n "$GPU_INDEX" ]]; then ok "will log: $GPU_DESC"
else warn "will log: $GPU_DESC"; fi
if [[ "$GPU_VENDOR" == "$VENDOR_NVIDIA" ]]; then
if have_nvml; then ok " NVML present → NVIDIA load/power/clocks will log"
else err " NVML missing → NVIDIA gpu_* columns will be 0 (install nvidia-utils)"; fi
fi
echo
# CPU — temp + power, vendor-neutral.
info "CPU: $(cpu_vendor)"
local ts; ts=$(cpu_temp_source)
if [[ -n "$ts" ]]; then ok " temp source: hwmon '$ts' → cpu_temp will log"
else warn " temp: no known sensor (k10temp/zenpower/coretemp) → cpu_temp may be 0"; fi
case "$(rapl_state)" in
readable) ok " power (RAPL): readable → cpu_power will log" ;;
denied) warn " power (RAPL): root-only → cpu_power = 0. Fix: $(basename "$0") fix-cpu-power" ;;
absent) warn " power (RAPL): no powercap interface → cpu_power = 0 (modprobe intel_rapl_msr)" ;;
esac
echo
if [[ -f "$CONFIG_FILE" ]] && grep -qF "$MARKER_BEGIN" "$CONFIG_FILE"; then
ok "logging is currently ENABLED (log folder: $LOG_DIR)"
else
info "logging is currently disabled — run '$(basename "$0")' to enable"
fi
}
usage() {
cat <<EOF
mangohud-logger — toggle MangoHud CSV logging, with cross-vendor telemetry setup
USAGE:
$(basename "$0") Toggle logging on/off (default action)
$(basename "$0") doctor Report GPU/CPU telemetry readiness (no changes)
$(basename "$0") fix-cpu-power Make RAPL CPU-power counters user-readable (sudo)
$(basename "$0") help Show this help
ENVIRONMENT:
MANGOHUD_LOGGER_GPU=<index|nvidia|amd|intel>
Force which GPU MangoHud logs (default: auto —
prefers the discrete gaming GPU, any vendor)
Works across AMD/Intel CPUs and NVIDIA/AMD/Intel GPUs, single- or multi-GPU.
EOF
}
# do_toggle — the original enable/disable behavior (default action).
do_toggle() {
if ! command -v mangohud >/dev/null 2>&1; then
err "mangohud is not installed — install it first (e.g. 'sudo pacman -S mangohud lib32-mangohud')." err "mangohud is not installed — install it first (e.g. 'sudo pacman -S mangohud lib32-mangohud')."
exit 1 exit 1
fi fi
mkdir -p "$CONFIG_DIR" mkdir -p "$CONFIG_DIR"
[[ -f "$CONFIG_FILE" ]] || : > "$CONFIG_FILE" [[ -f "$CONFIG_FILE" ]] || : > "$CONFIG_FILE"
if grep -qF "$MARKER_BEGIN" "$CONFIG_FILE"; then if grep -qF "$MARKER_BEGIN" "$CONFIG_FILE"; then
# Currently enabled → strip the block and remove the env file. # Currently enabled → strip the block and remove the env file.
tmp=$(mktemp) tmp=$(mktemp)
awk -v b="$MARKER_BEGIN" -v e="$MARKER_END" ' awk -v b="$MARKER_BEGIN" -v e="$MARKER_END" '
@ -121,10 +384,13 @@ else
# Currently disabled → append the config block + write the env file. # Currently disabled → append the config block + write the env file.
# Check for pre-existing manual log keys *before* we write, so the warning is accurate. # Check for pre-existing manual log keys *before* we write, so the warning is accurate.
manual_log_keys=0 manual_log_keys=0
if grep -qE '^\s*(output_folder|autostart_log|log_duration|log_interval|toggle_logging)\s*=' "$CONFIG_FILE"; then if grep -qE '^\s*(output_folder|autostart_log|log_duration|log_interval|toggle_logging|gpu_list)\s*=' "$CONFIG_FILE"; then
manual_log_keys=1 manual_log_keys=1
fi fi
# Work out which GPU to log (any vendor, single- or multi-GPU) before writing.
select_gpu
mkdir -p "$LOG_DIR" mkdir -p "$LOG_DIR"
{ {
[[ -s "$CONFIG_FILE" ]] && printf '\n' [[ -s "$CONFIG_FILE" ]] && printf '\n'
@ -138,6 +404,9 @@ else
# Override any earlier `no_display` — MangoHud's autostart_log rides on the # Override any earlier `no_display` — MangoHud's autostart_log rides on the
# render hook, which `no_display` disables. Without this, logs never start. # render hook, which `no_display` disables. Without this, logs never start.
printf 'no_display=0\n' printf 'no_display=0\n'
# Pin the GPU MangoHud logs to the actual gaming GPU (see select_gpu). Omitted
# only when no render node was found, so MangoHud keeps its own default.
[[ -n "$GPU_INDEX" ]] && printf 'gpu_list=%s\n' "$GPU_INDEX"
printf '%s\n' "$MARKER_END" printf '%s\n' "$MARKER_END"
} >> "$CONFIG_FILE" } >> "$CONFIG_FILE"
@ -147,6 +416,7 @@ else
# but it never sets MANGOHUD_CONFIG, so ours (from environment.d) wins for every game. # but it never sets MANGOHUD_CONFIG, so ours (from environment.d) wins for every game.
# NOTE: relies on output_folder having no commas/spaces (true for the default path). # NOTE: relies on output_folder having no commas/spaces (true for the default path).
MH_INLINE="output_folder=$LOG_DIR,autostart_log=1,log_duration=0,log_interval=100,toggle_logging=Shift_L+F2,no_display=0" MH_INLINE="output_folder=$LOG_DIR,autostart_log=1,log_duration=0,log_interval=100,toggle_logging=Shift_L+F2,no_display=0"
[[ -n "$GPU_INDEX" ]] && MH_INLINE+=",gpu_list=$GPU_INDEX"
mkdir -p "$ENV_DIR" mkdir -p "$ENV_DIR"
cat > "$ENV_FILE" <<EOF cat > "$ENV_FILE" <<EOF
@ -208,11 +478,32 @@ EOF
warn "pre-existing log-related keys found in config — they may override ours." warn "pre-existing log-related keys found in config — they may override ours."
fi fi
# NVIDIA GPU stats come from NVML; without it every gpu_* column logs as 0.
if [[ "$GPU_VENDOR" == "$VENDOR_NVIDIA" ]] && ! have_nvml; then
warn "selected an NVIDIA GPU but libnvidia-ml (NVML) not found — GPU load/power"
warn "won't be logged. Install the NVIDIA userspace (e.g. 'nvidia-utils')."
fi
# CPU power/temp readiness (same checks on AMD and Intel).
rapl=$(rapl_state)
if [[ "$rapl" == denied ]]; then
warn "cpu_power won't log — RAPL counters are root-only. Enable it with:"
warn " $(basename "$0") fix-cpu-power"
elif [[ "$rapl" == absent ]]; then
warn "cpu_power won't log — no RAPL powercap interface (try: sudo modprobe intel_rapl_msr)."
fi
[[ -z "$(cpu_temp_source)" ]] && warn "no known CPU temp sensor found — cpu_temp may read 0."
ok "MangoHud logging enabled" ok "MangoHud logging enabled"
info "log folder: $LOG_DIR" info "log folder: $LOG_DIR"
info "toggle key: Shift+F2 (during a game)" info "toggle key: Shift+F2 (during a game)"
info "config file: $CONFIG_FILE" info "config file: $CONFIG_FILE"
info "env file: $ENV_FILE (MANGOHUD=1)" info "env file: $ENV_FILE (MANGOHUD=1)"
info "gpu logged: ${GPU_DESC:-MangoHud default}"
if [[ -n "$GPU_INDEX" ]]; then
info " (override with MANGOHUD_LOGGER_GPU=<index|nvidia|amd|intel>)"
fi
info "cpu ($( cpu_vendor )): temp=$( [[ -n "$(cpu_temp_source)" ]] && echo ok || echo none ) power(RAPL)=$rapl"
info "session type: $GS_FLAVOR (gamescope override: $gs_action)" info "session type: $GS_FLAVOR (gamescope override: $gs_action)"
info "note: while logging is on, the HUD becomes visible during games" info "note: while logging is on, the HUD becomes visible during games"
info " (autostart_log needs the render hook that no_display disables)" info " (autostart_log needs the render hook that no_display disables)"
@ -231,3 +522,13 @@ EOF
warn "log out and back in (or restart your gamescope session) so the env var takes effect." warn "log out and back in (or restart your gamescope session) so the env var takes effect."
fi fi
fi fi
}
# ── Command dispatch ────────────────────────────────────────────────────────
case "${1:-toggle}" in
toggle|"") do_toggle ;;
doctor|check|--check|-c) do_doctor ;;
fix-cpu-power|fix|--fix-cpu-power) do_fix_cpu_power ;;
-h|--help|help) usage ;;
*) err "unknown command: $1"; echo; usage; exit 1 ;;
esac