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