| //===-- IOHandler.h ---------------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef liblldb_IOHandler_h_ |
| #define liblldb_IOHandler_h_ |
| |
| #include "lldb/Core/ValueObjectList.h" |
| #include "lldb/Host/Predicate.h" |
| #include "lldb/Utility/ConstString.h" |
| #include "lldb/Utility/Flags.h" |
| #include "lldb/Utility/Stream.h" |
| #include "lldb/Utility/StringList.h" |
| #include "lldb/lldb-defines.h" // for DISALLOW_COPY_AND_ASSIGN |
| #include "lldb/lldb-forward.h" // for IOHandlerSP, StreamFileSP |
| #include "llvm/ADT/StringRef.h" // for StringRef |
| |
| #include <memory> |
| #include <mutex> |
| #include <string> |
| #include <vector> |
| |
| #include <stdint.h> // for uint32_t |
| #include <stdio.h> // for FILE |
| |
| namespace lldb_private { |
| class Debugger; |
| } |
| |
| namespace curses { |
| class Application; |
| typedef std::unique_ptr<Application> ApplicationAP; |
| } // namespace curses |
| |
| namespace lldb_private { |
| |
| class IOHandler { |
| public: |
| enum class Type { |
| CommandInterpreter, |
| CommandList, |
| Confirm, |
| Curses, |
| Expression, |
| REPL, |
| ProcessIO, |
| PythonInterpreter, |
| PythonCode, |
| Other |
| }; |
| |
| IOHandler(Debugger &debugger, IOHandler::Type type); |
| |
| IOHandler(Debugger &debugger, IOHandler::Type type, |
| const lldb::StreamFileSP &input_sp, |
| const lldb::StreamFileSP &output_sp, |
| const lldb::StreamFileSP &error_sp, uint32_t flags); |
| |
| virtual ~IOHandler(); |
| |
| // Each IOHandler gets to run until it is done. It should read data from the |
| // "in" and place output into "out" and "err and return when done. |
| virtual void Run() = 0; |
| |
| // Called when an input reader should relinquish its control so another can |
| // be pushed onto the IO handler stack, or so the current IO handler can pop |
| // itself off the stack |
| |
| virtual void Cancel() = 0; |
| |
| // Called when CTRL+C is pressed which usually causes |
| // Debugger::DispatchInputInterrupt to be called. |
| |
| virtual bool Interrupt() = 0; |
| |
| virtual void GotEOF() = 0; |
| |
| virtual bool IsActive() { return m_active && !m_done; } |
| |
| virtual void SetIsDone(bool b) { m_done = b; } |
| |
| virtual bool GetIsDone() { return m_done; } |
| |
| Type GetType() const { return m_type; } |
| |
| virtual void Activate() { m_active = true; } |
| |
| virtual void Deactivate() { m_active = false; } |
| |
| virtual const char *GetPrompt() { |
| // Prompt support isn't mandatory |
| return nullptr; |
| } |
| |
| virtual bool SetPrompt(llvm::StringRef prompt) { |
| // Prompt support isn't mandatory |
| return false; |
| } |
| bool SetPrompt(const char *) = delete; |
| |
| virtual ConstString GetControlSequence(char ch) { return ConstString(); } |
| |
| virtual const char *GetCommandPrefix() { return nullptr; } |
| |
| virtual const char *GetHelpPrologue() { return nullptr; } |
| |
| int GetInputFD(); |
| |
| int GetOutputFD(); |
| |
| int GetErrorFD(); |
| |
| FILE *GetInputFILE(); |
| |
| FILE *GetOutputFILE(); |
| |
| FILE *GetErrorFILE(); |
| |
| lldb::StreamFileSP &GetInputStreamFile(); |
| |
| lldb::StreamFileSP &GetOutputStreamFile(); |
| |
| lldb::StreamFileSP &GetErrorStreamFile(); |
| |
| Debugger &GetDebugger() { return m_debugger; } |
| |
| void *GetUserData() { return m_user_data; } |
| |
| void SetUserData(void *user_data) { m_user_data = user_data; } |
| |
| Flags &GetFlags() { return m_flags; } |
| |
| const Flags &GetFlags() const { return m_flags; } |
| |
| //------------------------------------------------------------------ |
| /// Check if the input is being supplied interactively by a user |
| /// |
| /// This will return true if the input stream is a terminal (tty or |
| /// pty) and can cause IO handlers to do different things (like |
| /// for a confirmation when deleting all breakpoints). |
| //------------------------------------------------------------------ |
| bool GetIsInteractive(); |
| |
| //------------------------------------------------------------------ |
| /// Check if the input is coming from a real terminal. |
| /// |
| /// A real terminal has a valid size with a certain number of rows |
| /// and columns. If this function returns true, then terminal escape |
| /// sequences are expected to work (cursor movement escape sequences, |
| /// clearing lines, etc). |
| //------------------------------------------------------------------ |
| bool GetIsRealTerminal(); |
| |
| void SetPopped(bool b); |
| |
| void WaitForPop(); |
| |
| virtual void PrintAsync(Stream *stream, const char *s, size_t len) { |
| stream->Write(s, len); |
| stream->Flush(); |
| } |
| |
| protected: |
| Debugger &m_debugger; |
| lldb::StreamFileSP m_input_sp; |
| lldb::StreamFileSP m_output_sp; |
| lldb::StreamFileSP m_error_sp; |
| Predicate<bool> m_popped; |
| Flags m_flags; |
| Type m_type; |
| void *m_user_data; |
| bool m_done; |
| bool m_active; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(IOHandler); |
| }; |
| |
| //------------------------------------------------------------------ |
| /// A delegate class for use with IOHandler subclasses. |
| /// |
| /// The IOHandler delegate is designed to be mixed into classes so |
| /// they can use an IOHandler subclass to fetch input and notify the |
| /// object that inherits from this delegate class when a token is |
| /// received. |
| //------------------------------------------------------------------ |
| class IOHandlerDelegate { |
| public: |
| enum class Completion { None, LLDBCommand, Expression }; |
| |
| IOHandlerDelegate(Completion completion = Completion::None) |
| : m_completion(completion) {} |
| |
| virtual ~IOHandlerDelegate() = default; |
| |
| virtual void IOHandlerActivated(IOHandler &io_handler) {} |
| |
| virtual void IOHandlerDeactivated(IOHandler &io_handler) {} |
| |
| virtual int IOHandlerComplete(IOHandler &io_handler, const char *current_line, |
| const char *cursor, const char *last_char, |
| int skip_first_n_matches, int max_matches, |
| StringList &matches); |
| |
| virtual const char *IOHandlerGetFixIndentationCharacters() { return nullptr; } |
| |
| //------------------------------------------------------------------ |
| /// Called when a new line is created or one of an identified set of |
| /// indentation characters is typed. |
| /// |
| /// This function determines how much indentation should be added |
| /// or removed to match the recommended amount for the final line. |
| /// |
| /// @param[in] io_handler |
| /// The IOHandler that responsible for input. |
| /// |
| /// @param[in] lines |
| /// The current input up to the line to be corrected. Lines |
| /// following the line containing the cursor are not included. |
| /// |
| /// @param[in] cursor_position |
| /// The number of characters preceding the cursor on the final |
| /// line at the time. |
| /// |
| /// @return |
| /// Returns an integer describing the number of spaces needed |
| /// to correct the indentation level. Positive values indicate |
| /// that spaces should be added, while negative values represent |
| /// spaces that should be removed. |
| //------------------------------------------------------------------ |
| virtual int IOHandlerFixIndentation(IOHandler &io_handler, |
| const StringList &lines, |
| int cursor_position) { |
| return 0; |
| } |
| |
| //------------------------------------------------------------------ |
| /// Called when a line or lines have been retrieved. |
| /// |
| /// This function can handle the current line and possibly call |
| /// IOHandler::SetIsDone(true) when the IO handler is done like when |
| /// "quit" is entered as a command, of when an empty line is |
| /// received. It is up to the delegate to determine when a line |
| /// should cause a IOHandler to exit. |
| //------------------------------------------------------------------ |
| virtual void IOHandlerInputComplete(IOHandler &io_handler, |
| std::string &data) = 0; |
| |
| virtual void IOHandlerInputInterrupted(IOHandler &io_handler, |
| std::string &data) {} |
| |
| //------------------------------------------------------------------ |
| /// Called to determine whether typing enter after the last line in |
| /// \a lines should end input. This function will not be called on |
| /// IOHandler objects that are getting single lines. |
| /// @param[in] io_handler |
| /// The IOHandler that responsible for updating the lines. |
| /// |
| /// @param[in] lines |
| /// The current multi-line content. May be altered to provide |
| /// alternative input when complete. |
| /// |
| /// @return |
| /// Return an boolean to indicate whether input is complete, |
| /// true indicates that no additional input is necessary, while |
| /// false indicates that more input is required. |
| //------------------------------------------------------------------ |
| virtual bool IOHandlerIsInputComplete(IOHandler &io_handler, |
| StringList &lines) { |
| // Impose no requirements for input to be considered complete. subclasses |
| // should do something more intelligent. |
| return true; |
| } |
| |
| virtual ConstString IOHandlerGetControlSequence(char ch) { |
| return ConstString(); |
| } |
| |
| virtual const char *IOHandlerGetCommandPrefix() { return nullptr; } |
| |
| virtual const char *IOHandlerGetHelpPrologue() { return nullptr; } |
| |
| //------------------------------------------------------------------ |
| // Intercept the IOHandler::Interrupt() calls and do something. |
| // |
| // Return true if the interrupt was handled, false if the IOHandler should |
| // continue to try handle the interrupt itself. |
| //------------------------------------------------------------------ |
| virtual bool IOHandlerInterrupt(IOHandler &io_handler) { return false; } |
| |
| protected: |
| Completion m_completion; // Support for common builtin completions |
| }; |
| |
| //---------------------------------------------------------------------- |
| // IOHandlerDelegateMultiline |
| // |
| // A IOHandlerDelegate that handles terminating multi-line input when |
| // the last line is equal to "end_line" which is specified in the constructor. |
| //---------------------------------------------------------------------- |
| class IOHandlerDelegateMultiline : public IOHandlerDelegate { |
| public: |
| IOHandlerDelegateMultiline(const char *end_line, |
| Completion completion = Completion::None) |
| : IOHandlerDelegate(completion), |
| m_end_line((end_line && end_line[0]) ? end_line : "") {} |
| |
| ~IOHandlerDelegateMultiline() override = default; |
| |
| ConstString IOHandlerGetControlSequence(char ch) override { |
| if (ch == 'd') |
| return ConstString(m_end_line + "\n"); |
| return ConstString(); |
| } |
| |
| bool IOHandlerIsInputComplete(IOHandler &io_handler, |
| StringList &lines) override { |
| // Determine whether the end of input signal has been entered |
| const size_t num_lines = lines.GetSize(); |
| if (num_lines > 0 && lines[num_lines - 1] == m_end_line) { |
| // Remove the terminal line from "lines" so it doesn't appear in the |
| // resulting input and return true to indicate we are done getting lines |
| lines.PopBack(); |
| return true; |
| } |
| return false; |
| } |
| |
| protected: |
| const std::string m_end_line; |
| }; |
| |
| class IOHandlerEditline : public IOHandler { |
| public: |
| IOHandlerEditline(Debugger &debugger, IOHandler::Type type, |
| const char *editline_name, // Used for saving history files |
| llvm::StringRef prompt, llvm::StringRef continuation_prompt, |
| bool multi_line, bool color_prompts, |
| uint32_t line_number_start, // If non-zero show line numbers |
| // starting at |
| // 'line_number_start' |
| IOHandlerDelegate &delegate); |
| |
| IOHandlerEditline(Debugger &debugger, IOHandler::Type type, |
| const lldb::StreamFileSP &input_sp, |
| const lldb::StreamFileSP &output_sp, |
| const lldb::StreamFileSP &error_sp, uint32_t flags, |
| const char *editline_name, // Used for saving history files |
| llvm::StringRef prompt, llvm::StringRef continuation_prompt, |
| bool multi_line, bool color_prompts, |
| uint32_t line_number_start, // If non-zero show line numbers |
| // starting at |
| // 'line_number_start' |
| IOHandlerDelegate &delegate); |
| |
| IOHandlerEditline(Debugger &, IOHandler::Type, const char *, const char *, |
| const char *, bool, bool, uint32_t, |
| IOHandlerDelegate &) = delete; |
| |
| IOHandlerEditline(Debugger &, IOHandler::Type, const lldb::StreamFileSP &, |
| const lldb::StreamFileSP &, const lldb::StreamFileSP &, |
| uint32_t, const char *, const char *, const char *, bool, |
| bool, uint32_t, IOHandlerDelegate &) = delete; |
| |
| ~IOHandlerEditline() override; |
| |
| void Run() override; |
| |
| void Cancel() override; |
| |
| bool Interrupt() override; |
| |
| void GotEOF() override; |
| |
| void Activate() override; |
| |
| void Deactivate() override; |
| |
| ConstString GetControlSequence(char ch) override { |
| return m_delegate.IOHandlerGetControlSequence(ch); |
| } |
| |
| const char *GetCommandPrefix() override { |
| return m_delegate.IOHandlerGetCommandPrefix(); |
| } |
| |
| const char *GetHelpPrologue() override { |
| return m_delegate.IOHandlerGetHelpPrologue(); |
| } |
| |
| const char *GetPrompt() override; |
| |
| bool SetPrompt(llvm::StringRef prompt) override; |
| bool SetPrompt(const char *prompt) = delete; |
| |
| const char *GetContinuationPrompt(); |
| |
| void SetContinuationPrompt(llvm::StringRef prompt); |
| void SetContinuationPrompt(const char *) = delete; |
| |
| bool GetLine(std::string &line, bool &interrupted); |
| |
| bool GetLines(StringList &lines, bool &interrupted); |
| |
| void SetBaseLineNumber(uint32_t line); |
| |
| bool GetInterruptExits() { return m_interrupt_exits; } |
| |
| void SetInterruptExits(bool b) { m_interrupt_exits = b; } |
| |
| const StringList *GetCurrentLines() const { return m_current_lines_ptr; } |
| |
| uint32_t GetCurrentLineIndex() const; |
| |
| void PrintAsync(Stream *stream, const char *s, size_t len) override; |
| |
| private: |
| #ifndef LLDB_DISABLE_LIBEDIT |
| static bool IsInputCompleteCallback(Editline *editline, StringList &lines, |
| void *baton); |
| |
| static int FixIndentationCallback(Editline *editline, const StringList &lines, |
| int cursor_position, void *baton); |
| |
| static int AutoCompleteCallback(const char *current_line, const char *cursor, |
| const char *last_char, |
| int skip_first_n_matches, int max_matches, |
| StringList &matches, void *baton); |
| #endif |
| |
| protected: |
| #ifndef LLDB_DISABLE_LIBEDIT |
| std::unique_ptr<Editline> m_editline_ap; |
| #endif |
| IOHandlerDelegate &m_delegate; |
| std::string m_prompt; |
| std::string m_continuation_prompt; |
| StringList *m_current_lines_ptr; |
| uint32_t m_base_line_number; // If non-zero, then show line numbers in prompt |
| uint32_t m_curr_line_idx; |
| bool m_multi_line; |
| bool m_color_prompts; |
| bool m_interrupt_exits; |
| bool m_editing; // Set to true when fetching a line manually (not using |
| // libedit) |
| }; |
| |
| // The order of base classes is important. Look at the constructor of |
| // IOHandlerConfirm to see how. |
| class IOHandlerConfirm : public IOHandlerDelegate, public IOHandlerEditline { |
| public: |
| IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt, |
| bool default_response); |
| |
| ~IOHandlerConfirm() override; |
| |
| bool GetResponse() const { return m_user_response; } |
| |
| int IOHandlerComplete(IOHandler &io_handler, const char *current_line, |
| const char *cursor, const char *last_char, |
| int skip_first_n_matches, int max_matches, |
| StringList &matches) override; |
| |
| void IOHandlerInputComplete(IOHandler &io_handler, |
| std::string &data) override; |
| |
| protected: |
| const bool m_default_response; |
| bool m_user_response; |
| }; |
| |
| class IOHandlerCursesGUI : public IOHandler { |
| public: |
| IOHandlerCursesGUI(Debugger &debugger); |
| |
| ~IOHandlerCursesGUI() override; |
| |
| void Run() override; |
| |
| void Cancel() override; |
| |
| bool Interrupt() override; |
| |
| void GotEOF() override; |
| |
| void Activate() override; |
| |
| void Deactivate() override; |
| |
| protected: |
| curses::ApplicationAP m_app_ap; |
| }; |
| |
| class IOHandlerCursesValueObjectList : public IOHandler { |
| public: |
| IOHandlerCursesValueObjectList(Debugger &debugger, |
| ValueObjectList &valobj_list); |
| |
| ~IOHandlerCursesValueObjectList() override; |
| |
| void Run() override; |
| |
| void GotEOF() override; |
| |
| protected: |
| ValueObjectList m_valobj_list; |
| }; |
| |
| class IOHandlerStack { |
| public: |
| IOHandlerStack() : m_stack(), m_mutex(), m_top(nullptr) {} |
| |
| ~IOHandlerStack() = default; |
| |
| size_t GetSize() const { |
| std::lock_guard<std::recursive_mutex> guard(m_mutex); |
| return m_stack.size(); |
| } |
| |
| void Push(const lldb::IOHandlerSP &sp) { |
| if (sp) { |
| std::lock_guard<std::recursive_mutex> guard(m_mutex); |
| sp->SetPopped(false); |
| m_stack.push_back(sp); |
| // Set m_top the non-locking IsTop() call |
| m_top = sp.get(); |
| } |
| } |
| |
| bool IsEmpty() const { |
| std::lock_guard<std::recursive_mutex> guard(m_mutex); |
| return m_stack.empty(); |
| } |
| |
| lldb::IOHandlerSP Top() { |
| lldb::IOHandlerSP sp; |
| { |
| std::lock_guard<std::recursive_mutex> guard(m_mutex); |
| if (!m_stack.empty()) |
| sp = m_stack.back(); |
| } |
| return sp; |
| } |
| |
| void Pop() { |
| std::lock_guard<std::recursive_mutex> guard(m_mutex); |
| if (!m_stack.empty()) { |
| lldb::IOHandlerSP sp(m_stack.back()); |
| m_stack.pop_back(); |
| sp->SetPopped(true); |
| } |
| // Set m_top the non-locking IsTop() call |
| |
| m_top = (m_stack.empty() ? nullptr : m_stack.back().get()); |
| } |
| |
| std::recursive_mutex &GetMutex() { return m_mutex; } |
| |
| bool IsTop(const lldb::IOHandlerSP &io_handler_sp) const { |
| return m_top == io_handler_sp.get(); |
| } |
| |
| bool CheckTopIOHandlerTypes(IOHandler::Type top_type, |
| IOHandler::Type second_top_type) { |
| std::lock_guard<std::recursive_mutex> guard(m_mutex); |
| const size_t num_io_handlers = m_stack.size(); |
| return (num_io_handlers >= 2 && |
| m_stack[num_io_handlers - 1]->GetType() == top_type && |
| m_stack[num_io_handlers - 2]->GetType() == second_top_type); |
| } |
| |
| ConstString GetTopIOHandlerControlSequence(char ch) { |
| return ((m_top != nullptr) ? m_top->GetControlSequence(ch) : ConstString()); |
| } |
| |
| const char *GetTopIOHandlerCommandPrefix() { |
| return ((m_top != nullptr) ? m_top->GetCommandPrefix() : nullptr); |
| } |
| |
| const char *GetTopIOHandlerHelpPrologue() { |
| return ((m_top != nullptr) ? m_top->GetHelpPrologue() : nullptr); |
| } |
| |
| void PrintAsync(Stream *stream, const char *s, size_t len); |
| |
| protected: |
| typedef std::vector<lldb::IOHandlerSP> collection; |
| collection m_stack; |
| mutable std::recursive_mutex m_mutex; |
| IOHandler *m_top; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(IOHandlerStack); |
| }; |
| |
| } // namespace lldb_private |
| |
| #endif // liblldb_IOHandler_h_ |