Files
revng-revng/debug.h
T
Alessandro Di Federico cb8a987122 Introduce Logger
`Logger` is the new infrastructrure for debug messages. They are
supposed to be used by just creating a global variable in a translation
unit and then writing there directly as if they were a stream.

Key features:

* Self-registration: a `Logger` by default register itself automatically
  in a register. This means that at run-time, unlike with `DBG`, we have
  a list of all the possible `Logger`s. One benefit of this approach is
  being able to list all the available `Logger`s in `--help`.
* Indentation: `Logger`s can be indented. In particular the
  `LoggerIndent` helper class can automatically indent a certain
  `Logger` during the lifetime of its instance.
* Atomic messages: a message is no longer simply delimited by a "\n": to
  mark the end of a message, an instance of `LogTerminator` has to be
  streamed to the `Logger`. This can also be associated directly to the
  lifetime of an object by using the `LogOnReturn` class.
* Custom class formatting: it is possible to decide how a certain object
  should be formatted when sent to a `Logger` by implementing the
  `writeToLog` function. For example, `llvm::Value`-derived objects are
  passed through the `getName` function. If no custom formatter is
  specified, the `operator<<` is employed.
2018-05-30 12:45:45 +02:00

222 lines
5.7 KiB
C++

#ifndef _DEBUG_H
#define _DEBUG_H
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
// Standard includes
#include <functional>
#include <ostream>
#include <string>
#include <sstream>
#include <vector>
// LLVM includes
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ManagedStatic.h"
// TODO: use a dedicated namespace
extern bool DebuggingEnabled;
extern std::ostream &dbg;
bool isDebugFeatureEnabled(std::string Name);
void enableDebugFeature(std::string Name);
void disableDebugFeature(std::string Name);
/// \brief Executes \p code only if \p feature is enabled
///
/// \note This approach is deprecated, please consider using Logger and letting
/// the DCE do its job for you.
#define DBG(feature, code) do { \
if (DebuggingEnabled && isDebugFeatureEnabled(feature)) { \
code; \
} \
} while (0)
/// \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)
enableDebugFeature(Name);
}
~ScopedDebugFeature() {
if (Enabled)
disableDebugFeature(Name);
}
private:
std::string Name;
bool Enabled;
};
/// \brief Stream an instance of this class to call Logger::emit()
struct LogTerminator { };
extern LogTerminator DoLog;
/// \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<bool StaticEnabled=true>
class Logger {
private:
static unsigned IndentLevel;
public:
Logger(llvm::StringRef Name) : Name(Name), Enabled(false) { init(); }
void indent(unsigned Level=1) {
if (isEnabled())
IndentLevel += Level;
}
void unindent(unsigned Level=1) {
if (isEnabled())
IndentLevel -= Level;
}
void setIndentation(unsigned Level) {
if (isEnabled())
IndentLevel = Level;
}
bool isEnabled() const { return StaticEnabled && Enabled; }
llvm::StringRef name() const { return Name; }
void enable() { 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 emit();
template<typename T>
inline Logger &operator<<(const T Other) {
writeToLog(*this, Other, static_cast<int>(0));
return *this;
}
template<bool X>
friend void writeToLog(Logger<X> &This, const LogTerminator T, int Ignore);
template<bool X, typename T, typename LowPrio>
friend void writeToLog(Logger<X> &This, const T Other, LowPrio Ignore);
private:
void init();
private:
llvm::StringRef Name;
std::stringstream Buffer;
bool Enabled;
};
/// \brief Indent all loggers within the scope of this object
template<bool StaticEnabled=true>
class LoggerIndent {
public:
LoggerIndent(Logger<StaticEnabled> &L) : L(L) { L.indent(); }
~LoggerIndent() { L.unindent(); }
private:
Logger<StaticEnabled> &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<bool StaticEnabled=true>
class LogOnReturn {
public:
LogOnReturn(Logger<StaticEnabled> &L) : L(L) { }
~LogOnReturn() { L.emit(); }
private:
Logger<StaticEnabled> &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<bool X, typename T, typename LowPrio>
inline void writeToLog(Logger<X> &This, const T Other, LowPrio Ignore) {
(void) Other;
(void) Ignore;
if (This.isEnabled())
This.Buffer << Other;
}
/// \brief Specialization of writeToLog to emit a message
template<bool X>
inline void writeToLog(Logger<X> &This, const LogTerminator T, int Ignore) {
(void) T;
(void) Ignore;
This.emit();
}
/// \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<true> *L) { Loggers.push_back(L); }
void add(Logger<false> *L) { (void) L; }
void enable(llvm::StringRef Name) {
for (Logger<true> *L : Loggers)
if (L->name() == Name)
L->enable();
}
void disable(llvm::StringRef Name) {
for (Logger<true> *L : Loggers)
if (L->name() == Name)
L->disable();
}
private:
std::vector<Logger<true> *> Loggers;
};
extern llvm::ManagedStatic<LoggersRegistry> Loggers;
template<>
inline void Logger<true>::init() {
Loggers->add(this);
}
template<>
inline void Logger<false>::init() {
}
#endif // _DEBUG_H