Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions rigforge.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4546,12 +4546,18 @@ _lockdown_blocks_msr() { # <level> -> 0 when MSR writes are denied
# Parse the worker's xmrig.log for XMRig's MSR-write confirmation. Per (re)start XMRig logs
# 'msr register values for "<preset>" preset have been set successfully' (or a failure). Echoes
# "<ok|fail|none>\t<preset>" for the LAST msr line. One-line awk so kcov attributes it correctly.
# #367: the line is written at miner START, so on a long-lived worker it sits near the BEGINNING of a
# log that can reach 100MB+ — `awk` scanning the whole file on every `doctor` got expensive. `grep`
# (C-speed) finds every match and `tail -1` keeps the same last-match semantics; a naive `tail`-first
# approach would miss the line entirely on a big file, so don't "optimize" this into one.
_msr_log_status() { # <logfile>
if [ ! -f "$1" ]; then
printf 'none\t'
return 0
fi
awk '/msr +register values for/{p="";if(match($0,/"[^"]+"/))p=substr($0,RSTART+1,RLENGTH-2);if(index($0,"set successfully")>0){st="ok";pr=p}else if(index($0,"FAILED")>0||index($0,"failed")>0||index($0,"cannot")>0){st="fail";pr=p}} END{if(st=="")printf "none\t";else printf "%s\t%s",st,pr}' "$1" 2>/dev/null
# `|| true`: under pipefail, grep finding zero matches (no msr line yet — a healthy, common state)
# would otherwise make the whole pipeline — and this function — exit non-zero.
grep -E 'msr +register values for' "$1" 2>/dev/null | tail -1 | awk '{p="";if(match($0,/"[^"]+"/))p=substr($0,RSTART+1,RLENGTH-2);if(index($0,"set successfully")>0){st="ok";pr=p}else if(index($0,"FAILED")>0||index($0,"failed")>0||index($0,"cannot")>0){st="fail";pr=p}else{st="none";pr=p}} END{if(NR==0)printf "none\t";else printf "%s\t%s",st,pr}' || true
}

# The (register, value, mask) triples XMRig writes per MSR preset — verified against XMRig v6.26.0
Expand Down Expand Up @@ -5176,8 +5182,18 @@ EOF
fi
issues=$((issues + 1))
;;
*) : ;; # no msr line yet (the miner may not have started a RandomX job) — stay quiet
# not-found is not proof of not-applied (miner hasn't started a RandomX job yet, or a
# copytruncate rotation just cleared the live log) — stay quiet.
*) : ;;
esac
elif [ -z "$log_file" ]; then
# #367: no config.json, so the worker root above never resolved. Distinct wording from the
# "resolved but no log" case below — an absent block must not read as a failed check.
_ck_info "MSR unverifiable — no config.json, so the worker root couldn't be resolved"
else
# #367: the worker root DID resolve, but nothing is logged at that path yet — a miner that
# hasn't started, or a copytruncate rotation window, both look like this on a healthy rig.
_ck_info "MSR unverifiable — no xmrig.log at $log_file"
fi

