diff --git a/rigforge.sh b/rigforge.sh index 04c0f40..a02e1d8 100755 --- a/rigforge.sh +++ b/rigforge.sh @@ -4546,12 +4546,18 @@ _lockdown_blocks_msr() { # -> 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 have been set successfully' (or a failure). Echoes # "\t" 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() { # 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 @@ -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 diff --git a/tests/e2e-real.sh b/tests/e2e-real.sh index 2d47bd2..07f9251 100755 --- a/tests/e2e-real.sh +++ b/tests/e2e-real.sh @@ -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:-}', 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 diff --git a/tests/run.sh b/tests/run.sh index 4ad23a6..9f2063c 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -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" < + ( + 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) =="