[Lldb-commits] [lldb] [lldb] Add ability to show enum as name and value at the same time (PR #90059)
David Spickett via lldb-commits
lldb-commits at lists.llvm.org
Tue May 28 02:18:22 PDT 2024
https://github.com/DavidSpickett updated https://github.com/llvm/llvm-project/pull/90059
>From 6344ce3feff235f6a3b1bc1d5eb1e50e5695d320 Mon Sep 17 00:00:00 2001
From: David Spickett <david.spickett at linaro.org>
Date: Mon, 11 Mar 2024 10:18:51 +0000
Subject: [PATCH] [lldb] Add format eFormatEnumWithValues to ensure raw enum
value is always 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.
---
lldb/include/lldb/lldb-enumerations.h | 12 +++++---
lldb/source/Commands/CommandObjectMemory.cpp | 1 +
lldb/source/Core/DumpDataExtractor.cpp | 1 +
lldb/source/Core/ValueObject.cpp | 3 +-
lldb/source/DataFormatters/FormatManager.cpp | 1 +
lldb/source/DataFormatters/VectorType.cpp | 2 ++
.../TypeSystem/Clang/TypeSystemClang.cpp | 28 ++++++++++++++-----
.../API/lang/c/enum_types/TestEnumTypes.py | 20 +++++++++++++
lldb/unittests/Core/DumpDataExtractorTest.cpp | 1 +
.../DumpValueObjectOptionsTests.cpp | 16 +++++++++++
10 files changed, 73 insertions(+), 12 deletions(-)
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index 8e05f6ba9c876..99b2ec820e608 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -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
};
diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp
index b78a0492cca55..7748aa5b7f083 100644
--- a/lldb/source/Commands/CommandObjectMemory.cpp
+++ b/lldb/source/Commands/CommandObjectMemory.cpp
@@ -1387,6 +1387,7 @@ class CommandObjectMemoryWrite : public CommandObjectParsed {
case eFormatBytesWithASCII:
case eFormatComplex:
case eFormatEnum:
+ case eFormatEnumWithValues:
case eFormatUnicode8:
case eFormatUnicode16:
case eFormatUnicode32:
diff --git a/lldb/source/Core/DumpDataExtractor.cpp b/lldb/source/Core/DumpDataExtractor.cpp
index 826edd7bab046..e3fe2343b240d 100644
--- a/lldb/source/Core/DumpDataExtractor.cpp
+++ b/lldb/source/Core/DumpDataExtractor.cpp
@@ -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,
diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp
index 1443d9dfc3280..d816f33b005ec 100644
--- a/lldb/source/Core/ValueObject.cpp
+++ b/lldb/source/Core/ValueObject.cpp
@@ -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
diff --git a/lldb/source/DataFormatters/FormatManager.cpp b/lldb/source/DataFormatters/FormatManager.cpp
index d7ba5b4b70c94..24ee2becabd3e 100644
--- a/lldb/source/DataFormatters/FormatManager.cpp
+++ b/lldb/source/DataFormatters/FormatManager.cpp
@@ -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])) ==
diff --git a/lldb/source/DataFormatters/VectorType.cpp b/lldb/source/DataFormatters/VectorType.cpp
index 19de204c24353..5a79574fb3563 100644
--- a/lldb/source/DataFormatters/VectorType.cpp
+++ b/lldb/source/DataFormatters/VectorType.cpp
@@ -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:
@@ -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:
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 369ae46cf264a..58d08e9c257f0 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -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();
@@ -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;
}
}
@@ -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;
@@ -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) {
@@ -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]];
@@ -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:
diff --git a/lldb/test/API/lang/c/enum_types/TestEnumTypes.py b/lldb/test/API/lang/c/enum_types/TestEnumTypes.py
index 0015c8f478578..26c3e8f2b54d2 100644
--- a/lldb/test/API/lang/c/enum_types/TestEnumTypes.py
+++ b/lldb/test/API/lang/c/enum_types/TestEnumTypes.py
@@ -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$"])
@@ -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(
diff --git a/lldb/unittests/Core/DumpDataExtractorTest.cpp b/lldb/unittests/Core/DumpDataExtractorTest.cpp
index 3d1e8bc5e4623..2cb09f5011c55 100644
--- a/lldb/unittests/Core/DumpDataExtractorTest.cpp
+++ b/lldb/unittests/Core/DumpDataExtractorTest.cpp
@@ -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");
diff --git a/lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp b/lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp
index 31068a04d8dfe..b4101a0a41a9e 100644
--- a/lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp
+++ b/lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp
@@ -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"},
@@ -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"}});
}
@@ -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"},
More information about the lldb-commits
mailing list