Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions llvm/include/llvm/Support/Debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,19 @@ class raw_ostream;
/// isCurrentDebugType - Return true if the specified string is the debug type
/// specified on the command line, or if none was specified on the command line
/// with the -debug-only=X option.
///
bool isCurrentDebugType(const char *Type);
/// An optional level can be provided to control the verbosity of the output.
/// If the provided level is not 0 and user specified a level below the provided
/// level, return false.
bool isCurrentDebugType(const char *Type, int Level = 0);

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

/// setCurrentDebugTypes - Set the current debug type, as if the
Expand Down
69 changes: 52 additions & 17 deletions llvm/include/llvm/Support/DebugLog.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,63 @@
namespace llvm {
#ifndef NDEBUG

// Output with given inputs and trailing newline. E.g.,
// LDBG() is a macro that can be used as a raw_ostream for debugging.
// It will stream the output to the dbgs() stream, with a prefix of the
// debug type and the file and line number. A trailing newline is added to the
// output automatically. If the streamed content contains a newline, the prefix
// is added to each beginning of a new line. Nothing is printed if the debug
// output is not enabled or the debug type does not match.
//
// E.g.,
// LDBG() << "Bitset contains: " << Bitset;
// is equivalent to
// LLVM_DEBUG(dbgs() << DEBUG_TYPE << " [" << __FILE__ << ":" << __LINE__
// << "] " << "Bitset contains: " << Bitset << "\n");
#define LDBG() DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), DEBUG_TYPE)

#define DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, TYPE, FILE) \
for (bool _c = (::llvm::DebugFlag && ::llvm::isCurrentDebugType(TYPE)); _c; \
_c = false) \
// is somehow equivalent to
// LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE << "] " << __FILE__ << ":" <<
// __LINE__ << " "
// << "Bitset contains: " << Bitset << "\n");
//
// An optional `level` argument can be provided to control the verbosity of the
// output. The default level is 1, and is in increasing level of verbosity.
//
// The `level` argument can be a literal integer, or a macro that evaluates to
// an integer.
//
#define LDBG(...) _GET_LDBG_MACRO(__VA_ARGS__)(__VA_ARGS__)

// Helper macros to choose the correct macro based on the number of arguments.
#define LDBG_FUNC_CHOOSER(_f1, _f2, ...) _f2
#define LDBG_FUNC_RECOMPOSER(argsWithParentheses) \
LDBG_FUNC_CHOOSER argsWithParentheses
#define LDBG_CHOOSE_FROM_ARG_COUNT(...) \
LDBG_FUNC_RECOMPOSER((__VA_ARGS__, LDBG_LOG_LEVEL, ))
#define LDBG_NO_ARG_EXPANDER() , LDBG_LOG_LEVEL_1
#define _GET_LDBG_MACRO(...) \
LDBG_CHOOSE_FROM_ARG_COUNT(LDBG_NO_ARG_EXPANDER __VA_ARGS__())

// Dispatch macros to support the `level` argument or none (default to 1)
#define LDBG_LOG_LEVEL(LEVEL) \
DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), LEVEL, DEBUG_TYPE)
#define LDBG_LOG_LEVEL_1() LDBG_LOG_LEVEL(1)

#define DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL, TYPE, FILE, \
LINE) \
for (bool _c = \
(::llvm::DebugFlag && ::llvm::isCurrentDebugType(TYPE, LEVEL)); \
_c; _c = false) \
::llvm::impl::raw_ldbg_ostream{ \
::llvm::impl::computePrefix(TYPE, FILE, __LINE__), (STREAM)} \
::llvm::impl::computePrefix(TYPE, FILE, LINE, LEVEL), (STREAM)} \
.asLvalue()

#define DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, FILE) \
DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL, TYPE, FILE, __LINE__)
// When __SHORT_FILE__ is not defined, the File is the full path,
// otherwise __SHORT_FILE__ is defined in CMake to provide the file name
// without the path prefix.
#if defined(__SHORT_FILE__)
#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, TYPE) \
DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, TYPE, __SHORT_FILE__)
#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, LEVEL, TYPE) \
DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, __SHORT_FILE__)
#else
#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, TYPE) \
DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, TYPE, \
#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, LEVEL, TYPE) \
DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, \
::llvm::impl::getShortFileName(__FILE__))
#endif

Expand Down Expand Up @@ -119,19 +154,19 @@ getShortFileName(const char *path) {
/// "[DebugType] File:Line "
/// Where the File is the file name without the path prefix.
static LLVM_ATTRIBUTE_UNUSED std::string
computePrefix(const char *DebugType, const char *File, int Line) {
computePrefix(const char *DebugType, const char *File, int Line, int Level) {
std::string Prefix;
raw_string_ostream OsPrefix(Prefix);
if (DebugType)
OsPrefix << "[" << DebugType << "] ";
OsPrefix << "[" << DebugType << ":" << Level << "] ";
OsPrefix << File << ":" << Line << " ";
return OsPrefix.str();
}
} // end namespace impl
#else
// As others in Debug, When compiling without assertions, the -debug-* options
// and all inputs too LDBG() are ignored.
#define LDBG() \
#define LDBG(...) \
for (bool _c = false; _c; _c = false) \
::llvm::nulls()
#endif
Expand Down
69 changes: 57 additions & 12 deletions llvm/lib/Support/Debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@
//===----------------------------------------------------------------------===//

#include "llvm/Support/Debug.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/circular_raw_ostream.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>

#include "DebugOptions.h"

Expand All @@ -38,27 +40,62 @@

using namespace llvm;

/// Parse a debug type string into a pair of the debug type and the debug level.
/// The expected format is "type[:level]", where the level is an optional
/// integer.
static std::pair<std::string, std::optional<int>>
parseDebugType(StringRef DbgType) {
std::optional<int> Level;
size_t ColonPos = DbgType.find(':');
if (ColonPos != StringRef::npos) {
StringRef LevelStr = DbgType.substr(ColonPos + 1);
DbgType = DbgType.take_front(ColonPos);
if (LevelStr.empty())
Level = 0;
else {
int parsedLevel;
if (to_integer(LevelStr, parsedLevel, 10))
Level = parsedLevel;
}
}
return std::make_pair(DbgType.str(), Level);
}

// Even though LLVM might be built with NDEBUG, define symbols that the code
// built without NDEBUG can depend on via the llvm/Support/Debug.h header.
namespace llvm {
/// Exported boolean set by the -debug option.
bool DebugFlag = false;

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

/// Return true if the specified string is the debug type
/// specified on the command line, or if none was specified on the command line
/// with the -debug-only=X option.
bool isCurrentDebugType(const char *DebugType) {
bool isCurrentDebugType(const char *DebugType, int Level) {
if (CurrentDebugType->empty())
return true;
// Track if there is at least one debug type with a level, this is used
// to allow to opt-out of some DebugType and leaving all the others enabled.
bool HasEnabledDebugType = false;
// See if DebugType is in list. Note: do not use find() as that forces us to
// unnecessarily create an std::string instance.
for (auto &d : *CurrentDebugType) {
if (d == DebugType)
for (auto &D : *CurrentDebugType) {
HasEnabledDebugType =
HasEnabledDebugType || (!D.second.has_value() || D.second.value() > 0);
if (D.first != DebugType)
continue;
if (!D.second.has_value())
return true;
return D.second >= Level;
}
return false;
return !HasEnabledDebugType;
}

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

void setCurrentDebugTypes(const char **Types, unsigned Count) {
CurrentDebugType->clear();
llvm::append_range(*CurrentDebugType, ArrayRef(Types, Count));
CurrentDebugType->reserve(Count);
for (const char *Type : ArrayRef(Types, Count))
CurrentDebugType->push_back(parseDebugType(Type));
}

} // namespace llvm

// All Debug.h functionality is a no-op in NDEBUG mode.
Expand Down Expand Up @@ -114,10 +154,10 @@ struct DebugOnlyOpt {
if (Val.empty())
return;
DebugFlag = true;
SmallVector<StringRef,8> dbgTypes;
StringRef(Val).split(dbgTypes, ',', -1, false);
for (auto dbgType : dbgTypes)
CurrentDebugType->push_back(std::string(dbgType));
SmallVector<StringRef, 8> DbgTypes;
StringRef(Val).split(DbgTypes, ',', -1, false);
for (auto DbgType : DbgTypes)
CurrentDebugType->push_back(parseDebugType(DbgType));
}
};
} // namespace
Expand All @@ -129,8 +169,13 @@ struct CreateDebugOnly {
static void *call() {
return new cl::opt<DebugOnlyOpt, true, cl::parser<std::string>>(
"debug-only",
cl::desc("Enable a specific type of debug output (comma separated list "
"of types)"),
cl::desc(
"Enable a specific type of debug output (comma separated list "
"of types using the format \"type[:level]\", where the level "
"is an optional integer. The level can be set to 1, 2, 3, etc. to "
"control the verbosity of the output. Setting a debug-type level "
"to zero acts as an opt-out for this specific debug-type without "
"affecting the others."),
cl::Hidden, cl::value_desc("debug string"),
cl::location(DebugOnlyOptLoc), cl::ValueRequired);
}
Expand Down
50 changes: 42 additions & 8 deletions llvm/unittests/Support/DebugLogTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#undef __SHORT_FILE__

#include "llvm/Support/DebugLog.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/Support/raw_ostream.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
Expand All @@ -31,7 +32,7 @@ TEST(DebugLogTest, Basic) {
{
std::string str;
raw_string_ostream os(str);
DEBUGLOG_WITH_STREAM_AND_TYPE(os, nullptr) << "NoType";
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, nullptr) << "NoType";
EXPECT_FALSE(StringRef(os.str()).starts_with('['));
EXPECT_TRUE(StringRef(os.str()).ends_with("NoType\n"));
}
Expand All @@ -40,8 +41,8 @@ TEST(DebugLogTest, Basic) {
{
std::string str;
raw_string_ostream os(str);
DEBUGLOG_WITH_STREAM_AND_TYPE(os, "A") << "A";
DEBUGLOG_WITH_STREAM_AND_TYPE(os, "B") << "B";
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "A";
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "B") << "B";
EXPECT_TRUE(StringRef(os.str()).starts_with('['));
EXPECT_THAT(os.str(), AllOf(HasSubstr("A\n"), HasSubstr("B\n")));
}
Expand All @@ -52,22 +53,55 @@ TEST(DebugLogTest, Basic) {
raw_string_ostream os(str);
// Just check that the macro doesn't result in dangling else.
if (true)
DEBUGLOG_WITH_STREAM_AND_TYPE(os, "A") << "A";
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "A";
else
DEBUGLOG_WITH_STREAM_AND_TYPE(os, "A") << "B";
DEBUGLOG_WITH_STREAM_AND_TYPE(os, "B") << "B";
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "B";
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "B") << "B";
EXPECT_THAT(os.str(), AllOf(HasSubstr("A\n"), Not(HasSubstr("B\n"))));

int count = 0;
auto inc = [&]() { return ++count; };
EXPECT_THAT(count, Eq(0));
DEBUGLOG_WITH_STREAM_AND_TYPE(os, "A") << inc();
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << inc();
EXPECT_THAT(count, Eq(1));
DEBUGLOG_WITH_STREAM_AND_TYPE(os, "B") << inc();
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "B") << inc();
EXPECT_THAT(count, Eq(1));
}
}

