/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/include/lldb/Core/IOHandler.h
Line | Count | Source (jump to first uncovered line) |
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 | 10.3k | bool IsActive() { return m_active && !m_done10.3k ; } |
85 | | |
86 | 19.4k | void SetIsDone(bool b) { m_done = b; } |
87 | | |
88 | 622 | bool GetIsDone() { return m_done; } |
89 | | |
90 | 0 | Type GetType() const { return m_type; } |
91 | | |
92 | 10.0k | virtual void Activate() { m_active = true; } |
93 | | |
94 | 10.0k | virtual void Deactivate() { m_active = false; } |
95 | | |
96 | 0 | virtual void TerminalSizeChanged() {} |
97 | | |
98 | 0 | virtual const char *GetPrompt() { |
99 | | // Prompt support isn't mandatory |
100 | 0 | return nullptr; |
101 | 0 | } |
102 | | |
103 | 0 | virtual bool SetPrompt(llvm::StringRef prompt) { |
104 | | // Prompt support isn't mandatory |
105 | 0 | return false; |
106 | 0 | } |
107 | | bool SetPrompt(const char *) = delete; |
108 | | |
109 | 0 | virtual llvm::StringRef GetControlSequence(char ch) { return {}; } |
110 | | |
111 | 0 | virtual const char *GetCommandPrefix() { return nullptr; } |
112 | | |
113 | 0 | 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 | 0 | Debugger &GetDebugger() { return m_debugger; } |
134 | | |
135 | 2 | void *GetUserData() { return m_user_data; } |
136 | | |
137 | 4 | void SetUserData(void *user_data) { m_user_data = user_data; } |
138 | | |
139 | 17.6k | Flags &GetFlags() { return m_flags; } |
140 | | |
141 | 0 | 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 | 24.5k | 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 | 56.6k | : m_completion(completion) {} |
196 | | |
197 | 56.6k | virtual ~IOHandlerDelegate() = default; |
198 | | |
199 | 2.50k | virtual void IOHandlerActivated(IOHandler &io_handler, bool interactive) {} |
200 | | |
201 | 2.50k | 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 | 12 | 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 | 0 | int cursor_position) { |
236 | 0 | return 0; |
237 | 0 | } |
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 | 6 | 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 | 0 | StringList &lines) { |
268 | | // Impose no requirements for input to be considered complete. subclasses |
269 | | // should do something more intelligent. |
270 | 0 | return true; |
271 | 0 | } |
272 | | |
273 | 0 | virtual llvm::StringRef IOHandlerGetControlSequence(char ch) { return {}; } |
274 | | |
275 | 2 | virtual const char *IOHandlerGetCommandPrefix() { return nullptr; } |
276 | | |
277 | 1 | 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 | 0 | 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 | 44.4k | : IOHandlerDelegate(completion), m_end_line(end_line.str() + "\n") {} |
298 | | |
299 | 44.4k | ~IOHandlerDelegateMultiline() override = default; |
300 | | |
301 | 0 | llvm::StringRef IOHandlerGetControlSequence(char ch) override { |
302 | 0 | if (ch == 'd') |
303 | 0 | return m_end_line; |
304 | 0 | return {}; |
305 | 0 | } |
306 | | |
307 | | bool IOHandlerIsInputComplete(IOHandler &io_handler, |
308 | 15 | StringList &lines) override { |
309 | | // Determine whether the end of input signal has been entered |
310 | 15 | const size_t num_lines = lines.GetSize(); |
311 | 15 | const llvm::StringRef end_line = |
312 | 15 | llvm::StringRef(m_end_line).drop_back(1); // Drop '\n' |
313 | 15 | 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 | 6 | lines.PopBack(); |
317 | 6 | return true; |
318 | 6 | } |
319 | 9 | return false; |
320 | 15 | } |
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 | 0 | llvm::StringRef GetControlSequence(char ch) override { |
375 | 0 | return m_delegate.IOHandlerGetControlSequence(ch); |
376 | 0 | } |
377 | | |
378 | 2 | const char *GetCommandPrefix() override { |
379 | 2 | return m_delegate.IOHandlerGetCommandPrefix(); |
380 | 2 | } |
381 | | |
382 | 1 | const char *GetHelpPrologue() override { |
383 | 1 | return m_delegate.IOHandlerGetHelpPrologue(); |
384 | 1 | } |
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 | 0 | bool GetInterruptExits() { return m_interrupt_exits; } |
403 | | |
404 | 0 | 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 | 15 | 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 | 6.09k | IOHandlerStack() = default; |
465 | | |
466 | 6.63k | size_t GetSize() const { |
467 | 6.63k | std::lock_guard<std::recursive_mutex> guard(m_mutex); |
468 | 6.63k | return m_stack.size(); |
469 | 6.63k | } |
470 | | |
471 | 8.95k | void Push(const lldb::IOHandlerSP &sp) { |
472 | 8.95k | if (sp) { |
473 | 8.95k | std::lock_guard<std::recursive_mutex> guard(m_mutex); |
474 | 8.95k | sp->SetPopped(false); |
475 | 8.95k | m_stack.push_back(sp); |
476 | | // Set m_top the non-locking IsTop() call |
477 | 8.95k | m_top = sp.get(); |
478 | 8.95k | } |
479 | 8.95k | } |
480 | | |
481 | 18.7k | bool IsEmpty() const { |
482 | 18.7k | std::lock_guard<std::recursive_mutex> guard(m_mutex); |
483 | 18.7k | return m_stack.empty(); |
484 | 18.7k | } |
485 | | |
486 | 36.4k | lldb::IOHandlerSP Top() { |
487 | 36.4k | lldb::IOHandlerSP sp; |
488 | 36.4k | { |
489 | 36.4k | std::lock_guard<std::recursive_mutex> guard(m_mutex); |
490 | 36.4k | if (!m_stack.empty()) |
491 | 14.1k | sp = m_stack.back(); |
492 | 36.4k | } |
493 | 36.4k | return sp; |
494 | 36.4k | } |
495 | | |
496 | 8.95k | void Pop() { |
497 | 8.95k | std::lock_guard<std::recursive_mutex> guard(m_mutex); |
498 | 8.95k | if (!m_stack.empty()) { |
499 | 8.95k | lldb::IOHandlerSP sp(m_stack.back()); |
500 | 8.95k | m_stack.pop_back(); |
501 | 8.95k | sp->SetPopped(true); |
502 | 8.95k | } |
503 | | // Set m_top the non-locking IsTop() call |
504 | | |
505 | 8.95k | m_top = (m_stack.empty() ? nullptr7.90k : m_stack.back().get()1.04k ); |
506 | 8.95k | } |
507 | | |
508 | 41.4k | std::recursive_mutex &GetMutex() { return m_mutex; } |
509 | | |
510 | 0 | bool IsTop(const lldb::IOHandlerSP &io_handler_sp) const { |
511 | 0 | return m_top == io_handler_sp.get(); |
512 | 0 | } |
513 | | |
514 | | bool CheckTopIOHandlerTypes(IOHandler::Type top_type, |
515 | 0 | IOHandler::Type second_top_type) { |
516 | 0 | std::lock_guard<std::recursive_mutex> guard(m_mutex); |
517 | 0 | const size_t num_io_handlers = m_stack.size(); |
518 | 0 | return (num_io_handlers >= 2 && |
519 | 0 | m_stack[num_io_handlers - 1]->GetType() == top_type && |
520 | 0 | m_stack[num_io_handlers - 2]->GetType() == second_top_type); |
521 | 0 | } |
522 | | |
523 | 0 | llvm::StringRef GetTopIOHandlerControlSequence(char ch) { |
524 | 0 | return ((m_top != nullptr) ? m_top->GetControlSequence(ch) |
525 | 0 | : llvm::StringRef()); |
526 | 0 | } |
527 | | |
528 | 25 | const char *GetTopIOHandlerCommandPrefix() { |
529 | 25 | return ((m_top != nullptr) ? m_top->GetCommandPrefix()2 : nullptr23 ); |
530 | 25 | } |
531 | | |
532 | 13 | const char *GetTopIOHandlerHelpPrologue() { |
533 | 13 | return ((m_top != nullptr) ? m_top->GetHelpPrologue()1 : nullptr12 ); |
534 | 13 | } |
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 |