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.SetErrorString("--count option is disabled" ); |
85 | } else { |
86 | error = m_count.SetValueFromString(value: option_arg); |
87 | if (m_count.GetCurrentValue() == 0) |
88 | error.SetErrorStringWithFormat("invalid --count option value '%s'" , |
89 | option_arg.str().c_str()); |
90 | } |
91 | break; |
92 | |
93 | case 's': |
94 | if (m_byte_size.GetDefaultValue() == 0) { |
95 | error.SetErrorString("--size option is disabled" ); |
96 | } else { |
97 | error = m_byte_size.SetValueFromString(value: option_arg); |
98 | if (m_byte_size.GetCurrentValue() == 0) |
99 | error.SetErrorStringWithFormat("invalid --size option value '%s'" , |
100 | 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.SetErrorStringWithFormat("invalid gdb format string '%s'" , |
126 | 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.SetErrorString( |
148 | "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.SetErrorString("this command doesn't support specifying a count" ); |
162 | return error; |
163 | } |
164 | } |
165 | |
166 | m_format.SetCurrentValue(format); |
167 | m_format.SetOptionWasSet(); |
168 | if (byte_size_enabled) { |
169 | m_byte_size.SetCurrentValue(byte_size); |
170 | m_byte_size.SetOptionWasSet(); |
171 | } |
172 | if (count_enabled) { |
173 | m_count.SetCurrentValue(count); |
174 | m_count.SetOptionWasSet(); |
175 | } |
176 | } break; |
177 | |
178 | default: |
179 | llvm_unreachable("Unimplemented option" ); |
180 | } |
181 | |
182 | return error; |
183 | } |
184 | |
185 | bool OptionGroupFormat::ParserGDBFormatLetter( |
186 | ExecutionContext *execution_context, char format_letter, Format &format, |
187 | uint32_t &byte_size) { |
188 | m_has_gdb_format = true; |
189 | switch (format_letter) { |
190 | case 'o': |
191 | format = eFormatOctal; |
192 | m_prev_gdb_format = format_letter; |
193 | return true; |
194 | case 'x': |
195 | format = eFormatHex; |
196 | m_prev_gdb_format = format_letter; |
197 | return true; |
198 | case 'd': |
199 | format = eFormatDecimal; |
200 | m_prev_gdb_format = format_letter; |
201 | return true; |
202 | case 'u': |
203 | format = eFormatUnsigned; |
204 | m_prev_gdb_format = format_letter; |
205 | return true; |
206 | case 't': |
207 | format = eFormatBinary; |
208 | m_prev_gdb_format = format_letter; |
209 | return true; |
210 | case 'f': |
211 | format = eFormatFloat; |
212 | m_prev_gdb_format = format_letter; |
213 | return true; |
214 | case 'a': |
215 | format = eFormatAddressInfo; |
216 | { |
217 | TargetSP target_sp = |
218 | execution_context ? execution_context->GetTargetSP() : TargetSP(); |
219 | if (target_sp) |
220 | byte_size = target_sp->GetArchitecture().GetAddressByteSize(); |
221 | m_prev_gdb_format = format_letter; |
222 | return true; |
223 | } |
224 | case 'i': |
225 | format = eFormatInstruction; |
226 | m_prev_gdb_format = format_letter; |
227 | return true; |
228 | case 'c': |
229 | format = eFormatChar; |
230 | m_prev_gdb_format = format_letter; |
231 | return true; |
232 | case 's': |
233 | format = eFormatCString; |
234 | m_prev_gdb_format = format_letter; |
235 | return true; |
236 | case 'T': |
237 | format = eFormatOSType; |
238 | m_prev_gdb_format = format_letter; |
239 | return true; |
240 | case 'A': |
241 | format = eFormatHexFloat; |
242 | m_prev_gdb_format = format_letter; |
243 | return true; |
244 | |
245 | case 'b': |
246 | case 'h': |
247 | case 'w': |
248 | case 'g': |
249 | { |
250 | // Size isn't used for printing instructions, so if a size is specified, |
251 | // and the previous format was 'i', then we should reset it to the |
252 | // default ('x'). Otherwise we'll continue to print as instructions, |
253 | // which isn't expected. |
254 | if (format_letter == 'b') |
255 | byte_size = 1; |
256 | else if (format_letter == 'h') |
257 | byte_size = 2; |
258 | else if (format_letter == 'w') |
259 | byte_size = 4; |
260 | else if (format_letter == 'g') |
261 | byte_size = 8; |
262 | |
263 | m_prev_gdb_size = format_letter; |
264 | if (m_prev_gdb_format == 'i') |
265 | m_prev_gdb_format = 'x'; |
266 | return true; |
267 | } |
268 | break; |
269 | default: |
270 | break; |
271 | } |
272 | |
273 | |
274 | return false; |
275 | } |
276 | |
277 | void OptionGroupFormat::OptionParsingStarting( |
278 | ExecutionContext *execution_context) { |
279 | m_format.Clear(); |
280 | m_byte_size.Clear(); |
281 | m_count.Clear(); |
282 | m_has_gdb_format = false; |
283 | } |
284 | |