Skip to content

Commit

Permalink
[lldb] Add format eFormatEnumWithValues to ensure raw enum value is a…
Browse files Browse the repository at this point in the history
…lways shown

When an enum is used to represent certain data it can be useful to know
its name and the value of it. For instance, register fields are
often set in source code as numbers, but in the debugger you'd like
to see the meaning as well.

(lldb) register read fpcr
    fpcr = 0x00000000
         = (... RMode = RN (0), ...)

Often you do just want the meaning but the value saves you having
to manually decode it if you want to confirm what your source code
has done, or try to replicate the current state in your source code.

This also works for bitfield like enums, with the added change
that if a bitfield like enum has the value 0, we will print 0 if
asked to always show a value. Normally we don't print a 0 there
because 0 means no flags are set.

I did not intend to make this new format avaialable to the user,
but it ended up being so. So you can do:

expression --format "enumeration with values" -- foo

So testing is mainly from c++ but I have added a couple to the
Python tests.
  • Loading branch information
DavidSpickett committed May 28, 2024
1 parent 7cdd53d commit 6344ce3
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 12 deletions.
12 changes: 8 additions & 4 deletions lldb/include/lldb/lldb-enumerations.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,15 @@ enum Format {
///< character arrays that can contain non printable
///< characters
eFormatAddressInfo, ///< Describe what an address points to (func + offset
///< with file/line, symbol + offset, data, etc)
eFormatHexFloat, ///< ISO C99 hex float string
eFormatInstruction, ///< Disassemble an opcode
eFormatVoid, ///< Do not print this
///< with file/line, symbol + offset, data, etc)
eFormatHexFloat, ///< ISO C99 hex float string
eFormatInstruction, ///< Disassemble an opcode
eFormatVoid, ///< Do not print this
eFormatUnicode8,
eFormatEnumWithValues, ///< Format as an enum but if the value matches one or
///< more enumerators, print the enumerator name and
///< value of those enumerators. For example "foo (1)"
///< instead of "foo".
kNumFormats
};

