-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make server panic hook more error resilient #12610
Conversation
4d02556
to
98ec2fe
Compare
98ec2fe
to
16b0a9d
Compare
16b0a9d
to
1ff2f9b
Compare
// This communicates that this isn't a linter error but ruff itself hard-errored for | ||
// some reason (e.g. failed to resolve the configuration) | ||
eprintln!("{}", "ruff failed".red().bold()); | ||
writeln!(stderr, "{}", "ruff failed".red().bold()).ok(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the original trace, I strongly suspect that ruff panics here... when trying to print the reason why ruff errored:
failed printing to stderr: Broken pipe (os error 32)
0: ruff_server::message::init_messenger::{{closure}}
1: std::panicking::rust_panic_with_hook
2: std::panicking::begin_panic_handler::{{closure}}
3: std::sys_common::backtrace::__rust_end_short_backtrace
4: rust_begin_unwind
5: core::panicking::panic_fmt
6: std::io::stdio::_eprint
7: ruff::main
8: std::sys_common::backtrace::__rust_begin_short_backtrace
9: std::rt::lang_start::{{closure}}
10: std::rt::lang_start_internal
11: main
12: <unknown>
13: __libc_start_main
14: <unknown>
|
let mut stderr = std::io::stderr().lock(); | ||
writeln!(stderr, "{panic_info}\n{backtrace}").ok(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main change is to use writeln
here
|
||
// unregister any previously registered panic hook | ||
let _ = std::panic::take_hook(); | ||
|
||
// When we panic, try to notify the client. | ||
std::panic::set_hook(Box::new(move |panic_info| { | ||
if let Some(messenger) = MESSENGER.get() { | ||
let _ = messenger.send(lsp_server::Message::Notification( | ||
lsp_server::Notification { | ||
method: lsp_types::notification::ShowMessage::METHOD.into(), | ||
params: serde_json::to_value(lsp_types::ShowMessageParams { | ||
typ: lsp_types::MessageType::ERROR, | ||
message: String::from( | ||
"The Ruff language server exited with a panic. See the logs for more details." | ||
), | ||
}) | ||
.unwrap_or_default(), | ||
}, | ||
)); | ||
} | ||
|
||
let backtrace = std::backtrace::Backtrace::force_capture(); | ||
tracing::error!("{panic_info}\n{backtrace}"); | ||
#[allow(clippy::print_stderr)] | ||
{ | ||
// we also need to print to stderr directly in case tracing hasn't | ||
// been initialized. | ||
eprintln!("{panic_info}\n{backtrace}"); | ||
} | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to move this out of Messenger to ensure that we unset the panic hook when the server shuts down. We may consider using catch_unwind
on a per request/response model in the future. But I think that's good enough for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems reasonable although I've never faced this panic.
Summary
eprintln
can panic when the stderr pipe is broken which is very unfortunate if it happens in the server's panic hook.This PR uses
writeln
with.ok()
as a more forgiving way to write the error message.This should fix #12545 although it is still unclear to me how to get into the broken pipe case.
Test Plan
I added a panic in the diagnostics request: