Skip to content

Commit 213d1b6

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 0f2484a commit 213d1b6

File tree

2 files changed

+108
-35
lines changed

2 files changed

+108
-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+
}
35+
// When __SHORT_FILE__ is not define, 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 {
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+
if (!Str.empty()) {
60+
if (HasPendingNewline) {
61+
emitPrefix();
62+
HasPendingNewline = false;
63+
}
64+
}
65+
auto Eol = Str.find('\n');
66+
while (Eol != StringRef::npos) {
67+
StringRef Line = Str.take_front(Eol + 1);
68+
if (!Line.empty()) {
69+
if (HasPendingNewline) {
70+
emitPrefix();
71+
HasPendingNewline = false;
72+
}
73+
Os.write(Line.data(), Line.size());
74+
}
75+
HasPendingNewline = true;
76+
Str = Str.drop_front(Eol + 1);
77+
Eol = Str.find('\n');
78+
}
79+
if (!Str.empty()) {
80+
if (HasPendingNewline) {
81+
emitPrefix();
82+
HasPendingNewline = false;
83+
}
84+
Os.write(Str.data(), Str.size());
85+
}
86+
}
87+
void emitPrefix() { Os.write(Prefix.c_str(), Prefix.size()); }
88+
4389
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 << " ";
90+
explicit raw_ldbg_ostream(std::string Prefix, raw_ostream &Os)
91+
: Prefix(std::move(Prefix)), Os(Os) {
92+
SetUnbuffered();
5393
}
54-
~LogWithNewline() { os << '\n'; }
55-
template <typename T> raw_ostream &operator<<(const T &t) && {
56-
return os << t;
94+
~raw_ldbg_ostream() final {
95+
flushEol();
96+
Os << '\n';
5797
}
58-
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-
}
98+
void flushEol() {
99+
if (HasPendingNewline) {
100+
emitPrefix();
101+
HasPendingNewline = false;
71102
}
72-
return filename;
73103
}
74104

75-
private:
76-
raw_ostream &os;
105+
/// Forward the current_pos method to the underlying stream.
106+
uint64_t current_pos() const final { return Os.tell(); }
77107
};
108+
109+
/// Remove the path prefix from the file name.
110+
static constexpr const char *getShortFileName(const char *path) {
111+
for (const char *p = path; *p != '\0'; ++p) {
112+
if (*p == '/' || *p == '\\')
113+
path = p + 1;
114+
}
115+
return path;
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 std::string computePrefix(const char *DebugType, const char *File,
122+
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: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,27 @@ 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\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\n";
79+
ldbg_osB << "3\n4\n";
80+
ldbg_osA << "5";
81+
EXPECT_EQ(os.str(), expected);
82+
}
83+
// After destructors, there was a pending newline for stream B.
84+
EXPECT_EQ(os.str(), expected + "\nPrefixB \n");
85+
}
6586
#else
6687
TEST(DebugLogTest, Basic) {
6788
// LDBG should be compiled out in NDEBUG, so just check it compiles and has

0 commit comments

Comments
 (0)