-
Notifications
You must be signed in to change notification settings - Fork 15k
Introduce LDBG_OS() macro as a variant of LDBG() (#157194) #158260
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
Conversation
Also, improve LDBG() to accept debug type and level in any order, and add unit-tests for LDBG() and LGDB_OS(). LDBG_OS() is a macro that behaves like LDBG() but instead of directly using it to stream the output, it takes a callback function that will be called with a raw_ostream. Co-authored-by: Andrzej Warzyński <[email protected]>
|
@llvm/pr-subscribers-llvm-support Author: Mehdi Amini (joker-eph) ChangesAlso, improve LDBG() to accept debug type and level in any order, and Patch is 27.83 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/158260.diff 4 Files Affected:
diff --git a/llvm/include/llvm/Support/Debug.h b/llvm/include/llvm/Support/Debug.h
index a7795d403721c..b73f2d7c8b852 100644
--- a/llvm/include/llvm/Support/Debug.h
+++ b/llvm/include/llvm/Support/Debug.h
@@ -44,11 +44,6 @@ class raw_ostream;
/// level, return false.
LLVM_ABI bool isCurrentDebugType(const char *Type, int Level = 0);
-/// Overload allowing to swap the order of the Type and Level arguments.
-LLVM_ABI inline bool isCurrentDebugType(int Level, const char *Type) {
- return isCurrentDebugType(Type, Level);
-}
-
/// setCurrentDebugType - Set the current debug type, as if the -debug-only=X
/// option were specified. Note that DebugFlag also needs to be set to true for
/// debug output to be produced.
diff --git a/llvm/include/llvm/Support/DebugLog.h b/llvm/include/llvm/Support/DebugLog.h
index dce706e196bde..f7748bc9904b1 100644
--- a/llvm/include/llvm/Support/DebugLog.h
+++ b/llvm/include/llvm/Support/DebugLog.h
@@ -19,52 +19,55 @@
namespace llvm {
#ifndef NDEBUG
-// LDBG() is a macro that can be used as a raw_ostream for debugging.
-// It will stream the output to the dbgs() stream, with a prefix of the
-// debug type and the file and line number. A trailing newline is added to the
-// output automatically. If the streamed content contains a newline, the prefix
-// is added to each beginning of a new line. Nothing is printed if the debug
-// output is not enabled or the debug type does not match.
-//
-// E.g.,
-// LDBG() << "Bitset contains: " << Bitset;
-// is somehow equivalent to
-// LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE << "] " << __FILE__ << ":" <<
-// __LINE__ << " "
-// << "Bitset contains: " << Bitset << "\n");
-//
+/// LDBG() is a macro that can be used as a raw_ostream for debugging.
+/// It will stream the output to the dbgs() stream, with a prefix of the
+/// debug type and the file and line number. A trailing newline is added to the
+/// output automatically. If the streamed content contains a newline, the prefix
+/// is added to each beginning of a new line. Nothing is printed if the debug
+/// output is not enabled or the debug type does not match.
+///
+/// E.g.,
+/// LDBG() << "Bitset contains: " << Bitset;
+/// is equivalent to
+/// LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE << "] " << __FILE__ << ":" <<
+/// __LINE__ << " "
+/// << "Bitset contains: " << Bitset << "\n");
+///
// An optional `level` argument can be provided to control the verbosity of the
-// output. The default level is 1, and is in increasing level of verbosity.
-//
-// The `level` argument can be a literal integer, or a macro that evaluates to
-// an integer.
-//
-// An optional `type` argument can be provided to control the debug type. The
-// default type is DEBUG_TYPE. The `type` argument can be a literal string, or a
-// macro that evaluates to a string.
+/// output. The default level is 1, and is in increasing level of verbosity.
+///
+/// The `level` argument can be a literal integer, or a macro that evaluates to
+/// an integer.
+///
+/// An optional `type` argument can be provided to control the debug type. The
+/// default type is DEBUG_TYPE. The `type` argument can be a literal string, or
+/// a macro that evaluates to a string.
+///
+/// E.g.,
+/// LDBG(2) << "Bitset contains: " << Bitset;
+/// LDBG("debug_type") << "Bitset contains: " << Bitset;
+/// LDBG("debug_type", 2) << "Bitset contains: " << Bitset;
#define LDBG(...) _GET_LDBG_MACRO(__VA_ARGS__)(__VA_ARGS__)
-// Helper macros to choose the correct macro based on the number of arguments.
-#define LDBG_FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
-#define LDBG_FUNC_RECOMPOSER(argsWithParentheses) \
- LDBG_FUNC_CHOOSER argsWithParentheses
-#define LDBG_CHOOSE_FROM_ARG_COUNT(...) \
- LDBG_FUNC_RECOMPOSER( \
- (__VA_ARGS__, LDBG_LOG_LEVEL_WITH_TYPE, LDBG_LOG_LEVEL, ))
-#define LDBG_NO_ARG_EXPANDER() , , LDBG_LOG_LEVEL_1
-#define _GET_LDBG_MACRO(...) \
- LDBG_CHOOSE_FROM_ARG_COUNT(LDBG_NO_ARG_EXPANDER __VA_ARGS__())
-
-// Dispatch macros to support the `level` argument or none (default to 1)
-#define LDBG_LOG_LEVEL(LEVEL) \
- DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), LEVEL, DEBUG_TYPE)
-#define LDBG_LOG_LEVEL_1() LDBG_LOG_LEVEL(1)
-// This macro is a helper when LDBG() is called with 2 arguments.
-// In this case we want to allow the order of the arguments to be swapped.
-// We rely on the fact that the `level` argument is an integer, and the `type`
-// is a string and dispatch to a C++ API that is overloaded.
-#define LDBG_LOG_LEVEL_WITH_TYPE(LEVEL_OR_TYPE, TYPE_OR_LEVEL) \
- DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), (LEVEL_OR_TYPE), (TYPE_OR_LEVEL))
+/// LDBG_OS() is a macro that behaves like LDBG() but instead of directly using
+/// it to stream the output, it takes a callback function that will be called
+/// with a raw_ostream.
+/// This is useful when you need to pass a `raw_ostream` to a helper function to
+/// be able to print (when the `<<` operator is not available).
+///
+/// E.g.,
+/// LDBG_OS([&] (raw_ostream &Os) {
+/// Os << "Pass Manager contains: ";
+/// pm.printAsTextual(Os);
+/// });
+///
+/// Just like LDBG(), it optionally accepts a `level` and `type` arguments.
+/// E.g.,
+/// LDBG_OS(2, [&] (raw_ostream &Os) { ... });
+/// LDBG_OS("debug_type", [&] (raw_ostream &Os) { ... });
+/// LDBG_OS("debug_type", 2, [&] (raw_ostream &Os) { ... });
+///
+#define LDBG_OS(...) _GET_LDBG_OS_MACRO(__VA_ARGS__)(__VA_ARGS__)
// We want the filename without the full path. We are using the __FILE__ macro
// and a constexpr function to strip the path prefix. We can avoid the frontend
@@ -76,22 +79,167 @@ namespace llvm {
#define __LLVM_FILE_NAME__ ::llvm::impl::getShortFileName(__FILE__)
#endif
-#define DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL, TYPE, FILE, \
- LINE) \
- for (bool _c = \
- (::llvm::DebugFlag && ::llvm::isCurrentDebugType(TYPE, LEVEL)); \
+// Everything below are implementation details of the macros above.
+namespace impl {
+
+/// This macro expands to the stream to use for output, we use a macro to allow
+/// unit-testing to override.
+#define LDBG_STREAM ::llvm::dbgs()
+
+// ----------------------------------------------------------------------------
+// LDBG() implementation
+// ----------------------------------------------------------------------------
+
+// Helper macros to choose the correct LDBG() macro based on the number of
+// arguments.
+#define LDBG_FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
+#define LDBG_FUNC_RECOMPOSER(argsWithParentheses) \
+ LDBG_FUNC_CHOOSER argsWithParentheses
+#define LDBG_CHOOSE_FROM_ARG_COUNT(...) \
+ LDBG_FUNC_RECOMPOSER((__VA_ARGS__, LDBG_TYPE_AND_LEVEL, LDBG_LEVEL_OR_TYPE, ))
+#define LDBG_NO_ARG_EXPANDER() , , LDBG_NO_ARG
+#define _GET_LDBG_MACRO(...) \
+ LDBG_CHOOSE_FROM_ARG_COUNT(LDBG_NO_ARG_EXPANDER __VA_ARGS__())
+
+/// This macro is the core of the LDBG() implementation. It is used to print the
+/// debug output with the given stream, level, type, file, and line number.
+#define LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(STREAM, LEVEL_OR_TYPE, \
+ TYPE_OR_LEVEL, FILE, LINE) \
+ for (bool _c = ::llvm::DebugFlag && ::llvm::impl::ldbgIsCurrentDebugType( \
+ TYPE_OR_LEVEL, LEVEL_OR_TYPE); \
_c; _c = false) \
- for (::llvm::impl::raw_ldbg_ostream LdbgOS{ \
- ::llvm::impl::computePrefix(TYPE, FILE, LINE, LEVEL), (STREAM)}; \
- _c; _c = false) \
- ::llvm::impl::RAIINewLineStream{LdbgOS}.asLvalue()
+ ::llvm::impl::raw_ldbg_ostream{ \
+ ::llvm::impl::computePrefix(TYPE_OR_LEVEL, FILE, LINE, LEVEL_OR_TYPE), \
+ (STREAM), /*ShouldPrefixNextString=*/true, \
+ /*ShouldEmitNewLineOnDestruction=*/true} \
+ .asLvalue()
-#define DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, FILE) \
- DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL, TYPE, FILE, __LINE__)
-#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, LEVEL, TYPE) \
- DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, __LLVM_FILE_NAME__)
+/// These macros are helpers to implement LDBG() with an increasing amount of
+/// optional arguments made explicit.
+#define LDBG_STREAM_LEVEL_TYPE_AND_FILE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \
+ FILE) \
+ LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \
+ FILE, __LINE__)
+#define LDGB_STREAM_LEVEL_AND_TYPE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL) \
+ LDBG_STREAM_LEVEL_TYPE_AND_FILE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \
+ __LLVM_FILE_NAME__)
+/// This macro is a helper when LDBG() is called with 2 arguments.
+/// In this case we want to force the first argument to be the type for
+/// consistency in the codebase.
+/// We trick this by casting the first argument to a (const char *) which
+/// won't compile with an int.
+#define LDBG_TYPE_AND_LEVEL(TYPE, LEVEL) \
+ LDGB_STREAM_LEVEL_AND_TYPE(LDBG_STREAM, static_cast<const char *>(TYPE), \
+ (LEVEL))
-namespace impl {
+/// When a single argument is provided. This can be either a level or the debug
+/// type. If a level is provided, we default the debug type to DEBUG_TYPE, if a
+/// string is provided, we default the level to 1.
+#define LDBG_LEVEL_OR_TYPE(LEVEL_OR_TYPE) \
+ LDGB_STREAM_LEVEL_AND_TYPE(LDBG_STREAM, (LEVEL_OR_TYPE), \
+ LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE))
+#define LDBG_NO_ARG() LDBG_LEVEL_OR_TYPE(1)
+
+// ----------------------------------------------------------------------------
+// LDBG_OS() implementation
+// ----------------------------------------------------------------------------
+
+// Helper macros to choose the correct LDBG_OS() macro based on the number of
+// arguments.
+#define LDBG_OS_FUNC_CHOOSER(_f1, _f2, _f3, _f4, ...) _f4
+#define LDBG_OS_FUNC_RECOMPOSER(argsWithParentheses) \
+ LDBG_OS_FUNC_CHOOSER argsWithParentheses
+#define LDBG_OS_CHOOSE_FROM_ARG_COUNT(...) \
+ LDBG_OS_FUNC_RECOMPOSER((__VA_ARGS__, LDBG_OS_TYPE_AND_LEVEL_AND_CALLBACK, \
+ LDBG_OS_LEVEL_OR_TYPE_AND_CALLBACK, \
+ LDBG_OS_CALLBACK, ))
+#define LDBG_OS_NO_ARG_EXPANDER() , , , LDBG_OS_CALLBACK
+#define _GET_LDBG_OS_MACRO(...) \
+ LDBG_OS_CHOOSE_FROM_ARG_COUNT(LDBG_OS_NO_ARG_EXPANDER __VA_ARGS__())
+
+/// This macro is the core of the LDBG_OS() macros. It is used to print the
+/// debug output with the given stream, level, type, file, and line number.
+#define LDBG_OS_IMPL(TYPE_OR_LEVEL, LEVEL_OR_TYPE, CALLBACK, STREAM, FILE, \
+ LINE) \
+ if (::llvm::DebugFlag && \
+ ::llvm::impl::ldbgIsCurrentDebugType(TYPE_OR_LEVEL, LEVEL_OR_TYPE)) { \
+ ::llvm::impl::raw_ldbg_ostream LdbgOS{ \
+ ::llvm::impl::computePrefix(TYPE_OR_LEVEL, FILE, LINE, LEVEL_OR_TYPE), \
+ (STREAM), /*ShouldPrefixNextString=*/true, \
+ /*ShouldEmitNewLineOnDestruction=*/true}; \
+ CALLBACK(LdbgOS); \
+ }
+
+#define LDBG_OS_TYPE_AND_LEVEL_AND_CALLBACK(TYPE, LEVEL, CALLBACK) \
+ LDBG_OS_IMPL(static_cast<const char *>(TYPE), LEVEL, CALLBACK, LDBG_STREAM, \
+ __LLVM_FILE_NAME__, __LINE__)
+#define LDBG_OS_LEVEL_OR_TYPE_AND_CALLBACK(LEVEL_OR_TYPE, CALLBACK) \
+ LDBG_OS_IMPL(LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE), LEVEL_OR_TYPE, \
+ CALLBACK, LDBG_STREAM, __LLVM_FILE_NAME__, __LINE__)
+#define LDBG_OS_CALLBACK(CALLBACK) \
+ LDBG_OS_LEVEL_OR_TYPE_AND_CALLBACK(1, CALLBACK)
+
+// ----------------------------------------------------------------------------
+// General Helpers for the implementation above
+// ----------------------------------------------------------------------------
+
+/// Return the stringified macro as a StringRef.
+/// Also, strip out potential surrounding quotes: this comes from an artifact of
+/// the macro stringification, if DEBUG_TYPE is undefined we get the string
+/// "DEBUG_TYPE", however if it is defined we get the string with the quotes.
+/// For example if DEBUG_TYPE is "foo", we get "\"foo\"" but we want to return
+/// "foo" here.
+constexpr ::llvm::StringRef strip_quotes(const char *Str) {
+ ::llvm::StringRef S(Str);
+ if (Str[0] == '"' && Str[S.size() - 1] == '"')
+ return StringRef(Str + 1, S.size() - 2);
+ return S;
+}
+
+/// Fail compilation if DEBUG_TYPE is not defined.
+/// This is a workaround for GCC <=12 which does not support static_assert in
+/// templated constexpr functions.
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 12
+#define MISSING_DEBUG_TYPE() \
+ extern void missing_DEBUG_TYPE(void); \
+ missing_DEBUG_TYPE();
+#else
+#define MISSING_DEBUG_TYPE() static_assert(false, "DEBUG_TYPE is not defined");
+#endif
+
+/// Helper to provide the default level (=1) or type (=DEBUG_TYPE). This is used
+/// when a single argument is passed to LDBG() (or LDBG_OS()), if it is an
+/// integer we return DEBUG_TYPE and if it is a string we return 1. This fails
+/// with a static_assert if we pass an integer and DEBUG_TYPE is not defined.
+#define LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE) \
+ [](auto LevelOrType) { \
+ if constexpr (std::is_integral_v<decltype(LevelOrType)>) { \
+ constexpr const char *DebugType = LDBG_GET_DEBUG_TYPE_STR(); \
+ if constexpr (DebugType[0] == '"') { \
+ return ::llvm::impl::strip_quotes(DebugType); \
+ } else { \
+ MISSING_DEBUG_TYPE(); \
+ } \
+ } else { \
+ return 1; \
+ } \
+ }(LEVEL_OR_TYPE)
+
+/// Helpers to get DEBUG_TYPE as a StringRef, even when DEBUG_TYPE is not
+/// defined (in which case it expands to "DEBUG_TYPE")
+#define LDBG_GET_DEBUG_TYPE_STR__(X) #X
+#define LDBG_GET_DEBUG_TYPE_STR_(X) LDBG_GET_DEBUG_TYPE_STR__(X)
+#define LDBG_GET_DEBUG_TYPE_STR() LDBG_GET_DEBUG_TYPE_STR_(DEBUG_TYPE)
+
+/// Helper to call isCurrentDebugType with a StringRef.
+static LLVM_ATTRIBUTE_UNUSED bool ldbgIsCurrentDebugType(StringRef Type,
+ int Level) {
+ return ::llvm::isCurrentDebugType(Type.str().c_str(), Level);
+}
+static LLVM_ATTRIBUTE_UNUSED bool ldbgIsCurrentDebugType(int Level,
+ StringRef Type) {
+ return ::llvm::isCurrentDebugType(Type.str().c_str(), Level);
+}
/// A raw_ostream that tracks `\n` and print the prefix after each
/// newline.
@@ -99,6 +247,7 @@ class LLVM_ABI raw_ldbg_ostream final : public raw_ostream {
std::string Prefix;
raw_ostream &Os;
bool ShouldPrefixNextString;
+ bool ShouldEmitNewLineOnDestruction;
/// Split the line on newlines and insert the prefix before each
/// newline. Forward everything to the underlying stream.
@@ -131,12 +280,17 @@ class LLVM_ABI raw_ldbg_ostream final : public raw_ostream {
public:
explicit raw_ldbg_ostream(std::string Prefix, raw_ostream &Os,
- bool ShouldPrefixNextString = true)
+ bool ShouldPrefixNextString = true,
+ bool ShouldEmitNewLineOnDestruction = false)
: Prefix(std::move(Prefix)), Os(Os),
- ShouldPrefixNextString(ShouldPrefixNextString) {
+ ShouldPrefixNextString(ShouldPrefixNextString),
+ ShouldEmitNewLineOnDestruction(ShouldEmitNewLineOnDestruction) {
SetUnbuffered();
}
- ~raw_ldbg_ostream() final {}
+ ~raw_ldbg_ostream() final {
+ if (ShouldEmitNewLineOnDestruction)
+ Os << '\n';
+ }
/// Forward the current_pos method to the underlying stream.
uint64_t current_pos() const final { return Os.tell(); }
@@ -173,17 +327,17 @@ getShortFileName(const char *path) {
/// "[DebugType] File:Line "
/// Where the File is the file name without the path prefix.
static LLVM_ATTRIBUTE_UNUSED std::string
-computePrefix(const char *DebugType, const char *File, int Line, int Level) {
+computePrefix(StringRef DebugType, const char *File, int Line, int Level) {
std::string Prefix;
raw_string_ostream OsPrefix(Prefix);
- if (DebugType)
+ if (!DebugType.empty())
OsPrefix << "[" << DebugType << ":" << Level << "] ";
OsPrefix << File << ":" << Line << " ";
return OsPrefix.str();
}
/// Overload allowing to swap the order of the DebugType and Level arguments.
static LLVM_ATTRIBUTE_UNUSED std::string
-computePrefix(int Level, const char *File, int Line, const char *DebugType) {
+computePrefix(int Level, const char *File, int Line, StringRef DebugType) {
return computePrefix(DebugType, File, Line, Level);
}
@@ -194,6 +348,7 @@ computePrefix(int Level, const char *File, int Line, const char *DebugType) {
#define LDBG(...) \
for (bool _c = false; _c; _c = false) \
::llvm::nulls()
+#define LDBG_OS(...)
#endif
} // end namespace llvm
diff --git a/llvm/unittests/Support/DebugLogTest.cpp b/llvm/unittests/Support/DebugLogTest.cpp
index e087705b72586..da3851ed86b35 100644
--- a/llvm/unittests/Support/DebugLogTest.cpp
+++ b/llvm/unittests/Support/DebugLogTest.cpp
@@ -27,7 +27,7 @@ TEST(DebugLogTest, Basic) {
{
std::string str;
raw_string_ostream os(str);
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, nullptr) << "NoType";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, "", 0) << "NoType";
EXPECT_FALSE(StringRef(os.str()).starts_with('['));
EXPECT_TRUE(StringRef(os.str()).ends_with("NoType\n"));
}
@@ -36,8 +36,8 @@ TEST(DebugLogTest, Basic) {
{
std::string str;
raw_string_ostream os(str);
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "A";
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "B") << "B";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, 0, "A") << "A";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, "B", 0) << "B";
EXPECT_TRUE(StringRef(os.str()).starts_with('['));
EXPECT_THAT(os.str(), AllOf(HasSubstr("A\n"), HasSubstr("B\n")));
}
@@ -48,18 +48,18 @@ TEST(DebugLogTest, Basic) {
raw_string_ostream os(str);
// Just check that the macro doesn't result in dangling else.
if (true)
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "A";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, 0, "A") << "A";
else
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "B";
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "B") << "B";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, 0, "A")...
[truncated]
|
|
@llvm/pr-subscribers-mlir Author: Mehdi Amini (joker-eph) ChangesAlso, improve LDBG() to accept debug type and level in any order, and Patch is 27.83 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/158260.diff 4 Files Affected:
diff --git a/llvm/include/llvm/Support/Debug.h b/llvm/include/llvm/Support/Debug.h
index a7795d403721c..b73f2d7c8b852 100644
--- a/llvm/include/llvm/Support/Debug.h
+++ b/llvm/include/llvm/Support/Debug.h
@@ -44,11 +44,6 @@ class raw_ostream;
/// level, return false.
LLVM_ABI bool isCurrentDebugType(const char *Type, int Level = 0);
-/// Overload allowing to swap the order of the Type and Level arguments.
-LLVM_ABI inline bool isCurrentDebugType(int Level, const char *Type) {
- return isCurrentDebugType(Type, Level);
-}
-
/// setCurrentDebugType - Set the current debug type, as if the -debug-only=X
/// option were specified. Note that DebugFlag also needs to be set to true for
/// debug output to be produced.
diff --git a/llvm/include/llvm/Support/DebugLog.h b/llvm/include/llvm/Support/DebugLog.h
index dce706e196bde..f7748bc9904b1 100644
--- a/llvm/include/llvm/Support/DebugLog.h
+++ b/llvm/include/llvm/Support/DebugLog.h
@@ -19,52 +19,55 @@
namespace llvm {
#ifndef NDEBUG
-// LDBG() is a macro that can be used as a raw_ostream for debugging.
-// It will stream the output to the dbgs() stream, with a prefix of the
-// debug type and the file and line number. A trailing newline is added to the
-// output automatically. If the streamed content contains a newline, the prefix
-// is added to each beginning of a new line. Nothing is printed if the debug
-// output is not enabled or the debug type does not match.
-//
-// E.g.,
-// LDBG() << "Bitset contains: " << Bitset;
-// is somehow equivalent to
-// LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE << "] " << __FILE__ << ":" <<
-// __LINE__ << " "
-// << "Bitset contains: " << Bitset << "\n");
-//
+/// LDBG() is a macro that can be used as a raw_ostream for debugging.
+/// It will stream the output to the dbgs() stream, with a prefix of the
+/// debug type and the file and line number. A trailing newline is added to the
+/// output automatically. If the streamed content contains a newline, the prefix
+/// is added to each beginning of a new line. Nothing is printed if the debug
+/// output is not enabled or the debug type does not match.
+///
+/// E.g.,
+/// LDBG() << "Bitset contains: " << Bitset;
+/// is equivalent to
+/// LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE << "] " << __FILE__ << ":" <<
+/// __LINE__ << " "
+/// << "Bitset contains: " << Bitset << "\n");
+///
// An optional `level` argument can be provided to control the verbosity of the
-// output. The default level is 1, and is in increasing level of verbosity.
-//
-// The `level` argument can be a literal integer, or a macro that evaluates to
-// an integer.
-//
-// An optional `type` argument can be provided to control the debug type. The
-// default type is DEBUG_TYPE. The `type` argument can be a literal string, or a
-// macro that evaluates to a string.
+/// output. The default level is 1, and is in increasing level of verbosity.
+///
+/// The `level` argument can be a literal integer, or a macro that evaluates to
+/// an integer.
+///
+/// An optional `type` argument can be provided to control the debug type. The
+/// default type is DEBUG_TYPE. The `type` argument can be a literal string, or
+/// a macro that evaluates to a string.
+///
+/// E.g.,
+/// LDBG(2) << "Bitset contains: " << Bitset;
+/// LDBG("debug_type") << "Bitset contains: " << Bitset;
+/// LDBG("debug_type", 2) << "Bitset contains: " << Bitset;
#define LDBG(...) _GET_LDBG_MACRO(__VA_ARGS__)(__VA_ARGS__)
-// Helper macros to choose the correct macro based on the number of arguments.
-#define LDBG_FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
-#define LDBG_FUNC_RECOMPOSER(argsWithParentheses) \
- LDBG_FUNC_CHOOSER argsWithParentheses
-#define LDBG_CHOOSE_FROM_ARG_COUNT(...) \
- LDBG_FUNC_RECOMPOSER( \
- (__VA_ARGS__, LDBG_LOG_LEVEL_WITH_TYPE, LDBG_LOG_LEVEL, ))
-#define LDBG_NO_ARG_EXPANDER() , , LDBG_LOG_LEVEL_1
-#define _GET_LDBG_MACRO(...) \
- LDBG_CHOOSE_FROM_ARG_COUNT(LDBG_NO_ARG_EXPANDER __VA_ARGS__())
-
-// Dispatch macros to support the `level` argument or none (default to 1)
-#define LDBG_LOG_LEVEL(LEVEL) \
- DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), LEVEL, DEBUG_TYPE)
-#define LDBG_LOG_LEVEL_1() LDBG_LOG_LEVEL(1)
-// This macro is a helper when LDBG() is called with 2 arguments.
-// In this case we want to allow the order of the arguments to be swapped.
-// We rely on the fact that the `level` argument is an integer, and the `type`
-// is a string and dispatch to a C++ API that is overloaded.
-#define LDBG_LOG_LEVEL_WITH_TYPE(LEVEL_OR_TYPE, TYPE_OR_LEVEL) \
- DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), (LEVEL_OR_TYPE), (TYPE_OR_LEVEL))
+/// LDBG_OS() is a macro that behaves like LDBG() but instead of directly using
+/// it to stream the output, it takes a callback function that will be called
+/// with a raw_ostream.
+/// This is useful when you need to pass a `raw_ostream` to a helper function to
+/// be able to print (when the `<<` operator is not available).
+///
+/// E.g.,
+/// LDBG_OS([&] (raw_ostream &Os) {
+/// Os << "Pass Manager contains: ";
+/// pm.printAsTextual(Os);
+/// });
+///
+/// Just like LDBG(), it optionally accepts a `level` and `type` arguments.
+/// E.g.,
+/// LDBG_OS(2, [&] (raw_ostream &Os) { ... });
+/// LDBG_OS("debug_type", [&] (raw_ostream &Os) { ... });
+/// LDBG_OS("debug_type", 2, [&] (raw_ostream &Os) { ... });
+///
+#define LDBG_OS(...) _GET_LDBG_OS_MACRO(__VA_ARGS__)(__VA_ARGS__)
// We want the filename without the full path. We are using the __FILE__ macro
// and a constexpr function to strip the path prefix. We can avoid the frontend
@@ -76,22 +79,167 @@ namespace llvm {
#define __LLVM_FILE_NAME__ ::llvm::impl::getShortFileName(__FILE__)
#endif
-#define DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL, TYPE, FILE, \
- LINE) \
- for (bool _c = \
- (::llvm::DebugFlag && ::llvm::isCurrentDebugType(TYPE, LEVEL)); \
+// Everything below are implementation details of the macros above.
+namespace impl {
+
+/// This macro expands to the stream to use for output, we use a macro to allow
+/// unit-testing to override.
+#define LDBG_STREAM ::llvm::dbgs()
+
+// ----------------------------------------------------------------------------
+// LDBG() implementation
+// ----------------------------------------------------------------------------
+
+// Helper macros to choose the correct LDBG() macro based on the number of
+// arguments.
+#define LDBG_FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
+#define LDBG_FUNC_RECOMPOSER(argsWithParentheses) \
+ LDBG_FUNC_CHOOSER argsWithParentheses
+#define LDBG_CHOOSE_FROM_ARG_COUNT(...) \
+ LDBG_FUNC_RECOMPOSER((__VA_ARGS__, LDBG_TYPE_AND_LEVEL, LDBG_LEVEL_OR_TYPE, ))
+#define LDBG_NO_ARG_EXPANDER() , , LDBG_NO_ARG
+#define _GET_LDBG_MACRO(...) \
+ LDBG_CHOOSE_FROM_ARG_COUNT(LDBG_NO_ARG_EXPANDER __VA_ARGS__())
+
+/// This macro is the core of the LDBG() implementation. It is used to print the
+/// debug output with the given stream, level, type, file, and line number.
+#define LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(STREAM, LEVEL_OR_TYPE, \
+ TYPE_OR_LEVEL, FILE, LINE) \
+ for (bool _c = ::llvm::DebugFlag && ::llvm::impl::ldbgIsCurrentDebugType( \
+ TYPE_OR_LEVEL, LEVEL_OR_TYPE); \
_c; _c = false) \
- for (::llvm::impl::raw_ldbg_ostream LdbgOS{ \
- ::llvm::impl::computePrefix(TYPE, FILE, LINE, LEVEL), (STREAM)}; \
- _c; _c = false) \
- ::llvm::impl::RAIINewLineStream{LdbgOS}.asLvalue()
+ ::llvm::impl::raw_ldbg_ostream{ \
+ ::llvm::impl::computePrefix(TYPE_OR_LEVEL, FILE, LINE, LEVEL_OR_TYPE), \
+ (STREAM), /*ShouldPrefixNextString=*/true, \
+ /*ShouldEmitNewLineOnDestruction=*/true} \
+ .asLvalue()
-#define DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, FILE) \
- DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL, TYPE, FILE, __LINE__)
-#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, LEVEL, TYPE) \
- DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, __LLVM_FILE_NAME__)
+/// These macros are helpers to implement LDBG() with an increasing amount of
+/// optional arguments made explicit.
+#define LDBG_STREAM_LEVEL_TYPE_AND_FILE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \
+ FILE) \
+ LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \
+ FILE, __LINE__)
+#define LDGB_STREAM_LEVEL_AND_TYPE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL) \
+ LDBG_STREAM_LEVEL_TYPE_AND_FILE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \
+ __LLVM_FILE_NAME__)
+/// This macro is a helper when LDBG() is called with 2 arguments.
+/// In this case we want to force the first argument to be the type for
+/// consistency in the codebase.
+/// We trick this by casting the first argument to a (const char *) which
+/// won't compile with an int.
+#define LDBG_TYPE_AND_LEVEL(TYPE, LEVEL) \
+ LDGB_STREAM_LEVEL_AND_TYPE(LDBG_STREAM, static_cast<const char *>(TYPE), \
+ (LEVEL))
-namespace impl {
+/// When a single argument is provided. This can be either a level or the debug
+/// type. If a level is provided, we default the debug type to DEBUG_TYPE, if a
+/// string is provided, we default the level to 1.
+#define LDBG_LEVEL_OR_TYPE(LEVEL_OR_TYPE) \
+ LDGB_STREAM_LEVEL_AND_TYPE(LDBG_STREAM, (LEVEL_OR_TYPE), \
+ LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE))
+#define LDBG_NO_ARG() LDBG_LEVEL_OR_TYPE(1)
+
+// ----------------------------------------------------------------------------
+// LDBG_OS() implementation
+// ----------------------------------------------------------------------------
+
+// Helper macros to choose the correct LDBG_OS() macro based on the number of
+// arguments.
+#define LDBG_OS_FUNC_CHOOSER(_f1, _f2, _f3, _f4, ...) _f4
+#define LDBG_OS_FUNC_RECOMPOSER(argsWithParentheses) \
+ LDBG_OS_FUNC_CHOOSER argsWithParentheses
+#define LDBG_OS_CHOOSE_FROM_ARG_COUNT(...) \
+ LDBG_OS_FUNC_RECOMPOSER((__VA_ARGS__, LDBG_OS_TYPE_AND_LEVEL_AND_CALLBACK, \
+ LDBG_OS_LEVEL_OR_TYPE_AND_CALLBACK, \
+ LDBG_OS_CALLBACK, ))
+#define LDBG_OS_NO_ARG_EXPANDER() , , , LDBG_OS_CALLBACK
+#define _GET_LDBG_OS_MACRO(...) \
+ LDBG_OS_CHOOSE_FROM_ARG_COUNT(LDBG_OS_NO_ARG_EXPANDER __VA_ARGS__())
+
+/// This macro is the core of the LDBG_OS() macros. It is used to print the
+/// debug output with the given stream, level, type, file, and line number.
+#define LDBG_OS_IMPL(TYPE_OR_LEVEL, LEVEL_OR_TYPE, CALLBACK, STREAM, FILE, \
+ LINE) \
+ if (::llvm::DebugFlag && \
+ ::llvm::impl::ldbgIsCurrentDebugType(TYPE_OR_LEVEL, LEVEL_OR_TYPE)) { \
+ ::llvm::impl::raw_ldbg_ostream LdbgOS{ \
+ ::llvm::impl::computePrefix(TYPE_OR_LEVEL, FILE, LINE, LEVEL_OR_TYPE), \
+ (STREAM), /*ShouldPrefixNextString=*/true, \
+ /*ShouldEmitNewLineOnDestruction=*/true}; \
+ CALLBACK(LdbgOS); \
+ }
+
+#define LDBG_OS_TYPE_AND_LEVEL_AND_CALLBACK(TYPE, LEVEL, CALLBACK) \
+ LDBG_OS_IMPL(static_cast<const char *>(TYPE), LEVEL, CALLBACK, LDBG_STREAM, \
+ __LLVM_FILE_NAME__, __LINE__)
+#define LDBG_OS_LEVEL_OR_TYPE_AND_CALLBACK(LEVEL_OR_TYPE, CALLBACK) \
+ LDBG_OS_IMPL(LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE), LEVEL_OR_TYPE, \
+ CALLBACK, LDBG_STREAM, __LLVM_FILE_NAME__, __LINE__)
+#define LDBG_OS_CALLBACK(CALLBACK) \
+ LDBG_OS_LEVEL_OR_TYPE_AND_CALLBACK(1, CALLBACK)
+
+// ----------------------------------------------------------------------------
+// General Helpers for the implementation above
+// ----------------------------------------------------------------------------
+
+/// Return the stringified macro as a StringRef.
+/// Also, strip out potential surrounding quotes: this comes from an artifact of
+/// the macro stringification, if DEBUG_TYPE is undefined we get the string
+/// "DEBUG_TYPE", however if it is defined we get the string with the quotes.
+/// For example if DEBUG_TYPE is "foo", we get "\"foo\"" but we want to return
+/// "foo" here.
+constexpr ::llvm::StringRef strip_quotes(const char *Str) {
+ ::llvm::StringRef S(Str);
+ if (Str[0] == '"' && Str[S.size() - 1] == '"')
+ return StringRef(Str + 1, S.size() - 2);
+ return S;
+}
+
+/// Fail compilation if DEBUG_TYPE is not defined.
+/// This is a workaround for GCC <=12 which does not support static_assert in
+/// templated constexpr functions.
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 12
+#define MISSING_DEBUG_TYPE() \
+ extern void missing_DEBUG_TYPE(void); \
+ missing_DEBUG_TYPE();
+#else
+#define MISSING_DEBUG_TYPE() static_assert(false, "DEBUG_TYPE is not defined");
+#endif
+
+/// Helper to provide the default level (=1) or type (=DEBUG_TYPE). This is used
+/// when a single argument is passed to LDBG() (or LDBG_OS()), if it is an
+/// integer we return DEBUG_TYPE and if it is a string we return 1. This fails
+/// with a static_assert if we pass an integer and DEBUG_TYPE is not defined.
+#define LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE) \
+ [](auto LevelOrType) { \
+ if constexpr (std::is_integral_v<decltype(LevelOrType)>) { \
+ constexpr const char *DebugType = LDBG_GET_DEBUG_TYPE_STR(); \
+ if constexpr (DebugType[0] == '"') { \
+ return ::llvm::impl::strip_quotes(DebugType); \
+ } else { \
+ MISSING_DEBUG_TYPE(); \
+ } \
+ } else { \
+ return 1; \
+ } \
+ }(LEVEL_OR_TYPE)
+
+/// Helpers to get DEBUG_TYPE as a StringRef, even when DEBUG_TYPE is not
+/// defined (in which case it expands to "DEBUG_TYPE")
+#define LDBG_GET_DEBUG_TYPE_STR__(X) #X
+#define LDBG_GET_DEBUG_TYPE_STR_(X) LDBG_GET_DEBUG_TYPE_STR__(X)
+#define LDBG_GET_DEBUG_TYPE_STR() LDBG_GET_DEBUG_TYPE_STR_(DEBUG_TYPE)
+
+/// Helper to call isCurrentDebugType with a StringRef.
+static LLVM_ATTRIBUTE_UNUSED bool ldbgIsCurrentDebugType(StringRef Type,
+ int Level) {
+ return ::llvm::isCurrentDebugType(Type.str().c_str(), Level);
+}
+static LLVM_ATTRIBUTE_UNUSED bool ldbgIsCurrentDebugType(int Level,
+ StringRef Type) {
+ return ::llvm::isCurrentDebugType(Type.str().c_str(), Level);
+}
/// A raw_ostream that tracks `\n` and print the prefix after each
/// newline.
@@ -99,6 +247,7 @@ class LLVM_ABI raw_ldbg_ostream final : public raw_ostream {
std::string Prefix;
raw_ostream &Os;
bool ShouldPrefixNextString;
+ bool ShouldEmitNewLineOnDestruction;
/// Split the line on newlines and insert the prefix before each
/// newline. Forward everything to the underlying stream.
@@ -131,12 +280,17 @@ class LLVM_ABI raw_ldbg_ostream final : public raw_ostream {
public:
explicit raw_ldbg_ostream(std::string Prefix, raw_ostream &Os,
- bool ShouldPrefixNextString = true)
+ bool ShouldPrefixNextString = true,
+ bool ShouldEmitNewLineOnDestruction = false)
: Prefix(std::move(Prefix)), Os(Os),
- ShouldPrefixNextString(ShouldPrefixNextString) {
+ ShouldPrefixNextString(ShouldPrefixNextString),
+ ShouldEmitNewLineOnDestruction(ShouldEmitNewLineOnDestruction) {
SetUnbuffered();
}
- ~raw_ldbg_ostream() final {}
+ ~raw_ldbg_ostream() final {
+ if (ShouldEmitNewLineOnDestruction)
+ Os << '\n';
+ }
/// Forward the current_pos method to the underlying stream.
uint64_t current_pos() const final { return Os.tell(); }
@@ -173,17 +327,17 @@ getShortFileName(const char *path) {
/// "[DebugType] File:Line "
/// Where the File is the file name without the path prefix.
static LLVM_ATTRIBUTE_UNUSED std::string
-computePrefix(const char *DebugType, const char *File, int Line, int Level) {
+computePrefix(StringRef DebugType, const char *File, int Line, int Level) {
std::string Prefix;
raw_string_ostream OsPrefix(Prefix);
- if (DebugType)
+ if (!DebugType.empty())
OsPrefix << "[" << DebugType << ":" << Level << "] ";
OsPrefix << File << ":" << Line << " ";
return OsPrefix.str();
}
/// Overload allowing to swap the order of the DebugType and Level arguments.
static LLVM_ATTRIBUTE_UNUSED std::string
-computePrefix(int Level, const char *File, int Line, const char *DebugType) {
+computePrefix(int Level, const char *File, int Line, StringRef DebugType) {
return computePrefix(DebugType, File, Line, Level);
}
@@ -194,6 +348,7 @@ computePrefix(int Level, const char *File, int Line, const char *DebugType) {
#define LDBG(...) \
for (bool _c = false; _c; _c = false) \
::llvm::nulls()
+#define LDBG_OS(...)
#endif
} // end namespace llvm
diff --git a/llvm/unittests/Support/DebugLogTest.cpp b/llvm/unittests/Support/DebugLogTest.cpp
index e087705b72586..da3851ed86b35 100644
--- a/llvm/unittests/Support/DebugLogTest.cpp
+++ b/llvm/unittests/Support/DebugLogTest.cpp
@@ -27,7 +27,7 @@ TEST(DebugLogTest, Basic) {
{
std::string str;
raw_string_ostream os(str);
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, nullptr) << "NoType";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, "", 0) << "NoType";
EXPECT_FALSE(StringRef(os.str()).starts_with('['));
EXPECT_TRUE(StringRef(os.str()).ends_with("NoType\n"));
}
@@ -36,8 +36,8 @@ TEST(DebugLogTest, Basic) {
{
std::string str;
raw_string_ostream os(str);
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "A";
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "B") << "B";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, 0, "A") << "A";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, "B", 0) << "B";
EXPECT_TRUE(StringRef(os.str()).starts_with('['));
EXPECT_THAT(os.str(), AllOf(HasSubstr("A\n"), HasSubstr("B\n")));
}
@@ -48,18 +48,18 @@ TEST(DebugLogTest, Basic) {
raw_string_ostream os(str);
// Just check that the macro doesn't result in dangling else.
if (true)
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "A";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, 0, "A") << "A";
else
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "B";
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "B") << "B";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, 0, "A")...
[truncated]
|
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/190/builds/27312 Here is the relevant piece of the build log for the reference |
…158260) Also, improve LDBG() to accept debug type and level in any order, and add unit-tests for LDBG() and LGDB_OS(). LDBG_OS() is a macro that behaves like LDBG() but instead of directly using it to stream the output, it takes a callback function that will be called with a raw_ostream. Co-authored-by: Andrzej Warzyński <[email protected]> Co-authored-by: Andrzej Warzyński <[email protected]>
Also, improve LDBG() to accept debug type and level in any order, and add unit-tests for LDBG() and LGDB_OS(). LDBG_OS() is a macro that behaves like LDBG() but instead of directly using it to stream the output, it takes a callback function that will be called with a raw_ostream. This is a re-land with workarounds for older gcc and clang versions. Previous attempts in #157194 and #158260 Co-authored-by: Andrzej Warzyński <[email protected]>
…#157194)" (#158264) Reverts llvm/llvm-project#158260 second attempt to land this fixed some bots, but left others broken, need an extra iteration!
Also, improve LDBG() to accept debug type and level in any order, and
add unit-tests for LDBG() and LGDB_OS().
LDBG_OS() is a macro that behaves like LDBG() but instead of directly
using it to stream the output, it takes a callback function that will be
called with a raw_ostream.
Co-authored-by: Andrzej Warzyński [email protected]