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