Skip to content

Commit e18f4db

Browse files
committed
[LLDB] Adding caching to libc++ std::function formatter for lookups that require scanning symbols
Performance issues lead to the libc++ std::function formatter to be disabled. This change is the first of two changes that should address the performance issues and allow us to enable the formatter again. In some cases we end up scanning the symbol table for the callable wrapped by std::function for those cases we will now cache the results and used the cache in subsequent look-ups. This still leaves a large cost for the initial lookup which will be addressed in the next change. Differential Revision: https://reviews.llvm.org/D67111
1 parent d17bcf2 commit e18f4db

File tree

4 files changed

+84
-26
lines changed

4 files changed

+84
-26
lines changed

lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/TestLibCxxFunction.py

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,22 @@ class LibCxxFunctionTestCase(TestBase):
1515

1616
mydir = TestBase.compute_mydir(__file__)
1717

18-
def get_variable(self, name):
19-
var = self.frame().FindVariable(name)
20-
var.SetPreferDynamicValue(lldb.eDynamicCanRunTarget)
21-
var.SetPreferSyntheticValue(True)
22-
return var
18+
# Run frame var for a variable twice. Verify we do not hit the cache
19+
# the first time but do the second time.
20+
def run_frame_var_check_cache_use(self, variable, result_to_match):
21+
self.runCmd("log timers reset")
22+
self.expect("frame variable " + variable,
23+
substrs=[variable + " = " + result_to_match])
24+
self.expect("log timers dump",
25+
substrs=["lldb_private::Module::FindSymbolsMatchingRegExAndType"])
26+
27+
self.runCmd("log timers reset")
28+
self.expect("frame variable " + variable,
29+
substrs=[variable + " = " + result_to_match])
30+
self.expect("log timers dump",
31+
matching=False,
32+
substrs=["lldb_private::Module::FindSymbolsMatchingRegExAndType"])
33+
2334

2435
# Temporarily skipping for everywhere b/c we are disabling the std::function formatter
2536
# due to performance issues but plan on turning it back on once they are addressed.
@@ -41,17 +52,22 @@ def test(self):
4152
substrs=['stopped',
4253
'stop reason = breakpoint'])
4354

44-
self.expect("frame variable f1",
45-
substrs=['f1 = Function = foo(int, int)'])
55+
self.run_frame_var_check_cache_use("foo2_f", "Lambda in File main.cpp at Line 30")
56+
57+
lldbutil.continue_to_breakpoint(self.process(), bkpt)
4658

47-
self.expect("frame variable f2",
48-
substrs=['f2 = Lambda in File main.cpp at Line 26'])
59+
self.run_frame_var_check_cache_use("add_num2_f", "Lambda in File main.cpp at Line 21")
4960

50-
self.expect("frame variable f3",
51-
substrs=['f3 = Lambda in File main.cpp at Line 30'])
61+
lldbutil.continue_to_breakpoint(self.process(), bkpt)
5262

53-
self.expect("frame variable f4",
54-
substrs=['f4 = Function in File main.cpp at Line 16'])
63+
self.run_frame_var_check_cache_use("f2", "Lambda in File main.cpp at Line 43")
64+
self.run_frame_var_check_cache_use("f3", "Lambda in File main.cpp at Line 47")
65+
self.run_frame_var_check_cache_use("f4", "Function in File main.cpp at Line 16")
66+
67+
# These cases won't hit the cache at all but also don't require
68+
# an expensive lookup.
69+
self.expect("frame variable f1",
70+
substrs=['f1 = Function = foo(int, int)'])
5571

5672
self.expect("frame variable f5",
5773
substrs=['f5 = Function = Bar::add_num(int) const'])

lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,25 @@ struct Bar {
1717
return 66 ;
1818
}
1919
int add_num(int i) const { return i + 3 ; }
20+
int add_num2(int i) {
21+
std::function<int (int)> add_num2_f = [](int x) {
22+
return x+1;
23+
};
24+
25+
return add_num2_f(i); // Set break point at this line.
26+
}
2027
} ;
2128

