Skip to content

Commit 06c158a

Browse files
committed
Implement a custom stream for LDBG macro to handle newlines
This prints the prefix on every new line, allowing for an output that looks like: [dead-code-analysis] DeadCodeAnalysis.cpp:284 Visiting program point: 0x55ea5be2d4c8 <after operation>:func.func private @private_1() -> (i32, i32) {...} [dead-code-analysis] DeadCodeAnalysis.cpp:288 Visiting operation: func.func private @private_1() -> (i32, i32) { [dead-code-analysis] DeadCodeAnalysis.cpp:288 %c0_i32 = arith.constant 0 : i32 [dead-code-analysis] DeadCodeAnalysis.cpp:288 %0 = arith.addi %c0_i32, %c0_i32 {tag = "one"} : i32 [dead-code-analysis] DeadCodeAnalysis.cpp:288 return %c0_i32, %0 : i32, i32 [dead-code-analysis] DeadCodeAnalysis.cpp:288 } [dead-code-analysis] DeadCodeAnalysis.cpp:313 Visiting callable operation: func.func private @private_1() -> (i32, i32) { [dead-code-analysis] DeadCodeAnalysis.cpp:313 %c0_i32 = arith.constant 0 : i32 [dead-code-analysis] DeadCodeAnalysis.cpp:313 %0 = arith.addi %c0_i32, %c0_i32 {tag = "one"} : i32 [dead-code-analysis] DeadCodeAnalysis.cpp:313 return %c0_i32, %0 : i32, i32 [dead-code-analysis] DeadCodeAnalysis.cpp:313 }
1 parent 9e5f9ff commit 06c158a

File tree

2 files changed

+109
-35
lines changed

2 files changed

+109
-35
lines changed

llvm/include/llvm/Support/DebugLog.h

Lines changed: 87 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -26,55 +26,107 @@ namespace llvm {
2626
// << "] " << "Bitset contains: " << Bitset << "\n");
2727
#define LDBG() DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), DEBUG_TYPE)
2828

29-
#if defined(__SHORT_FILE__)
30-
#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, TYPE) \
29+
#define DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, TYPE, FILE) \
3130
for (bool _c = (::llvm::DebugFlag && ::llvm::isCurrentDebugType(TYPE)); _c; \
3231
_c = false) \
33-
::llvm::impl::LogWithNewline(TYPE, __SHORT_FILE__, __LINE__, (STREAM))
32+
::llvm::impl::raw_ldbg_ostream{ \
33+
::llvm::impl::computePrefix(TYPE, FILE, __LINE__), (STREAM)} \
34+
.asLvalue()
35+
// When __SHORT_FILE__ is not defined, the File is the full path,
36+
// otherwise __SHORT_FILE__ is defined in CMake to provide the file name
37+
// without the path prefix.
38+
#if defined(__SHORT_FILE__)
39+
#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, TYPE) \
40+
DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, TYPE, __SHORT_FILE__)
3441
#else
3542
#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, TYPE) \
36-
for (bool _c = (::llvm::DebugFlag && ::llvm::isCurrentDebugType(TYPE)); _c; \
37-
_c = false) \
38-
::llvm::impl::LogWithNewline(TYPE, __FILE__, __LINE__, (STREAM))
43+
DEBUGLOG_WITH_STREAM_TYPE_AND_FILE( \
44+
STREAM, TYPE, ::llvm::impl::LogWithNewline::getShortFileName(__FILE__))
3945
#endif
4046

