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
13using namespace lldb;
14using namespace lldb_private;
15
16class CommandObjectSessionSave : public CommandObjectParsed {
17public:
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
38protected:
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
55class CommandObjectSessionHistory : public CommandObjectParsed {
56public:
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
68  Options *GetOptions() override { return &m_options; }
69
70protected:
71  class CommandOptions : public Options {
72  public:
73    CommandOptions()
74        : m_start_idx(0), m_stop_idx(0), m_count(0), m_clear(false) {}
75
76    ~CommandOptions() override = default;
77
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
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
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
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
196CommandObjectSession::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