Skip to content

Commit c84f34b

Browse files
Introduce LDBG_OS() macro as a variant of LDBG() (#157194)
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]>
1 parent 48661a4 commit c84f34b

File tree

4 files changed

+330
-83
lines changed

4 files changed

+330
-83
lines changed

llvm/include/llvm/Support/Debug.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,6 @@ class raw_ostream;
4444
/// level, return false.
4545
LLVM_ABI bool isCurrentDebugType(const char *Type, int Level = 0);
4646

47-
/// Overload allowing to swap the order of the Type and Level arguments.
48-
LLVM_ABI inline bool isCurrentDebugType(int Level, const char *Type) {
49-
return isCurrentDebugType(Type, Level);
50-
}
51-
5247
/// setCurrentDebugType - Set the current debug type, as if the -debug-only=X
5348
/// option were specified. Note that DebugFlag also needs to be set to true for
5449
/// debug output to be produced.

llvm/include/llvm/Support/DebugLog.h

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

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.,
30-
// LDBG() << "Bitset contains: " << Bitset;
31-
// is somehow equivalent to
32-
// LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE << "] " << __FILE__ << ":" <<
33-
// __LINE__ << " "
34-
// << "Bitset contains: " << Bitset << "\n");
35-
//
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.,
30+
/// LDBG() << "Bitset contains: " << Bitset;
31+
/// is equivalent to
32+
/// LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE << "] " << __FILE__ << ":" <<
33+
/// __LINE__ << " "
34+
/// << "Bitset contains: " << Bitset << "\n");
35+
///
3636
// 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-
// An optional `type` argument can be provided to control the debug type. The
43-
// default type is DEBUG_TYPE. The `type` argument can be a literal string, or a
44-
// macro that evaluates to a string.
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+
/// An optional `type` argument can be provided to control the debug type. The
43+
/// default type is DEBUG_TYPE. The `type` argument can be a literal string, or
44+
/// a macro that evaluates to a string.
45+
///
46+
/// E.g.,
47+
/// LDBG(2) << "Bitset contains: " << Bitset;
48+
/// LDBG("debug_type") << "Bitset contains: " << Bitset;
49+
/// LDBG("debug_type", 2) << "Bitset contains: " << Bitset;
4550
#define LDBG(...) _GET_LDBG_MACRO(__VA_ARGS__)(__VA_ARGS__)
4651

47-
// Helper macros to choose the correct macro based on the number of arguments.
48-
#define LDBG_FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
49-
#define LDBG_FUNC_RECOMPOSER(argsWithParentheses) \
50-
LDBG_FUNC_CHOOSER argsWithParentheses
51-
#define LDBG_CHOOSE_FROM_ARG_COUNT(...) \
52-
LDBG_FUNC_RECOMPOSER( \
53-
(__VA_ARGS__, LDBG_LOG_LEVEL_WITH_TYPE, LDBG_LOG_LEVEL, ))
54-
#define LDBG_NO_ARG_EXPANDER() , , LDBG_LOG_LEVEL_1
55-
#define _GET_LDBG_MACRO(...) \
56-
LDBG_CHOOSE_FROM_ARG_COUNT(LDBG_NO_ARG_EXPANDER __VA_ARGS__())
57-
58-
// Dispatch macros to support the `level` argument or none (default to 1)
59-
#define LDBG_LOG_LEVEL(LEVEL) \
60-
DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), LEVEL, DEBUG_TYPE)
61-
#define LDBG_LOG_LEVEL_1() LDBG_LOG_LEVEL(1)
62-
// This macro is a helper when LDBG() is called with 2 arguments.
63-
// In this case we want to allow the order of the arguments to be swapped.
64-
// We rely on the fact that the `level` argument is an integer, and the `type`
65-
// is a string and dispatch to a C++ API that is overloaded.
66-
#define LDBG_LOG_LEVEL_WITH_TYPE(LEVEL_OR_TYPE, TYPE_OR_LEVEL) \
67-
DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), (LEVEL_OR_TYPE), (TYPE_OR_LEVEL))
52+
/// LDBG_OS() is a macro that behaves like LDBG() but instead of directly using
53+
/// it to stream the output, it takes a callback function that will be called
54+
/// with a raw_ostream.
55+
/// This is useful when you need to pass a `raw_ostream` to a helper function to
56+
/// be able to print (when the `<<` operator is not available).
57+
///
58+
/// E.g.,
59+
/// LDBG_OS([&] (raw_ostream &Os) {
60+
/// Os << "Pass Manager contains: ";
61+
/// pm.printAsTextual(Os);
62+
/// });
63+
///
64+
/// Just like LDBG(), it optionally accepts a `level` and `type` arguments.
65+
/// E.g.,
66+
/// LDBG_OS(2, [&] (raw_ostream &Os) { ... });
67+
/// LDBG_OS("debug_type", [&] (raw_ostream &Os) { ... });
68+
/// LDBG_OS("debug_type", 2, [&] (raw_ostream &Os) { ... });
69+
///
70+
#define LDBG_OS(...) _GET_LDBG_OS_MACRO(__VA_ARGS__)(__VA_ARGS__)
6871

