[Clang][Driver] In -fintegrated-cc1 mode, avoid crashing on exit after a compiler...
authorAlexandre Ganea <alexandre.ganea@ubisoft.com>
Fri, 13 Mar 2020 12:15:20 +0000 (08:15 -0400)
committerAlexandre Ganea <alexandre.ganea@ubisoft.com>
Fri, 13 Mar 2020 12:15:35 +0000 (08:15 -0400)
After a crash catched by the CrashRecoveryContext, this patch prevents from accessing dangling pointers in TimerGroup structures before the clang tool exits. Previously, the default TimerGroup had internal linked lists which were still pointing to old Timer or TimerGroup instances, which lived in stack frames released by the CrashRecoveryContext.

Fixes PR45164.

Differential Revision: https://reviews.llvm.org/D76099

clang/lib/Lex/Pragma.cpp
clang/tools/driver/driver.cpp
llvm/include/llvm/Support/ManagedStatic.h
llvm/include/llvm/Support/Timer.h
llvm/lib/Support/Timer.cpp

index a8cd18b..57a9581 100644 (file)
@@ -42,6 +42,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Timer.h"
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
@@ -1038,6 +1039,8 @@ struct PragmaDebugHandler : public PragmaHandler {
       if (!PP.getPreprocessorOpts().DisablePragmaDebugCrash)
         llvm_unreachable("This is an assertion!");
     } else if (II->isStr("crash")) {
+      llvm::Timer T("crash", "pragma crash");
+      llvm::TimeRegion R(&T);
       if (!PP.getPreprocessorOpts().DisablePragmaDebugCrash)
         LLVM_BUILTIN_TRAP;
     } else if (II->isStr("parser_crash")) {
index 843fa4b..c597521 100644 (file)
@@ -30,6 +30,7 @@
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/OptTable.h"
 #include "llvm/Option/Option.h"
+#include "llvm/Support/BuryPointer.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/CrashRecoveryContext.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -491,6 +492,7 @@ int main(int argc_, const char **argv_) {
 
   std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(argv));
   int Res = 1;
+  bool IsCrash = false;
   if (C && !C->containsError()) {
     SmallVector<std::pair<int, const Command *>, 4> FailingCommands;
     Res = TheDriver.ExecuteCompilation(*C, FailingCommands);
@@ -517,11 +519,11 @@ int main(int argc_, const char **argv_) {
       // If result status is 70, then the driver command reported a fatal error.
       // On Windows, abort will return an exit code of 3.  In these cases,
       // generate additional diagnostic information if possible.
-      bool DiagnoseCrash = CommandRes < 0 || CommandRes == 70;
+      IsCrash = CommandRes < 0 || CommandRes == 70;
 #ifdef _WIN32
-      DiagnoseCrash |= CommandRes == 3;
+      IsCrash |= CommandRes == 3;
 #endif
-      if (DiagnoseCrash) {
+      if (IsCrash) {
         TheDriver.generateCompilationDiagnostics(*C, *FailingCommand);
         break;
       }
@@ -530,10 +532,16 @@ int main(int argc_, const char **argv_) {
 
   Diags.getClient()->finish();
 
-  // 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());
-  llvm::TimerGroup::clearAll();
+  if (!UseNewCC1Process && IsCrash) {
+    // When crashing in -fintegrated-cc1 mode, bury the timer pointers, because
+    // the internal linked list might point to already released stack frames.
+    llvm::BuryPointer(llvm::TimerGroup::aquireDefaultGroup());
+  } else {
+    // 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());
+    llvm::TimerGroup::clearAll();
+  }
 
 #ifdef _WIN32
   // Exit status should not be negative on Win32, unless abnormal termination.
index e65bb05..bbd0d04 100644 (file)
@@ -102,6 +102,12 @@ public:
   }
 
   const C *operator->() const { return &**this; }
+
+  // Extract the instance, leaving the ManagedStatic uninitialized. The
+  // user is then responsible for the lifetime of the returned instance.
+  C *claim() {
+    return static_cast<C *>(Ptr.exchange(nullptr));
+  }
 };
 
 /// llvm_shutdown - Deallocate and destroy all ManagedStatic variables.
index a298ecd..045ac44 100644 (file)
@@ -230,6 +230,11 @@ public:
   /// used by the Statistic code to influence the construction and destruction
   /// order of the global timer lists.
   static void ConstructTimerLists();
+
+  /// This makes the default group unmanaged, and lets the user manage the
+  /// group's lifetime.
+  static std::unique_ptr<TimerGroup> aquireDefaultGroup();
+
 private:
   friend class Timer;
   friend void PrintStatisticsJSON(raw_ostream &OS);
index dcb96d1..c97538c 100644 (file)
@@ -442,3 +442,7 @@ const char *TimerGroup::printAllJSONValues(raw_ostream &OS, const char *delim) {
 void TimerGroup::ConstructTimerLists() {
   (void)*NamedGroupedTimers;
 }
+
+std::unique_ptr<TimerGroup> TimerGroup::aquireDefaultGroup() {
+  return std::unique_ptr<TimerGroup>(DefaultTimerGroup.claim());
+}