/// Implementation of the debug framework. // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include "llvm/ADT/Twine.h" #include "llvm/IR/Value.h" #include "llvm/Support/ManagedStatic.h" #include "revng/Support/Assert.h" #include "revng/Support/CommandLine.h" #include "revng/Support/Debug.h" namespace cl = llvm::cl; using llvm::Twine; static cl::opt MaxLocationLength("debug-location-max-length", cl::desc("emit file and line number " "for log " "messages of at most this " "size."), cl::cat(MainCategory), cl::init(0)); size_t MaxLoggerNameLength = 0; /// 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); } size_t size() const { return Loggers.size(); } /// \return false is logger does not exists [[nodiscard]] bool enable(llvm::StringRef Name) { for (Logger *L : Loggers) { if (L->name() == Name) { L->enable(); return true; } } return false; } 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; }; static llvm::ManagedStatic Loggers; ScopedDebugFeature::ScopedDebugFeature(std::string Name, bool Enable) : Name(Name), Enabled(Enable) { if (Enabled) { bool Found = Loggers->enable(Name); revng_assert(Found); } } ScopedDebugFeature::~ScopedDebugFeature() { if (Enabled) Loggers->disable(Name); } std::ostream &dbg(std::cerr); Logger PassesLog("passes"); Logger ReleaseLog("release"); Logger VerifyLog("verify"); void Logger::flush(const LogTerminator &LineInfo) { if (Enabled) { std::string Pad; if (MaxLocationLength != 0) { std::string Suffix = (Twine(":") + Twine(LineInfo.Line)).str(); revng_assert(Suffix.size() < MaxLocationLength); std::string Location(LineInfo.File); size_t LastSlash = Location.rfind("/"); if (LastSlash != std::string::npos) Location.erase(0, LastSlash + 1); if (Location.size() > MaxLocationLength - Suffix.size()) { Location.erase(MaxLocationLength - Suffix.size(), std::string::npos); } Pad = std::string(MaxLocationLength - Location.size() - Suffix.size(), ' '); dbg << "[" << Location << Suffix << Pad << "] "; } Pad = std::string(MaxLoggerNameLength - Name.size(), ' '); dbg << "[" << Name.data() << Pad << "] "; dbg << std::string(IndentLevel * 2, ' '); std::string Data = Buffer.str(); if (Data.size() > 0 and Data.back() == '\n') Data.resize(Data.size() - 1); std::string Delimiter = "\n"; size_t Start = 0; size_t End = Data.find(Delimiter); dbg << Data.substr(Start, End) << "\n"; if (End != std::string::npos) { Pad = std::string(3 + MaxLoggerNameLength + IndentLevel * 2, ' '); do { Start = End + Delimiter.length(); End = Data.find(Delimiter, Start); dbg << Pad << Data.substr(Start, End - Start) << "\n"; } while (End != std::string::npos); } Buffer.str(""); Buffer.clear(); } } 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 { bool Found = Loggers->enable(Value); if (not Found) { dbg << "Logger \"" << Value.str() << "\" not found.\n"; return true; } return list::addOccurrence(Pos, ArgName, Value, MultiArg); } }; struct DebugLogOptionWrapper { DebugLogOptionList TheOption; }; static std::unique_ptr> DebugLogging; static std::unique_ptr DebugLoggingAlias; llvm::ManagedStatic DebugLogOption; void Logger::init() { Loggers->add(this); if (Name.size() > 0) { auto &Parser = DebugLogOption->TheOption.getParser(); Parser.addLiteralOption(Name.data(), Loggers->size(), description().data()); } } unsigned Logger::IndentLevel; void Logger::indent(unsigned Level) { if (isEnabled()) IndentLevel += Level; } void Logger::unindent(unsigned Level) { if (isEnabled()) { revng_assert(static_cast(IndentLevel) - static_cast(Level) >= 0); IndentLevel -= Level; } } void Logger::setIndentation(unsigned Level) { if (isEnabled()) IndentLevel = Level; } void writeToFile(llvm::StringRef What, llvm::StringRef Path) { std::error_code EC; llvm::raw_fd_ostream File(Path, EC); if (EC) revng_abort(llvm::Twine("Error opening file ", Path).str().c_str()); File << What; }