[Lldb-commits] [lldb] [lldb] Synchronize the debugger's stdout and stderr streams (PR #126630)
Pavel Labath via lldb-commits
lldb-commits at lists.llvm.org
Fri Feb 14 02:05:09 PST 2025
labath wrote:
> > I do want to spend some time discussing the relationship between stdout and stderr. The current implementation uses a separate mutex for each stream, which is not _un_reasonable, but I can see at least two other possible philosophies:
> >
> > 1. Since both of the streams will be going to the same terminal most of the time, it might make sense to use a single mutex for both of them, as that's sort of the only way to ensure consistent output. (Imagine the situation of printing the "stderr" of a CLI command while the status line is being updated)
> > 2. Alternatively, we could say that stderr is for immediate and exceptional output where it is more important to get the message to the user than it being formatted beautifully. In this world, we could keep "stderr" as a regular unlocked stream. In specific cases, where synchronizing the output is particularly important to us (like the beforementioned case of "stderr" of a CLI command) we could synchronize its output by holding the "stdout" mutex while writing it.
> >
> > What do you think of that? I'm sort of leaning towards option two since stderr might be used in contexts where holding a mutex might not be completely safe (and the first option would make that even worse), and the current implementation with separate mutexes doesn't really guarantee "reasonable" stdout/err sequencing.
>
> I had a feeling this was going to come up... The tricky part is that, as you pointed out, most of the time the output is going to same terminal. But on the other hand, we totally support having those two streams go to totally separate files and it feels wrong to force them to be synchronized. How about a compromise between (1) and (2) where we use the same mutex when the debugger is creating the StreamFile for stdout and stderr, but we use separate murexes when they're set through the setter (and the SB API)?
How would that work? We'd check the value of the passed-in `FILE*` and choose a mutex based on whether it happens to be `stdout` ? I don't think I'd like that for several reasons:
- it doesn't handle the case where `stdout/err` is redirected externally (it still locks even though it shouldn't)
- it's doesn't let you create a terminal-like experience when embedding lldb-as-a-library (it doesn't lock even though it should)
> Conceptually I'd also lean towards (2) but I worry about losing the benefits the current approach brings. Deciding whether "the output is particularly important" is a judgement call that we can't enforce at the API level. I like how the current implementation makes it easy to do the right thing and hard to do the wrong thing (or at least make you think about locking). If the default is to not lock the stdout (the alternative of always locking is equivalent to (1)) then I'm skeptical that we won't just end up with the same outcome that we have with two mutexes.
But with two mutexes you still don't get stdout<=>stderr synchronization, which means that stderr output can corrupt things you're writing to stdout. What the stderr mutex buys you is stderr<=>stderr synchronization, which is... maybe nice, but maybe also not necessary?
FWIW, there is some precedent for unconditional stdout-stderr interaction: writes to `std::cerr` will automatically flush `std::cout` regardless of whether the two streams refer to the same terminal. And the `iostream` library allows you to connect arbitrary two streams in this way. So, I think we *could* say we "tie" debuggers output and error streams in this way, even if they are completely independent, because they are "our" stdout and stderr.
https://github.com/llvm/llvm-project/pull/126630
More information about the lldb-commits
mailing list