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