Skip to content

Commit 58279d1

Browse files
authored
[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.
1 parent 557628d commit 58279d1

23 files changed

+472
-314
lines changed

lldb/include/lldb/Core/Debugger.h

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,16 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
131131

132132
void SetAsyncExecution(bool async);
133133

134-
File &GetInputFile() { return *m_input_file_sp; }
135-
136134
lldb::FileSP GetInputFileSP() { return m_input_file_sp; }
135+
File &GetInputFile() { return *m_input_file_sp; }
137136

138-
lldb::FileSP GetOutputFileSP() { return m_output_stream_sp->GetFileSP(); }
137+
lldb::FileSP GetOutputFileSP() {
138+
return m_output_stream_sp->GetUnlockedFileSP();
139+
}
139140

140-
lldb::FileSP GetErrorFileSP() { return m_error_stream_sp->GetFileSP(); }
141+
lldb::FileSP GetErrorFileSP() {
142+
return m_error_stream_sp->GetUnlockedFileSP();
143+
}
141144

142145
repro::DataRecorder *GetInputRecorder();
143146

@@ -198,8 +201,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
198201
// If any of the streams are not set, set them to the in/out/err stream of
199202
// the top most input reader to ensure they at least have something
200203
void AdoptTopIOHandlerFilesIfInvalid(lldb::FileSP &in,
201-
lldb::StreamFileSP &out,
202-
lldb::StreamFileSP &err);
204+
lldb::LockableStreamFileSP &out,
205+
lldb::LockableStreamFileSP &err);
203206

