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