Skip to content

Commit 7606fa3

Browse files
committed
Introduce LDBG_OS() macro as a variant of LDBG()
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.
1 parent ba4ce60 commit 7606fa3

File tree

3 files changed

+321
-58
lines changed

3 files changed

+321
-58
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: 194 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -19,52 +19,81 @@
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 somehow 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(2, "debug_type") << "Bitset contains: " << Bitset;
50+
/// LDBG("debug_type", 2) << "Bitset contains: " << Bitset;
4551
#define LDBG(...) _GET_LDBG_MACRO(__VA_ARGS__)(__VA_ARGS__)
4652

47-
// Helper macros to choose the correct macro based on the number of arguments.
53+
/// LDBG_OS() is a macro that behaves like LDBG() but instead of directly using
54+
/// it to stream the output, it takes a callback function that will be called
55+
/// with a raw_ostream.
56+
/// This is useful when you need to pass a `raw_ostream` to a helper function to
57+
/// be able to print (when the `<<` operator is not available).
58+
///
59+
/// E.g.,
60+
/// LDBG_OS([&] (raw_ostream &Os) {
61+
/// Os << "Pass Manager contains: ";
62+
/// pm.printAsTextual(Os);
63+
/// });
64+
///
65+
/// Just like LDBG(), it optionally accepts a `level` and `type` arguments.
66+
/// E.g.,
67+
/// LDBG_OS(2, [&] (raw_ostream &Os) { ... });
68+
/// LDBG_OS("debug_type", [&] (raw_ostream &Os) { ... });
69+
/// LDBG_OS(2, "debug_type", [&] (raw_ostream &Os) { ... });
70+
/// LDBG_OS("debug_type", 2, [&] (raw_ostream &Os) { ... });
71+
///
72+
#define LDBG_OS(...) _GET_LDBG_OS_MACRO(__VA_ARGS__)(__VA_ARGS__)
73+
74+
// Helper macros to choose the correct LDBG() macro based on the number of
75+
// arguments.
4876
#define LDBG_FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
4977
#define LDBG_FUNC_RECOMPOSER(argsWithParentheses) \
5078
LDBG_FUNC_CHOOSER argsWithParentheses
5179
#define LDBG_CHOOSE_FROM_ARG_COUNT(...) \
5280
LDBG_FUNC_RECOMPOSER( \
53-
(__VA_ARGS__, LDBG_LOG_LEVEL_WITH_TYPE, LDBG_LOG_LEVEL, ))
54-
#define LDBG_NO_ARG_EXPANDER() , , LDBG_LOG_LEVEL_1
81+
(__VA_ARGS__, LDBG_LOG_LEVEL_WITH_TYPE, LDBG_LOG_LEVEL_1_ARG, ))
82+
#define LDBG_NO_ARG_EXPANDER() , , LDBG_LOG_LEVEL_NO_ARG
5583
#define _GET_LDBG_MACRO(...) \
5684
LDBG_CHOOSE_FROM_ARG_COUNT(LDBG_NO_ARG_EXPANDER __VA_ARGS__())
5785

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))
86+
// Helper macros to choose the correct LDBG_OS() macro based on the number of
87+
// arguments.
88+
#define LDBG_OS_FUNC_CHOOSER(_f1, _f2, _f3, _f4, ...) _f4
89+
#define LDBG_OS_FUNC_RECOMPOSER(argsWithParentheses) \
90+
LDBG_OS_FUNC_CHOOSER argsWithParentheses
91+
#define LDBG_OS_CHOOSE_FROM_ARG_COUNT(...) \
92+
LDBG_OS_FUNC_RECOMPOSER( \
93+
(__VA_ARGS__, LDBG_OS_3_ARGS, LDBG_OS_2_ARGS, LDBG_OS_1_ARG, ))
94+
#define LDBG_OS_NO_ARG_EXPANDER() , , , LDBG_OS_1_ARG
95+
#define _GET_LDBG_OS_MACRO(...) \
96+
LDBG_OS_CHOOSE_FROM_ARG_COUNT(LDBG_OS_NO_ARG_EXPANDER __VA_ARGS__())
6897

