diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:09:23 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:09:23 +0000 |
| commit | f73363f1dd94996356cefbf24388f561891acf0b (patch) | |
| tree | e3c31248bdb36eaec5fd833490d4278162dba2a0 /tools/lldb-test | |
| parent | 160ee69dd7ae18978f4068116777639ea98dc951 (diff) | |
Notes
Diffstat (limited to 'tools/lldb-test')
| -rw-r--r-- | tools/lldb-test/SystemInitializerTest.cpp | 17 | ||||
| -rw-r--r-- | tools/lldb-test/lldb-test.cpp | 791 |
2 files changed, 779 insertions, 29 deletions
diff --git a/tools/lldb-test/SystemInitializerTest.cpp b/tools/lldb-test/SystemInitializerTest.cpp index cae395e133ee..2c190ecfc745 100644 --- a/tools/lldb-test/SystemInitializerTest.cpp +++ b/tools/lldb-test/SystemInitializerTest.cpp @@ -33,6 +33,7 @@ #include "Plugins/ABI/SysV-s390x/ABISysV_s390x.h" #include "Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h" #include "Plugins/Architecture/Arm/ArchitectureArm.h" +#include "Plugins/Architecture/PPC64/ArchitecturePPC64.h" #include "Plugins/Disassembler/llvm/DisassemblerLLVMC.h" #include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h" #include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h" @@ -40,6 +41,7 @@ #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" #include "Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h" #include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h" +#include "Plugins/Instruction/PPC64/EmulateInstructionPPC64.h" #include "Plugins/InstrumentationRuntime/ASan/ASanRuntime.h" #include "Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h" #include "Plugins/InstrumentationRuntime/TSan/TSanRuntime.h" @@ -58,6 +60,9 @@ #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h" #include "Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h" #include "Plugins/MemoryHistory/asan/MemoryHistoryASan.h" +#include "Plugins/ObjectFile/ELF/ObjectFileELF.h" +#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" +#include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" #include "Plugins/OperatingSystem/Go/OperatingSystemGo.h" #include "Plugins/Platform/Android/PlatformAndroid.h" #include "Plugins/Platform/FreeBSD/PlatformFreeBSD.h" @@ -117,6 +122,11 @@ SystemInitializerTest::~SystemInitializerTest() {} void SystemInitializerTest::Initialize() { SystemInitializerCommon::Initialize(); + + ObjectFileELF::Initialize(); + ObjectFileMachO::Initialize(); + ObjectFilePECOFF::Initialize(); + ScriptInterpreterNone::Initialize(); OperatingSystemGo::Initialize(); @@ -161,6 +171,7 @@ void SystemInitializerTest::Initialize() { ABISysV_s390x::Initialize(); ArchitectureArm::Initialize(); + ArchitecturePPC64::Initialize(); DisassemblerLLVMC::Initialize(); @@ -180,6 +191,7 @@ void SystemInitializerTest::Initialize() { UnwindAssemblyInstEmulation::Initialize(); UnwindAssembly_x86::Initialize(); EmulateInstructionARM64::Initialize(); + EmulateInstructionPPC64::Initialize(); SymbolFileDWARFDebugMap::Initialize(); ItaniumABILanguageRuntime::Initialize(); AppleObjCRuntimeV2::Initialize(); @@ -283,6 +295,7 @@ void SystemInitializerTest::Terminate() { UnwindAssembly_x86::Terminate(); UnwindAssemblyInstEmulation::Terminate(); EmulateInstructionARM64::Terminate(); + EmulateInstructionPPC64::Terminate(); SymbolFileDWARFDebugMap::Terminate(); ItaniumABILanguageRuntime::Terminate(); AppleObjCRuntimeV2::Terminate(); @@ -340,6 +353,10 @@ void SystemInitializerTest::Terminate() { PlatformDarwinKernel::Terminate(); #endif + ObjectFileELF::Terminate(); + ObjectFileMachO::Terminate(); + ObjectFilePECOFF::Terminate(); + // Now shutdown the common parts, in reverse order. SystemInitializerCommon::Terminate(); } diff --git a/tools/lldb-test/lldb-test.cpp b/tools/lldb-test/lldb-test.cpp index 1fe2f5b63859..c9225f63d303 100644 --- a/tools/lldb-test/lldb-test.cpp +++ b/tools/lldb-test/lldb-test.cpp @@ -11,20 +11,37 @@ #include "SystemInitializerTest.h" #include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" +#include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/Section.h" +#include "lldb/Expression/IRMemoryMap.h" #include "lldb/Initialization/SystemLifetimeManager.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/CleanUp.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/StreamString.h" +#include "llvm/ADT/IntervalMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/WithColor.h" +#include <cstdio> #include <thread> using namespace lldb; @@ -32,55 +49,597 @@ using namespace lldb_private; using namespace llvm; namespace opts { -cl::SubCommand ModuleSubcommand("module-sections", - "Display LLDB Module Information"); +static cl::SubCommand BreakpointSubcommand("breakpoints", + "Test breakpoint resolution"); +cl::SubCommand ObjectFileSubcommand("object-file", + "Display LLDB object file information"); cl::SubCommand SymbolsSubcommand("symbols", "Dump symbols for an object file"); +cl::SubCommand IRMemoryMapSubcommand("ir-memory-map", "Test IRMemoryMap"); -namespace module { +cl::opt<std::string> Log("log", cl::desc("Path to a log file"), cl::init(""), + cl::sub(BreakpointSubcommand), + cl::sub(ObjectFileSubcommand), + cl::sub(SymbolsSubcommand), + cl::sub(IRMemoryMapSubcommand)); + +/// Create a target using the file pointed to by \p Filename, or abort. +TargetSP createTarget(Debugger &Dbg, const std::string &Filename); + +/// Read \p Filename into a null-terminated buffer, or abort. +std::unique_ptr<MemoryBuffer> openFile(const std::string &Filename); + +namespace breakpoint { +static cl::opt<std::string> Target(cl::Positional, cl::desc("<target>"), + cl::Required, cl::sub(BreakpointSubcommand)); +static cl::opt<std::string> CommandFile(cl::Positional, + cl::desc("<command-file>"), + cl::init("-"), + cl::sub(BreakpointSubcommand)); +static cl::opt<bool> Persistent( + "persistent", + cl::desc("Don't automatically remove all breakpoints before each command"), + cl::sub(BreakpointSubcommand)); + +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 int evaluateBreakpoints(Debugger &Dbg); +} // namespace breakpoint + +namespace object { cl::opt<bool> SectionContents("contents", cl::desc("Dump each section's contents"), - cl::sub(ModuleSubcommand)); + cl::sub(ObjectFileSubcommand)); cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>"), - cl::OneOrMore, cl::sub(ModuleSubcommand)); -} // namespace module + cl::OneOrMore, + cl::sub(ObjectFileSubcommand)); +} // namespace object namespace symbols { -cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>"), - cl::OneOrMore, cl::sub(SymbolsSubcommand)); +static cl::list<std::string> InputFilenames(cl::Positional, + cl::desc("<input files>"), + cl::OneOrMore, + cl::sub(SymbolsSubcommand)); +enum class FindType { + None, + Function, + Namespace, + Type, + Variable, +}; +static cl::opt<FindType> 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<std::string> Name("name", cl::desc("Name to find."), + cl::sub(SymbolsSubcommand)); +static cl::opt<bool> + Regex("regex", + cl::desc("Search using regular expressions (avaliable for variables " + "and functions only)."), + cl::sub(SymbolsSubcommand)); +static cl::opt<std::string> + Context("context", + cl::desc("Restrict search to the context of the given variable."), + cl::value_desc("variable"), cl::sub(SymbolsSubcommand)); + +static cl::list<FunctionNameType> 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 cl::opt<bool> Verify("verify", cl::desc("Verify symbol information."), + cl::sub(SymbolsSubcommand)); + +static cl::opt<std::string> File("file", + cl::desc("File (compile unit) to search."), + cl::sub(SymbolsSubcommand)); +static cl::opt<int> Line("line", cl::desc("Line to search."), + cl::sub(SymbolsSubcommand)); + +static Expected<CompilerDeclContext> 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 Error verify(lldb_private::Module &Module); + +static Expected<Error (*)(lldb_private::Module &)> getAction(); +static int dumpSymbols(Debugger &Dbg); +} // namespace symbols + +namespace irmemorymap { +static cl::opt<std::string> Target(cl::Positional, cl::desc("<target>"), + cl::Required, + cl::sub(IRMemoryMapSubcommand)); +static cl::opt<std::string> CommandFile(cl::Positional, + cl::desc("<command-file>"), + cl::init("-"), + cl::sub(IRMemoryMapSubcommand)); +static cl::opt<bool> UseHostOnlyAllocationPolicy( + "host-only", cl::desc("Use the host-only allocation policy"), + cl::init(false), cl::sub(IRMemoryMapSubcommand)); + +using AllocationT = std::pair<addr_t, addr_t>; +using AddrIntervalMap = + IntervalMap<addr_t, unsigned, 8, IntervalMapHalfOpenInfo<addr_t>>; + +struct IRMemoryMapTestState { + TargetSP Target; + IRMemoryMap Map; + + AddrIntervalMap::Allocator IntervalMapAllocator; + AddrIntervalMap Allocations; + + StringMap<addr_t> Label2AddrMap; + + IRMemoryMapTestState(TargetSP Target) + : 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); +} // namespace irmemorymap + } // namespace opts -static llvm::ManagedStatic<SystemLifetimeManager> DebuggerLifetime; +template <typename... Args> +static Error make_string_error(const char *Format, Args &&... args) { + return llvm::make_error<llvm::StringError>( + llvm::formatv(Format, std::forward<Args>(args)...).str(), + llvm::inconvertibleErrorCode()); +} + +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); + if (ST.Fail()) { + errs() << formatv("Failed to create target '{0}: {1}\n", Filename, ST); + exit(1); + } + return Target; +} + +std::unique_ptr<MemoryBuffer> opts::openFile(const std::string &Filename) { + auto MB = MemoryBuffer::getFileOrSTDIN(Filename); + if (!MB) { + errs() << formatv("Could not open file '{0}: {1}\n", Filename, + MB.getError().message()); + exit(1); + } + return std::move(*MB); +} + +void opts::breakpoint::dumpState(const BreakpointList &List, LinePrinter &P) { + P.formatLine("{0} breakpoint{1}", List.GetSize(), plural(List.GetSize())); + if (List.GetSize() > 0) + P.formatLine("At least one breakpoint."); + for (size_t i = 0, e = List.GetSize(); i < e; ++i) { + BreakpointSP BP = List.GetBreakpointAtIndex(i); + P.formatLine("Breakpoint ID {0}:", BP->GetID()); + AutoIndent Indent(P, 2); + P.formatLine("{0} location{1}.", BP->GetNumLocations(), + plural(BP->GetNumLocations())); + if (BP->GetNumLocations() > 0) + P.formatLine("At least one location."); + P.formatLine("{0} resolved location{1}.", BP->GetNumResolvedLocations(), + plural(BP->GetNumResolvedLocations())); + if (BP->GetNumResolvedLocations() > 0) + P.formatLine("At least one resolved location."); + for (size_t l = 0, le = BP->GetNumLocations(); l < le; ++l) { + BreakpointLocationSP Loc = BP->GetLocationAtIndex(l); + P.formatLine("Location ID {0}:", Loc->GetID()); + AutoIndent Indent(P, 2); + P.formatLine("Enabled: {0}", Loc->IsEnabled()); + P.formatLine("Resolved: {0}", Loc->IsResolved()); + SymbolContext sc; + Loc->GetAddress().CalculateSymbolContext(&sc); + lldb_private::StreamString S; + sc.DumpStopContext(&S, BP->GetTarget().GetProcessSP().get(), + Loc->GetAddress(), false, true, false, true, true); + P.formatLine("Address: {0}", S.GetString()); + } + } + P.NewLine(); +} + +std::string opts::breakpoint::substitute(StringRef Cmd) { + std::string Result; + raw_string_ostream OS(Result); + while (!Cmd.empty()) { + switch (Cmd[0]) { + case '%': + if (Cmd.consume_front("%p") && (Cmd.empty() || !isalnum(Cmd[0]))) { + OS << sys::path::parent_path(breakpoint::CommandFile); + break; + } + // fall through + default: + size_t pos = Cmd.find('%'); + OS << Cmd.substr(0, pos); + Cmd = Cmd.substr(pos); + break; + } + } + return std::move(OS.str()); +} + +int opts::breakpoint::evaluateBreakpoints(Debugger &Dbg) { + TargetSP Target = opts::createTarget(Dbg, breakpoint::Target); + std::unique_ptr<MemoryBuffer> MB = opts::openFile(breakpoint::CommandFile); + + LinePrinter P(4, outs()); + StringRef Rest = MB->getBuffer(); + int HadErrors = 0; + while (!Rest.empty()) { + StringRef Line; + std::tie(Line, Rest) = Rest.split('\n'); + Line = Line.ltrim(); + if (Line.empty() || Line[0] == '#') + continue; + + if (!Persistent) + Target->RemoveAllBreakpoints(/*internal_also*/ true); + + std::string Command = substitute(Line); + P.formatLine("Command: {0}", Command); + CommandReturnObject Result; + 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<CompilerDeclContext> +opts::symbols::getDeclContext(SymbolVendor &Vendor) { + if (Context.empty()) + return CompilerDeclContext(); + VariableList List; + Vendor.FindGlobalVariables(ConstString(Context), nullptr, UINT32_MAX, List); + if (List.Empty()) + return make_string_error("Context search didn't find a match."); + if (List.GetSize() > 1) + return make_string_error("Context search found multiple matches."); + return List.GetVariableAtIndex(0)->GetDeclContext(); +} + +Error opts::symbols::findFunctions(lldb_private::Module &Module) { + SymbolVendor &Vendor = *Module.GetSymbolVendor(); + SymbolContextList List; + if (!File.empty()) { + assert(Line != 0); + + FileSpec src_file(File, false); + 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, eSymbolContextFunction); + if (resolved & eSymbolContextFunction) + List.Append(sc); + } + } else if (Regex) { + RegularExpression RE(Name); + assert(RE.IsValid()); + Vendor.FindFunctions(RE, true, false, List); + } else { + Expected<CompilerDeclContext> 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<CompilerDeclContext> 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<CompilerDeclContext> ContextOr = getDeclContext(Vendor); + if (!ContextOr) + return ContextOr.takeError(); + CompilerDeclContext *ContextPtr = + ContextOr->IsValid() ? &*ContextOr : nullptr; + + SymbolContext SC; + DenseSet<SymbolFile *> 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, UINT32_MAX, List); + } else if (!File.empty()) { + CompUnitSP CU; + for (size_t Ind = 0; !CU && Ind < Module.GetNumCompileUnits(); ++Ind) { + CompUnitSP Candidate = Module.GetCompileUnitAtIndex(Ind); + if (!Candidate || Candidate->GetFilename().GetStringRef() != File) + continue; + if (CU) + return make_string_error("Multiple compile units for file `{0}` found.", + File); + CU = std::move(Candidate); + } + + if (!CU) + return make_string_error("Compile unit `{0}` not found.", File); + + List.AddVariables(CU->GetVariableList(true).get()); + } else { + Expected<CompilerDeclContext> ContextOr = getDeclContext(Vendor); + if (!ContextOr) + return ContextOr.takeError(); + CompilerDeclContext *ContextPtr = + ContextOr->IsValid() ? &*ContextOr : nullptr; + + Vendor.FindGlobalVariables(ConstString(Name), ContextPtr, UINT32_MAX, List); + } + outs() << formatv("Found {0} variables:\n", List.GetSize()); + StreamString Stream; + List.Dump(&Stream, false); + outs() << Stream.GetData() << "\n"; + return Error::success(); +} + +Error opts::symbols::dumpModule(lldb_private::Module &Module) { + StreamString Stream; + Module.ParseAllDebugSymbols(); + Module.Dump(&Stream); + outs() << Stream.GetData() << "\n"; + return Error::success(); +} + +Error opts::symbols::verify(lldb_private::Module &Module) { + SymbolVendor &plugin = *Module.GetSymbolVendor(); + + SymbolFile *symfile = plugin.GetSymbolFile(); + if (!symfile) + return make_string_error("Module has no symbol file."); + + uint32_t comp_units_count = symfile->GetNumCompileUnits(); + + outs() << "Found " << comp_units_count << " compile units.\n"; + + for (uint32_t i = 0; i < comp_units_count; i++) { + lldb::CompUnitSP comp_unit = symfile->ParseCompileUnitAtIndex(i); + if (!comp_unit) + return make_string_error("Connot parse compile unit {0}.", i); + + outs() << "Processing '" << comp_unit->GetFilename().AsCString() + << "' compile unit.\n"; -static void dumpSymbols(Debugger &Dbg) { - for (const auto &File : opts::symbols::InputFilenames) { + LineTable *lt = comp_unit->GetLineTable(); + if (!lt) + return make_string_error("Can't get a line table of a compile unit."); + + uint32_t count = lt->GetSize(); + + outs() << "The line table contains " << count << " entries.\n"; + + if (count == 0) + continue; + + LineEntry le; + if (!lt->GetLineEntryAtIndex(0, le)) + return make_string_error("Can't get a line entry of a compile unit."); + + for (uint32_t i = 1; i < count; i++) { + lldb::addr_t curr_end = + le.range.GetBaseAddress().GetFileAddress() + le.range.GetByteSize(); + + if (!lt->GetLineEntryAtIndex(i, le)) + return make_string_error("Can't get a line entry of a compile unit"); + + if (curr_end > le.range.GetBaseAddress().GetFileAddress()) + return make_string_error( + "Line table of a compile unit is inconsistent."); + } + } + + outs() << "The symbol information is verified.\n"; + + return Error::success(); +} + +Expected<Error (*)(lldb_private::Module &)> opts::symbols::getAction() { + if (Verify) { + if (Find != FindType::None) + return make_string_error( + "Cannot both search and verify symbol information."); + 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 symbol verification."); + return verify; + } + + if (Regex && !Context.empty()) + return make_string_error( + "Cannot search using both regular expressions and context."); + + if (Regex && !RegularExpression(Name).IsValid()) + return make_string_error("`{0}` is not a valid regular expression.", Name); + + if (Regex + !Context.empty() + !File.empty() >= 2) + return make_string_error( + "Only one of -regex, -context and -file may be used simultaneously."); + if (Regex && Name.empty()) + return make_string_error("-regex used without a -name"); + + switch (Find) { + case FindType::None: + if (!Context.empty() || !Name.empty() || !File.empty() || Line != 0) + return make_string_error( + "Specify search type (-find) to use search options."); + return dumpModule; + + case FindType::Function: + if (!File.empty() + (Line != 0) == 1) + return make_string_error("Both file name and line number must be " + "specified when searching a function " + "by file position."); + if (Regex + (getFunctionNameFlags() != 0) + !File.empty() >= 2) + return make_string_error("Only one of regular expression, function-flags " + "and file position may be used simultaneously " + "when searching a function."); + return findFunctions; + + case FindType::Namespace: + if (Regex || !File.empty() || Line != 0) + return make_string_error("Cannot search for namespaces using regular " + "expressions, file names or line numbers."); + return findNamespaces; + + case FindType::Type: + if (Regex || !File.empty() || Line != 0) + return make_string_error("Cannot search for types using regular " + "expressions, file names or line numbers."); + return findTypes; + + case FindType::Variable: + if (Line != 0) + return make_string_error("Cannot search for variables " + "using line numbers."); + return findVariables; + } +} + +int opts::symbols::dumpSymbols(Debugger &Dbg) { + auto ActionOr = getAction(); + if (!ActionOr) { + logAllUnhandledErrors(ActionOr.takeError(), WithColor::error(), ""); + return 1; + } + 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); + Spec.GetSymbolFileSpec().SetFile(File, false, 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; + } - StreamString Stream; - ModulePtr->ParseAllDebugSymbols(); - ModulePtr->Dump(&Stream); - llvm::outs() << Stream.GetData() << "\n"; - llvm::outs().flush(); + if (Error E = Action(*ModulePtr)) { + WithColor::error() << toString(std::move(E)) << "\n"; + HadErrors = 1; + } + + outs().flush(); } + return HadErrors; } -static void dumpModules(Debugger &Dbg) { +static int dumpObjectFiles(Debugger &Dbg) { LinePrinter Printer(4, llvm::outs()); - for (const auto &File : opts::module::InputFilenames) { + int HadErrors = 0; + for (const auto &File : opts::object::InputFilenames) { ModuleSpec Spec{FileSpec(File, false)}; - Spec.GetSymbolFileSpec().SetFile(File, false); auto ModulePtr = std::make_shared<lldb_private::Module>(Spec); + // Fetch symbol vendor before we get the section list to give the symbol + // vendor a chance to populate it. + ModulePtr->GetSymbolVendor(); SectionList *Sections = ModulePtr->GetSectionList(); if (!Sections) { llvm::errs() << "Could not load sections for module " << File << "\n"; + HadErrors = 1; continue; } + 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) { @@ -89,10 +648,11 @@ static void dumpModules(Debugger &Dbg) { 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::module::SectionContents) { + if (opts::object::SectionContents) { DataExtractor Data; S->GetSectionData(Data); ArrayRef<uint8_t> Bytes = {Data.GetDataStart(), Data.GetDataEnd()}; @@ -101,6 +661,170 @@ static void dumpModules(Debugger &Dbg) { Printer.NewLine(); } } + 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> + StringRef Label; + std::tie(Label, Line) = Line.split('='); + if (Line.empty()) + return false; + Label = Label.trim(); + Line = Line.trim(); + size_t Size; + uint8_t Alignment; + int Matches = sscanf(Line.data(), "malloc %zu %hhu", &Size, &Alignment); + if (Matches != 2) + return false; + + outs() << formatv("Command: {0} = malloc(size={1}, alignment={2})\n", Label, + Size, Alignment); + if (!isPowerOf2_32(Alignment)) { + outs() << "Malloc error: alignment is not a power of 2\n"; + exit(1); + } + + IRMemoryMap::AllocationPolicy AP = + UseHostOnlyAllocationPolicy ? IRMemoryMap::eAllocationPolicyHostOnly + : IRMemoryMap::eAllocationPolicyProcessOnly; + + // Issue the malloc in the target process with "-rw" permissions. + const uint32_t Permissions = 0x3; + const bool ZeroMemory = false; + Status ST; + addr_t Addr = + State.Map.Malloc(Size, Alignment, Permissions, AP, ZeroMemory, ST); + if (ST.Fail()) { + outs() << formatv("Malloc error: {0}\n", ST); + return true; + } + + // Print the result of the allocation before checking its validity. + outs() << formatv("Malloc: address = {0:x}\n", Addr); + + // Check that the allocation is aligned. + if (!Addr || Addr % Alignment != 0) { + outs() << "Malloc error: zero or unaligned allocation detected\n"; + 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; + } + + // 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++); + + // Store the label -> address mapping. + State.Label2AddrMap[Label] = Addr; + + return true; +} + +bool opts::irmemorymap::evalFree(StringRef Line, IRMemoryMapTestState &State) { + // ::= free <label> + if (!Line.consume_front("free")) + return false; + StringRef Label = Line.trim(); + + outs() << formatv("Command: free({0})\n", Label); + auto LabelIt = State.Label2AddrMap.find(Label); + if (LabelIt == State.Label2AddrMap.end()) { + outs() << "Free error: Invalid allocation label\n"; + exit(1); + } + + Status ST; + addr_t Addr = LabelIt->getValue(); + State.Map.Free(Addr, ST); + if (ST.Fail()) { + outs() << formatv("Free error: {0}\n", ST); + exit(1); + } + + // Erase the allocation from the live interval map. + auto Interval = State.Allocations.find(Addr); + if (Interval != State.Allocations.end()) { + outs() << formatv("Free: [{0:x}, {1:x})\n", Interval.start(), + Interval.stop()); + Interval.erase(); + } + + return true; +} + +int opts::irmemorymap::evaluateMemoryMapCommands(Debugger &Dbg) { + // Set up a Target. + TargetSP Target = opts::createTarget(Dbg, irmemorymap::Target); + + // Set up a Process. In order to allocate memory within a target, this + // process must be alive and must support JIT'ing. + CommandReturnObject Result; + Dbg.SetAsyncExecution(false); + CommandInterpreter &CI = Dbg.GetCommandInterpreter(); + auto IssueCmd = [&](const char *Cmd) -> bool { + return CI.HandleCommand(Cmd, eLazyBoolNo, Result); + }; + if (!IssueCmd("b main") || !IssueCmd("run")) { + outs() << formatv("Failed: {0}\n", Result.GetErrorData()); + exit(1); + } + + ProcessSP Process = Target->GetProcessSP(); + if (!Process || !Process->IsAlive() || !Process->CanJIT()) { + outs() << "Cannot use process to test IRMemoryMap\n"; + exit(1); + } + + // Set up an IRMemoryMap and associated testing state. + IRMemoryMapTestState State(Target); + + // Parse and apply commands from the command file. + std::unique_ptr<MemoryBuffer> MB = opts::openFile(irmemorymap::CommandFile); + StringRef Rest = MB->getBuffer(); + while (!Rest.empty()) { + StringRef Line; + std::tie(Line, Rest) = Rest.split('\n'); + Line = Line.ltrim(); + + if (Line.empty() || Line[0] == '#') + continue; + + if (evalMalloc(Line, State)) + continue; + + if (evalFree(Line, State)) + continue; + + errs() << "Could not parse line: " << Line << "\n"; + exit(1); + } + return 0; } int main(int argc, const char *argv[]) { @@ -111,16 +835,25 @@ int main(int argc, const char *argv[]) { cl::ParseCommandLineOptions(argc, argv, "LLDB Testing Utility\n"); - DebuggerLifetime->Initialize(llvm::make_unique<SystemInitializerTest>(), - nullptr); + SystemLifetimeManager DebuggerLifetime; + DebuggerLifetime.Initialize(llvm::make_unique<SystemInitializerTest>(), + nullptr); + CleanUp TerminateDebugger([&] { DebuggerLifetime.Terminate(); }); auto Dbg = lldb_private::Debugger::CreateInstance(); - if (opts::ModuleSubcommand) - dumpModules(*Dbg); - else if (opts::SymbolsSubcommand) - dumpSymbols(*Dbg); + if (!opts::Log.empty()) + Dbg->EnableLog("lldb", {"all"}, opts::Log, 0, errs()); - DebuggerLifetime->Terminate(); - return 0; + if (opts::BreakpointSubcommand) + return opts::breakpoint::evaluateBreakpoints(*Dbg); + if (opts::ObjectFileSubcommand) + return dumpObjectFiles(*Dbg); + if (opts::SymbolsSubcommand) + return opts::symbols::dumpSymbols(*Dbg); + if (opts::IRMemoryMapSubcommand) + return opts::irmemorymap::evaluateMemoryMapCommands(*Dbg); + + WithColor::error() << "No command specified.\n"; + return 1; } |
