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