Skip to content

Commit 094a7f7

Browse files
committed
[lldb] Synchronize Debugger's stdout and stderr at the StreamFile level
This patch moves the synchronization for the Debugger's output and error into StreamFile. Currently, the debugger's output is synchronized at the IOHandler level. Because Debugger::PrintAsync first gives the top IOHandler a chance to print output, that works most of the time, except when we fall back to printing to the output and error streams directly. By storing the mutex in the new SynchronizedStreamFile, everyone from the Debugger level down has access to it. This should make it easier to do the right thing. It also means we don't have to pass the mutex around separately (e.g. between IOHandler and Editline). Locking at the StreamFile level also eliminates a few lock guard around calls to StreamFile::Write. I resisted the urge to simplify the code to aid reviewing. There's definitely some repetition in Editline that could be solved by extracting some helper methods. I'm planning on doing that in a follow-up.
1 parent ced23aa commit 094a7f7

File tree

15 files changed

+188
-139
lines changed

15 files changed

+188
-139
lines changed

lldb/include/lldb/Core/Debugger.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,13 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
133133

134134
lldb::FileSP GetInputFileSP() { return m_input_file_sp; }
135135

136-
lldb::StreamFileSP GetOutputStreamSP() { return m_output_stream_sp; }
136+
lldb::SynchronizedStreamFileSP GetOutputStreamSP() {
137+
return m_output_stream_sp;
138+
}
137139

138-
lldb::StreamFileSP GetErrorStreamSP() { return m_error_stream_sp; }
140+
lldb::SynchronizedStreamFileSP GetErrorStreamSP() {
141+
return m_error_stream_sp;
142+
}
139143

140144
File &GetInputFile() { return *m_input_file_sp; }
141145

@@ -206,8 +210,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
206210
// If any of the streams are not set, set them to the in/out/err stream of
207211
// the top most input reader to ensure they at least have something
208212
void AdoptTopIOHandlerFilesIfInvalid(lldb::FileSP &in,
209-
lldb::StreamFileSP &out,
210-
lldb::StreamFileSP &err);
213+
lldb::SynchronizedStreamFileSP &out,
214+
lldb::SynchronizedStreamFileSP &err);
211215

