Skip to content

Commit 242c574

Browse files
committed
[lldb] Synchronize output through the IOHandler
Add synchronization to the IOHandler to prevent multiple threads from writing concurrently to the output or error stream. A scenario where this could happen is when a thread (the default event thread for example) is using the debugger's asynchronous stream. We would delegate this operation to the IOHandler which might be running on another thread. Until this patch there was nothing to synchronize the two at the IOHandler level. Differential revision: https://reviews.llvm.org/D121500
1 parent a6ec1e3 commit 242c574

File tree

7 files changed

+38
-19
lines changed

7 files changed

+38
-19
lines changed

lldb/include/lldb/Core/IOHandler.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,14 @@ class IOHandler {
165165

166166
virtual void PrintAsync(const char *s, size_t len, bool is_stdout);
167167

168+
std::mutex &GetOutputMutex() { return m_output_mutex; }
169+
168170
protected:
169171
Debugger &m_debugger;
170172
lldb::FileSP m_input_sp;
171173
lldb::StreamFileSP m_output_sp;
172174
lldb::StreamFileSP m_error_sp;
175+
std::mutex m_output_mutex;
173176
repro::DataRecorder *m_data_recorder;
174177
Predicate<bool> m_popped;
175178
Flags m_flags;

lldb/include/lldb/Host/Editline.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ using namespace line_editor;
154154
class Editline {
155155
public:
156156
Editline(const char *editor_name, FILE *input_file, FILE *output_file,
157-
FILE *error_file, bool color_prompts);
157+
FILE *error_file, std::mutex &output_mutex, bool color_prompts);
158158

159159
~Editline();
160160

@@ -402,7 +402,7 @@ class Editline {
402402
std::string m_suggestion_ansi_suffix;
403403

404404
std::size_t m_previous_autosuggestion_size = 0;
405-
std::mutex m_output_mutex;
405+
std::mutex &m_output_mutex;
406406
};
407407
}
408408

lldb/include/lldb/Interpreter/CommandInterpreter.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,8 @@ class CommandInterpreter : public Broadcaster,
655655
const CommandObject::CommandMap &command_map);
656656

657657
// An interruptible wrapper around the stream output
658-
void PrintCommandOutput(Stream &stream, llvm::StringRef str);
658+
void PrintCommandOutput(IOHandler &io_handler, llvm::StringRef str,
659+
bool is_stdout);
659660

660661
bool EchoCommandNonInteractive(llvm::StringRef line,
661662
const Flags &io_handler_flags) const;

lldb/source/Core/IOHandler.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); }
123123
void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); }
124124

