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