1 #include "CommandObjectSession.h"
2 #include "lldb/Host/OptionParser.h"
3 #include "lldb/Interpreter/CommandInterpreter.h"
4 #include "lldb/Interpreter/CommandOptionArgumentTable.h"
5 #include "lldb/Interpreter/CommandReturnObject.h"
6 #include "lldb/Interpreter/OptionArgParser.h"
7 #include "lldb/Interpreter/OptionValue.h"
8 #include "lldb/Interpreter/OptionValueBoolean.h"
9 #include "lldb/Interpreter/OptionValueString.h"
10 #include "lldb/Interpreter/OptionValueUInt64.h"
11 #include "lldb/Interpreter/Options.h"
12 
13 using namespace lldb;
14 using namespace lldb_private;
15 
16 class CommandObjectSessionSave : public CommandObjectParsed {
17 public:
18   CommandObjectSessionSave(CommandInterpreter &interpreter)
19       : CommandObjectParsed(interpreter, "session save",
20                             "Save the current session transcripts to a file.\n"
21                             "If no file if specified, transcripts will be "
22                             "saved to a temporary file.",
23                             "session save [file]") {
24     CommandArgumentEntry arg1;
25     arg1.emplace_back(eArgTypePath, eArgRepeatOptional);
26     m_arguments.push_back(arg1);
27   }
28 
29   ~CommandObjectSessionSave() override = default;
30 
31   void
32   HandleArgumentCompletion(CompletionRequest &request,
33                            OptionElementVector &opt_element_vector) override {
34     lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
35         GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr);
36   }
37 
38 protected:
39   bool DoExecute(Args &args, CommandReturnObject &result) override {
40     llvm::StringRef file_path;
41 
42     if (!args.empty())
43       file_path = args[0].ref();
44 
45     if (m_interpreter.SaveTranscript(result, file_path.str()))
46       result.SetStatus(eReturnStatusSuccessFinishNoResult);
47     else
48       result.SetStatus(eReturnStatusFailed);
49     return result.Succeeded();
50   }
51 };
52 
53 #define LLDB_OPTIONS_history
54 #include "CommandOptions.inc"
55 
56 class CommandObjectSessionHistory : public CommandObjectParsed {
57 public:
58   CommandObjectSessionHistory(CommandInterpreter &interpreter)
59       : CommandObjectParsed(interpreter, "session history",
60                             "Dump the history of commands in this session.\n"
61                             "Commands in the history list can be run again "
62                             "using \"!<INDEX>\".   \"!-<OFFSET>\" will re-run "
63                             "the command that is <OFFSET> commands from the end"
64                             " of the list (counting the current command).",
65                             nullptr) {}
66 
67   ~CommandObjectSessionHistory() override = default;
68 
69   Options *GetOptions() override { return &m_options; }
70 
71 protected:
72   class CommandOptions : public Options {
73   public:
74     CommandOptions()
75         : m_start_idx(0), m_stop_idx(0), m_count(0), m_clear(false) {}
76 
77     ~CommandOptions() override = default;
78 
79     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
80                           ExecutionContext *execution_context) override {
81       Status error;
82       const int short_option = m_getopt_table[option_idx].val;
83 
84       switch (short_option) {
85       case 'c':
86         error = m_count.SetValueFromString(option_arg, eVarSetOperationAssign);
87         break;
88       case 's':
89         if (option_arg == "end") {
90           m_start_idx.SetCurrentValue(UINT64_MAX);
91           m_start_idx.SetOptionWasSet();
92         } else
93           error = m_start_idx.SetValueFromString(option_arg,
94                                                  eVarSetOperationAssign);
95         break;
96       case 'e':
97         error =
98             m_stop_idx.SetValueFromString(option_arg, eVarSetOperationAssign);
99         break;
100       case 'C':
101         m_clear.SetCurrentValue(true);
102         m_clear.SetOptionWasSet();
103         break;
104       default:
105         llvm_unreachable("Unimplemented option");
106       }
107 
108       return error;
109     }
110 
111     void OptionParsingStarting(ExecutionContext *execution_context) override {
112       m_start_idx.Clear();
113       m_stop_idx.Clear();
114       m_count.Clear();
115       m_clear.Clear();
116     }
117 
118     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
119       return llvm::ArrayRef(g_history_options);
120     }
121 
122     // Instance variables to hold the values for command options.
123 
124     OptionValueUInt64 m_start_idx;
125     OptionValueUInt64 m_stop_idx;
126     OptionValueUInt64 m_count;
127     OptionValueBoolean m_clear;
128   };
129 
130   bool DoExecute(Args &command, CommandReturnObject &result) override {
131     if (m_options.m_clear.GetCurrentValue() &&
132         m_options.m_clear.OptionWasSet()) {
133       m_interpreter.GetCommandHistory().Clear();
134       result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
135     } else {
136       if (m_options.m_start_idx.OptionWasSet() &&
137           m_options.m_stop_idx.OptionWasSet() &&
138           m_options.m_count.OptionWasSet()) {
139         result.AppendError("--count, --start-index and --end-index cannot be "
140                            "all specified in the same invocation");
141         result.SetStatus(lldb::eReturnStatusFailed);
142       } else {
143         std::pair<bool, uint64_t> start_idx(
144             m_options.m_start_idx.OptionWasSet(),
145             m_options.m_start_idx.GetCurrentValue());
146         std::pair<bool, uint64_t> stop_idx(
147             m_options.m_stop_idx.OptionWasSet(),
148             m_options.m_stop_idx.GetCurrentValue());
149         std::pair<bool, uint64_t> count(m_options.m_count.OptionWasSet(),
150                                         m_options.m_count.GetCurrentValue());
151 
152         const CommandHistory &history(m_interpreter.GetCommandHistory());
153 
154         if (start_idx.first && start_idx.second == UINT64_MAX) {
155           if (count.first) {
156             start_idx.second = history.GetSize() - count.second;
157             stop_idx.second = history.GetSize() - 1;
158           } else if (stop_idx.first) {
159             start_idx.second = stop_idx.second;
160             stop_idx.second = history.GetSize() - 1;
161           } else {
162             start_idx.second = 0;
163             stop_idx.second = history.GetSize() - 1;
164           }
165         } else {
166           if (!start_idx.first && !stop_idx.first && !count.first) {
167             start_idx.second = 0;
168             stop_idx.second = history.GetSize() - 1;
169           } else if (start_idx.first) {
170             if (count.first) {
171               stop_idx.second = start_idx.second + count.second - 1;
172             } else if (!stop_idx.first) {
173               stop_idx.second = history.GetSize() - 1;
174             }
175           } else if (stop_idx.first) {
176             if (count.first) {
177               if (stop_idx.second >= count.second)
178                 start_idx.second = stop_idx.second - count.second + 1;
179               else
180                 start_idx.second = 0;
181             }
182           } else /* if (count.first) */
183           {
184             start_idx.second = 0;
185             stop_idx.second = count.second - 1;
186           }
187         }
188         history.Dump(result.GetOutputStream(), start_idx.second,
189                      stop_idx.second);
190       }
191     }
192     return result.Succeeded();
193   }
194 
195   CommandOptions m_options;
196 };
197 
198 CommandObjectSession::CommandObjectSession(CommandInterpreter &interpreter)
199     : CommandObjectMultiword(interpreter, "session",
200                              "Commands controlling LLDB session.",
201                              "session <subcommand> [<command-options>]") {
202   LoadSubCommand("save",
203                  CommandObjectSP(new CommandObjectSessionSave(interpreter)));
204   LoadSubCommand("history",
205                  CommandObjectSP(new CommandObjectSessionHistory(interpreter)));
206 }
207