1. 求解器无输出且进程静默退出:定位入口参数与日志断点
现象拆解:静默退出发生在哪一层
求解器启动后终端无任何输出、进程以非零或零码直接退出,这类现象的本质是可观测性在某个环节被切断。常见切断点有三类:命令行参数解析阶段提前 exit、日志系统尚未初始化就发生异常、异常被过宽的 catch 边界吞掉。排障目标不是猜原因,而是按启动顺序逐层确认“最后一个有输出的位置”和“第一个失效的位置”。
第一步:用参数解析自检确认入口是否放行
先排除“参数没被正确接收”这一层。以常见的 C++ 求解器入口为例,在解析完成后立即向 stderr 打印解析结果,不依赖日志系统,因为此时日志可能还没初始化:
int main(int argc, char** argv) {
Options opt;
try {
opt = parseArgs(argc, argv); // 可能抛异常或内部 exit
} catch (const std::exception& e) {
std::fprintf(stderr, "[FATAL] parseArgs: %s\n", e.what());
return 2;
}
std::fprintf(stderr, "[TRACE] model=%s tol=%g log=%s\n",
opt.modelPath.c_str(), opt.tol, opt.logPath.c_str());
// 后续日志初始化……
}
若连 [TRACE] 都看不到,说明进程在 parseArgs 内部就退出了。此时重点检查:是否有 std::exit() 被直接调用、是否在参数缺失时静默 return、是否 --help 分支误触发。把 parseArgs 中所有提前返回点改为抛出带消息的异常,是恢复可观测性的最小改动。
第二步:核对日志初始化顺序与可写性
日志初始化失败是“有参数、无输出”的高频原因。典型错误顺序是:先打开日志文件、再校验路径,或日志对象构造失败后异常被上层吞掉。正确的初始化应满足三点:
- 日志路径在打开前先做存在性与可写性检查,失败时写
stderr而非静默返回; - 日志初始化完成后立即写一条
[TRACE] logger ready并flush(),作为 B2 断点; - 日志级别默认不低于
INFO,避免--quiet类参数把全部输出压掉。
bool initLogger(const std::string& path) {
std::ofstream probe(path, std::ios::app);
if (!probe) {
std::fprintf(stderr, "[FATAL] log not writable: %s\n", path.c_str());
return false;
}
probe.close();
Logger::instance().open(path);
Logger::instance().setLevel(Level::INFO);
LOG_INFO("logger ready, path=%s", path.c_str());
Logger::instance().flush(); // 关键:确保断点可见
return true;
}
第三步:收紧异常捕获边界,让首个失效点自己说话
静默退出的最后一层原因是过宽的 catch。检查主流程中是否存在 catch (...) { return -1; } 这类写法。修复原则是:捕获后必须至少向 stderr 输出异常类型与 what(),并在返回前 flush 所有日志 sink。对模型加载段,建议单独包裹并标注阶段名:
try {
model = loadModel(opt.modelPath);
LOG_INFO("model loaded: vars=%zu cons=%zu", model.numVars(), model.numCons());
} catch (const std::exception& e) {
LOG_ERROR("stage=loadModel path=%s err=%s", opt.modelPath.c_str(), e.what());
Logger::instance().flush();
std::fprintf(stderr, "[FATAL] loadModel: %s\n", e.what());
return 3;
}
--help/--version 分支被误命中,或日志级别被配置为 OFF。退出码非零但无消息,则优先检查 catch 吞异常与日志未 flush 两种情况。验证恢复效果
完成上述三处修改后,正常启动应依次看到 [TRACE] model=...、logger ready、model loaded: vars=... cons=... 三条输出。若仍静默,用 strace -f -e trace=openat,write ./solver ... 观察是否在打开日志或模型文件时即失败,从而把问题从“代码逻辑”收敛到“文件系统权限或路径”。