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