xref: /freebsd/contrib/llvm-project/lldb/include/lldb/Core/IOHandler.h (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
1 //===-- IOHandler.h ---------------------------------------------*- 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 #ifndef LLDB_CORE_IOHANDLER_H
10 #define LLDB_CORE_IOHANDLER_H
11 
12 #include "lldb/Core/ValueObjectList.h"
13 #include "lldb/Host/Config.h"
14 #include "lldb/Utility/CompletionRequest.h"
15 #include "lldb/Utility/Flags.h"
16 #include "lldb/Utility/Predicate.h"
17 #include "lldb/Utility/Stream.h"
18 #include "lldb/Utility/StringList.h"
19 #include "lldb/lldb-defines.h"
20 #include "lldb/lldb-forward.h"
21 #include "llvm/ADT/StringRef.h"
22 
23 #include <memory>
24 #include <mutex>
25 #include <optional>
26 #include <string>
27 #include <vector>
28 
29 #include <cstdint>
30 #include <cstdio>
31 
32 namespace lldb_private {
33 class Debugger;
34 } // namespace lldb_private
35 
36 namespace curses {
37 class Application;
38 typedef std::unique_ptr<Application> ApplicationAP;
39 } // namespace curses
40 
41 namespace lldb_private {
42 
43 class IOHandler {
44 public:
45   enum class Type {
46     CommandInterpreter,
47     CommandList,
48     Confirm,
49     Curses,
50     Expression,
51     REPL,
52     ProcessIO,
53     PythonInterpreter,
54     LuaInterpreter,
55     PythonCode,
56     Other
57   };
58 
59   IOHandler(Debugger &debugger, IOHandler::Type type);
60 
61   IOHandler(Debugger &debugger, IOHandler::Type type,
62             const lldb::FileSP &input_sp, const lldb::StreamFileSP &output_sp,
63             const lldb::StreamFileSP &error_sp, uint32_t flags);
64 
65   virtual ~IOHandler();
66 
67   // Each IOHandler gets to run until it is done. It should read data from the
68   // "in" and place output into "out" and "err and return when done.
69   virtual void Run() = 0;
70 
71   // Called when an input reader should relinquish its control so another can
72   // be pushed onto the IO handler stack, or so the current IO handler can pop
73   // itself off the stack
74 
75   virtual void Cancel() = 0;
76 
77   // Called when CTRL+C is pressed which usually causes
78   // Debugger::DispatchInputInterrupt to be called.
79 
80   virtual bool Interrupt() = 0;
81 
82   virtual void GotEOF() = 0;
83 
84   bool IsActive() { return m_active && !m_done; }
85 
86   void SetIsDone(bool b) { m_done = b; }
87 
88   bool GetIsDone() { return m_done; }
89 
90   Type GetType() const { return m_type; }
91 
92   virtual void Activate() { m_active = true; }
93 
94   virtual void Deactivate() { m_active = false; }
95 
96   virtual void TerminalSizeChanged() {}
97 
98   virtual const char *GetPrompt() {
99     // Prompt support isn't mandatory
100     return nullptr;
101   }
102 
103   virtual bool SetPrompt(llvm::StringRef prompt) {
104     // Prompt support isn't mandatory
105     return false;
106   }
107   bool SetPrompt(const char *) = delete;
108 
109   virtual llvm::StringRef GetControlSequence(char ch) { return {}; }
110 
111   virtual const char *GetCommandPrefix() { return nullptr; }
112 
113   virtual const char *GetHelpPrologue() { return nullptr; }
114 
115   int GetInputFD();
116 
117   int GetOutputFD();
118 
119   int GetErrorFD();
120 
121   FILE *GetInputFILE();
122 
123   FILE *GetOutputFILE();
124 
125   FILE *GetErrorFILE();
126 
127   lldb::FileSP GetInputFileSP();
128 
129   lldb::StreamFileSP GetOutputStreamFileSP();
130 
131   lldb::StreamFileSP GetErrorStreamFileSP();
132 
133   Debugger &GetDebugger() { return m_debugger; }
134 
135   void *GetUserData() { return m_user_data; }
136 
137   void SetUserData(void *user_data) { m_user_data = user_data; }
138 
139   Flags &GetFlags() { return m_flags; }
140 
141   const Flags &GetFlags() const { return m_flags; }
142 
143   /// Check if the input is being supplied interactively by a user
144   ///
145   /// This will return true if the input stream is a terminal (tty or
146   /// pty) and can cause IO handlers to do different things (like
147   /// for a confirmation when deleting all breakpoints).
148   bool GetIsInteractive();
149 
150   /// Check if the input is coming from a real terminal.
151   ///
152   /// A real terminal has a valid size with a certain number of rows
153   /// and columns. If this function returns true, then terminal escape
154   /// sequences are expected to work (cursor movement escape sequences,
155   /// clearing lines, etc).
156   bool GetIsRealTerminal();
157 
158   void SetPopped(bool b);
159 
160   void WaitForPop();
161 
162   virtual void PrintAsync(const char *s, size_t len, bool is_stdout);
163 
164   std::recursive_mutex &GetOutputMutex() { return m_output_mutex; }
165 
166 protected:
167   Debugger &m_debugger;
168   lldb::FileSP m_input_sp;
169   lldb::StreamFileSP m_output_sp;
170   lldb::StreamFileSP m_error_sp;
171   std::recursive_mutex m_output_mutex;
172   Predicate<bool> m_popped;
173   Flags m_flags;
174   Type m_type;
175   void *m_user_data;
176   bool m_done;
177   bool m_active;
178 
179 private:
180   IOHandler(const IOHandler &) = delete;
181   const IOHandler &operator=(const IOHandler &) = delete;
182 };
183 
184 /// A delegate class for use with IOHandler subclasses.
185 ///
186 /// The IOHandler delegate is designed to be mixed into classes so
187 /// they can use an IOHandler subclass to fetch input and notify the
188 /// object that inherits from this delegate class when a token is
189 /// received.
190 class IOHandlerDelegate {
191 public:
192   enum class Completion { None, LLDBCommand, Expression };
193 
194   IOHandlerDelegate(Completion completion = Completion::None)
195       : m_completion(completion) {}
196 
197   virtual ~IOHandlerDelegate() = default;
198 
199   virtual void IOHandlerActivated(IOHandler &io_handler, bool interactive) {}
200 
201   virtual void IOHandlerDeactivated(IOHandler &io_handler) {}
202 
203   virtual std::optional<std::string> IOHandlerSuggestion(IOHandler &io_handler,
204                                                          llvm::StringRef line);
205 
206   virtual void IOHandlerComplete(IOHandler &io_handler,
207                                  CompletionRequest &request);
208 
209   virtual const char *IOHandlerGetFixIndentationCharacters() { return nullptr; }
210 
211   /// Called when a new line is created or one of an identified set of
212   /// indentation characters is typed.
213   ///
214   /// This function determines how much indentation should be added
215   /// or removed to match the recommended amount for the final line.
216   ///
217   /// \param[in] io_handler
218   ///     The IOHandler that responsible for input.
219   ///
220   /// \param[in] lines
221   ///     The current input up to the line to be corrected.  Lines
222   ///     following the line containing the cursor are not included.
223   ///
224   /// \param[in] cursor_position
225   ///     The number of characters preceding the cursor on the final
226   ///     line at the time.
227   ///
228   /// \return
229   ///     Returns an integer describing the number of spaces needed
230   ///     to correct the indentation level.  Positive values indicate
231   ///     that spaces should be added, while negative values represent
232   ///     spaces that should be removed.
233   virtual int IOHandlerFixIndentation(IOHandler &io_handler,
234                                       const StringList &lines,
235                                       int cursor_position) {
236     return 0;
237   }
238 
239   /// Called when a line or lines have been retrieved.
240   ///
241   /// This function can handle the current line and possibly call
242   /// IOHandler::SetIsDone(true) when the IO handler is done like when
243   /// "quit" is entered as a command, of when an empty line is
244   /// received. It is up to the delegate to determine when a line
245   /// should cause a IOHandler to exit.
246   virtual void IOHandlerInputComplete(IOHandler &io_handler,
247                                       std::string &data) = 0;
248 
249   virtual void IOHandlerInputInterrupted(IOHandler &io_handler,
250                                          std::string &data) {}
251 
252   /// Called to determine whether typing enter after the last line in
253   /// \a lines should end input.  This function will not be called on
254   /// IOHandler objects that are getting single lines.
255   /// \param[in] io_handler
256   ///     The IOHandler that responsible for updating the lines.
257   ///
258   /// \param[in] lines
259   ///     The current multi-line content.  May be altered to provide
260   ///     alternative input when complete.
261   ///
262   /// \return
263   ///     Return an boolean to indicate whether input is complete,
264   ///     true indicates that no additional input is necessary, while
265   ///     false indicates that more input is required.
266   virtual bool IOHandlerIsInputComplete(IOHandler &io_handler,
267                                         StringList &lines) {
268     // Impose no requirements for input to be considered complete.  subclasses
269     // should do something more intelligent.
270     return true;
271   }
272 
273   virtual llvm::StringRef IOHandlerGetControlSequence(char ch) { return {}; }
274 
275   virtual const char *IOHandlerGetCommandPrefix() { return nullptr; }
276 
277   virtual const char *IOHandlerGetHelpPrologue() { return nullptr; }
278 
279   // Intercept the IOHandler::Interrupt() calls and do something.
280   //
281   // Return true if the interrupt was handled, false if the IOHandler should
282   // continue to try handle the interrupt itself.
283   virtual bool IOHandlerInterrupt(IOHandler &io_handler) { return false; }
284 
285 protected:
286   Completion m_completion; // Support for common builtin completions
287 };
288 
289 // IOHandlerDelegateMultiline
290 //
291 // A IOHandlerDelegate that handles terminating multi-line input when
292 // the last line is equal to "end_line" which is specified in the constructor.
293 class IOHandlerDelegateMultiline : public IOHandlerDelegate {
294 public:
295   IOHandlerDelegateMultiline(llvm::StringRef end_line,
296                              Completion completion = Completion::None)
297       : IOHandlerDelegate(completion), m_end_line(end_line.str() + "\n") {}
298 
299   ~IOHandlerDelegateMultiline() override = default;
300 
301   llvm::StringRef IOHandlerGetControlSequence(char ch) override {
302     if (ch == 'd')
303       return m_end_line;
304     return {};
305   }
306 
307   bool IOHandlerIsInputComplete(IOHandler &io_handler,
308                                 StringList &lines) override {
309     // Determine whether the end of input signal has been entered
310     const size_t num_lines = lines.GetSize();
311     const llvm::StringRef end_line =
312         llvm::StringRef(m_end_line).drop_back(1); // Drop '\n'
313     if (num_lines > 0 && llvm::StringRef(lines[num_lines - 1]) == end_line) {
314       // Remove the terminal line from "lines" so it doesn't appear in the
315       // resulting input and return true to indicate we are done getting lines
316       lines.PopBack();
317       return true;
318     }
319     return false;
320   }
321 
322 protected:
323   const std::string m_end_line;
324 };
325 
326 class IOHandlerEditline : public IOHandler {
327 public:
328   IOHandlerEditline(Debugger &debugger, IOHandler::Type type,
329                     const char *editline_name, // Used for saving history files
330                     llvm::StringRef prompt, llvm::StringRef continuation_prompt,
331                     bool multi_line, bool color,
332                     uint32_t line_number_start, // If non-zero show line numbers
333                                                 // starting at
334                                                 // 'line_number_start'
335                     IOHandlerDelegate &delegate);
336 
337   IOHandlerEditline(Debugger &debugger, IOHandler::Type type,
338                     const lldb::FileSP &input_sp,
339                     const lldb::StreamFileSP &output_sp,
340                     const lldb::StreamFileSP &error_sp, uint32_t flags,
341                     const char *editline_name, // Used for saving history files
342                     llvm::StringRef prompt, llvm::StringRef continuation_prompt,
343                     bool multi_line, bool color,
344                     uint32_t line_number_start, // If non-zero show line numbers
345                                                 // starting at
346                                                 // 'line_number_start'
347                     IOHandlerDelegate &delegate);
348 
349   IOHandlerEditline(Debugger &, IOHandler::Type, const char *, const char *,
350                     const char *, bool, bool, uint32_t,
351                     IOHandlerDelegate &) = delete;
352 
353   IOHandlerEditline(Debugger &, IOHandler::Type, const lldb::FileSP &,
354                     const lldb::StreamFileSP &, const lldb::StreamFileSP &,
355                     uint32_t, const char *, const char *, const char *, bool,
356                     bool, uint32_t, IOHandlerDelegate &) = delete;
357 
358   ~IOHandlerEditline() override;
359 
360   void Run() override;
361 
362   void Cancel() override;
363 
364   bool Interrupt() override;
365 
366   void GotEOF() override;
367 
368   void Activate() override;
369 
370   void Deactivate() override;
371 
372   void TerminalSizeChanged() override;
373 
374   llvm::StringRef GetControlSequence(char ch) override {
375     return m_delegate.IOHandlerGetControlSequence(ch);
376   }
377 
378   const char *GetCommandPrefix() override {
379     return m_delegate.IOHandlerGetCommandPrefix();
380   }
381 
382   const char *GetHelpPrologue() override {
383     return m_delegate.IOHandlerGetHelpPrologue();
384   }
385 
386   const char *GetPrompt() override;
387 
388   bool SetPrompt(llvm::StringRef prompt) override;
389   bool SetPrompt(const char *prompt) = delete;
390 
391   const char *GetContinuationPrompt();
392 
393   void SetContinuationPrompt(llvm::StringRef prompt);
394   void SetContinuationPrompt(const char *) = delete;
395 
396   bool GetLine(std::string &line, bool &interrupted);
397 
398   bool GetLines(StringList &lines, bool &interrupted);
399 
400   void SetBaseLineNumber(uint32_t line);
401 
402   bool GetInterruptExits() { return m_interrupt_exits; }
403 
404   void SetInterruptExits(bool b) { m_interrupt_exits = b; }
405 
406   StringList GetCurrentLines() const;
407 
408   uint32_t GetCurrentLineIndex() const;
409 
410   void PrintAsync(const char *s, size_t len, bool is_stdout) override;
411 
412 private:
413 #if LLDB_ENABLE_LIBEDIT
414   bool IsInputCompleteCallback(Editline *editline, StringList &lines);
415 
416   int FixIndentationCallback(Editline *editline, const StringList &lines,
417                              int cursor_position);
418 
419   std::optional<std::string> SuggestionCallback(llvm::StringRef line);
420 
421   void AutoCompleteCallback(CompletionRequest &request);
422 #endif
423 
424 protected:
425 #if LLDB_ENABLE_LIBEDIT
426   std::unique_ptr<Editline> m_editline_up;
427 #endif
428   IOHandlerDelegate &m_delegate;
429   std::string m_prompt;
430   std::string m_continuation_prompt;
431   StringList *m_current_lines_ptr;
432   uint32_t m_base_line_number; // If non-zero, then show line numbers in prompt
433   uint32_t m_curr_line_idx;
434   bool m_multi_line;
435   bool m_color;
436   bool m_interrupt_exits;
437   std::string m_line_buffer;
438 };
439 
440 // The order of base classes is important. Look at the constructor of
441 // IOHandlerConfirm to see how.
442 class IOHandlerConfirm : public IOHandlerDelegate, public IOHandlerEditline {
443 public:
444   IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt,
445                    bool default_response);
446 
447   ~IOHandlerConfirm() override;
448 
449   bool GetResponse() const { return m_user_response; }
450 
451   void IOHandlerComplete(IOHandler &io_handler,
452                          CompletionRequest &request) override;
453 
454   void IOHandlerInputComplete(IOHandler &io_handler,
455                               std::string &data) override;
456 
457 protected:
458   const bool m_default_response;
459   bool m_user_response;
460 };
461 
462 class IOHandlerStack {
463 public:
464   IOHandlerStack() = default;
465 
466   size_t GetSize() const {
467     std::lock_guard<std::recursive_mutex> guard(m_mutex);
468     return m_stack.size();
469   }
470 
471   void Push(const lldb::IOHandlerSP &sp) {
472     if (sp) {
473       std::lock_guard<std::recursive_mutex> guard(m_mutex);
474       sp->SetPopped(false);
475       m_stack.push_back(sp);
476       // Set m_top the non-locking IsTop() call
477       m_top = sp.get();
478     }
479   }
480 
481   bool IsEmpty() const {
482     std::lock_guard<std::recursive_mutex> guard(m_mutex);
483     return m_stack.empty();
484   }
485 
486   lldb::IOHandlerSP Top() {
487     lldb::IOHandlerSP sp;
488     {
489       std::lock_guard<std::recursive_mutex> guard(m_mutex);
490       if (!m_stack.empty())
491         sp = m_stack.back();
492     }
493     return sp;
494   }
495 
496   void Pop() {
497     std::lock_guard<std::recursive_mutex> guard(m_mutex);
498     if (!m_stack.empty()) {
499       lldb::IOHandlerSP sp(m_stack.back());
500       m_stack.pop_back();
501       sp->SetPopped(true);
502     }
503     // Set m_top the non-locking IsTop() call
504 
505     m_top = (m_stack.empty() ? nullptr : m_stack.back().get());
506   }
507 
508   std::recursive_mutex &GetMutex() { return m_mutex; }
509 
510   bool IsTop(const lldb::IOHandlerSP &io_handler_sp) const {
511     return m_top == io_handler_sp.get();
512   }
513 
514   bool CheckTopIOHandlerTypes(IOHandler::Type top_type,
515                               IOHandler::Type second_top_type) {
516     std::lock_guard<std::recursive_mutex> guard(m_mutex);
517     const size_t num_io_handlers = m_stack.size();
518     return (num_io_handlers >= 2 &&
519             m_stack[num_io_handlers - 1]->GetType() == top_type &&
520             m_stack[num_io_handlers - 2]->GetType() == second_top_type);
521   }
522 
523   llvm::StringRef GetTopIOHandlerControlSequence(char ch) {
524     return ((m_top != nullptr) ? m_top->GetControlSequence(ch)
525                                : llvm::StringRef());
526   }
527 
528   const char *GetTopIOHandlerCommandPrefix() {
529     return ((m_top != nullptr) ? m_top->GetCommandPrefix() : nullptr);
530   }
531 
532   const char *GetTopIOHandlerHelpPrologue() {
533     return ((m_top != nullptr) ? m_top->GetHelpPrologue() : nullptr);
534   }
535 
536   bool PrintAsync(const char *s, size_t len, bool is_stdout);
537 
538 protected:
539   typedef std::vector<lldb::IOHandlerSP> collection;
540   collection m_stack;
541   mutable std::recursive_mutex m_mutex;
542   IOHandler *m_top = nullptr;
543 
544 private:
545   IOHandlerStack(const IOHandlerStack &) = delete;
546   const IOHandlerStack &operator=(const IOHandlerStack &) = delete;
547 };
548 
549 } // namespace lldb_private
550 
551 #endif // LLDB_CORE_IOHANDLER_H
552