ldrumm created this revision. ldrumm added reviewers: spyffe, clayborg. ldrumm added a subscriber: LLDB. Herald added a subscriber: aemerson.
This is a redux of Ewan's patch <https://reviews.llvm.org/D17957> , refactored to properly substitute primitive types using a hook in the itanium demangler, and updated after the previous patch went stale The new `SubsPrimitiveParmItanium` function takes a symbol name and replacement primitive type parameter as before but parses it using the FastDemangler, which has been modified to be able to notify clients of parse events (primitive types at this point). Additionally, we now use a `set` of `ConstStrings` instead of a `vector` so that we try and resolve the same invalid candidate multiple times. I'd particularly welcome feedback on whether there is a better way of doing the hook from the demangler. In the present implementation, the time tradeoff is a single `nullptr` check in the hot path, which was the least invasive change of a few I prototyped. https://reviews.llvm.org/D27223 Files: include/lldb/Core/FastDemangle.h packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/Makefile packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/TestCallOverloadedCFunction.py packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/main.c source/Core/FastDemangle.cpp source/Expression/IRExecutionUnit.cpp source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
Index: source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h =================================================================== --- source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -12,6 +12,7 @@ // C Includes // C++ Includes +#include <set> #include <vector> // Other libraries and framework includes @@ -93,7 +94,6 @@ } std::unique_ptr<TypeScavenger> GetTypeScavenger() override; - lldb::TypeCategoryImplSP GetFormatters() override; HardcodedFormatters::HardcodedSummaryFinder GetHardcodedSummaries() override; @@ -142,6 +142,12 @@ static uint32_t FindEquivalentNames(ConstString type_name, std::vector<ConstString> &equivalents); + // Given a mangled function name, calculates some alternative manglings since + // the compiler mangling may not line up with the symbol we are expecting + static uint32_t + FindAlternateFunctionManglings(const ConstString mangled, + std::set<ConstString> &candidates); + //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ Index: source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp =================================================================== --- source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -10,17 +10,22 @@ #include "CPlusPlusLanguage.h" // C Includes -// C++ Includes #include <cctype> #include <cstring> + +// C++ Includes #include <functional> +#include <memory> #include <mutex> +#include <set> // Other libraries and framework includes #include "llvm/ADT/StringRef.h" // Project includes #include "lldb/Core/ConstString.h" +#include "lldb/Core/FastDemangle.h" +#include "lldb/Core/Log.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/UniqueCStringMap.h" @@ -440,6 +445,112 @@ return count; } +/// Given a mangled function `mangled`, replace all the primitive function type +/// arguments of `mangled` with type `replace`. +static ConstString SubsPrimitiveParmItanium(const char *mangled, + const char *search, + const char *replace) { + Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE); + assert(mangled && search && replace); + + const size_t search_len = ::strlen(search); + const size_t replace_len = ::strlen(replace); + const size_t mangled_len = ::strlen(mangled); + const size_t max_len = + mangled_len + llvm::StringRef(mangled).count(search) * replace_len + 1; + + // Make a temporary buffer to fix up the mangled parameter types + // and copy the original there + std::unique_ptr<char[]> output_buf(new char[max_len]); + ::memset(output_buf.get(), '\0', max_len); + ::strncpy(output_buf.get(), mangled, max_len - 1); + + size_t avail_sz = max_len - 1; + ptrdiff_t replaced_offset = 0; + + auto swap_parms_hook = [&](const char *s) { + if (!s || !*s) + return; + + // Check whether we've found a substitutee + if (::strncmp(s, search, search_len) == 0) { + // account for the case where a replacement is of a different length + // to the original + replaced_offset += replace_len - search_len; + + ptrdiff_t replace_idx = (s - mangled) + replaced_offset; + for (size_t i = 0; avail_sz && replace[i]; ++i, ++s) { + output_buf[replace_idx++] = replace[i]; + avail_sz--; + } + + // append the remains of the mangled string + for (char *p = output_buf.get() + replace_idx; *s && avail_sz--;) + *p++ = *s++; + } + }; + + // FastDemangle will call our hook for each instance of a primitive type, + // allowing us to perform substitution + const char *const demangled = + FastDemangle(mangled, mangled_len, swap_parms_hook); + + if (log) + log->Printf("substituted mangling for %s:{%s} %s:{%s}\n", mangled, + demangled, output_buf.get(), FastDemangle(output_buf.get())); + + return ::strcmp(output_buf.get(), mangled) ? ConstString(output_buf.get()) + : ConstString(); +} + +uint32_t CPlusPlusLanguage::FindAlternateFunctionManglings( + const ConstString mangled_name, std::set<ConstString> &alternates) { + const auto start_size = alternates.size(); + /// Get a basic set of alternative manglings for the given symbol `name`, by + /// making a few basic possible substitutions on basic types, storage duration + /// and `const`ness for the given symbol. The output parameter `alternates` + /// is filled with a best-guess, non-exhaustive set of different manglings + /// for the given name. + + // Maybe we're looking for a const symbol but the debug info told us it was + // non-const... + if (!strncmp(mangled_name.GetCString(), "_ZN", 3) && + strncmp(mangled_name.GetCString(), "_ZNK", 4)) { + std::string fixed_scratch("_ZNK"); + fixed_scratch.append(mangled_name.GetCString() + 3); + alternates.insert(ConstString(fixed_scratch)); + } + + // Maybe we're looking for a static symbol but we thought it was global... + if (!strncmp(mangled_name.GetCString(), "_Z", 2) && + strncmp(mangled_name.GetCString(), "_ZL", 3)) { + std::string fixed_scratch("_ZL"); + fixed_scratch.append(mangled_name.GetCString() + 2); + alternates.insert(ConstString(fixed_scratch)); + } + + // `char` is implementation defined as either `signed` or `unsigned`. As a + // result a char parameter has 3 possible manglings: 'c'-char, 'a'-signed + // char, 'h'-unsigned char. If we're looking for symbols with a signed char + // parameter, try finding matches which have the general case 'c'. + if (ConstString char_fixup = + SubsPrimitiveParmItanium(mangled_name.GetCString(), "a", "c")) + alternates.insert(char_fixup); + + // long long parameter mangling 'x', may actually just be a long 'l' argument + if (ConstString long_fixup = + SubsPrimitiveParmItanium(mangled_name.GetCString(), "x", "l")) + alternates.insert(long_fixup); + + // unsigned long long parameter mangling 'y', may actually just be unsigned + // long 'm' argument + if (ConstString ulong_fixup = + SubsPrimitiveParmItanium(mangled_name.GetCString(), "y", "m")) + alternates.insert(ulong_fixup); + + return alternates.size() - start_size; +} + static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { if (!cpp_category_sp) return; @@ -931,7 +1042,7 @@ return candidate; } }; - + return std::unique_ptr<TypeScavenger>(new CPlusPlusTypeScavenger()); } Index: source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp =================================================================== --- source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -1978,11 +1978,12 @@ if (function) { Type *function_type = function->GetType(); - const lldb::LanguageType comp_unit_language = - function->GetCompileUnit()->GetLanguage(); - const bool extern_c = Language::LanguageIsC(comp_unit_language) || - (Language::LanguageIsObjC(comp_unit_language) && - !Language::LanguageIsCPlusPlus(comp_unit_language)); + const auto lang = function->GetCompileUnit()->GetLanguage(); + const auto name = function->GetMangled().GetMangledName().AsCString(); + const bool extern_c = (Language::LanguageIsC(lang) && + !CPlusPlusLanguage::IsCPPMangledName(name)) || + (Language::LanguageIsObjC(lang) && + !Language::LanguageIsCPlusPlus(lang)); if (!extern_c) { TypeSystem *type_system = function->GetDeclContext().GetTypeSystem(); Index: source/Expression/IRExecutionUnit.cpp =================================================================== --- source/Expression/IRExecutionUnit.cpp +++ source/Expression/IRExecutionUnit.cpp @@ -776,22 +776,9 @@ } } - // Maybe we're looking for a const symbol but the debug info told us it was - // const... - if (!strncmp(name.GetCString(), "_ZN", 3) && - strncmp(name.GetCString(), "_ZNK", 4)) { - std::string fixed_scratch("_ZNK"); - fixed_scratch.append(name.GetCString() + 3); - CPP_specs.push_back(ConstString(fixed_scratch.c_str())); - } - - // Maybe we're looking for a static symbol but we thought it was global... - if (!strncmp(name.GetCString(), "_Z", 2) && - strncmp(name.GetCString(), "_ZL", 3)) { - std::string fixed_scratch("_ZL"); - fixed_scratch.append(name.GetCString() + 2); - CPP_specs.push_back(ConstString(fixed_scratch.c_str())); - } + std::set<ConstString> alternates; + CPlusPlusLanguage::FindAlternateFunctionManglings(name, alternates); + CPP_specs.insert(CPP_specs.end(), alternates.begin(), alternates.end()); } } Index: source/Core/FastDemangle.cpp =================================================================== --- source/Core/FastDemangle.cpp +++ source/Core/FastDemangle.cpp @@ -11,6 +11,9 @@ #include <stdlib.h> #include <string.h> +#include <functional> + +#include "lldb/Core/FastDemangle.h" #include "lldb/lldb-private.h" //#define DEBUG_FAILURES 1 @@ -119,7 +122,9 @@ /// @param storage_size Number of bytes of space available scratch memory /// referenced by storage_ptr - SymbolDemangler(void *storage_ptr, int storage_size) { + SymbolDemangler(void *storage_ptr, size_t storage_size, + std::function<void(const char *)> builtins_hook = nullptr) + : m_builtins_hook(builtins_hook) { // Use up to 1/8th of the provided space for rewrite ranges m_rewrite_ranges_size = (storage_size >> 3) / sizeof(BufferRange); m_rewrite_ranges = (BufferRange *)storage_ptr; @@ -509,6 +514,9 @@ // ::= u <source-name> # vendor extended type const char *TryParseBuiltinType() { + if (m_builtins_hook) + m_builtins_hook(m_read_ptr); + switch (*m_read_ptr++) { case 'v': return "void"; @@ -1150,6 +1158,23 @@ return RewriteTemplateArg(count + 1); } + // <vector-type> + // Dv <dimension number> _ <vector type> + bool TryParseVectorType() { + const int dimension = TryParseNumber(); + if (dimension == -1) + return false; + + if (*m_read_ptr++ != '_') + return false; + + char vec_dimens[32] = {'\0'}; + ::snprintf(vec_dimens, sizeof vec_dimens - 1, " __vector(%d)", dimension); + ParseType(); + Write(vec_dimens); + return true; + } + // <type> ::= <builtin-type> // ::= <function-type> // ::= <class-enum-type> @@ -1191,9 +1216,12 @@ if (!ParseType()) return false; break; + case 'v': + if (!TryParseVectorType()) + return false; + break; case 'T': case 't': - case 'v': default: #ifdef DEBUG_FAILURES printf("*** Unsupported type: %.3s\n", failed_type); @@ -2346,6 +2374,7 @@ char *m_write_ptr; int m_next_template_arg_index; int m_next_substitute_index; + std::function<void(const char *s)> m_builtins_hook; }; } // Anonymous namespace @@ -2358,9 +2387,10 @@ return demangler.GetDemangledCopy(mangled_name); } -char *FastDemangle(const char *mangled_name, long mangled_name_length) { +char *FastDemangle(const char *mangled_name, size_t mangled_name_length, + std::function<void(const char *s)> builtins_hook) { char buffer[16384]; - SymbolDemangler demangler(buffer, sizeof(buffer)); + SymbolDemangler demangler(buffer, sizeof(buffer), builtins_hook); return demangler.GetDemangledCopy(mangled_name, mangled_name_length); } } // lldb_private namespace Index: packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/main.c =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/main.c @@ -0,0 +1,17 @@ +char __attribute__((overloadable)) get_arg_type(float x) +{ + return 'F'; // 'F' for float +} + +char __attribute__((overloadable)) get_arg_type(int x) +{ + return 'I'; // 'I' for int +} + +int main() +{ + char float_result = get_arg_type(0.1f); + char int_result = get_arg_type(2); + + return 0; // break here +} Index: packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/TestCallOverloadedCFunction.py =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/TestCallOverloadedCFunction.py @@ -0,0 +1,60 @@ +""" +Test calling user defined C functions using expression evaluation. +This test checks that expression evaluation works correctly for +functions defined with __attribute__((overloadable)). + +Ticket: https://llvm.org/bugs/show_bug.cgi?id=26694 +""" + +from __future__ import print_function + +import lldb + +from lldbsuite.test.decorators import ( + skipIf, + expectedFailureAll +) +from lldbsuite.test.lldbtest import ( + TestBase, + CURRENT_EXECUTABLE_SET, + RUN_SUCCEEDED, + line_number, + no_match, +) +from lldbsuite.test import lldbutil + + +class TestExprCallOverloadedCFunction(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + super(TestExprCallOverloadedCFunction, self).setUp() + # Find the line number to break for main.c. + self.line = line_number('main.c', '// break here') + + @skipIf(compiler=no_match('clang')) # `overloadable` is only supported by clang" + @expectedFailureAll( + oslist=["windows"], + bugnumber="llvm.org/pr24489: Name lookup broken on Windows") + def test(self): + """Test calling overloaded C functions.""" + self.build() + + # Set breakpoint in main and run exe + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + lldbutil.run_break_set_by_file_and_line( + self, + "main.c", + self.line, + num_expected_locations=-1, + loc_exact=True + ) + + self.runCmd("run", RUN_SUCCEEDED) + + # Test floating point type overload. + self.expect("expr get_arg_type(0.5f)", substrs=["$0 = 'F'"]) + + # Test integer type overload + self.expect("expr get_arg_type(8)", substrs=["$1 = 'I'"]) Index: packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/Makefile =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules Index: include/lldb/Core/FastDemangle.h =================================================================== --- include/lldb/Core/FastDemangle.h +++ include/lldb/Core/FastDemangle.h @@ -10,11 +10,17 @@ #ifndef liblldb_FastDemangle_h_ #define liblldb_FastDemangle_h_ +#include <cstddef> + +#include <functional> + namespace lldb_private { char *FastDemangle(const char *mangled_name); -char *FastDemangle(const char *mangled_name, long mangled_name_length); +char * +FastDemangle(const char *mangled_name, size_t mangled_name_length, + std::function<void(const char *s)> primitive_type_hook = nullptr); } #endif
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits