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