6972
// We want the filename without the full path. We are using the __FILE__ macro
7073
// and a constexpr function to strip the path prefix. We can avoid the frontend
@@ -76,29 +79,163 @@ namespace llvm {
7679
#define __LLVM_FILE_NAME__ ::llvm::impl::getShortFileName(__FILE__)
7780
#endif
7881

79-
#define DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL, TYPE, FILE, \
80-
LINE) \
81-
for (bool _c = \
82-
(::llvm::DebugFlag && ::llvm::isCurrentDebugType(TYPE, LEVEL)); \
82+
// Everything below are implementation details of the macros above.
83+
namespace impl {
84+
85+
/// This macro expands to the stream to use for output, we use a macro to allow
86+
/// unit-testing to override.
87+
#define LDBG_STREAM ::llvm::dbgs()
88+
89+
// ----------------------------------------------------------------------------
90+
// LDBG() implementation
91+
// ----------------------------------------------------------------------------
92+
93+
// Helper macros to choose the correct LDBG() macro based on the number of
94+
// arguments.
95+
#define LDBG_FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
96+
#define LDBG_FUNC_RECOMPOSER(argsWithParentheses) \
97+
LDBG_FUNC_CHOOSER argsWithParentheses
98+
#define LDBG_CHOOSE_FROM_ARG_COUNT(...) \
99+
LDBG_FUNC_RECOMPOSER((__VA_ARGS__, LDBG_TYPE_AND_LEVEL, LDBG_LEVEL_OR_TYPE, ))
100+
#define LDBG_NO_ARG_EXPANDER() , , LDBG_NO_ARG
101+
#define _GET_LDBG_MACRO(...) \
102+
LDBG_CHOOSE_FROM_ARG_COUNT(LDBG_NO_ARG_EXPANDER __VA_ARGS__())
103+
104+
/// This macro is the core of the LDBG() implementation. It is used to print the
105+
/// debug output with the given stream, level, type, file, and line number.
106+
#define LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(STREAM, LEVEL_OR_TYPE, \
107+
TYPE_OR_LEVEL, FILE, LINE) \
108+
for (bool _c = ::llvm::DebugFlag && ::llvm::impl::ldbgIsCurrentDebugType( \
109+
TYPE_OR_LEVEL, LEVEL_OR_TYPE); \
83110
_c; _c = false) \
84-
for (::llvm::impl::raw_ldbg_ostream LdbgOS{ \
85-
::llvm::impl::computePrefix(TYPE, FILE, LINE, LEVEL), (STREAM)}; \
86-
_c; _c = false) \
87-
::llvm::impl::RAIINewLineStream{LdbgOS}.asLvalue()
111+
::llvm::impl::raw_ldbg_ostream{ \
112+
::llvm::impl::computePrefix(TYPE_OR_LEVEL, FILE, LINE, LEVEL_OR_TYPE), \
113+
(STREAM), /*ShouldPrefixNextString=*/true, \
114+
/*ShouldEmitNewLineOnDestruction=*/true} \
115+
.asLvalue()
88116

89-
#define DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, FILE) \
90-
DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL, TYPE, FILE, __LINE__)
91-
#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, LEVEL, TYPE) \
92-
DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, __LLVM_FILE_NAME__)
117+
/// These macros are helpers to implement LDBG() with an increasing amount of
118+
/// optional arguments made explicit.
119+
#define LDBG_STREAM_LEVEL_TYPE_AND_FILE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \
120+
FILE) \
121+
LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \
122+
FILE, __LINE__)
123+
#define LDGB_STREAM_LEVEL_AND_TYPE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL) \
124+
LDBG_STREAM_LEVEL_TYPE_AND_FILE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \
125+
__LLVM_FILE_NAME__)
126+
/// This macro is a helper when LDBG() is called with 2 arguments.
127+
/// In this case we want to force the first argument to be the type for
128+
/// consistency in the codebase.
129+
/// We trick this by casting the first argument to a (const char *) which
130+
/// won't compile with an int.
131+
#define LDBG_TYPE_AND_LEVEL(TYPE, LEVEL) \
132+
LDGB_STREAM_LEVEL_AND_TYPE(LDBG_STREAM, static_cast<const char *>(TYPE), \
133+
(LEVEL))
93134