# CPU governor
Expand Down
12 changes: 12 additions & 0 deletions tests/e2e-real.sh
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,18 @@ verify() {
# msr can be a loadable module OR built into the kernel; either way it shows under /sys/module
# (lsmod only lists loadable modules, so a built-in msr would be a false negative) — match doctor.
[ -d /sys/module/msr ] && ok "msr available (/sys/module/msr)" || bad "msr not available"
# #367: assert the MSR guard's INPUTS first — resolve the same worker root + log path doctor's MSR
# block requires (mirrors the #reown resolver below) — so a run where doctor SKIPPED the block
# (guard failed) fails HERE as "could not resolve xmrig.log", not as "MSR not applied" below. The
# #66 flake that broke both assertions on a healthy rig was exactly this: an absent block reading
# identically to a failed check.
local msr_wr msr_raw_home msr_log
msr_raw_home="$(jq -r '.HOME_DIR // "DYNAMIC_HOME"' "$HERE/config.json" 2>/dev/null)"
msr_wr="$(RIGFORGE_HOME="$HERE" bash -c 'source "$1"; _worker_root_for_home "$2"' _ "$RIGFORGE" "$msr_raw_home" 2>/dev/null || true)"
msr_log="${msr_wr:+$msr_wr/xmrig.log}"
[ -n "$msr_log" ] && [ -f "$msr_log" ] &&
ok "resolved the worker's xmrig.log for doctor's MSR checks ($msr_log) (#367)" ||
bad "could not resolve xmrig.log for doctor's MSR checks (worker root '${msr_wr:-<unresolved>}', HOME_DIR='$msr_raw_home') (#367) — doctor's MSR block would have been SKIPPED, not failed"
# #66: doctor must confirm the MSR mod actually APPLIED (from XMRig's log) and — since setup installs
# msr-tools — verify the prefetcher registers hold the preset's values via rdmsr. Hardware-agnostic:
# it asserts on doctor's output (whatever per-family preset this CPU uses — e.g. ryzen_19h_zen4 on
Expand Down
39 changes: 39 additions & 0 deletions tests/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4252,6 +4252,45 @@ printf 'msr register values for "intel" preset FAILED to set\n' >"$MSRD/hom
out="$(run_doctor_msr "$DOC/rdmsr_ok")"
assert_contains "doctor: MSR FAILED-to-set WARN (#66)" "$out" "FAILED to set"

# #367: the MSR guard's inputs — config resolution, then the log path — must be distinguishable when
# either one comes up empty, so a skipped block never reads the same as a failed check (the flake that
# broke both #66 e2e assertions on a healthy rig).
echo "== unit: doctor MSR unverifiable — guard-input failures named (#367) =="
UNV="$DOC/unv"
mkdir -p "$UNV/home" # HOME_DIR resolves; worker/xmrig.log is deliberately never created
cat >"$UNV/config.json" <<EOF
{ "HOME_DIR": "$UNV/home", "pools": [{"url": "h:3333"}] }
EOF
run_doctor_cfg() { # <config.json path, possibly nonexistent>
(
source "$SCRIPT"
OS_TYPE=Linux
SCRIPT_DIR="$ROOT"
CONFIG_JSON="$1"
MEMINFO="$DOC/meminfo_ok"
MSR_MODULE_DIR="$DOC/msrmod"
GOVERNOR_FILE="$DOC/gov_perf"
HUGEPAGES_1G_NR="$DOC/nr1g"
set +e
PATH="$DOC/asroot:$STUBS:$PATH" doctor 2>&1
)
}
# (a) unresolved config: no config.json at all -> the worker root never resolves.
out="$(run_doctor_cfg "$DOC/nonexistent-config-367.json")"
assert_contains "doctor: MSR unverifiable names an unresolved config (#367)" "$out" \
"MSR unverifiable — no config.json, so the worker root couldn't be resolved"
assert_absent "doctor: unresolved-config is advisory, not a counted issue (#367)" "$out" "issue(s) found"
# (b) resolved root, log absent: config parses fine, but nothing is logged at the resolved path yet
# (fresh install, or a copytruncate rotation window on an otherwise healthy rig).
out="$(run_doctor_cfg "$UNV/config.json")"
assert_contains "doctor: MSR unverifiable names the missing log path (#367)" "$out" \
"MSR unverifiable — no xmrig.log at $UNV/home/worker/xmrig.log"
assert_absent "doctor: resolved-root-no-log is advisory, not a counted issue (#367)" "$out" "issue(s) found"
# (c) line present: once the log resolves and holds an msr line, neither unverifiable wording appears.
msr_log ryzen_19h_zen4
assert_absent "doctor: MSR unverifiable does not leak once the log has a line (#367)" \
"$(run_doctor_msr "$DOC/rdmsr_ok")" "MSR unverifiable"

# #140: with miner_user set, doctor (a) compares config against the unit's actual User= and (b)
# verifies the root-side preset recorded by msr-apply via the same rdmsr read-back.
echo "== unit: doctor miner_user + root-side MSR preset (#140) =="
Expand Down