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