diff options
Diffstat (limited to 'llvm/include')
-rw-r--r-- | llvm/include/llvm/Support/Debug.h | 12 | ||||
-rw-r--r-- | llvm/include/llvm/Support/DebugLog.h | 173 |
2 files changed, 139 insertions, 46 deletions
diff --git a/llvm/include/llvm/Support/Debug.h b/llvm/include/llvm/Support/Debug.h index 924d7b2..5542089 100644 --- a/llvm/include/llvm/Support/Debug.h +++ b/llvm/include/llvm/Support/Debug.h @@ -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 diff --git a/llvm/include/llvm/Support/DebugLog.h b/llvm/include/llvm/Support/DebugLog.h index b1b17e3..8fca2d5 100644 --- a/llvm/include/llvm/Support/DebugLog.h +++ b/llvm/include/llvm/Support/DebugLog.h @@ -19,67 +19,154 @@ 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) +// 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, 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) \ - for (bool _c = (::llvm::DebugFlag && ::llvm::isCurrentDebugType(TYPE)); _c; \ - _c = false) \ - ::llvm::impl::LogWithNewline(TYPE, __SHORT_FILE__, __LINE__, (STREAM)) +#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) \ - for (bool _c = (::llvm::DebugFlag && ::llvm::isCurrentDebugType(TYPE)); _c; \ - _c = false) \ - ::llvm::impl::LogWithNewline(TYPE, __FILE__, __LINE__, (STREAM)) +#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, LEVEL, TYPE) \ + DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, \ + ::llvm::impl::getShortFileName(__FILE__)) #endif namespace impl { -class LogWithNewline { -public: - LogWithNewline(const char *debug_type, const char *file, int line, - raw_ostream &os) - : os(os) { -#if !defined(__SHORT_FILE__) - file = ::llvm::impl::LogWithNewline::getShortFileName(file); -#endif - if (debug_type) - os << "[" << debug_type << "] "; - os << file << ":" << line << " "; + +/// A raw_ostream that tracks `\n` and print the prefix. +class LLVM_ABI raw_ldbg_ostream final : public raw_ostream { + std::string Prefix; + raw_ostream &Os; + bool HasPendingNewline = true; + + /// Split the line on newlines and insert the prefix before each newline. + /// Forward everything to the underlying stream. + void write_impl(const char *Ptr, size_t Size) final { + auto Str = StringRef(Ptr, Size); + // Handle the initial prefix. + if (!Str.empty()) + writeWithPrefix(StringRef()); + + auto Eol = Str.find('\n'); + while (Eol != StringRef::npos) { + StringRef Line = Str.take_front(Eol + 1); + if (!Line.empty()) + writeWithPrefix(Line); + HasPendingNewline = true; + Str = Str.drop_front(Eol + 1); + Eol = Str.find('\n'); + } + if (!Str.empty()) + writeWithPrefix(Str); } - ~LogWithNewline() { os << '\n'; } - template <typename T> raw_ostream &operator<<(const T &t) && { - return os << t; + void emitPrefix() { Os.write(Prefix.c_str(), Prefix.size()); } + void writeWithPrefix(StringRef Str) { + if (HasPendingNewline) { + emitPrefix(); + HasPendingNewline = false; + } + Os.write(Str.data(), Str.size()); } - // Prevent copying, as this class manages newline responsibility and is - // intended for use as a temporary. - LogWithNewline(const LogWithNewline &) = delete; - LogWithNewline &operator=(const LogWithNewline &) = delete; - LogWithNewline &operator=(LogWithNewline &&) = delete; - static constexpr const char *getShortFileName(const char *path) { - // Remove the path prefix from the file name. - const char *filename = path; - for (const char *p = path; *p != '\0'; ++p) { - if (*p == '/' || *p == '\\') { - filename = p + 1; - } +public: + explicit raw_ldbg_ostream(std::string Prefix, raw_ostream &Os) + : Prefix(std::move(Prefix)), Os(Os) { + SetUnbuffered(); + } + ~raw_ldbg_ostream() final { + flushEol(); + Os << '\n'; + } + void flushEol() { + if (HasPendingNewline) { + emitPrefix(); + HasPendingNewline = false; } - return filename; } -private: - raw_ostream &os; + /// Forward the current_pos method to the underlying stream. + uint64_t current_pos() const final { return Os.tell(); } + + /// Some of the `<<` operators expect an lvalue, so we trick the type system. + raw_ldbg_ostream &asLvalue() { return *this; } }; + +/// Remove the path prefix from the file name. +static LLVM_ATTRIBUTE_UNUSED constexpr const char * +getShortFileName(const char *path) { + const char *filename = path; + for (const char *p = path; *p != '\0'; ++p) { + if (*p == '/' || *p == '\\') + filename = p + 1; + } + return filename; +} + +/// Compute the prefix for the debug log in the form of: +/// "[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, int Level) { + std::string Prefix; + raw_string_ostream OsPrefix(Prefix); + if (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 |