#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include #include #include "llvm/ADT/StringRef.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/raw_os_ostream.h" #include "llvm/Support/raw_ostream.h" #include "revng/Support/Assert.h" #include "revng/Support/CommandLine.h" // TODO: use a dedicated namespace extern std::ostream &dbg; extern size_t MaxLoggerNameLength; #define debug_function __attribute__((used, noinline)) /// \brief Emits \p Indentation space pairs template void indent(Stream &Output, size_t Indentation) { for (size_t I = 0; I < Indentation; I++) Output << " "; } /// \brief Stream an instance of this class to call Logger::emit() struct LogTerminator { const char *File; uint64_t Line; }; #define DoLog (LogTerminator{ __FILE__, __LINE__ }) /// \brief Logger that self-registers itself, can be disable, has a name and /// follows the global indentation level /// /// The typical usage of this class is to be a static global variable in a /// translation unit. template class Logger { private: static unsigned IndentLevel; public: Logger(llvm::StringRef Name) : Name(Name), Enabled(false) { init(); } void indent(unsigned Level = 1); void unindent(unsigned Level = 1); void setIndentation(unsigned Level); bool isEnabled() const { return StaticEnabled && Enabled; } llvm::StringRef name() const { return Name; } // TODO: allow optional description llvm::StringRef description() const { return ""; } void enable() { MaxLoggerNameLength = std::max(MaxLoggerNameLength, Name.size()); Enabled = true; } void disable() { Enabled = false; } /// \brief Write a log line /// /// To call this method using the stream syntax, see LogTerminator, or simply /// MyLogger << DoLog; void flush(const LogTerminator &LineInfo = LogTerminator{ "", 0 }); template inline Logger &operator<<(const T &Other) { writeToLog(*this, Other, static_cast(0)); return *this; } template friend void writeToLog(Logger &This, const LogTerminator &T, int Ignore); template friend void writeToLog(Logger &This, const T Other, LowPrio Ignore); std::unique_ptr getAsLLVMStream() { if (Enabled) return std::make_unique(Buffer); return std::make_unique(); } private: void init(); private: llvm::StringRef Name; std::stringstream Buffer; bool Enabled; }; /// \brief Indent all loggers within the scope of this object template class LoggerIndent { public: LoggerIndent(Logger &L) : L(L) { L.indent(); } ~LoggerIndent() { L.unindent(); } private: Logger &L; }; /// \brief Emit a message for the specified logger upon return /// /// You can create an instance of this object associated to a Logger, so that /// when the object goes out of scope (typically, on return), the emit method /// will be invoked. template class LogOnReturn { public: LogOnReturn(Logger &L) : L(L) {} ~LogOnReturn() { L.flush(); } private: Logger &L; }; /// \brief The catch-all function for logging, it can log any type not already /// handled /// /// The \p Ignore argument is not used. It's only purpose is having a template /// argument as type, which makes it less specific that other functions that /// have a non-template type for the same argument. /// /// In practice, you can overload this function, specifying as type of \p Ignore /// int, and then choose the type T you want to handle. /// /// Note that here we introduced only two levels of priority, the fall back /// level (this function) and the ad-hoc level. Therefore, all the ad-hoc /// overloads should never have overlapping types for \p Other. /// /// For an example see the next specialization. template inline void writeToLog(Logger &This, const T Other, LowPrio) { if (This.isEnabled()) This.Buffer << Other; } /// \brief Specialization of writeToLog to emit a message template inline void writeToLog(Logger &This, const LogTerminator &LineInfo, int) { This.flush(LineInfo); } /// \brief Specialization for llvm::StringRef template inline void writeToLog(Logger &This, const llvm::StringRef &S, int Ign) { writeToLog(This, S.data(), Ign); } /// \brief Specialization for llvm::StringRef template inline void writeToLog(Logger &This, const llvm::Error &Error, int Ign) { std::string Message; { llvm::raw_string_ostream Stream(Message); Stream << Error; } writeToLog(This, Message, Ign); } /// \brief A global registry for all the loggers /// /// Loggers are usually global static variables in translation units, the role /// of this class is collecting them. class LoggersRegistry { public: LoggersRegistry() {} void add(Logger *L) { Loggers.push_back(L); } void add(Logger *) {} size_t size() const { return Loggers.size(); } void enable(llvm::StringRef Name) { for (Logger *L : Loggers) { if (L->name() == Name) { L->enable(); return; } } revng_abort("Requested logger not available"); } void disable(llvm::StringRef Name) { for (Logger *L : Loggers) { if (L->name() == Name) { L->disable(); return; } } revng_abort("Requested logger not available"); } void registerArguments() const; private: std::vector *> Loggers; }; extern llvm::ManagedStatic Loggers; enum PlaceholderEnum {}; struct DebugLogOptionList : public llvm::cl::list { using list = llvm::cl::list; DebugLogOptionList() : list("debug-log", llvm::cl::desc("enable verbose logging"), llvm::cl::cat(MainCategory)) {} virtual bool addOccurrence(unsigned Pos, llvm::StringRef ArgName, llvm::StringRef Value, bool MultiArg = false) override { Loggers->enable(Value); return list::addOccurrence(Pos, ArgName, Value, MultiArg); } }; struct DebugLogOptionWrapper { DebugLogOptionList TheOption; }; extern llvm::ManagedStatic DebugLogOption; template<> inline void Logger::init() { Loggers->add(this); DebugLogOption->TheOption.getParser().addLiteralOption(Name.data(), Loggers->size(), description().data()); } template<> inline void Logger::init() { } class StreamWrapperBase { public: virtual void flush(std::stringstream &Buffer) = 0; virtual ~StreamWrapperBase() {} }; /// Class that wraps a stream of any type. It has a `flush` method, that, when /// called with a `std::stringstream` copy all of its content in the wrapped /// stream. /// /// The main reason for this is being able to have a dumpInternal method in a /// .cpp file while preserving a stream-agnostic dump method in the header. template class StreamWrapper : public StreamWrapperBase { public: StreamWrapper(O &Stream) : Stream(Stream) {} virtual void flush(std::stringstream &Buffer) override { Buffer.flush(); Stream << Buffer.str(); std::stringstream Empty; Buffer.swap(Empty); } private: O &Stream; }; /// \brief Enables a debug feature and disables it when goes out of scope class ScopedDebugFeature { public: /// \param Name the name of the debugging feature /// \param Enable whether to actually enable it or not ScopedDebugFeature(std::string Name, bool Enable) : Name(Name), Enabled(Enable) { if (Enabled) Loggers->enable(Name); } ~ScopedDebugFeature() { if (Enabled) Loggers->disable(Name); } private: std::string Name; bool Enabled; }; #define revng_log(Logger, Expr) \ do { \ if (Logger.isEnabled()) { \ (Logger) << Expr << DoLog; \ } \ } while (0) extern Logger<> NRALog; extern Logger<> PassesLog; extern Logger<> ReleaseLog; extern Logger<> VerifyLog; void writeToFile(llvm::StringRef What, llvm::StringRef Path) debug_function;