1 | //===-- OptionGroupFormat.cpp ---------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "lldb/Interpreter/OptionGroupFormat.h" |
10 | |
11 | #include "lldb/Host/OptionParser.h" |
12 | #include "lldb/Interpreter/CommandInterpreter.h" |
13 | #include "lldb/Target/ExecutionContext.h" |
14 | #include "lldb/Target/Target.h" |
15 | |
16 | using namespace lldb; |
17 | using namespace lldb_private; |
18 | |
19 | static constexpr OptionDefinition g_default_option_definitions[] = { |
20 | {LLDB_OPT_SET_1, .required: false, .long_option: "format" , .short_option: 'f', .option_has_arg: OptionParser::eRequiredArgument, |
21 | .validator: nullptr, .enum_values: {}, .completion_type: 0, .argument_type: eArgTypeFormat, |
22 | .usage_text: "Specify a format to be used for display." }, |
23 | {LLDB_OPT_SET_2, .required: false, .long_option: "gdb-format" , .short_option: 'G', .option_has_arg: OptionParser::eRequiredArgument, |
24 | .validator: nullptr, .enum_values: {}, .completion_type: 0, .argument_type: eArgTypeGDBFormat, |
25 | .usage_text: "Specify a format using a GDB format specifier string." }, |
26 | {LLDB_OPT_SET_3, .required: false, .long_option: "size" , .short_option: 's', .option_has_arg: OptionParser::eRequiredArgument, |
27 | .validator: nullptr, .enum_values: {}, .completion_type: 0, .argument_type: eArgTypeByteSize, |
28 | .usage_text: "The size in bytes to use when displaying with the selected format." }, |
29 | {LLDB_OPT_SET_4, .required: false, .long_option: "count" , .short_option: 'c', .option_has_arg: OptionParser::eRequiredArgument, |
30 | .validator: nullptr, .enum_values: {}, .completion_type: 0, .argument_type: eArgTypeCount, |
31 | .usage_text: "The number of total items to display." }, |
32 | }; |
33 | |
34 | OptionGroupFormat::OptionGroupFormat( |
35 | lldb::Format default_format, uint64_t default_byte_size, |
36 | uint64_t default_count, OptionGroupFormatUsageTextVector usage_text_vector) |
37 | : m_format(default_format, default_format), |
38 | m_byte_size(default_byte_size, default_byte_size), |
39 | m_count(default_count, default_count), m_prev_gdb_format('x'), |
40 | m_prev_gdb_size('w'), m_has_gdb_format(false) { |
41 | // Copy the default option definitions. |
42 | std::copy(first: std::begin(arr: g_default_option_definitions), |
43 | last: std::end(arr: g_default_option_definitions), |
44 | result: std::begin(arr&: m_option_definitions)); |
45 | |
46 | for (auto usage_text_tuple : usage_text_vector) { |
47 | switch (std::get<0>(t&: usage_text_tuple)) { |
48 | case eArgTypeFormat: |
49 | m_option_definitions[0].usage_text = std::get<1>(t&: usage_text_tuple); |
50 | break; |
51 | case eArgTypeByteSize: |
52 | m_option_definitions[2].usage_text = std::get<1>(t&: usage_text_tuple); |
53 | break; |
54 | default: |
55 | llvm_unreachable("Unimplemented option" ); |
56 | } |
57 | } |
58 | } |
59 | |
60 | llvm::ArrayRef<OptionDefinition> OptionGroupFormat::GetDefinitions() { |
61 | auto result = llvm::ArrayRef(m_option_definitions); |
62 | if (m_byte_size.GetDefaultValue() < UINT64_MAX) { |
63 | if (m_count.GetDefaultValue() < UINT64_MAX) |
64 | return result; |
65 | else |
66 | return result.take_front(N: 3); |
67 | } |
68 | return result.take_front(N: 2); |
69 | } |
70 | |
71 | Status OptionGroupFormat::SetOptionValue(uint32_t option_idx, |
72 | llvm::StringRef option_arg, |
73 | ExecutionContext *execution_context) { |
74 | Status error; |
75 | const int short_option = m_option_definitions[option_idx].short_option; |
76 | |
77 | switch (short_option) { |
78 | case 'f': |
79 | error = m_format.SetValueFromString(value: option_arg); |
80 | break; |
81 | |
82 | case 'c': |
83 | if (m_count.GetDefaultValue() == 0) { |
84 | error = Status::FromErrorString(str: "--count option is disabled" ); |
85 | } else { |
86 | error = m_count.SetValueFromString(value: option_arg); |
87 | if (m_count.GetCurrentValue() == 0) |
88 | error = Status::FromErrorStringWithFormat( |
89 | format: "invalid --count option value '%s'" , option_arg.str().c_str()); |
90 | } |
91 | break; |
92 | |
93 | case 's': |
94 | if (m_byte_size.GetDefaultValue() == 0) { |
95 | error = Status::FromErrorString(str: "--size option is disabled" ); |
96 | } else { |
97 | error = m_byte_size.SetValueFromString(value: option_arg); |
98 | if (m_byte_size.GetCurrentValue() == 0) |
99 | error = Status::FromErrorStringWithFormat( |
100 | format: "invalid --size option value '%s'" , option_arg.str().c_str()); |
101 | } |
102 | break; |
103 | |
104 | case 'G': { |
105 | uint64_t count = 0; |
106 | llvm::StringRef gdb_format_str = option_arg; |
107 | gdb_format_str.consumeInteger(Radix: 0, Result&: count); |
108 | |
109 | Format format = eFormatDefault; |
110 | uint32_t byte_size = 0; |
111 | |
112 | while (!gdb_format_str.empty() && |
113 | ParserGDBFormatLetter(execution_context, format_letter: gdb_format_str[0], format, |
114 | byte_size)) { |
115 | gdb_format_str = gdb_format_str.drop_front(); |
116 | } |
117 | |
118 | // We the first character of the "gdb_format_str" is not the |
119 | // NULL terminator, we didn't consume the entire string and |
120 | // something is wrong. Also, if none of the format, size or count was |
121 | // specified correctly, then abort. |
122 | if (!gdb_format_str.empty() || |
123 | (format == eFormatInvalid && byte_size == 0 && count == 0)) { |
124 | // Nothing got set correctly |
125 | error = Status::FromErrorStringWithFormat( |
126 | format: "invalid gdb format string '%s'" , option_arg.str().c_str()); |
127 | return error; |
128 | } |
129 | |
130 | // At least one of the format, size or count was set correctly. Anything |
131 | // that wasn't set correctly should be set to the previous default |
132 | if (format == eFormatInvalid) |
133 | ParserGDBFormatLetter(execution_context, format_letter: m_prev_gdb_format, format, |
134 | byte_size); |
135 | |
136 | const bool byte_size_enabled = m_byte_size.GetDefaultValue() < UINT64_MAX; |
137 | const bool count_enabled = m_count.GetDefaultValue() < UINT64_MAX; |
138 | if (byte_size_enabled) { |
139 | // Byte size is enabled |
140 | if (byte_size == 0) |
141 | ParserGDBFormatLetter(execution_context, format_letter: m_prev_gdb_size, format, |
142 | byte_size); |
143 | } else { |
144 | // Byte size is disabled, make sure it wasn't specified but if this is an |
145 | // address, it's actually necessary to specify one so don't error out |
146 | if (byte_size > 0 && format != lldb::eFormatAddressInfo) { |
147 | error = Status::FromErrorString( |
148 | str: "this command doesn't support specifying a byte size" ); |
149 | return error; |
150 | } |
151 | } |
152 | |
153 | if (count_enabled) { |
154 | // Count is enabled and was not set, set it to the default for gdb format |
155 | // statements (which is 1). |
156 | if (count == 0) |
157 | count = 1; |
158 | } else { |
159 | // Count is disabled, make sure it wasn't specified |
160 | if (count > 0) { |
161 | error = Status::FromErrorString( |
162 | str: "this command doesn't support specifying a count" ); |
163 | return error; |
164 | } |
165 | } |
166 | |
167 | m_format.SetCurrentValue(format); |
168 | m_format.SetOptionWasSet(); |
169 | if (byte_size_enabled) { |
170 | m_byte_size.SetCurrentValue(byte_size); |
171 | m_byte_size.SetOptionWasSet(); |
172 | } |
173 | if (count_enabled) { |
174 | m_count.SetCurrentValue(count); |
175 | m_count.SetOptionWasSet(); |
176 | } |
177 | } break; |
178 | |
179 | default: |
180 | llvm_unreachable("Unimplemented option" ); |
181 | } |
182 | |
183 | return error; |
184 | } |
185 | |
186 | bool OptionGroupFormat::ParserGDBFormatLetter( |
187 | ExecutionContext *execution_context, char format_letter, Format &format, |
188 | uint32_t &byte_size) { |
189 | m_has_gdb_format = true; |
190 | switch (format_letter) { |
191 | case 'o': |
192 | format = eFormatOctal; |
193 | m_prev_gdb_format = format_letter; |
194 | return true; |
195 | case 'x': |
196 | format = eFormatHex; |
197 | m_prev_gdb_format = format_letter; |
198 | return true; |
199 | case 'd': |
200 | format = eFormatDecimal; |
201 | m_prev_gdb_format = format_letter; |
202 | return true; |
203 | case 'u': |
204 | format = eFormatUnsigned; |
205 | m_prev_gdb_format = format_letter; |
206 | return true; |
207 | case 't': |
208 | format = eFormatBinary; |
209 | m_prev_gdb_format = format_letter; |
210 | return true; |
211 | case 'f': |
212 | format = eFormatFloat; |
213 | m_prev_gdb_format = format_letter; |
214 | return true; |
215 | case 'a': |
216 | format = eFormatAddressInfo; |
217 | { |
218 | TargetSP target_sp = |
219 | execution_context ? execution_context->GetTargetSP() : TargetSP(); |
220 | if (target_sp) |
221 | byte_size = target_sp->GetArchitecture().GetAddressByteSize(); |
222 | m_prev_gdb_format = format_letter; |
223 | return true; |
224 | } |
225 | case 'i': |
226 | format = eFormatInstruction; |
227 | m_prev_gdb_format = format_letter; |
228 | return true; |
229 | case 'c': |
230 | format = eFormatChar; |
231 | m_prev_gdb_format = format_letter; |
232 | return true; |
233 | case 's': |
234 | format = eFormatCString; |
235 | m_prev_gdb_format = format_letter; |
236 | return true; |
237 | case 'T': |
238 | format = eFormatOSType; |
239 | m_prev_gdb_format = format_letter; |
240 | return true; |
241 | case 'A': |
242 | format = eFormatHexFloat; |
243 | m_prev_gdb_format = format_letter; |
244 | return true; |
245 | |
246 | case 'b': |
247 | case 'h': |
248 | case 'w': |
249 | case 'g': |
250 | { |
251 | // Size isn't used for printing instructions, so if a size is specified, |
252 | // and the previous format was 'i', then we should reset it to the |
253 | // default ('x'). Otherwise we'll continue to print as instructions, |
254 | // which isn't expected. |
255 | if (format_letter == 'b') |
256 | byte_size = 1; |
257 | else if (format_letter == 'h') |
258 | byte_size = 2; |
259 | else if (format_letter == 'w') |
260 | byte_size = 4; |
261 | else if (format_letter == 'g') |
262 | byte_size = 8; |
263 | |
264 | m_prev_gdb_size = format_letter; |
265 | if (m_prev_gdb_format == 'i') |
266 | m_prev_gdb_format = 'x'; |
267 | return true; |
268 | } |
269 | break; |
270 | default: |
271 | break; |
272 | } |
273 | |
274 | |
275 | return false; |
276 | } |
277 | |
278 | void OptionGroupFormat::OptionParsingStarting( |
279 | ExecutionContext *execution_context) { |
280 | m_format.Clear(); |
281 | m_byte_size.Clear(); |
282 | m_count.Clear(); |
283 | m_has_gdb_format = false; |
284 | } |
285 | |