1 //===-- REPL.cpp ------------------------------------------------*- C++ -*-===// 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/Target.h" 19 #include "lldb/Target/Thread.h" 20 #include "lldb/Utility/AnsiTerminal.h" 21 22 #include <memory> 23 24 using namespace lldb_private; 25 26 REPL::REPL(LLVMCastKind kind, Target &target) : m_target(target), m_kind(kind) { 27 // Make sure all option values have sane defaults 28 Debugger &debugger = m_target.GetDebugger(); 29 auto exe_ctx = debugger.GetCommandInterpreter().GetExecutionContext(); 30 m_format_options.OptionParsingStarting(&exe_ctx); 31 m_varobj_options.OptionParsingStarting(&exe_ctx); 32 m_command_options.OptionParsingStarting(&exe_ctx); 33 34 // Default certain settings for REPL regardless of the global settings. 35 m_command_options.unwind_on_error = false; 36 m_command_options.ignore_breakpoints = false; 37 m_command_options.debug = false; 38 } 39 40 REPL::~REPL() = default; 41 42 lldb::REPLSP REPL::Create(Status &err, lldb::LanguageType language, 43 Debugger *debugger, Target *target, 44 const char *repl_options) { 45 uint32_t idx = 0; 46 lldb::REPLSP ret; 47 48 while (REPLCreateInstance create_instance = 49 PluginManager::GetREPLCreateCallbackAtIndex(idx++)) { 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 ConstString file_basename = GetSourceFileBasename(); 61 FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir(); 62 if (tmpdir_file_spec) { 63 tmpdir_file_spec.GetFilename().SetCString(file_basename.AsCString()); 64 m_repl_source_path = tmpdir_file_spec.GetPath(); 65 } else { 66 tmpdir_file_spec = FileSpec("/tmp"); 67 tmpdir_file_spec.AppendPathComponent(file_basename.AsCString()); 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) { 103 lldb::ProcessSP process_sp = m_target.GetProcessSP(); 104 if (process_sp && process_sp->IsAlive()) 105 return; 106 lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFile()); 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 ConstString REPL::IOHandlerGetControlSequence(char ch) { 121 if (ch == 'd') 122 return ConstString(":quit\n"); 123 return ConstString(); 124 } 125 126 const char *REPL::IOHandlerGetCommandPrefix() { return ":"; } 127 128 const char *REPL::IOHandlerGetHelpPrologue() { 129 return "\nThe REPL (Read-Eval-Print-Loop) acts like an interpreter. " 130 "Valid statements, expressions, and declarations are immediately " 131 "compiled and executed.\n\n" 132 "The complete set of LLDB debugging commands are also available as " 133 "described below. Commands " 134 "must be prefixed with a colon at the REPL prompt (:quit for " 135 "example.) Typing just a colon " 136 "followed by return will switch to the LLDB prompt.\n\n"; 137 } 138 139 bool REPL::IOHandlerIsInputComplete(IOHandler &io_handler, StringList &lines) { 140 // Check for meta command 141 const size_t num_lines = lines.GetSize(); 142 if (num_lines == 1) { 143 const char *first_line = lines.GetStringAtIndex(0); 144 if (first_line[0] == ':') 145 return true; // Meta command is a single line where that starts with ':' 146 } 147 148 // Check if REPL input is done 149 std::string source_string(lines.CopyList()); 150 return SourceIsComplete(source_string); 151 } 152 153 int REPL::CalculateActualIndentation(const StringList &lines) { 154 std::string last_line = lines[lines.GetSize() - 1]; 155 156 int actual_indent = 0; 157 for (char &ch : last_line) { 158 if (ch != ' ') 159 break; 160 ++actual_indent; 161 } 162 163 return actual_indent; 164 } 165 166 int REPL::IOHandlerFixIndentation(IOHandler &io_handler, 167 const StringList &lines, 168 int cursor_position) { 169 if (!m_enable_auto_indent) 170 return 0; 171 172 if (!lines.GetSize()) { 173 return 0; 174 } 175 176 int tab_size = io_handler.GetDebugger().GetTabSize(); 177 178 lldb::offset_t desired_indent = 179 GetDesiredIndentation(lines, cursor_position, tab_size); 180 181 int actual_indent = REPL::CalculateActualIndentation(lines); 182 183 if (desired_indent == LLDB_INVALID_OFFSET) 184 return 0; 185 186 return (int)desired_indent - actual_indent; 187 } 188 189 void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) { 190 lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFile()); 191 lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFile()); 192 bool extra_line = false; 193 bool did_quit = false; 194 195 if (code.empty()) { 196 m_code.AppendString(""); 197 static_cast<IOHandlerEditline &>(io_handler) 198 .SetBaseLineNumber(m_code.GetSize() + 1); 199 } else { 200 Debugger &debugger = m_target.GetDebugger(); 201 CommandInterpreter &ci = debugger.GetCommandInterpreter(); 202 extra_line = ci.GetSpaceReplPrompts(); 203 204 ExecutionContext exe_ctx(m_target.GetProcessSP() 205 ->GetThreadList() 206 .GetSelectedThread() 207 ->GetSelectedFrame() 208 .get()); 209 210 lldb::ProcessSP process_sp(exe_ctx.GetProcessSP()); 211 212 if (code[0] == ':') { 213 // Meta command 214 // Strip the ':' 215 code.erase(0, 1); 216 if (Args::StripSpaces(code)) { 217 // "lldb" was followed by arguments, so just execute the command dump 218 // the results 219 220 // Turn off prompt on quit in case the user types ":quit" 221 const bool saved_prompt_on_quit = ci.GetPromptOnQuit(); 222 if (saved_prompt_on_quit) 223 ci.SetPromptOnQuit(false); 224 225 // Execute the command 226 CommandReturnObject result; 227 result.SetImmediateOutputStream(output_sp); 228 result.SetImmediateErrorStream(error_sp); 229 ci.HandleCommand(code.c_str(), eLazyBoolNo, result); 230 231 if (saved_prompt_on_quit) 232 ci.SetPromptOnQuit(true); 233 234 if (result.GetStatus() == lldb::eReturnStatusQuit) { 235 did_quit = true; 236 io_handler.SetIsDone(true); 237 if (debugger.CheckTopIOHandlerTypes( 238 IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) { 239 // We typed "quit" or an alias to quit so we need to check if the 240 // command interpreter is above us and tell it that it is done as 241 // well so we don't drop back into the command interpreter if we 242 // have already quit 243 lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler()); 244 if (io_handler_sp) 245 io_handler_sp->SetIsDone(true); 246 } 247 } 248 } else { 249 // ":" was followed by no arguments, so push the LLDB command prompt 250 if (debugger.CheckTopIOHandlerTypes( 251 IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) { 252 // If the user wants to get back to the command interpreter and the 253 // command interpreter is what launched the REPL, then just let the 254 // REPL exit and fall back to the command interpreter. 255 io_handler.SetIsDone(true); 256 } else { 257 // The REPL wasn't launched the by the command interpreter, it is the 258 // base IOHandler, so we need to get the command interpreter and 259 lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler()); 260 if (io_handler_sp) { 261 io_handler_sp->SetIsDone(false); 262 debugger.PushIOHandler(ci.GetIOHandler()); 263 } 264 } 265 } 266 } else { 267 // Unwind any expression we might have been running in case our REPL 268 // expression crashed and the user was looking around 269 if (m_dedicated_repl_mode) { 270 Thread *thread = exe_ctx.GetThreadPtr(); 271 if (thread && thread->UnwindInnermostExpression().Success()) { 272 thread->SetSelectedFrameByIndex(0, false); 273 exe_ctx.SetFrameSP(thread->GetSelectedFrame()); 274 } 275 } 276 277 const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors(); 278 279 EvaluateExpressionOptions expr_options; 280 expr_options.SetCoerceToId(m_varobj_options.use_objc); 281 expr_options.SetUnwindOnError(m_command_options.unwind_on_error); 282 expr_options.SetIgnoreBreakpoints(m_command_options.ignore_breakpoints); 283 expr_options.SetKeepInMemory(true); 284 expr_options.SetUseDynamic(m_varobj_options.use_dynamic); 285 expr_options.SetTryAllThreads(m_command_options.try_all_threads); 286 expr_options.SetGenerateDebugInfo(true); 287 expr_options.SetREPLEnabled(true); 288 expr_options.SetColorizeErrors(colorize_err); 289 expr_options.SetPoundLine(m_repl_source_path.c_str(), 290 m_code.GetSize() + 1); 291 if (m_command_options.timeout > 0) 292 expr_options.SetTimeout(std::chrono::microseconds(m_command_options.timeout)); 293 else 294 expr_options.SetTimeout(llvm::None); 295 296 expr_options.SetLanguage(GetLanguage()); 297 298 PersistentExpressionState *persistent_state = 299 m_target.GetPersistentExpressionStateForLanguage(GetLanguage()); 300 301 const size_t var_count_before = persistent_state->GetSize(); 302 303 const char *expr_prefix = nullptr; 304 lldb::ValueObjectSP result_valobj_sp; 305 Status error; 306 lldb::ModuleSP jit_module_sp; 307 lldb::ExpressionResults execution_results = 308 UserExpression::Evaluate(exe_ctx, expr_options, code.c_str(), 309 expr_prefix, result_valobj_sp, error, 310 0, // Line offset 311 nullptr, // Fixed Expression 312 &jit_module_sp); 313 314 // CommandInterpreter &ci = debugger.GetCommandInterpreter(); 315 316 if (process_sp && process_sp->IsAlive()) { 317 bool add_to_code = true; 318 bool handled = false; 319 if (result_valobj_sp) { 320 lldb::Format format = m_format_options.GetFormat(); 321 322 if (result_valobj_sp->GetError().Success()) { 323 handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp); 324 } else if (result_valobj_sp->GetError().GetError() == 325 UserExpression::kNoResult) { 326 if (format != lldb::eFormatVoid && debugger.GetNotifyVoid()) { 327 error_sp->PutCString("(void)\n"); 328 handled = true; 329 } 330 } 331 } 332 333 if (debugger.GetPrintDecls()) { 334 for (size_t vi = var_count_before, ve = persistent_state->GetSize(); 335 vi != ve; ++vi) { 336 lldb::ExpressionVariableSP persistent_var_sp = 337 persistent_state->GetVariableAtIndex(vi); 338 lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject(); 339 340 PrintOneVariable(debugger, output_sp, valobj_sp, 341 persistent_var_sp.get()); 342 } 343 } 344 345 if (!handled) { 346 bool useColors = error_sp->GetFile().GetIsTerminalWithColors(); 347 switch (execution_results) { 348 case lldb::eExpressionSetupError: 349 case lldb::eExpressionParseError: 350 add_to_code = false; 351 LLVM_FALLTHROUGH; 352 case lldb::eExpressionDiscarded: 353 error_sp->Printf("%s\n", error.AsCString()); 354 break; 355 356 case lldb::eExpressionCompleted: 357 break; 358 case lldb::eExpressionInterrupted: 359 if (useColors) { 360 error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED)); 361 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD)); 362 } 363 error_sp->Printf("Execution interrupted. "); 364 if (useColors) 365 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL)); 366 error_sp->Printf("Enter code to recover and continue.\nEnter LLDB " 367 "commands to investigate (type :help for " 368 "assistance.)\n"); 369 break; 370 371 case lldb::eExpressionHitBreakpoint: 372 // Breakpoint was hit, drop into LLDB command interpreter 373 if (useColors) { 374 error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED)); 375 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD)); 376 } 377 output_sp->Printf("Execution stopped at breakpoint. "); 378 if (useColors) 379 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL)); 380 output_sp->Printf("Enter LLDB commands to investigate (type help " 381 "for assistance.)\n"); 382 { 383 lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler()); 384 if (io_handler_sp) { 385 io_handler_sp->SetIsDone(false); 386 debugger.PushIOHandler(ci.GetIOHandler()); 387 } 388 } 389 break; 390 391 case lldb::eExpressionTimedOut: 392 error_sp->Printf("error: timeout\n"); 393 if (error.AsCString()) 394 error_sp->Printf("error: %s\n", error.AsCString()); 395 break; 396 case lldb::eExpressionResultUnavailable: 397 // Shoulnd't happen??? 398 error_sp->Printf("error: could not fetch result -- %s\n", 399 error.AsCString()); 400 break; 401 case lldb::eExpressionStoppedForDebug: 402 // Shoulnd't happen??? 403 error_sp->Printf("error: stopped for debug -- %s\n", 404 error.AsCString()); 405 break; 406 } 407 } 408 409 if (add_to_code) { 410 const uint32_t new_default_line = m_code.GetSize() + 1; 411 412 m_code.SplitIntoLines(code); 413 414 // Update our code on disk 415 if (!m_repl_source_path.empty()) { 416 lldb_private::File file; 417 FileSystem::Instance().Open(file, FileSpec(m_repl_source_path), 418 File::eOpenOptionWrite | 419 File::eOpenOptionTruncate | 420 File::eOpenOptionCanCreate, 421 lldb::eFilePermissionsFileDefault); 422 std::string code(m_code.CopyList()); 423 code.append(1, '\n'); 424 size_t bytes_written = code.size(); 425 file.Write(code.c_str(), bytes_written); 426 file.Close(); 427 428 // Now set the default file and line to the REPL source file 429 m_target.GetSourceManager().SetDefaultFileAndLine( 430 FileSpec(m_repl_source_path), new_default_line); 431 } 432 static_cast<IOHandlerEditline &>(io_handler) 433 .SetBaseLineNumber(m_code.GetSize() + 1); 434 } 435 if (extra_line) { 436 fprintf(output_sp->GetFile().GetStream(), "\n"); 437 } 438 } 439 } 440 441 // Don't complain about the REPL process going away if we are in the 442 // process of quitting. 443 if (!did_quit && (!process_sp || !process_sp->IsAlive())) { 444 error_sp->Printf( 445 "error: REPL process is no longer alive, exiting REPL\n"); 446 io_handler.SetIsDone(true); 447 } 448 } 449 } 450 451 int REPL::IOHandlerComplete(IOHandler &io_handler, const char *current_line, 452 const char *cursor, const char *last_char, 453 int skip_first_n_matches, int max_matches, 454 StringList &matches, StringList &descriptions) { 455 matches.Clear(); 456 457 llvm::StringRef line(current_line, cursor - current_line); 458 459 // Complete an LLDB command if the first character is a colon... 460 if (!line.empty() && line[0] == ':') { 461 Debugger &debugger = m_target.GetDebugger(); 462 463 // auto complete LLDB commands 464 const char *lldb_current_line = line.substr(1).data(); 465 return debugger.GetCommandInterpreter().HandleCompletion( 466 lldb_current_line, cursor, last_char, skip_first_n_matches, max_matches, 467 matches, descriptions); 468 } 469 470 // Strip spaces from the line and see if we had only spaces 471 line = line.ltrim(); 472 if (line.empty()) { 473 // Only spaces on this line, so just indent 474 matches.AppendString(m_indent_str); 475 return 1; 476 } 477 478 std::string current_code; 479 current_code.append(m_code.CopyList()); 480 481 IOHandlerEditline &editline = static_cast<IOHandlerEditline &>(io_handler); 482 const StringList *current_lines = editline.GetCurrentLines(); 483 if (current_lines) { 484 const uint32_t current_line_idx = editline.GetCurrentLineIndex(); 485 486 if (current_line_idx < current_lines->GetSize()) { 487 for (uint32_t i = 0; i < current_line_idx; ++i) { 488 const char *line_cstr = current_lines->GetStringAtIndex(i); 489 if (line_cstr) { 490 current_code.append("\n"); 491 current_code.append(line_cstr); 492 } 493 } 494 } 495 } 496 497 if (cursor > current_line) { 498 current_code.append("\n"); 499 current_code.append(current_line, cursor - current_line); 500 } 501 502 return CompleteCode(current_code, matches); 503 } 504 505 bool QuitCommandOverrideCallback(void *baton, const char **argv) { 506 Target *target = (Target *)baton; 507 lldb::ProcessSP process_sp(target->GetProcessSP()); 508 if (process_sp) { 509 process_sp->Destroy(false); 510 process_sp->GetTarget().GetDebugger().ClearIOHandlers(); 511 } 512 return false; 513 } 514 515 Status REPL::RunLoop() { 516 Status error; 517 518 error = DoInitialization(); 519 m_repl_source_path = GetSourcePath(); 520 521 if (!error.Success()) 522 return error; 523 524 Debugger &debugger = m_target.GetDebugger(); 525 526 lldb::IOHandlerSP io_handler_sp(GetIOHandler()); 527 528 FileSpec save_default_file; 529 uint32_t save_default_line = 0; 530 531 if (!m_repl_source_path.empty()) { 532 // Save the current default file and line 533 m_target.GetSourceManager().GetDefaultFileAndLine(save_default_file, 534 save_default_line); 535 } 536 537 debugger.PushIOHandler(io_handler_sp); 538 539 // Check if we are in dedicated REPL mode where LLDB was start with the "-- 540 // repl" option from the command line. Currently we know this by checking if 541 // the debugger already has a IOHandler thread. 542 if (!debugger.HasIOHandlerThread()) { 543 // The debugger doesn't have an existing IOHandler thread, so this must be 544 // dedicated REPL mode... 545 m_dedicated_repl_mode = true; 546 debugger.StartIOHandlerThread(); 547 llvm::StringRef command_name_str("quit"); 548 CommandObject *cmd_obj = 549 debugger.GetCommandInterpreter().GetCommandObjectForCommand( 550 command_name_str); 551 if (cmd_obj) { 552 assert(command_name_str.empty()); 553 cmd_obj->SetOverrideCallback(QuitCommandOverrideCallback, &m_target); 554 } 555 } 556 557 // Wait for the REPL command interpreter to get popped 558 io_handler_sp->WaitForPop(); 559 560 if (m_dedicated_repl_mode) { 561 // If we were in dedicated REPL mode we would have started the IOHandler 562 // thread, and we should kill our process 563 lldb::ProcessSP process_sp = m_target.GetProcessSP(); 564 if (process_sp && process_sp->IsAlive()) 565 process_sp->Destroy(false); 566 567 // Wait for the IO handler thread to exit (TODO: don't do this if the IO 568 // handler thread already exists...) 569 debugger.JoinIOHandlerThread(); 570 } 571 572 // Restore the default file and line 573 if (save_default_file && save_default_line != 0) 574 m_target.GetSourceManager().SetDefaultFileAndLine(save_default_file, 575 save_default_line); 576 return error; 577 } 578