//===- llvm/Support/DebugLog.h - Logging like debug output ------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // This file contains macros for logging like debug output. It builds upon the // support in Debug.h but provides a utility function for common debug output // style. //===----------------------------------------------------------------------===// #ifndef LLVM_SUPPORT_DEBUGLOG_H #define LLVM_SUPPORT_DEBUGLOG_H #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" namespace llvm { #ifndef NDEBUG /// 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"); /// // 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. /// /// An optional `type` argument can be provided to control the debug type. The /// default type is DEBUG_TYPE. The `type` argument can be a literal string, or /// a macro that evaluates to a string. /// /// E.g., /// LDBG(2) << "Bitset contains: " << Bitset; /// LDBG("debug_type") << "Bitset contains: " << Bitset; /// LDBG("debug_type", 2) << "Bitset contains: " << Bitset; #define LDBG(...) _GET_LDBG_MACRO(__VA_ARGS__)(__VA_ARGS__) /// 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. /// This is useful when you need to pass a `raw_ostream` to a helper function to /// be able to print (when the `<<` operator is not available). /// /// E.g., /// LDBG_OS([&] (raw_ostream &Os) { /// Os << "Pass Manager contains: "; /// pm.printAsTextual(Os); /// }); /// /// Just like LDBG(), it optionally accepts a `level` and `type` arguments. /// E.g., /// LDBG_OS(2, [&] (raw_ostream &Os) { ... }); /// LDBG_OS("debug_type", [&] (raw_ostream &Os) { ... }); /// LDBG_OS("debug_type", 2, [&] (raw_ostream &Os) { ... }); /// #define LDBG_OS(...) _GET_LDBG_OS_MACRO(__VA_ARGS__)(__VA_ARGS__) // We want the filename without the full path. We are using the __FILE__ macro // and a constexpr function to strip the path prefix. We can avoid the frontend // repeated evaluation of __FILE__ by using the __FILE_NAME__ when defined // (gcc and clang do) which contains the file name already. #if defined(__FILE_NAME__) #define __LLVM_FILE_NAME__ __FILE_NAME__ #else #define __LLVM_FILE_NAME__ ::llvm::impl::getShortFileName(__FILE__) #endif // Everything below are implementation details of the macros above. namespace impl { /// This macro expands to the stream to use for output, we use a macro to allow /// unit-testing to override. #define LDBG_STREAM ::llvm::dbgs() // ---------------------------------------------------------------------------- // LDBG() implementation // ---------------------------------------------------------------------------- // Helper macros to choose the correct LDBG() macro based on the number of // arguments. #define LDBG_FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3 #define LDBG_FUNC_RECOMPOSER(argsWithParentheses) \ LDBG_FUNC_CHOOSER argsWithParentheses #define LDBG_CHOOSE_FROM_ARG_COUNT(...) \ LDBG_FUNC_RECOMPOSER((__VA_ARGS__, LDBG_TYPE_AND_LEVEL, LDBG_LEVEL_OR_TYPE, )) #define LDBG_NO_ARG_EXPANDER() , , LDBG_NO_ARG #define _GET_LDBG_MACRO(...) \ LDBG_CHOOSE_FROM_ARG_COUNT(LDBG_NO_ARG_EXPANDER __VA_ARGS__()) /// This macro is the core of the LDBG() implementation. It is used to print the /// debug output with the given stream, level, type, file, and line number. #define LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(STREAM, LEVEL_OR_TYPE, \ TYPE_OR_LEVEL, FILE, LINE) \ for (bool _c = ::llvm::DebugFlag && ::llvm::impl::ldbgIsCurrentDebugType( \ TYPE_OR_LEVEL, LEVEL_OR_TYPE); \ _c; _c = false) \ ::llvm::impl::raw_ldbg_ostream{ \ ::llvm::impl::computePrefix(TYPE_OR_LEVEL, FILE, LINE, LEVEL_OR_TYPE), \ (STREAM), /*ShouldPrefixNextString=*/true, \ /*ShouldEmitNewLineOnDestruction=*/true} \ .asLvalue() /// These macros are helpers to implement LDBG() with an increasing amount of /// optional arguments made explicit. #define LDBG_STREAM_LEVEL_TYPE_AND_FILE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \ FILE) \ LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \ FILE, __LINE__) #define LDGB_STREAM_LEVEL_AND_TYPE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL) \ LDBG_STREAM_LEVEL_TYPE_AND_FILE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \ __LLVM_FILE_NAME__) /// This macro is a helper when LDBG() is called with 2 arguments. /// In this case we want to force the first argument to be the type for /// consistency in the codebase. /// We trick this by casting the first argument to a (const char *) which /// won't compile with an int. #define LDBG_TYPE_AND_LEVEL(TYPE, LEVEL) \ LDGB_STREAM_LEVEL_AND_TYPE(LDBG_STREAM, static_cast(TYPE), \ (LEVEL)) /// When a single argument is provided. This can be either a level or the debug /// type. If a level is provided, we default the debug type to DEBUG_TYPE, if a /// string is provided, we default the level to 1. #define LDBG_LEVEL_OR_TYPE(LEVEL_OR_TYPE) \ LDGB_STREAM_LEVEL_AND_TYPE(LDBG_STREAM, (LEVEL_OR_TYPE), \ LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE)) #define LDBG_NO_ARG() LDBG_LEVEL_OR_TYPE(1) // ---------------------------------------------------------------------------- // LDBG_OS() implementation // ---------------------------------------------------------------------------- // Helper macros to choose the correct LDBG_OS() macro based on the number of // arguments. #define LDBG_OS_FUNC_CHOOSER(_f1, _f2, _f3, _f4, ...) _f4 #define LDBG_OS_FUNC_RECOMPOSER(argsWithParentheses) \ LDBG_OS_FUNC_CHOOSER argsWithParentheses #define LDBG_OS_CHOOSE_FROM_ARG_COUNT(...) \ LDBG_OS_FUNC_RECOMPOSER((__VA_ARGS__, LDBG_OS_TYPE_AND_LEVEL_AND_CALLBACK, \ LDBG_OS_LEVEL_OR_TYPE_AND_CALLBACK, \ LDBG_OS_CALLBACK, )) #define LDBG_OS_NO_ARG_EXPANDER() , , , LDBG_OS_CALLBACK #define _GET_LDBG_OS_MACRO(...) \ LDBG_OS_CHOOSE_FROM_ARG_COUNT(LDBG_OS_NO_ARG_EXPANDER __VA_ARGS__()) /// This macro is the core of the LDBG_OS() macros. It is used to print the /// debug output with the given stream, level, type, file, and line number. #define LDBG_OS_IMPL(TYPE_OR_LEVEL, LEVEL_OR_TYPE, CALLBACK, STREAM, FILE, \ LINE) \ if (::llvm::DebugFlag && \ ::llvm::impl::ldbgIsCurrentDebugType(TYPE_OR_LEVEL, LEVEL_OR_TYPE)) { \ ::llvm::impl::raw_ldbg_ostream LdbgOS{ \ ::llvm::impl::computePrefix(TYPE_OR_LEVEL, FILE, LINE, LEVEL_OR_TYPE), \ (STREAM), /*ShouldPrefixNextString=*/true, \ /*ShouldEmitNewLineOnDestruction=*/true}; \ CALLBACK(LdbgOS); \ } #define LDBG_OS_TYPE_AND_LEVEL_AND_CALLBACK(TYPE, LEVEL, CALLBACK) \ LDBG_OS_IMPL(static_cast(TYPE), LEVEL, CALLBACK, LDBG_STREAM, \ __LLVM_FILE_NAME__, __LINE__) #define LDBG_OS_LEVEL_OR_TYPE_AND_CALLBACK(LEVEL_OR_TYPE, CALLBACK) \ LDBG_OS_IMPL(LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE), LEVEL_OR_TYPE, \ CALLBACK, LDBG_STREAM, __LLVM_FILE_NAME__, __LINE__) #define LDBG_OS_CALLBACK(CALLBACK) \ LDBG_OS_LEVEL_OR_TYPE_AND_CALLBACK(1, CALLBACK) // ---------------------------------------------------------------------------- // General Helpers for the implementation above // ---------------------------------------------------------------------------- /// Return the stringified macro as a StringRef. /// Also, strip out potential surrounding quotes: this comes from an artifact of /// the macro stringification, if DEBUG_TYPE is undefined we get the string /// "DEBUG_TYPE", however if it is defined we get the string with the quotes. /// For example if DEBUG_TYPE is "foo", we get "\"foo\"" but we want to return /// "foo" here. constexpr ::llvm::StringRef strip_quotes(const char *Str) { ::llvm::StringRef S(Str); if (Str[0] == '"' && Str[S.size() - 1] == '"') return StringRef(Str + 1, S.size() - 2); return S; } /// Helper to provide the default level (=1) or type (=DEBUG_TYPE). This is used /// when a single argument is passed to LDBG() (or LDBG_OS()), if it is an /// integer we return DEBUG_TYPE and if it is a string we return 1. /// When DEBUG_TYPE is not defined, we return the current file name instead. #define LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE) \ [](auto LevelOrType) { \ if constexpr (std::is_integral_v) { \ constexpr const char *DebugType = LDBG_GET_DEBUG_TYPE_STR(); \ if constexpr (DebugType[0] == '"') { \ return ::llvm::impl::strip_quotes(DebugType); \ } else { \ return __LLVM_FILE_NAME__; \ } \ } else { \ return 1; \ } \ }(LEVEL_OR_TYPE) /// Helpers to get DEBUG_TYPE as a StringRef, even when DEBUG_TYPE is not /// defined (in which case it expands to "DEBUG_TYPE") #define LDBG_GET_DEBUG_TYPE_STR__(X) #X #define LDBG_GET_DEBUG_TYPE_STR_(X) LDBG_GET_DEBUG_TYPE_STR__(X) #define LDBG_GET_DEBUG_TYPE_STR() LDBG_GET_DEBUG_TYPE_STR_(DEBUG_TYPE) /// Helper to call isCurrentDebugType with a StringRef. [[maybe_unused]] static bool ldbgIsCurrentDebugType(StringRef Type, int Level) { return ::llvm::isCurrentDebugType(Type.str().c_str(), Level); } [[maybe_unused]] static bool ldbgIsCurrentDebugType(int Level, StringRef Type) { return ::llvm::isCurrentDebugType(Type.str().c_str(), Level); } /// A raw_ostream that tracks `\n` and print the prefix after each /// newline. class LLVM_ABI raw_ldbg_ostream final : public raw_ostream { std::string Prefix; raw_ostream &Os; bool ShouldPrefixNextString; bool ShouldEmitNewLineOnDestruction; /// 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); auto Eol = Str.find('\n'); // Handle `\n` occurring in the string, ensure to print the prefix at the // beginning of each line. while (Eol != StringRef::npos) { // Take the line up to the newline (including the newline). StringRef Line = Str.take_front(Eol + 1); if (!Line.empty()) writeWithPrefix(Line); // We printed a newline, record here to print a prefix. ShouldPrefixNextString = true; Str = Str.drop_front(Eol + 1); Eol = Str.find('\n'); } if (!Str.empty()) writeWithPrefix(Str); } void emitPrefix() { Os.write(Prefix.c_str(), Prefix.size()); } void writeWithPrefix(StringRef Str) { if (ShouldPrefixNextString) { emitPrefix(); ShouldPrefixNextString = false; } Os.write(Str.data(), Str.size()); } public: explicit raw_ldbg_ostream(std::string Prefix, raw_ostream &Os, bool ShouldPrefixNextString = true, bool ShouldEmitNewLineOnDestruction = false) : Prefix(std::move(Prefix)), Os(Os), ShouldPrefixNextString(ShouldPrefixNextString), ShouldEmitNewLineOnDestruction(ShouldEmitNewLineOnDestruction) { SetUnbuffered(); } ~raw_ldbg_ostream() final { if (ShouldEmitNewLineOnDestruction) Os << '\n'; } /// 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; } }; /// A raw_ostream that prints a newline on destruction, useful for LDBG() class RAIINewLineStream final : public raw_ostream { raw_ostream &Os; public: RAIINewLineStream(raw_ostream &Os) : Os(Os) { SetUnbuffered(); } ~RAIINewLineStream() override { Os << '\n'; } void write_impl(const char *Ptr, size_t Size) final { Os.write(Ptr, Size); } uint64_t current_pos() const final { return Os.tell(); } RAIINewLineStream &asLvalue() { return *this; } }; /// Remove the path prefix from the file name. [[maybe_unused]] static 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. [[maybe_unused]] static std::string computePrefix(StringRef DebugType, const char *File, int Line, int Level) { std::string Prefix; raw_string_ostream OsPrefix(Prefix); OsPrefix << "["; if (!DebugType.empty() && DebugType != File) OsPrefix << DebugType << " "; OsPrefix << File << ":" << Line << " " << Level << "] "; return OsPrefix.str(); } /// Overload allowing to swap the order of the DebugType and Level arguments. [[maybe_unused]] static std::string computePrefix(int Level, const char *File, int Line, StringRef DebugType) { return computePrefix(DebugType, File, Line, Level); } } // end namespace impl #else // As others in Debug, When compiling without assertions, the -debug-* options // and all inputs too LDBG() are ignored. #define LDBG(...) \ for (bool _c = false; _c; _c = false) \ ::llvm::nulls() #define LDBG_OS(...) #endif } // end namespace llvm #endif // LLVM_SUPPORT_DEBUGLOG_H