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