4147
namespace impl {
42-
class LogWithNewline {
43-
public:
44-
LogWithNewline(const char *debug_type, const char *file, int line,
45-
raw_ostream &os)
46-
: os(os) {
47-
#if !defined(__SHORT_FILE__)
48-
file = ::llvm::impl::LogWithNewline::getShortFileName(file);
49-
#endif
50-
if (debug_type)
51-
os << "[" << debug_type << "] ";
52-
os << file << ":" << line << " ";
48+
49+
/// A raw_ostream that tracks `\n` and print the prefix.
50+
class LLVM_ABI raw_ldbg_ostream final : public raw_ostream {
51+
std::string Prefix;
52+
raw_ostream &Os;
53+
bool HasPendingNewline = true;
54+
55+
/// Split the line on newlines and insert the prefix before each newline.
56+
/// Forward everything to the underlying stream.
57+
void write_impl(const char *Ptr, size_t Size) final {
58+
auto Str = StringRef(Ptr, Size);
59+
// Handle the initial prefix.
60+
if (!Str.empty())
61+
writeWithPrefix(StringRef());
62+
63+
auto Eol = Str.find('\n');
64+
while (Eol != StringRef::npos) {
65+
StringRef Line = Str.take_front(Eol + 1);
66+
if (!Line.empty())
67+
writeWithPrefix(Line);
68+
HasPendingNewline = true;
69+
Str = Str.drop_front(Eol + 1);
70+
Eol = Str.find('\n');
71+
}
72+
if (!Str.empty())
73+
writeWithPrefix(Str);
5374
}
54-
~LogWithNewline() { os << '\n'; }
55-
template <typename T> raw_ostream &operator<<(const T &t) && {
56-
return os << t;
75+
void emitPrefix() { Os.write(Prefix.c_str(), Prefix.size()); }
76+
void writeWithPrefix(StringRef Str) {
77+
if (HasPendingNewline) {
78+
emitPrefix();
79+
HasPendingNewline = false;
80+
}
81+
Os.write(Str.data(), Str.size());
5782
}
5883

59-
// Prevent copying, as this class manages newline responsibility and is
60-
// intended for use as a temporary.
61-
LogWithNewline(const LogWithNewline &) = delete;
62-
LogWithNewline &operator=(const LogWithNewline &) = delete;
63-
LogWithNewline &operator=(LogWithNewline &&) = delete;
64-
static constexpr const char *getShortFileName(const char *path) {
65-
// Remove the path prefix from the file name.
66-
const char *filename = path;
67-
for (const char *p = path; *p != '\0'; ++p) {
68-
if (*p == '/' || *p == '\\') {
69-
filename = p + 1;
70-
}
84+
public:
85+
explicit raw_ldbg_ostream(std::string Prefix, raw_ostream &Os)
86+
: Prefix(std::move(Prefix)), Os(Os) {
87+
SetUnbuffered();
88+
}
89+
~raw_ldbg_ostream() final {
90+
flushEol();
91+
Os << '\n';
92+
}
93+
void flushEol() {
94+
if (HasPendingNewline) {
95+
emitPrefix();
96+
HasPendingNewline = false;
7197
}
72-
return filename;
7398
}
7499

75-
private:
76-
raw_ostream &os;
100+
/// Forward the current_pos method to the underlying stream.
101+
uint64_t current_pos() const final { return Os.tell(); }
102+
103+
/// Some of the `<<` operators expect an lvalue, so we trick the type system.
104+
raw_ldbg_ostream &asLvalue() { return *this; }
77105
};
106+
107+
/// Remove the path prefix from the file name.
108+
static LLVM_ATTRIBUTE_UNUSED constexpr const char *
109+
getShortFileName(const char *path) {
110+
const char *filename = path;
111+
for (const char *p = path; *p != '\0'; ++p) {
112+
if (*p == '/' || *p == '\\')
113+
filename = p + 1;
114+
}
115+
return filename;
116+
}
117+
118+
/// Compute the prefix for the debug log in the form of:
119+
/// "[DebugType] File:Line "
120+
/// Where the File is the file name without the path prefix.
121+
static LLVM_ATTRIBUTE_UNUSED std::string
122+
computePrefix(const char *DebugType, const char *File, int Line) {
123+
std::string Prefix;
124+
raw_string_ostream OsPrefix(Prefix);
125+
if (DebugType)
126+
OsPrefix << "[" << DebugType << "] ";
127+
OsPrefix << File << ":" << Line << " ";
128+
return OsPrefix.str();
129+
}
78130
} // end namespace impl
79131
#else
80132
// As others in Debug, When compiling without assertions, the -debug-* options

llvm/unittests/Support/DebugLogTest.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,28 @@ TEST(DebugLogTest, Basic) {
6262
EXPECT_THAT(count, Eq(1));
6363
}
6464
}
65+
66+
TEST(DebugLogTest, StreamPrefix) {
67+
llvm::DebugFlag = true;
68+
static const char *DT[] = {"A", "B"};
69+
setCurrentDebugTypes(DT, 2);
70+
71+
std::string str;
72+
raw_string_ostream os(str);
73+
std::string expected = "PrefixA 1\nPrefixA 2\nPrefixA \nPrefixB "
74+
"3\nPrefixB 4\nPrefixA 5";
75+
{
76+
llvm::impl::raw_ldbg_ostream ldbg_osB("PrefixB ", os);
77+
llvm::impl::raw_ldbg_ostream ldbg_osA("PrefixA ", os);
78+
ldbg_osA << "1\n2";
79+
ldbg_osA << "\n\n";
80+
ldbg_osB << "3\n4\n";
81+
ldbg_osA << "5";
82+
EXPECT_EQ(os.str(), expected);
83+
}
84+
// After destructors, there was a pending newline for stream B.
85+
EXPECT_EQ(os.str(), expected + "\nPrefixB \n");
86+
}
6587
#else
6688
TEST(DebugLogTest, Basic) {
6789
// LDBG should be compiled out in NDEBUG, so just check it compiles and has

0 commit comments

Comments
 (0)