fix(m188): r1 复审 P2-1——后端启动失败的具名原因进 launcher.log
复审结论 p2-1/1 item + fix-then-merge:主线成立,但「启动器自己因 GPU / 渲染后端起不来」的 事故原因仍只进 stderr —— 双击启动没有 stderr,那条路径上等于没有日志。 - renderer/mod.rs::report_failure 的两行(`[gpu] <backend> 初始化失败(具名)` / `[gpu] 失败详情(具名)`)改走 diag!; - renderer/log_bridge.rs 转出的 wgpu 日志(含 `…Selected: AdapterInfo…`,GPU 真生效的唯一证据 行)改走 diag!; - renderer/preflight.rs 的 `[gpu-preflight]`(ULL_PREFLIGHT=1)改走 diag!; - probe.rs::apply_with_preflight 的三行 `[probe]` 改走 diag!。 - app/diag.rs 模块文档 + docs/logs-location.md §3 的判据改准: 「逐帧度量不接;显式取证开关与一次性事故行可接」,并修掉 `[corner]` 两处自相矛盾 (唯一打印点在 app/update/appearance.rs,走 diag! ⇒ 进日志;corner.rs 只 format_line)。 - 新守门 renderer::tests::a_backend_startup_failure_lands_in_the_launcher_log: 断言那两行真的落进 launcher.log;变异(改回纯 eprintln)→ 红。 - README 两语的 launcher.log 行补上「渲染后端起不来的具名原因」。
This commit is contained in:
1 parent
5fd5cd47dc
commit
8c5bebf3e3
9 files changed
+87
-27
No files matched your search
@@ -88,7 +88,9 @@ impl log::Log for Bridge {
|
||||
}
|
||||
// iced_wgpu 用 `{:#?}` 打多行结构体;压成一行才好 grep / 贴验收报告。
|
||||
let message = record.args().to_string().replace('\n', " ");
|
||||
eprintln!("[gpu] {}: {}", record.target(), message.trim_end());
|
||||
// M188 复审 P2-1:`[gpu] iced_wgpu: …Selected: AdapterInfo…` 是「GPU 真生效」的**唯一**
|
||||
// 证据行,也是「为什么降级了」的第一现场,必须进 `<.ULL>/logs/launcher.log`。
|
||||
crate::app::diag::diag!("[gpu] {}: {}", record.target(), message.trim_end());
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
|
||||
@@ -199,9 +199,12 @@ pub fn forced_fallback() -> Option<Kind> {
|
||||
/// `iced::Error::GraphicsCreationFailed` 的 `Display` **不包含**内部错误
|
||||
/// (`#[error("the application graphics context could not be created")]`,没有 `{0}`),
|
||||
/// 所以这里必须显式把内层错误打出来,否则又变回「只知道失败,不知道为什么」。
|
||||
///
|
||||
/// **M188 复审 P2-1**:这两行是**事故本体**,必须同时进 `<.ULL>/logs/launcher.log`——
|
||||
/// 双击启动的 GUI 进程没有 stderr,只打 `eprintln!` 等于「最该有日志的那条路径上没有日志」。
|
||||
pub fn report_failure(backend: Kind, error: &(impl std::fmt::Display + std::fmt::Debug)) {
|
||||
eprintln!("[gpu] {} 初始化失败(具名):{error}", backend.name());
|
||||
eprintln!("[gpu] 失败详情(具名):{error:?}");
|
||||
crate::app::diag::diag!("[gpu] {} 初始化失败(具名):{error}", backend.name());
|
||||
crate::app::diag::diag!("[gpu] 失败详情(具名):{error:?}");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -246,4 +249,44 @@ mod tests {
|
||||
});
|
||||
assert_eq!(calls, 1, "进程内只许跑一次;重试只能换进程");
|
||||
}
|
||||
|
||||
/// **M188 复审 P2-1 的守门**:后端起不来那两行必须进 `<.ULL>/logs/launcher.log`,不只是 stderr。
|
||||
///
|
||||
/// 为什么这条最要紧:双击启动的 GUI 进程**没有 stderr 去向**,而「GPU / 渲染后端起不来」
|
||||
/// 正是最需要事后复盘的一类事故。只打 `eprintln!` 等于在这条路径上什么都没留下——
|
||||
/// 用户那句「崩了但只有一个安装日志」会在最该有日志的地方继续成立。
|
||||
///
|
||||
/// 抓得住:`report_failure` 退回纯 `eprintln!`(变异证明:改回去本条立刻红)。
|
||||
/// 抓不住:真机上 iced 起不来的样子(那要真 GPU/驱动,见报告「没验的部分」)。
|
||||
#[test]
|
||||
fn a_backend_startup_failure_lands_in_the_launcher_log() {
|
||||
use crate::app::diag::LOG_STATE_LOCK;
|
||||
use unlucky_core::launch::launcher_log;
|
||||
|
||||
// `launcher_log` 是进程级单例:与 `app/diag.rs` 的滚动闸、`app/dialog/tests.rs` 的启动事实
|
||||
// 用例串行(同一个 `LOG_STATE_LOCK`)。
|
||||
let _guard = LOG_STATE_LOCK
|
||||
.lock()
|
||||
.unwrap_or_else(|error| error.into_inner());
|
||||
let dir = std::env::temp_dir().join(format!("ull-m188-gpu-fail-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).expect("建夹具目录");
|
||||
launcher_log::set_dir(&dir);
|
||||
|
||||
report_failure(Kind::Wgpu, &"no suitable adapter (host: test)".to_string());
|
||||
|
||||
let path = dir.join(launcher_log::FILE_NAME);
|
||||
let text = std::fs::read_to_string(&path).expect("launcher.log");
|
||||
assert!(
|
||||
text.contains("[gpu] wgpu 初始化失败(具名):no suitable adapter"),
|
||||
"具名原因没进日志:{text}"
|
||||
);
|
||||
assert!(
|
||||
text.contains("[gpu] 失败详情(具名):"),
|
||||
"失败详情没进日志:{text}"
|
||||
);
|
||||
// 取证:`cargo test … -- --nocapture` 时把真实落盘内容打出来(报告/复审可复核)。
|
||||
println!("launcher.log:\n{text}");
|
||||
std::fs::remove_dir_all(&dir).ok();
|
||||
}
|
||||
}
|
||||
@@ -76,10 +76,14 @@ pub fn run_if_requested() -> Option<Result<Verdict, Failure>> {
|
||||
let started = std::time::Instant::now();
|
||||
let verdict = run();
|
||||
let took = started.elapsed().as_secs_f64() * 1000.0;
|
||||
// M188:显式开关(`ULL_PREFLIGHT=1`)下的一次性行,按 `app::diag` 的判据可以进
|
||||
// `launcher.log`——用户开这个开关就是为了拿到「为什么起不来」的证据。
|
||||
match &verdict {
|
||||
Ok(ok) => eprintln!("[gpu-preflight] {} took={took:.0}ms", ok.describe()),
|
||||
Ok(ok) => {
|
||||
crate::app::diag::diag!("[gpu-preflight] {} took={took:.0}ms", ok.describe());
|
||||
}
|
||||
Err(failure) => {
|
||||
eprintln!("[gpu-preflight] {} took={took:.0}ms", failure.describe());
|
||||
crate::app::diag::diag!("[gpu-preflight] {} took={took:.0}ms", failure.describe());
|
||||
}
|
||||
}
|
||||
Some(verdict)
|
||||
|
||||
Reference in new issue
Block a user