Skip to content

Commit 6344ce3

Browse files
committed
[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.
1 parent 7cdd53d commit 6344ce3

File tree

10 files changed

+73
-12
lines changed

10 files changed

+73
-12
lines changed

lldb/include/lldb/lldb-enumerations.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,15 @@ enum Format {
195195
///< character arrays that can contain non printable
196196
///< characters
197197
eFormatAddressInfo, ///< Describe what an address points to (func + offset
198-
///< with file/line, symbol + offset, data, etc)
199-
eFormatHexFloat, ///< ISO C99 hex float string
200-
eFormatInstruction, ///< Disassemble an opcode
201-
eFormatVoid, ///< Do not print this
198+
///< with file/line, symbol + offset, data, etc)
199+
eFormatHexFloat, ///< ISO C99 hex float string
200+
eFormatInstruction, ///< Disassemble an opcode
201+
eFormatVoid, ///< Do not print this
202202
eFormatUnicode8,
203+
eFormatEnumWithValues, ///< Format as an enum but if the value matches one or
204+
///< more enumerators, print the enumerator name and
205+
///< value of those enumerators. For example "foo (1)"
206+
///< instead of "foo".
203207
kNumFormats
204208
};
205209

lldb/source/Commands/CommandObjectMemory.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,6 +1387,7 @@ class CommandObjectMemoryWrite : public CommandObjectParsed {
13871387
case eFormatBytesWithASCII:
13881388
case eFormatComplex:
13891389
case eFormatEnum:
1390+
case eFormatEnumWithValues:
13901391
case eFormatUnicode8:
13911392
case eFormatUnicode16:
13921393
case eFormatUnicode32:

lldb/source/Core/DumpDataExtractor.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ lldb::offset_t lldb_private::DumpDataExtractor(
496496

497497
case eFormatEnum: // Print enum value as a signed integer when we don't get
498498
// the enum type
499+
case eFormatEnumWithValues:
499500
case eFormatDecimal:
500501
if (item_byte_size <= 8)
501502
s->Printf("%" PRId64,

lldb/source/Core/ValueObject.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1189,7 +1189,8 @@ bool ValueObject::DumpPrintableRepresentation(
11891189
return !error.Fail();
11901190
}
11911191

1192-
if (custom_format == eFormatEnum)
1192+
if (custom_format == eFormatEnum ||
1193+
custom_format == eFormatEnumWithValues)
11931194
return false;
11941195

11951196
// this only works for arrays, because I have no way to know when the

lldb/source/DataFormatters/FormatManager.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ static constexpr FormatInfo g_format_infos[] = {
7171
{eFormatInstruction, 'i', "instruction"},
7272
{eFormatVoid, 'v', "void"},
7373
{eFormatUnicode8, 'u', "unicode8"},
74+
{eFormatEnumWithValues, '\0', "enumeration with values"},
7475
};
7576

7677
static_assert((sizeof(g_format_infos) / sizeof(g_format_infos[0])) ==

lldb/source/DataFormatters/VectorType.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ static CompilerType GetCompilerTypeForFormat(lldb::Format format,
115115
case lldb::eFormatComplexInteger:
116116
case lldb::eFormatDecimal:
117117
case lldb::eFormatEnum:
118+
case lldb::eFormatEnumWithValues:
118119
case lldb::eFormatInstruction:
119120
case lldb::eFormatOSType:
120121
case lldb::eFormatVoid:
@@ -150,6 +151,7 @@ static lldb::Format GetItemFormatForFormat(lldb::Format format,
150151
case lldb::eFormatComplexInteger:
151152
case lldb::eFormatDecimal:
152153
case lldb::eFormatEnum:
154+
case lldb::eFormatEnumWithValues:
153155
case lldb::eFormatInstruction:
154156
case lldb::eFormatOSType:
155157
case lldb::eFormatVoid:

lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8572,7 +8572,7 @@ void TypeSystemClang::DumpFromSymbolFile(Stream &s,
85728572
static bool DumpEnumValue(const clang::QualType &qual_type, Stream &s,
85738573
const DataExtractor &data, lldb::offset_t byte_offset,
85748574
size_t byte_size, uint32_t bitfield_bit_offset,
8575-
uint32_t bitfield_bit_size) {
8575+
uint32_t bitfield_bit_size, bool always_show_value) {
85768576
const clang::EnumType *enutype =
85778577
llvm::cast<clang::EnumType>(qual_type.getTypePtr());
85788578
const clang::EnumDecl *enum_decl = enutype->getDecl();
@@ -8599,7 +8599,11 @@ static bool DumpEnumValue(const clang::QualType &qual_type, Stream &s,
85998599
++num_enumerators;
86008600
if (val == enum_svalue) {
86018601
// Found an exact match, that's all we need to do.
8602-
s.PutCString(enumerator->getNameAsString());
8602+
if (always_show_value)
8603+
s.Printf("%s (%" PRIi64 ")", enumerator->getNameAsString().c_str(),
8604+
enum_svalue);
8605+
else
8606+
s.PutCString(enumerator->getNameAsString());
86038607
return true;
86048608
}
86058609
}
@@ -8634,17 +8638,23 @@ static bool DumpEnumValue(const clang::QualType &qual_type, Stream &s,
86348638
return llvm::popcount(a.first) > llvm::popcount(b.first);
86358639
});
86368640

8641+
bool found_enumerator = false;
86378642
for (const auto &val : values) {
86388643
if ((remaining_value & val.first) != val.first)
86398644
continue;
8645+
found_enumerator = true;
86408646
remaining_value &= ~val.first;
86418647
s.PutCString(val.second);
8648+
if (always_show_value)
8649+
s.Printf(" (0x%" PRIx64 ")", val.first);
86428650
if (remaining_value)
86438651
s.PutCString(" | ");
86448652
}
86458653

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

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

86678677
if (type_class == clang::Type::Elaborated) {
86688678
qual_type = llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType();
8669-
return DumpTypeValue(qual_type.getAsOpaquePtr(), s, format, data, byte_offset, byte_size,
8670-
bitfield_bit_size, bitfield_bit_offset, exe_scope);
8679+
return DumpTypeValue(qual_type.getAsOpaquePtr(), s, format, data,
8680+
byte_offset, byte_size, bitfield_bit_size,
8681+
bitfield_bit_offset, exe_scope);
86718682
}
86728683

86738684
switch (type_class) {
@@ -8699,10 +8710,12 @@ bool TypeSystemClang::DumpTypeValue(
86998710
case clang::Type::Enum:
87008711
// If our format is enum or default, show the enumeration value as its
87018712
// enumeration string value, else just display it as requested.
8702-
if ((format == eFormatEnum || format == eFormatDefault) &&
8713+
if ((format == eFormatEnum || format == eFormatEnumWithValues ||
8714+
format == eFormatDefault) &&
87038715
GetCompleteType(type))
87048716
return DumpEnumValue(qual_type, s, data, byte_offset, byte_size,
8705-
bitfield_bit_offset, bitfield_bit_size);
8717+
bitfield_bit_offset, bitfield_bit_size,
8718+
format == eFormatEnumWithValues);
87068719
// format was not enum, just fall through and dump the value as
87078720
// requested....
87088721
[[fallthrough]];
@@ -8722,6 +8735,7 @@ bool TypeSystemClang::DumpTypeValue(
87228735
case eFormatCString: // NULL terminated C strings
87238736
case eFormatDecimal:
87248737
case eFormatEnum:
8738+
case eFormatEnumWithValues:
87258739
case eFormatHex:
87268740
case eFormatHexUppercase:
87278741
case eFormatFloat:

lldb/test/API/lang/c/enum_types/TestEnumTypes.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,28 @@ def test_command_line(self):
2323
)
2424

2525
self.expect("fr var a", DATA_TYPES_DISPLAYED_CORRECTLY, patterns=[" = A$"])
26+
self.expect(
27+
'fr var --format "enumeration with values" -- a',
28+
DATA_TYPES_DISPLAYED_CORRECTLY,
29+
patterns=[" = A \(1\)$"],
30+
)
2631
self.expect("fr var b", DATA_TYPES_DISPLAYED_CORRECTLY, patterns=[" = B$"])
2732
self.expect("fr var c", DATA_TYPES_DISPLAYED_CORRECTLY, patterns=[" = C$"])
2833
self.expect("fr var ab", DATA_TYPES_DISPLAYED_CORRECTLY, patterns=[" = AB$"])
2934
self.expect(
3035
"fr var ac", DATA_TYPES_DISPLAYED_CORRECTLY, patterns=[" = A \| C$"]
3136
)
37+
self.expect(
38+
'fr var --format "enumeration with values" -- ac',
39+
DATA_TYPES_DISPLAYED_CORRECTLY,
40+
patterns=[" = A \(0x1\) | C \(0x4\)$"],
41+
)
3242
self.expect("fr var all", DATA_TYPES_DISPLAYED_CORRECTLY, patterns=[" = ALL$"])
43+
self.expect(
44+
'fr var --format "enumeration with values" -- all',
45+
DATA_TYPES_DISPLAYED_CORRECTLY,
46+
patterns=[" = ALL \(7\)$"],
47+
)
3348
# Test that an enum that doesn't match the heuristic we use in
3449
# TypeSystemClang::DumpEnumValue, gets printed as a raw integer.
3550
self.expect("fr var omega", DATA_TYPES_DISPLAYED_CORRECTLY, patterns=[" = 7$"])
@@ -41,6 +56,11 @@ def test_command_line(self):
4156
DATA_TYPES_DISPLAYED_CORRECTLY,
4257
patterns=[" = B \| C \| 0x10$"],
4358
)
59+
self.expect(
60+
'expression --format "enumeration with values" -- (enum bitfield)nonsense',
61+
DATA_TYPES_DISPLAYED_CORRECTLY,
62+
patterns=[" = B \(0x2\) \| C \(0x4\) \| 0x10$"],
63+
)
4464

4565
# Break inside the main.
4666
bkpt_id = lldbutil.run_break_set_by_file_and_line(

lldb/unittests/Core/DumpDataExtractorTest.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ TEST_F(DumpDataExtractorTest, Formats) {
160160
TestDump<uint16_t>(99, lldb::Format::eFormatDecimal, "99");
161161
// Just prints as a signed integer.
162162
TestDump(-1, lldb::Format::eFormatEnum, "-1");
163+
TestDump(-1, lldb::Format::eFormatEnumWithValues, "-1");
163164
TestDump(0xcafef00d, lldb::Format::eFormatHex, "0xcafef00d");
164165
TestDump(0xcafef00d, lldb::Format::eFormatHexUppercase, "0xCAFEF00D");
165166
TestDump(0.456, lldb::Format::eFormatFloat, "0.45600000000000002");

lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,12 @@ TEST_F(ValueObjectMockProcessTest, Enum) {
129129
TestDumpValueObject(
130130
MakeEnumType({{"test_2", 2}, {"test_3", 3}}),
131131
{{0, {}, "(TestEnum) test_var = 0\n"},
132+
{0, DumpValueObjectOptions().SetFormat(eFormatEnumWithValues),
133+
"(TestEnum) test_var = 0\n"},
132134
{1, {}, "(TestEnum) test_var = 1\n"},
133135
{2, {}, "(TestEnum) test_var = test_2\n"},
136+
{2, DumpValueObjectOptions().SetFormat(eFormatEnumWithValues),
137+
"(TestEnum) test_var = test_2 (2)\n"},
134138
{3, {}, "(TestEnum) test_var = test_3\n"},
135139
{4, {}, "(TestEnum) test_var = 4\n"},
136140
{5, {}, "(TestEnum) test_var = 5\n"},
@@ -141,6 +145,10 @@ TEST_F(ValueObjectMockProcessTest, Enum) {
141145
{1, DumpValueObjectOptions().SetHideName(true), "(TestEnum) 1\n"},
142146
{1, DumpValueObjectOptions().SetHideValue(true),
143147
"(TestEnum) test_var =\n"},
148+
{1,
149+
DumpValueObjectOptions().SetHideValue(true).SetFormat(
150+
eFormatEnumWithValues),
151+
"(TestEnum) test_var =\n"},
144152
{1, DumpValueObjectOptions().SetHideName(true).SetHideValue(true),
145153
"(TestEnum) \n"}});
146154
}
@@ -154,11 +162,19 @@ TEST_F(ValueObjectMockProcessTest, BitFieldLikeEnum) {
154162
MakeEnumType({{"test_2", 2}, {"test_4", 4}}),
155163
{
156164
{0, {}, "(TestEnum) test_var =\n"},
165+
{0, DumpValueObjectOptions().SetFormat(eFormatEnumWithValues),
166+
"(TestEnum) test_var = 0x0\n"},
157167
{1, {}, "(TestEnum) test_var = 0x1\n"},
158168
{2, {}, "(TestEnum) test_var = test_2\n"},
169+
{2, DumpValueObjectOptions().SetFormat(eFormatEnumWithValues),
170+
"(TestEnum) test_var = test_2 (2)\n"},
159171
{4, {}, "(TestEnum) test_var = test_4\n"},
160172
{6, {}, "(TestEnum) test_var = test_2 | test_4\n"},
173+
{6, DumpValueObjectOptions().SetFormat(eFormatEnumWithValues),
174+
"(TestEnum) test_var = test_2 (0x2) | test_4 (0x4)\n"},
161175
{7, {}, "(TestEnum) test_var = test_2 | test_4 | 0x1\n"},
176+
{7, DumpValueObjectOptions().SetFormat(eFormatEnumWithValues),
177+
"(TestEnum) test_var = test_2 (0x2) | test_4 (0x4) | 0x1\n"},
162178
{8, {}, "(TestEnum) test_var = 0x8\n"},
163179
{1, DumpValueObjectOptions().SetHideRootName(true),
164180
"(TestEnum) 0x1\n"},

0 commit comments

Comments
 (0)