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