Skip to content

[lldb] Make lldbassert fire only once per instance #134343

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 4, 2025

Conversation

JDevlieghere
Copy link
Member

The lldbassert macro in LLDB behaves like a regular assert when assertions are enabled, and otherwise prints a pretty backtrace and prompts the user to file a bug. By default, this is emitted as a diagnostic event, but vendors can provide their own behavior, for example pre-populating a bug report.

Recently, we ran into an issue where an lldbassert (in the Swift language plugin) would fire excessively, to the point that it was interfering with the usability of the debugger.

Once an lldbasser has fired, there's no point in bothering the user over and over again for the same problem. This PR solves the problem by introducing a static std::once_flag in the macro. This way, every lldbasser will fire at most once per lldb instance.

rdar://148520448

The `lldbassert` macro in LLDB behaves like a regular `assert` when
assertions are enabled, and otherwise prints a pretty backtrace and
prompts the user to file a bug. By default, this is emitted as a
diagnostic event, but vendors can provide their own behavior, for
example pre-populating a bug report.

Recently, we ran into an issue where an `lldbassert` (in the Swift
language plugin) would fire excessively, to the point that it was
interfering with the usability of the debugger.

Once an `lldbasser` has fired, there's no point in bothering the user
over and over again for the same problem. This PR solves the problem by
introducing a static `std::once_flag` in the macro. This way, every
`lldbasser` will fire at most once per lldb instance.

rdar://148520448
@llvmbot
Copy link
Member

llvmbot commented Apr 4, 2025

@llvm/pr-subscribers-lldb

Author: Jonas Devlieghere (JDevlieghere)

Changes

The lldbassert macro in LLDB behaves like a regular assert when assertions are enabled, and otherwise prints a pretty backtrace and prompts the user to file a bug. By default, this is emitted as a diagnostic event, but vendors can provide their own behavior, for example pre-populating a bug report.

Recently, we ran into an issue where an lldbassert (in the Swift language plugin) would fire excessively, to the point that it was interfering with the usability of the debugger.

Once an lldbasser has fired, there's no point in bothering the user over and over again for the same problem. This PR solves the problem by introducing a static std::once_flag in the macro. This way, every lldbasser will fire at most once per lldb instance.

rdar://148520448


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

2 Files Affected:

  • (modified) lldb/include/lldb/Utility/LLDBAssert.h (+8-3)
  • (modified) lldb/source/Utility/LLDBAssert.cpp (+21-16)
diff --git a/lldb/include/lldb/Utility/LLDBAssert.h b/lldb/include/lldb/Utility/LLDBAssert.h
index 21dbdb3b3202d..cee30b81402ca 100644
--- a/lldb/include/lldb/Utility/LLDBAssert.h
+++ b/lldb/include/lldb/Utility/LLDBAssert.h
@@ -10,6 +10,7 @@
 #define LLDB_UTILITY_LLDBASSERT_H
 
 #include "llvm/ADT/StringRef.h"
+#include <mutex>
 
 #ifndef NDEBUG
 #define lldbassert(x) assert(x)
@@ -19,8 +20,11 @@
 // __FILE__ but only renders the last path component (the filename) instead of
 // an invocation dependent full path to that file.
 #define lldbassert(x)                                                          \
