]> granicus.if.org Git - clang/commitdiff
When the compiler crashes, the compiler driver now produces diagnostic
authorChad Rosier <mcrosier@apple.com>
Tue, 2 Aug 2011 17:58:04 +0000 (17:58 +0000)
committerChad Rosier <mcrosier@apple.com>
Tue, 2 Aug 2011 17:58:04 +0000 (17:58 +0000)
information including the fully preprocessed source file(s) and command line
arguments.  The developer is asked to attach this diagnostic information to a
bug report.
rdar://9575623

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@136702 91177308-0d34-0410-b5e6-96231b3b80d8

13 files changed:
CMakeLists.txt
include/clang/Basic/DiagnosticDriverKinds.td
include/clang/Config/config.h.cmake
include/clang/Driver/ArgList.h
include/clang/Driver/Compilation.h
include/clang/Driver/Driver.h
include/clang/Driver/Job.h
lib/Driver/ArgList.cpp
lib/Driver/Compilation.cpp
lib/Driver/Driver.cpp
lib/Driver/Job.cpp
lib/Driver/Tools.cpp
tools/driver/driver.cpp

index 3ad60eaff516c37e28f4263cb5d5aac9c75b40be..404b5988bbceed15415600fd6d2eab0564d426be 100644 (file)
@@ -272,3 +272,6 @@ if( CLANG_BUILT_STANDALONE AND MSVC_VERSION EQUAL 1600 )
     file(APPEND "${CLANG_SLN_FILENAME}" "\n# This should be regenerated!\n")
   endif()
 endif()
+
+set(BUG_REPORT_URL "http://llvm.org" CACHE STRING
+  "Default URL where bug reports are to be submitted.")
index e33b67ef7a0748464708eaa1d66e85f3c4964a58..f76af053d97585170b54df3b5db455b2804e16d0 100644 (file)
@@ -123,4 +123,7 @@ def warn_drv_objc_gc_unsupported : Warning<
 def warn_drv_pch_not_first_include : Warning<
   "precompiled header '%0' was ignored because '%1' is not first '-include'">;
 
+def note_drv_command_failed_diag_msg : Note<
+  "diagnostic msg: %0">;
+
 }
index 5f13d2faa3119e2086b23f3cae8398b4b1d52251..6ca625d1d64373c319623c6fecadcd4798ae1f7a 100644 (file)
@@ -13,5 +13,8 @@
 /* Directory with the libstdc++ headers. */
 #define CXX_INCLUDE_ROOT "${CXX_INCLUDE_ROOT}"
 
-/* Directories clang will search for headers */
+/* Directories clang will search for headers. */
 #define C_INCLUDE_DIRS "${C_INCLUDE_DIRS}"
+
+/* Define default bug reporting URL. */
+#cmakedefine BUG_REPORT_URL "${BUG_REPORT_URL}"
index 617561b0f6d57a500f05b9532c744212d2515604..b31eea5b5b886a1535c12fd05d5eebdadac79584 100644 (file)
@@ -149,6 +149,13 @@ namespace driver {
       return arg_iterator(Args.end(), *this);
     }
 
+    /// @}
+    /// @name Arg Removal
+    /// @{
+
+    /// eraseArg - Remove any option matching \arg Id.
+    void eraseArg(OptSpecifier Id);
+
     /// @}
     /// @name Arg Access
     /// @{
