[lldb] Synchronize the debuggers output & error streams

This patch improves the synchronization of the debugger's output and error
streams using two new abstractions: `LockableStreamFile` and
`LockedStreamFile`.

 - `LockableStreamFile` is a wrapper around a `StreamFile` and a mutex. Client
   cannot use the `StreamFile` without calling `Lock`, which returns a
   `LockedStreamFile`.

 - `LockedStreamFile` is an RAII object that locks the stream for the duration
   of its existence.  As long as you hold on to the returned object you are
   permitted to write to the stream. The destruction of the object
   automatically flush the output stream.
This commit is contained in:
Jonas Devlieghere
2025-02-19 20:32:00 -08:00
committed by GitHub
parent 557628dbe6
commit 58279d1ee1
23 changed files with 472 additions and 314 deletions

View File

@@ -7,7 +7,10 @@
//===----------------------------------------------------------------------===//
#include "lldb/Host/Config.h"
#include "lldb/Host/StreamFile.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
#include <locale>
#if LLDB_ENABLE_PYTHON
@@ -489,11 +492,11 @@ def function (frame, bp_loc, internal_dict):
break;
}
if (instructions) {
StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
if (output_sp && interactive) {
output_sp->PutCString(instructions);
output_sp->Flush();
if (instructions && interactive) {
if (LockableStreamFileSP stream_sp = io_handler.GetOutputStreamFileSP()) {
LockedStreamFile locked_stream = stream_sp->Lock();
locked_stream.PutCString(instructions);
locked_stream.Flush();
}
}
}
@@ -527,10 +530,9 @@ void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler,
bp_options.SetCallback(
ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp);
} else if (!batch_mode) {
StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
if (error_sp) {
error_sp->Printf("Warning: No command attached to breakpoint.\n");
error_sp->Flush();
if (LockableStreamFileSP error_sp = io_handler.GetErrorStreamFileSP()) {
LockedStreamFile locked_stream = error_sp->Lock();
locked_stream.Printf("Warning: No command attached to breakpoint.\n");
}
}
}
@@ -550,10 +552,9 @@ void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler,
wp_options->SetCallback(
ScriptInterpreterPythonImpl::WatchpointCallbackFunction, baton_sp);
} else if (!batch_mode) {
StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
if (error_sp) {
error_sp->Printf("Warning: No command attached to breakpoint.\n");
error_sp->Flush();
if (LockableStreamFileSP error_sp = io_handler.GetErrorStreamFileSP()) {
LockedStreamFile locked_stream = error_sp->Lock();
locked_stream.Printf("Warning: No command attached to breakpoint.\n");
}
}
m_active_io_handler = eIOHandlerNone;
@@ -680,7 +681,7 @@ bool ScriptInterpreterPythonImpl::EnterSession(uint16_t on_entry_flags,
PythonDictionary &sys_module_dict = GetSysModuleDictionary();
if (sys_module_dict.IsValid()) {
lldb::FileSP top_in_sp;
lldb::StreamFileSP top_out_sp, top_err_sp;
lldb::LockableStreamFileSP top_out_sp, top_err_sp;
if (!in_sp || !out_sp || !err_sp || !*in_sp || !*out_sp || !*err_sp)
m_debugger.AdoptTopIOHandlerFilesIfInvalid(top_in_sp, top_out_sp,
top_err_sp);
@@ -696,12 +697,14 @@ bool ScriptInterpreterPythonImpl::EnterSession(uint16_t on_entry_flags,
if (!SetStdHandle(out_sp, "stdout", m_saved_stdout, "w")) {
if (top_out_sp)
SetStdHandle(top_out_sp->GetFileSP(), "stdout", m_saved_stdout, "w");
SetStdHandle(top_out_sp->GetUnlockedFileSP(), "stdout", m_saved_stdout,
"w");
}
if (!SetStdHandle(err_sp, "stderr", m_saved_stderr, "w")) {
if (top_err_sp)
SetStdHandle(top_err_sp->GetFileSP(), "stderr", m_saved_stderr, "w");
SetStdHandle(top_err_sp->GetUnlockedFileSP(), "stderr", m_saved_stderr,
"w");
}
}