This is lead up to making crash reports more useful when modules are
being used. Since module imports are left unchanged by the preprocessor,
these reports aren't really useful for reproducing problems today.

The attached patches add a -cc1 flag to dump module dependencies to a
directory, and then to create a VFS mapping file so that these headers
can be used with -ivfsoverlay. Later, I plan on hooking the flag up in
generateCompilationDiagnostics.

No tests included. I'll spin some up before I commit, but I thought I
might as well send this to be reviewed in the mean time.

>From edbc97a4dafc7df6d7f342e26d2cbb9d982f9dbd Mon Sep 17 00:00:00 2001
From: Justin Bogner <[email protected]>
Date: Mon, 12 May 2014 23:16:57 -0700
Subject: [PATCH 1/2] Frontend: Add a CC1 flag to dump module dependencies to a
 directory

This adds the -module-dependency-dir to clang -cc1, which specifies a
directory to copy all of a module's dependencies into in a form
suitable to be used as a VFS. This is useful for crashdumps that
involve modules, so that the module dependencies will be intact when a
crash report script is used to reproduce a problem on another machine.
---
 include/clang/Driver/Options.td                  |  2 +
 include/clang/Frontend/CompilerInstance.h        |  7 +++
 include/clang/Frontend/DependencyOutputOptions.h |  5 +-
 include/clang/Frontend/Utils.h                   | 19 ++++++++
 lib/Frontend/CMakeLists.txt                      |  1 +
 lib/Frontend/CompilerInstance.cpp                | 26 ++++++++++
 lib/Frontend/CompilerInvocation.cpp              |  2 +
 lib/Frontend/ModuleDependencyCollector.cpp       | 62 ++++++++++++++++++++++++
 8 files changed, 123 insertions(+), 1 deletion(-)
 create mode 100644 lib/Frontend/ModuleDependencyCollector.cpp

diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
index 85f2e0c..dd8722e 100644
--- a/include/clang/Driver/Options.td
+++ b/include/clang/Driver/Options.td
@@ -342,6 +342,8 @@ def dependency_file : Separate<["-"], "dependency-file">, Flags<[CC1Option]>,
   HelpText<"Filename (or -) to write dependency output to">;
 def dependency_dot : Separate<["-"], "dependency-dot">, Flags<[CC1Option]>,
   HelpText<"Filename to write DOT-formatted header dependencies to">;
+def module_dependency_dir : Separate<["-"], "module-dependency-dir">,
+  Flags<[CC1Option]>, HelpText<"Directory to dump module dependencies to">;
 def dumpmachine : Flag<["-"], "dumpmachine">;
 def dumpspecs : Flag<["-"], "dumpspecs">, Flags<[Unsupported]>;
 def dumpversion : Flag<["-"], "dumpversion">;
diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h
index b144069..d72f904 100644
--- a/include/clang/Frontend/CompilerInstance.h
+++ b/include/clang/Frontend/CompilerInstance.h
@@ -105,6 +105,9 @@ class CompilerInstance : public ModuleLoader {
   /// \brief The ASTReader, if one exists.
   IntrusiveRefCntPtr<ASTReader> ModuleManager;
 
+  /// \brief The module dependency collector for crashdumps
+  std::shared_ptr<ModuleDependencyCollector> ModuleDepCollector;
+
   /// \brief The dependency file generator.
   std::unique_ptr<DependencyFileGenerator> TheDependencyFileGenerator;
 
@@ -464,6 +467,10 @@ public:
   IntrusiveRefCntPtr<ASTReader> getModuleManager() const;
   void setModuleManager(IntrusiveRefCntPtr<ASTReader> Reader);
 
+  std::shared_ptr<ModuleDependencyCollector> getModuleDepCollector() const;
+  void setModuleDepCollector(
+      std::shared_ptr<ModuleDependencyCollector> Collector);
+
   /// }
   /// @name Code Completion
   /// {
diff --git a/include/clang/Frontend/DependencyOutputOptions.h b/include/clang/Frontend/DependencyOutputOptions.h
index d275249..5da1459 100644
--- a/include/clang/Frontend/DependencyOutputOptions.h
+++ b/include/clang/Frontend/DependencyOutputOptions.h
@@ -43,7 +43,10 @@ public:
 
   /// \brief The file to write GraphViz-formatted header dependencies to.
   std::string DOTOutputFile;
-  
+
+  /// \brief The directory to copy module dependencies to when collecting them.
+  std::string ModuleDependencyOutputDir;
+
 public:
   DependencyOutputOptions() {
     IncludeSystemHeaders = 0;
diff --git a/include/clang/Frontend/Utils.h b/include/clang/Frontend/Utils.h
index 8fb536f..3a1338a 100644
--- a/include/clang/Frontend/Utils.h
+++ b/include/clang/Frontend/Utils.h
@@ -17,6 +17,7 @@
 #include "clang/Basic/Diagnostic.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/Option/OptSpecifier.h"
 
 namespace llvm {
@@ -80,6 +81,24 @@ public:
   void AttachToASTReader(ASTReader &R);
 };
 
+/// Collects the dependencies for imported modules into a directory.  Users
+/// should attach to the AST reader whenever a module is loaded.
+class ModuleDependencyCollector {
+  StringRef DestDir;
+  bool HasErrors;
+  llvm::StringSet<> Seen;
+
+public:
+  StringRef getDest() { return DestDir; }
+  bool insertSeen(StringRef Filename) { return Seen.insert(Filename); }
+  void reportError() { HasErrors = true; }
+
+  void attachToASTReader(ASTReader &R);
+  bool hasErrors() { return HasErrors; }
+  ModuleDependencyCollector(StringRef DestDir)
+      : DestDir(DestDir), HasErrors(false) {}
+};
+
 /// AttachDependencyGraphGen - Create a dependency graph generator, and attach
 /// it to the given preprocessor.
   void AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile,
diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt
index b67e0ae..3fa7a2c 100644
--- a/lib/Frontend/CMakeLists.txt
+++ b/lib/Frontend/CMakeLists.txt
@@ -25,6 +25,7 @@ add_clang_library(clangFrontend
   LangStandards.cpp
   LayoutOverrideSource.cpp
   LogDiagnosticPrinter.cpp
+  ModuleDependencyCollector.cpp
   MultiplexConsumer.cpp
   PrintPreprocessedOutput.cpp
   SerializedDiagnosticPrinter.cpp
diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp
index f5e2749..4e59f4d 100644
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -115,6 +115,25 @@ void CompilerInstance::setModuleManager(IntrusiveRefCntPtr<ASTReader> Reader) {
   ModuleManager = Reader;
 }
 
+std::shared_ptr<ModuleDependencyCollector>
+CompilerInstance::getModuleDepCollector() const {
+  if (ModuleDepCollector)
+    return ModuleDepCollector;
+
+  // If we don't have a collector, but we are collecting module dependencies,
+  // then we're the top level compiler instance and need to create one.
+  const DependencyOutputOptions &DepOpts = getDependencyOutputOpts();
+  if (DepOpts.ModuleDependencyOutputDir.empty())
+    return nullptr;
+  StringRef OutputDir = DepOpts.ModuleDependencyOutputDir;
+  return std::make_shared<ModuleDependencyCollector>(OutputDir);
+}
+
+void CompilerInstance::setModuleDepCollector(
+    std::shared_ptr<ModuleDependencyCollector> Collector) {
+  ModuleDepCollector = Collector;
+}
+
 // Diagnostics
 static void SetUpDiagnosticLog(DiagnosticOptions *DiagOpts,
                                const CodeGenOptions *CodeGenOpts,
@@ -850,6 +869,10 @@ static void compileModuleImpl(CompilerInstance &ImportingInstance,
   SourceMgr.pushModuleBuildStack(Module->getTopLevelModuleName(),
     FullSourceLoc(ImportLoc, ImportingInstance.getSourceManager()));
 
+  // If we're collecting module dependencies, we need to share a collector
+  // between all of the module CompilerInstances.
+  Instance.setModuleDepCollector(ImportingInstance.getModuleDepCollector());
+
   // Get or create the module map that we'll use to build this module.
   std::string InferredModuleMapContent;
   if (const FileEntry *ModuleMapFile =
@@ -1188,6 +1211,9 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
     if (TheDependencyFileGenerator)
       TheDependencyFileGenerator->AttachToASTReader(*ModuleManager);
 
+    if (ModuleDepCollector)
+      ModuleDepCollector->attachToASTReader(*ModuleManager);
+
     // Try to load the module file.
     unsigned ARRFlags = ASTReader::ARR_OutOfDate | ASTReader::ARR_Missing;
     switch (ModuleManager->ReadAST(ModuleFileName, serialization::MK_Module,
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index d8abd53..e276a65 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -589,6 +589,8 @@ static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts,
   Opts.AddMissingHeaderDeps = Args.hasArg(OPT_MG);
   Opts.PrintShowIncludes = Args.hasArg(OPT_show_includes);
   Opts.DOTOutputFile = Args.getLastArgValue(OPT_dependency_dot);
+  Opts.ModuleDependencyOutputDir =
+      Args.getLastArgValue(OPT_module_dependency_dir);
 }
 
 bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
diff --git a/lib/Frontend/ModuleDependencyCollector.cpp b/lib/Frontend/ModuleDependencyCollector.cpp
new file mode 100644
index 0000000..804fa55
--- /dev/null
+++ b/lib/Frontend/ModuleDependencyCollector.cpp
@@ -0,0 +1,62 @@
+//===--- ModuleDependencyCollector.cpp - Collect module dependencies ------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Collect the dependencies of a set of modules.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Frontend/Utils.h"
+#include "clang/Serialization/ASTReader.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/Filesystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+
+namespace {
+/// Private implementation for ModuleDependencyCollector
+class ModuleDependencyListener : public ASTReaderListener {
+  ModuleDependencyCollector &Collector;
+
+  llvm::error_code copyToRoot(StringRef Src);
+public:
+  ModuleDependencyListener(ModuleDependencyCollector &Collector)
+      : Collector(Collector) {}
+  bool needsInputFileVisitation() override { return true; }
+  bool needsSystemInputFileVisitation() override { return true; }
+  bool visitInputFile(StringRef Filename, bool IsSystem,
+                      bool IsOverridden) override;
+};
+}
+
+void ModuleDependencyCollector::attachToASTReader(ASTReader &R) {
+  R.addListener(new ModuleDependencyListener(*this));
+}
+
+llvm::error_code ModuleDependencyListener::copyToRoot(StringRef Src) {
+  using namespace llvm::sys;
+  SmallString<256> Dest = Collector.getDest();
+  path::append(Dest, path::relative_path(Src));
+  if (llvm::error_code EC = fs::create_directories(path::parent_path(Dest),
+                                                   /*IgnoreExisting=*/true))
+    return EC;
+  if (llvm::error_code EC = fs::copy_file(Src, Dest.str()))
+    return EC;
+  return llvm::error_code();
+}
+
+bool ModuleDependencyListener::visitInputFile(StringRef Filename, bool IsSystem,
+                                              bool IsOverridden) {
+  if (!Collector.insertSeen(Filename))
+    return true;
+  if (copyToRoot(Filename))
+    Collector.reportError();
+  return true;
+}
-- 
1.9.3 (Apple Git-50)

>From cf70ea5ebfcf53dffb1c71f7499dde0d0308dc71 Mon Sep 17 00:00:00 2001
From: Justin Bogner <[email protected]>
Date: Mon, 9 Jun 2014 16:00:56 -0700
Subject: [PATCH 2/2] Frontend: Include a VFS map when dumping module
 dependencies

Create a mapping from the module dependency dump directory back to the
original locations of the dumped files suitable for use with
-ivfsoverlay when trying to reproduce a build using the module
dependencies.

This currently encodes the absolute path to the dump directory, due to
limitations in the VFS system. Until we can handle relative paths in
the VFS, users of the VFS map may need to run a simple search and
replace in the file.
---
 include/clang/Frontend/Utils.h             |  7 +++++++
 lib/Frontend/ModuleDependencyCollector.cpp | 17 +++++++++++++++++
 2 files changed, 24 insertions(+)

diff --git a/include/clang/Frontend/Utils.h b/include/clang/Frontend/Utils.h
index 3a1338a..d408c0c 100644
--- a/include/clang/Frontend/Utils.h
+++ b/include/clang/Frontend/Utils.h
@@ -15,6 +15,7 @@
 #define LLVM_CLANG_FRONTEND_UTILS_H
 
 #include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/VirtualFileSystem.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
@@ -87,16 +88,22 @@ class ModuleDependencyCollector {
   StringRef DestDir;
   bool HasErrors;
   llvm::StringSet<> Seen;
+  vfs::YAMLVFSWriter VFSWriter;
 
 public:
   StringRef getDest() { return DestDir; }
   bool insertSeen(StringRef Filename) { return Seen.insert(Filename); }
   void reportError() { HasErrors = true; }
+  void addFileMapping(StringRef VPath, StringRef RPath) {
+    VFSWriter.addFileMapping(VPath, RPath);
+  }
 
   void attachToASTReader(ASTReader &R);
+  void writeFileMap();
   bool hasErrors() { return HasErrors; }
   ModuleDependencyCollector(StringRef DestDir)
       : DestDir(DestDir), HasErrors(false) {}
+  ~ModuleDependencyCollector() { writeFileMap(); }
 };
 
 /// AttachDependencyGraphGen - Create a dependency graph generator, and attach
diff --git a/lib/Frontend/ModuleDependencyCollector.cpp b/lib/Frontend/ModuleDependencyCollector.cpp
index 804fa55..1219a4f 100644
--- a/lib/Frontend/ModuleDependencyCollector.cpp
+++ b/lib/Frontend/ModuleDependencyCollector.cpp
@@ -40,6 +40,22 @@ void ModuleDependencyCollector::attachToASTReader(ASTReader &R) {
   R.addListener(new ModuleDependencyListener(*this));
 }
 
+void ModuleDependencyCollector::writeFileMap() {
+  if (Seen.empty())
+    return;
+
+  SmallString<256> Dest = getDest();
+  llvm::sys::path::append(Dest, "vfs.yaml");
+
+  std::string ErrorInfo;
+  llvm::raw_fd_ostream OS(Dest.c_str(), ErrorInfo, llvm::sys::fs::F_Text);
+  if (!ErrorInfo.empty()) {
+    reportError();
+    return;
+  }
+  VFSWriter.write(OS);
+}
+
 llvm::error_code ModuleDependencyListener::copyToRoot(StringRef Src) {
   using namespace llvm::sys;
   SmallString<256> Dest = Collector.getDest();
@@ -49,6 +65,7 @@ llvm::error_code ModuleDependencyListener::copyToRoot(StringRef Src) {
     return EC;
   if (llvm::error_code EC = fs::copy_file(Src, Dest.str()))
     return EC;
+  Collector.addFileMapping(Src, Dest.str());
   return llvm::error_code();
 }
 
-- 
1.9.3 (Apple Git-50)

_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to