@@ -242,6 +249,10 @@ namespace driver {
     /// option id.
     void ClaimAllArgs(OptSpecifier Id0) const;
 
+    /// ClaimAllArgs - Claim all arguments.
+    ///
+    void ClaimAllArgs() const;
+
     /// @}
     /// @name Arg Synthesis
     /// @{
index 4f69d0a4203cbb402e20fa4363c861c449915ee1..8c9990909ebb006b4db113382fb5e9296cc3aa75 100644 (file)
@@ -13,6 +13,7 @@
 #include "clang/Driver/Job.h"
 #include "clang/Driver/Util.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/Path.h"
 
 namespace clang {
 namespace driver {
@@ -55,6 +56,9 @@ class Compilation {
   /// Result files which should be removed on failure.
   ArgStringList ResultFiles;
 
+  /// Redirection for stdout, stderr, etc.
+  const llvm::sys::Path **Redirects;
+
 public:
   Compilation(const Driver &D, const ToolChain &DefaultToolChain,
               InputArgList *Args, DerivedArgList *TranslatedArgs);
@@ -131,6 +135,11 @@ public:
   /// Command which failed.
   /// \return The accumulated result code of the job.
   int ExecuteJob(const Job &J, const Command *&FailingCommand) const;
+
+  /// initCompilationForDiagnostics - Remove stale state and suppress output
+  /// so compilation can be reexecuted to generate additional diagnostic
+  /// information (e.g., preprocessed source(s)).
+  void initCompilationForDiagnostics();
 };
 
 } // end namespace driver
index 0e5e81a7e2b3476b8da3fe37f1e055b22569706e..8a1e1cbbc09cfd65530fbaa9d627893bcb9b9212 100644 (file)
@@ -31,6 +31,7 @@ namespace driver {
   class Action;
   class Arg;
   class ArgList;
+  class Command;
   class Compilation;
   class DerivedArgList;
   class HostInfo;
@@ -134,6 +135,9 @@ public:
   /// format.
   unsigned CCLogDiagnostics : 1;
 
+  /// Whether the driver is generating diagnostics for debugging purposes.
+  unsigned CCGenDiagnostics : 1;
+
 private:
   /// Name to use when invoking gcc/g++.
   std::string CCCGenericGCCName;
@@ -268,7 +272,14 @@ public:
   /// This routine handles additional processing that must be done in addition
   /// to just running the subprocesses, for example reporting errors, removing
   /// temporary files, etc.
-  int ExecuteCompilation(const Compilation &C) const;
+  int ExecuteCompilation(const Compilation &C,
+                         const Command *&FailingCommand) const;
+  
+  /// generateCompilationDiagnostics - Generate diagnostics information 
+  /// including preprocessed source file(s).
+  /// 
+  void generateCompilationDiagnostics(Compilation &C,
+                                      const Command *FailingCommand);
 
   /// @}
   /// @name Helper Methods
index f2b6357dfbb87b0c7d727f62a4a1d6b9068a5d04..367955f59f04913ed74d49facebaa78f1d312408 100644 (file)
@@ -97,6 +97,9 @@ public:
   /// Add a job to the list (taking ownership).
   void addJob(Job *J) { Jobs.push_back(J); }
 
+  /// Clear the job list.
+  void clear();
+
   const list_type &getJobs() const { return Jobs; }
 
   size_type size() const { return Jobs.size(); }
index 86da885a4097a1ddf9b506c738a7cae68dddfd66..e5341884eecfb3eef9ae1c5ff63da560f557da1d 100644 (file)
@@ -46,6 +46,16 @@ void ArgList::append(Arg *A) {
   Args.push_back(A);
 }
 
+void ArgList::eraseArg(OptSpecifier Id) {
+  for (iterator it = begin(), ie = end(); it != ie; ++it) {
+    if ((*it)->getOption().matches(Id)) {
+      Args.erase(it);
+      it = begin();
+      ie = end();
+    }
+  }
+}
+
 Arg *ArgList::getLastArgNoClaim(OptSpecifier Id) const {
   // FIXME: Make search efficient?
   for (const_reverse_iterator it = rbegin(), ie = rend(); it != ie; ++it)
@@ -192,6 +202,12 @@ void ArgList::ClaimAllArgs(OptSpecifier Id0) const {
     (*it)->claim();
 }
 
+void ArgList::ClaimAllArgs() const {
+  for (const_iterator it = begin(), ie = end(); it != ie; ++it)
+    if (!(*it)->isClaimed())
+      (*it)->claim();
+}
+
 const char *ArgList::MakeArgString(const Twine &T) const {
   llvm::SmallString<256> Str;
   T.toVector(Str);
index 47ac1756c81ee6913bb69a956c3dd10c376c98bb..85a5fc933044c306f0711ca4e4f6d7037032591e 100644 (file)
@@ -16,6 +16,7 @@
 #include "clang/Driver/Options.h"
 #include "clang/Driver/ToolChain.h"
 
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/Program.h"
 #include <sys/stat.h>
@@ -27,7 +28,7 @@ using namespace clang;
 Compilation::Compilation(const Driver &D, const ToolChain &_DefaultToolChain,
                          InputArgList *_Args, DerivedArgList *_TranslatedArgs)
   : TheDriver(D), DefaultToolChain(_DefaultToolChain), Args(_Args),
-    TranslatedArgs(_TranslatedArgs) {
+    TranslatedArgs(_TranslatedArgs), Redirects(0) {
 }
 
 Compilation::~Compilation() {
@@ -45,6 +46,13 @@ Compilation::~Compilation() {
   for (ActionList::iterator it = Actions.begin(), ie = Actions.end();
        it != ie; ++it)
     delete *it;
+
+  // Free redirections of stdout/stderr.
+  if (Redirects) {
+    delete Redirects[1];
+    delete Redirects[2];
+    delete [] Redirects;
+  }
 }
 
 const DerivedArgList &Compilation::getArgsForToolChain(const ToolChain *TC,
@@ -137,8 +145,8 @@ int Compilation::ExecuteCommand(const Command &C,
   std::copy(C.getArguments().begin(), C.getArguments().end(), Argv+1);
   Argv[C.getArguments().size() + 1] = 0;
 
-  if (getDriver().CCCEcho || getDriver().CCPrintOptions ||
-      getArgs().hasArg(options::OPT_v)) {
+  if ((getDriver().CCCEcho || getDriver().CCPrintOptions ||
+       getArgs().hasArg(options::OPT_v)) && !getDriver().CCGenDiagnostics) {
     raw_ostream *OS = &llvm::errs();
 
     // Follow gcc implementation of CC_PRINT_OPTIONS; we could also cache the
@@ -169,7 +177,7 @@ int Compilation::ExecuteCommand(const Command &C,
   std::string Error;
   int Res =
     llvm::sys::Program::ExecuteAndWait(Prog, Argv,
-                                       /*env*/0, /*redirects*/0,
+                                       /*env*/0, Redirects,
                                        /*secondsToWait*/0, /*memoryLimit*/0,
                                        &Error);
   if (!Error.empty()) {
@@ -197,3 +205,24 @@ int Compilation::ExecuteJob(const Job &J,
     return 0;
   }
 }
+
+void Compilation::initCompilationForDiagnostics(void) {
+  // Free actions and jobs.
+  DeleteContainerPointers(Actions);
+  Jobs.clear();
+
+  // Clear temporary/results file lists.
+  TempFiles.clear();
+  ResultFiles.clear();
+
+  // Remove any user specified output.  Claim any unclaimed arguments, so as
+  // to avoid emitting warnings about unused args.
+  if (TranslatedArgs->hasArg(options::OPT_o))
+    TranslatedArgs->eraseArg(options::OPT_o);
+  TranslatedArgs->ClaimAllArgs();
+
+  // Redirect stdout/stderr to /dev/null.
+  Redirects = new const llvm::sys::Path*[3]();
+  Redirects[1] = new const llvm::sys::Path();
+  Redirects[2] = new const llvm::sys::Path();
+}
index ac5cedf7719e77a65672bed7970279f5db355374..fc2ec673e3fd0dc672e5153f05557555daf0dd47 100644 (file)
@@ -60,9 +60,9 @@ Driver::Driver(StringRef ClangExecutable,
     CCLogDiagnosticsFilename(0), CCCIsCXX(false),
     CCCIsCPP(false),CCCEcho(false), CCCPrintBindings(false),
     CCPrintOptions(false), CCPrintHeaders(false), CCLogDiagnostics(false),
-    CCCGenericGCCName(""), CheckInputsExist(true), CCCUseClang(true),
-    CCCUseClangCXX(true), CCCUseClangCPP(true), CCCUsePCH(true),
-    SuppressMissingInputWarning(false) {
+    CCGenDiagnostics(false), CCCGenericGCCName(""), CheckInputsExist(true),
+    CCCUseClang(true), CCCUseClangCXX(true), CCCUseClangCPP(true),
+    CCCUsePCH(true), SuppressMissingInputWarning(false) {
   if (IsProduction) {
     // In a "production" build, only use clang on architectures we expect to
     // work, and don't use clang C++.
@@ -367,7 +367,63 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
   return C;
 }
 
-int Driver::ExecuteCompilation(const Compilation &C) const {
+// When clang crashes, produce diagnostic information including the fully 
+// preprocessed source file(s).  Request that the developer attach the 
+// diagnostic information to a bug report.
+void Driver::generateCompilationDiagnostics(Compilation &C,
+                                            const Command *FailingCommand) {
+  Diag(clang::diag::note_drv_command_failed_diag_msg)
+    << "Please submit a bug report to " BUG_REPORT_URL " and include command"
+    " line arguments and all diagnostic information.";
+
+  // Suppress driver output and emit preprocessor output to temp file.
+  CCCIsCPP = true;
+  CCGenDiagnostics = true;
+
+  // Clear stale state and suppress tool output.
+  C.initCompilationForDiagnostics();
+
+  // Construct the list of abstract actions to perform for this compilation.
+  Diags.Reset();
+  if (Host->useDriverDriver())
+    BuildUniversalActions(C.getDefaultToolChain(), C.getArgs(),
+                          C.getActions());
+  else
+    BuildActions(C.getDefaultToolChain(), C.getArgs(), C.getActions());
+
+  BuildJobs(C);
+
+  // If there were errors building the compilation, quit now.
+  if (Diags.hasErrorOccurred()) {
+    Diag(clang::diag::note_drv_command_failed_diag_msg)
+      << "Error generating preprocessed source(s).";
+    return;
+  }
+
+  // Generate preprocessed output.
+  FailingCommand = 0;
+  int Res = C.ExecuteJob(C.getJobs(), FailingCommand);
+
+  // If the command succeeded, we are done.
+  if (Res == 0) {
+    Diag(clang::diag::note_drv_command_failed_diag_msg)
+      << "Preprocessed source(s) are located at:";
+    ArgStringList Files = C.getTempFiles();
+    for (ArgStringList::const_iterator it = Files.begin(), ie = Files.end(); 
+         it != ie; ++it)
+      Diag(clang::diag::note_drv_command_failed_diag_msg) << *it;
+  } else {
+    // Failure, remove preprocessed files.
+    if (!C.getArgs().hasArg(options::OPT_save_temps))
+      C.CleanupFileList(C.getTempFiles(), true);
+
+    Diag(clang::diag::note_drv_command_failed_diag_msg)
+      << "Error generating preprocessed source(s).";
+  }
+}
+
+int Driver::ExecuteCompilation(const Compilation &C,
+                               const Command *&FailingCommand) const {
   // Just print if -### was present.
   if (C.getArgs().hasArg(options::OPT__HASH_HASH_HASH)) {
     C.PrintJob(llvm::errs(), C.getJobs(), "\n", true);
@@ -375,10 +431,9 @@ int Driver::ExecuteCompilation(const Compilation &C) const {
   }
 
   // If there were errors building the compilation, quit now.
-  if (getDiags().hasErrorOccurred())
+  if (Diags.hasErrorOccurred())
     return 1;
 
-  const Command *FailingCommand = 0;
   int Res = C.ExecuteJob(C.getJobs(), FailingCommand);
 
   // Remove temp files.
@@ -1226,7 +1281,7 @@ void Driver::BuildJobsForAction(Compilation &C,
                        A->getType(), BaseInput);
   }
 
-  if (CCCPrintBindings) {
+  if (CCCPrintBindings && !CCGenDiagnostics) {
     llvm::errs() << "# \"" << T.getToolChain().getTripleString() << '"'
                  << " - \"" << T.getName() << "\", inputs: [";
     for (unsigned i = 0, e = InputInfos.size(); i != e; ++i) {
@@ -1253,11 +1308,12 @@ const char *Driver::GetNamedOutputPath(Compilation &C,
   }
 
   // Default to writing to stdout?
-  if (AtTopLevel && isa<PreprocessJobAction>(JA))
+  if (AtTopLevel && isa<PreprocessJobAction>(JA) && !CCGenDiagnostics)
     return "-";
 
   // Output to a temporary file?
-  if (!AtTopLevel && !C.getArgs().hasArg(options::OPT_save_temps)) {
+  if ((!AtTopLevel && !C.getArgs().hasArg(options::OPT_save_temps)) ||
+      CCGenDiagnostics) {
     std::string TmpName =
       GetTemporaryPath(types::getTypeTempSuffix(JA.getType()));
     return C.addTempFile(C.getArgs().MakeArgString(TmpName.c_str()));
index 51055e93f55898452b4debeb82fd9bb9938023eb..5443d70e825c124815b71873ebb5ec8ad0e3bbaf 100644 (file)
@@ -9,6 +9,8 @@
 
 #include "clang/Driver/Job.h"
 
+#include "llvm/ADT/STLExtras.h"
+
 #include <cassert>
 using namespace clang::driver;
 
@@ -28,6 +30,10 @@ JobList::~JobList() {
     delete *it;
 }
 
+void JobList::clear() {
+  DeleteContainerPointers(Jobs);
+}
+
 void Job::addCommand(Command *C) {
   cast<JobList>(this)->addJob(C);
 }
index 9748da449c373a67bafe9362ccb713b780427a9d..43c3fbd810bff989b421cb3b617887e57b626c68 100644 (file)
@@ -1378,7 +1378,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
 
   Args.AddAllArgs(CmdArgs, options::OPT_v);
   Args.AddLastArg(CmdArgs, options::OPT_H);
-  if (D.CCPrintHeaders) {
+  if (D.CCPrintHeaders && !D.CCGenDiagnostics) {
     CmdArgs.push_back("-header-include-file");
     CmdArgs.push_back(D.CCPrintHeadersFilename ?
                       D.CCPrintHeadersFilename : "-");
@@ -1386,7 +1386,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
   Args.AddLastArg(CmdArgs, options::OPT_P);
   Args.AddLastArg(CmdArgs, options::OPT_print_ivar_layout);
 
-  if (D.CCLogDiagnostics) {
+  if (D.CCLogDiagnostics && !D.CCGenDiagnostics) {
     CmdArgs.push_back("-diagnostic-log-file");
     CmdArgs.push_back(D.CCLogDiagnosticsFilename ?
                       D.CCLogDiagnosticsFilename : "-");
index 096260c421499dbd9e6042c30d88bd0d6e5c6f56..3ae1487118581aac3fbfb69f0500f1af5c7d815c 100644 (file)
@@ -458,9 +458,15 @@ int main(int argc_, const char **argv_) {
 
   llvm::OwningPtr<Compilation> C(TheDriver.BuildCompilation(argv));
   int Res = 0;
+  const Command *FailingCommand = 0;
   if (C.get())
-    Res = TheDriver.ExecuteCompilation(*C);
-  
+    Res = TheDriver.ExecuteCompilation(*C, FailingCommand);
+
+  // If result status is < 0, then the driver command signalled an error.
+  // In this case, generate additional diagnostic information if possible.
+  if (Res < 0)
+    TheDriver.generateCompilationDiagnostics(*C, FailingCommand);
+
   // If any timers were active but haven't been destroyed yet, print their
   // results now.  This happens in -disable-free mode.
   llvm::TimerGroup::printAll(llvm::errs());