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