Index: include/lldb/Symbol/VariableList.h =================================================================== --- include/lldb/Symbol/VariableList.h +++ include/lldb/Symbol/VariableList.h @@ -66,6 +66,7 @@ size_t MemorySize() const; size_t GetSize() const; + bool Empty() const { return m_variables.empty(); } protected: typedef std::vector collection; Index: lit/CMakeLists.txt =================================================================== --- lit/CMakeLists.txt +++ lit/CMakeLists.txt @@ -18,6 +18,24 @@ string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} LLDB_LIBS_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}) string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} LLDB_TOOLS_DIR ${LLVM_RUNTIME_OUTPUT_INTDIR}) +set(LLDB_TEST_DEPS + LLDBUnitTests + dsymutil + lldb + lldb-test + llvm-config + llvm-mc + llvm-objcopy + ) + +if(TARGET lld) + list(APPEND LLDB_TEST_DEPS lld) + set(LLDB_HAVE_LLD 1) +else() + set(LLDB_HAVE_LLD 0) +endif() + + if(BUILD_SHARED_LIBS) set(ENABLE_SHARED 1) else() @@ -36,16 +54,6 @@ ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg ) -set(LLDB_TEST_DEPS - LLDBUnitTests - dsymutil - lldb - lldb-test - llvm-config - llvm-mc - llvm-objcopy - ) - if(NOT LLDB_BUILT_STANDALONE) list(APPEND LLDB_TEST_DEPS FileCheck not yaml2obj) endif() Index: lit/SymbolFile/DWARF/find-basic-function.cpp =================================================================== --- /dev/null +++ lit/SymbolFile/DWARF/find-basic-function.cpp @@ -0,0 +1,67 @@ +// REQUIRES: lld + +// RUN: clang %s -g -c -o %t.o --target=x86_64-pc-linux +// RUN: ld.lld %t.o -o %t +// RUN: lldb-test symbols --name=foo --find=function --function-flags=base %t | \ +// RUN: FileCheck --check-prefix=BASE %s +// RUN: lldb-test symbols --name=foo --find=function --function-flags=method %t | \ +// RUN: FileCheck --check-prefix=METHOD %s +// RUN: lldb-test symbols --name=foo --find=function --function-flags=full %t | \ +// RUN: FileCheck --check-prefix=FULL %s +// RUN: lldb-test symbols --name=foo --context=context --find=function --function-flags=base %t | \ +// RUN: FileCheck --check-prefix=CONTEXT %s +// RUN: lldb-test symbols --name=not_there --find=function %t | \ +// RUN: FileCheck --check-prefix=EMPTY %s + +// BASE: Found 4 functions: +// BASE-DAG: name = "foo()", mangled = "_Z3foov" +// BASE-DAG: name = "foo(int)", mangled = "_Z3fooi" +// BASE-DAG: name = "bar::foo()", mangled = "_ZN3bar3fooEv" +// BASE-DAG: name = "bar::baz::foo()", mangled = "_ZN3bar3baz3fooEv" + +// METHOD: Found 3 functions: +// METHOD-DAG: name = "sbar::foo()", mangled = "_ZN4sbar3fooEv" +// METHOD-DAG: name = "sbar::foo(int)", mangled = "_ZN4sbar3fooEi" +// METHOD-DAG: name = "ffbar()::sbar::foo()", mangled = "_ZZ5ffbarvEN4sbar3fooEv" + +// FULL: Found 2 functions: +// FULL-DAG: name = "foo()", mangled = "_Z3foov" +// FULL-DAG: name = "foo(int)", mangled = "_Z3fooi" + +// CONTEXT: Found 1 functions: +// CONTEXT-DAG: name = "bar::foo()", mangled = "_ZN3bar3fooEv" + +// EMPTY: Found 0 functions: + +void foo() {} +void foo(int) {} + +namespace bar { +int context; +void foo() {} +namespace baz { +void foo() {} +} // namespace baz +} // namespace bar + +struct foo {}; +void fbar(struct foo) {} + +void Foo() {} + +struct sbar { + void foo(); + static void foo(int); +}; +void sbar::foo() {} +void sbar::foo(int) {} + +void ffbar() { + struct sbar { + void foo() {} + }; + sbar a; + a.foo(); +} + +extern "C" void _start() {} Index: lit/SymbolFile/DWARF/find-basic-namespace.cpp =================================================================== --- /dev/null +++ lit/SymbolFile/DWARF/find-basic-namespace.cpp @@ -0,0 +1,29 @@ +// REQUIRES: lld + +// RUN: clang %s -g -c -o %t.o --target=x86_64-pc-linux +// RUN: ld.lld %t.o -o %t +// RUN: lldb-test symbols --name=foo --find=namespace %t | \ +// RUN: FileCheck --check-prefix=FOO %s +// RUN: lldb-test symbols --name=foo --find=namespace --context=context %t | \ +// RUN: FileCheck --check-prefix=CONTEXT %s +// RUN: lldb-test symbols --name=not_there --find=namespace %t | \ +// RUN: FileCheck --check-prefix=EMPTY %s + +// FOO: Found namespace: foo + +// CONTEXT: Found namespace: bar::foo + +// EMPTY: Namespace not found. + +namespace foo { +int X; +} + +namespace bar { +int context; +namespace foo { +int X; +} +} // namespace bar + +extern "C" void _start() {} Index: lit/SymbolFile/DWARF/find-basic-type.cpp =================================================================== --- /dev/null +++ lit/SymbolFile/DWARF/find-basic-type.cpp @@ -0,0 +1,38 @@ +// REQUIRES: lld + +// RUN: clang %s -g -c -o %t.o --target=x86_64-pc-linux +// RUN: ld.lld %t.o -o %t +// RUN: lldb-test symbols --name=foo --find=type %t | \ +// RUN: FileCheck --check-prefix=NAME %s +// RUN: lldb-test symbols --name=foo --context=context --find=type %t | \ +// RUN: FileCheck --check-prefix=CONTEXT %s +// RUN: lldb-test symbols --name=not_there --find=type %t | \ +// RUN: FileCheck --check-prefix=EMPTY %s + +// EMPTY: Found 0 types: +// NAME: Found 4 types: +// CONTEXT: Found 1 types: +struct foo { }; +// NAME-DAG: name = "foo", {{.*}} decl = find-basic-type.cpp:[[@LINE-1]] + +namespace bar { +int context; +struct foo {}; +// NAME-DAG: name = "foo", {{.*}} decl = find-basic-type.cpp:[[@LINE-1]] +// CONTEXT-DAG: name = "foo", {{.*}} decl = find-basic-type.cpp:[[@LINE-2]] +namespace baz { +struct foo {}; +// NAME-DAG: name = "foo", {{.*}} decl = find-basic-type.cpp:[[@LINE-1]] +} +} + +struct sbar { + struct foo {}; +// NAME-DAG: name = "foo", {{.*}} decl = find-basic-type.cpp:[[@LINE-1]] +}; + +struct foobar {}; + +struct Foo {}; + +extern "C" void _start(foo, bar::foo, bar::baz::foo, sbar::foo, foobar, Foo) {} Index: lit/SymbolFile/DWARF/find-basic-variable.cpp =================================================================== --- /dev/null +++ lit/SymbolFile/DWARF/find-basic-variable.cpp @@ -0,0 +1,55 @@ +// REQUIRES: lld + +// RUN: clang %s -g -c -o %t.o --target=x86_64-pc-linux +// RUN: ld.lld %t.o -o %t +// RUN: lldb-test symbols --name=foo --find=variable --context=context %t | \ +// RUN: FileCheck --check-prefix=CONTEXT %s +// RUN: lldb-test symbols --name=foo --find=variable %t | \ +// RUN: FileCheck --check-prefix=NAME %s +// RUN: lldb-test symbols --regex --name=foo --find=variable %t | \ +// RUN: FileCheck --check-prefix=REGEX %s +// RUN: lldb-test symbols --name=not_there --find=variable %t | \ +// RUN: FileCheck --check-prefix=EMPTY %s + +// EMPTY: Found 0 variables: +// NAME: Found 4 variables: +// CONTEXT: Found 1 variables: +// REGEX: Found 5 variables: +int foo; +// NAME-DAG: name = "foo", type = {{.*}} (int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-1]] +// REGEX-DAG: name = "foo", type = {{.*}} (int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-2]] +namespace bar { +int context; +long foo; +// NAME-DAG: name = "foo", type = {{.*}} (long int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-1]] +// CONTEXT-DAG: name = "foo", type = {{.*}} (long int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-2]] +// REGEX-DAG: name = "foo", type = {{.*}} (long int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-3]] +namespace baz { +static short foo; +// NAME-DAG: name = "foo", type = {{.*}} (short), {{.*}} decl = find-basic-variable.cpp:[[@LINE-1]] +// REGEX-DAG: name = "foo", type = {{.*}} (short), {{.*}} decl = find-basic-variable.cpp:[[@LINE-2]] +} +} + +struct sbar { + static int foo; +// NAME-DAG: name = "foo", type = {{.*}} (int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-1]] +// REGEX-DAG: name = "foo", type = {{.*}} (int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-2]] +}; +int sbar::foo; + +int foobar; +// REGEX-DAG: name = "foobar", type = {{.*}} (int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-1]] + +int fbar() { + static int foo; + return foo + bar::baz::foo; +} + +int Foo; + +struct ssbar { + int foo; +}; + +extern "C" void _start(sbar, ssbar) {} Index: lit/SymbolFile/DWARF/find-type-in-function.cpp =================================================================== --- /dev/null +++ lit/SymbolFile/DWARF/find-type-in-function.cpp @@ -0,0 +1,24 @@ +// REQUIRES: lld + +// XFAIL: * + +// RUN: clang %s -g -c -o %t.o --target=x86_64-pc-linux +// RUN: ld.lld %t.o -o %t +// RUN: lldb-test symbols --name=foo --find=type %t | \ +// RUN: FileCheck --check-prefix=NAME %s + +// Lookup for "foo" should find either both "struct foo" types or just the +// global one. Right now, it finds the definition inside bar(), which is +// definitely wrong. + +// NAME: Found 2 types: +struct foo {}; +// NAME-DAG: name = "foo", {{.*}} decl = find-type-in-function.cpp:[[@LINE-1]] + +void bar() { + struct foo {}; +// NAME-DAG: name = "foo", {{.*}} decl = find-type-in-function.cpp:[[@LINE-1]] + foo a; +} + +extern "C" void _start(foo) {} Index: lit/SymbolFile/DWARF/lit.local.cfg =================================================================== --- /dev/null +++ lit/SymbolFile/DWARF/lit.local.cfg @@ -0,0 +1 @@ +config.suffixes = ['.cpp'] Index: lit/lit.cfg =================================================================== --- lit/lit.cfg +++ lit/lit.cfg @@ -132,6 +132,8 @@ config.available_features.add("compiler-msvc") config.available_features.add(binary_feature(config.have_zlib, "zlib", "no")) +if config.have_lld: + config.available_features.add("lld") # llvm-config knows whether it is compiled with asserts (and) # whether we are operating in release/debug mode. Index: lit/lit.site.cfg.in =================================================================== --- lit/lit.site.cfg.in +++ lit/lit.site.cfg.in @@ -13,6 +13,7 @@ config.cc = "@LLDB_TEST_C_COMPILER@" config.cxx = "@LLDB_TEST_CXX_COMPILER@" config.have_zlib = @LLVM_ENABLE_ZLIB@ +config.have_lld = @LLDB_HAVE_LLD@ # Support substitution of the tools and libs dirs with user parameters. This is # used when we can't determine the tool dir at configuration time. Index: tools/lldb-test/lldb-test.cpp =================================================================== --- tools/lldb-test/lldb-test.cpp +++ tools/lldb-test/lldb-test.cpp @@ -20,6 +20,9 @@ #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/VariableList.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/StreamString.h" @@ -29,6 +32,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/WithColor.h" #include using namespace lldb; @@ -69,8 +73,65 @@ } // namespace module namespace symbols { -cl::list InputFilenames(cl::Positional, cl::desc(""), - cl::OneOrMore, cl::sub(SymbolsSubcommand)); +static cl::list InputFilenames(cl::Positional, + cl::desc(""), + cl::OneOrMore, + cl::sub(SymbolsSubcommand)); +enum class FindType { + None, + Function, + Namespace, + Type, + Variable, +}; +static cl::opt Find( + "find", cl::desc("Choose search type:"), + cl::values( + clEnumValN(FindType::None, "none", + "No search, just dump the module."), + clEnumValN(FindType::Function, "function", "Find functions."), + clEnumValN(FindType::Namespace, "namespace", "Find namespaces."), + clEnumValN(FindType::Type, "type", "Find types."), + clEnumValN(FindType::Variable, "variable", "Find global variables.")), + cl::sub(SymbolsSubcommand)); + +static cl::opt Name("name", cl::desc("Name to find."), + cl::sub(SymbolsSubcommand)); +static cl::opt + Regex("regex", + cl::desc("Search using regular expressions (avaliable for variables " + "and functions only)."), + cl::sub(SymbolsSubcommand)); +static cl::opt + Context("context", + cl::desc("Restrict search to the context of the given variable."), + cl::value_desc("variable"), cl::sub(SymbolsSubcommand)); + +static cl::list FunctionNameFlags( + "function-flags", cl::desc("Function search flags:"), + cl::values(clEnumValN(eFunctionNameTypeAuto, "auto", + "Automatically deduce flags based on name."), + clEnumValN(eFunctionNameTypeFull, "full", "Full function name."), + clEnumValN(eFunctionNameTypeBase, "base", "Base name."), + clEnumValN(eFunctionNameTypeMethod, "method", "Method name."), + clEnumValN(eFunctionNameTypeSelector, "selector", + "Selector name.")), + cl::sub(SymbolsSubcommand)); +static FunctionNameType getFunctionNameFlags() { + FunctionNameType Result = FunctionNameType(0); + for (FunctionNameType Flag : FunctionNameFlags) + Result = FunctionNameType(Result | Flag); + return Result; +} + +static Expected getDeclContext(SymbolVendor &Vendor); + +static void findFunctions(SymbolVendor &Vendor); +static void findNamespaces(SymbolVendor &Vendor); +static void findTypes(SymbolVendor &Vendor); +static void findVariables(SymbolVendor &Vendor); + +static void dumpSymbols(Debugger &Dbg); } } // namespace opts @@ -174,18 +235,169 @@ } } -static void dumpSymbols(Debugger &Dbg) { - for (const auto &File : opts::symbols::InputFilenames) { +Expected +opts::symbols::getDeclContext(SymbolVendor &Vendor) { + if (Context.empty()) + return CompilerDeclContext(); + VariableList List; + Vendor.FindGlobalVariables(ConstString(Context), nullptr, false, UINT32_MAX, + List); + if (List.Empty()) { + return make_error("Context search didn't find a match.", + inconvertibleErrorCode()); + } + if (List.GetSize() > 1) { + return make_error("Context search found multiple matches.", + inconvertibleErrorCode()); + } + return List.GetVariableAtIndex(0)->GetDeclContext(); +} + +void opts::symbols::findFunctions(SymbolVendor &Vendor) { + SymbolContextList List; + if (Regex) { + RegularExpression RE(Name); + if (!RE.IsValid()) { + WithColor::error() << "`" << Name + << "` is not a valid regular expression.\n"; + return; + } + Vendor.FindFunctions(RE, true, false, List); + } else { + Expected ContextOr = getDeclContext(Vendor); + if (!ContextOr) { + WithColor::error() << toString(ContextOr.takeError()) << "\n"; + return; + } + CompilerDeclContext *ContextPtr = + ContextOr->IsValid() ? &*ContextOr : nullptr; + + Vendor.FindFunctions(ConstString(Name), ContextPtr, getFunctionNameFlags(), + true, false, List); + } + outs() << formatv("Found {0} functions:\n", List.GetSize()); + StreamString Stream; + List.Dump(&Stream, nullptr); + outs() << Stream.GetData() << "\n"; +} + +void opts::symbols::findNamespaces(SymbolVendor &Vendor) { + Expected ContextOr = getDeclContext(Vendor); + if (!ContextOr) { + WithColor::error() << toString(ContextOr.takeError()) << "\n"; + return; + } + CompilerDeclContext *ContextPtr = + ContextOr->IsValid() ? &*ContextOr : nullptr; + + SymbolContext SC; + CompilerDeclContext Result = + Vendor.FindNamespace(SC, ConstString(Name), ContextPtr); + if (Result) + outs() << "Found namespace: " + << Result.GetScopeQualifiedName().GetStringRef() << "\n"; + else + outs() << "Namespace not found.\n"; +} + +void opts::symbols::findTypes(SymbolVendor &Vendor) { + Expected ContextOr = getDeclContext(Vendor); + if (!ContextOr) { + WithColor::error() << toString(ContextOr.takeError()) << "\n"; + return; + } + CompilerDeclContext *ContextPtr = + ContextOr->IsValid() ? &*ContextOr : nullptr; + + SymbolContext SC; + DenseSet SearchedFiles; + TypeMap Map; + Vendor.FindTypes(SC, ConstString(Name), ContextPtr, true, UINT32_MAX, + SearchedFiles, Map); + + outs() << formatv("Found {0} types:\n", Map.GetSize()); + StreamString Stream; + Map.Dump(&Stream, false); + outs() << Stream.GetData() << "\n"; +} + +void opts::symbols::findVariables(SymbolVendor &Vendor) { + VariableList List; + if (Regex) { + RegularExpression RE(Name); + if (!RE.IsValid()) { + WithColor::error() << "`" << Name + << "` is not a valid regular expression.\n"; + return; + } + Vendor.FindGlobalVariables(RE, false, UINT32_MAX, List); + } else { + Expected ContextOr = getDeclContext(Vendor); + if (!ContextOr) { + WithColor::error() << toString(ContextOr.takeError()) << "\n"; + return; + } + CompilerDeclContext *ContextPtr = + ContextOr->IsValid() ? &*ContextOr : nullptr; + + Vendor.FindGlobalVariables(ConstString(Name), ContextPtr, false, UINT32_MAX, + List); + } + outs() << formatv("Found {0} variables:\n", List.GetSize()); + StreamString Stream; + List.Dump(&Stream, false); + outs() << Stream.GetData() << "\n"; +} + +void opts::symbols::dumpSymbols(Debugger &Dbg) { + if (Find != FindType::None && Regex && !Context.empty()) { + WithColor::error() + << "Cannot search using both regular expressions and context.\n"; + return; + } + if ((Find == FindType::Type || Find == FindType::Namespace) && Regex) { + WithColor::error() << "Cannot search for types and namespaces using " + "regular expressions.\n"; + return; + } + if (Find == FindType::Function && Regex && getFunctionNameFlags() != 0) { + WithColor::error() << "Cannot search for types using both regular " + "expressions and function-flags.\n"; + return; + } + for (const auto &File : InputFilenames) { + outs() << "Module: " << File << "\n"; ModuleSpec Spec{FileSpec(File, false)}; Spec.GetSymbolFileSpec().SetFile(File, false); auto ModulePtr = std::make_shared(Spec); + SymbolVendor *Vendor = ModulePtr->GetSymbolVendor(); + if (!Vendor) { + WithColor::error() << "Module has no symbol vendor.\n"; + continue; + } - StreamString Stream; - ModulePtr->ParseAllDebugSymbols(); - ModulePtr->Dump(&Stream); - llvm::outs() << Stream.GetData() << "\n"; - llvm::outs().flush(); + switch(Find) { + case FindType::Function: + findFunctions(*Vendor); + break; + case FindType::Namespace: + findNamespaces(*Vendor); + break; + case FindType::Type: + findTypes(*Vendor); + break; + case FindType::Variable: + findVariables(*Vendor); + break; + case FindType::None: + StreamString Stream; + ModulePtr->ParseAllDebugSymbols(); + ModulePtr->Dump(&Stream); + outs() << Stream.GetData() << "\n"; + break; + } + outs().flush(); } } @@ -246,7 +458,7 @@ if (opts::ModuleSubcommand) dumpModules(*Dbg); else if (opts::SymbolsSubcommand) - dumpSymbols(*Dbg); + opts::symbols::dumpSymbols(*Dbg); DebuggerLifetime->Terminate(); return 0;