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