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