lldb-test symbols: Add -file argument and the ability to dump global variables in a file

The motivation for this is to be able to Dwarf index ability to look up
variables within a given compilation unit. It also fits in with the
patch in progress at D47939, which will add the ability to look up
funtions using file+line pairs.

The verification of which lldb-test options can be used together was
getting a bit unwieldy, so I moved the logic out into a separate
function.

llvm-svn: 334498
This commit is contained in:
Pavel Labath 2018-06-12 12:57:36 +00:00
parent 3d671248ab
commit e6954cb2a1
4 changed files with 128 additions and 75 deletions

View File

@ -0,0 +1,3 @@
namespace two {
int foo;
}

View File

@ -0,0 +1,20 @@
// REQUIRES: lld
// RUN: clang -g -c -o %t-1.o --target=x86_64-pc-linux %s
// RUN: clang -g -c -o %t-2.o --target=x86_64-pc-linux %S/Inputs/find-variable-file-2.cpp
// RUN: ld.lld %t-1.o %t-2.o -o %t
// RUN: lldb-test symbols --file=find-variable-file.cpp --find=variable %t | \
// RUN: FileCheck --check-prefix=ONE %s
// RUN: lldb-test symbols --file=find-variable-file-2.cpp --find=variable %t | \
// RUN: FileCheck --check-prefix=TWO %s
// ONE: Found 1 variables:
namespace one {
int foo;
// ONE-DAG: name = "foo", type = {{.*}} (int), {{.*}} decl = find-variable-file.cpp:[[@LINE-1]]
} // namespace one
extern "C" void _start() {}
// TWO: Found 1 variables:
// TWO-DAG: name = "foo", {{.*}} decl = find-variable-file-2.cpp:2

View File

@ -28,6 +28,8 @@ config.test_format = lit.formats.ShTest(execute_external)
# suffixes: We only support unit tests
config.suffixes = []
config.excludes = ['Inputs']
# test_source_root: The root path where tests are located.
config.test_source_root = os.path.dirname(__file__)

View File

