diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/tools/lldb-test/lldb-test.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/tools/lldb-test/lldb-test.cpp')
-rw-r--r-- | gnu/llvm/lldb/tools/lldb-test/lldb-test.cpp | 1103 |
1 files changed, 1103 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/tools/lldb-test/lldb-test.cpp b/gnu/llvm/lldb/tools/lldb-test/lldb-test.cpp new file mode 100644 index 00000000000..66c9a87301e --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-test/lldb-test.cpp @@ -0,0 +1,1103 @@ +//===- lldb-test.cpp ------------------------------------------ *- C++ --*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "FormatUtil.h" +#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/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/TypeMap.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/StreamString.h" + +#include "llvm/ADT/IntervalMap.h" +#include "llvm/ADT/ScopeExit.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; +using namespace lldb_private; +using namespace llvm; + +namespace opts { +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"); + +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(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::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, +}; +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::Block, "block", "Find blocks."), + 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::opt<std::string> CompilerContext( + "compiler-context", + cl::desc("Specify a compiler context as \"kind:name,...\"."), + cl::value_desc("context"), cl::sub(SymbolsSubcommand)); + +static cl::opt<std::string> + Language("language", cl::desc("Specify a language type, like C99."), + cl::value_desc("language"), 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> DumpAST("dump-ast", + cl::desc("Dump AST restored from symbols."), + cl::sub(SymbolsSubcommand)); +static cl::opt<bool> + DumpClangAST("dump-clang-ast", + cl::desc("Dump clang AST restored from symbols."), + cl::sub(SymbolsSubcommand)); + +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(SymbolFile &Symfile); + +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 dumpClangAST(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 evalMalloc(StringRef Line, IRMemoryMapTestState &State); +bool evalFree(StringRef Line, IRMemoryMapTestState &State); +int evaluateMemoryMapCommands(Debugger &Dbg); +} // namespace irmemorymap + +} // namespace opts + +std::vector<CompilerContext> parseCompilerContext() { + std::vector<CompilerContext> result; + if (opts::symbols::CompilerContext.empty()) + return result; + + StringRef str{opts::symbols::CompilerContext}; + SmallVector<StringRef, 8> entries_str; + str.split(entries_str, ',', /*maxSplit*/-1, /*keepEmpty=*/false); + for (auto entry_str : entries_str) { + StringRef key, value; + std::tie(key, value) = entry_str.split(':'); + auto kind = + StringSwitch<CompilerContextKind>(key) + .Case("TranslationUnit", CompilerContextKind::TranslationUnit) + .Case("Module", CompilerContextKind::Module) + .Case("Namespace", CompilerContextKind::Namespace) + .Case("Class", CompilerContextKind::Class) + .Case("Struct", CompilerContextKind::Struct) + .Case("Union", CompilerContextKind::Union) + .Case("Function", CompilerContextKind::Function) + .Case("Variable", CompilerContextKind::Variable) + .Case("Enum", CompilerContextKind::Enum) + .Case("Typedef", CompilerContextKind::Typedef) + .Case("AnyModule", CompilerContextKind::AnyModule) + .Case("AnyType", CompilerContextKind::AnyType) + .Default(CompilerContextKind::Invalid); + if (value.empty()) { + WithColor::error() << "compiler context entry has no \"name\"\n"; + exit(1); + } + result.push_back({kind, ConstString{value}}); + } + outs() << "Search context: {\n"; + for (auto entry: result) + entry.Dump(); + outs() << "}\n"; + + return result; +} + +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*/ "", eLoadDependentsNo, + /*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; + } + LLVM_FALLTHROUGH; + 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().rtrim(); + 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(SymbolFile &Symfile) { + if (Context.empty()) + return CompilerDeclContext(); + VariableList List; + Symfile.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) { + SymbolFile &Symfile = *Module.GetSymbolFile(); + SymbolContextList List; + if (!File.empty()) { + assert(Line != 0); + + 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; + const bool include_inlined_functions = false; + auto addr = + le.GetSameLineContiguousAddressRange(include_inlined_functions) + .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()); + List.Clear(); + Symfile.FindFunctions(RE, true, List); + } else { + Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile); + if (!ContextOr) + return ContextOr.takeError(); + CompilerDeclContext *ContextPtr = + ContextOr->IsValid() ? &*ContextOr : nullptr; + + List.Clear(); + Symfile.FindFunctions(ConstString(Name), ContextPtr, getFunctionNameFlags(), + true, 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::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; + const bool include_inlined_functions = false; + auto addr = le.GetSameLineContiguousAddressRange(include_inlined_functions) + .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) { + SymbolFile &Symfile = *Module.GetSymbolFile(); + Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile); + if (!ContextOr) + return ContextOr.takeError(); + CompilerDeclContext *ContextPtr = + ContextOr->IsValid() ? &*ContextOr : nullptr; + + CompilerDeclContext Result = + Symfile.FindNamespace(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) { + SymbolFile &Symfile = *Module.GetSymbolFile(); + Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile); + if (!ContextOr) + return ContextOr.takeError(); + CompilerDeclContext *ContextPtr = + ContextOr->IsValid() ? &*ContextOr : nullptr; + + LanguageSet languages; + if (!Language.empty()) + languages.Insert(Language::GetLanguageTypeFromString(Language)); + + DenseSet<SymbolFile *> SearchedFiles; + TypeMap Map; + if (!Name.empty()) + Symfile.FindTypes(ConstString(Name), ContextPtr, UINT32_MAX, SearchedFiles, + Map); + else + Module.FindTypes(parseCompilerContext(), languages, 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) { + SymbolFile &Symfile = *Module.GetSymbolFile(); + VariableList List; + if (Regex) { + RegularExpression RE(Name); + assert(RE.IsValid()); + Symfile.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->GetPrimaryFile().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(Symfile); + if (!ContextOr) + return ContextOr.takeError(); + CompilerDeclContext *ContextPtr = + ContextOr->IsValid() ? &*ContextOr : nullptr; + + Symfile.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::dumpAST(lldb_private::Module &Module) { + Module.ParseAllDebugSymbols(); + + SymbolFile *symfile = Module.GetSymbolFile(); + if (!symfile) + return make_string_error("Module has no symbol file."); + + llvm::Expected<TypeSystem &> type_system_or_err = + symfile->GetTypeSystemForLanguage(eLanguageTypeC_plus_plus); + if (!type_system_or_err) + return make_string_error("Can't retrieve ClangASTContext"); + + auto *clang_ast_ctx = + llvm::dyn_cast_or_null<ClangASTContext>(&type_system_or_err.get()); + if (!clang_ast_ctx) + return make_string_error("Retrieved TypeSystem was not a ClangASTContext"); + + clang::ASTContext &ast_ctx = clang_ast_ctx->getASTContext(); + + clang::TranslationUnitDecl *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::dumpClangAST(lldb_private::Module &Module) { + Module.ParseAllDebugSymbols(); + + SymbolFile *symfile = Module.GetSymbolFile(); + if (!symfile) + return make_string_error("Module has no symbol file."); + + llvm::Expected<TypeSystem &> type_system_or_err = + symfile->GetTypeSystemForLanguage(eLanguageTypeObjC_plus_plus); + if (!type_system_or_err) + return make_string_error("Can't retrieve ClangASTContext"); + + auto *clang_ast_ctx = + llvm::dyn_cast_or_null<ClangASTContext>(&type_system_or_err.get()); + if (!clang_ast_ctx) + return make_string_error("Retrieved TypeSystem was not a ClangASTContext"); + + StreamString Stream; + clang_ast_ctx->DumpFromSymbolFile(Stream, Name); + outs() << Stream.GetData() << "\n"; + + return Error::success(); +} + +Error opts::symbols::verify(lldb_private::Module &Module) { + SymbolFile *symfile = Module.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->GetCompileUnitAtIndex(i); + if (!comp_unit) + return make_string_error("Connot parse compile unit {0}.", i); + + outs() << "Processing '" + << comp_unit->GetPrimaryFile().GetFilename().AsCString() + << "' compile unit.\n"; + + 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 && DumpAST) + return make_string_error( + "Cannot both verify symbol information and dump AST."); + + 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 (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 (DumpClangAST) { + if (Find != FindType::None) + return make_string_error("Cannot both search and dump clang AST."); + if (Regex || !Context.empty() || !File.empty() || Line != 0) + return make_string_error( + "-regex, -context, -name, -file and -line options are not " + "applicable for dumping clang AST."); + return dumpClangAST; + } + + 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::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 " + "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."); + if (!Name.empty() && !CompilerContext.empty()) + return make_string_error("Name is ignored if compiler context present."); + + return findTypes; + + case FindType::Variable: + if (Line != 0) + return make_string_error("Cannot search for variables " + "using line numbers."); + return findVariables; + } + + llvm_unreachable("Unsupported symbol action."); +} + +int opts::symbols::dumpSymbols(Debugger &Dbg) { + auto ActionOr = getAction(); + if (!ActionOr) { + logAllUnhandledErrors(ActionOr.takeError(), WithColor::error(), ""); + return 1; + } + auto Action = *ActionOr; + + 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); + SymbolFile *Symfile = ModulePtr->GetSymbolFile(); + if (!Symfile) { + WithColor::error() << "Module has no symbol vendor.\n"; + return 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) { + lldb_private::DataExtractor Data; + S->GetSectionData(Data); + ArrayRef<uint8_t> Bytes = {Data.GetDataStart(), Data.GetDataEnd()}; + Printer.formatBinary("Data: ", Bytes, 0); + } + + if (S->GetType() == eSectionTypeContainer) + dumpSectionList(Printer, S->GetChildren(), true); + Printer.NewLine(); + } +} + +static int dumpObjectFiles(Debugger &Dbg) { + LinePrinter Printer(4, llvm::outs()); + + int HadErrors = 0; + for (const auto &File : opts::object::InputFilenames) { + 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->GetSymbolFile(); + SectionList *Sections = ModulePtr->GetSectionList(); + if (!Sections) { + llvm::errs() << "Could not load sections for module " << File << "\n"; + HadErrors = 1; + continue; + } + + Printer.formatLine("Plugin name: {0}", ObjectPtr->GetPluginName()); + Printer.formatLine("Architecture: {0}", + ModulePtr->GetArchitecture().GetTriple().getTriple()); + Printer.formatLine("UUID: {0}", ModulePtr->GetUUID().GetAsString()); + 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(); + } + } + return HadErrors; +} + +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); + } + + // 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. + static unsigned AllocationID = 0; + 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().rtrim(); + + 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[]) { + StringRef ToolName = argv[0]; + sys::PrintStackTraceOnErrorSignal(ToolName); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; + + cl::ParseCommandLineOptions(argc, argv, "LLDB Testing Utility\n"); + + SystemLifetimeManager DebuggerLifetime; + if (auto e = DebuggerLifetime.Initialize( + std::make_unique<SystemInitializerTest>(), nullptr)) { + WithColor::error() << "initialization failed: " << toString(std::move(e)) + << '\n'; + return 1; + } + + auto TerminateDebugger = + llvm::make_scope_exit([&] { DebuggerLifetime.Terminate(); }); + + auto Dbg = lldb_private::Debugger::CreateInstance(); + ModuleList::GetGlobalModuleListProperties().SetEnableExternalLookup(false); + CommandReturnObject Result; + Dbg->GetCommandInterpreter().HandleCommand( + "settings set plugin.process.gdb-remote.packet-timeout 60", + /*add_to_history*/ eLazyBoolNo, Result); + + if (!opts::Log.empty()) + Dbg->EnableLog("lldb", {"all"}, opts::Log, 0, errs()); + + 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; +} |