diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:06:29 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:06:29 +0000 |
commit | 94994d372d014ce4c8758b9605d63fae651bd8aa (patch) | |
tree | 51c0b708bd59f205d6b35cb2a8c24d62f0c33d77 /tools/lldb-test/lldb-test.cpp | |
parent | 39be7ce23363d12ae3e49aeb1fdb2bfeb892e836 (diff) |
Notes
Diffstat (limited to 'tools/lldb-test/lldb-test.cpp')
-rw-r--r-- | tools/lldb-test/lldb-test.cpp | 290 |
1 files changed, 211 insertions, 79 deletions
diff --git a/tools/lldb-test/lldb-test.cpp b/tools/lldb-test/lldb-test.cpp index c9225f63d303..f7bfc487c0ce 100644 --- a/tools/lldb-test/lldb-test.cpp +++ b/tools/lldb-test/lldb-test.cpp @@ -30,6 +30,7 @@ #include "lldb/Target/Target.h" #include "lldb/Utility/CleanUp.h" #include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/State.h" #include "lldb/Utility/StreamString.h" #include "llvm/ADT/IntervalMap.h" @@ -90,19 +91,27 @@ namespace object { cl::opt<bool> SectionContents("contents", cl::desc("Dump each section's contents"), cl::sub(ObjectFileSubcommand)); +cl::opt<bool> SectionDependentModules("dep-modules", + cl::desc("Dump each dependent module"), + cl::sub(ObjectFileSubcommand)); cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>"), cl::OneOrMore, cl::sub(ObjectFileSubcommand)); } // namespace object namespace symbols { -static cl::list<std::string> InputFilenames(cl::Positional, - cl::desc("<input files>"), - cl::OneOrMore, - cl::sub(SymbolsSubcommand)); +static cl::opt<std::string> InputFile(cl::Positional, cl::desc("<input file>"), + cl::Required, cl::sub(SymbolsSubcommand)); + +static cl::opt<std::string> + SymbolPath("symbol-file", + cl::desc("The file from which to fetch symbol information."), + cl::value_desc("file"), cl::sub(SymbolsSubcommand)); + enum class FindType { None, Function, + Block, Namespace, Type, Variable, @@ -112,6 +121,7 @@ static cl::opt<FindType> Find( cl::values( clEnumValN(FindType::None, "none", "No search, just dump the module."), clEnumValN(FindType::Function, "function", "Find functions."), + clEnumValN(FindType::Block, "block", "Find blocks."), clEnumValN(FindType::Namespace, "namespace", "Find namespaces."), clEnumValN(FindType::Type, "type", "Find types."), clEnumValN(FindType::Variable, "variable", "Find global variables.")), @@ -146,6 +156,10 @@ static FunctionNameType getFunctionNameFlags() { return Result; } +static cl::opt<bool> DumpAST("dump-ast", + cl::desc("Dump AST restored from symbols."), + cl::sub(SymbolsSubcommand)); + static cl::opt<bool> Verify("verify", cl::desc("Verify symbol information."), cl::sub(SymbolsSubcommand)); @@ -158,10 +172,12 @@ static cl::opt<int> Line("line", cl::desc("Line to search."), static Expected<CompilerDeclContext> getDeclContext(SymbolVendor &Vendor); static Error findFunctions(lldb_private::Module &Module); +static Error findBlocks(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 Error dumpAST(lldb_private::Module &Module); static Error verify(lldb_private::Module &Module); static Expected<Error (*)(lldb_private::Module &)> getAction(); @@ -197,7 +213,6 @@ struct IRMemoryMapTestState { : Target(Target), Map(Target), Allocations(IntervalMapAllocator) {} }; -bool areAllocationsOverlapping(const AllocationT &L, const AllocationT &R); bool evalMalloc(StringRef Line, IRMemoryMapTestState &State); bool evalFree(StringRef Line, IRMemoryMapTestState &State); int evaluateMemoryMapCommands(Debugger &Dbg); @@ -214,10 +229,9 @@ static Error make_string_error(const char *Format, Args &&... args) { TargetSP opts::createTarget(Debugger &Dbg, const std::string &Filename) { TargetSP Target; - Status ST = - Dbg.GetTargetList().CreateTarget(Dbg, Filename, /*triple*/ "", - /*get_dependent_modules*/ false, - /*platform_options*/ nullptr, Target); + Status ST = Dbg.GetTargetList().CreateTarget( + Dbg, Filename, /*triple*/ "", eLoadDependentsNo, + /*platform_options*/ nullptr, Target); if (ST.Fail()) { errs() << formatv("Failed to create target '{0}: {1}\n", Filename, ST); exit(1); @@ -278,7 +292,7 @@ std::string opts::breakpoint::substitute(StringRef Cmd) { OS << sys::path::parent_path(breakpoint::CommandFile); break; } - // fall through + LLVM_FALLTHROUGH; default: size_t pos = Cmd.find('%'); OS << Cmd.substr(0, pos); @@ -340,7 +354,7 @@ Error opts::symbols::findFunctions(lldb_private::Module &Module) { if (!File.empty()) { assert(Line != 0); - FileSpec src_file(File, false); + FileSpec src_file(File); size_t cu_count = Module.GetNumCompileUnits(); for (size_t i = 0; i < cu_count; i++) { lldb::CompUnitSP cu_sp = Module.GetCompileUnitAtIndex(i); @@ -383,6 +397,42 @@ Error opts::symbols::findFunctions(lldb_private::Module &Module) { return Error::success(); } +Error opts::symbols::findBlocks(lldb_private::Module &Module) { + assert(!Regex); + assert(!File.empty()); + assert(Line != 0); + + SymbolContextList List; + + FileSpec src_file(File); + size_t cu_count = Module.GetNumCompileUnits(); + for (size_t i = 0; i < cu_count; i++) { + lldb::CompUnitSP cu_sp = Module.GetCompileUnitAtIndex(i); + if (!cu_sp) + continue; + + LineEntry le; + cu_sp->FindLineEntry(0, Line, &src_file, false, &le); + if (!le.IsValid()) + continue; + + auto addr = le.GetSameLineContiguousAddressRange().GetBaseAddress(); + if (!addr.IsValid()) + continue; + + SymbolContext sc; + uint32_t resolved = addr.CalculateSymbolContext(&sc, eSymbolContextBlock); + if (resolved & eSymbolContextBlock) + List.Append(sc); + } + + outs() << formatv("Found {0} blocks:\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<CompilerDeclContext> ContextOr = getDeclContext(Vendor); @@ -391,9 +441,8 @@ Error opts::symbols::findNamespaces(lldb_private::Module &Module) { CompilerDeclContext *ContextPtr = ContextOr->IsValid() ? &*ContextOr : nullptr; - SymbolContext SC; CompilerDeclContext Result = - Vendor.FindNamespace(SC, ConstString(Name), ContextPtr); + Vendor.FindNamespace(ConstString(Name), ContextPtr); if (Result) outs() << "Found namespace: " << Result.GetScopeQualifiedName().GetStringRef() << "\n"; @@ -410,10 +459,9 @@ Error opts::symbols::findTypes(lldb_private::Module &Module) { CompilerDeclContext *ContextPtr = ContextOr->IsValid() ? &*ContextOr : nullptr; - SymbolContext SC; DenseSet<SymbolFile *> SearchedFiles; TypeMap Map; - Vendor.FindTypes(SC, ConstString(Name), ContextPtr, true, UINT32_MAX, + Vendor.FindTypes(ConstString(Name), ContextPtr, true, UINT32_MAX, SearchedFiles, Map); outs() << formatv("Found {0} types:\n", Map.GetSize()); @@ -470,6 +518,32 @@ Error opts::symbols::dumpModule(lldb_private::Module &Module) { return Error::success(); } +Error opts::symbols::dumpAST(lldb_private::Module &Module) { + SymbolVendor &plugin = *Module.GetSymbolVendor(); + Module.ParseAllDebugSymbols(); + + auto symfile = plugin.GetSymbolFile(); + if (!symfile) + return make_string_error("Module has no symbol file."); + + auto clang_ast_ctx = llvm::dyn_cast_or_null<ClangASTContext>( + symfile->GetTypeSystemForLanguage(eLanguageTypeC_plus_plus)); + if (!clang_ast_ctx) + return make_string_error("Can't retrieve Clang AST context."); + + auto ast_ctx = clang_ast_ctx->getASTContext(); + if (!ast_ctx) + return make_string_error("Can't retrieve AST context."); + + auto tu = ast_ctx->getTranslationUnitDecl(); + if (!tu) + return make_string_error("Can't retrieve translation unit declaration."); + + tu->print(outs()); + + return Error::success(); +} + Error opts::symbols::verify(lldb_private::Module &Module) { SymbolVendor &plugin = *Module.GetSymbolVendor(); @@ -523,6 +597,10 @@ Error opts::symbols::verify(lldb_private::Module &Module) { } Expected<Error (*)(lldb_private::Module &)> opts::symbols::getAction() { + if (Verify && DumpAST) + return make_string_error( + "Cannot both verify symbol information and dump AST."); + if (Verify) { if (Find != FindType::None) return make_string_error( @@ -535,6 +613,17 @@ Expected<Error (*)(lldb_private::Module &)> opts::symbols::getAction() { return verify; } + if (DumpAST) { + if (Find != FindType::None) + return make_string_error("Cannot both search and dump AST."); + if (Regex || !Context.empty() || !Name.empty() || !File.empty() || + Line != 0) + return make_string_error( + "-regex, -context, -name, -file and -line options are not " + "applicable for dumping AST."); + return dumpAST; + } + if (Regex && !Context.empty()) return make_string_error( "Cannot search using both regular expressions and context."); @@ -566,6 +655,15 @@ Expected<Error (*)(lldb_private::Module &)> opts::symbols::getAction() { "when searching a function."); return findFunctions; + case FindType::Block: + if (File.empty() || Line == 0) + return make_string_error("Both file name and line number must be " + "specified when searching a block."); + if (Regex || getFunctionNameFlags() != 0) + return make_string_error("Cannot use regular expression or " + "function-flags for searching a block."); + return findBlocks; + case FindType::Namespace: if (Regex || !File.empty() || Line != 0) return make_string_error("Cannot search for namespaces using regular " @@ -584,6 +682,8 @@ Expected<Error (*)(lldb_private::Module &)> opts::symbols::getAction() { "using line numbers."); return findVariables; } + + llvm_unreachable("Unsupported symbol action."); } int opts::symbols::dumpSymbols(Debugger &Dbg) { @@ -594,28 +694,59 @@ int opts::symbols::dumpSymbols(Debugger &Dbg) { } auto Action = *ActionOr; - int HadErrors = 0; - for (const auto &File : InputFilenames) { - outs() << "Module: " << File << "\n"; - ModuleSpec Spec{FileSpec(File, false)}; - Spec.GetSymbolFileSpec().SetFile(File, false, FileSpec::Style::native); + outs() << "Module: " << InputFile << "\n"; + ModuleSpec Spec{FileSpec(InputFile)}; + StringRef Symbols = SymbolPath.empty() ? InputFile : SymbolPath; + Spec.GetSymbolFileSpec().SetFile(Symbols, FileSpec::Style::native); - auto ModulePtr = std::make_shared<lldb_private::Module>(Spec); - SymbolVendor *Vendor = ModulePtr->GetSymbolVendor(); - if (!Vendor) { - WithColor::error() << "Module has no symbol vendor.\n"; - HadErrors = 1; - continue; - } + auto ModulePtr = std::make_shared<lldb_private::Module>(Spec); + SymbolVendor *Vendor = ModulePtr->GetSymbolVendor(); + if (!Vendor) { + WithColor::error() << "Module has no symbol vendor.\n"; + return 1; + } - if (Error E = Action(*ModulePtr)) { - WithColor::error() << toString(std::move(E)) << "\n"; - HadErrors = 1; + if (Error E = Action(*ModulePtr)) { + WithColor::error() << toString(std::move(E)) << "\n"; + return 1; + } + + return 0; +} + +static void dumpSectionList(LinePrinter &Printer, const SectionList &List, bool is_subsection) { + size_t Count = List.GetNumSections(0); + if (Count == 0) { + Printer.formatLine("There are no {0}sections", is_subsection ? "sub" : ""); + return; + } + Printer.formatLine("Showing {0} {1}sections", Count, + is_subsection ? "sub" : ""); + for (size_t I = 0; I < Count; ++I) { + auto S = List.GetSectionAtIndex(I); + assert(S); + AutoIndent Indent(Printer, 2); + Printer.formatLine("Index: {0}", I); + Printer.formatLine("ID: {0:x}", S->GetID()); + Printer.formatLine("Name: {0}", S->GetName().GetStringRef()); + Printer.formatLine("Type: {0}", S->GetTypeAsCString()); + Printer.formatLine("Permissions: {0}", GetPermissionsAsCString(S->GetPermissions())); + Printer.formatLine("Thread specific: {0:y}", S->IsThreadSpecific()); + Printer.formatLine("VM address: {0:x}", S->GetFileAddress()); + Printer.formatLine("VM size: {0}", S->GetByteSize()); + Printer.formatLine("File size: {0}", S->GetFileSize()); + + if (opts::object::SectionContents) { + DataExtractor Data; + S->GetSectionData(Data); + ArrayRef<uint8_t> Bytes = {Data.GetDataStart(), Data.GetDataEnd()}; + Printer.formatBinary("Data: ", Bytes, 0); } - outs().flush(); + if (S->GetType() == eSectionTypeContainer) + dumpSectionList(Printer, S->GetChildren(), true); + Printer.NewLine(); } - return HadErrors; } static int dumpObjectFiles(Debugger &Dbg) { @@ -623,9 +754,17 @@ static int dumpObjectFiles(Debugger &Dbg) { int HadErrors = 0; for (const auto &File : opts::object::InputFilenames) { - ModuleSpec Spec{FileSpec(File, false)}; + ModuleSpec Spec{FileSpec(File)}; auto ModulePtr = std::make_shared<lldb_private::Module>(Spec); + + ObjectFile *ObjectPtr = ModulePtr->GetObjectFile(); + if (!ObjectPtr) { + WithColor::error() << File << " not recognised as an object file\n"; + HadErrors = 1; + continue; + } + // Fetch symbol vendor before we get the section list to give the symbol // vendor a chance to populate it. ModulePtr->GetSymbolVendor(); @@ -636,27 +775,29 @@ static int dumpObjectFiles(Debugger &Dbg) { continue; } + Printer.formatLine("Plugin name: {0}", ObjectPtr->GetPluginName()); Printer.formatLine("Architecture: {0}", ModulePtr->GetArchitecture().GetTriple().getTriple()); Printer.formatLine("UUID: {0}", ModulePtr->GetUUID().GetAsString()); - - size_t Count = Sections->GetNumSections(0); - Printer.formatLine("Showing {0} sections", Count); - for (size_t I = 0; I < Count; ++I) { - AutoIndent Indent(Printer, 2); - auto S = Sections->GetSectionAtIndex(I); - assert(S); - Printer.formatLine("Index: {0}", I); - Printer.formatLine("Name: {0}", S->GetName().GetStringRef()); - Printer.formatLine("Type: {0}", S->GetTypeAsCString()); - Printer.formatLine("VM size: {0}", S->GetByteSize()); - Printer.formatLine("File size: {0}", S->GetFileSize()); - - if (opts::object::SectionContents) { - DataExtractor Data; - S->GetSectionData(Data); - ArrayRef<uint8_t> Bytes = {Data.GetDataStart(), Data.GetDataEnd()}; - Printer.formatBinary("Data: ", Bytes, 0); + Printer.formatLine("Executable: {0}", ObjectPtr->IsExecutable()); + Printer.formatLine("Stripped: {0}", ObjectPtr->IsStripped()); + Printer.formatLine("Type: {0}", ObjectPtr->GetType()); + Printer.formatLine("Strata: {0}", ObjectPtr->GetStrata()); + Printer.formatLine("Base VM address: {0:x}", + ObjectPtr->GetBaseAddress().GetFileAddress()); + + dumpSectionList(Printer, *Sections, /*is_subsection*/ false); + + if (opts::object::SectionDependentModules) { + // A non-empty section list ensures a valid object file. + auto Obj = ModulePtr->GetObjectFile(); + FileSpecList Files; + auto Count = Obj->GetDependentModules(Files); + Printer.formatLine("Showing {0} dependent module(s)", Count); + for (size_t I = 0; I < Files.GetSize(); ++I) { + AutoIndent Indent(Printer, 2); + Printer.formatLine("Name: {0}", + Files.GetFileSpecAtIndex(I).GetCString()); } Printer.NewLine(); } @@ -664,13 +805,6 @@ static int dumpObjectFiles(Debugger &Dbg) { return HadErrors; } -/// Check if two half-open intervals intersect: -/// http://world.std.com/~swmcd/steven/tech/interval.html -bool opts::irmemorymap::areAllocationsOverlapping(const AllocationT &L, - const AllocationT &R) { - return R.first < L.second && L.first < R.second; -} - bool opts::irmemorymap::evalMalloc(StringRef Line, IRMemoryMapTestState &State) { // ::= <label> = malloc <size> <alignment> @@ -717,28 +851,21 @@ bool opts::irmemorymap::evalMalloc(StringRef Line, exit(1); } - // Check that the allocation does not overlap another allocation. Do so by - // testing each allocation which may cover the interval [Addr, EndOfRegion). - addr_t EndOfRegion = Addr + Size; - auto Probe = State.Allocations.begin(); - Probe.advanceTo(Addr); //< First interval s.t stop >= Addr. - AllocationT NewAllocation = {Addr, EndOfRegion}; - while (Probe != State.Allocations.end() && Probe.start() < EndOfRegion) { - AllocationT ProbeAllocation = {Probe.start(), Probe.stop()}; - if (areAllocationsOverlapping(ProbeAllocation, NewAllocation)) { - outs() << "Malloc error: overlapping allocation detected" - << formatv(", previous allocation at [{0:x}, {1:x})\n", - Probe.start(), Probe.stop()); - exit(1); - } - ++Probe; + // In case of Size == 0, we still expect the returned address to be unique and + // non-overlapping. + addr_t EndOfRegion = Addr + std::max<size_t>(Size, 1); + if (State.Allocations.overlaps(Addr, EndOfRegion)) { + auto I = State.Allocations.find(Addr); + outs() << "Malloc error: overlapping allocation detected" + << formatv(", previous allocation at [{0:x}, {1:x})\n", I.start(), + I.stop()); + exit(1); } - // Insert the new allocation into the interval map. Use unique allocation IDs - // to inhibit interval coalescing. + // Insert the new allocation into the interval map. Use unique allocation + // IDs to inhibit interval coalescing. static unsigned AllocationID = 0; - if (Size) - State.Allocations.insert(Addr, EndOfRegion, AllocationID++); + State.Allocations.insert(Addr, EndOfRegion, AllocationID++); // Store the label -> address mapping. State.Label2AddrMap[Label] = Addr; @@ -836,8 +963,13 @@ int main(int argc, const char *argv[]) { cl::ParseCommandLineOptions(argc, argv, "LLDB Testing Utility\n"); SystemLifetimeManager DebuggerLifetime; - DebuggerLifetime.Initialize(llvm::make_unique<SystemInitializerTest>(), - nullptr); + if (auto e = DebuggerLifetime.Initialize( + llvm::make_unique<SystemInitializerTest>(), {}, nullptr)) { + WithColor::error() << "initialization failed: " << toString(std::move(e)) + << '\n'; + return 1; + } + CleanUp TerminateDebugger([&] { DebuggerLifetime.Terminate(); }); auto Dbg = lldb_private::Debugger::CreateInstance(); |