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(N: 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(x: 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 | |