Index: lit/SymbolFile/DWARF/debug-types-missing-signature.test =================================================================== --- lit/SymbolFile/DWARF/debug-types-missing-signature.test +++ lit/SymbolFile/DWARF/debug-types-missing-signature.test @@ -22,5 +22,5 @@ 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 Index: packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py =================================================================== --- packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py +++ packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py @@ -25,11 +25,30 @@ 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$']) + self.expect("fr var omega", DATA_TYPES_DISPLAYED_CORRECTLY, + patterns=[' = 7$']) + 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, Index: packages/Python/lldbsuite/test/lang/c/enum_types/main.c =================================================================== --- packages/Python/lldbsuite/test/lang/c/enum_types/main.c +++ packages/Python/lldbsuite/test/lang/c/enum_types/main.c @@ -18,6 +18,20 @@ 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 @@ 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. Index: source/Symbol/ClangASTContext.cpp =================================================================== --- source/Symbol/ClangASTContext.cpp +++ source/Symbol/ClangASTContext.cpp @@ -9370,6 +9370,82 @@ } } +static bool DumpEnumValue(const clang::QualType &qual_type, Stream *s, + const DataExtractor &data, lldb::offset_t byte_offset, + size_t byte_size, uint32_t bitfield_bit_offset, + uint32_t bitfield_bit_size) { + const clang::EnumType *enutype = + llvm::cast(qual_type.getTypePtr()); + const clang::EnumDecl *enum_decl = enutype->getDecl(); + assert(enum_decl); + lldb::offset_t offset = byte_offset; + 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()) { + 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; + } + } + + // 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> 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 < 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; +} + bool ClangASTContext::DumpTypeValue( lldb::opaque_compiler_type_t type, Stream *s, lldb::Format format, const DataExtractor &data, lldb::offset_t byte_offset, size_t byte_size, @@ -9383,6 +9459,13 @@ 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(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 = @@ -9413,45 +9496,9 @@ // If our format is enum or default, show the enumeration value as its // enumeration string value, else just display it as requested. if ((format == eFormatEnum || format == eFormatDefault) && - GetCompleteType(type)) { - const clang::EnumType *enutype = - llvm::cast(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; - if (is_signed) { - const int64_t enum_svalue = data.GetMaxS64Bitfield( - &offset, byte_size, bitfield_bit_size, bitfield_bit_offset); - for (enum_pos = enum_decl->enumerator_begin(), - enum_end_pos = enum_decl->enumerator_end(); - enum_pos != enum_end_pos; ++enum_pos) { - if (enum_pos->getInitVal().getSExtValue() == enum_svalue) { - s->PutCString(enum_pos->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_svalue); - } else { - const uint64_t enum_uvalue = data.GetMaxU64Bitfield( - &offset, byte_size, bitfield_bit_size, bitfield_bit_offset); - for (enum_pos = enum_decl->enumerator_begin(), - enum_end_pos = enum_decl->enumerator_end(); - enum_pos != enum_end_pos; ++enum_pos) { - if (enum_pos->getInitVal().getZExtValue() == enum_uvalue) { - s->PutCString(enum_pos->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("%" PRIu64, enum_uvalue); - } - return true; - } + GetCompleteType(type)) + return DumpEnumValue(qual_type, s, data, byte_offset, byte_size, + bitfield_bit_offset, bitfield_bit_size); // format was not enum, just fall through and dump the value as // requested.... LLVM_FALLTHROUGH;