Skip to content

Commit 763b96c

Browse files
authored
[lldb] Avoid (unlimited) GetNumChildren calls when printing values (#93946)
For some data formatters, even getting the number of children can be an expensive operations (e.g., needing to walk a linked list to determine the number of elements). This is then wasted work when we know we will be printing only small number of them. This patch replaces the calls to GetNumChildren (at least those on the "frame var" path) with the calls to the capped version, passing the value of `max-children-count` setting (plus one)
1 parent ce73e17 commit 763b96c

File tree

4 files changed

+46
-17
lines changed

4 files changed

+46
-17
lines changed

lldb/source/DataFormatters/FormatManager.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "lldb/DataFormatters/FormatManager.h"
1010

1111
#include "lldb/Core/Debugger.h"
12+
#include "lldb/Core/ValueObject.h"
1213
#include "lldb/DataFormatters/FormattersHelpers.h"
1314
#include "lldb/DataFormatters/LanguageCategory.h"
1415
#include "lldb/Interpreter/ScriptInterpreter.h"
@@ -448,16 +449,19 @@ lldb::Format FormatManager::GetSingleItemFormat(lldb::Format vector_format) {
448449
}
449450

450451
bool FormatManager::ShouldPrintAsOneLiner(ValueObject &valobj) {
452+
TargetSP target_sp = valobj.GetTargetSP();
451453
// if settings say no oneline whatsoever
452-
if (valobj.GetTargetSP().get() &&
453-
!valobj.GetTargetSP()->GetDebugger().GetAutoOneLineSummaries())
454+
if (target_sp && !target_sp->GetDebugger().GetAutoOneLineSummaries())
454455
return false; // then don't oneline
455456

456457
// if this object has a summary, then ask the summary
457458
if (valobj.GetSummaryFormat().get() != nullptr)
458459
return valobj.GetSummaryFormat()->IsOneLiner();
459460

460-
auto num_children = valobj.GetNumChildren();
461+
const size_t max_num_children =
462+
(target_sp ? *target_sp : Target::GetGlobalProperties())
463+
.GetMaximumNumberOfChildrenToDisplay();
464+
auto num_children = valobj.GetNumChildren(max_num_children);
461465
if (!num_children) {
462466
llvm::consumeError(num_children.takeError());
463467
return true;

lldb/source/DataFormatters/ValueObjectPrinter.cpp

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include "lldb/Target/Language.h"
1515
#include "lldb/Target/Target.h"
1616
#include "lldb/Utility/Stream.h"
17+
#include "llvm/Support/MathExtras.h"
18+
#include <cstdint>
1719

1820
using namespace lldb;
1921
using namespace lldb_private;
@@ -628,22 +630,21 @@ ValueObjectPrinter::GetMaxNumChildrenToPrint(bool &print_dotdotdot) {
628630
if (m_options.m_pointer_as_array)
629631
return m_options.m_pointer_as_array.m_element_count;
630632

631-
auto num_children_or_err = synth_valobj.GetNumChildren();
633+
const uint32_t max_num_children =
634+
m_options.m_ignore_cap ? UINT32_MAX
635+
: GetMostSpecializedValue()
636+
.GetTargetSP()
637+
->GetMaximumNumberOfChildrenToDisplay();
638+
// Ask for one more child than the maximum to see if we should print "...".
639+
auto num_children_or_err = synth_valobj.GetNumChildren(
640+
llvm::SaturatingAdd(max_num_children, uint32_t(1)));
632641
if (!num_children_or_err)
633642
return num_children_or_err;
634-
uint32_t num_children = *num_children_or_err;
635-
print_dotdotdot = false;
636-
if (num_children) {
637-
const size_t max_num_children = GetMostSpecializedValue()
638-
.GetTargetSP()
639-
->GetMaximumNumberOfChildrenToDisplay();
640-
641-
if (num_children > max_num_children && !m_options.m_ignore_cap) {
642-
print_dotdotdot = true;
643-
return max_num_children;
644-
}
643+
if (*num_children_or_err > max_num_children) {
644+
print_dotdotdot = true;
645+
return max_num_children;
645646
}
646-
return num_children;
647+
return num_children_or_err;
647648
}
648649

649650
void ValueObjectPrinter::PrintChildrenPostamble(bool print_dotdotdot) {

lldb/test/API/functionalities/data-formatter/synthcapping/TestSyntheticCapping.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ def cleanup():
6868
"r = 34",
6969
],
7070
)
71+
# num_children() should be called with at most max_num_children=257
72+
# (target.max-children-count + 1)
73+
self.expect(
74+
"script fooSynthProvider.reset_max_num_children_max()", substrs=["257"]
75+
)
7176

7277
# check that capping works
7378
self.runCmd("settings set target.max-children-count 2", check=False)
@@ -80,9 +85,15 @@ def cleanup():
8085
"...",
8186
],
8287
)
88+
self.expect(
89+
"script fooSynthProvider.reset_max_num_children_max()", substrs=["3"]
90+
)
8391

8492
self.expect("frame variable f00_1", matching=False, substrs=["r = 34"])
8593

8694
self.runCmd("settings set target.max-children-count 256", check=False)
8795

8896
self.expect("frame variable f00_1", matching=True, substrs=["r = 34"])
97+
self.expect(
98+
"script fooSynthProvider.reset_max_num_children_max()", substrs=["257"]
99+
)

lldb/test/API/functionalities/data-formatter/synthcapping/fooSynthProvider.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,24 @@
22

33

44
class fooSynthProvider:
5+
# For testing purposes, we'll keep track of the maximum value of
6+
# max_num_children we've been called with.
7+
MAX_NUM_CHILDREN_MAX = 0
8+
9+
@classmethod
10+
def reset_max_num_children_max(cls):
11+
old_value = fooSynthProvider.MAX_NUM_CHILDREN_MAX
12+
fooSynthProvider.MAX_NUM_CHILDREN_MAX = 0
13+
return old_value
14+
515
def __init__(self, valobj, dict):
616
self.valobj = valobj
717
self.int_type = valobj.GetType().GetBasicType(lldb.eBasicTypeInt)
818

9-
def num_children(self):
19+
def num_children(self, max_num_children):
20+
fooSynthProvider.MAX_NUM_CHILDREN_MAX = max(
21+
fooSynthProvider.MAX_NUM_CHILDREN_MAX, max_num_children
22+
)
1023
return 3
1124

1225
def get_child_at_index(self, index):

0 commit comments

Comments
 (0)