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