diff --git a/app/igneum-app/src/ember.rs b/app/igneum-app/src/ember.rs index 9a9f5a35..60b4325b 100644 --- a/app/igneum-app/src/ember.rs +++ b/app/igneum-app/src/ember.rs @@ -906,6 +906,28 @@ pub fn tuned_line(mhs: f64, watts: f64, eff: f64) -> String { format!("Tuned: {mhs:.1} MH/s at {watts:.0} W ({eff:.3} MH/W)") } +/// (D) May a step be SET on this card? Measure-only (no control: Apple, NVIDIA without Power control, an AMD card +/// whose probe gave no tune line) sets nothing: the run notices the missing acknowledgement and measures. An AMD +/// card also needs its ADLX ordinal. 6 October 2026, PC 1 17:57Z: a measure-only 9070 XT was sent a set, refused. +pub fn may_set(vendor: &str, tune_control: bool, amd_ordinal: i64) -> bool { + match vendor { + "nvidia" => tune_control, + "amd" => tune_control && amd_ordinal >= 0, + _ => false, + } +} + +/// (E) The row's words while a back-off from a failed tune holds: when the retry comes and why it stopped. +pub fn retry_note(remaining_s: u64, last_note: &str) -> String { + let mins = (remaining_s + 59) / 60; + let why = last_note.trim_start_matches("tuning stopped: ").trim_start_matches("tuning: ").trim(); + if why.is_empty() { + format!("tuning: retry in {mins} min") + } else { + format!("tuning: retry in {mins} min ({why})") + } +} + /// The row's line for a baseline (measure-only) result: the card was measured as it runs, nothing was set. /// (6 October 2026: PC 1's rows read "Tuned: 114.2 MH/s at 305 W" for a baseline, which is not a tune.) pub fn measured_line(mhs: f64, watts: f64, eff: f64) -> String { @@ -1007,6 +1029,18 @@ mod tests { assert!(Run::applied(&step, Readback { limit_w: 90.0, acked: false }, 100.0)); } + #[test] + fn a_measure_only_card_is_never_set_and_a_back_off_says_when_the_retry_comes() { + assert!(!may_set("amd", false, 1), "the probe gave no tune line: measure only"); + assert!(may_set("amd", true, 1)); + assert!(!may_set("amd", true, -1), "no ADLX ordinal"); + assert!(!may_set("nvidia", false, -1), "Power control off: measure only"); + assert!(may_set("nvidia", true, -1)); + assert!(!may_set("apple", true, -1)); + assert_eq!(retry_note(3599, "tuning stopped: the card refused the setting (see the log)"), "tuning: retry in 60 min (the card refused the setting (see the log))"); + assert_eq!(retry_note(30, ""), "tuning: retry in 1 min"); + } + #[test] fn a_baseline_result_reads_measured_and_a_tune_reads_tuned() { assert_eq!(result_line(PlanKind::Baseline, 114.2, 305.0, 0.3744), "Measured: 114.2 MH/s at 305 W (0.374 MH/W)"); diff --git a/app/igneum-app/src/engine.rs b/app/igneum-app/src/engine.rs index 5c86061d..abbb3f79 100644 --- a/app/igneum-app/src/engine.rs +++ b/app/igneum-app/src/engine.rs @@ -2219,8 +2219,23 @@ impl Engine { if c.tune_control && c.vendor == "nvidia" && !power_control && !forced { // measure-only until Power control is on; the baseline plan covers it below } - if self.sweep_retry.get(&c.index).map(|t| now < *t).unwrap_or(false) { - continue; + // (E) 6 October 2026, PC 1 18:18Z: three "Tune now" requests sat behind the hour's back-off from the + // 17:55 failures and the rows said "queued" with no start; an explicit request now clears the back-off, + // and a held card's row says when the retry comes + if let Some(t) = self.sweep_retry.get(&c.index).copied() { + if now < t { + if forced { + self.sweep_retry.remove(&c.index); + } else { + if c.sweep_pct == 0 || c.sweep_note.starts_with("tuning stopped") { + let note = crate::ember::retry_note((t - now).as_secs(), &c.sweep_note); + if let Some(cc) = self.st().mining.cards.get_mut(c.index) { + cc.sweep_note = note; + } + } + continue; + } + } } let stable = self.miners.iter().find(|m| m.card == c.index).map(|m| m.proc.as_ref().map(|p| p.started.elapsed() >= Duration::from_secs(crate::ember::STABLE_S)).unwrap_or(false) && m.last_status.is_some()).unwrap_or(false); let wait = if c.state != "mining" { @@ -2567,16 +2582,47 @@ impl Engine { } let dev = device.to_string(); let cmd = format!("{seq}0 dev {dev}\n{seq}1 pl {w}\n{seq}2 {}\n{seq}3 {}\n", if clock > 0 { format!("lgc {clock}") } else { "rgc".to_string() }, if mem > 0 { format!("lmc {mem}") } else { "rmc".to_string() }); - let _ = std::fs::write(self.helper_cmd_dir().join("cmd.txt"), cmd); - // the helper polls twice a second and nvidia-smi answers within a second or two + let dir = self.helper_cmd_dir(); + let is_task = self.sweep_helper_is_task; + // 6 October 2026, PC 1: (B) the task's helper had exited while the engine's flag still said it ran, + // so commands went to nobody and a blind 4-second sleep called them acknowledged; (C) the first + // request of the first tune was written in the same second the helper started, and the helper + // skipped it as stale. Now, in the task case: the helper's heartbeat says whether it runs; the task + // is started and a fresh heartbeat awaited BEFORE the command is written; and the acknowledgement + // is the helper's own log line for this sequence (`3 nvidia-smi ...`), read for up to 15 s; + // a missing line is a failure with that reason + let want = format!(" {seq}3 nvidia-smi"); + let log_file = dir.join("helper.log"); std::thread::spawn(move || { - std::thread::sleep(Duration::from_secs(4)); - shared.send(Cmd::TuneSet(seq, Ok("helper".into()))); + if is_task { + if let Err(e) = crate::powertask::ensure_running(&dir, Duration::from_secs(12)) { + shared.send(Cmd::TuneSet(seq, Err(e))); + return; + } + } + let _ = std::fs::create_dir_all(&dir); + let _ = std::fs::write(dir.join("cmd.txt"), cmd); + let until = Instant::now() + Duration::from_secs(if is_task { 15 } else { 6 }); + loop { + std::thread::sleep(Duration::from_millis(500)); + let text = std::fs::read_to_string(&log_file).unwrap_or_default(); + if let Some(line) = text.lines().rev().find(|l| l.contains(&want)) { + shared.send(Cmd::TuneSet(seq, Ok(format!("helper: {}", line.trim())))); + return; + } + if Instant::now() >= until { + shared.send(Cmd::TuneSet(seq, Err(format!("the helper did not run sequence {seq} within {} s (no line in {})", if is_task { 15 } else { 6 }, log_file.display())))); + return; + } + } }); } "amd" => { let Some(exe) = self.bins.telemetry.clone() else { return }; - if c.amd_ordinal < 0 { + // (D) 6 October 2026, PC 1 17:57Z: a measure-only card (the probe gave no tune line) was still sent a + // set, which the helper refused and the tune stopped; measure only means nothing is set, the run + // notices the missing acknowledgement and measures + if !crate::ember::may_set("amd", c.tune_control, c.amd_ordinal) { return; } let n = c.amd_ordinal.to_string(); diff --git a/app/igneum-app/src/powertask.rs b/app/igneum-app/src/powertask.rs index 232ef558..3f22d237 100644 --- a/app/igneum-app/src/powertask.rs +++ b/app/igneum-app/src/powertask.rs @@ -35,6 +35,46 @@ use std::time::{Duration, Instant}; pub const TASK_NAME: &str = "Igneum Power Helper"; /// The helper ends after this long without a new command. pub const IDLE_S: u64 = 20 * 60; +/// The heartbeat file the helper refreshes every poll; an engine judges "the helper runs" by it, never by a flag +/// of its own (6 October 2026, PC 1: the flag said yes after the helper's idle exit, and commands went to nobody). +pub const ALIVE_FILE: &str = "helper.alive"; +/// A heartbeat older than this is a dead helper. +pub const ALIVE_MAX_S: u64 = 4; + +/// Writes the heartbeat: the unix time, as text. +pub fn beat(dir: &Path, now: u64) { + let _ = std::fs::write(dir.join(ALIVE_FILE), now.to_string()); +} + +/// Reads a heartbeat: is the helper alive at `now`? +pub fn alive_at(text: &str, now: u64) -> bool { + text.trim().parse::().map(|t| now.saturating_sub(t) <= ALIVE_MAX_S).unwrap_or(false) +} + +/// Is the helper that reads `dir` alive now? +pub fn alive(dir: &Path) -> bool { + std::fs::read_to_string(dir.join(ALIVE_FILE)).map(|t| alive_at(&t, crate::platform::unix_now())).unwrap_or(false) +} + +/// Starts the task unless the helper already runs, then waits for a fresh heartbeat (up to `wait`). The engine +/// writes a command only after this says Ok: the helper skips what the file held before its start (a stale quit +/// is not a command), so a command written before the start would be skipped with it (6 October 2026, 17:55:25Z +/// on PC 1: the first request of the first tune). +pub fn ensure_running(dir: &Path, wait: Duration) -> Result<(), String> { + if alive(dir) { + return Ok(()); + } + let _ = std::fs::create_dir_all(dir); + start()?; + let until = Instant::now() + wait; + while Instant::now() < until { + std::thread::sleep(Duration::from_millis(250)); + if alive(dir) { + return Ok(()); + } + } + Err(format!("the Igneum Power Helper task gave no heartbeat within {} s of its start", wait.as_secs())) +} /// One parsed command from cmd.txt. #[derive(Clone, Debug, PartialEq, Eq)] @@ -79,6 +119,13 @@ pub fn parse_line(line: &str) -> Option<(u64, HelperCmd)> { } } +/// The commands a helper acts on: the lines added after its start (`skip` = the line count at the start, 0 again +/// when the file shrank). A stale `quit` or `remove` from an earlier engine is never a command. +pub fn commands_after(text: &str, skip: usize) -> Vec<(u64, HelperCmd)> { + let skip = if text.lines().count() < skip { 0 } else { skip }; + text.lines().skip(skip).filter_map(parse_line).collect() +} + /// The nvidia-smi arguments a command becomes (None for the verbs that run nothing). pub fn smi_args(dev: &str, c: &HelperCmd) -> Option> { match c { @@ -195,20 +242,30 @@ pub fn run_helper(dir: &Path) -> i32 { } }; log("helper started (scheduled task, elevated)"); - // a stale file from an earlier run is not a command: only lines after the start count - let mut last_seq: u64 = std::fs::read_to_string(&cmd_file).ok().and_then(|t| t.lines().filter_map(parse_line).map(|(s, _)| s).max()).unwrap_or(0); - let mut last_text = String::new(); + beat(dir, crate::platform::unix_now()); + // a stale file from an earlier run is not a command: only lines ADDED after the start count. 6 October 2026, + // PC 1 17:55:55Z: a helper that started after an engine had written `quit` read that line and exited in the + // same second, and every later start did the same; so the lines present at the start are skipped whole + // (quit and remove included), and a file that shrinks starts the count again + let initial = std::fs::read_to_string(&cmd_file).unwrap_or_default(); + let mut last_seq: u64 = initial.lines().filter_map(parse_line).map(|(s, _)| s).max().unwrap_or(0); + let mut skip = initial.lines().count(); + let mut last_text = initial; let mut dev = "0".to_string(); let mut idle = Instant::now(); let smi = crate::platform::tool("nvidia-smi"); loop { let text = std::fs::read_to_string(&cmd_file).unwrap_or_default(); if text != last_text { + if text.lines().count() < skip { + skip = 0; + } last_text = text.clone(); - for (seq, c) in text.lines().filter_map(parse_line) { + for (seq, c) in commands_after(&text, skip) { match c { HelperCmd::Quit => { log("quit"); + let _ = std::fs::remove_file(dir.join(ALIVE_FILE)); return 0; } HelperCmd::Remove => { @@ -261,8 +318,10 @@ pub fn run_helper(dir: &Path) -> i32 { } if idle.elapsed() >= Duration::from_secs(IDLE_S) { log("idle 20 min: exit (the engine starts the task again when it needs it)"); + let _ = std::fs::remove_file(dir.join(ALIVE_FILE)); return 0; } + beat(dir, crate::platform::unix_now()); std::thread::sleep(Duration::from_millis(500)); } } @@ -296,6 +355,15 @@ mod tests { assert_eq!(parse_line("quit"), Some((0, HelperCmd::Quit))); assert_eq!(parse_line("remove"), Some((0, HelperCmd::Remove))); assert_eq!(parse_line("12 reregister"), Some((12, HelperCmd::Reregister))); + // a stale quit present at the start is skipped; a quit added later counts; a rewritten (shorter) file counts whole + assert_eq!(commands_after("5 dev 0\n6 pl 460\nquit\n", 3), vec![]); + assert_eq!(commands_after("5 dev 0\n6 pl 460\nquit\n7 dev 1\n", 3), vec![(7, HelperCmd::Dev("1".into()))]); + assert_eq!(commands_after("quit\n", 3), vec![(0, HelperCmd::Quit)]); + // the heartbeat: fresh within ALIVE_MAX_S, dead after, dead when unreadable + assert!(alive_at("1791309325", 1791309325 + ALIVE_MAX_S)); + assert!(!alive_at("1791309325", 1791309325 + ALIVE_MAX_S + 1)); + assert!(!alive_at("", 1791309325)); + assert!(!alive_at("soon", 1791309325)); assert_eq!(parse_line("12 reregister C:\\evil.exe"), None, "no path argument: the helper picks the install folder itself"); assert_eq!(smi_args("0", &HelperCmd::Reregister), None); assert!(readback_command().contains("Actions[0].Execute")); diff --git a/docs/bench-log.md b/docs/bench-log.md index 8b81238a..9244b8ac 100644 --- a/docs/bench-log.md +++ b/docs/bench-log.md @@ -2564,3 +2564,12 @@ genesis replay left behind, with `--igneum-exec-snapshot=peer`, sets the pair as A's state with no hand action; its state root at 60 equals A's. `reorg.mjs` 18/18 in 262.1 s on the same binary; the exec suite 20. Consequence: a PC or a box left with the tip-0 pair recovers on its own once a hand serves (the 5-minute no-progress rule fires the ask even when the error text is new); until 0.3.14.1 the recovery file by hand is the way. + +## 6 October 2026, 18:18Z: the shipped 0.3.14 app asked to tune itself on PC 1 (ember-installed-pc1-1): FAILED, no tune ran + +Gate ok (0.3.14, Power control on, the task on the installed exe). Every card's "after" was its 17:55Z failure from +the app's own first tune: 5090 and 4070 "did not take within 30 s (card reports 460 / 160 W, acknowledged true)", +9070 XT "the card refused the setting"; the three "Tune now" requests sat queued behind the hour's back-off. Five +causes (A to E) and their fixes in the plan's 0.3.14 window section; the window repeats on 0.3.15. The cards kept +mining at the driver's held locks: 5090 222.9 W at 1845 MHz, 4070 75.9 W at 1860 MHz, 9070 XT 201 W. Lever-2 TUNING +records during the window: 5090 135.6 MH/s at 236 W (0.574 MH/W), 4070 31.1 MH/s at 79 W (0.393). diff --git a/docs/plans/ember-tune.md b/docs/plans/ember-tune.md index 50487f36..6e806324 100644 --- a/docs/plans/ember-tune.md +++ b/docs/plans/ember-tune.md @@ -296,8 +296,32 @@ record. Known in 0.3.13 on PC 1 until 0.3.14: the rows show the app's own earlie to receive the run's result), and the 80% caps are the app's setting; the draw is the tuned one regardless because the driver holds the clock locks and the caps do not bind. +### The 0.3.14 window (6 October 2026, 18:18 to 18:27Z): no tune ran; five causes + +The shipped 0.3.14 app, Power control on, the task registered, asked over its own API to tune each card +(`relay/playbooks/ember-installed-tune-pc1.ps1`). From its log and helper.log: + +| | When (UTC) | What | Cause | Fix (0.3.15) | +|---|---|---|---|---| +| C | 17:55:25 | the app's own first 5090 tune wrote its first request in the second the helper started; the helper skipped it as "present at my start"; 30 s later "did not take (card reports 460 W, acknowledged true)" | the engine wrote before the helper was up | the helper writes a heartbeat (`helper.alive`) every poll; the engine starts the task and waits for a fresh heartbeat BEFORE writing (`powertask::ensure_running`) | +| A | 17:55:55 | the 4070's helper start read the stale `quit` from the 5090's abort and exited in the same second | quit/remove matched before the sequence check | the helper acts only on lines added after its start (`commands_after`) | +| D | 17:57:27 | the 9070 XT, measure only ("igneum-gpu-telemetry gave no tune line": the 3.0 coordinator's job was on the ADLX helper), was still sent a set; refused; "tuning stopped" | a measure-only card was set | `ember::may_set`: measure only sets nothing | +| E | 18:18 to 18:27 | three "Tune now" requests sat "queued" behind the hour's back-off from the 17:55 failures; none started | `sweep_retry` blocked a forced request silently | a forced request clears the back-off; a held row says "tuning: retry in N min (why)" (`ember::retry_note`) | +| B | (latent) | the engine's helper flag stayed true after the helper's idle exit; a later step would have been acknowledged after a blind 4 s sleep | a flag instead of a fact | the heartbeat decides; the acknowledgement is the helper's own log line for the sequence (up to 15 s), a missing line fails the step with that reason | + +What a user on 0.3.14 sees: Power control on, no prompt, and no card tunes (the rows keep the baseline or +"tuning stopped"); the lever-2 TUNING records still flow. 0.3.15 closes it; the window repeats there. + ## 8a. Next-cut notes (for the 0.3.12 shipper) +For the 0.3.15 cut (6 October 2026 evening), on ember-tune: + +| Commit | What | Where | +|---|---|---| +| a8cab23 | (A) the helper acts only on lines added after its start; the acknowledgement is the helper's log line for the sequence | powertask.rs `commands_after` + test, engine.rs | +| (tip) | (B, C) the heartbeat, `ensure_running` before every task-path write; (D) `may_set`; (E) the back-off yields to a forced request, `retry_note`; the window playbook's gate at 0.3.15; `ember-tune-pc1.ps1` deleted (superseded by the installed-tune playbook) | powertask.rs, engine.rs, ember.rs + tests, relay/playbooks | +| e70ac5e | every task command goes to the helper's fixed folder | platform.rs, powertask.rs, engine.rs, main.rs + test | + For the 0.3.14 cut (6 October 2026, after run 6), on ember-tune: | Commit | What | Where | diff --git a/relay/playbooks/ember-installed-tune-pc1.ps1 b/relay/playbooks/ember-installed-tune-pc1.ps1 index 4426ef81..1848b53b 100644 --- a/relay/playbooks/ember-installed-tune-pc1.ps1 +++ b/relay/playbooks/ember-installed-tune-pc1.ps1 @@ -16,6 +16,11 @@ function Post([string] $path, [string] $json) { try { Invoke-WebRequest -Uri ($b $s = State if (-not $s) { Write-Output 'RESULT TUNE error=state_unreadable'; exit 1 } $ver = [string]$s.version +# the first line: the version gate (main, 6 October 2026): 0.3.14 or later carries bd7fcf4 (AMD apply on ack) and +# 5165de7 (api/tune/goal with the climb); 0.3.15 the helper fixes A to E (6 October 2026); an older app aborts here +$vok = $false; try { $vok = ([version]($ver -replace '[^0-9.].*$', '')) -ge [version]'0.3.15' } catch { } +Write-Output ('RESULT TUNE version=' + $ver + ' gate=' + $(if ($vok) { 'ok (0.3.15 or later)' } else { 'FAILED (needs 0.3.15: the helper fixes A to E)' }) + ' climb_field=' + $(if ($null -ne $s.settings.tune_climb) { 'present' } else { 'absent' })) +if (-not $vok -or $null -eq $s.settings.tune_climb) { Write-Output 'RESULT TUNE error=version_gate'; exit 1 } Write-Output ('RESULT TUNE installed app ' + $ver + ' power_control=' + $s.settings.power_control + ' tuning_off=' + $s.settings.tuning_off + ' climb_before=' + $s.settings.tune_climb + ' goal_before=' + $s.settings.tune_goal) $t = Get-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue Write-Output ('RESULT TUNE task ' + $(if ($t) { $t.Actions[0].Execute + ' state=' + $t.State } else { 'NOT REGISTERED' })) @@ -30,10 +35,13 @@ if (-not (Post 'api/tune/goal' '{"goal":"balanced","climb":true}')) { Write-Outp Start-Sleep -Seconds 2 $order = @($cards | Where-Object { $_.vendor -eq 'amd' }) + @($cards | Where-Object { $_.vendor -eq 'nvidia' }) $deadline = (Get-Date).AddMinutes(27) -$logs = Join-Path $appDir 'logs' +$logs = Join-Path $env:LOCALAPPDATA 'igneum\logs' # the app's log folder is \logs (platform::log_dir), not app\logs (6 October 2026: the first window read no log at all) function LogMark() { $l = Get-ChildItem -Path $logs -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1; if ($l) { return @{ f = $l.FullName; n = (Get-Content -LiteralPath $l.FullName).Count } } else { return @{ f = ''; n = 0 } } } function LogSince($m) { if (-not $m.f) { return @() }; $all = @(Get-Content -LiteralPath $m.f -ErrorAction SilentlyContinue); if ($all.Count -le $m.n) { return @() }; return @($all[$m.n..($all.Count - 1)]) } $rows = 0 +$m0 = LogMark +# run 6's ladder floors (6 October 2026, PC 1), what the climb has to beat +$floor = @{ 'NVIDIA GeForce RTX 5090' = @{ eff = 0.5632; mhs = 127.71; watts = 226.8; point = '1854 MHz at 100%' }; 'NVIDIA GeForce RTX 4070' = @{ eff = 0.3809; mhs = 28.78; watts = 75.6; point = '1863 MHz at 50%' } } foreach ($c in $order) { if ((Get-Date) -gt $deadline) { Say 'budget spent before ' + $c.key; break } $m = LogMark @@ -58,6 +66,24 @@ foreach ($c in $order) { $st = State if ($st) { $cc = $st.mining.cards | Where-Object { $_.key -eq $c.key } | Select-Object -First 1; if ($cc) { Write-Output ('RESULT TUNE after key=' + $c.key + ' power_w=' + $cc.power_w + ' power_pct=' + $cc.power_pct + ' clock_cap=' + $cc.clock_cap_mhz + ' line=' + $cc.tune_line + ' source=' + $cc.tune_source + ' note=' + $cc.sweep_note); if ($cc.tune_curve) { Write-Output ('RESULT TUNE curve key=' + $c.key + ' ' + (($cc.tune_curve | ForEach-Object { ($_.clock_mhz) + 'MHz/' + ($_.mem_mhz) + 'mem/' + ($_.power_pct) + '%:' + $_.mhs + 'MH/s ' + $_.watts + 'W ' + $_.eff + ' ' + $_.mark }) -join ' | ')) } } } } +# the prompt count: the app's own log over the window; a task line says "(no prompt)", a prompted helper says +# "administrator prompt" (the Security log's process audit is off on PC 1, so the app's log is the record) +$win = @(LogSince $m0) +$noPrompt = @($win | Where-Object { $_ -match 'Power Helper task \(no prompt\)' }).Count +$prompted = @($win | Where-Object { $_ -match 'administrator prompt|runas|UAC' }).Count +Write-Output ('RESULT TUNE prompts=' + $prompted + ' task_lines=' + $noPrompt + ' (from the app log over the window)') +# the climb against run 6's ladder floor, per NVIDIA card +$st = State +if ($st) { + foreach ($c in @($cards | Where-Object { $_.vendor -eq 'nvidia' })) { + $cc = $st.mining.cards | Where-Object { $_.key -eq $c.key } | Select-Object -First 1 + $f = $floor[[string]$c.name] + if ($cc -and $f -and $cc.sweep_eff -gt 0) { + $d = [math]::Round(($cc.sweep_eff - $f.eff) / $f.eff * 100, 1) + Write-Output ('RESULT TUNE versus_run6 key=' + $c.key + ' climb=' + $cc.sweep_eff + ' MH/W (' + $cc.sweep_mhs + ' MH/s at ' + $cc.sweep_watts + ' W) ladder_floor=' + $f.eff + ' (' + $f.point + ') delta=' + $d + '% ' + $(if ($cc.sweep_eff -gt $f.eff) { 'the climb beat the floor' } elseif ($cc.sweep_eff -eq $f.eff) { 'equal' } else { 'the ladder floor stands' })) + } elseif ($cc -and $f) { Write-Output ('RESULT TUNE versus_run6 key=' + $c.key + ' no chosen point this run (' + $cc.sweep_note + ')') } + } +} # the climb switch back to what it was Post 'api/tune/goal' ('{"climb":' + $(if ($climbBefore) { 'true' } else { 'false' }) + '}') | Out-Null Write-Output ('RESULT TUNE climb_restored=' + $climbBefore + ' rows=' + $rows) diff --git a/relay/playbooks/ember-log-grep-pc1.ps1 b/relay/playbooks/ember-log-grep-pc1.ps1 new file mode 100644 index 00000000..118661bf --- /dev/null +++ b/relay/playbooks/ember-log-grep-pc1.ps1 @@ -0,0 +1,12 @@ +# Ember: reads only. The installed app's own tune lines (not the job echoes) from \logs, newest file: +# the engine's "tune:" requests and answers, "tune helper", TUNE start/aborted/chosen/rows, and the helper.log tail. +$ErrorActionPreference = 'Continue' +$l = Get-ChildItem (Join-Path $env:LOCALAPPDATA 'igneum\logs') -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 +if (-not $l) { Write-Output 'RESULT GREP no app log'; exit 1 } +Write-Output ('RESULT GREP log=' + $l.FullName) +Get-Content -LiteralPath $l.FullName | Where-Object { ($_ -match '\] tune|^\d+ tune|tune helper|TUNE (start|aborted|chosen|card=|helper|probe)|refused|did not take|power cap|Power Helper|probe|telemetry|gfx1201|queued|tune due|not due') -and ($_ -notmatch 'job ember-|RESULT ') } | Select-Object -Last 80 | ForEach-Object { Write-Output ('RESULT GREP ' + $_) } +$h = Join-Path $env:LOCALAPPDATA 'igneum\app\sweep\helper.log' +if (Test-Path -LiteralPath $h) { Get-Content -LiteralPath $h | Select-Object -Last 12 | ForEach-Object { Write-Output ('RESULT GREP helper ' + $_) } } +$c = Join-Path $env:LOCALAPPDATA 'igneum\app\sweep\cmd.txt' +if (Test-Path -LiteralPath $c) { Write-Output ('RESULT GREP cmd.txt: ' + ((Get-Content -LiteralPath $c) -join ' / ')) } +exit 0 diff --git a/relay/playbooks/ember-proof-collect-pc1.ps1 b/relay/playbooks/ember-proof-collect-pc1.ps1 index 3db3b7e8..91fb11a1 100644 --- a/relay/playbooks/ember-proof-collect-pc1.ps1 +++ b/relay/playbooks/ember-proof-collect-pc1.ps1 @@ -3,10 +3,10 @@ # last two hours from the Security log. Never writes, quits, pauses or resumes anything. $ErrorActionPreference = 'Continue' $a = Join-Path $env:LOCALAPPDATA 'igneum\app' -$l = Get-ChildItem (Join-Path $a 'logs') -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 +$l = Get-ChildItem (Join-Path $env:LOCALAPPDATA 'igneum\logs') -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if ($l) { Write-Output ('RESULT PROOF log=' + $l.FullName) - Get-Content -LiteralPath $l.FullName | Where-Object { $_ -match 'power cap|Power Helper|elevat|UAC|runas|tune' } | Select-Object -Last 24 | ForEach-Object { Write-Output ('RESULT PROOF applog ' + $_) } + Get-Content -LiteralPath $l.FullName | Where-Object { $_ -match 'power cap|Power Helper|elevat|UAC|runas|tune' } | Select-Object -Last 60 | ForEach-Object { Write-Output ('RESULT PROOF applog ' + $_) } } $h = Join-Path $a 'sweep\helper.log' if (Test-Path -LiteralPath $h) { Get-Content -LiteralPath $h | Select-Object -Last 15 | ForEach-Object { Write-Output ('RESULT PROOF helper ' + $_) } } else { Write-Output 'RESULT PROOF helper none' }