Skip to content

Commit c2b01c8

Browse files
authored
Make ValueObjectPrinter's handling of its ValueObject pointers more principled (NFC) (llvm#81314)
I get a small but fairly steady stream of crash reports which I can only explain by ValueObjectPrinter trying to access its m_valobj field, and finding it NULL. I have never been able to reproduce any of these, and the reports show a state too long after the fact to know what went wrong. I've read through this section of lldb a bunch of times trying to figure out how this could happen, but haven't ever found anything actually wrong that could cause this. OTOH, ValueObjectPrinter is somewhat sloppy about how it handles the ValueObject it is printing. a) lldb allows you to make a ValueObjectPrinter with a Null incoming ValueObject. However, there's no affordance to set the ValueObject in the Printer after the fact, and it doesn't really make sense to do that. So I change the ValueObjectPrinter API's to take a ValueObject reference, rather than a pointer. All the places that make ValueObjectPrinters already check the non-null status of their ValueObject's before making the ValueObjectPrinter, so sadly, I didn't find the bug, but this will enforce the intent. b) The next step in printing the ValueObject is deciding which of the associated DynamicValue/SyntheticValue we are actually printing (based on the use_dynamic and use_synthetic settings in the original ValueObject. This was put in a pointer by GetMostSpecializedValue, but most of the printer code just accessed the pointer, and it was hard to reason out whether we were guaranteed to always call this before using m_valobj. So far as I could see we always do (sigh, didn't find the bug there either) but this was way too hard to reason about. In fact, we figure out once which ValueObject we're going to print and don't change that through the life of the printer. So I changed this to both set the "most specialized value" in the constructor, and then to always access it through GetMostSpecializedValue(). That makes it easier to reason about the use of this ValueObject as well. This is an NFC change, all it does is make the code easier to reason about.
1 parent 2dbfa84 commit c2b01c8

File tree

5 files changed

+153
-122
lines changed

5 files changed

+153
-122
lines changed

lldb/include/lldb/DataFormatters/ValueObjectPrinter.h

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,15 @@
2121
namespace lldb_private {
2222

2323
class ValueObjectPrinter {
24+
/// The ValueObjectPrinter is a one-shot printer for ValueObjects. It
25+
/// does not retain the ValueObject it is printing, that is the job of
26+
/// its caller. It also doesn't attempt to track changes in the
27+
/// ValueObject, e.g. changing synthetic child providers or changing
28+
/// dynamic vrs. static vrs. synthetic settings.
2429
public:
25-
ValueObjectPrinter(ValueObject *valobj, Stream *s);
30+
ValueObjectPrinter(ValueObject &valobj, Stream *s);
2631

27-
ValueObjectPrinter(ValueObject *valobj, Stream *s,
32+
ValueObjectPrinter(ValueObject &valobj, Stream *s,
2833
const DumpValueObjectOptions &options);
2934

3035
~ValueObjectPrinter() = default;
@@ -39,21 +44,37 @@ class ValueObjectPrinter {
3944

4045
// only this class (and subclasses, if any) should ever be concerned with the
4146
// depth mechanism
42-
ValueObjectPrinter(ValueObject *valobj, Stream *s,
47+
ValueObjectPrinter(ValueObject &valobj, Stream *s,
4348
const DumpValueObjectOptions &options,
4449
const DumpValueObjectOptions::PointerDepth &ptr_depth,
4550
uint32_t curr_depth,
4651
InstancePointersSetSP printed_instance_pointers);
4752

4853
// we should actually be using delegating constructors here but some versions
4954
// of GCC still have trouble with those
50-
void Init(ValueObject *valobj, Stream *s,
55+
void Init(ValueObject &valobj, Stream *s,
5156
const DumpValueObjectOptions &options,
5257
const DumpValueObjectOptions::PointerDepth &ptr_depth,
5358
uint32_t curr_depth,
5459
InstancePointersSetSP printed_instance_pointers);
5560

56-
bool GetMostSpecializedValue();
61+
/// Cache the ValueObject we are actually going to print. If this
62+
/// ValueObject has a Dynamic type, we return that, if either the original
63+
/// ValueObject or its Dynamic type has a Synthetic provider, return that.
64+
/// This will never return an empty ValueObject, since we use the ValueObject
65+
/// to carry errors.
66+
/// Note, this gets called when making the printer object, and uses the
67+
/// use dynamic and use synthetic settings of the ValueObject being printed,
68+
/// so changes made to these settings won't affect already made
69+
/// ValueObjectPrinters. SetupMostSpecializedValue();
70+
71+
/// Access the cached "most specialized value" - that is the one to use for
72+
/// printing the value object's value. However, be sure to use
73+
/// GetValueForChildGeneration when you are generating the children of this
74+
/// value.
75+
ValueObject &GetMostSpecializedValue();
76+
77+
void SetupMostSpecializedValue();
5778

5879
const char *GetDescriptionForDisplay();
5980

@@ -95,13 +116,13 @@ class ValueObjectPrinter {
95116

96117
bool ShouldExpandEmptyAggregates();
97118

98-
ValueObject *GetValueObjectForChildrenGeneration();
119+
ValueObject &GetValueObjectForChildrenGeneration();
99120

100121
void PrintChildrenPreamble(bool value_printed, bool summary_printed);
101122

102123
void PrintChildrenPostamble(bool print_dotdotdot);
103124

104-
lldb::ValueObjectSP GenerateChild(ValueObject *synth_valobj, size_t idx);
125+
lldb::ValueObjectSP GenerateChild(ValueObject &synth_valobj, size_t idx);
105126

106127
void PrintChild(lldb::ValueObjectSP child_sp,
107128
const DumpValueObjectOptions::PointerDepth &curr_ptr_depth);
@@ -121,8 +142,10 @@ class ValueObjectPrinter {
121142
private:
122143
bool ShouldShowName() const;
123144

124-
ValueObject *m_orig_valobj;
125-
ValueObject *m_valobj;
145+
ValueObject &m_orig_valobj;
146+
ValueObject *m_cached_valobj; /// Cache the current "most specialized" value.
147+
/// Don't use this directly, use
148+
/// GetMostSpecializedValue.
126149
Stream *m_stream;
127150
DumpValueObjectOptions m_options;
128151
Flags m_type_flags;

lldb/source/Commands/CommandObjectFrame.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,10 @@ class CommandObjectFrameDiagnose : public CommandObjectParsed {
177177

178178
DumpValueObjectOptions options;
179179
options.SetDeclPrintingHelper(helper);
180-
ValueObjectPrinter printer(valobj_sp.get(), &result.GetOutputStream(),
180+
// We've already handled the case where the value object sp is null, so
181+
// this is just to make sure future changes don't skip that:
182+
assert(valobj_sp.get() && "Must have a valid ValueObject to print");
183+
ValueObjectPrinter printer(*valobj_sp, &result.GetOutputStream(),
181184
options);
182185
printer.PrintValueObject();
183186
}

lldb/source/Core/ValueObject.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2525,7 +2525,7 @@ ValueObjectSP ValueObject::GetValueForExpressionPath_Impl(
25252525
void ValueObject::Dump(Stream &s) { Dump(s, DumpValueObjectOptions(*this)); }
25262526

25272527
void ValueObject::Dump(Stream &s, const DumpValueObjectOptions &options) {
2528-
ValueObjectPrinter printer(this, &s, options);
2528+
ValueObjectPrinter printer(*this, &s, options);
25292529
printer.PrintValueObject();
25302530
}
25312531

lldb/source/DataFormatters/TypeSummary.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ bool StringSummaryFormat::FormatObject(ValueObject *valobj, std::string &retval,
8080
sc = frame->GetSymbolContext(lldb::eSymbolContextEverything);
8181

8282
if (IsOneLiner()) {
83-
ValueObjectPrinter printer(valobj, &s, DumpValueObjectOptions());
83+
// We've already checked the case of a NULL valobj above. Let's put in an
84+
// assert here to make sure someone doesn't take that out:
85+
assert(valobj && "Must have a valid ValueObject to summarize");
86+
ValueObjectPrinter printer(*valobj, &s, DumpValueObjectOptions());
8487
printer.PrintChildrenOneLiner(HideNames(valobj));
8588
retval = std::string(s.GetString());
8689
return true;

0 commit comments

Comments
 (0)