212216
/// Run the given IO handler and return immediately.
213217
void RunIOHandlerAsync(const lldb::IOHandlerSP &reader_sp,
@@ -693,8 +697,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
693697

694698
// these should never be NULL
695699
lldb::FileSP m_input_file_sp;
696-
lldb::StreamFileSP m_output_stream_sp;
697-
lldb::StreamFileSP m_error_stream_sp;
700+
lldb::SynchronizedStreamFileSP m_output_stream_sp;
701+
lldb::SynchronizedStreamFileSP m_error_stream_sp;
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: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,9 @@ class IOHandler {
5858
IOHandler(Debugger &debugger, IOHandler::Type type);
5959

6060
IOHandler(Debugger &debugger, IOHandler::Type type,
61-
const lldb::FileSP &input_sp, const lldb::StreamFileSP &output_sp,
62-
const lldb::StreamFileSP &error_sp, uint32_t flags);
61+
const lldb::FileSP &input_sp,
62+
const lldb::SynchronizedStreamFileSP &output_sp,
63+
const lldb::SynchronizedStreamFileSP &error_sp, uint32_t flags);
6364

6465
virtual ~IOHandler();
6566

@@ -117,17 +118,11 @@ class IOHandler {
117118

118119
int GetErrorFD();
119120

120-
FILE *GetInputFILE();
121-
122-
FILE *GetOutputFILE();
123-
124-
FILE *GetErrorFILE();
125-
126121
lldb::FileSP GetInputFileSP();
127122

128-
lldb::StreamFileSP GetOutputStreamFileSP();
123+
lldb::SynchronizedStreamFileSP GetOutputStreamFileSP();
129124

130-
lldb::StreamFileSP GetErrorStreamFileSP();
125+
lldb::SynchronizedStreamFileSP GetErrorStreamFileSP();
131126

132127
Debugger &GetDebugger() { return m_debugger; }
133128

@@ -160,14 +155,11 @@ class IOHandler {
160155

161156
virtual void PrintAsync(const char *s, size_t len, bool is_stdout);
162157

163-
std::recursive_mutex &GetOutputMutex() { return m_output_mutex; }
164-
165158
protected:
166159
Debugger &m_debugger;
167160
lldb::FileSP m_input_sp;
168-
lldb::StreamFileSP m_output_sp;
169-
lldb::StreamFileSP m_error_sp;
170-
std::recursive_mutex m_output_mutex;
161+
lldb::SynchronizedStreamFileSP m_output_sp;
162+
lldb::SynchronizedStreamFileSP m_error_sp;
171163
Predicate<bool> m_popped;
172164
Flags m_flags;
173165
Type m_type;
@@ -335,8 +327,9 @@ class IOHandlerEditline : public IOHandler {
335327

336328
IOHandlerEditline(Debugger &debugger, IOHandler::Type type,
337329
const lldb::FileSP &input_sp,
338-
const lldb::StreamFileSP &output_sp,
339-
const lldb::StreamFileSP &error_sp, uint32_t flags,
330+
const lldb::SynchronizedStreamFileSP &output_sp,
331+
const lldb::SynchronizedStreamFileSP &error_sp,
332+
uint32_t flags,
340333
const char *editline_name, // Used for saving history files
341334
llvm::StringRef prompt, llvm::StringRef continuation_prompt,
342335
bool multi_line, bool color,
@@ -350,9 +343,10 @@ class IOHandlerEditline : public IOHandler {
350343
IOHandlerDelegate &) = delete;
351344

352345
IOHandlerEditline(Debugger &, IOHandler::Type, const lldb::FileSP &,
353-
const lldb::StreamFileSP &, const lldb::StreamFileSP &,
354-
uint32_t, const char *, const char *, const char *, bool,
355-
bool, uint32_t, IOHandlerDelegate &) = delete;
346+
const lldb::SynchronizedStreamFileSP &,
347+
const lldb::SynchronizedStreamFileSP &, uint32_t,
348+
const char *, const char *, const char *, bool, bool,
349+
uint32_t, IOHandlerDelegate &) = delete;
356350

357351
~IOHandlerEditline() override;
358352

lldb/include/lldb/Host/Editline.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,9 @@ using namespace line_editor;
151151
/// facility. Both single- and multi-line editing are supported.
152152
class Editline {
153153
public:
154-
Editline(const char *editor_name, FILE *input_file, FILE *output_file,
155-
FILE *error_file, bool color, std::recursive_mutex &output_mutex);
154+
Editline(const char *editor_name, FILE *input_file,
155+
lldb::SynchronizedStreamFileSP output_stream_sp,
156+
lldb::SynchronizedStreamFileSP error_stream_sp, bool color);
156157

157158
~Editline();
158159

@@ -392,8 +393,8 @@ class Editline {
392393
volatile std::sig_atomic_t m_terminal_size_has_changed = 0;
393394
std::string m_editor_name;
394395
FILE *m_input_file;
395-
FILE *m_output_file;
396-
FILE *m_error_file;
396+
lldb::SynchronizedStreamFileSP m_output_stream_sp;
397+
lldb::SynchronizedStreamFileSP m_error_stream_sp;
397398
ConnectionFileDescriptor m_input_connection;
398399

399400
IsInputCompleteCallbackType m_is_input_complete_callback;
@@ -411,7 +412,6 @@ class Editline {
411412
std::string m_suggestion_ansi_suffix;
412413

413414
std::size_t m_previous_autosuggestion_size = 0;
414-
std::recursive_mutex &m_output_mutex;
415415
};
416416
}
417417

lldb/include/lldb/Host/StreamFile.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,39 @@ class StreamFile : public Stream {
5252
const StreamFile &operator=(const StreamFile &) = delete;
5353
};
5454

55+
class SynchronizedStreamFile : public StreamFile {
56+
public:
57+
SynchronizedStreamFile(uint32_t flags, uint32_t addr_size,
58+
lldb::ByteOrder byte_order)
59+
: StreamFile(flags, addr_size, byte_order) {}
60+
61+
SynchronizedStreamFile(int fd, bool transfer_ownership)
62+
: StreamFile(fd, transfer_ownership) {}
63+
64+
SynchronizedStreamFile(
65+
const char *path, File::OpenOptions options,
66+
uint32_t permissions = lldb::eFilePermissionsFileDefault)
67+
: StreamFile(path, options, permissions) {}
68+
69+
SynchronizedStreamFile(FILE *fh, bool transfer_ownership)
70+
: StreamFile(fh, transfer_ownership) {}
71+
72+
SynchronizedStreamFile(std::shared_ptr<File> file) : StreamFile(file) {}
73+
74+
~SynchronizedStreamFile() override;
75+
76+
std::recursive_mutex &GetMutex() { return m_mutex; }
77+
78+
protected:
79+
size_t WriteImpl(const void *s, size_t length) override;
80+
std::recursive_mutex m_mutex;
81+
82+
private:
83+
SynchronizedStreamFile(const SynchronizedStreamFile &) = delete;
84+
const SynchronizedStreamFile &
85+
operator=(const SynchronizedStreamFile &) = delete;
86+
};
87+
5588
} // namespace lldb_private
5689

5790
#endif // LLDB_HOST_STREAMFILE_H

lldb/include/lldb/Interpreter/ScriptInterpreter.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ class ScriptInterpreterIORedirect {
128128
ScriptInterpreterIORedirect(Debugger &debugger, CommandReturnObject *result);
129129

130130
lldb::FileSP m_input_file_sp;
131-
lldb::StreamFileSP m_output_file_sp;
132-
lldb::StreamFileSP m_error_file_sp;
131+
lldb::SynchronizedStreamFileSP m_output_file_sp;
132+
lldb::SynchronizedStreamFileSP m_error_file_sp;
133133
ThreadedCommunication m_communication;
134134
bool m_disconnect;
135135
};
@@ -478,7 +478,7 @@ class ScriptInterpreter : public PluginInterface {
478478
dest.clear();
479479
return false;
480480
}
481-
481+
482482
virtual StructuredData::ObjectSP
483483
GetOptionsForCommandObject(StructuredData::GenericSP cmd_obj_sp) {
484484
return {};
@@ -488,9 +488,9 @@ class ScriptInterpreter : public PluginInterface {
488488
GetArgumentsForCommandObject(StructuredData::GenericSP cmd_obj_sp) {
489489
return {};
490490
}
491-
491+
492492
virtual bool SetOptionValueForCommandObject(
493-
StructuredData::GenericSP cmd_obj_sp, ExecutionContext *exe_ctx,
493+
StructuredData::GenericSP cmd_obj_sp, ExecutionContext *exe_ctx,
494494
llvm::StringRef long_option, llvm::StringRef value) {
495495
return false;
496496
}

lldb/include/lldb/lldb-forward.h

Lines changed: 3 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 SynchronizedStreamFile;
218219
class StringList;
219220
class StringTableReader;
220221
class StructuredDataImpl;
@@ -432,6 +433,8 @@ 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::SynchronizedStreamFile>
437+
SynchronizedStreamFileSP;
435438
typedef std::shared_ptr<lldb_private::StringSummaryFormat>
436439
StringTypeSummaryImplSP;
437440
typedef std::unique_ptr<lldb_private::StructuredDataImpl> StructuredDataImplUP;

lldb/source/Core/Debugger.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -875,8 +875,10 @@ Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton)
875875
: UserID(g_unique_id++),
876876
Properties(std::make_shared<OptionValueProperties>()),
877877
m_input_file_sp(std::make_shared<NativeFile>(stdin, false)),
878-
m_output_stream_sp(std::make_shared<StreamFile>(stdout, false)),
879-
m_error_stream_sp(std::make_shared<StreamFile>(stderr, false)),
878+
m_output_stream_sp(
879+
std::make_shared<SynchronizedStreamFile>(stdout, false)),
880+
m_error_stream_sp(
881+
std::make_shared<SynchronizedStreamFile>(stderr, false)),
880882
m_input_recorder(nullptr),
881883
m_broadcaster_manager_sp(BroadcasterManager::MakeBroadcasterManager()),
882884
m_terminal_state(), m_target_list(*this), m_platform_list(),
@@ -1084,12 +1086,12 @@ void Debugger::SetInputFile(FileSP file_sp) {
10841086

10851087
void Debugger::SetOutputFile(FileSP file_sp) {
10861088
assert(file_sp && file_sp->IsValid());
1087-
m_output_stream_sp = std::make_shared<StreamFile>(file_sp);
1089+
m_output_stream_sp = std::make_shared<SynchronizedStreamFile>(file_sp);
10881090
}
10891091

10901092
void Debugger::SetErrorFile(FileSP file_sp) {
10911093
assert(file_sp && file_sp->IsValid());
1092-
m_error_stream_sp = std::make_shared<StreamFile>(file_sp);
1094+
m_error_stream_sp = std::make_shared<SynchronizedStreamFile>(file_sp);
10931095
}
10941096

10951097
void Debugger::SaveInputTerminalState() {
@@ -1226,8 +1228,9 @@ void Debugger::RunIOHandlerAsync(const IOHandlerSP &reader_sp,
12261228
PushIOHandler(reader_sp, cancel_top_handler);
12271229
}
12281230

1229-
void Debugger::AdoptTopIOHandlerFilesIfInvalid(FileSP &in, StreamFileSP &out,
1230-
StreamFileSP &err) {
1231+
void Debugger::AdoptTopIOHandlerFilesIfInvalid(FileSP &in,
1232+
SynchronizedStreamFileSP &out,
1233+
SynchronizedStreamFileSP &err) {
12311234
// Before an IOHandler runs, it must have in/out/err streams. This function
12321235
// is called when one ore more of the streams are nullptr. We use the top
12331236
// input reader's in/out/err streams, or fall back to the debugger file
@@ -1253,7 +1256,7 @@ void Debugger::AdoptTopIOHandlerFilesIfInvalid(FileSP &in, StreamFileSP &out,
12531256
out = GetOutputStreamSP();
12541257
// If there is nothing, use stdout
12551258
if (!out)
1256-
out = std::make_shared<StreamFile>(stdout, false);
1259+
out = std::make_shared<SynchronizedStreamFile>(stdout, false);
12571260
}
12581261
// If no STDERR has been set, then set it appropriately
12591262
if (!err || !err->GetFile().IsValid()) {
@@ -1263,7 +1266,7 @@ void Debugger::AdoptTopIOHandlerFilesIfInvalid(FileSP &in, StreamFileSP &out,
12631266
err = GetErrorStreamSP();
12641267
// If there is nothing, use stderr
12651268
if (!err)
1266-
err = std::make_shared<StreamFile>(stderr, false);
1269+
err = std::make_shared<SynchronizedStreamFile>(stderr, false);
12671270
}
12681271
}
12691272

0 commit comments

Comments
 (0)