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
32namespace lldb_private {
33class Debugger;
34} // namespace lldb_private
35
36namespace curses {
37class Application;
38typedef std::unique_ptr<Application> ApplicationAP;
39} // namespace curses
40
41namespace lldb_private {
42
43class IOHandler {
44public:
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
166protected:
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
179private:
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.
190class IOHandlerDelegate {
191public:
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
285protected:
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.
293class IOHandlerDelegateMultiline : public IOHandlerDelegate {
294public:
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
322protected:
323 const std::string m_end_line;
324};
325
326class IOHandlerEditline : public IOHandler {
327public:
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
412private:
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
424protected:
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.
442class IOHandlerConfirm : public IOHandlerDelegate, public IOHandlerEditline {
443public:
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
457protected:
458 const bool m_default_response;
459 bool m_user_response;
460};
461
462class IOHandlerStack {
463public:
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
538protected:
539 typedef std::vector<lldb::IOHandlerSP> collection;
540 collection m_stack;
541 mutable std::recursive_mutex m_mutex;
542 IOHandler *m_top = nullptr;
543
544private:
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

source code of lldb/include/lldb/Core/IOHandler.h