6998
// We want the filename without the full path. We are using the __FILE__ macro
7099
// and a constexpr function to strip the path prefix. We can avoid the frontend
@@ -76,29 +105,137 @@ namespace llvm {
76105
#define __LLVM_FILE_NAME__ ::llvm::impl::getShortFileName(__FILE__)
77106
#endif
78107

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)); \
108+
/// These macros are detecting if the DEBUG_TYPE or LDBG_DEBUG_STREAM macros are
109+
/// defined. They are using a combination of preprocessor tricks and C++17
110+
// used-defined string literals to achieve this.
111+
// For example, if DEBUG_TYPE is defined to "foo", the preprocessor will expand
112+
// the macro and then stringify the result to
113+
// "foo"_LDBG_VARIABLE_CHECK
114+
// This dispatch to the user-defined string literal operator named
115+
// _LDBG_VARIABLE_CHECK which returns true. Otherwise it expands to
116+
// DEBUG_TYPE_LDBG_VARIABLE_CHECK which we define as a macro that returns false.
117+
#define LDBG_VARIABLE_CHECK_(VARIABLE, ...) VARIABLE##__VA_ARGS__
118+
#define LDBG_VARIABLE_CHECK(VARIABLE) \
119+
LDBG_VARIABLE_CHECK_(VARIABLE, _LDBG_VARIABLE_CHECK)
120+
// User-defined string literal operator for the LDBG_VARIABLE_CHECK macro.
121+
constexpr bool operator""_LDBG_VARIABLE_CHECK(const char *, std::size_t) {
122+
return true;
123+
}
124+
125+
#define IS_DEBUG_TYPE_DEFINED() LDBG_VARIABLE_CHECK(DEBUG_TYPE)
126+
#define DEBUG_TYPE_LDBG_VARIABLE_CHECK 0
127+
128+
#define IS_LDBG_DEBUG_STREAM_DEFINED() LDBG_VARIABLE_CHECK(LDBG_DEBUG_STREAM)
129+
#define LDBG_DEBUG_STREAM_LDBG_VARIABLE_CHECK 0
130+
131+
/// Helpers to get DEBUG_TYPE as a string literal, even when DEBUG_TYPE is not
132+
/// defined (in which case it expand to "DEBUG_TYPE")
133+
#define LDBG_GET_DEBUG_TYPE_STR__(X) #X##_LDBG_DEBUG_STRING
134+
#define LDBG_GET_DEBUG_TYPE_STR_(X) LDBG_GET_DEBUG_TYPE_STR__(X)
135+
#define LDBG_GET_DEBUG_TYPE_STR() LDBG_GET_DEBUG_TYPE_STR_(DEBUG_TYPE)
136+
/// If DEBUG_TYPE is defined, we get the string with the quotes, we need to
137+
/// remove them here.
138+
constexpr ::llvm::StringRef operator""_LDBG_DEBUG_STRING(const char *Str,
139+
std::size_t) {
140+
::llvm::StringRef S(Str);
141+
if (S.front() == '"' && S.back() == '"')
142+
return S.drop_front().drop_back();
143+
return S;
144+
}
145+
146+
/// Helper to provide the default level (=1) or type (=DEBUG_TYPE). This is used
147+
/// when a single argument is passed, if it is an integer we return DEBUG_TYPE
148+
/// and if it is a string we return 1. This fails with a static_assert if we
149+
/// pass an integer and DEBUG_TYPE is not defined.
150+
#define LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE) \
151+
[](auto LevelOrType) { \
152+
if constexpr (std::is_integral_v<decltype(LevelOrType)>) { \
153+
using ::llvm::operator""_LDBG_VARIABLE_CHECK; \
154+
using ::llvm::operator""_LDBG_DEBUG_STRING; \
155+
if constexpr (IS_DEBUG_TYPE_DEFINED()) \
156+
return LDBG_GET_DEBUG_TYPE_STR(); \
157+
else \
158+
static_assert(false, "DEBUG_TYPE is not defined"); \
159+
} else { \
160+
return 1; \
161+
} \
162+
}(LEVEL_OR_TYPE)
163+
164+
/// Use a macro to allow unit-testing to override.
165+
#define LDBG_STREAM ::llvm::dbgs()
166+
167+
#define DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL_OR_TYPE, \
168+
TYPE_OR_LEVEL, FILE, LINE) \
169+
for (bool _c = (::llvm::DebugFlag && ::llvm::impl::ldbgIsCurrentDebugType( \
170+
TYPE_OR_LEVEL, LEVEL_OR_TYPE)); \
83171
_c; _c = false) \
84172
for (::llvm::impl::raw_ldbg_ostream LdbgOS{ \
85-
::llvm::impl::computePrefix(TYPE, FILE, LINE, LEVEL), (STREAM)}; \
173+
::llvm::impl::computePrefix(TYPE_OR_LEVEL, FILE, LINE, \
174+
LEVEL_OR_TYPE), \
175+
(STREAM), /*ShouldPrefixNextString=*/true, \
176+
/*ShouldEmitNewLineOnDestruction=*/true}; \
86177
_c; _c = false) \
87-
::llvm::impl::RAIINewLineStream{LdbgOS}.asLvalue()
178+
LdbgOS
88179

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__)
180+
#define DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL_OR_TYPE, \
181+
TYPE_OR_LEVEL, FILE) \
182+
DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL_OR_TYPE, \
183+
TYPE_OR_LEVEL, FILE, __LINE__)
184+
#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL) \
185+
DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \
186+
__LLVM_FILE_NAME__)
187+
188+
// Dispatch macros when a signle argument is provided. This can be either a
189+
// level of the debug type.
190+
#define LDBG_LOG_LEVEL_1_ARG(LEVEL_OR_TYPE) \
191+
DEBUGLOG_WITH_STREAM_AND_TYPE(LDBG_STREAM, LEVEL_OR_TYPE, \
192+
LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE))
193+
#define LDBG_LOG_LEVEL_NO_ARG() LDBG_LOG_LEVEL_1_ARG(1)
194+
// This macro is a helper when LDBG() is called with 2 arguments.
195+
// In this case we want to allow the order of the arguments to be swapped.
196+
// We rely on the fact that the `level` argument is an integer, and the `type`
197+
// is a string and dispatch to a C++ API that is overloaded.
198+
#define LDBG_LOG_LEVEL_WITH_TYPE(LEVEL_OR_TYPE, TYPE_OR_LEVEL) \
199+
DEBUGLOG_WITH_STREAM_AND_TYPE(LDBG_STREAM, (LEVEL_OR_TYPE), (TYPE_OR_LEVEL))
200+
201+
#define LDBG_OS_IMPL(TYPE_OR_LEVEL, LEVEL_OR_TYPE, CALLBACK, STREAM, FILE, \
202+
LINE) \
203+
if (::llvm::DebugFlag && \
204+
::llvm::impl::ldbgIsCurrentDebugType(TYPE_OR_LEVEL, LEVEL_OR_TYPE)) { \
205+
::llvm::impl::raw_ldbg_ostream LdbgOS{ \
206+
::llvm::impl::computePrefix(TYPE_OR_LEVEL, FILE, LINE, LEVEL_OR_TYPE), \
207+
(STREAM), /*ShouldPrefixNextString=*/true, \
208+
/*ShouldEmitNewLineOnDestruction=*/true}; \
209+
CALLBACK(LdbgOS); \
210+
}
211+
212+
#define LDBG_OS_3_ARGS(TYPE_OR_LEVEL, LEVEL_OR_TYPE, CALLBACK) \
213+
LDBG_OS_IMPL(TYPE_OR_LEVEL, LEVEL_OR_TYPE, CALLBACK, LDBG_STREAM, \
214+
__LLVM_FILE_NAME__, __LINE__)
215+
216+
#define LDBG_OS_2_ARGS(LEVEL_OR_TYPE, CALLBACK) \
217+
LDBG_OS_3_ARGS(LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE), LEVEL_OR_TYPE, \
218+
CALLBACK)
219+
#define LDBG_OS_1_ARG(CALLBACK) LDBG_OS_2_ARGS(1, CALLBACK)
93220

94221
namespace impl {
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)