1 //===-- REPL.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/Expression/REPL.h" 10 #include "lldb/Core/Debugger.h" 11 #include "lldb/Core/PluginManager.h" 12 #include "lldb/Expression/ExpressionVariable.h" 13 #include "lldb/Expression/UserExpression.h" 14 #include "lldb/Host/HostInfo.h" 15 #include "lldb/Host/StreamFile.h" 16 #include "lldb/Interpreter/CommandInterpreter.h" 17 #include "lldb/Interpreter/CommandReturnObject.h" 18 #include "lldb/Target/Thread.h" 19 #include "lldb/Utility/AnsiTerminal.h" 20 21 #include <memory> 22 23 using namespace lldb_private; 24 25 char REPL::ID; 26 27 REPL::REPL(Target &target) : m_target(target) { 28 // Make sure all option values have sane defaults 29 Debugger &debugger = m_target.GetDebugger(); 30 debugger.SetShowProgress(false); 31 auto exe_ctx = debugger.GetCommandInterpreter().GetExecutionContext(); 32 m_format_options.OptionParsingStarting(&exe_ctx); 33 m_varobj_options.OptionParsingStarting(&exe_ctx); 34 } 35 36 REPL::~REPL() = default; 37 38 lldb::REPLSP REPL::Create(Status &err, lldb::LanguageType language, 39 Debugger *debugger, Target *target, 40 const char *repl_options) { 41 uint32_t idx = 0; 42 lldb::REPLSP ret; 43 44 while (REPLCreateInstance create_instance = 45 PluginManager::GetREPLCreateCallbackAtIndex(idx)) { 46 LanguageSet supported_languages = 47 PluginManager::GetREPLSupportedLanguagesAtIndex(idx++); 48 if (!supported_languages[language]) 49 continue; 50 ret = (*create_instance)(err, language, debugger, target, repl_options); 51 if (ret) { 52 break; 53 } 54 } 55 56 return ret; 57 } 58 59 std::string REPL::GetSourcePath() { 60 llvm::StringRef file_basename = GetSourceFileBasename(); 61 FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir(); 62 if (tmpdir_file_spec) { 63 tmpdir_file_spec.SetFilename(file_basename); 64 m_repl_source_path = tmpdir_file_spec.GetPath(); 65 } else { 66 tmpdir_file_spec = FileSpec("/tmp"); 67 tmpdir_file_spec.AppendPathComponent(file_basename); 68 } 69 70 return tmpdir_file_spec.GetPath(); 71 } 72 73 lldb::IOHandlerSP REPL::GetIOHandler() { 74 if (!m_io_handler_sp) { 75 Debugger &debugger = m_target.GetDebugger(); 76 m_io_handler_sp = std::make_shared<IOHandlerEditline>( 77 debugger, IOHandler::Type::REPL, 78 "lldb-repl", // Name of input reader for history 79 llvm::StringRef("> "), // prompt 80 llvm::StringRef(". "), // Continuation prompt 81 true, // Multi-line 82 true, // The REPL prompt is always colored 83 1, // Line number 84 *this); 85 86 // Don't exit if CTRL+C is pressed 87 static_cast<IOHandlerEditline *>(m_io_handler_sp.get()) 88 ->SetInterruptExits(false); 89 90 if (m_io_handler_sp->GetIsInteractive() && 91 m_io_handler_sp->GetIsRealTerminal()) { 92 m_indent_str.assign(debugger.GetTabSize(), ' '); 93 m_enable_auto_indent = debugger.GetAutoIndent(); 94 } else { 95 m_indent_str.clear(); 96 m_enable_auto_indent = false; 97 } 98 } 99 return m_io_handler_sp; 100 } 101 102 void REPL::IOHandlerActivated(IOHandler &io_handler, bool interactive) { 103 lldb::ProcessSP process_sp = m_target.GetProcessSP(); 104 if (process_sp && process_sp->IsAlive()) 105 return; 106 lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP()); 107 error_sp->Printf("REPL requires a running target process.\n"); 108 io_handler.SetIsDone(true); 109 } 110 111 bool REPL::IOHandlerInterrupt(IOHandler &io_handler) { return false; } 112 113 void REPL::IOHandlerInputInterrupted(IOHandler &io_handler, std::string &line) { 114 } 115 116 const char *REPL::IOHandlerGetFixIndentationCharacters() { 117 return (m_enable_auto_indent ? GetAutoIndentCharacters() : nullptr); 118 } 119 120 llvm::StringRef REPL::IOHandlerGetControlSequence(char ch) { 121 static constexpr llvm::StringLiteral control_sequence(":quit\n"); 122 if (ch == 'd') 123 return control_sequence; 124 return {}; 125 } 126 127 const char *REPL::IOHandlerGetCommandPrefix() { return ":"; } 128 129 const char *REPL::IOHandlerGetHelpPrologue() { 130 return "\nThe REPL (Read-Eval-Print-Loop) acts like an interpreter. " 131 "Valid statements, expressions, and declarations are immediately " 132 "compiled and executed.\n\n" 133 "The complete set of LLDB debugging commands are also available as " 134 "described below.\n\nCommands " 135 "must be prefixed with a colon at the REPL prompt (:quit for " 136 "example.) Typing just a colon " 137 "followed by return will switch to the LLDB prompt.\n\n" 138 "Type “< path” to read in code from a text file “path”.\n\n"; 139 } 140 141 bool REPL::IOHandlerIsInputComplete(IOHandler &io_handler, StringList &lines) { 142 // Check for meta command 143 const size_t num_lines = lines.GetSize(); 144 if (num_lines == 1) { 145 const char *first_line = lines.GetStringAtIndex(0); 146 if (first_line[0] == ':') 147 return true; // Meta command is a single line where that starts with ':' 148 } 149 150 // Check if REPL input is done 151 std::string source_string(lines.CopyList()); 152 return SourceIsComplete(source_string); 153 } 154 155 int REPL::CalculateActualIndentation(const StringList &lines) { 156 std::string last_line = lines[lines.GetSize() - 1]; 157 158 int actual_indent = 0; 159 for (char &ch : last_line) { 160 if (ch != ' ') 161 break; 162 ++actual_indent; 163 } 164 165 return actual_indent; 166 } 167 168 int REPL::IOHandlerFixIndentation(IOHandler &io_handler, 169 const StringList &lines, 170 int cursor_position) { 171 if (!m_enable_auto_indent) 172 return 0; 173 174 if (!lines.GetSize()) { 175 return 0; 176 } 177 178 int tab_size = io_handler.GetDebugger().GetTabSize(); 179 180 lldb::offset_t desired_indent = 181 GetDesiredIndentation(lines, cursor_position, tab_size); 182 183 int actual_indent = REPL::CalculateActualIndentation(lines); 184 185 if (desired_indent == LLDB_INVALID_OFFSET) 186 return 0; 187 188 return (int)desired_indent - actual_indent; 189 } 190 191 static bool ReadCode(const std::string &path, std::string &code, 192 lldb::StreamFileSP &error_sp) { 193 auto &fs = FileSystem::Instance(); 194 llvm::Twine pathTwine(path); 195 if (!fs.Exists(pathTwine)) { 196 error_sp->Printf("no such file at path '%s'\n", path.c_str()); 197 return false; 198 } 199 if (!fs.Readable(pathTwine)) { 200 error_sp->Printf("could not read file at path '%s'\n", path.c_str()); 201 return false; 202 } 203 const size_t file_size = fs.GetByteSize(pathTwine); 204 const size_t max_size = code.max_size(); 205 if (file_size > max_size) { 206 error_sp->Printf("file at path '%s' too large: " 207 "file_size = %zu, max_size = %zu\n", 208 path.c_str(), file_size, max_size); 209 return false; 210 } 211 auto data_sp = fs.CreateDataBuffer(pathTwine); 212 if (data_sp == nullptr) { 213 error_sp->Printf("could not create buffer for file at path '%s'\n", 214 path.c_str()); 215 return false; 216 } 217 code.assign((const char *)data_sp->GetBytes(), data_sp->GetByteSize()); 218 return true; 219 } 220 221 void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) { 222 lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFileSP()); 223 lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP()); 224 bool extra_line = false; 225 bool did_quit = false; 226 227 if (code.empty()) { 228 m_code.AppendString(""); 229 static_cast<IOHandlerEditline &>(io_handler) 230 .SetBaseLineNumber(m_code.GetSize() + 1); 231 } else { 232 Debugger &debugger = m_target.GetDebugger(); 233 CommandInterpreter &ci = debugger.GetCommandInterpreter(); 234 extra_line = ci.GetSpaceReplPrompts(); 235 236 ExecutionContext exe_ctx(m_target.GetProcessSP() 237 ->GetThreadList() 238 .GetSelectedThread() 239 ->GetSelectedFrame(DoNoSelectMostRelevantFrame) 240 .get()); 241 242 lldb::ProcessSP process_sp(exe_ctx.GetProcessSP()); 243 244 if (code[0] == ':') { 245 // Meta command 246 // Strip the ':' 247 code.erase(0, 1); 248 if (!llvm::StringRef(code).trim().empty()) { 249 // "lldb" was followed by arguments, so just execute the command dump 250 // the results 251 252 // Turn off prompt on quit in case the user types ":quit" 253 const bool saved_prompt_on_quit = ci.GetPromptOnQuit(); 254 if (saved_prompt_on_quit) 255 ci.SetPromptOnQuit(false); 256 257 // Execute the command 258 CommandReturnObject result(debugger.GetUseColor()); 259 result.SetImmediateOutputStream(output_sp); 260 result.SetImmediateErrorStream(error_sp); 261 ci.HandleCommand(code.c_str(), eLazyBoolNo, result); 262 263 if (saved_prompt_on_quit) 264 ci.SetPromptOnQuit(true); 265 266 if (result.GetStatus() == lldb::eReturnStatusQuit) { 267 did_quit = true; 268 io_handler.SetIsDone(true); 269 if (debugger.CheckTopIOHandlerTypes( 270 IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) { 271 // We typed "quit" or an alias to quit so we need to check if the 272 // command interpreter is above us and tell it that it is done as 273 // well so we don't drop back into the command interpreter if we 274 // have already quit 275 lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler()); 276 if (io_handler_sp) 277 io_handler_sp->SetIsDone(true); 278 } 279 } 280 } else { 281 // ":" was followed by no arguments, so push the LLDB command prompt 282 if (debugger.CheckTopIOHandlerTypes( 283 IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) { 284 // If the user wants to get back to the command interpreter and the 285 // command interpreter is what launched the REPL, then just let the 286 // REPL exit and fall back to the command interpreter. 287 io_handler.SetIsDone(true); 288 } else { 289 // The REPL wasn't launched the by the command interpreter, it is the 290 // base IOHandler, so we need to get the command interpreter and 291 lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler()); 292 if (io_handler_sp) { 293 io_handler_sp->SetIsDone(false); 294 debugger.RunIOHandlerAsync(ci.GetIOHandler()); 295 } 296 } 297 } 298 } else { 299 if (code[0] == '<') { 300 // User wants to read code from a file. 301 // Interpret rest of line as a literal path. 302 auto path = llvm::StringRef(code.substr(1)).trim().str(); 303 if (!ReadCode(path, code, error_sp)) { 304 return; 305 } 306 } 307 308 // Unwind any expression we might have been running in case our REPL 309 // expression crashed and the user was looking around 310 if (m_dedicated_repl_mode) { 311 Thread *thread = exe_ctx.GetThreadPtr(); 312 if (thread && thread->UnwindInnermostExpression().Success()) { 313 thread->SetSelectedFrameByIndex(0, false); 314 exe_ctx.SetFrameSP( 315 thread->GetSelectedFrame(DoNoSelectMostRelevantFrame)); 316 } 317 } 318 319 const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors(); 320 321 EvaluateExpressionOptions expr_options = m_expr_options; 322 expr_options.SetCoerceToId(m_varobj_options.use_objc); 323 expr_options.SetKeepInMemory(true); 324 expr_options.SetUseDynamic(m_varobj_options.use_dynamic); 325 expr_options.SetGenerateDebugInfo(true); 326 expr_options.SetREPLEnabled(true); 327 expr_options.SetColorizeErrors(colorize_err); 328 expr_options.SetPoundLine(m_repl_source_path.c_str(), 329 m_code.GetSize() + 1); 330 331 expr_options.SetLanguage(GetLanguage()); 332 333 PersistentExpressionState *persistent_state = 334 m_target.GetPersistentExpressionStateForLanguage(GetLanguage()); 335 if (!persistent_state) 336 return; 337 338 const size_t var_count_before = persistent_state->GetSize(); 339 340 const char *expr_prefix = nullptr; 341 lldb::ValueObjectSP result_valobj_sp; 342 Status error; 343 lldb::ExpressionResults execution_results = 344 UserExpression::Evaluate(exe_ctx, expr_options, code.c_str(), 345 expr_prefix, result_valobj_sp, error, 346 nullptr); // fixed expression 347 348 if (llvm::Error err = OnExpressionEvaluated(exe_ctx, code, expr_options, 349 execution_results, 350 result_valobj_sp, error)) { 351 *error_sp << llvm::toString(std::move(err)) << "\n"; 352 } else if (process_sp && process_sp->IsAlive()) { 353 bool add_to_code = true; 354 bool handled = false; 355 if (result_valobj_sp) { 356 lldb::Format format = m_format_options.GetFormat(); 357 358 if (result_valobj_sp->GetError().Success()) { 359 handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp); 360 } else if (result_valobj_sp->GetError().GetError() == 361 UserExpression::kNoResult) { 362 if (format != lldb::eFormatVoid && debugger.GetNotifyVoid()) { 363 error_sp->PutCString("(void)\n"); 364 handled = true; 365 } 366 } 367 } 368 369 if (debugger.GetPrintDecls()) { 370 for (size_t vi = var_count_before, ve = persistent_state->GetSize(); 371 vi != ve; ++vi) { 372 lldb::ExpressionVariableSP persistent_var_sp = 373 persistent_state->GetVariableAtIndex(vi); 374 lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject(); 375 376 PrintOneVariable(debugger, output_sp, valobj_sp, 377 persistent_var_sp.get()); 378 } 379 } 380 381 if (!handled) { 382 bool useColors = error_sp->GetFile().GetIsTerminalWithColors(); 383 switch (execution_results) { 384 case lldb::eExpressionSetupError: 385 case lldb::eExpressionParseError: 386 add_to_code = false; 387 [[fallthrough]]; 388 case lldb::eExpressionDiscarded: 389 error_sp->Printf("%s\n", error.AsCString()); 390 break; 391 392 case lldb::eExpressionCompleted: 393 break; 394 case lldb::eExpressionInterrupted: 395 if (useColors) { 396 error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED)); 397 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD)); 398 } 399 error_sp->Printf("Execution interrupted. "); 400 if (useColors) 401 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL)); 402 error_sp->Printf("Enter code to recover and continue.\nEnter LLDB " 403 "commands to investigate (type :help for " 404 "assistance.)\n"); 405 break; 406 407 case lldb::eExpressionHitBreakpoint: 408 // Breakpoint was hit, drop into LLDB command interpreter 409 if (useColors) { 410 error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED)); 411 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD)); 412 } 413 output_sp->Printf("Execution stopped at breakpoint. "); 414 if (useColors) 415 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL)); 416 output_sp->Printf("Enter LLDB commands to investigate (type help " 417 "for assistance.)\n"); 418 { 419 lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler()); 420 if (io_handler_sp) { 421 io_handler_sp->SetIsDone(false); 422 debugger.RunIOHandlerAsync(ci.GetIOHandler()); 423 } 424 } 425 break; 426 427 case lldb::eExpressionTimedOut: 428 error_sp->Printf("error: timeout\n"); 429 if (error.AsCString()) 430 error_sp->Printf("error: %s\n", error.AsCString()); 431 break; 432 case lldb::eExpressionResultUnavailable: 433 // Shoulnd't happen??? 434 error_sp->Printf("error: could not fetch result -- %s\n", 435 error.AsCString()); 436 break; 437 case lldb::eExpressionStoppedForDebug: 438 // Shoulnd't happen??? 439 error_sp->Printf("error: stopped for debug -- %s\n", 440 error.AsCString()); 441 break; 442 case lldb::eExpressionThreadVanished: 443 // Shoulnd't happen??? 444 error_sp->Printf("error: expression thread vanished -- %s\n", 445 error.AsCString()); 446 break; 447 } 448 } 449 450 if (add_to_code) { 451 const uint32_t new_default_line = m_code.GetSize() + 1; 452 453 m_code.SplitIntoLines(code); 454 455 // Update our code on disk 456 if (!m_repl_source_path.empty()) { 457 auto file = FileSystem::Instance().Open( 458 FileSpec(m_repl_source_path), 459 File::eOpenOptionWriteOnly | File::eOpenOptionTruncate | 460 File::eOpenOptionCanCreate, 461 lldb::eFilePermissionsFileDefault); 462 if (file) { 463 std::string code(m_code.CopyList()); 464 code.append(1, '\n'); 465 size_t bytes_written = code.size(); 466 file.get()->Write(code.c_str(), bytes_written); 467 file.get()->Close(); 468 } else { 469 std::string message = llvm::toString(file.takeError()); 470 error_sp->Printf("error: couldn't open %s: %s\n", 471 m_repl_source_path.c_str(), message.c_str()); 472 } 473 474 // Now set the default file and line to the REPL source file 475 m_target.GetSourceManager().SetDefaultFileAndLine( 476 FileSpec(m_repl_source_path), new_default_line); 477 } 478 static_cast<IOHandlerEditline &>(io_handler) 479 .SetBaseLineNumber(m_code.GetSize() + 1); 480 } 481 if (extra_line) { 482 output_sp->Printf("\n"); 483 } 484 } 485 } 486 487 // Don't complain about the REPL process going away if we are in the 488 // process of quitting. 489 if (!did_quit && (!process_sp || !process_sp->IsAlive())) { 490 error_sp->Printf( 491 "error: REPL process is no longer alive, exiting REPL\n"); 492 io_handler.SetIsDone(true); 493 } 494 } 495 } 496 497 void REPL::IOHandlerComplete(IOHandler &io_handler, 498 CompletionRequest &request) { 499 // Complete an LLDB command if the first character is a colon... 500 if (request.GetRawLine().starts_with(":")) { 501 Debugger &debugger = m_target.GetDebugger(); 502 503 // auto complete LLDB commands 504 llvm::StringRef new_line = request.GetRawLine().drop_front(); 505 CompletionResult sub_result; 506 CompletionRequest sub_request(new_line, request.GetRawCursorPos() - 1, 507 sub_result); 508 debugger.GetCommandInterpreter().HandleCompletion(sub_request); 509 StringList matches, descriptions; 510 sub_result.GetMatches(matches); 511 // Prepend command prefix that was excluded in the completion request. 512 if (request.GetCursorIndex() == 0) 513 for (auto &match : matches) 514 match.insert(0, 1, ':'); 515 sub_result.GetDescriptions(descriptions); 516 request.AddCompletions(matches, descriptions); 517 return; 518 } 519 520 // Strip spaces from the line and see if we had only spaces 521 if (request.GetRawLine().trim().empty()) { 522 // Only spaces on this line, so just indent 523 request.AddCompletion(m_indent_str); 524 return; 525 } 526 527 std::string current_code; 528 current_code.append(m_code.CopyList()); 529 530 IOHandlerEditline &editline = static_cast<IOHandlerEditline &>(io_handler); 531 StringList current_lines = editline.GetCurrentLines(); 532 const uint32_t current_line_idx = editline.GetCurrentLineIndex(); 533 534 if (current_line_idx < current_lines.GetSize()) { 535 for (uint32_t i = 0; i < current_line_idx; ++i) { 536 const char *line_cstr = current_lines.GetStringAtIndex(i); 537 if (line_cstr) { 538 current_code.append("\n"); 539 current_code.append(line_cstr); 540 } 541 } 542 } 543 544 current_code.append("\n"); 545 current_code += request.GetRawLine(); 546 547 CompleteCode(current_code, request); 548 } 549 550 bool QuitCommandOverrideCallback(void *baton, const char **argv) { 551 Target *target = (Target *)baton; 552 lldb::ProcessSP process_sp(target->GetProcessSP()); 553 if (process_sp) { 554 process_sp->Destroy(false); 555 process_sp->GetTarget().GetDebugger().ClearIOHandlers(); 556 } 557 return false; 558 } 559 560 Status REPL::RunLoop() { 561 Status error; 562 563 error = DoInitialization(); 564 m_repl_source_path = GetSourcePath(); 565 566 if (!error.Success()) 567 return error; 568 569 Debugger &debugger = m_target.GetDebugger(); 570 571 lldb::IOHandlerSP io_handler_sp(GetIOHandler()); 572 573 FileSpec save_default_file; 574 uint32_t save_default_line = 0; 575 576 if (!m_repl_source_path.empty()) { 577 // Save the current default file and line 578 m_target.GetSourceManager().GetDefaultFileAndLine(save_default_file, 579 save_default_line); 580 } 581 582 debugger.RunIOHandlerAsync(io_handler_sp); 583 584 // Check if we are in dedicated REPL mode where LLDB was start with the "-- 585 // repl" option from the command line. Currently we know this by checking if 586 // the debugger already has a IOHandler thread. 587 if (!debugger.HasIOHandlerThread()) { 588 // The debugger doesn't have an existing IOHandler thread, so this must be 589 // dedicated REPL mode... 590 m_dedicated_repl_mode = true; 591 debugger.StartIOHandlerThread(); 592 llvm::StringRef command_name_str("quit"); 593 CommandObject *cmd_obj = 594 debugger.GetCommandInterpreter().GetCommandObjectForCommand( 595 command_name_str); 596 if (cmd_obj) { 597 assert(command_name_str.empty()); 598 cmd_obj->SetOverrideCallback(QuitCommandOverrideCallback, &m_target); 599 } 600 } 601 602 // Wait for the REPL command interpreter to get popped 603 io_handler_sp->WaitForPop(); 604 605 if (m_dedicated_repl_mode) { 606 // If we were in dedicated REPL mode we would have started the IOHandler 607 // thread, and we should kill our process 608 lldb::ProcessSP process_sp = m_target.GetProcessSP(); 609 if (process_sp && process_sp->IsAlive()) 610 process_sp->Destroy(false); 611 612 // Wait for the IO handler thread to exit (TODO: don't do this if the IO 613 // handler thread already exists...) 614 debugger.JoinIOHandlerThread(); 615 } 616 617 // Restore the default file and line 618 if (save_default_file && save_default_line != 0) 619 m_target.GetSourceManager().SetDefaultFileAndLine(save_default_file, 620 save_default_line); 621 return error; 622 } 623