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