@ -148,6 +148,10 @@ static FunctionNameType getFunctionNameFlags() {
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 Expected<CompilerDeclContext> getDeclContext(SymbolVendor &Vendor);
static Error findFunctions(lldb_private::Module &Module);
@ -157,6 +161,7 @@ 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);
}
@ -197,6 +202,13 @@ int evaluateMemoryMapCommands(Debugger &Dbg);
} // namespace opts
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 =
@ -312,14 +324,10 @@ opts::symbols::getDeclContext(SymbolVendor &Vendor) {
return CompilerDeclContext();
VariableList List;
Vendor.FindGlobalVariables(ConstString(Context), nullptr, UINT32_MAX, List);
if (List.Empty()) {
return make_error<StringError>("Context search didn't find a match.",
inconvertibleErrorCode());
}
if (List.GetSize() > 1) {
return make_error<StringError>("Context search found multiple matches.",
inconvertibleErrorCode());
}
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();
}
@ -394,6 +402,22 @@ Error opts::symbols::findVariables(lldb_private::Module &Module) {
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)
@ -419,15 +443,11 @@ Error opts::symbols::dumpModule(lldb_private::Module &Module) {
}
Error opts::symbols::verify(lldb_private::Module &Module) {
SymbolVendor *plugin = Module.GetSymbolVendor();
if (!plugin)
return make_error<StringError>("Can't get a symbol vendor.",
inconvertibleErrorCode());
SymbolVendor &plugin = *Module.GetSymbolVendor();
SymbolFile *symfile = plugin->GetSymbolFile();
SymbolFile *symfile = plugin.GetSymbolFile();
if (!symfile)
return make_error<StringError>("Can't get a symbol file.",
inconvertibleErrorCode());
return make_string_error("Module has no symbol file.");
uint32_t comp_units_count = symfile->GetNumCompileUnits();
@ -436,17 +456,14 @@ Error opts::symbols::verify(lldb_private::Module &Module) {
for (uint32_t i = 0; i < comp_units_count; i++) {
lldb::CompUnitSP comp_unit = symfile->ParseCompileUnitAtIndex(i);
if (!comp_unit)
return make_error<StringError>("Can't get a compile unit.",
inconvertibleErrorCode());
return make_string_error("Connot parse compile unit {0}.", i);
outs() << "Processing '" << comp_unit->GetFilename().AsCString() <<
"' compile unit.\n";
LineTable *lt = comp_unit->GetLineTable();
if (!lt)
return make_error<StringError>(
"Can't get a line table of a compile unit.",
inconvertibleErrorCode());
return make_string_error("Can't get a line table of a compile unit.");
uint32_t count = lt->GetSize();
@ -457,23 +474,18 @@ Error opts::symbols::verify(lldb_private::Module &Module) {
LineEntry le;
if (!lt->GetLineEntryAtIndex(0, le))
return make_error<StringError>(
"Can't get a line entry of a compile unit",
inconvertibleErrorCode());
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_error<StringError>(
"Can't get a line entry of a compile unit",
inconvertibleErrorCode());
return make_string_error("Can't get a line entry of a compile unit");
if (curr_end > le.range.GetBaseAddress().GetFileAddress())
return make_error<StringError>(
"Line table of a compile unit is inconsistent",
inconvertibleErrorCode());
return make_string_error(
"Line table of a compile unit is inconsistent.");
}
}
@ -482,53 +494,69 @@ Error opts::symbols::verify(lldb_private::Module &Module) {
return Error::success();
}
int opts::symbols::dumpSymbols(Debugger &Dbg) {
if (Verify && Find != FindType::None) {
WithColor::error() << "Cannot both search and verify symbol information.\n";
return 1;
}
if (Find != FindType::None && Regex && !Context.empty()) {
WithColor::error()
<< "Cannot search using both regular expressions and context.\n";
return 1;
}
if ((Find == FindType::Type || Find == FindType::Namespace) && Regex) {
WithColor::error() << "Cannot search for types and namespaces using "
"regular expressions.\n";
return 1;
}
if (Find == FindType::Function && Regex && getFunctionNameFlags() != 0) {
WithColor::error() << "Cannot search for types using both regular "
"expressions and function-flags.\n";
return 1;
}
if (Regex && !RegularExpression(Name).IsValid()) {
WithColor::error() << "`" << Name
<< "` is not a valid regular expression.\n";
return 1;
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())
return make_string_error("-regex, -context, -name and -file options are not "
"applicable for symbol verification.");
return verify;
}
Error (*Action)(lldb_private::Module &);
if (!Verify) {
switch (Find) {
case FindType::Function:
Action = findFunctions;
break;
case FindType::Namespace:
Action = findNamespaces;
break;
case FindType::Type:
Action = findTypes;
break;
case FindType::Variable:
Action = findVariables;
break;
case FindType::None:
Action = dumpModule;
break;
}
} else
Action = 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())
return make_string_error(
"Specify search type (-find) to use search options.");
return dumpModule;
case FindType::Function:
if (!File.empty())
return make_string_error("Cannot search for functions by file name.");
if (Regex && getFunctionNameFlags() != 0)
return make_string_error("Cannot search for functions using both regular "
"expressions and function-flags.");
return findFunctions;
case FindType::Namespace:
if (Regex || !File.empty())
return make_string_error("Cannot search for namespaces using regular "
"expressions or file names.");
return findNamespaces;
case FindType::Type:
if (Regex || !File.empty())
return make_string_error("Cannot search for types using regular "
"expressions or file names.");
return findTypes;
case FindType::Variable:
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) {
@ -543,7 +571,7 @@ int opts::symbols::dumpSymbols(Debugger &Dbg) {
HadErrors = 1;
continue;
}
if (Error E = Action(*ModulePtr)) {
WithColor::error() << toString(std::move(E)) << "\n";
HadErrors = 1;