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