[Lldb-commits] [lldb] r374067 - Add pretty printing of Clang "bitfield" enums
Frederic Riss via lldb-commits
lldb-commits at lists.llvm.org
Tue Oct 8 08:35:59 PDT 2019
Author: friss
Date: Tue Oct 8 08:35:59 2019
New Revision: 374067
URL: http://llvm.org/viewvc/llvm-project?rev=374067&view=rev
Log:
Add pretty printing of Clang "bitfield" enums
Summary:
Using enumerators as flags is standard practice. This patch adds
support to LLDB to display such enum values symbolically, eg:
(E) e1 = A | B
If enumerators don't cover the whole value, the remaining bits are
displayed as hexadecimal:
(E) e4 = A | 0x10
Detecting whether an enum is used as a bitfield or not is
complicated. This patch implements a heuristic that assumes that such
enumerators will either have only 1 bit set or will be a combination
of previous values.
This patch doesn't change the way we currently display enums which the
above heuristic would not consider as bitfields.
Reviewers: jingham, labath
Subscribers: lldb-commits
Differential Revision: https://reviews.llvm.org/D67520
Modified:
lldb/trunk/lit/SymbolFile/DWARF/debug-types-missing-signature.test
lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py
lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/main.c
lldb/trunk/source/Symbol/ClangASTContext.cpp
Modified: lldb/trunk/lit/SymbolFile/DWARF/debug-types-missing-signature.test
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/SymbolFile/DWARF/debug-types-missing-signature.test?rev=374067&r1=374066&r2=374067&view=diff
==============================================================================
--- lldb/trunk/lit/SymbolFile/DWARF/debug-types-missing-signature.test (original)
+++ lldb/trunk/lit/SymbolFile/DWARF/debug-types-missing-signature.test Tue Oct 8 08:35:59 2019
@@ -22,5 +22,5 @@ PRINTEC: use of undeclared identifier 'E
RUN: %lldb %t -b -o "target variable a e ec" | FileCheck --check-prefix=VARS %s
VARS: (const (anonymous struct)) a = {}
-VARS: (const (anonymous enum)) e = 1
-VARS: (const (anonymous enum)) ec = 1
+VARS: (const (anonymous enum)) e = 0x1
+VARS: (const (anonymous enum)) ec = 0x1
Modified: lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py?rev=374067&r1=374066&r2=374067&view=diff
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py (original)
+++ lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py Tue Oct 8 08:35:59 2019
@@ -25,11 +25,35 @@ class EnumTypesTestCase(TestBase):
exe = self.getBuildArtifact("a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ lldbutil.run_to_source_breakpoint(
+ self, '// Breakpoint for bitfield', lldb.SBFileSpec("main.c"))
+
+ self.expect("fr var a", DATA_TYPES_DISPLAYED_CORRECTLY,
+ patterns=[' = A$'])
+ self.expect("fr var b", DATA_TYPES_DISPLAYED_CORRECTLY,
+ patterns=[' = B$'])
+ self.expect("fr var c", DATA_TYPES_DISPLAYED_CORRECTLY,
+ patterns=[' = C$'])
+ self.expect("fr var ab", DATA_TYPES_DISPLAYED_CORRECTLY,
+ patterns=[' = AB$'])
+ self.expect("fr var ac", DATA_TYPES_DISPLAYED_CORRECTLY,
+ patterns=[' = A | C$'])
+ self.expect("fr var all", DATA_TYPES_DISPLAYED_CORRECTLY,
+ patterns=[' = ALL$'])
+ # Test that an enum that doesn't match the heuristic we use in
+ # ClangASTContext::DumpEnumValue, gets printed as a raw integer.
+ self.expect("fr var omega", DATA_TYPES_DISPLAYED_CORRECTLY,
+ patterns=[' = 7$'])
+ # Test the behavior in case have a variable of a type considered
+ # 'bitfield' by the heuristic, but the value isn't actually fully
+ # covered by the enumerators.
+ self.expect("p (enum bitfield)nonsense", DATA_TYPES_DISPLAYED_CORRECTLY,
+ patterns=[' = B | C | 0x10$'])
+
# Break inside the main.
bkpt_id = lldbutil.run_break_set_by_file_and_line(
self, "main.c", self.line, num_expected_locations=1, loc_exact=True)
-
- self.runCmd("run", RUN_SUCCEEDED)
+ self.runCmd("c", RUN_SUCCEEDED)
# The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
Modified: lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/main.c
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/main.c?rev=374067&r1=374066&r2=374067&view=diff
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/main.c (original)
+++ lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/main.c Tue Oct 8 08:35:59 2019
@@ -18,6 +18,20 @@ struct foo {
int main (int argc, char const *argv[])
{
+ enum bitfield {
+ None = 0,
+ A = 1 << 0,
+ B = 1 << 1,
+ C = 1 << 2,
+ AB = A | B,
+ ALL = A | B | C,
+ };
+
+ enum non_bitfield {
+ Alpha = 3,
+ Beta = 4
+ };
+
enum days {
Monday = -3,
Tuesday,
@@ -28,9 +42,14 @@ int main (int argc, char const *argv[])
Sunday,
kNumDays
};
+
+ enum bitfield a = A, b = B, c = C, ab = AB, ac = A | C, all = ALL;
+ int nonsense = a + b + c + ab + ac + all;
+ enum non_bitfield omega = Alpha | Beta;
+
enum days day;
struct foo f;
- f.op = NULL;
+ f.op = NULL; // Breakpoint for bitfield
for (day = Monday - 1; day <= kNumDays + 1; day++)
{
printf("day as int is %i\n", (int)day); // Set break point at this line.
Modified: lldb/trunk/source/Symbol/ClangASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Symbol/ClangASTContext.cpp?rev=374067&r1=374066&r2=374067&view=diff
==============================================================================
--- lldb/trunk/source/Symbol/ClangASTContext.cpp (original)
+++ lldb/trunk/source/Symbol/ClangASTContext.cpp Tue Oct 8 08:35:59 2019
@@ -9359,21 +9359,71 @@ static bool DumpEnumValue(const clang::Q
llvm::cast<clang::EnumType>(qual_type.getTypePtr());
const clang::EnumDecl *enum_decl = enutype->getDecl();
assert(enum_decl);
- clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos;
- const bool is_signed = qual_type->isSignedIntegerOrEnumerationType();
lldb::offset_t offset = byte_offset;
- const int64_t enum_value = data.GetMaxS64Bitfield(
+ const uint64_t enum_svalue = data.GetMaxS64Bitfield(
&offset, byte_size, bitfield_bit_size, bitfield_bit_offset);
+ bool can_be_bitfield = true;
+ uint64_t covered_bits = 0;
+ int num_enumerators = 0;
+ // Try to find an exact match for the value.
+ // At the same time, we're applying a heuristic to determine whether we want
+ // to print this enum as a bitfield. We're likely dealing with a bitfield if
+ // every enumrator is either a one bit value or a superset of the previous
+ // enumerators. Also 0 doesn't make sense when the enumerators are used as
+ // flags.
for (auto enumerator : enum_decl->enumerators()) {
- if (enumerator->getInitVal().getSExtValue() == enum_value) {
+ uint64_t val = enumerator->getInitVal().getSExtValue();
+ if (llvm::countPopulation(val) != 1 && (val & ~covered_bits) != 0)
+ can_be_bitfield = false;
+ covered_bits |= val;
+ ++num_enumerators;
+ if (val == enum_svalue) {
+ // Found an exact match, that's all we need to do.
s->PutCString(enumerator->getNameAsString());
return true;
}
}
- // If we have gotten here we didn't get find the enumerator in the
- // enum decl, so just print the integer.
- s->Printf("%" PRIi64, enum_value);
+
+ // No exact match, but we don't think this is a bitfield. Print the value as
+ // decimal.
+ if (!can_be_bitfield) {
+ s->Printf("%" PRIi64, enum_svalue);
+ return true;
+ }
+
+ // Unsigned values make more sense for flags.
+ offset = byte_offset;
+ const uint64_t enum_uvalue = data.GetMaxU64Bitfield(
+ &offset, byte_size, bitfield_bit_size, bitfield_bit_offset);
+
+ uint64_t remaining_value = enum_uvalue;
+ std::vector<std::pair<uint64_t, llvm::StringRef>> values;
+ values.reserve(num_enumerators);
+ for (auto enumerator : enum_decl->enumerators())
+ if (auto val = enumerator->getInitVal().getZExtValue())
+ values.emplace_back(val, enumerator->getName());
+
+ // Sort in reverse order of the number of the population count, so that in
+ // `enum {A, B, ALL = A|B }` we visit ALL first. Use a stable sort so that
+ // A | C where A is declared before C is displayed in this order.
+ std::stable_sort(values.begin(), values.end(), [](const auto &a, const auto &b) {
+ return llvm::countPopulation(a.first) > llvm::countPopulation(b.first);
+ });
+
+ for (const auto &val : values) {
+ if ((remaining_value & val.first) != val.first)
+ continue;
+ remaining_value &= ~val.first;
+ s->PutCString(val.second);
+ if (remaining_value)
+ s->PutCString(" | ");
+ }
+
+ // If there is a remainder that is not covered by the value, print it as hex.
+ if (remaining_value)
+ s->Printf("0x%" PRIx64, remaining_value);
+
return true;
}
@@ -9390,6 +9440,13 @@ bool ClangASTContext::DumpTypeValue(
clang::QualType qual_type(GetQualType(type));
const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+
+ if (type_class == clang::Type::Elaborated) {
+ qual_type = llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType();
+ return DumpTypeValue(qual_type.getAsOpaquePtr(), s, format, data, byte_offset, byte_size,
+ bitfield_bit_size, bitfield_bit_offset, exe_scope);
+ }
+
switch (type_class) {
case clang::Type::Typedef: {
clang::QualType typedef_qual_type =
More information about the lldb-commits
mailing list