Expand Down
1 change: 1 addition & 0 deletions lldb/source/Commands/CommandObjectMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,7 @@ class CommandObjectMemoryWrite : public CommandObjectParsed {
case eFormatBytesWithASCII:
case eFormatComplex:
case eFormatEnum:
case eFormatEnumWithValues:
case eFormatUnicode8:
case eFormatUnicode16:
case eFormatUnicode32:
Expand Down
1 change: 1 addition & 0 deletions lldb/source/Core/DumpDataExtractor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ lldb::offset_t lldb_private::DumpDataExtractor(

case eFormatEnum: // Print enum value as a signed integer when we don't get
// the enum type
case eFormatEnumWithValues:
case eFormatDecimal:
if (item_byte_size <= 8)
s->Printf("%" PRId64,
Expand Down
3 changes: 2 additions & 1 deletion lldb/source/Core/ValueObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1189,7 +1189,8 @@ bool ValueObject::DumpPrintableRepresentation(
return !error.Fail();
}

if (custom_format == eFormatEnum)
if (custom_format == eFormatEnum ||
custom_format == eFormatEnumWithValues)
return false;

// this only works for arrays, because I have no way to know when the
Expand Down
1 change: 1 addition & 0 deletions lldb/source/DataFormatters/FormatManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ static constexpr FormatInfo g_format_infos[] = {
{eFormatInstruction, 'i', "instruction"},
{eFormatVoid, 'v', "void"},
{eFormatUnicode8, 'u', "unicode8"},
{eFormatEnumWithValues, '\0', "enumeration with values"},
};

static_assert((sizeof(g_format_infos) / sizeof(g_format_infos[0])) ==
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/DataFormatters/VectorType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ static CompilerType GetCompilerTypeForFormat(lldb::Format format,
case lldb::eFormatComplexInteger:
case lldb::eFormatDecimal:
case lldb::eFormatEnum:
case lldb::eFormatEnumWithValues:
case lldb::eFormatInstruction:
case lldb::eFormatOSType:
case lldb::eFormatVoid:
Expand Down Expand Up @@ -150,6 +151,7 @@ static lldb::Format GetItemFormatForFormat(lldb::Format format,
case lldb::eFormatComplexInteger:
case lldb::eFormatDecimal:
case lldb::eFormatEnum:
case lldb::eFormatEnumWithValues:
case lldb::eFormatInstruction:
case lldb::eFormatOSType:
case lldb::eFormatVoid:
Expand Down
28 changes: 21 additions & 7 deletions lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8572,7 +8572,7 @@ void TypeSystemClang::DumpFromSymbolFile(Stream &s,
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) {
uint32_t bitfield_bit_size, bool always_show_value) {
const clang::EnumType *enutype =
llvm::cast<clang::EnumType>(qual_type.getTypePtr());
const clang::EnumDecl *enum_decl = enutype->getDecl();
Expand All @@ -8599,7 +8599,11 @@ static bool DumpEnumValue(const clang::QualType &qual_type, Stream &s,
++num_enumerators;
if (val == enum_svalue) {
// Found an exact match, that's all we need to do.
s.PutCString(enumerator->getNameAsString());
if (always_show_value)
s.Printf("%s (%" PRIi64 ")", enumerator->getNameAsString().c_str(),
enum_svalue);
else
s.PutCString(enumerator->getNameAsString());
return true;
}
}
Expand Down Expand Up @@ -8634,17 +8638,23 @@ static bool DumpEnumValue(const clang::QualType &qual_type, Stream &s,
return llvm::popcount(a.first) > llvm::popcount(b.first);
});

bool found_enumerator = false;
for (const auto &val : values) {
if ((remaining_value & val.first) != val.first)
continue;
found_enumerator = true;
remaining_value &= ~val.first;
s.PutCString(val.second);
if (always_show_value)
s.Printf(" (0x%" PRIx64 ")", val.first);
if (remaining_value)
s.PutCString(" | ");
}

// If there is a remainder that is not covered by the value, print it as hex.
if (remaining_value)
// If we found no matching values but were asked to always print a value,
// print it as hex.
if (remaining_value || (!found_enumerator && always_show_value))
s.Printf("0x%" PRIx64, remaining_value);

return true;
Expand All @@ -8666,8 +8676,9 @@ bool TypeSystemClang::DumpTypeValue(

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);
return DumpTypeValue(qual_type.getAsOpaquePtr(), s, format, data,
byte_offset, byte_size, bitfield_bit_size,
bitfield_bit_offset, exe_scope);
}

switch (type_class) {
Expand Down Expand Up @@ -8699,10 +8710,12 @@ bool TypeSystemClang::DumpTypeValue(
case clang::Type::Enum:
// 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) &&
if ((format == eFormatEnum || format == eFormatEnumWithValues ||
format == eFormatDefault) &&
GetCompleteType(type))
return DumpEnumValue(qual_type, s, data, byte_offset, byte_size,
bitfield_bit_offset, bitfield_bit_size);
bitfield_bit_offset, bitfield_bit_size,
format == eFormatEnumWithValues);
// format was not enum, just fall through and dump the value as
// requested....
[[fallthrough]];
Expand All @@ -8722,6 +8735,7 @@ bool TypeSystemClang::DumpTypeValue(
case eFormatCString: // NULL terminated C strings
case eFormatDecimal:
case eFormatEnum:
case eFormatEnumWithValues:
case eFormatHex:
case eFormatHexUppercase:
case eFormatFloat:
Expand Down
20 changes: 20 additions & 0 deletions lldb/test/API/lang/c/enum_types/TestEnumTypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,28 @@ def test_command_line(self):
)

self.expect("fr var a", DATA_TYPES_DISPLAYED_CORRECTLY, patterns=[" = A$"])
self.expect(
'fr var --format "enumeration with values" -- a',
DATA_TYPES_DISPLAYED_CORRECTLY,
patterns=[" = A \(1\)$"],
)
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 --format "enumeration with values" -- ac',
DATA_TYPES_DISPLAYED_CORRECTLY,
patterns=[" = A \(0x1\) | C \(0x4\)$"],
)
self.expect("fr var all", DATA_TYPES_DISPLAYED_CORRECTLY, patterns=[" = ALL$"])
self.expect(
'fr var --format "enumeration with values" -- all',
DATA_TYPES_DISPLAYED_CORRECTLY,
patterns=[" = ALL \(7\)$"],
)
# Test that an enum that doesn't match the heuristic we use in
# TypeSystemClang::DumpEnumValue, gets printed as a raw integer.
self.expect("fr var omega", DATA_TYPES_DISPLAYED_CORRECTLY, patterns=[" = 7$"])
Expand All @@ -41,6 +56,11 @@ def test_command_line(self):
DATA_TYPES_DISPLAYED_CORRECTLY,
patterns=[" = B \| C \| 0x10$"],
)
self.expect(
'expression --format "enumeration with values" -- (enum bitfield)nonsense',
DATA_TYPES_DISPLAYED_CORRECTLY,
patterns=[" = B \(0x2\) \| C \(0x4\) \| 0x10$"],
)

# Break inside the main.
bkpt_id = lldbutil.run_break_set_by_file_and_line(
Expand Down
1 change: 1 addition & 0 deletions lldb/unittests/Core/DumpDataExtractorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ TEST_F(DumpDataExtractorTest, Formats) {
TestDump<uint16_t>(99, lldb::Format::eFormatDecimal, "99");
// Just prints as a signed integer.
TestDump(-1, lldb::Format::eFormatEnum, "-1");
TestDump(-1, lldb::Format::eFormatEnumWithValues, "-1");
TestDump(0xcafef00d, lldb::Format::eFormatHex, "0xcafef00d");
TestDump(0xcafef00d, lldb::Format::eFormatHexUppercase, "0xCAFEF00D");
TestDump(0.456, lldb::Format::eFormatFloat, "0.45600000000000002");
Expand Down
16 changes: 16 additions & 0 deletions lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,12 @@ TEST_F(ValueObjectMockProcessTest, Enum) {
TestDumpValueObject(
MakeEnumType({{"test_2", 2}, {"test_3", 3}}),
{{0, {}, "(TestEnum) test_var = 0\n"},
{0, DumpValueObjectOptions().SetFormat(eFormatEnumWithValues),
"(TestEnum) test_var = 0\n"},
{1, {}, "(TestEnum) test_var = 1\n"},
{2, {}, "(TestEnum) test_var = test_2\n"},
{2, DumpValueObjectOptions().SetFormat(eFormatEnumWithValues),
"(TestEnum) test_var = test_2 (2)\n"},
{3, {}, "(TestEnum) test_var = test_3\n"},
{4, {}, "(TestEnum) test_var = 4\n"},
{5, {}, "(TestEnum) test_var = 5\n"},
Expand All @@ -141,6 +145,10 @@ TEST_F(ValueObjectMockProcessTest, Enum) {
{1, DumpValueObjectOptions().SetHideName(true), "(TestEnum) 1\n"},
{1, DumpValueObjectOptions().SetHideValue(true),
"(TestEnum) test_var =\n"},
{1,
DumpValueObjectOptions().SetHideValue(true).SetFormat(
eFormatEnumWithValues),
"(TestEnum) test_var =\n"},
{1, DumpValueObjectOptions().SetHideName(true).SetHideValue(true),
"(TestEnum) \n"}});
}
Expand All @@ -154,11 +162,19 @@ TEST_F(ValueObjectMockProcessTest, BitFieldLikeEnum) {
MakeEnumType({{"test_2", 2}, {"test_4", 4}}),
{
{0, {}, "(TestEnum) test_var =\n"},
{0, DumpValueObjectOptions().SetFormat(eFormatEnumWithValues),
"(TestEnum) test_var = 0x0\n"},
{1, {}, "(TestEnum) test_var = 0x1\n"},
{2, {}, "(TestEnum) test_var = test_2\n"},
{2, DumpValueObjectOptions().SetFormat(eFormatEnumWithValues),
"(TestEnum) test_var = test_2 (2)\n"},
{4, {}, "(TestEnum) test_var = test_4\n"},
{6, {}, "(TestEnum) test_var = test_2 | test_4\n"},
{6, DumpValueObjectOptions().SetFormat(eFormatEnumWithValues),
"(TestEnum) test_var = test_2 (0x2) | test_4 (0x4)\n"},
{7, {}, "(TestEnum) test_var = test_2 | test_4 | 0x1\n"},
{7, DumpValueObjectOptions().SetFormat(eFormatEnumWithValues),
"(TestEnum) test_var = test_2 (0x2) | test_4 (0x4) | 0x1\n"},
{8, {}, "(TestEnum) test_var = 0x8\n"},
{1, DumpValueObjectOptions().SetHideRootName(true),
"(TestEnum) 0x1\n"},
Expand Down

0 comments on commit 6344ce3

Please sign in to comment.