From e18f4db208baa84800cf304d7e15f2ee7343cd05 Mon Sep 17 00:00:00 2001 From: shafik Date: Wed, 6 Nov 2019 15:57:52 -0800 Subject: [PATCH] [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 --- .../libcxx/function/TestLibCxxFunction.py | 42 +++++++++++++------ .../libcxx/function/main.cpp | 20 +++++++++ .../CPlusPlus/CPPLanguageRuntime.cpp | 40 ++++++++++++------ .../CPlusPlus/CPPLanguageRuntime.h | 8 ++++ 4 files changed, 84 insertions(+), 26 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/TestLibCxxFunction.py b/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/TestLibCxxFunction.py index f06ab5d70ba3..a0cf19b3bed6 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/TestLibCxxFunction.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/TestLibCxxFunction.py @@ -15,11 +15,22 @@ class LibCxxFunctionTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) - def get_variable(self, name): - var = self.frame().FindVariable(name) - var.SetPreferDynamicValue(lldb.eDynamicCanRunTarget) - var.SetPreferSyntheticValue(True) - return var + # Run frame var for a variable twice. Verify we do not hit the cache + # the first time but do the second time. + def run_frame_var_check_cache_use(self, variable, result_to_match): + self.runCmd("log timers reset") + self.expect("frame variable " + variable, + substrs=[variable + " = " + result_to_match]) + self.expect("log timers dump", + substrs=["lldb_private::Module::FindSymbolsMatchingRegExAndType"]) + + self.runCmd("log timers reset") + self.expect("frame variable " + variable, + substrs=[variable + " = " + result_to_match]) + self.expect("log timers dump", + matching=False, + substrs=["lldb_private::Module::FindSymbolsMatchingRegExAndType"]) + # Temporarily skipping for everywhere b/c we are disabling the std::function formatter # due to performance issues but plan on turning it back on once they are addressed. @@ -41,17 +52,22 @@ class LibCxxFunctionTestCase(TestBase): substrs=['stopped', 'stop reason = breakpoint']) - self.expect("frame variable f1", - substrs=['f1 = Function = foo(int, int)']) + self.run_frame_var_check_cache_use("foo2_f", "Lambda in File main.cpp at Line 30") + + lldbutil.continue_to_breakpoint(self.process(), bkpt) - self.expect("frame variable f2", - substrs=['f2 = Lambda in File main.cpp at Line 26']) + self.run_frame_var_check_cache_use("add_num2_f", "Lambda in File main.cpp at Line 21") - self.expect("frame variable f3", - substrs=['f3 = Lambda in File main.cpp at Line 30']) + lldbutil.continue_to_breakpoint(self.process(), bkpt) - self.expect("frame variable f4", - substrs=['f4 = Function in File main.cpp at Line 16']) + self.run_frame_var_check_cache_use("f2", "Lambda in File main.cpp at Line 43") + self.run_frame_var_check_cache_use("f3", "Lambda in File main.cpp at Line 47") + self.run_frame_var_check_cache_use("f4", "Function in File main.cpp at Line 16") + + # These cases won't hit the cache at all but also don't require + # an expensive lookup. + self.expect("frame variable f1", + substrs=['f1 = Function = foo(int, int)']) self.expect("frame variable f5", substrs=['f5 = Function = Bar::add_num(int) const']) diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp b/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp index b78161df2c1e..d0c931ddc8b4 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp +++ b/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp @@ -17,8 +17,25 @@ struct Bar { return 66 ; } int add_num(int i) const { return i + 3 ; } + int add_num2(int i) { + std::function add_num2_f = [](int x) { + return x+1; + }; + + return add_num2_f(i); // Set break point at this line. + } } ; +int foo2() { + auto f = [](int x) { + return x+1; + }; + + std::function foo2_f = f; + + return foo2_f(10); // Set break point at this line. +} + int main (int argc, char *argv[]) { int acc = 42; @@ -35,5 +52,8 @@ int main (int argc, char *argv[]) std::function f4( bar1 ) ; std::function f5 = &Bar::add_num; + int foo2_result = foo2(); + int bar_add_num2_result = bar1.add_num2(10); + return f1(acc,acc) + f2(acc) + f3(acc+1,acc+2) + f4() + f5(bar1, 10); // Set break point at this line. } diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp index f38014505a8b..379b0ba1b4d9 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp @@ -192,14 +192,33 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( function_address_resolved, eSymbolContextEverything, sc); symbol = sc.symbol; } + + auto contains_lambda_identifier = []( llvm::StringRef & str_ref ) { + return str_ref.contains("$_") || str_ref.contains("'lambda'"); + }; - auto get_name = [&first_template_parameter, &symbol]() { + // Case 4 or 5 + // We eliminate these cases early because they don't need the potentially + // expensive lookup through the symbol table. + if (symbol && !symbol->GetName().GetStringRef().startswith("vtable for") && + !contains_lambda_identifier(first_template_parameter) && + !symbol->GetName().GetStringRef().contains("__invoke")) { + optional_info.callable_case = + LibCppStdFunctionCallableCase::FreeOrMemberFunction; + optional_info.callable_address = function_address_resolved; + optional_info.callable_symbol = *symbol; + + return optional_info; + } + + auto get_name = [&first_template_parameter, &symbol, contains_lambda_identifier]() { // Given case 1: // // main::$_0 + // Bar::add_num2(int)::'lambda'(int) // // we want to append ::operator()() - if (first_template_parameter.contains("$_")) + if (contains_lambda_identifier(first_template_parameter)) return llvm::Regex::escape(first_template_parameter.str()) + R"(::operator\(\)\(.*\))"; @@ -228,6 +247,10 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( std::string func_to_match = get_name(); + auto it = CallableLookupCache.find(func_to_match); + if (it != CallableLookupCache.end()) + return it->second; + SymbolContextList scl; target.GetImages().FindSymbolsMatchingRegExAndType( @@ -248,7 +271,7 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( LineEntry line_entry; addr.CalculateSymbolContextLineEntry(line_entry); - if (first_template_parameter.contains("$_") || + if (contains_lambda_identifier(first_template_parameter) || (symbol != nullptr && symbol->GetName().GetStringRef().contains("__invoke"))) { // Case 1 and 2 @@ -262,19 +285,10 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( optional_info.callable_symbol = *symbol; optional_info.callable_line_entry = line_entry; optional_info.callable_address = addr; - return optional_info; } } - // Case 4 or 5 - if (symbol && !symbol->GetName().GetStringRef().startswith("vtable for")) { - optional_info.callable_case = - LibCppStdFunctionCallableCase::FreeOrMemberFunction; - optional_info.callable_address = function_address_resolved; - optional_info.callable_symbol = *symbol; - - return optional_info; - } + CallableLookupCache[func_to_match] = optional_info; return optional_info; } diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h index 28526361efc4..abdd79fcd7b9 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h @@ -10,6 +10,9 @@ #define liblldb_CPPLanguageRuntime_h_ #include + +#include "llvm/ADT/StringMap.h" + #include "lldb/Core/PluginInterface.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/lldb-private.h" @@ -82,6 +85,11 @@ protected: CPPLanguageRuntime(Process *process); private: + using OperatorStringToCallableInfoMap = + llvm::StringMap; + + OperatorStringToCallableInfoMap CallableLookupCache; + DISALLOW_COPY_AND_ASSIGN(CPPLanguageRuntime); }; -- GitLab