LLDB mainline
RegisterFlags.cpp
Go to the documentation of this file.
1//===-- RegisterFlags.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
10#include "lldb/Utility/Log.h"
12
13#include "llvm/ADT/StringExtras.h"
14
15#include <limits>
16#include <numeric>
17#include <optional>
18
19using namespace lldb_private;
20
21RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end)
22 : m_name(std::move(name)), m_start(start), m_end(end),
23 m_enum_type(nullptr) {
24 assert(m_start <= m_end && "Start bit must be <= end bit.");
25}
26
27RegisterFlags::Field::Field(std::string name, unsigned bit_position)
28 : m_name(std::move(name)), m_start(bit_position), m_end(bit_position),
29 m_enum_type(nullptr) {}
30
31RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end,
32 const FieldEnum *enum_type)
33 : m_name(std::move(name)), m_start(start), m_end(end),
34 m_enum_type(enum_type) {
35 if (m_enum_type) {
36 // Check that all values fit into this field. The XML parser will also
37 // do this check so at runtime nothing should fail this check.
38 // We can also make enums in C++ at compile time, which might fail this
39 // check, so we catch them before it makes it into a release.
40 uint64_t max_value = GetMaxValue();
42 for (const auto &enumerator : m_enum_type->GetEnumerators()) {
43 UNUSED_IF_ASSERT_DISABLED(enumerator);
44 assert(enumerator.m_value <= max_value &&
45 "Enumerator value exceeds maximum value for this field");
46 }
47 }
48}
49
51 LLDB_LOG(log, " Name: \"{0}\" Start: {1} End: {2}", m_name.c_str(), m_start,
52 m_end);
53}
54
55bool RegisterFlags::Field::Overlaps(const Field &other) const {
56 unsigned overlap_start = std::max(GetStart(), other.GetStart());
57 unsigned overlap_end = std::min(GetEnd(), other.GetEnd());
58 return overlap_start <= overlap_end;
59}
60
61unsigned RegisterFlags::Field::PaddingDistance(const Field &other) const {
62 assert(!Overlaps(other) &&
63 "Cannot get padding distance for overlapping fields.");
64 assert((other < (*this)) && "Expected fields in MSB to LSB order.");
65
66 // If they don't overlap they are either next to each other or separated
67 // by some number of bits.
68
69 // Where left will be the MSB and right will be the LSB.
70 unsigned lhs_start = GetStart();
71 unsigned rhs_end = other.GetStart() + other.GetSizeInBits() - 1;
72
73 if (*this < other) {
74 lhs_start = other.GetStart();
75 rhs_end = GetStart() + GetSizeInBits() - 1;
76 }
77
78 return lhs_start - rhs_end - 1;
79}
80
81unsigned RegisterFlags::Field::GetSizeInBits(unsigned start, unsigned end) {
82 return end - start + 1;
83}
84
86 return GetSizeInBits(m_start, m_end);
87}
88
89uint64_t RegisterFlags::Field::GetMaxValue(unsigned start, unsigned end) {
90 uint64_t max = std::numeric_limits<uint64_t>::max();
91 unsigned bits = GetSizeInBits(start, end);
92 // If the field is >= 64 bits the shift below would be undefined.
93 // We assume the GDB client has discarded any field that would fail this
94 // assert, it's only to check information we define directly in C++.
95 assert(bits <= 64 && "Cannot handle field with size > 64 bits");
96 if (bits < 64) {
97 max = ((uint64_t)1 << bits) - 1;
98 }
99 return max;
100}
101
102uint64_t RegisterFlags::Field::GetMaxValue() const {
103 return GetMaxValue(m_start, m_end);
104}
105
106uint64_t RegisterFlags::Field::GetMask() const {
107 return GetMaxValue() << m_start;
108}
109
110void RegisterFlags::SetFields(const std::vector<Field> &fields) {
111 // We expect that these are unsorted but do not overlap.
112 // They could fill the register but may have gaps.
113 std::vector<Field> provided_fields = fields;
114
115 m_fields.clear();
116 m_fields.reserve(provided_fields.size());
117
118 // ProcessGDBRemote should have sorted these in descending order already.
119 assert(std::is_sorted(provided_fields.rbegin(), provided_fields.rend()));
120
121 // Build a new list of fields that includes anonymous (empty name) fields
122 // wherever there is a gap. This will simplify processing later.
123 std::optional<Field> previous_field;
124 unsigned register_msb = (m_size * 8) - 1;
125 for (auto field : provided_fields) {
126 if (previous_field) {
127 unsigned padding = previous_field->PaddingDistance(field);
128 if (padding) {
129 // -1 to end just before the previous field.
130 unsigned end = previous_field->GetStart() - 1;
131 // +1 because if you want to pad 1 bit you want to start and end
132 // on the same bit.
133 m_fields.push_back(Field("", field.GetEnd() + 1, end));
134 }
135 } else {
136 // This is the first field. Check that it starts at the register's MSB.
137 if (field.GetEnd() != register_msb)
138 m_fields.push_back(Field("", field.GetEnd() + 1, register_msb));
139 }
140 m_fields.push_back(field);
141 previous_field = field;
142 }
143
144 // The last field may not extend all the way to bit 0.
145 if (previous_field && previous_field->GetStart() != 0)
146 m_fields.push_back(Field("", 0, previous_field->GetStart() - 1));
147}
148
149RegisterFlags::RegisterFlags(std::string id, unsigned size,
150 const std::vector<Field> &fields)
151 : m_id(std::move(id)), m_size(size) {
152 SetFields(fields);
153}
154
155void RegisterFlags::DumpToLog(Log *log) const {
156 LLDB_LOG(log, "ID: \"{0}\" Size: {1}", m_id.c_str(), m_size);
157 for (const Field &field : m_fields)
158 field.DumpToLog(log);
159}
160
162 unsigned column_width) {
163 unsigned pad = column_width - content.GetString().size();
164 std::string pad_l;
165 std::string pad_r;
166 if (pad) {
167 pad_l = std::string(pad / 2, ' ');
168 pad_r = std::string((pad / 2) + (pad % 2), ' ');
169 }
170
171 StreamString aligned;
172 aligned.Printf("|%s%s%s", pad_l.c_str(), content.GetString().data(),
173 pad_r.c_str());
174 return aligned;
175}
176
177static void EmitTable(std::string &out, std::array<std::string, 3> &table) {
178 // Close the table.
179 for (std::string &line : table)
180 line += '|';
181
182 out += std::accumulate(table.begin() + 1, table.end(), table.front(),
183 [](std::string lhs, const auto &rhs) {
184 return std::move(lhs) + "\n" + rhs;
185 });
186}
187
188std::string RegisterFlags::AsTable(uint32_t max_width) const {
189 std::string table;
190 // position / gridline / name
191 std::array<std::string, 3> lines;
192 uint32_t current_width = 0;
193
194 for (const RegisterFlags::Field &field : m_fields) {
195 StreamString position;
196 if (field.GetEnd() == field.GetStart())
197 position.Printf(" %d ", field.GetEnd());
198 else
199 position.Printf(" %d-%d ", field.GetEnd(), field.GetStart());
200
201 StreamString name;
202 name.Printf(" %s ", field.GetName().c_str());
203
204 unsigned column_width = position.GetString().size();
205 unsigned name_width = name.GetString().size();
206 if (name_width > column_width)
207 column_width = name_width;
208
209 // If the next column would overflow and we have already formatted at least
210 // one column, put out what we have and move to a new table on the next line
211 // (+1 here because we need to cap the ends with '|'). If this is the first
212 // column, just let it overflow and we'll wrap next time around. There's not
213 // much we can do with a very small terminal.
214 if (current_width && ((current_width + column_width + 1) >= max_width)) {
215 EmitTable(table, lines);
216 // Blank line between each.
217 table += "\n\n";
218
219 for (std::string &line : lines)
220 line.clear();
221 current_width = 0;
222 }
223
224 StreamString aligned_position = FormatCell(position, column_width);
225 lines[0] += aligned_position.GetString();
226 StreamString grid;
227 grid << '|' << std::string(column_width, '-');
228 lines[1] += grid.GetString();
229 StreamString aligned_name = FormatCell(name, column_width);
230 lines[2] += aligned_name.GetString();
231
232 // +1 for the left side '|'.
233 current_width += column_width + 1;
234 }
235
236 // If we didn't overflow and still have table to print out.
237 if (lines[0].size())
238 EmitTable(table, lines);
239
240 return table;
241}
242
243// Print enums as:
244// value = name, value2 = name2
245// Subject to the limits of the terminal width.
246static void DumpEnumerators(StreamString &strm, size_t indent,
247 size_t current_width, uint32_t max_width,
248 const FieldEnum::Enumerators &enumerators) {
249 for (auto it = enumerators.cbegin(); it != enumerators.cend(); ++it) {
250 StreamString enumerator_strm;
251 // The first enumerator of a line doesn't need to be separated.
252 if (current_width != indent)
253 enumerator_strm << ' ';
254
255 enumerator_strm.Printf("%" PRIu64 " = %s", it->m_value, it->m_name.c_str());
256
257 // Don't put "," after the last enumerator.
258 if (std::next(it) != enumerators.cend())
259 enumerator_strm << ",";
260
261 llvm::StringRef enumerator_string = enumerator_strm.GetString();
262 // If printing the next enumerator would take us over the width, start
263 // a new line. However, if we're printing the first enumerator of this
264 // line, don't start a new one. Resulting in there being at least one per
265 // line.
266 //
267 // This means for very small widths we get:
268 // A: 0 = foo,
269 // 1 = bar
270 // Instead of:
271 // A:
272 // 0 = foo,
273 // 1 = bar
274 if ((current_width + enumerator_string.size() > max_width) &&
275 current_width != indent) {
276 current_width = indent;
277 strm << '\n' << std::string(indent, ' ');
278 // We're going to a new line so we don't need a space before the
279 // name of the enumerator.
280 enumerator_string = enumerator_string.drop_front();
281 }
282
283 current_width += enumerator_string.size();
284 strm << enumerator_string;
285 }
286}
287
288std::string RegisterFlags::DumpEnums(uint32_t max_width) const {
289 StreamString strm;
290 bool printed_enumerators_once = false;
291
292 for (const auto &field : m_fields) {
293 const FieldEnum *enum_type = field.GetEnum();
294 if (!enum_type)
295 continue;
296
297 const FieldEnum::Enumerators &enumerators = enum_type->GetEnumerators();
298 if (enumerators.empty())
299 continue;
300
301 // Break between enumerators of different fields.
302 if (printed_enumerators_once)
303 strm << "\n\n";
304 else
305 printed_enumerators_once = true;
306
307 std::string name_string = field.GetName() + ": ";
308 size_t indent = name_string.size();
309 size_t current_width = indent;
310
311 strm << name_string;
312
313 DumpEnumerators(strm, indent, current_width, max_width, enumerators);
314 }
315
316 return strm.GetString().str();
317}
318
319void RegisterFlags::EnumsToXML(Stream &strm, llvm::StringSet<> &seen) const {
320 for (const Field &field : m_fields)
321 if (const FieldEnum *enum_type = field.GetEnum()) {
322 const std::string &id = enum_type->GetID();
323 if (!seen.contains(id)) {
324 enum_type->ToXML(strm, GetSize());
325 seen.insert(id);
326 }
327 }
328}
329
330void FieldEnum::ToXML(Stream &strm, unsigned size) const {
331 // Example XML:
332 // <enum id="foo" size="4">
333 // <evalue name="bar" value="1"/>
334 // </enum>
335 // Note that "size" is only emitted for GDB compatibility, LLDB does not need
336 // it.
337
338 strm.Indent();
339 strm << "<enum id=\"" << GetID() << "\" ";
340 // This is the size of the underlying enum type if this were a C type.
341 // In other words, the size of the register in bytes.
342 strm.Printf("size=\"%d\"", size);
343
344 const Enumerators &enumerators = GetEnumerators();
345 if (enumerators.empty()) {
346 strm << "/>\n";
347 return;
348 }
349
350 strm << ">\n";
351 strm.IndentMore();
352 for (const auto &enumerator : enumerators) {
353 strm.Indent();
354 enumerator.ToXML(strm);
355 strm.PutChar('\n');
356 }
357 strm.IndentLess();
358 strm.Indent("</enum>\n");
359}
360
362 std::string escaped_name;
363 llvm::raw_string_ostream escape_strm(escaped_name);
364 llvm::printHTMLEscaped(m_name, escape_strm);
365 strm.Printf("<evalue name=\"%s\" value=\"%" PRIu64 "\"/>",
366 escaped_name.c_str(), m_value);
367}
368
370 LLDB_LOG(log, " Name: \"{0}\" Value: {1}", m_name.c_str(), m_value);
371}
372
373void FieldEnum::DumpToLog(Log *log) const {
374 LLDB_LOG(log, "ID: \"{0}\"", m_id.c_str());
375 for (const auto &enumerator : GetEnumerators())
376 enumerator.DumpToLog(log);
377}
378
379void RegisterFlags::ToXML(Stream &strm) const {
380 // Example XML:
381 // <flags id="cpsr_flags" size="4">
382 // <field name="incorrect" start="0" end="0"/>
383 // </flags>
384 strm.Indent();
385 strm << "<flags id=\"" << GetID() << "\" ";
386 strm.Printf("size=\"%d\"", GetSize());
387 strm << ">";
388 for (const Field &field : m_fields) {
389 // Skip padding fields.
390 if (field.GetName().empty())
391 continue;
392
393 strm << "\n";
394 strm.IndentMore();
395 field.ToXML(strm);
396 strm.IndentLess();
397 }
398 strm.PutChar('\n');
399 strm.Indent("</flags>\n");
400}
401
403 // Example XML with an enum:
404 // <field name="correct" start="0" end="0" type="some_enum">
405 // Without:
406 // <field name="correct" start="0" end="0"/>
407 strm.Indent();
408 strm << "<field name=\"";
409
410 std::string escaped_name;
411 llvm::raw_string_ostream escape_strm(escaped_name);
412 llvm::printHTMLEscaped(GetName(), escape_strm);
413 strm << escaped_name << "\" ";
414
415 strm.Printf("start=\"%d\" end=\"%d\"", GetStart(), GetEnd());
416
417 if (const FieldEnum *enum_type = GetEnum())
418 strm << " type=\"" << enum_type->GetID() << "\"";
419
420 strm << "/>";
421}
422
423FieldEnum::FieldEnum(std::string id, const Enumerators &enumerators)
424 : m_id(id), m_enumerators(enumerators) {
425 for (const auto &enumerator : m_enumerators) {
426 UNUSED_IF_ASSERT_DISABLED(enumerator);
427 assert(enumerator.m_name.size() && "Enumerator name cannot be empty");
428 }
429}
static char ID
Definition: HostInfoBase.h:37
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition: Log.h:359
static void DumpEnumerators(StreamString &strm, size_t indent, size_t current_width, uint32_t max_width, const FieldEnum::Enumerators &enumerators)
static StreamString FormatCell(const StreamString &content, unsigned column_width)
static void EmitTable(std::string &out, std::array< std::string, 3 > &table)
static llvm::StringRef GetName(XcodeSDK::Type type)
Definition: XcodeSDK.cpp:21
FieldEnum(std::string id, const Enumerators &enumerators)
std::vector< Enumerator > Enumerators
Definition: RegisterFlags.h:39
const Enumerators & GetEnumerators() const
Definition: RegisterFlags.h:46
void DumpToLog(Log *log) const
void ToXML(Stream &strm, unsigned size) const
bool Overlaps(const Field &other) const
unsigned PaddingDistance(const Field &other) const
Return the number of bits between this field and the other, that are not covered by either field.
Field(std::string name, unsigned start, unsigned end)
Where start is the least significant bit and end is the most significant bit.
uint64_t GetMaxValue() const
The maximum unsigned value that could be contained in this field.
unsigned GetSizeInBits() const
Get size of the field in bits. Will always be at least 1.
unsigned m_start
Start/end bit positions.
void ToXML(Stream &strm) const
Output XML that describes this field, to be inserted into a target XML file.
std::vector< Field > m_fields
std::string DumpEnums(uint32_t max_width) const
Make a string where each line contains the name of a field that has enum values, and lists what those...
const unsigned m_size
Size in bytes.
void EnumsToXML(Stream &strm, llvm::StringSet<> &seen) const
Enum types must be defined before use, and GDBRemoteCommunicationServerLLGS view of the register type...
const std::string & GetID() const
void ToXML(Stream &strm) const
Output XML that describes this set of flags.
std::string AsTable(uint32_t max_width) const
Produce a text table showing the layout of all the fields.
llvm::StringRef GetString() const
A stream class that can stream formatted output to a file.
Definition: Stream.h:28
size_t Indent(llvm::StringRef s="")
Indent the current line in the stream.
Definition: Stream.cpp:157
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition: Stream.cpp:134
size_t PutChar(char ch)
Definition: Stream.cpp:131
void IndentLess(unsigned amount=2)
Decrement the current indentation level.
Definition: Stream.cpp:198
void IndentMore(unsigned amount=2)
Increment the current indentation level.
Definition: Stream.cpp:195
#define UNUSED_IF_ASSERT_DISABLED(x)
Definition: lldb-defines.h:140
A class that represents a running process on the host machine.
static uint32_t bits(const uint32_t val, const uint32_t msbit, const uint32_t lsbit)
Definition: ARMUtils.h:265
void ToXML(Stream &strm) const