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(args: eArgTypePath, args: eArgRepeatOptional); |
26 | m_arguments.push_back(x: 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 | interpreter&: GetCommandInterpreter(), completion_mask: lldb::eDiskFileCompletion, request, searcher: nullptr); |
36 | } |
37 | |
38 | protected: |
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, output_file: 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: |
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 | |
70 | protected: |
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(value: option_arg, op: 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(value: option_arg, |
93 | op: eVarSetOperationAssign); |
94 | break; |
95 | case 'e': |
96 | error = |
97 | m_stop_idx.SetValueFromString(value: option_arg, op: 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(in_string: "--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(stream&: result.GetOutputStream(), start_idx: start_idx.second, |
188 | stop_idx: stop_idx.second); |
189 | } |
190 | } |
191 | } |
192 | |
193 | CommandOptions m_options; |
194 | }; |
195 | |
196 | CommandObjectSession::CommandObjectSession(CommandInterpreter &interpreter) |
197 | : CommandObjectMultiword(interpreter, "session" , |
198 | "Commands controlling LLDB session." , |
199 | "session <subcommand> [<command-options>]" ) { |
200 | LoadSubCommand(cmd_name: "save" , |
201 | command_obj: CommandObjectSP(new CommandObjectSessionSave(interpreter))); |
202 | LoadSubCommand(cmd_name: "history" , |
203 | command_obj: CommandObjectSP(new CommandObjectSessionHistory(interpreter))); |
204 | } |
205 | |