204207
/// Run the given IO handler and return immediately.
205208
void RunIOHandlerAsync(const lldb::IOHandlerSP &reader_sp,
@@ -649,8 +652,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
649652
/// should not be used directly. Use GetAsyncOutputStream and
650653
/// GetAsyncErrorStream instead.
651654
/// @{
652-
lldb::StreamFileSP GetOutputStreamSP() { return m_output_stream_sp; }
653-
lldb::StreamFileSP GetErrorStreamSP() { return m_error_stream_sp; }
655+
lldb::LockableStreamFileSP GetOutputStreamSP() { return m_output_stream_sp; }
656+
lldb::LockableStreamFileSP GetErrorStreamSP() { return m_error_stream_sp; }
654657
/// @}
655658

656659
void PushIOHandler(const lldb::IOHandlerSP &reader_sp,
@@ -693,8 +696,9 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
693696

694697
// these should never be NULL
695698
lldb::FileSP m_input_file_sp;
696-
lldb::StreamFileSP m_output_stream_sp;
697-
lldb::StreamFileSP m_error_stream_sp;
699+
lldb::LockableStreamFileSP m_output_stream_sp;
700+
lldb::LockableStreamFileSP m_error_stream_sp;
701+
LockableStreamFile::Mutex m_output_mutex;
698702

699703
/// Used for shadowing the input file when capturing a reproducer.
700704
repro::DataRecorder *m_input_recorder;

lldb/include/lldb/Core/IOHandler.h

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ class IOHandler {
5353
IOHandler(Debugger &debugger, IOHandler::Type type);
5454

5555
IOHandler(Debugger &debugger, IOHandler::Type type,
56-
const lldb::FileSP &input_sp, const lldb::StreamFileSP &output_sp,
57-
const lldb::StreamFileSP &error_sp, uint32_t flags);
56+
const lldb::FileSP &input_sp,
57+
const lldb::LockableStreamFileSP &output_sp,
58+
const lldb::LockableStreamFileSP &error_sp, uint32_t flags);
5859

5960
virtual ~IOHandler();
6061

@@ -112,17 +113,11 @@ class IOHandler {
112113

113114
int GetErrorFD();
114115

115-
FILE *GetInputFILE();
116-
117-
FILE *GetOutputFILE();
118-
119-
FILE *GetErrorFILE();
120-
121116
lldb::FileSP GetInputFileSP();
122117

123-
lldb::StreamFileSP GetOutputStreamFileSP();
118+
lldb::LockableStreamFileSP GetOutputStreamFileSP();
124119

125-
lldb::StreamFileSP GetErrorStreamFileSP();
120+
lldb::LockableStreamFileSP GetErrorStreamFileSP();
126121

127122
Debugger &GetDebugger() { return m_debugger; }
128123

@@ -155,14 +150,11 @@ class IOHandler {
155150

156151
virtual void PrintAsync(const char *s, size_t len, bool is_stdout);
157152

158-
std::recursive_mutex &GetOutputMutex() { return m_output_mutex; }
159-
160153
protected:
161154
Debugger &m_debugger;
162155
lldb::FileSP m_input_sp;
163-
lldb::StreamFileSP m_output_sp;
164-
lldb::StreamFileSP m_error_sp;
165-
std::recursive_mutex m_output_mutex;
156+
lldb::LockableStreamFileSP m_output_sp;
157+
lldb::LockableStreamFileSP m_error_sp;
166158
Predicate<bool> m_popped;
167159
Flags m_flags;
168160
Type m_type;
@@ -330,8 +322,8 @@ class IOHandlerEditline : public IOHandler {
330322

331323
IOHandlerEditline(Debugger &debugger, IOHandler::Type type,
332324
const lldb::FileSP &input_sp,
333-
const lldb::StreamFileSP &output_sp,
334-
const lldb::StreamFileSP &error_sp, uint32_t flags,
325+
const lldb::LockableStreamFileSP &output_sp,
326+
const lldb::LockableStreamFileSP &error_sp, uint32_t flags,
335327
const char *editline_name, // Used for saving history files
336328
llvm::StringRef prompt, llvm::StringRef continuation_prompt,
337329
bool multi_line, bool color,
@@ -345,9 +337,10 @@ class IOHandlerEditline : public IOHandler {
345337
IOHandlerDelegate &) = delete;
346338

347339
IOHandlerEditline(Debugger &, IOHandler::Type, const lldb::FileSP &,
348-
const lldb::StreamFileSP &, const lldb::StreamFileSP &,
349-
uint32_t, const char *, const char *, const char *, bool,
350-
bool, uint32_t, IOHandlerDelegate &) = delete;
340+
const lldb::LockableStreamFileSP &,
341+
const lldb::LockableStreamFileSP &, uint32_t, const char *,
342+
const char *, const char *, bool, bool, uint32_t,
343+
IOHandlerDelegate &) = delete;
351344

352345
~IOHandlerEditline() override;
353346

lldb/include/lldb/Host/Editline.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <sstream>
3535
#include <vector>
3636

37+
#include "lldb/Host/StreamFile.h"
3738
#include "lldb/lldb-private.h"
3839

3940
#if !defined(_WIN32) && !defined(__ANDROID__)
@@ -151,8 +152,9 @@ using namespace line_editor;
151152
/// facility. Both single- and multi-line editing are supported.
152153
class Editline {
153154
public:
154-
Editline(const char *editor_name, FILE *input_file, FILE *output_file,
155-
FILE *error_file, bool color, std::recursive_mutex &output_mutex);
155+
Editline(const char *editor_name, FILE *input_file,
156+
lldb::LockableStreamFileSP output_stream_sp,
157+
lldb::LockableStreamFileSP error_stream_sp, bool color);
156158

157159
~Editline();
158160

@@ -237,7 +239,8 @@ class Editline {
237239
/// Prompts for and reads a multi-line batch of user input.
238240
bool GetLines(int first_line_number, StringList &lines, bool &interrupted);
239241

240-
void PrintAsync(Stream *stream, const char *s, size_t len);
242+
void PrintAsync(lldb::LockableStreamFileSP stream_sp, const char *s,
243+
size_t len);
241244

242245
/// Convert the current input lines into a UTF8 StringList
243246
StringList GetInputAsStringList(int line_count = UINT32_MAX);
@@ -392,8 +395,11 @@ class Editline {
392395
volatile std::sig_atomic_t m_terminal_size_has_changed = 0;
393396
std::string m_editor_name;
394397
FILE *m_input_file;
395-
FILE *m_output_file;
396-
FILE *m_error_file;
398+
lldb::LockableStreamFileSP m_output_stream_sp;
399+
lldb::LockableStreamFileSP m_error_stream_sp;
400+
401+
std::optional<LockedStreamFile> m_locked_output;
402+
397403
ConnectionFileDescriptor m_input_connection;
398404

399405
IsInputCompleteCallbackType m_is_input_complete_callback;
@@ -411,7 +417,6 @@ class Editline {
411417
std::string m_suggestion_ansi_suffix;
412418

413419
std::size_t m_previous_autosuggestion_size = 0;
414-
std::recursive_mutex &m_output_mutex;
415420
};
416421
}
417422

lldb/include/lldb/Host/File.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,11 @@ class File : public IOObject {
377377

378378
class NativeFile : public File {
379379
public:
380+
enum TransferOwnership : bool {
381+
Owned = true,
382+
Unowned = false,
383+
};
384+
380385
NativeFile() : m_descriptor(kInvalidDescriptor), m_stream(kInvalidStream) {}
381386

382387
NativeFile(FILE *fh, bool transfer_ownership)

lldb/include/lldb/Host/StreamFile.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
#include "lldb/Utility/Stream.h"
1414
#include "lldb/lldb-defines.h"
1515
#include "lldb/lldb-enumerations.h"
16+
#include "lldb/lldb-forward.h"
1617

1718
#include <cstdint>
1819
#include <cstdio>
20+
#include <memory>
21+
#include <mutex>
1922

2023
namespace lldb_private {
2124

@@ -52,6 +55,55 @@ class StreamFile : public Stream {
5255
const StreamFile &operator=(const StreamFile &) = delete;
5356
};
5457

58+
class LockableStreamFile;
59+
class LockedStreamFile : public StreamFile {
60+
public:
61+
~LockedStreamFile() { Flush(); }
62+
63+
LockedStreamFile(LockedStreamFile &&other)
64+
: StreamFile(other.m_file_sp), m_lock(std::move(other.m_lock)) {}
65+
66+
private:
67+
LockedStreamFile(std::shared_ptr<File> file, std::recursive_mutex &mutex)
68+
: StreamFile(file), m_lock(mutex) {}
69+
70+
friend class LockableStreamFile;
71+
72+
std::unique_lock<std::recursive_mutex> m_lock;
73+
};
74+
75+
class LockableStreamFile {
76+
public:
77+
using Mutex = std::recursive_mutex;
78+
79+
LockableStreamFile(std::shared_ptr<StreamFile> stream_file_sp, Mutex &mutex)
80+
: m_file_sp(stream_file_sp->GetFileSP()), m_mutex(mutex) {}
81+
LockableStreamFile(StreamFile &stream_file, Mutex &mutex)
82+
: m_file_sp(stream_file.GetFileSP()), m_mutex(mutex) {}
83+
LockableStreamFile(FILE *fh, bool transfer_ownership, Mutex &mutex)
84+
: m_file_sp(std::make_shared<NativeFile>(fh, transfer_ownership)),
85+
m_mutex(mutex) {}
86+
LockableStreamFile(std::shared_ptr<File> file_sp, Mutex &mutex)
87+
: m_file_sp(file_sp), m_mutex(mutex) {}
88+
89+
LockedStreamFile Lock() { return LockedStreamFile(m_file_sp, m_mutex); }
90+
91+
/// Unsafe accessors to get the underlying File without a lock. Exists for
92+
/// legacy reasons.
93+
/// @{
94+
File &GetUnlockedFile() { return *m_file_sp; }
95+
std::shared_ptr<File> GetUnlockedFileSP() { return m_file_sp; }
96+
/// @}
97+
98+
protected:
99+
std::shared_ptr<File> m_file_sp;
100+
Mutex &m_mutex;
101+
102+
private:
103+
LockableStreamFile(const LockableStreamFile &) = delete;
104+
const LockableStreamFile &operator=(const LockableStreamFile &) = delete;
105+
};
106+
55107
} // namespace lldb_private
56108

57109
#endif // LLDB_HOST_STREAMFILE_H

lldb/include/lldb/Interpreter/ScriptInterpreter.h

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,12 @@ class ScriptInterpreterIORedirect {
116116
~ScriptInterpreterIORedirect();
117117

118118
lldb::FileSP GetInputFile() const { return m_input_file_sp; }
119-
lldb::FileSP GetOutputFile() const { return m_output_file_sp->GetFileSP(); }
120-
lldb::FileSP GetErrorFile() const { return m_error_file_sp->GetFileSP(); }
119+
lldb::FileSP GetOutputFile() const {
120+
return m_output_file_sp->GetUnlockedFileSP();
121+
}
122+
lldb::FileSP GetErrorFile() const {
123+
return m_error_file_sp->GetUnlockedFileSP();
124+
}
121125

122126
/// Flush our output and error file handles.
123127
void Flush();
@@ -128,8 +132,9 @@ class ScriptInterpreterIORedirect {
128132
ScriptInterpreterIORedirect(Debugger &debugger, CommandReturnObject *result);
129133

130134
lldb::FileSP m_input_file_sp;
131-
lldb::StreamFileSP m_output_file_sp;
132-
lldb::StreamFileSP m_error_file_sp;
135+
lldb::LockableStreamFileSP m_output_file_sp;
136+
lldb::LockableStreamFileSP m_error_file_sp;
137+
LockableStreamFile::Mutex m_output_mutex;
133138
ThreadedCommunication m_communication;
134139
bool m_disconnect;
135140
};
@@ -478,7 +483,7 @@ class ScriptInterpreter : public PluginInterface {
478483
dest.clear();
479484
return false;
480485
}
481-
486+
482487
virtual StructuredData::ObjectSP
483488
GetOptionsForCommandObject(StructuredData::GenericSP cmd_obj_sp) {
484489
return {};
@@ -488,9 +493,9 @@ class ScriptInterpreter : public PluginInterface {
488493
GetArgumentsForCommandObject(StructuredData::GenericSP cmd_obj_sp) {
489494
return {};
490495
}
491-
496+
492497
virtual bool SetOptionValueForCommandObject(
493-
StructuredData::GenericSP cmd_obj_sp, ExecutionContext *exe_ctx,
498+
StructuredData::GenericSP cmd_obj_sp, ExecutionContext *exe_ctx,
494499
llvm::StringRef long_option, llvm::StringRef value) {
495500
return false;
496501
}

lldb/include/lldb/lldb-forward.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ class StoppointCallbackContext;
215215
class Stream;
216216
class StreamFile;
217217
class StreamString;
218+
class LockableStreamFile;
218219
class StringList;
219220
class StringTableReader;
220221
class StructuredDataImpl;
@@ -432,6 +433,7 @@ typedef std::unique_ptr<lldb_private::StackFrameRecognizerManager>
432433
typedef std::shared_ptr<lldb_private::StopInfo> StopInfoSP;
433434
typedef std::shared_ptr<lldb_private::Stream> StreamSP;
434435
typedef std::shared_ptr<lldb_private::StreamFile> StreamFileSP;
436+
typedef std::shared_ptr<lldb_private::LockableStreamFile> LockableStreamFileSP;
435437
typedef std::shared_ptr<lldb_private::StringSummaryFormat>
436438
StringTypeSummaryImplSP;
437439
typedef std::unique_ptr<lldb_private::StructuredDataImpl> StructuredDataImplUP;

lldb/source/Commands/CommandObjectBreakpointCommand.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,12 @@ are no syntax errors may indicate that a function was declared but never called.
193193
Options *GetOptions() override { return &m_all_options; }
194194

195195
void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
196-
StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
197-
if (output_sp && interactive) {
198-
output_sp->PutCString(g_reader_instructions);
199-
output_sp->Flush();
196+
if (interactive) {
197+
if (lldb::LockableStreamFileSP output_sp =
198+
io_handler.GetOutputStreamFileSP()) {
199+
LockedStreamFile locked_stream = output_sp->Lock();
200+
locked_stream.PutCString(g_reader_instructions);
201+
}
200202
}
201203
}
202204

0 commit comments

Comments
 (0)