Index: lldb/trunk/include/lldb/Symbol/VariableList.h =================================================================== --- lldb/trunk/include/lldb/Symbol/VariableList.h +++ lldb/trunk/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: lldb/trunk/lit/CMakeLists.txt =================================================================== --- lldb/trunk/lit/CMakeLists.txt +++ lldb/trunk/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: lldb/trunk/lit/SymbolFile/DWARF/find-basic-function.cpp =================================================================== --- lldb/trunk/lit/SymbolFile/DWARF/find-basic-function.cpp +++ lldb/trunk/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: lldb/trunk/lit/SymbolFile/DWARF/find-basic-namespace.cpp =================================================================== --- lldb/trunk/lit/SymbolFile/DWARF/find-basic-namespace.cpp +++ lldb/trunk/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: lldb/trunk/lit/SymbolFile/DWARF/find-basic-type.cpp =================================================================== --- lldb/trunk/lit/SymbolFile/DWARF/find-basic-type.cpp +++ lldb/trunk/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: lldb/trunk/lit/SymbolFile/DWARF/find-basic-variable.cpp =================================================================== --- lldb/trunk/lit/SymbolFile/DWARF/find-basic-variable.cpp +++ lldb/trunk/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: lldb/trunk/lit/SymbolFile/DWARF/find-type-in-function.cpp =================================================================== --- lldb/trunk/lit/SymbolFile/DWARF/find-type-in-function.cpp +++ lldb/trunk/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: lldb/trunk/lit/SymbolFile/DWARF/lit.local.cfg =================================================================== --- lldb/trunk/lit/SymbolFile/DWARF/lit.local.cfg +++ lldb/trunk/lit/SymbolFile/DWARF/lit.local.cfg @@ -0,0 +1 @@ +config.suffixes = ['.cpp'] Index: lldb/trunk/lit/lit.cfg =================================================================== --- lldb/trunk/lit/lit.cfg +++ lldb/trunk/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: lldb/trunk/lit/lit.site.cfg.in =================================================================== --- lldb/trunk/lit/lit.site.cfg.in +++ lldb/trunk/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: lldb/trunk/tools/lldb-test/lldb-test.cpp =================================================================== --- lldb/trunk/tools/lldb-test/lldb-test.cpp +++ lldb/trunk/tools/lldb-test/lldb-test.cpp @@ -20,6 +20,10 @@ #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/CleanUp.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/StreamString.h" @@ -29,6 +33,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; @@ -57,7 +62,7 @@ static llvm::StringRef plural(uintmax_t value) { return value == 1 ? "" : "s"; } static void dumpState(const BreakpointList &List, LinePrinter &P); static std::string substitute(StringRef Cmd); -static void evaluateBreakpoints(Debugger &Dbg); +static int evaluateBreakpoints(Debugger &Dbg); } // namespace breakpoint namespace module { @@ -69,12 +74,68 @@ } // 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; } -} // namespace opts -static llvm::ManagedStatic DebuggerLifetime; +static Expected getDeclContext(SymbolVendor &Vendor); + +static Error findFunctions(lldb_private::Module &Module); +static Error findNamespaces(lldb_private::Module &Module); +static Error findTypes(lldb_private::Module &Module); +static Error findVariables(lldb_private::Module &Module); +static Error dumpModule(lldb_private::Module &Module); + +static int dumpSymbols(Debugger &Dbg); +} +} // namespace opts void opts::breakpoint::dumpState(const BreakpointList &List, LinePrinter &P) { P.formatLine("{0} breakpoint{1}", List.GetSize(), plural(List.GetSize())); @@ -130,7 +191,7 @@ return std::move(OS.str()); } -void opts::breakpoint::evaluateBreakpoints(Debugger &Dbg) { +int opts::breakpoint::evaluateBreakpoints(Debugger &Dbg) { TargetSP Target; Status ST = Dbg.GetTargetList().CreateTarget(Dbg, breakpoint::Target, /*triple*/ "", @@ -151,6 +212,7 @@ LinePrinter P(4, outs()); StringRef Rest = (*MB)->getBuffer(); + int HadErrors = 0; while (!Rest.empty()) { StringRef Line; std::tie(Line, Rest) = Rest.split('\n'); @@ -167,31 +229,198 @@ if (!Dbg.GetCommandInterpreter().HandleCommand( Command.c_str(), /*add_to_history*/ eLazyBoolNo, Result)) { P.formatLine("Failed: {0}", Result.GetErrorData()); + HadErrors = 1; continue; } dumpState(Target->GetBreakpointList(/*internal*/ false), P); } + return HadErrors; +} + +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(); +} + +Error opts::symbols::findFunctions(lldb_private::Module &Module) { + SymbolVendor &Vendor = *Module.GetSymbolVendor(); + SymbolContextList List; + if (Regex) { + RegularExpression RE(Name); + assert(RE.IsValid()); + Vendor.FindFunctions(RE, true, false, List); + } else { + Expected ContextOr = getDeclContext(Vendor); + if (!ContextOr) + return ContextOr.takeError(); + 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"; + return Error::success(); +} + +Error opts::symbols::findNamespaces(lldb_private::Module &Module) { + SymbolVendor &Vendor = *Module.GetSymbolVendor(); + Expected ContextOr = getDeclContext(Vendor); + if (!ContextOr) + return ContextOr.takeError(); + 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"; + return Error::success(); +} + +Error opts::symbols::findTypes(lldb_private::Module &Module) { + SymbolVendor &Vendor = *Module.GetSymbolVendor(); + Expected ContextOr = getDeclContext(Vendor); + if (!ContextOr) + return ContextOr.takeError(); + 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"; + return Error::success(); +} + +Error opts::symbols::findVariables(lldb_private::Module &Module) { + SymbolVendor &Vendor = *Module.GetSymbolVendor(); + VariableList List; + if (Regex) { + RegularExpression RE(Name); + assert(RE.IsValid()); + Vendor.FindGlobalVariables(RE, false, UINT32_MAX, List); + } else { + Expected ContextOr = getDeclContext(Vendor); + if (!ContextOr) + return ContextOr.takeError(); + 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"; + return Error::success(); } -static void dumpSymbols(Debugger &Dbg) { - for (const auto &File : opts::symbols::InputFilenames) { +Error opts::symbols::dumpModule(lldb_private::Module &Module) { + StreamString Stream; + Module.ParseAllDebugSymbols(); + Module.Dump(&Stream); + outs() << Stream.GetData() << "\n"; + return Error::success(); +} + +int opts::symbols::dumpSymbols(Debugger &Dbg) { + if (Find != FindType::None && Regex && !Context.empty()) { + WithColor::error() + << "Cannot search using both regular expressions and context.\n"; + return 1; + } + if ((Find == FindType::Type || Find == FindType::Namespace) && Regex) { + WithColor::error() << "Cannot search for types and namespaces using " + "regular expressions.\n"; + return 1; + } + if (Find == FindType::Function && Regex && getFunctionNameFlags() != 0) { + WithColor::error() << "Cannot search for types using both regular " + "expressions and function-flags.\n"; + return 1; + } + if (Regex && !RegularExpression(Name).IsValid()) { + WithColor::error() << "`" << Name + << "` is not a valid regular expression.\n"; + return 1; + } + + Error (*Action)(lldb_private::Module &); + switch (Find) { + case FindType::Function: + Action = findFunctions; + break; + case FindType::Namespace: + Action = findNamespaces; + break; + case FindType::Type: + Action = findTypes; + break; + case FindType::Variable: + Action = findVariables; + break; + case FindType::None: + Action = dumpModule; + break; + } + + int HadErrors = 0; + 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"; + HadErrors = 1; + continue; + } + + if (Error E = Action(*ModulePtr)) { + WithColor::error() << toString(std::move(E)) << "\n"; + HadErrors = 1; + } - StreamString Stream; - ModulePtr->ParseAllDebugSymbols(); - ModulePtr->Dump(&Stream); - llvm::outs() << Stream.GetData() << "\n"; - llvm::outs().flush(); + outs().flush(); } + return HadErrors; } -static void dumpModules(Debugger &Dbg) { +static int dumpModules(Debugger &Dbg) { LinePrinter Printer(4, llvm::outs()); + int HadErrors = 0; for (const auto &File : opts::module::InputFilenames) { ModuleSpec Spec{FileSpec(File, false)}; @@ -202,6 +431,7 @@ SectionList *Sections = ModulePtr->GetSectionList(); if (!Sections) { llvm::errs() << "Could not load sections for module " << File << "\n"; + HadErrors = 1; continue; } @@ -226,6 +456,7 @@ Printer.NewLine(); } } + return HadErrors; } int main(int argc, const char *argv[]) { @@ -236,18 +467,20 @@ cl::ParseCommandLineOptions(argc, argv, "LLDB Testing Utility\n"); - DebuggerLifetime->Initialize(llvm::make_unique(), - nullptr); + SystemLifetimeManager DebuggerLifetime; + DebuggerLifetime.Initialize(llvm::make_unique(), + nullptr); + CleanUp TerminateDebugger([&] { DebuggerLifetime.Terminate(); }); auto Dbg = lldb_private::Debugger::CreateInstance(); if (opts::BreakpointSubcommand) - opts::breakpoint::evaluateBreakpoints(*Dbg); + return opts::breakpoint::evaluateBreakpoints(*Dbg); if (opts::ModuleSubcommand) - dumpModules(*Dbg); - else if (opts::SymbolsSubcommand) - dumpSymbols(*Dbg); + return dumpModules(*Dbg); + if (opts::SymbolsSubcommand) + return opts::symbols::dumpSymbols(*Dbg); - DebuggerLifetime->Terminate(); - return 0; + WithColor::error() << "No command specified.\n"; + return 1; }