29+
int foo2() {
30+
auto f = [](int x) {
31+
return x+1;
32+
};
33+
34+
std::function<int (int)> foo2_f = f;
35+
36+
return foo2_f(10); // Set break point at this line.
37+
}
38+
2239
int main (int argc, char *argv[])
2340
{
2441
int acc = 42;
@@ -35,5 +52,8 @@ int main (int argc, char *argv[])
3552
std::function<int ()> f4( bar1 ) ;
3653
std::function<int (const Bar&, int)> f5 = &Bar::add_num;
3754

55+
int foo2_result = foo2();
56+
int bar_add_num2_result = bar1.add_num2(10);
57+
3858
return f1(acc,acc) + f2(acc) + f3(acc+1,acc+2) + f4() + f5(bar1, 10); // Set break point at this line.
3959
}

lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,33 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(
192192
function_address_resolved, eSymbolContextEverything, sc);
193193
symbol = sc.symbol;
194194
}
195+
196+
auto contains_lambda_identifier = []( llvm::StringRef & str_ref ) {
197+
return str_ref.contains("$_") || str_ref.contains("'lambda'");
198+
};
195199

196-
auto get_name = [&first_template_parameter, &symbol]() {
200+
// Case 4 or 5
201+
// We eliminate these cases early because they don't need the potentially
202+
// expensive lookup through the symbol table.
203+
if (symbol && !symbol->GetName().GetStringRef().startswith("vtable for") &&
204+
!contains_lambda_identifier(first_template_parameter) &&
205+
!symbol->GetName().GetStringRef().contains("__invoke")) {
206+
optional_info.callable_case =
207+
LibCppStdFunctionCallableCase::FreeOrMemberFunction;
208+
optional_info.callable_address = function_address_resolved;
209+
optional_info.callable_symbol = *symbol;
210+
211+
return optional_info;
212+
}
213+
214+
auto get_name = [&first_template_parameter, &symbol, contains_lambda_identifier]() {
197215
// Given case 1:
198216
//
199217
// main::$_0
218+
// Bar::add_num2(int)::'lambda'(int)
200219
//
201220
// we want to append ::operator()()
202-
if (first_template_parameter.contains("$_"))
221+
if (contains_lambda_identifier(first_template_parameter))
203222
return llvm::Regex::escape(first_template_parameter.str()) +
204223
R"(::operator\(\)\(.*\))";
205224

@@ -228,6 +247,10 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(
228247

229248
std::string func_to_match = get_name();
230249

250+
auto it = CallableLookupCache.find(func_to_match);
251+
if (it != CallableLookupCache.end())
252+
return it->second;
253+
231254
SymbolContextList scl;
232255

233256
target.GetImages().FindSymbolsMatchingRegExAndType(
@@ -248,7 +271,7 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(
248271
LineEntry line_entry;
249272
addr.CalculateSymbolContextLineEntry(line_entry);
250273

251-
if (first_template_parameter.contains("$_") ||
274+
if (contains_lambda_identifier(first_template_parameter) ||
252275
(symbol != nullptr &&
253276
symbol->GetName().GetStringRef().contains("__invoke"))) {
254277
// Case 1 and 2
@@ -262,19 +285,10 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(
262285
optional_info.callable_symbol = *symbol;
263286
optional_info.callable_line_entry = line_entry;
264287
optional_info.callable_address = addr;
265-
return optional_info;
266288
}
267289
}
268290

269-
// Case 4 or 5
270-
if (symbol && !symbol->GetName().GetStringRef().startswith("vtable for")) {
271-
optional_info.callable_case =
272-
LibCppStdFunctionCallableCase::FreeOrMemberFunction;
273-
optional_info.callable_address = function_address_resolved;
274-
optional_info.callable_symbol = *symbol;
275-
276-
return optional_info;
277-
}
291+
CallableLookupCache[func_to_match] = optional_info;
278292

279293
return optional_info;
280294
}

lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
#define liblldb_CPPLanguageRuntime_h_
1111

1212
#include <vector>
13+
14+
#include "llvm/ADT/StringMap.h"
15+
1316
#include "lldb/Core/PluginInterface.h"
1417
#include "lldb/Target/LanguageRuntime.h"
1518
#include "lldb/lldb-private.h"
@@ -82,6 +85,11 @@ class CPPLanguageRuntime : public LanguageRuntime {
8285
CPPLanguageRuntime(Process *process);
8386

8487
private:
88+
using OperatorStringToCallableInfoMap =
89+
llvm::StringMap<CPPLanguageRuntime::LibCppStdFunctionCallableInfo>;
90+
91+
OperatorStringToCallableInfoMap CallableLookupCache;
92+
8593
DISALLOW_COPY_AND_ASSIGN(CPPLanguageRuntime);
8694
};
8795

0 commit comments

Comments
 (0)