Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Core/IOHandler.cpp
39587 views
1
//===-- IOHandler.cpp -----------------------------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "lldb/Core/IOHandler.h"
10
11
#if defined(__APPLE__)
12
#include <deque>
13
#endif
14
#include <string>
15
16
#include "lldb/Core/Debugger.h"
17
#include "lldb/Host/Config.h"
18
#include "lldb/Host/File.h"
19
#include "lldb/Host/StreamFile.h"
20
#include "lldb/Utility/AnsiTerminal.h"
21
#include "lldb/Utility/Predicate.h"
22
#include "lldb/Utility/Status.h"
23
#include "lldb/Utility/StreamString.h"
24
#include "lldb/Utility/StringList.h"
25
#include "lldb/lldb-forward.h"
26
27
#if LLDB_ENABLE_LIBEDIT
28
#include "lldb/Host/Editline.h"
29
#endif
30
#include "lldb/Interpreter/CommandCompletions.h"
31
#include "lldb/Interpreter/CommandInterpreter.h"
32
#include "llvm/ADT/StringRef.h"
33
34
#ifdef _WIN32
35
#include "lldb/Host/windows/windows.h"
36
#endif
37
38
#include <memory>
39
#include <mutex>
40
#include <optional>
41
42
#include <cassert>
43
#include <cctype>
44
#include <cerrno>
45
#include <clocale>
46
#include <cstdint>
47
#include <cstdio>
48
#include <cstring>
49
#include <type_traits>
50
51
using namespace lldb;
52
using namespace lldb_private;
53
using llvm::StringRef;
54
55
IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type)
56
: IOHandler(debugger, type,
57
FileSP(), // Adopt STDIN from top input reader
58
StreamFileSP(), // Adopt STDOUT from top input reader
59
StreamFileSP(), // Adopt STDERR from top input reader
60
0 // Flags
61
62
) {}
63
64
IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type,
65
const lldb::FileSP &input_sp,
66
const lldb::StreamFileSP &output_sp,
67
const lldb::StreamFileSP &error_sp, uint32_t flags)
68
: m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp),
69
m_error_sp(error_sp), m_popped(false), m_flags(flags), m_type(type),
70
m_user_data(nullptr), m_done(false), m_active(false) {
71
// If any files are not specified, then adopt them from the top input reader.
72
if (!m_input_sp || !m_output_sp || !m_error_sp)
73
debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp,
74
m_error_sp);
75
}
76
77
IOHandler::~IOHandler() = default;
78
79
int IOHandler::GetInputFD() {
80
return (m_input_sp ? m_input_sp->GetDescriptor() : -1);
81
}
82
83
int IOHandler::GetOutputFD() {
84
return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1);
85
}
86
87
int IOHandler::GetErrorFD() {
88
return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1);
89
}
90
91
FILE *IOHandler::GetInputFILE() {
92
return (m_input_sp ? m_input_sp->GetStream() : nullptr);
93
}
94
95
FILE *IOHandler::GetOutputFILE() {
96
return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr);
97
}
98
99
FILE *IOHandler::GetErrorFILE() {
100
return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr);
101
}
102
103
FileSP IOHandler::GetInputFileSP() { return m_input_sp; }
104
105
StreamFileSP IOHandler::GetOutputStreamFileSP() { return m_output_sp; }
106
107
StreamFileSP IOHandler::GetErrorStreamFileSP() { return m_error_sp; }
108
109
bool IOHandler::GetIsInteractive() {
110
return GetInputFileSP() ? GetInputFileSP()->GetIsInteractive() : false;
111
}
112
113
bool IOHandler::GetIsRealTerminal() {
114
return GetInputFileSP() ? GetInputFileSP()->GetIsRealTerminal() : false;
115
}
116
117
void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); }
118
119
void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); }
120
121
void IOHandler::PrintAsync(const char *s, size_t len, bool is_stdout) {
122
std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
123
lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
124
stream->Write(s, len);
125
stream->Flush();
126
}
127
128
bool IOHandlerStack::PrintAsync(const char *s, size_t len, bool is_stdout) {
129
std::lock_guard<std::recursive_mutex> guard(m_mutex);
130
if (!m_top)
131
return false;
132
m_top->PrintAsync(s, len, is_stdout);
133
return true;
134
}
135
136
IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt,
137
bool default_response)
138
: IOHandlerEditline(
139
debugger, IOHandler::Type::Confirm,
140
nullptr, // nullptr editline_name means no history loaded/saved
141
llvm::StringRef(), // No prompt
142
llvm::StringRef(), // No continuation prompt
143
false, // Multi-line
144
false, // Don't colorize the prompt (i.e. the confirm message.)
145
0, *this),
146
m_default_response(default_response), m_user_response(default_response) {
147
StreamString prompt_stream;
148
prompt_stream.PutCString(prompt);
149
if (m_default_response)
150
prompt_stream.Printf(": [Y/n] ");
151
else
152
prompt_stream.Printf(": [y/N] ");
153
154
SetPrompt(prompt_stream.GetString());
155
}
156
157
IOHandlerConfirm::~IOHandlerConfirm() = default;
158
159
void IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler,
160
CompletionRequest &request) {
161
if (request.GetRawCursorPos() != 0)
162
return;
163
request.AddCompletion(m_default_response ? "y" : "n");
164
}
165
166
void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler,
167
std::string &line) {
168
if (line.empty()) {
169
// User just hit enter, set the response to the default
170
m_user_response = m_default_response;
171
io_handler.SetIsDone(true);
172
return;
173
}
174
175
if (line.size() == 1) {
176
switch (line[0]) {
177
case 'y':
178
case 'Y':
179
m_user_response = true;
180
io_handler.SetIsDone(true);
181
return;
182
case 'n':
183
case 'N':
184
m_user_response = false;
185
io_handler.SetIsDone(true);
186
return;
187
default:
188
break;
189
}
190
}
191
192
if (line == "yes" || line == "YES" || line == "Yes") {
193
m_user_response = true;
194
io_handler.SetIsDone(true);
195
} else if (line == "no" || line == "NO" || line == "No") {
196
m_user_response = false;
197
io_handler.SetIsDone(true);
198
}
199
}
200
201
std::optional<std::string>
202
IOHandlerDelegate::IOHandlerSuggestion(IOHandler &io_handler,
203
llvm::StringRef line) {
204
return io_handler.GetDebugger()
205
.GetCommandInterpreter()
206
.GetAutoSuggestionForCommand(line);
207
}
208
209
void IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler,
210
CompletionRequest &request) {
211
switch (m_completion) {
212
case Completion::None:
213
break;
214
case Completion::LLDBCommand:
215
io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(request);
216
break;
217
case Completion::Expression:
218
lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
219
io_handler.GetDebugger().GetCommandInterpreter(),
220
lldb::eVariablePathCompletion, request, nullptr);
221
break;
222
}
223
}
224
225
IOHandlerEditline::IOHandlerEditline(
226
Debugger &debugger, IOHandler::Type type,
227
const char *editline_name, // Used for saving history files
228
llvm::StringRef prompt, llvm::StringRef continuation_prompt,
229
bool multi_line, bool color, uint32_t line_number_start,
230
IOHandlerDelegate &delegate)
231
: IOHandlerEditline(debugger, type,
232
FileSP(), // Inherit input from top input reader
233
StreamFileSP(), // Inherit output from top input reader
234
StreamFileSP(), // Inherit error from top input reader
235
0, // Flags
236
editline_name, // Used for saving history files
237
prompt, continuation_prompt, multi_line, color,
238
line_number_start, delegate) {}
239
240
IOHandlerEditline::IOHandlerEditline(
241
Debugger &debugger, IOHandler::Type type, const lldb::FileSP &input_sp,
242
const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp,
243
uint32_t flags,
244
const char *editline_name, // Used for saving history files
245
llvm::StringRef prompt, llvm::StringRef continuation_prompt,
246
bool multi_line, bool color, uint32_t line_number_start,
247
IOHandlerDelegate &delegate)
248
: IOHandler(debugger, type, input_sp, output_sp, error_sp, flags),
249
#if LLDB_ENABLE_LIBEDIT
250
m_editline_up(),
251
#endif
252
m_delegate(delegate), m_prompt(), m_continuation_prompt(),
253
m_current_lines_ptr(nullptr), m_base_line_number(line_number_start),
254
m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line), m_color(color),
255
m_interrupt_exits(true) {
256
SetPrompt(prompt);
257
258
#if LLDB_ENABLE_LIBEDIT
259
bool use_editline = false;
260
261
use_editline = GetInputFILE() && GetOutputFILE() && GetErrorFILE() &&
262
m_input_sp && m_input_sp->GetIsRealTerminal();
263
264
if (use_editline) {
265
m_editline_up = std::make_unique<Editline>(editline_name, GetInputFILE(),
266
GetOutputFILE(), GetErrorFILE(),
267
GetOutputMutex());
268
m_editline_up->SetIsInputCompleteCallback(
269
[this](Editline *editline, StringList &lines) {
270
return this->IsInputCompleteCallback(editline, lines);
271
});
272
273
m_editline_up->SetAutoCompleteCallback([this](CompletionRequest &request) {
274
this->AutoCompleteCallback(request);
275
});
276
277
if (debugger.GetUseAutosuggestion()) {
278
m_editline_up->SetSuggestionCallback([this](llvm::StringRef line) {
279
return this->SuggestionCallback(line);
280
});
281
if (m_color) {
282
m_editline_up->SetSuggestionAnsiPrefix(ansi::FormatAnsiTerminalCodes(
283
debugger.GetAutosuggestionAnsiPrefix()));
284
m_editline_up->SetSuggestionAnsiSuffix(ansi::FormatAnsiTerminalCodes(
285
debugger.GetAutosuggestionAnsiSuffix()));
286
}
287
}
288
// See if the delegate supports fixing indentation
289
const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
290
if (indent_chars) {
291
// The delegate does support indentation, hook it up so when any
292
// indentation character is typed, the delegate gets a chance to fix it
293
FixIndentationCallbackType f = [this](Editline *editline,
294
const StringList &lines,
295
int cursor_position) {
296
return this->FixIndentationCallback(editline, lines, cursor_position);
297
};
298
m_editline_up->SetFixIndentationCallback(std::move(f), indent_chars);
299
}
300
}
301
#endif
302
SetBaseLineNumber(m_base_line_number);
303
SetPrompt(prompt);
304
SetContinuationPrompt(continuation_prompt);
305
}
306
307
IOHandlerEditline::~IOHandlerEditline() {
308
#if LLDB_ENABLE_LIBEDIT
309
m_editline_up.reset();
310
#endif
311
}
312
313
void IOHandlerEditline::Activate() {
314
IOHandler::Activate();
315
m_delegate.IOHandlerActivated(*this, GetIsInteractive());
316
}
317
318
void IOHandlerEditline::Deactivate() {
319
IOHandler::Deactivate();
320
m_delegate.IOHandlerDeactivated(*this);
321
}
322
323
void IOHandlerEditline::TerminalSizeChanged() {
324
#if LLDB_ENABLE_LIBEDIT
325
if (m_editline_up)
326
m_editline_up->TerminalSizeChanged();
327
#endif
328
}
329
330
// Split out a line from the buffer, if there is a full one to get.
331
static std::optional<std::string> SplitLine(std::string &line_buffer) {
332
size_t pos = line_buffer.find('\n');
333
if (pos == std::string::npos)
334
return std::nullopt;
335
std::string line =
336
std::string(StringRef(line_buffer.c_str(), pos).rtrim("\n\r"));
337
line_buffer = line_buffer.substr(pos + 1);
338
return line;
339
}
340
341
// If the final line of the file ends without a end-of-line, return
342
// it as a line anyway.
343
static std::optional<std::string> SplitLineEOF(std::string &line_buffer) {
344
if (llvm::all_of(line_buffer, llvm::isSpace))
345
return std::nullopt;
346
std::string line = std::move(line_buffer);
347
line_buffer.clear();
348
return line;
349
}
350
351
bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
352
#if LLDB_ENABLE_LIBEDIT
353
if (m_editline_up) {
354
return m_editline_up->GetLine(line, interrupted);
355
}
356
#endif
357
358
line.clear();
359
360
if (GetIsInteractive()) {
361
const char *prompt = nullptr;
362
363
if (m_multi_line && m_curr_line_idx > 0)
364
prompt = GetContinuationPrompt();
365
366
if (prompt == nullptr)
367
prompt = GetPrompt();
368
369
if (prompt && prompt[0]) {
370
if (m_output_sp) {
371
m_output_sp->Printf("%s", prompt);
372
m_output_sp->Flush();
373
}
374
}
375
}
376
377
std::optional<std::string> got_line = SplitLine(m_line_buffer);
378
379
if (!got_line && !m_input_sp) {
380
// No more input file, we are done...
381
SetIsDone(true);
382
return false;
383
}
384
385
FILE *in = GetInputFILE();
386
char buffer[256];
387
388
if (!got_line && !in && m_input_sp) {
389
// there is no FILE*, fall back on just reading bytes from the stream.
390
while (!got_line) {
391
size_t bytes_read = sizeof(buffer);
392
Status error = m_input_sp->Read((void *)buffer, bytes_read);
393
if (error.Success() && !bytes_read) {
394
got_line = SplitLineEOF(m_line_buffer);
395
break;
396
}
397
if (error.Fail())
398
break;
399
m_line_buffer += StringRef(buffer, bytes_read);
400
got_line = SplitLine(m_line_buffer);
401
}
402
}
403
404
if (!got_line && in) {
405
while (!got_line) {
406
char *r = fgets(buffer, sizeof(buffer), in);
407
#ifdef _WIN32
408
// ReadFile on Windows is supposed to set ERROR_OPERATION_ABORTED
409
// according to the docs on MSDN. However, this has evidently been a
410
// known bug since Windows 8. Therefore, we can't detect if a signal
411
// interrupted in the fgets. So pressing ctrl-c causes the repl to end
412
// and the process to exit. A temporary workaround is just to attempt to
413
// fgets twice until this bug is fixed.
414
if (r == nullptr)
415
r = fgets(buffer, sizeof(buffer), in);
416
// this is the equivalent of EINTR for Windows
417
if (r == nullptr && GetLastError() == ERROR_OPERATION_ABORTED)
418
continue;
419
#endif
420
if (r == nullptr) {
421
if (ferror(in) && errno == EINTR)
422
continue;
423
if (feof(in))
424
got_line = SplitLineEOF(m_line_buffer);
425
break;
426
}
427
m_line_buffer += buffer;
428
got_line = SplitLine(m_line_buffer);
429
}
430
}
431
432
if (got_line) {
433
line = *got_line;
434
}
435
436
return (bool)got_line;
437
}
438
439
#if LLDB_ENABLE_LIBEDIT
440
bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline,
441
StringList &lines) {
442
return m_delegate.IOHandlerIsInputComplete(*this, lines);
443
}
444
445
int IOHandlerEditline::FixIndentationCallback(Editline *editline,
446
const StringList &lines,
447
int cursor_position) {
448
return m_delegate.IOHandlerFixIndentation(*this, lines, cursor_position);
449
}
450
451
std::optional<std::string>
452
IOHandlerEditline::SuggestionCallback(llvm::StringRef line) {
453
return m_delegate.IOHandlerSuggestion(*this, line);
454
}
455
456
void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request) {
457
m_delegate.IOHandlerComplete(*this, request);
458
}
459
#endif
460
461
const char *IOHandlerEditline::GetPrompt() {
462
#if LLDB_ENABLE_LIBEDIT
463
if (m_editline_up) {
464
return m_editline_up->GetPrompt();
465
} else {
466
#endif
467
if (m_prompt.empty())
468
return nullptr;
469
#if LLDB_ENABLE_LIBEDIT
470
}
471
#endif
472
return m_prompt.c_str();
473
}
474
475
bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) {
476
m_prompt = std::string(prompt);
477
478
#if LLDB_ENABLE_LIBEDIT
479
if (m_editline_up) {
480
m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
481
if (m_color) {
482
m_editline_up->SetPromptAnsiPrefix(
483
ansi::FormatAnsiTerminalCodes(m_debugger.GetPromptAnsiPrefix()));
484
m_editline_up->SetPromptAnsiSuffix(
485
ansi::FormatAnsiTerminalCodes(m_debugger.GetPromptAnsiSuffix()));
486
}
487
}
488
#endif
489
return true;
490
}
491
492
const char *IOHandlerEditline::GetContinuationPrompt() {
493
return (m_continuation_prompt.empty() ? nullptr
494
: m_continuation_prompt.c_str());
495
}
496
497
void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) {
498
m_continuation_prompt = std::string(prompt);
499
500
#if LLDB_ENABLE_LIBEDIT
501
if (m_editline_up)
502
m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty()
503
? nullptr
504
: m_continuation_prompt.c_str());
505
#endif
506
}
507
508
void IOHandlerEditline::SetBaseLineNumber(uint32_t line) {
509
m_base_line_number = line;
510
}
511
512
uint32_t IOHandlerEditline::GetCurrentLineIndex() const {
513
#if LLDB_ENABLE_LIBEDIT
514
if (m_editline_up)
515
return m_editline_up->GetCurrentLine();
516
#endif
517
return m_curr_line_idx;
518
}
519
520
StringList IOHandlerEditline::GetCurrentLines() const {
521
#if LLDB_ENABLE_LIBEDIT
522
if (m_editline_up)
523
return m_editline_up->GetInputAsStringList();
524
#endif
525
// When libedit is not used, the current lines can be gotten from
526
// `m_current_lines_ptr`, which is updated whenever a new line is processed.
527
// This doesn't happen when libedit is used, in which case
528
// `m_current_lines_ptr` is only updated when the full input is terminated.
529
530
if (m_current_lines_ptr)
531
return *m_current_lines_ptr;
532
return StringList();
533
}
534
535
bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) {
536
m_current_lines_ptr = &lines;
537
538
bool success = false;
539
#if LLDB_ENABLE_LIBEDIT
540
if (m_editline_up) {
541
return m_editline_up->GetLines(m_base_line_number, lines, interrupted);
542
} else {
543
#endif
544
bool done = false;
545
Status error;
546
547
while (!done) {
548
// Show line numbers if we are asked to
549
std::string line;
550
if (m_base_line_number > 0 && GetIsInteractive()) {
551
if (m_output_sp) {
552
m_output_sp->Printf("%u%s",
553
m_base_line_number + (uint32_t)lines.GetSize(),
554
GetPrompt() == nullptr ? " " : "");
555
}
556
}
557
558
m_curr_line_idx = lines.GetSize();
559
560
bool interrupted = false;
561
if (GetLine(line, interrupted) && !interrupted) {
562
lines.AppendString(line);
563
done = m_delegate.IOHandlerIsInputComplete(*this, lines);
564
} else {
565
done = true;
566
}
567
}
568
success = lines.GetSize() > 0;
569
#if LLDB_ENABLE_LIBEDIT
570
}
571
#endif
572
return success;
573
}
574
575
// Each IOHandler gets to run until it is done. It should read data from the
576
// "in" and place output into "out" and "err and return when done.
577
void IOHandlerEditline::Run() {
578
std::string line;
579
while (IsActive()) {
580
bool interrupted = false;
581
if (m_multi_line) {
582
StringList lines;
583
if (GetLines(lines, interrupted)) {
584
if (interrupted) {
585
m_done = m_interrupt_exits;
586
m_delegate.IOHandlerInputInterrupted(*this, line);
587
588
} else {
589
line = lines.CopyList();
590
m_delegate.IOHandlerInputComplete(*this, line);
591
}
592
} else {
593
m_done = true;
594
}
595
} else {
596
if (GetLine(line, interrupted)) {
597
if (interrupted)
598
m_delegate.IOHandlerInputInterrupted(*this, line);
599
else
600
m_delegate.IOHandlerInputComplete(*this, line);
601
} else {
602
m_done = true;
603
}
604
}
605
}
606
}
607
608
void IOHandlerEditline::Cancel() {
609
#if LLDB_ENABLE_LIBEDIT
610
if (m_editline_up)
611
m_editline_up->Cancel();
612
#endif
613
}
614
615
bool IOHandlerEditline::Interrupt() {
616
// Let the delgate handle it first
617
if (m_delegate.IOHandlerInterrupt(*this))
618
return true;
619
620
#if LLDB_ENABLE_LIBEDIT
621
if (m_editline_up)
622
return m_editline_up->Interrupt();
623
#endif
624
return false;
625
}
626
627
void IOHandlerEditline::GotEOF() {
628
#if LLDB_ENABLE_LIBEDIT
629
if (m_editline_up)
630
m_editline_up->Interrupt();
631
#endif
632
}
633
634
void IOHandlerEditline::PrintAsync(const char *s, size_t len, bool is_stdout) {
635
#if LLDB_ENABLE_LIBEDIT
636
if (m_editline_up) {
637
std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
638
lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
639
m_editline_up->PrintAsync(stream.get(), s, len);
640
} else
641
#endif
642
{
643
#ifdef _WIN32
644
const char *prompt = GetPrompt();
645
if (prompt) {
646
// Back up over previous prompt using Windows API
647
CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
648
HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
649
GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info);
650
COORD coord = screen_buffer_info.dwCursorPosition;
651
coord.X -= strlen(prompt);
652
if (coord.X < 0)
653
coord.X = 0;
654
SetConsoleCursorPosition(console_handle, coord);
655
}
656
#endif
657
IOHandler::PrintAsync(s, len, is_stdout);
658
#ifdef _WIN32
659
if (prompt)
660
IOHandler::PrintAsync(prompt, strlen(prompt), is_stdout);
661
#endif
662
}
663
}
664
665