94-
namespace impl {
135+
/// When a single argument is provided. This can be either a level or the debug
136+
/// type. If a level is provided, we default the debug type to DEBUG_TYPE, if a
137+
/// string is provided, we default the level to 1.
138+
#define LDBG_LEVEL_OR_TYPE(LEVEL_OR_TYPE) \
139+
LDGB_STREAM_LEVEL_AND_TYPE(LDBG_STREAM, (LEVEL_OR_TYPE), \
140+
LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE))
141+
#define LDBG_NO_ARG() LDBG_LEVEL_OR_TYPE(1)
142+
143+
// ----------------------------------------------------------------------------
144+
// LDBG_OS() implementation
145+
// ----------------------------------------------------------------------------
146+
147+
// Helper macros to choose the correct LDBG_OS() macro based on the number of
148+
// arguments.
149+
#define LDBG_OS_FUNC_CHOOSER(_f1, _f2, _f3, _f4, ...) _f4
150+
#define LDBG_OS_FUNC_RECOMPOSER(argsWithParentheses) \
151+
LDBG_OS_FUNC_CHOOSER argsWithParentheses
152+
#define LDBG_OS_CHOOSE_FROM_ARG_COUNT(...) \
153+
LDBG_OS_FUNC_RECOMPOSER((__VA_ARGS__, LDBG_OS_TYPE_AND_LEVEL_AND_CALLBACK, \
154+
LDBG_OS_LEVEL_OR_TYPE_AND_CALLBACK, \
155+
LDBG_OS_CALLBACK, ))
156+
#define LDBG_OS_NO_ARG_EXPANDER() , , , LDBG_OS_CALLBACK
157+
#define _GET_LDBG_OS_MACRO(...) \
158+
LDBG_OS_CHOOSE_FROM_ARG_COUNT(LDBG_OS_NO_ARG_EXPANDER __VA_ARGS__())
159+
160+
/// This macro is the core of the LDBG_OS() macros. It is used to print the
161+
/// debug output with the given stream, level, type, file, and line number.
162+
#define LDBG_OS_IMPL(TYPE_OR_LEVEL, LEVEL_OR_TYPE, CALLBACK, STREAM, FILE, \
163+
LINE) \
164+
if (::llvm::DebugFlag && \
165+
::llvm::impl::ldbgIsCurrentDebugType(TYPE_OR_LEVEL, LEVEL_OR_TYPE)) { \
166+
::llvm::impl::raw_ldbg_ostream LdbgOS{ \
167+
::llvm::impl::computePrefix(TYPE_OR_LEVEL, FILE, LINE, LEVEL_OR_TYPE), \
168+
(STREAM), /*ShouldPrefixNextString=*/true, \
169+
/*ShouldEmitNewLineOnDestruction=*/true}; \
170+
CALLBACK(LdbgOS); \
171+
}
172+
173+
#define LDBG_OS_TYPE_AND_LEVEL_AND_CALLBACK(TYPE, LEVEL, CALLBACK) \
174+
LDBG_OS_IMPL(static_cast<const char *>(TYPE), LEVEL, CALLBACK, LDBG_STREAM, \
175+
__LLVM_FILE_NAME__, __LINE__)
176+
#define LDBG_OS_LEVEL_OR_TYPE_AND_CALLBACK(LEVEL_OR_TYPE, CALLBACK) \
177+
LDBG_OS_IMPL(LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE), LEVEL_OR_TYPE, \
178+
CALLBACK, LDBG_STREAM, __LLVM_FILE_NAME__, __LINE__)
179+
#define LDBG_OS_CALLBACK(CALLBACK) \
180+
LDBG_OS_LEVEL_OR_TYPE_AND_CALLBACK(1, CALLBACK)
181+
182+
// ----------------------------------------------------------------------------
183+
// General Helpers for the implementation above
184+
// ----------------------------------------------------------------------------
185+
186+
/// Return the stringified macro as a StringRef.
187+
/// Also, strip out potential surrounding quotes: this comes from an artifact of
188+
/// the macro stringification, if DEBUG_TYPE is undefined we get the string
189+
/// "DEBUG_TYPE", however if it is defined we get the string with the quotes.
190+
/// For example if DEBUG_TYPE is "foo", we get "\"foo\"" but we want to return
191+
/// "foo" here.
192+
constexpr ::llvm::StringRef strip_quotes(const char *Str) {
193+
::llvm::StringRef S(Str);
194+
if (Str[0] == '"' && Str[S.size() - 1] == '"')
195+
return StringRef(Str + 1, S.size() - 2);
196+
return S;
197+
}
198+
199+
/// Helper to provide the default level (=1) or type (=DEBUG_TYPE). This is used
200+
/// when a single argument is passed to LDBG() (or LDBG_OS()), if it is an
201+
/// integer we return DEBUG_TYPE and if it is a string we return 1. This fails
202+
/// with a static_assert if we pass an integer and DEBUG_TYPE is not defined.
203+
#define LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE) \
204+
[](auto LevelOrType) { \
205+
if constexpr (std::is_integral_v<decltype(LevelOrType)>) { \
206+
constexpr const char *DebugType = LDBG_GET_DEBUG_TYPE_STR(); \
207+
if constexpr (DebugType[0] == '"') \
208+
return ::llvm::impl::strip_quotes(DebugType); \
209+
else \
210+
static_assert(false, "DEBUG_TYPE is not defined"); \
211+
} else { \
212+
return 1; \
213+
} \
214+
}(LEVEL_OR_TYPE)
215+
216+
/// Helpers to get DEBUG_TYPE as a StringRef, even when DEBUG_TYPE is not
217+
/// defined (in which case it expands to "DEBUG_TYPE")
218+
#define LDBG_GET_DEBUG_TYPE_STR__(X) #X
219+
#define LDBG_GET_DEBUG_TYPE_STR_(X) LDBG_GET_DEBUG_TYPE_STR__(X)
220+
#define LDBG_GET_DEBUG_TYPE_STR() LDBG_GET_DEBUG_TYPE_STR_(DEBUG_TYPE)
221+
222+
/// Helper to call isCurrentDebugType with a StringRef.
223+
static LLVM_ATTRIBUTE_UNUSED bool ldbgIsCurrentDebugType(StringRef Type,
224+
int Level) {
225+
return ::llvm::isCurrentDebugType(Type.str().c_str(), Level);
226+
}
227+
static LLVM_ATTRIBUTE_UNUSED bool ldbgIsCurrentDebugType(int Level,
228+
StringRef Type) {
229+
return ::llvm::isCurrentDebugType(Type.str().c_str(), Level);
230+
}
95231

