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