125125
void IOHandler::PrintAsync(const char *s, size_t len, bool is_stdout) {
126+
std::lock_guard<std::mutex> guard(m_output_mutex);
126127
lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
127128
stream->Write(s, len);
128129
stream->Flush();
@@ -266,9 +267,9 @@ IOHandlerEditline::IOHandlerEditline(
266267
m_input_sp && m_input_sp->GetIsRealTerminal();
267268

268269
if (use_editline) {
269-
m_editline_up = std::make_unique<Editline>(editline_name, GetInputFILE(),
270-
GetOutputFILE(), GetErrorFILE(),
271-
m_color_prompts);
270+
m_editline_up = std::make_unique<Editline>(
271+
editline_name, GetInputFILE(), GetOutputFILE(), GetErrorFILE(),
272+
GetOutputMutex(), m_color_prompts);
272273
m_editline_up->SetIsInputCompleteCallback(
273274
[this](Editline *editline, StringList &lines) {
274275
return this->IsInputCompleteCallback(editline, lines);
@@ -619,6 +620,7 @@ void IOHandlerEditline::GotEOF() {
619620
void IOHandlerEditline::PrintAsync(const char *s, size_t len, bool is_stdout) {
620621
#if LLDB_ENABLE_LIBEDIT
621622
if (m_editline_up) {
623+
std::lock_guard<std::mutex> guard(m_output_mutex);
622624
lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
623625
m_editline_up->PrintAsync(stream.get(), s, len);
624626
} else

lldb/source/Host/common/Editline.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,10 +1376,12 @@ Editline *Editline::InstanceFor(EditLine *editline) {
13761376
}
13771377

13781378
Editline::Editline(const char *editline_name, FILE *input_file,
1379-
FILE *output_file, FILE *error_file, bool color_prompts)
1379+
FILE *output_file, FILE *error_file,
1380+
std::mutex &output_mutex, bool color_prompts)
13801381
: m_editor_status(EditorStatus::Complete), m_color_prompts(color_prompts),
13811382
m_input_file(input_file), m_output_file(output_file),
1382-
m_error_file(error_file), m_input_connection(fileno(input_file), false) {
1383+
m_error_file(error_file), m_input_connection(fileno(input_file), false),
1384+
m_output_mutex(output_mutex) {
13831385
// Get a shared history instance
13841386
m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name;
13851387
m_history_sp = EditlineHistory::GetHistory(m_editor_name);

lldb/source/Interpreter/CommandInterpreter.cpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2975,8 +2975,12 @@ bool CommandInterpreter::WasInterrupted() const {
29752975
return was_interrupted;
29762976
}
29772977

2978-
void CommandInterpreter::PrintCommandOutput(Stream &stream,
2979-
llvm::StringRef str) {
2978+
void CommandInterpreter::PrintCommandOutput(IOHandler &io_handler,
2979+
llvm::StringRef str,
2980+
bool is_stdout) {
2981+
2982+
lldb::StreamFileSP stream = is_stdout ? io_handler.GetOutputStreamFileSP()
2983+
: io_handler.GetErrorStreamFileSP();
29802984
// Split the output into lines and poll for interrupt requests
29812985
const char *data = str.data();
29822986
size_t size = str.size();
@@ -2989,15 +2993,19 @@ void CommandInterpreter::PrintCommandOutput(Stream &stream,
29892993
break;
29902994
}
29912995
}
2992-
chunk_size = stream.Write(data, chunk_size);
2996+
{
2997+
std::lock_guard<std::mutex> guard(io_handler.GetOutputMutex());
2998+
chunk_size = stream->Write(data, chunk_size);
2999+
}
29933000
lldbassert(size >= chunk_size);
29943001
data += chunk_size;
29953002
size -= chunk_size;
29963003
}
2997-
if (size > 0) {
2998-
stream.Printf("\n... Interrupted.\n");
2999-
}
3000-
stream.Flush();
3004+
3005+
std::lock_guard<std::mutex> guard(io_handler.GetOutputMutex());
3006+
if (size > 0)
3007+
stream->Printf("\n... Interrupted.\n");
3008+
stream->Flush();
30013009
}
30023010

30033011
bool CommandInterpreter::EchoCommandNonInteractive(
@@ -3033,9 +3041,11 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
30333041
// When using a non-interactive file handle (like when sourcing commands
30343042
// from a file) we need to echo the command out so we don't just see the
30353043
// command output and no command...
3036-
if (EchoCommandNonInteractive(line, io_handler.GetFlags()))
3044+
if (EchoCommandNonInteractive(line, io_handler.GetFlags())) {
3045+
std::lock_guard<std::mutex> guard(io_handler.GetOutputMutex());
30373046
io_handler.GetOutputStreamFileSP()->Printf(
30383047
"%s%s\n", io_handler.GetPrompt(), line.c_str());
3048+
}
30393049
}
30403050

30413051
StartHandlingCommand();
@@ -3057,13 +3067,13 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
30573067

30583068
if (!result.GetImmediateOutputStream()) {
30593069
llvm::StringRef output = result.GetOutputData();
3060-
PrintCommandOutput(*io_handler.GetOutputStreamFileSP(), output);
3070+
PrintCommandOutput(io_handler, output, true);
30613071
}
30623072

30633073
// Now emit the command error text from the command we just executed
30643074
if (!result.GetImmediateErrorStream()) {
30653075
llvm::StringRef error = result.GetErrorData();
3066-
PrintCommandOutput(*io_handler.GetErrorStreamFileSP(), error);
3076+
PrintCommandOutput(io_handler, error, false);
30673077
}
30683078
}
30693079

lldb/unittests/Editline/EditlineTest.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class EditlineAdapter {
8484
bool IsInputComplete(lldb_private::Editline *editline,
8585
lldb_private::StringList &lines);
8686

87+
std::mutex output_mutex;
8788
std::unique_ptr<lldb_private::Editline> _editline_sp;
8889

8990
PseudoTerminal _pty;
@@ -117,7 +118,7 @@ EditlineAdapter::EditlineAdapter()
117118
// Create an Editline instance.
118119
_editline_sp.reset(new lldb_private::Editline(
119120
"gtest editor", *_el_secondary_file, *_el_secondary_file,
120-
*_el_secondary_file, false));
121+
*_el_secondary_file, output_mutex, false));
121122
_editline_sp->SetPrompt("> ");
122123

123124
// Hookup our input complete callback.

0 commit comments

Comments
 (0)