-  lldb_private::_lldb_assert(static_cast<bool>(x), #x, __FUNCTION__,           \
-                             __FILE_NAME__, __LINE__)
+  do {                                                                         \
+    static std::once_flag _once_flag;                                          \
+    lldb_private::_lldb_assert(static_cast<bool>(x), #x, __FUNCTION__,         \
+                               __FILE_NAME__, __LINE__, _once_flag)            \
+  } while (0)
 #else
 #define lldbassert(x)                                                          \
   lldb_private::_lldb_assert(static_cast<bool>(x), #x, __FUNCTION__, __FILE__, \
@@ -33,7 +37,8 @@ namespace lldb_private {
 /// Don't use _lldb_assert directly. Use the lldbassert macro instead so that
 /// LLDB asserts become regular asserts in NDEBUG builds.
 void _lldb_assert(bool expression, const char *expr_text, const char *func,
-                  const char *file, unsigned int line);
+                  const char *file, unsigned int line,
+                  std::once_flag &once_flag);
 
 /// The default LLDB assert callback, which prints to stderr.
 typedef void (*LLDBAssertCallback)(llvm::StringRef message,
diff --git a/lldb/source/Utility/LLDBAssert.cpp b/lldb/source/Utility/LLDBAssert.cpp
index b84c581ccf822..611ad43cd071b 100644
--- a/lldb/source/Utility/LLDBAssert.cpp
+++ b/lldb/source/Utility/LLDBAssert.cpp
@@ -11,6 +11,7 @@
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/Signals.h"
 #include "llvm/Support/raw_ostream.h"
+#include <mutex>
 
 #if LLVM_SUPPORT_XCODE_SIGNPOSTS
 #include <os/log.h>
@@ -33,29 +34,33 @@ static std::atomic<LLDBAssertCallback> g_lldb_assert_callback =
     &DefaultAssertCallback;
 
 void _lldb_assert(bool expression, const char *expr_text, const char *func,
-                  const char *file, unsigned int line) {
+                  const char *file, unsigned int line,
+                  std::once_flag &once_flag) {
   if (LLVM_LIKELY(expression))
     return;
 
+  std::call_once(once_flag, [&]() {
 #if LLVM_SUPPORT_XCODE_SIGNPOSTS
-  if (__builtin_available(macos 10.12, iOS 10, tvOS 10, watchOS 3, *)) {
-    os_log_fault(OS_LOG_DEFAULT,
-                 "Assertion failed: (%s), function %s, file %s, line %u\n",
-                 expr_text, func, file, line);
-  }
+    if (__builtin_available(macos 10.12, iOS 10, tvOS 10, watchOS 3, *)) {
+      os_log_fault(OS_LOG_DEFAULT,
+                   "Assertion failed: (%s), function %s, file %s, line %u\n",
+                   expr_text, func, file, line);
+    }
 #endif
 
-  std::string buffer;
-  llvm::raw_string_ostream backtrace(buffer);
-  llvm::sys::PrintStackTrace(backtrace);
+    std::string buffer;
+    llvm::raw_string_ostream backtrace(buffer);
+    llvm::sys::PrintStackTrace(backtrace);
 
-  (*g_lldb_assert_callback.load())(
-      llvm::formatv("Assertion failed: ({0}), function {1}, file {2}, line {3}",
-                    expr_text, func, file, line)
-          .str(),
-      buffer,
-      "Please file a bug report against lldb and include the backtrace, the "
-      "version and as many details as possible.");
+    (*g_lldb_assert_callback.load())(
+        llvm::formatv(
+            "Assertion failed: ({0}), function {1}, file {2}, line {3}",
+            expr_text, func, file, line)
+            .str(),
+        buffer,
+        "Please file a bug report against lldb and include the backtrace, the "
+        "version and as many details as possible.");
+  });
 }
 
 void SetLLDBAssertCallback(LLDBAssertCallback callback) {

@JDevlieghere JDevlieghere merged commit 03604a7 into llvm:main Apr 4, 2025
12 checks passed
@JDevlieghere JDevlieghere deleted the lldbassert branch April 4, 2025 19:53
JDevlieghere added a commit to swiftlang/llvm-project that referenced this pull request Apr 4, 2025
The `lldbassert` macro in LLDB behaves like a regular `assert` when
assertions are enabled, and otherwise prints a pretty backtrace and
prompts the user to file a bug. By default, this is emitted as a
diagnostic event, but vendors can provide their own behavior, for
example pre-populating a bug report.

Recently, we ran into an issue where an `lldbassert` (in the Swift
language plugin) would fire excessively, to the point that it was
interfering with the usability of the debugger.

Once an `lldbasser` has fired, there's no point in bothering the user
over and over again for the same problem. This PR solves the problem by
introducing a static `std::once_flag` in the macro. This way, every
`lldbasser` will fire at most once per lldb instance.

rdar://148520448
(cherry picked from commit 03604a7)
JDevlieghere added a commit to swiftlang/llvm-project that referenced this pull request Apr 7, 2025
…4b04b411903-03604a784011

[🍒 stable/20240723] [lldb] Make lldbassert fire only once per instance (llvm#134343)
JDevlieghere added a commit to swiftlang/llvm-project that referenced this pull request Apr 11, 2025
The `lldbassert` macro in LLDB behaves like a regular `assert` when
assertions are enabled, and otherwise prints a pretty backtrace and
prompts the user to file a bug. By default, this is emitted as a
diagnostic event, but vendors can provide their own behavior, for
example pre-populating a bug report.

Recently, we ran into an issue where an `lldbassert` (in the Swift
language plugin) would fire excessively, to the point that it was
interfering with the usability of the debugger.

Once an `lldbasser` has fired, there's no point in bothering the user
over and over again for the same problem. This PR solves the problem by
introducing a static `std::once_flag` in the macro. This way, every
`lldbasser` will fire at most once per lldb instance.

rdar://148520448
(cherry picked from commit 03604a7)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants