Skip to content

Commit 02916a4

Browse files
authored
[lldb][Formatters] Add --pointer-match-depth option to type summary add command. (#138209)
Currently, the type `T`'s summary formatter will be matched for `T`, `T*`, `T**` and so on. This is unexpected in many data formatters. Such unhandled cases could cause the data formatter to crash. An example would be the lldb's built-in data formatter for `std::optional`: ``` $ cat main.cpp #include <optional> int main() { std::optional<int> o_null; auto po_null = &o_null; auto ppo_null = &po_null; auto pppo_null = &ppo_null; return 0; } $ clang++ -g main.cpp && lldb -o "b 8" -o "r" -o "v pppo_null" [lldb crash] ``` This change adds an options `--pointer-match-depth` to `type summary add` command to allow users to specify how many layer of pointers can be dereferenced at most when matching a summary formatter of type `T`, as Jim suggested [here](#124048). By default, this option has value 1 which means summary formatter for `T` could also be used for `T*` but not `T**` nor beyond. This option is no-op when `--skip-pointers` is set as well. I didn't add such option for `type synthetic add`, `type format add`, `type filter add`, because it useful for those command. Instead, they all have the pointer match depth of 1. When printing a type `T*`, lldb never print the children of `T` even if there is a synthetic formatter registered for `T`.
1 parent cb7f4ff commit 02916a4

File tree

17 files changed

+292
-55
lines changed

17 files changed

+292
-55
lines changed

lldb/docs/use/variable.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,20 @@ The command to obtain the output shown in the example is:
366366
Initially, we will focus on summary strings, and then describe the Python
367367
binding mechanism.
368368

369+
Summary Format Matching On Pointers
370+
----------------------
371+
372+
A summary formatter for a type ``T`` might or might not be appropriate to use
373+
for pointers to that type. If the formatter is only appropriate for the type and
374+
not its pointers, use the ``-p`` option to restrict it to match SBValues of type
375+
``T``. If you want the formatter to also match pointers to the type, you can use
376+
the ``-d`` option to specify how many pointer layers the formatter should match.
377+
The default value is 1, so if you don't specify ``-p`` or ``-d``, your formatter
378+
will be used on SBValues of type ``T`` and ``T*``. If you want to also match
379+
``T**`` set ``-d`` to 2, etc. In all cases, the SBValue passed to the summary
380+
formatter will be the matched ValueObject. lldb doesn't dereference the matched
381+
value down to the SBValue of type ``T`` before passing it to your formatter.
382+
369383
Summary Strings
370384
---------------
371385

lldb/include/lldb/API/SBTypeSummary.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ class SBTypeSummary {
109109

110110
void SetFunctionCode(const char *data);
111111

112+
uint32_t GetPtrMatchDepth();
113+
114+
void SetPtrMatchDepth(uint32_t ptr_match_depth);
115+
112116
uint32_t GetOptions();
113117

114118
void SetOptions(uint32_t);

lldb/include/lldb/DataFormatters/FormatClasses.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,10 @@ class FormattersMatchCandidate {
7676

7777
FormattersMatchCandidate(ConstString name,
7878
ScriptInterpreter *script_interpreter, TypeImpl type,
79-
Flags flags)
79+
Flags flags, uint32_t ptr_stripped_depth = 0)
8080
: m_type_name(name), m_script_interpreter(script_interpreter),
81-
m_type(type), m_flags(flags) {}
81+
m_type(type), m_flags(flags), m_ptr_stripped_depth(ptr_stripped_depth) {
82+
}
8283

8384
~FormattersMatchCandidate() = default;
8485

@@ -96,6 +97,8 @@ class FormattersMatchCandidate {
9697

9798
bool DidStripTypedef() const { return m_flags.stripped_typedef; }
9899

100+
uint32_t GetPtrStrippedDepth() const { return m_ptr_stripped_depth; }
101+
99102
template <class Formatter>
100103
bool IsMatch(const std::shared_ptr<Formatter> &formatter_sp) const {
101104
if (!formatter_sp)
@@ -104,6 +107,8 @@ class FormattersMatchCandidate {
104107
return false;
105108
if (formatter_sp->SkipsPointers() && DidStripPointer())
106109
return false;
110+
if (formatter_sp->GetPtrMatchDepth() < GetPtrStrippedDepth())
111+
return false;
107112
if (formatter_sp->SkipsReferences() && DidStripReference())
108113
return false;
109114
return true;
@@ -116,6 +121,7 @@ class FormattersMatchCandidate {
116121
ScriptInterpreter *m_script_interpreter;
117122
TypeImpl m_type;
118123
Flags m_flags;
124+
uint32_t m_ptr_stripped_depth;
119125
};
120126

121127
typedef std::vector<FormattersMatchCandidate> FormattersMatchVector;

lldb/include/lldb/DataFormatters/FormatManager.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ class FormatManager : public IFormatChangeListener {
180180
lldb::DynamicValueType use_dynamic,
181181
FormattersMatchVector &entries,
182182
FormattersMatchCandidate::Flags current_flags,
183-
bool root_level = false);
183+
bool root_level = false,
184+
uint32_t ptr_stripped_depth = 0);
184185

185186
std::atomic<uint32_t> m_last_revision;
186187
FormatCache m_format_cache;

lldb/include/lldb/DataFormatters/FormattersContainer.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,10 @@ template <typename ValueType> class FormattersContainer {
193193
bool Get(const FormattersMatchVector &candidates, ValueSP &entry) {
194194
for (const FormattersMatchCandidate &candidate : candidates) {
195195
if (Get(candidate, entry)) {
196-
if (candidate.IsMatch(entry) == false) {
197-
entry.reset();
198-
continue;
199-
} else {
196+
if (candidate.IsMatch(entry))
200197
return true;
201-
}
198+
entry.reset();
199+
continue;
202200
}
203201
}
204202
return false;

lldb/include/lldb/DataFormatters/TypeFormat.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ class TypeFormatImpl {
133133

134134
void SetOptions(uint32_t value) { m_flags.SetValue(value); }
135135

136+
uint32_t GetPtrMatchDepth() { return m_ptr_match_depth; }
137+
138+
void SetPtrMatchDepth(uint32_t value) { m_ptr_match_depth = value; }
139+
136140
uint32_t &GetRevision() { return m_my_revision; }
137141

138142
enum class Type { eTypeUnknown, eTypeFormat, eTypeEnum };
@@ -150,6 +154,7 @@ class TypeFormatImpl {
150154
protected:
151155
Flags m_flags;
152156
uint32_t m_my_revision = 0;
157+
uint32_t m_ptr_match_depth = 1;
153158

154159
private:
155160
TypeFormatImpl(const TypeFormatImpl &) = delete;

lldb/include/lldb/DataFormatters/TypeSummary.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,10 @@ class TypeSummaryImpl {
253253

254254
void SetOptions(uint32_t value) { m_flags.SetValue(value); }
255255

256+
uint32_t GetPtrMatchDepth() { return m_ptr_match_depth; }
257+
258+
void SetPtrMatchDepth(uint32_t value) { m_ptr_match_depth = value; }
259+
256260
// we are using a ValueObject* instead of a ValueObjectSP because we do not
257261
// need to hold on to this for extended periods of time and we trust the
258262
// ValueObject to stay around for as long as it is required for us to
@@ -278,10 +282,12 @@ class TypeSummaryImpl {
278282
uint32_t m_my_revision = 0;
279283
Flags m_flags;
280284

281-
TypeSummaryImpl(Kind kind, const TypeSummaryImpl::Flags &flags);
285+
TypeSummaryImpl(Kind kind, const TypeSummaryImpl::Flags &flags,
286+
uint32_t ptr_match_depth = 1);
282287

283288
private:
284289
Kind m_kind;
290+
uint32_t m_ptr_match_depth = 1;
285291
TypeSummaryImpl(const TypeSummaryImpl &) = delete;
286292
const TypeSummaryImpl &operator=(const TypeSummaryImpl &) = delete;
287293
};
@@ -292,7 +298,8 @@ struct StringSummaryFormat : public TypeSummaryImpl {
292298
FormatEntity::Entry m_format;
293299
Status m_error;
294300

295-
StringSummaryFormat(const TypeSummaryImpl::Flags &flags, const char *f);
301+
StringSummaryFormat(const TypeSummaryImpl::Flags &flags, const char *f,
302+
uint32_t ptr_match_depth = 1);
296303

297304
~StringSummaryFormat() override = default;
298305

@@ -328,7 +335,8 @@ struct CXXFunctionSummaryFormat : public TypeSummaryImpl {
328335
std::string m_description;
329336

330337
CXXFunctionSummaryFormat(const TypeSummaryImpl::Flags &flags, Callback impl,
331-
const char *description);
338+
const char *description,
339+
uint32_t ptr_match_depth = 1);
332340

333341
~CXXFunctionSummaryFormat() override = default;
334342

@@ -373,7 +381,8 @@ struct ScriptSummaryFormat : public TypeSummaryImpl {
373381

374382
ScriptSummaryFormat(const TypeSummaryImpl::Flags &flags,
375383
const char *function_name,
376-
const char *python_script = nullptr);
384+
const char *python_script = nullptr,
385+
uint32_t ptr_match_depth = 1);
377386

378387
~ScriptSummaryFormat() override = default;
379388

lldb/include/lldb/DataFormatters/TypeSynthetic.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,14 @@ class SyntheticChildren {
273273

274274
uint32_t &GetRevision() { return m_my_revision; }
275275

276+
uint32_t GetPtrMatchDepth() { return m_ptr_match_depth; }
277+
278+
void SetPtrMatchDepth(uint32_t value) { m_ptr_match_depth = value; }
279+
276280
protected:
277281
uint32_t m_my_revision = 0;
278282
Flags m_flags;
283+
uint32_t m_ptr_match_depth = 1;
279284

280285
private:
281286
SyntheticChildren(const SyntheticChildren &) = delete;

lldb/source/API/SBTypeSummary.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,22 @@ const char *SBTypeSummary::GetData() {
230230
return nullptr;
231231
}
232232

233+
uint32_t SBTypeSummary::GetPtrMatchDepth() {
234+
LLDB_INSTRUMENT_VA(this);
235+
236+
if (!IsValid())
237+
return 0;
238+
return m_opaque_sp->GetPtrMatchDepth();
239+
}
240+
241+
void SBTypeSummary::SetPtrMatchDepth(uint32_t ptr_match_depth) {
242+
LLDB_INSTRUMENT_VA(this);
243+
244+
if (!IsValid())
245+
return;
246+
return m_opaque_sp->SetPtrMatchDepth(ptr_match_depth);
247+
}
248+
233249
uint32_t SBTypeSummary::GetOptions() {
234250
LLDB_INSTRUMENT_VA(this);
235251

lldb/source/Commands/CommandObjectType.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,13 @@ class ScriptAddOptions {
5151
FormatterMatchType m_match_type;
5252
ConstString m_name;
5353
std::string m_category;
54+
uint32_t m_ptr_match_depth;
5455

5556
ScriptAddOptions(const TypeSummaryImpl::Flags &flags,
5657
FormatterMatchType match_type, ConstString name,
57-
std::string catg)
58+
std::string catg, uint32_t m_ptr_match_depth)
5859
: m_flags(flags), m_match_type(match_type), m_name(name),
59-
m_category(catg) {}
60+
m_category(catg), m_ptr_match_depth(m_ptr_match_depth) {}
6061

6162
typedef std::shared_ptr<ScriptAddOptions> SharedPointer;
6263
};
@@ -146,6 +147,7 @@ class CommandObjectTypeSummaryAdd : public CommandObjectParsed,
146147
std::string m_python_function;
147148
bool m_is_add_script = false;
148149
std::string m_category;
150+
uint32_t m_ptr_match_depth = 1;
149151
};
150152

151153
CommandOptions m_options;
@@ -211,7 +213,7 @@ class CommandObjectTypeSummaryAdd : public CommandObjectParsed,
211213
TypeSummaryImplSP script_format;
212214
script_format = std::make_shared<ScriptSummaryFormat>(
213215
options->m_flags, funct_name_str.c_str(),
214-
lines.CopyList(" ").c_str());
216+
lines.CopyList(" ").c_str(), options->m_ptr_match_depth);
215217

216218
Status error;
217219

@@ -1178,6 +1180,13 @@ Status CommandObjectTypeSummaryAdd::CommandOptions::SetOptionValue(
11781180
case 'p':
11791181
m_flags.SetSkipPointers(true);
11801182
break;
1183+
case 'd':
1184+
if (option_arg.getAsInteger(0, m_ptr_match_depth)) {
1185+
error = Status::FromErrorStringWithFormat(
1186+
"invalid integer value for option '%c': %s", short_option,
1187+
option_arg.data());
1188+
}
1189+
break;
11811190
case 'r':
11821191
m_flags.SetSkipReferences(true);
11831192
break;
@@ -1266,7 +1275,8 @@ bool CommandObjectTypeSummaryAdd::Execute_ScriptSummary(
12661275
(" " + m_options.m_python_function + "(valobj,internal_dict)");
12671276

12681277
script_format = std::make_shared<ScriptSummaryFormat>(
1269-
m_options.m_flags, funct_name, code.c_str());
1278+
m_options.m_flags, funct_name, code.c_str(),
1279+
m_options.m_ptr_match_depth);
12701280

12711281
ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
12721282

@@ -1300,12 +1310,13 @@ bool CommandObjectTypeSummaryAdd::Execute_ScriptSummary(
13001310
std::string code = " " + m_options.m_python_script;
13011311

13021312
script_format = std::make_shared<ScriptSummaryFormat>(
1303-
m_options.m_flags, funct_name_str.c_str(), code.c_str());
1313+
m_options.m_flags, funct_name_str.c_str(), code.c_str(),
1314+
m_options.m_ptr_match_depth);
13041315
} else {
13051316
// Use an IOHandler to grab Python code from the user
13061317
auto options = std::make_unique<ScriptAddOptions>(
13071318
m_options.m_flags, m_options.m_match_type, m_options.m_name,
1308-
m_options.m_category);
1319+
m_options.m_category, m_options.m_ptr_match_depth);
13091320

13101321
for (auto &entry : command.entries()) {
13111322
if (entry.ref().empty()) {
@@ -1380,8 +1391,8 @@ bool CommandObjectTypeSummaryAdd::Execute_StringSummary(
13801391
return false;
13811392
}
13821393

1383-
std::unique_ptr<StringSummaryFormat> string_format(
1384-
new StringSummaryFormat(m_options.m_flags, format_cstr));
1394+
std::unique_ptr<StringSummaryFormat> string_format(new StringSummaryFormat(
1395+
m_options.m_flags, format_cstr, m_options.m_ptr_match_depth));
13851396
if (!string_format) {
13861397
result.AppendError("summary creation failed");
13871398
return false;

lldb/source/Commands/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,10 @@ let Command = "type summary add" in {
12491249
Desc<"Don't show the value, just show the summary, for this type.">;
12501250
def type_summary_add_skip_pointers : Option<"skip-pointers", "p">,
12511251
Desc<"Don't use this format for pointers-to-type objects.">;
1252+
def type_summary_add_pointer_match_depth : Option<"pointer-match-depth", "d">,
1253+
Arg<"UnsignedInteger">,
1254+
Desc<"Specify the maximum pointer depth that this format can be apply to "
1255+
"(default to 1). It's only effective when --skip-pointers is not set.">;
12521256
def type_summary_add_skip_references : Option<"skip-references", "r">,
12531257
Desc<"Don't use this format for references-to-type objects.">;
12541258
def type_summary_add_regex : Option<"regex", "x">,

0 commit comments

Comments
 (0)