summaryrefslogtreecommitdiff
path: root/tools/lldb-test/lldb-test.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-01-19 10:06:29 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-01-19 10:06:29 +0000
commit94994d372d014ce4c8758b9605d63fae651bd8aa (patch)
tree51c0b708bd59f205d6b35cb2a8c24d62f0c33d77 /tools/lldb-test/lldb-test.cpp
parent39be7ce23363d12ae3e49aeb1fdb2bfeb892e836 (diff)
Notes
Diffstat (limited to 'tools/lldb-test/lldb-test.cpp')
-rw-r--r--tools/lldb-test/lldb-test.cpp290
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();