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