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
diff --git a/lldb/tools/lldb-test/lldb-test.cpp b/lldb/tools/lldb-test/lldb-test.cpp
index c37d56a..223ee6b 100644
--- a/lldb/tools/lldb-test/lldb-test.cpp
+++ b/lldb/tools/lldb-test/lldb-test.cpp
@@ -148,6 +148,10 @@
 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 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 @@
 
 } // 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 @@
     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 @@
     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::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 @@
   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 @@
 
     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 @@
   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 @@
       HadErrors = 1;
       continue;
     }
-    
+
     if (Error E = Action(*ModulePtr)) {
       WithColor::error() << toString(std::move(E)) << "\n";
       HadErrors = 1;