Skip to content

Conversation

@aganea
Copy link
Member

@aganea aganea commented Sep 21, 2025

This used to happen in the global destruction, after main() has exited. Previously, we were re-creating the llvm::TimerGlobals object at this point.

image

@llvmbot
Copy link
Member

llvmbot commented Sep 21, 2025

@llvm/pr-subscribers-llvm-support

Author: Alexandre Ganea (aganea)

Changes

This used to happen in the global destruction, after main() has exited. Previously, we were re-creating the llvm::TimerGlobals object at this point.

<img width="855" height="270" alt="image" src="https://github.com/user-attachments/assets/757e9416-a74a-406a-841e-d3e4cc6a69a1" />


Full diff: https://github.com/llvm/llvm-project/pull/159983.diff

1 Files Affected:

  • (modified) llvm/lib/Support/Timer.cpp (+19-4)
diff --git a/llvm/lib/Support/Timer.cpp b/llvm/lib/Support/Timer.cpp
index 75ec299a98376..20f51afe4ee74 100644
--- a/llvm/lib/Support/Timer.cpp
+++ b/llvm/lib/Support/Timer.cpp
@@ -57,6 +57,7 @@ static SignpostEmitter &signposts();
 static sys::SmartMutex<true> &timerLock();
 static TimerGroup &defaultTimerGroup();
 static Name2PairMap &namedGroupedTimers();
+static bool isTimerGlobalsConstructed();
 
 //===----------------------------------------------------------------------===//
 //
@@ -305,14 +306,24 @@ TimerGroup::~TimerGroup() {
     PrintQueuedTimers(*OutStream);
   }
 
+  auto unlink = [&]() {
+    *Prev = Next;
+    if (Next)
+      Next->Prev = Prev;
+  };
+
+  // If the managed instance is already dead, it means we're in the CRT
+  // destruction, so no need to lock.
+  if (!isTimerGlobalsConstructed()) {
+    unlink();
+    return;
+  }
+
   // Remove the group from the TimerGroupList.
   sys::SmartScopedLock<true> L(timerLock());
-  *Prev = Next;
-  if (Next)
-    Next->Prev = Prev;
+  unlink();
 }
 
-
 void TimerGroup::removeTimer(Timer &T) {
   sys::SmartScopedLock<true> L(timerLock());
 
@@ -557,3 +568,7 @@ void TimerGroup::constructForStatistics() {
 }
 
 void *TimerGroup::acquireTimerGlobals() { return ManagedTimerGlobals.claim(); }
+
+static bool isTimerGlobalsConstructed() {
+  return ManagedTimerGlobals.isConstructed();
+}

Next->Prev = Prev;
};

// If the managed instance is already dead, it means we're in the CRT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused by this comment.

llvm::TimerGroup::acquireTimerGlobals() frees ManagedStatic. Is the leak due to timerLock() re-constructing the ManagedStatic?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will clarify the comment.

@aganea aganea merged commit e52792e into llvm:main Sep 22, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants