Skip to content

Commit 9c82f87

Browse files
authored
Introduce a "log level" support for DEBUG_TYPE (#150855)
This allows to set an optional integer level for a given debug type. The string format is `type[:level]`, and the integer is interpreted as such: - if not provided: all debugging for this debug type is enabled. - if >0: all debug that is < to the level is enabled. - if 0: same as for >0 but also does not disable the other debug-types, it acts as a negative filter. The LDBG() macro is updated to accept an optional log level to illustrate the feature. Here is the expected behavior: LDBG() << "A"; // Identical to LDBG(1) << "A"; LDBG(2) << "B"; With `--debug-only=some_type`: we'll see A and B in the output. With `--debug-only=some_type:1`: we'll see A but not B in the output. With `--debug-only=some_type:2`: we'll see A and B in the output. (same with any level above 2) With `--debug-only=some_type:0`: we'll see neither A nor B in the output, but we'll see any other logging for other debug types.
1 parent b232277 commit 9c82f87

File tree

6 files changed

+166
-54
lines changed

6 files changed

+166
-54
lines changed

llvm/include/llvm/Support/Debug.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,19 @@ class raw_ostream;
3939
/// isCurrentDebugType - Return true if the specified string is the debug type
4040
/// specified on the command line, or if none was specified on the command line
4141
/// with the -debug-only=X option.
42-
///
43-
bool isCurrentDebugType(const char *Type);
42+
/// An optional level can be provided to control the verbosity of the output.
43+
/// If the provided level is not 0 and user specified a level below the provided
44+
/// level, return false.
45+
bool isCurrentDebugType(const char *Type, int Level = 0);
4446

4547
/// setCurrentDebugType - Set the current debug type, as if the -debug-only=X
4648
/// option were specified. Note that DebugFlag also needs to be set to true for
4749
/// debug output to be produced.
48-
///
50+
/// The debug type format is "type[:level]", where the level is an optional
51+
/// integer. If a level is provided, the debug output is enabled only if the
52+
/// user specified a level at least as high as the provided level.
53+
/// 0 is a special level that acts as an opt-out for this specific debug type
54+
/// without affecting the other debug output.
4955
void setCurrentDebugType(const char *Type);
5056

5157
/// setCurrentDebugTypes - Set the current debug type, as if the

llvm/include/llvm/Support/DebugLog.h

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,63 @@
1919
namespace llvm {
2020
#ifndef NDEBUG
2121

22-
// Output with given inputs and trailing newline. E.g.,
22+
// LDBG() is a macro that can be used as a raw_ostream for debugging.
23+
// It will stream the output to the dbgs() stream, with a prefix of the
24+
// debug type and the file and line number. A trailing newline is added to the
25+
// output automatically. If the streamed content contains a newline, the prefix
26+
// is added to each beginning of a new line. Nothing is printed if the debug
27+
// output is not enabled or the debug type does not match.
28+
//
29+
// E.g.,
2330
// LDBG() << "Bitset contains: " << Bitset;
24-
// is equivalent to
25-
// LLVM_DEBUG(dbgs() << DEBUG_TYPE << " [" << __FILE__ << ":" << __LINE__
26-
// << "] " << "Bitset contains: " << Bitset << "\n");
27-
#define LDBG() DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), DEBUG_TYPE)
28-
29-
#define DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, TYPE, FILE) \
30-
for (bool _c = (::llvm::DebugFlag && ::llvm::isCurrentDebugType(TYPE)); _c; \
31-
_c = false) \
31+
// is somehow equivalent to
32+
// LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE << "] " << __FILE__ << ":" <<
33+
// __LINE__ << " "
34+
// << "Bitset contains: " << Bitset << "\n");
35+
//
36+
// An optional `level` argument can be provided to control the verbosity of the
37+
// output. The default level is 1, and is in increasing level of verbosity.
38+
//
39+
// The `level` argument can be a literal integer, or a macro that evaluates to
40+
// an integer.
41+
//
42+
#define LDBG(...) _GET_LDBG_MACRO(__VA_ARGS__)(__VA_ARGS__)
43+
44+
// Helper macros to choose the correct macro based on the number of arguments.
45+
#define LDBG_FUNC_CHOOSER(_f1, _f2, ...) _f2
46+
#define LDBG_FUNC_RECOMPOSER(argsWithParentheses) \
47+
LDBG_FUNC_CHOOSER argsWithParentheses
48+
#define LDBG_CHOOSE_FROM_ARG_COUNT(...) \
49+
LDBG_FUNC_RECOMPOSER((__VA_ARGS__, LDBG_LOG_LEVEL, ))
50+
#define LDBG_NO_ARG_EXPANDER() , LDBG_LOG_LEVEL_1
51+
#define _GET_LDBG_MACRO(...) \
52+
LDBG_CHOOSE_FROM_ARG_COUNT(LDBG_NO_ARG_EXPANDER __VA_ARGS__())
53+
54+
// Dispatch macros to support the `level` argument or none (default to 1)
55+
#define LDBG_LOG_LEVEL(LEVEL) \
56+
DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), LEVEL, DEBUG_TYPE)
57+
#define LDBG_LOG_LEVEL_1() LDBG_LOG_LEVEL(1)
58+
59+
#define DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL, TYPE, FILE, \
60+
LINE) \
61+
for (bool _c = \
62+
(::llvm::DebugFlag && ::llvm::isCurrentDebugType(TYPE, LEVEL)); \
63+
_c; _c = false) \
3264
::llvm::impl::raw_ldbg_ostream{ \
33-
::llvm::impl::computePrefix(TYPE, FILE, __LINE__), (STREAM)} \
65+
::llvm::impl::computePrefix(TYPE, FILE, LINE, LEVEL), (STREAM)} \
3466
.asLvalue()
67+
68+
#define DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, FILE) \
69+
DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL, TYPE, FILE, __LINE__)
3570
// When __SHORT_FILE__ is not defined, the File is the full path,
3671
// otherwise __SHORT_FILE__ is defined in CMake to provide the file name
3772
// without the path prefix.
3873
#if defined(__SHORT_FILE__)
39-
#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, TYPE) \
40-
DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, TYPE, __SHORT_FILE__)
74+
#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, LEVEL, TYPE) \
75+
DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, __SHORT_FILE__)
4176
#else
42-
#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, TYPE) \
43-
DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, TYPE, \
77+
#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, LEVEL, TYPE) \
78+
DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, \
4479
::llvm::impl::getShortFileName(__FILE__))
4580
#endif
4681

@@ -119,19 +154,19 @@ getShortFileName(const char *path) {
119154
/// "[DebugType] File:Line "
120155
/// Where the File is the file name without the path prefix.
121156
static LLVM_ATTRIBUTE_UNUSED std::string
122-
computePrefix(const char *DebugType, const char *File, int Line) {
157+
computePrefix(const char *DebugType, const char *File, int Line, int Level) {
123158
std::string Prefix;
124159
raw_string_ostream OsPrefix(Prefix);
125160
if (DebugType)
126-
OsPrefix << "[" << DebugType << "] ";
161+
OsPrefix << "[" << DebugType << ":" << Level << "] ";
127162
OsPrefix << File << ":" << Line << " ";
128163
return OsPrefix.str();
129164
}
130165
} // end namespace impl
131166
#else
132167
// As others in Debug, When compiling without assertions, the -debug-* options
133168
// and all inputs too LDBG() are ignored.
134-
#define LDBG() \
169+
#define LDBG(...) \
135170
for (bool _c = false; _c; _c = false) \
136171
::llvm::nulls()
137172
#endif

llvm/lib/Support/Debug.cpp

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@
2424
//===----------------------------------------------------------------------===//
2525

2626
#include "llvm/Support/Debug.h"
27+
#include "llvm/ADT/StringExtras.h"
2728
#include "llvm/Support/CommandLine.h"
2829
#include "llvm/Support/ManagedStatic.h"
2930
#include "llvm/Support/Signals.h"
3031
#include "llvm/Support/circular_raw_ostream.h"
3132
#include "llvm/Support/raw_ostream.h"
33+
#include <utility>
3234

3335
#include "DebugOptions.h"
3436

@@ -38,27 +40,62 @@
3840

3941
using namespace llvm;
4042

43+
/// Parse a debug type string into a pair of the debug type and the debug level.
44+
/// The expected format is "type[:level]", where the level is an optional
45+
/// integer.
46+
static std::pair<std::string, std::optional<int>>
47+
parseDebugType(StringRef DbgType) {
48+
std::optional<int> Level;
49+
size_t ColonPos = DbgType.find(':');
50+
if (ColonPos != StringRef::npos) {
51+
StringRef LevelStr = DbgType.substr(ColonPos + 1);
52+
DbgType = DbgType.take_front(ColonPos);
53+
if (LevelStr.empty())
54+
Level = 0;
55+
else {
56+
int parsedLevel;
57+
if (to_integer(LevelStr, parsedLevel, 10))
58+
Level = parsedLevel;
59+
}
60+
}
61+
return std::make_pair(DbgType.str(), Level);
62+
}
63+
4164
// Even though LLVM might be built with NDEBUG, define symbols that the code
4265
// built without NDEBUG can depend on via the llvm/Support/Debug.h header.
4366
namespace llvm {
4467
/// Exported boolean set by the -debug option.
4568
bool DebugFlag = false;
4669

47-
static ManagedStatic<std::vector<std::string>> CurrentDebugType;
70+
/// The current debug type and an optional debug level.
71+
/// The debug level is the verbosity of the debug output.
72+
/// 0 is a special level that acts as an opt-out for this specific debug type.
73+
/// If provided, the debug output is enabled only if the user specified a level
74+
/// at least as high as the provided level.
75+
static ManagedStatic<std::vector<std::pair<std::string, std::optional<int>>>>
76+
CurrentDebugType;
4877

4978
/// Return true if the specified string is the debug type
5079
/// specified on the command line, or if none was specified on the command line
5180
/// with the -debug-only=X option.
52-
bool isCurrentDebugType(const char *DebugType) {
81+
bool isCurrentDebugType(const char *DebugType, int Level) {
5382
if (CurrentDebugType->empty())
5483
return true;
84+
// Track if there is at least one debug type with a level, this is used
85+
// to allow to opt-out of some DebugType and leaving all the others enabled.
86+
bool HasEnabledDebugType = false;
5587
// See if DebugType is in list. Note: do not use find() as that forces us to
5688
// unnecessarily create an std::string instance.
57-
for (auto &d : *CurrentDebugType) {
58-
if (d == DebugType)
89+
for (auto &D : *CurrentDebugType) {
90+
HasEnabledDebugType =
91+
HasEnabledDebugType || (!D.second.has_value() || D.second.value() > 0);
92+
if (D.first != DebugType)
93+
continue;
94+
if (!D.second.has_value())
5995
return true;
96+
return D.second >= Level;
6097
}
61-
return false;
98+
return !HasEnabledDebugType;
6299
}
63100

64101
/// Set the current debug type, as if the -debug-only=X
@@ -73,8 +110,11 @@ void setCurrentDebugType(const char *Type) {
73110

74111
void setCurrentDebugTypes(const char **Types, unsigned Count) {
75112
CurrentDebugType->clear();
76-
llvm::append_range(*CurrentDebugType, ArrayRef(Types, Count));
113+
CurrentDebugType->reserve(Count);
114+
for (const char *Type : ArrayRef(Types, Count))
115+
CurrentDebugType->push_back(parseDebugType(Type));
77116
}
117+
78118
} // namespace llvm
79119

80120
// All Debug.h functionality is a no-op in NDEBUG mode.
@@ -114,10 +154,10 @@ struct DebugOnlyOpt {
114154
if (Val.empty())
115155
return;
116156
DebugFlag = true;
117-
SmallVector<StringRef,8> dbgTypes;
118-
StringRef(Val).split(dbgTypes, ',', -1, false);
119-
for (auto dbgType : dbgTypes)
120-
CurrentDebugType->push_back(std::string(dbgType));
157+
SmallVector<StringRef, 8> DbgTypes;
158+
StringRef(Val).split(DbgTypes, ',', -1, false);
159+
for (auto DbgType : DbgTypes)
160+
CurrentDebugType->push_back(parseDebugType(DbgType));
121161
}
122162
};
123163
} // namespace
@@ -129,8 +169,13 @@ struct CreateDebugOnly {
129169
static void *call() {
130170
return new cl::opt<DebugOnlyOpt, true, cl::parser<std::string>>(
131171
"debug-only",
132-
cl::desc("Enable a specific type of debug output (comma separated list "
133-
"of types)"),
172+
cl::desc(
173+
"Enable a specific type of debug output (comma separated list "
174+
"of types using the format \"type[:level]\", where the level "
175+
"is an optional integer. The level can be set to 1, 2, 3, etc. to "
176+
"control the verbosity of the output. Setting a debug-type level "
177+
"to zero acts as an opt-out for this specific debug-type without "
178+
"affecting the others."),
134179
cl::Hidden, cl::value_desc("debug string"),
135180
cl::location(DebugOnlyOptLoc), cl::ValueRequired);
136181
}

llvm/unittests/Support/DebugLogTest.cpp

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#undef __SHORT_FILE__
1313

1414
#include "llvm/Support/DebugLog.h"
15+
#include "llvm/ADT/Sequence.h"
1516
#include "llvm/Support/raw_ostream.h"
1617
#include "gmock/gmock.h"
1718
#include "gtest/gtest.h"
@@ -31,7 +32,7 @@ TEST(DebugLogTest, Basic) {
3132
{
3233
std::string str;
3334
raw_string_ostream os(str);
34-
DEBUGLOG_WITH_STREAM_AND_TYPE(os, nullptr) << "NoType";
35+
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, nullptr) << "NoType";
3536
EXPECT_FALSE(StringRef(os.str()).starts_with('['));
3637
EXPECT_TRUE(StringRef(os.str()).ends_with("NoType\n"));
3738
}
@@ -40,8 +41,8 @@ TEST(DebugLogTest, Basic) {
4041
{
4142
std::string str;
4243
raw_string_ostream os(str);
43-
DEBUGLOG_WITH_STREAM_AND_TYPE(os, "A") << "A";
44-
DEBUGLOG_WITH_STREAM_AND_TYPE(os, "B") << "B";
44+
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "A";
45+
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "B") << "B";
4546
EXPECT_TRUE(StringRef(os.str()).starts_with('['));
4647
EXPECT_THAT(os.str(), AllOf(HasSubstr("A\n"), HasSubstr("B\n")));
4748
}
@@ -52,22 +53,55 @@ TEST(DebugLogTest, Basic) {
5253
raw_string_ostream os(str);
5354
// Just check that the macro doesn't result in dangling else.
5455
if (true)
55-
DEBUGLOG_WITH_STREAM_AND_TYPE(os, "A") << "A";
56+
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "A";
5657
else
57-
DEBUGLOG_WITH_STREAM_AND_TYPE(os, "A") << "B";
58-
DEBUGLOG_WITH_STREAM_AND_TYPE(os, "B") << "B";
58+
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "B";
59+
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "B") << "B";
5960
EXPECT_THAT(os.str(), AllOf(HasSubstr("A\n"), Not(HasSubstr("B\n"))));
6061

6162
int count = 0;
6263
auto inc = [&]() { return ++count; };
6364
EXPECT_THAT(count, Eq(0));
64-
DEBUGLOG_WITH_STREAM_AND_TYPE(os, "A") << inc();
65+
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << inc();
6566
EXPECT_THAT(count, Eq(1));
66-
DEBUGLOG_WITH_STREAM_AND_TYPE(os, "B") << inc();
67+
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "B") << inc();
6768
EXPECT_THAT(count, Eq(1));
6869
}
6970
}
7071

72+
TEST(DebugLogTest, BasicWithLevel) {
73+
llvm::DebugFlag = true;
74+
// We expect A to be always printed, B to be printed only when level is 1 or
75+
// below, and C to be printed only when level is 0 or below.
76+
static const char *DT[] = {"A", "B:1", "C:"};
77+
78+
setCurrentDebugTypes(DT, sizeof(DT) / sizeof(DT[0]));
79+
std::string str;
80+
raw_string_ostream os(str);
81+
for (auto type : {"A", "B", "C", "D"})
82+
for (int level : llvm::seq<int>(0, 4))
83+
DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(os, level, type, type, level)
84+
<< level;
85+
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 "
86+
"3\n[B:0] B:0 0\n[B:1] B:1 1\n[C:0] C:0 0\n");
87+
}
88+
89+
TEST(DebugLogTest, NegativeLevel) {
90+
llvm::DebugFlag = true;
91+
// Test the special behavior when all the levels are 0.
92+
// In this case we expect all the debug types to be printed.
93+
static const char *DT[] = {"A:"};
94+
95+
setCurrentDebugTypes(DT, sizeof(DT) / sizeof(DT[0]));
96+
std::string str;
97+
raw_string_ostream os(str);
98+
for (auto type : {"A", "B"})
99+
for (int level : llvm::seq<int>(0, 2))
100+
DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(os, level, type, type, level)
101+
<< level;
102+
EXPECT_EQ(os.str(), "[A:0] A:0 0\n[B:0] B:0 0\n[B:1] B:1 1\n");
103+
}
104+
71105
TEST(DebugLogTest, StreamPrefix) {
72106
llvm::DebugFlag = true;
73107
static const char *DT[] = {"A", "B"};

mlir/lib/Dialect/Transform/Interfaces/TransformInterfaces.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,8 @@
2121
#include "llvm/Support/InterleavedRange.h"
2222

2323
#define DEBUG_TYPE "transform-dialect"
24-
#define DEBUG_TYPE_FULL "transform-dialect-full"
2524
#define DEBUG_PRINT_AFTER_ALL "transform-dialect-print-top-level-after-all"
26-
#ifndef NDEBUG
27-
#define FULL_LDBG(X) \
28-
DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), DEBUG_TYPE_FULL)
29-
#else
30-
#define FULL_LDBG(X) \
31-
for (bool _c = false; _c; _c = false) \
32-
::llvm::nulls()
33-
#endif
25+
#define FULL_LDBG() LDBG(4)
3426

3527
using namespace mlir;
3628

mlir/test/IR/test-pattern-logging-listener.mlir

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
// {anonymous_namespace} vs `anonymous_namespace` (and maybe others?) on the
99
// various platforms.
1010

11-
// CHECK: [pattern-logging-listener]
11+
// CHECK: [pattern-logging-listener:1]
1212
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationInserted | test.new_op
13-
// CHECK: [pattern-logging-listener]
13+
// CHECK: [pattern-logging-listener:1]
1414
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationReplaced (with values) | test.replace_with_new_op
15-
// CHECK: [pattern-logging-listener]
15+
// CHECK: [pattern-logging-listener:1]
1616
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationModified | arith.addi
17-
// CHECK: [pattern-logging-listener]
17+
// CHECK: [pattern-logging-listener:1]
1818
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationModified | arith.addi
19-
// CHECK: [pattern-logging-listener]
19+
// CHECK: [pattern-logging-listener:1]
2020
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationErased | test.replace_with_new_op
2121
func.func @replace_with_new_op() -> i32 {
2222
%a = "test.replace_with_new_op"() : () -> (i32)

0 commit comments

Comments
 (0)