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