Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions llvm/docs/ProgrammersManual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,8 @@ It also supports a `level` argument to control the verbosity of the output.

LDBG(2) << "I am here!";

A ``DEBUG_TYPE`` macro should be defined in the file before using ``LDBG()``.
A ``DEBUG_TYPE`` macro may optionally be defined in the file before using
``LDBG()``, otherwise the file name is used as the debug type.
The file name and line number are automatically added to the output, as well as
a terminating newline.

Expand All @@ -1180,7 +1181,7 @@ The debug output can be enabled by passing the ``-debug`` command line argument.
$ opt < a.bc > /dev/null -mypass
<no output>
$ opt < a.bc > /dev/null -mypass -debug
[my-pass:2] MyPass.cpp:123 I am here!
[my-pass MyPass.cpp:123 2] I am here!

While `LDBG()` is useful to add debug output to your code, there are cases
where you may need to guard a block of code with a debug check. The
Expand Down Expand Up @@ -1222,39 +1223,39 @@ Fine grained debug info with ``DEBUG_TYPE`` and the ``-debug-only`` option
Sometimes you may find yourself in a situation where enabling ``-debug`` just
turns on **too much** information (such as when working on the code generator).
If you want to enable debug information with more fine-grained control, you
should define the ``DEBUG_TYPE`` macro and use the ``-debug-only`` option as
follows:
can control the debug type and level with associate with each logging statement
as follows:

.. code-block:: c++

#define DEBUG_TYPE "foo"
#define DEBUG_TYPE "foo" // Optional: the file name is used instead if not defined
LDBG(2) << "Hello,";
// DEBUG_TYPE can be overridden locally, here with "bar"
LDBG("bar", 3) << "'bar' debug type";


A more fine-grained control can be achieved by passing the ``-debug-only``
command line argument:
A more fine-grained control of the output can be achieved by passing the
``-debug-only`` command line argument:

.. code-block:: none

$ opt < a.bc > /dev/null -mypass -debug-only=foo
[foo:2] MyPass.cpp:123 Hello,
[foo MyPass.cpp:123 2] Hello,
$ opt < a.bc > /dev/null -mypass -debug-only=foo,bar
[foo:2] MyPass.cpp:123 Hello,
[bar:3] MyPass.cpp:124 World!
[foo MyPass.cpp:123 2] Hello,
[bar MyPass.cpp:124 3] World!
$ opt < a.bc > /dev/null -mypass -debug-only=bar
[bar:3] MyPass.cpp:124 World!
[bar MyPass.cpp:124 3] World!

The debug-only argument is a comma separated list of debug types and levels.
The level is an optional integer setting the maximum debug level to enable:

.. code-block:: none

$ opt < a.bc > /dev/null -mypass -debug-only=foo:2,bar:2
[foo:2] MyPass.cpp:123 Hello,
[foo MyPass.cpp:123 2] Hello,
$ opt < a.bc > /dev/null -mypass -debug-only=foo:1,bar:3
[bar:3] MyPass.cpp:124 World!
[bar MyPass.cpp:124 3] World!

Instead of opting in specific debug types, the ``-debug-only`` option also
works to filter out debug output for specific debug types, by omitting the
Expand All @@ -1263,7 +1264,7 @@ level (or setting it to 0):
.. code-block:: none

$ opt < a.bc > /dev/null -mypass -debug-only=foo:
[bar:3] MyPass.cpp:124 World!
[bar MyPass.cpp:124 3] World!
$ opt < a.bc > /dev/null -mypass -debug-only=bar:0,foo:


Expand Down
25 changes: 7 additions & 18 deletions llvm/include/llvm/Support/DebugLog.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,30 +196,18 @@ constexpr ::llvm::StringRef strip_quotes(const char *Str) {
return S;
}