96232
/// A raw_ostream that tracks `\n` and print the prefix after each
97233
/// newline.
98234
class LLVM_ABI raw_ldbg_ostream final : public raw_ostream {
99235
std::string Prefix;
100236
raw_ostream &Os;
101237
bool ShouldPrefixNextString;
238+
bool ShouldEmitNewLineOnDestruction;
102239

103240
/// Split the line on newlines and insert the prefix before each
104241
/// newline. Forward everything to the underlying stream.
@@ -131,12 +268,17 @@ class LLVM_ABI raw_ldbg_ostream final : public raw_ostream {
131268

132269
public:
133270
explicit raw_ldbg_ostream(std::string Prefix, raw_ostream &Os,
134-
bool ShouldPrefixNextString = true)
271+
bool ShouldPrefixNextString = true,
272+
bool ShouldEmitNewLineOnDestruction = false)
135273
: Prefix(std::move(Prefix)), Os(Os),
136-
ShouldPrefixNextString(ShouldPrefixNextString) {
274+
ShouldPrefixNextString(ShouldPrefixNextString),
275+
ShouldEmitNewLineOnDestruction(ShouldEmitNewLineOnDestruction) {
137276
SetUnbuffered();
138277
}
139-
~raw_ldbg_ostream() final {}
278+
~raw_ldbg_ostream() final {
279+
if (ShouldEmitNewLineOnDestruction)
280+
Os << '\n';
281+
}
140282

141283
/// Forward the current_pos method to the underlying stream.
142284
uint64_t current_pos() const final { return Os.tell(); }
@@ -173,17 +315,17 @@ getShortFileName(const char *path) {
173315
/// "[DebugType] File:Line "
174316
/// Where the File is the file name without the path prefix.
175317
static LLVM_ATTRIBUTE_UNUSED std::string
176-
computePrefix(const char *DebugType, const char *File, int Line, int Level) {
318+
computePrefix(StringRef DebugType, const char *File, int Line, int Level) {
177319
std::string Prefix;
178320
raw_string_ostream OsPrefix(Prefix);
179-
if (DebugType)
321+
if (!DebugType.empty())
180322
OsPrefix << "[" << DebugType << ":" << Level << "] ";
181323
OsPrefix << File << ":" << Line << " ";
182324
return OsPrefix.str();
183325
}
184326
/// Overload allowing to swap the order of the DebugType and Level arguments.
185327
static LLVM_ATTRIBUTE_UNUSED std::string
186-
computePrefix(int Level, const char *File, int Line, const char *DebugType) {
328+
computePrefix(int Level, const char *File, int Line, StringRef DebugType) {
187329
return computePrefix(DebugType, File, Line, Level);
188330
}
189331

@@ -194,6 +336,7 @@ computePrefix(int Level, const char *File, int Line, const char *DebugType) {
194336
#define LDBG(...) \
195337
for (bool _c = false; _c; _c = false) \
196338
::llvm::nulls()
339+
#define LDBG_OS(...)
197340
#endif
198341
} // end namespace llvm
199342

0 commit comments

Comments
 (0)