-
Notifications
You must be signed in to change notification settings - Fork 264
Add format_trace(impl fmt::Write, PrintFmt)
#265
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
Conversation
Servo has a signal handler that prints a backtrace on segfaults. Currently it uses |
This is similar to `write!(stream, "{:?}", Backtrace::new())`, but avoids memory allocation. This can be useful in a signal handler, where allocation may cause deadlocks.
|
Avoid allocation in the signal handler to fix a deadlock Fixes #24881 CC rust-lang/backtrace-rs#265
Avoid allocation in the signal handler to fix a deadlock Fixes #24881 CC rust-lang/backtrace-rs#265
Avoid allocation in the signal handler to fix a deadlock Fixes #24881 CC rust-lang/backtrace-rs#265
Avoid allocation in the signal handler to fix a deadlock Fixes #24881 CC rust-lang/backtrace-rs#265
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.
Sounds like a reasonable feature to me, thanks for this!
/// | ||
/// This is similar to `write!(stream, "{:?}", Backtrace::new())`, | ||
/// but avoids memory allocation. | ||
/// This can be useful in a signal handler, where allocation may cause deadlocks. |
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 unfortunately technically isn't quite true because the underlying implementation almost always allocates memory in one way or another. While this avoids all Rust-based allocations, the backtrace unwinder/etc, if called for the first time will allocate memory here.
In general nothing in this crate is suitable for usage in a signal handler right now unfortunately.
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 see.
For what it’s worth using something like this PR instead of Backtrace::new()
in Servo’s signal handler does seem to fix a deadlock, presumably through reducing the number of allocations. (Or at least I couldn’t reproduce it after running the test case a hundred times, whereas with Backtrace::new()
I would reliably reproduce within ten runs.) But it’s quite possible that there’s still another deadlock waiting to happen and maybe we should remove that handler entirely.
I’ll remove mention of signal handler in these doc-comments. What else do you think could be a good use case for this new feature? In theory this is doing less work and might be more efficient than creating a Backtrace
only to print it and immediately drop it, but I don’t know if the difference has a practical impact.
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.
Yes it's just a deadlock waiting to happen. The underlying implementation of backtraces are not at all guaranteed to not malloc, but they try not to to be efficient. Backtrace::new()
is guaranteed to allocate, where this API as-is just allocates some of the time, not all the time.
Getting a backtrace from a signal handler is a really tricky thing to do and generally not covered by this crate.
TBH I'm not sure what a use case for this feature is, folks are generally ok with the mild cost that Backtrace::new
has
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.
Right, my motivation for this PR was not the CPU time cost but trading a deadlock I could reliably reproduce for a possible one I haven’t seen happen. (Knowing this still isn’t a fully correct solution.)
If the only use case for this is incorrect and something the project doesn’t want to cover, maybe it shouldn’t be included? Even though based on what this code does it seems like within this crate would be the right location.
I’m OK with closing this PR in that case.
/// This function strives to never panic, but if the `cb` provided panics then | ||
/// some platforms will force a double panic to abort the process. Some | ||
/// platforms use a C library which internally uses callbacks which cannot be | ||
/// unwound through, so panicking from `cb` may trigger a process abort. |
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.
Could the docs here include an example of how to call this?
/// platforms use a C library which internally uses callbacks which cannot be | ||
/// unwound through, so panicking from `cb` may trigger a process abort. | ||
#[cfg(feature = "std")] | ||
#[inline(never)] |
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.
Mind throwing a small comment on this explaining why it's inline(never)
?
|
||
impl<W: std::io::Write> std::fmt::Write for Utf8<W> { | ||
fn write_str(&mut self, s: &str) -> Result<(), std::fmt::Error> { | ||
self.0.write_all(s.as_bytes()).map_err(|_| std::fmt::Error) |
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.
Mind backing this out by the time this lands?
Useful for context though!
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.
Do you mean removing changes to this file entirely? Or should they be a separate example program?
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.
Oh just in the sense that this example file was intended to just print one backtrace, but this might make a good # Examples
section for the new API?
Ok I'm gonna close this due to the discussion at #265 (comment), but perhaps clarifying in the README that this is not appropriate for usage in signal handlers would be good to indicate! |
This is similar to
write!(stream, "{:?}", Backtrace::new())
, but avoids memory allocation. This can be useful in a signal handler, where allocation may cause deadlocks.