/// Fail compilation if DEBUG_TYPE is not defined.
/// This is a workaround for GCC <=12 and clang <=16 which do not support
/// static_assert in templated constexpr functions.
#if (defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 12) || \
(defined(__clang__) && __clang_major__ <= 16)
#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.
/// integer we return DEBUG_TYPE and if it is a string we return 1.
/// When DEBUG_TYPE is not defined, we return the current file name instead.
#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(); \
return __LLVM_FILE_NAME__; \
} \
} else { \
return 1; \
Expand Down Expand Up @@ -331,9 +319,10 @@ static LLVM_ATTRIBUTE_UNUSED std::string
computePrefix(StringRef DebugType, const char *File, int Line, int Level) {
std::string Prefix;
raw_string_ostream OsPrefix(Prefix);
if (!DebugType.empty())
OsPrefix << "[" << DebugType << ":" << Level << "] ";
OsPrefix << File << ":" << Line << " ";
OsPrefix << "[";
if (!DebugType.empty() && DebugType != File)
OsPrefix << DebugType << " ";
OsPrefix << File << ":" << Line << " " << Level << "] ";
return OsPrefix.str();
}
/// Overload allowing to swap the order of the DebugType and Level arguments.
Expand Down
120 changes: 97 additions & 23 deletions llvm/unittests/Support/DebugLogTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ TEST(DebugLogTest, Basic) {
std::string str;
raw_string_ostream os(str);
LDGB_STREAM_LEVEL_AND_TYPE(os, "", 0) << "NoType";
EXPECT_FALSE(StringRef(os.str()).starts_with('['));
EXPECT_TRUE(StringRef(os.str()).starts_with('['));
EXPECT_TRUE(StringRef(os.str()).ends_with("NoType\n"));
}

Expand Down Expand Up @@ -77,8 +77,8 @@ TEST(DebugLogTest, BasicWithLevel) {
for (int level : llvm::seq<int>(0, 4))
LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(os, level, type, type, level)
<< level;
EXPECT_EQ(os.str(), "[A:0] A:0 0\n[A:1] A:1 1\n[A:2] A:2 2\n[A:3] A:3 "
"3\n[B:0] B:0 0\n[B:1] B:1 1\n[C:0] C:0 0\n");
EXPECT_EQ(os.str(), "[A:0 0] 0\n[A:1 1] 1\n[A:2 2] 2\n[A:3 3] 3\n[B:0 0] "
"0\n[B:1 1] 1\n[C:0 0] 0\n");
}

TEST(DebugLogTest, NegativeLevel) {
Expand All @@ -92,9 +92,10 @@ TEST(DebugLogTest, NegativeLevel) {
raw_string_ostream os(str);
for (auto type : {"A", "B"})
for (int level : llvm::seq<int>(0, 2))
LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(os, level, type, type, level)
LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(
os, level, type, (std::string(type) + ".cpp").c_str(), level)
<< level;
EXPECT_EQ(os.str(), "[A:0] A:0 0\n[B:0] B:0 0\n[B:1] B:1 1\n");
EXPECT_EQ(os.str(), "[A A.cpp:0 0] 0\n[B B.cpp:0 0] 0\n[B B.cpp:1 1] 1\n");
}

TEST(DebugLogTest, StreamPrefix) {
Expand Down Expand Up @@ -141,35 +142,71 @@ TEST(DebugLogTest, LDBG_MACROS) {
#define LDBG_STREAM DebugOs
#define DEBUG_TYPE "A"
LDBG() << "Hello, world!";
ExpectedOs << "[A:1] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " Hello, world!\n";
ExpectedOs << "[A " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " 1] Hello, world!\n";
EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
Str.clear();
StrExpected.clear();

// Test with a level, no type.
LDBG(2) << "Hello, world!";
ExpectedOs << "[A:2] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " Hello, world!\n";
ExpectedOs << "[A " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " 2] Hello, world!\n";
EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
Str.clear();
StrExpected.clear();

// Now the type will be explicit, check we don't use DEBUG_TYPE.
// Now check when we don't use DEBUG_TYPE, the file name is implicitly used
// instead.
#undef DEBUG_TYPE

// Repeat the tests above, they won't match since the debug types defined
// above don't match the file name.
LDBG() << "Hello, world!";
EXPECT_EQ(DebugOs.str(), "");
Str.clear();
StrExpected.clear();

// Test with a level, no type.
LDBG(2) << "Hello, world!";
EXPECT_EQ(DebugOs.str(), "");
Str.clear();
StrExpected.clear();

// Now enable the debug types that match the file name.
auto fileNameAndLevel = std::string(__LLVM_FILE_NAME__) + ":3";
static const char *DT2[] = {fileNameAndLevel.c_str(), "B:2"};
setCurrentDebugTypes(DT2, sizeof(DT2) / sizeof(DT2[0]));

// Repeat the tests above, they should match now.

LDBG() << "Hello, world!";
ExpectedOs << "[" << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " 1] Hello, world!\n";
EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
Str.clear();
StrExpected.clear();

// Test with a level, no type.
LDBG(2) << "Hello, world!";
ExpectedOs << "[" << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " 2] Hello, world!\n";
EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
Str.clear();
StrExpected.clear();

// Test with a type
LDBG("B") << "Hello, world!";
ExpectedOs << "[B:1] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " Hello, world!\n";
ExpectedOs << "[B " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " 1] Hello, world!\n";
EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
Str.clear();
StrExpected.clear();

// Test with a type and a level
LDBG("B", 2) << "Hello, world!";
ExpectedOs << "[B:2] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " Hello, world!\n";
ExpectedOs << "[B " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " 2] Hello, world!\n";
EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
Str.clear();
StrExpected.clear();
Expand All @@ -181,6 +218,8 @@ TEST(DebugLogTest, LDBG_MACROS) {
// Test with a level not enabled.
LDBG("B", 3) << "Hello, world!";
EXPECT_EQ(DebugOs.str(), "");
LDBG(__LLVM_FILE_NAME__, 4) << "Hello, world!";
EXPECT_EQ(DebugOs.str(), "");
}

TEST(DebugLogTest, LDBG_OS_MACROS) {
Expand All @@ -195,35 +234,70 @@ TEST(DebugLogTest, LDBG_OS_MACROS) {
#define LDBG_STREAM DebugOs
#define DEBUG_TYPE "A"
LDBG_OS([](raw_ostream &Os) { Os << "Hello, world!"; });
ExpectedOs << "[A:1] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " Hello, world!\n";
ExpectedOs << "[A " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " 1] Hello, world!\n";
EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
Str.clear();
StrExpected.clear();

// Test with a level, no type.
LDBG_OS(2, [](raw_ostream &Os) { Os << "Hello, world!"; });
ExpectedOs << "[A:2] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " Hello, world!\n";
ExpectedOs << "[A " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " 2] Hello, world!\n";
EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
Str.clear();
StrExpected.clear();

// Now the type will be explicit, check we don't use DEBUG_TYPE.
// Now check when we don't use DEBUG_TYPE, the file name is implicitly used
// instead.
#undef DEBUG_TYPE

// Repeat the tests above, they won't match since the debug types defined
// above don't match the file name.
LDBG_OS([](raw_ostream &Os) { Os << "Hello, world!"; });
EXPECT_EQ(DebugOs.str(), "");
Str.clear();
StrExpected.clear();

// Test with a level, no type.
LDBG_OS(2, [](raw_ostream &Os) { Os << "Hello, world!"; });
EXPECT_EQ(DebugOs.str(), "");
Str.clear();
StrExpected.clear();

// Now enable the debug types that match the file name.
auto fileNameAndLevel = std::string(__LLVM_FILE_NAME__) + ":3";
static const char *DT2[] = {fileNameAndLevel.c_str(), "B:2"};
setCurrentDebugTypes(DT2, sizeof(DT2) / sizeof(DT2[0]));

// Repeat the tests above, they should match now.
LDBG_OS([](raw_ostream &Os) { Os << "Hello, world!"; });
ExpectedOs << "[" << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " 1] Hello, world!\n";
EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
Str.clear();
StrExpected.clear();

// Test with a level, no type.
LDBG_OS(2, [](raw_ostream &Os) { Os << "Hello, world!"; });
ExpectedOs << "[" << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " 2] Hello, world!\n";
EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
Str.clear();
StrExpected.clear();

// Test with a type.
LDBG_OS("B", [](raw_ostream &Os) { Os << "Hello, world!"; });
ExpectedOs << "[B:1] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " Hello, world!\n";
ExpectedOs << "[B " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " 1] Hello, world!\n";
EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
Str.clear();
StrExpected.clear();

// Test with a type and a level
LDBG_OS("B", 2, [](raw_ostream &Os) { Os << "Hello, world!"; });
ExpectedOs << "[B:2] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " Hello, world!\n";
ExpectedOs << "[B " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
<< " 2] Hello, world!\n";
EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
Str.clear();
StrExpected.clear();
Expand Down
15 changes: 9 additions & 6 deletions mlir/test/IR/test-pattern-logging-listener.mlir
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
// REQUIRES: asserts
// RUN: mlir-opt %s --test-walk-pattern-rewrite-driver \
// RUN: --allow-unregistered-dialect --debug-only=pattern-logging-listener 2>&1 | FileCheck %s
// RUN: --debug-only=pattern-logging-listener 2>&1 | FileCheck %s

// Check that when replacing an op with a new op, we get appropriate
// pattern-logging lines. The use of check same is to avoid the complexity of
// matching the anonymous namespace prefix, which can be one of {anonymous} vs
// {anonymous_namespace} vs `anonymous_namespace` (and maybe others?) on the
// various platforms.

// CHECK: [pattern-logging-listener:1]
// Changing the reference output here need to also plan on updating the post-processing
// script here: https://github.com/llvm/mlir-www/blob/main/.github%2Fworkflows%2Fmain.yml#L71-L72

// CHECK: [pattern-logging-listener PatternLoggingListener.cpp:10 1]
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationInserted | test.new_op
// CHECK: [pattern-logging-listener:1]
// CHECK: [pattern-logging-listener PatternLoggingListener.cpp:31 1]
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationReplaced (with values) | test.replace_with_new_op
// CHECK: [pattern-logging-listener:1]
// CHECK: [pattern-logging-listener PatternLoggingListener.cpp:17 1]
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationModified | arith.addi
// CHECK: [pattern-logging-listener:1]
// CHECK: [pattern-logging-listener PatternLoggingListener.cpp:17 1]
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationModified | arith.addi
// CHECK: [pattern-logging-listener:1]
// CHECK: [pattern-logging-listener PatternLoggingListener.cpp:38 1]
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationErased | test.replace_with_new_op
func.func @replace_with_new_op() -> i32 {
%a = "test.replace_with_new_op"() : () -> (i32)
Expand Down