TEST(DebugLogTest, BasicWithLevel) {
llvm::DebugFlag = true;
// We expect A to be always printed, B to be printed only when level is 1 or
// below, and C to be printed only when level is 0 or below.
static const char *DT[] = {"A", "B:1", "C:"};

setCurrentDebugTypes(DT, sizeof(DT) / sizeof(DT[0]));
std::string str;
raw_string_ostream os(str);
for (auto type : {"A", "B", "C", "D"})
for (int level : llvm::seq<int>(0, 4))
DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(os, level, type, type, level)
<< level;
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 "
"3\n[B:0] B:0 0\n[B:1] B:1 1\n[C:0] C:0 0\n");
}

TEST(DebugLogTest, NegativeLevel) {
llvm::DebugFlag = true;
// Test the special behavior when all the levels are 0.
// In this case we expect all the debug types to be printed.
static const char *DT[] = {"A:"};

setCurrentDebugTypes(DT, sizeof(DT) / sizeof(DT[0]));
std::string str;
raw_string_ostream os(str);
for (auto type : {"A", "B"})
for (int level : llvm::seq<int>(0, 2))
DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(os, level, type, type, level)
<< level;
EXPECT_EQ(os.str(), "[A:0] A:0 0\n[B:0] B:0 0\n[B:1] B:1 1\n");
}

TEST(DebugLogTest, StreamPrefix) {
llvm::DebugFlag = true;
static const char *DT[] = {"A", "B"};
Expand Down
10 changes: 1 addition & 9 deletions mlir/lib/Dialect/Transform/Interfaces/TransformInterfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,8 @@
#include "llvm/Support/InterleavedRange.h"

#define DEBUG_TYPE "transform-dialect"
#define DEBUG_TYPE_FULL "transform-dialect-full"
#define DEBUG_PRINT_AFTER_ALL "transform-dialect-print-top-level-after-all"
#ifndef NDEBUG
#define FULL_LDBG(X) \
DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), DEBUG_TYPE_FULL)
#else
#define FULL_LDBG(X) \
for (bool _c = false; _c; _c = false) \
::llvm::nulls()
#endif
#define FULL_LDBG() LDBG(4)

using namespace mlir;

Expand Down
10 changes: 5 additions & 5 deletions mlir/test/IR/test-pattern-logging-listener.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
// {anonymous_namespace} vs `anonymous_namespace` (and maybe others?) on the
// various platforms.

// CHECK: [pattern-logging-listener]
// CHECK: [pattern-logging-listener:1]
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationInserted | test.new_op
// CHECK: [pattern-logging-listener]
// CHECK: [pattern-logging-listener:1]
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationReplaced (with values) | test.replace_with_new_op
// CHECK: [pattern-logging-listener]
// CHECK: [pattern-logging-listener:1]
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationModified | arith.addi
// CHECK: [pattern-logging-listener]
// CHECK: [pattern-logging-listener:1]
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationModified | arith.addi
// CHECK: [pattern-logging-listener]
// CHECK: [pattern-logging-listener:1]
// CHECK-SAME: ::ReplaceWithNewOp | notifyOperationErased | test.replace_with_new_op
func.func @replace_with_new_op() -> i32 {
%a = "test.replace_with_new_op"() : () -> (i32)
Expand Down