blob: 0bcd7377747e1e702d350ff1211d2848c8829e64 [file] [log] [blame]
#ifndef CAFFE2_CORE_LOGGING_H_
#define CAFFE2_CORE_LOGGING_H_
#include <climits>
#include <exception>
#include <functional>
#include <limits>
#include <sstream>
#include <ATen/core/Error.h>
#include "caffe2/core/flags.h"
#include "caffe2/proto/caffe2.pb.h"
// CAFFE2_LOG_THRESHOLD is a compile time flag that would allow us to turn off
// logging at compile time so no logging message below that level is produced
// at all. The value should be between INT_MIN and CAFFE_FATAL.
#ifndef CAFFE2_LOG_THRESHOLD
// If we have not defined the compile time log threshold, we keep all the
// log cases.
#define CAFFE2_LOG_THRESHOLD INT_MIN
#endif // CAFFE2_LOG_THRESHOLD
// Below are different implementations for glog and non-glog cases.
#ifdef CAFFE2_USE_GOOGLE_GLOG
#include "caffe2/core/logging_is_google_glog.h"
#else // !CAFFE2_USE_GOOGLE_GLOG
#include "caffe2/core/logging_is_not_google_glog.h"
#endif // CAFFE2_USE_GOOGLE_GLOG
CAFFE2_DECLARE_int(caffe2_log_level);
CAFFE2_DECLARE_bool(caffe2_use_fatal_for_enforce);
namespace caffe2 {
// Functions that we use for initialization.
bool InitCaffeLogging(int* argc, char** argv);
void UpdateLoggingLevelsFromFlags();
[[noreturn]] void ThrowEnforceNotMet(
const char* file,
const int line,
const char* condition,
const std::string& msg,
const void* caller = nullptr);
constexpr bool IsUsingGoogleLogging() {
#ifdef CAFFE2_USE_GOOGLE_GLOG
return true;
#else
return false;
#endif
}
/**
* A utility to allow one to show log info to stderr after the program starts.
*
* This is similar to calling GLOG's --logtostderr, or setting caffe2_log_level
* to smaller than INFO. You are recommended to only use this in a few sparse
* cases, such as when you want to write a tutorial or something. Normally, use
* the commandline flags to set the log level.
*/
void ShowLogInfoToStderr();
inline void MakeStringInternal(std::stringstream& /*ss*/) {}
template <typename T>
inline void MakeStringInternal(std::stringstream& ss, const T& t) {
ss << t;
}
template <typename T, typename... Args>
inline void
MakeStringInternal(std::stringstream& ss, const T& t, const Args&... args) {
MakeStringInternal(ss, t);
MakeStringInternal(ss, args...);
}
template <typename... Args>
string MakeString(const Args&... args) {
std::stringstream ss;
MakeStringInternal(ss, args...);
return string(ss.str());
}
// Specializations for already-a-string types.
template <>
inline string MakeString(const string& str) {
return str;
}
inline string MakeString(const char* c_str) {
return string(c_str);
}
template <class Container>
inline string Join(const string& delimiter, const Container& v) {
std::stringstream s;
int cnt = static_cast<int64_t>(v.size()) - 1;
for (auto i = v.begin(); i != v.end(); ++i, --cnt) {
s << (*i) << (cnt ? delimiter : "");
}
return s.str();
}
// Replace all occurrences of "from" substring to "to" string.
// Returns number of replacements
size_t ReplaceAll(string& s, const char* from, const char* to);
void SetStackTraceFetcher(std::function<string(void)> fetcher);
void SetOperatorLogger(std::function<void(const OperatorDef&)> tracer);
std::function<void(const OperatorDef&)> GetOperatorLogger();
using EnforceNotMet = at::Error;
#define CAFFE_ENFORCE(condition, ...) \
do { \
if (!(condition)) { \
::caffe2::ThrowEnforceNotMet( \
__FILE__, __LINE__, #condition, ::caffe2::MakeString(__VA_ARGS__)); \
} \
} while (false)
#define CAFFE_ENFORCE_WITH_CALLER(condition, ...) \
do { \
if (!(condition)) { \
::caffe2::ThrowEnforceNotMet( \
__FILE__, \
__LINE__, \
#condition, \
::caffe2::MakeString(__VA_ARGS__), \
this); \
} \
} while (false)
#define CAFFE_THROW(...) \
::caffe2::ThrowEnforceNotMet( \
__FILE__, __LINE__, "", ::caffe2::MakeString(__VA_ARGS__))
/**
* Rich logging messages
*
* CAFFE_ENFORCE_THAT can be used with one of the "checker functions" that
* capture input argument values and add it to the exception message. E.g.
* `CAFFE_ENFORCE_THAT(Equals(foo(x), bar(y)), "Optional additional message")`
* would evaluate both foo and bar only once and if the results are not equal -
* include them in the exception message.
*
* Some of the basic checker functions like Equals or Greater are already
* defined below. Other header might define customized checkers by adding
* functions to caffe2::enforce_detail namespace. For example:
*
* namespace caffe2 { namespace enforce_detail {
* inline EnforceFailMessage IsVector(const vector<TIndex>& shape) {
* if (shape.size() == 1) { return EnforceOK(); }
* return MakeString("Shape ", shape, " is not a vector");
* }
* }}
*
* With further usages like `CAFFE_ENFORCE_THAT(IsVector(Input(0).dims()))`
*
* Convenient wrappers for binary operations like CAFFE_ENFORCE_EQ are provided
* too. Please use them instead of CHECK_EQ and friends for failures in
* user-provided input.
*/
namespace enforce_detail {
struct EnforceOK {};
class EnforceFailMessage {
public:
#ifdef _MSC_VER
// MSVC + NVCC ignores constexpr and will issue a warning if included.
/* implicit */ EnforceFailMessage(EnforceOK) : msg_(nullptr) {}
#else
constexpr /* implicit */ EnforceFailMessage(EnforceOK) : msg_(nullptr) {}
#endif
EnforceFailMessage(EnforceFailMessage&&) = default;
EnforceFailMessage(const EnforceFailMessage&) = delete;
EnforceFailMessage& operator=(EnforceFailMessage&&) = delete;
EnforceFailMessage& operator=(const EnforceFailMessage&) = delete;
// Catch all wrong usages like CAFFE_ENFORCE_THAT(x < y)
template <class... Args>
/* implicit */ EnforceFailMessage(Args...) {
static_assert(
// This stands for an "impossible" condition. Plain `false` doesn't
// trick compiler enough.
sizeof...(Args) == std::numeric_limits<std::size_t>::max(),
"CAFFE_ENFORCE_THAT has to be used with one of special check functions "
"like `Equals`. Use CAFFE_ENFORCE for simple boolean checks.");
}
/* implicit */ EnforceFailMessage(std::string&& msg) {
msg_ = new std::string(std::move(msg));
}
inline bool bad() const {
return msg_ != nullptr;
}
std::string get_message_and_free(std::string&& extra) const {
std::string r;
if (extra.empty()) {
r = std::move(*msg_);
} else {
r = ::caffe2::MakeString(std::move(*msg_), ". ", std::move(extra));
}
delete msg_;
return r;
}
private:
std::string* msg_;
};
#define BINARY_COMP_HELPER(name, op) \
template <typename T1, typename T2> \
inline EnforceFailMessage name(const T1& x, const T2& y) { \
if (x op y) { \
return EnforceOK(); \
} \
return MakeString(x, " vs ", y); \
}
BINARY_COMP_HELPER(Equals, ==)
BINARY_COMP_HELPER(NotEquals, !=)
BINARY_COMP_HELPER(Greater, >)
BINARY_COMP_HELPER(GreaterEquals, >=)
BINARY_COMP_HELPER(Less, <)
BINARY_COMP_HELPER(LessEquals, <=)
#undef BINARY_COMP_HELPER
#define CAFFE_ENFORCE_THAT_IMPL(condition, expr, ...) \
do { \
using namespace ::caffe2::enforce_detail; \
const EnforceFailMessage& CAFFE_ENFORCE_THAT_IMPL_r_ = (condition); \
if (CAFFE_ENFORCE_THAT_IMPL_r_.bad()) { \
::caffe2::ThrowEnforceNotMet( \
__FILE__, \
__LINE__, \
expr, \
CAFFE_ENFORCE_THAT_IMPL_r_.get_message_and_free( \
::caffe2::MakeString(__VA_ARGS__))); \
} \
} while (false)
#define CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER(condition, expr, ...) \
do { \
using namespace ::caffe2::enforce_detail; \
const EnforceFailMessage& CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER_r_ = \
(condition); \
if (CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER_r_.bad()) { \
::caffe2::ThrowEnforceNotMet( \
__FILE__, \
__LINE__, \
expr, \
CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER_r_.get_message_and_free( \
::caffe2::MakeString(__VA_ARGS__)), \
this); \
} \
} while (false)
}
#define CAFFE_ENFORCE_THAT(condition, ...) \
CAFFE_ENFORCE_THAT_IMPL((condition), #condition, __VA_ARGS__)
#define CAFFE_ENFORCE_EQ(x, y, ...) \
CAFFE_ENFORCE_THAT_IMPL(Equals((x), (y)), #x " == " #y, __VA_ARGS__)
#define CAFFE_ENFORCE_NE(x, y, ...) \
CAFFE_ENFORCE_THAT_IMPL(NotEquals((x), (y)), #x " != " #y, __VA_ARGS__)
#define CAFFE_ENFORCE_LE(x, y, ...) \
CAFFE_ENFORCE_THAT_IMPL(LessEquals((x), (y)), #x " <= " #y, __VA_ARGS__)
#define CAFFE_ENFORCE_LT(x, y, ...) \
CAFFE_ENFORCE_THAT_IMPL(Less((x), (y)), #x " < " #y, __VA_ARGS__)
#define CAFFE_ENFORCE_GE(x, y, ...) \
CAFFE_ENFORCE_THAT_IMPL(GreaterEquals((x), (y)), #x " >= " #y, __VA_ARGS__)
#define CAFFE_ENFORCE_GT(x, y, ...) \
CAFFE_ENFORCE_THAT_IMPL(Greater((x), (y)), #x " > " #y, __VA_ARGS__)
#define CAFFE_ENFORCE_EQ_WITH_CALLER(x, y, ...) \
CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER(Equals((x), (y)), #x " == " #y, __VA_ARGS__)
#define CAFFE_ENFORCE_NE_WITH_CALLER(x, y, ...) \
CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER(NotEquals((x), (y)), #x " != " #y, __VA_ARGS__)
#define CAFFE_ENFORCE_LE_WITH_CALLER(x, y, ...) \
CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER(LessEquals((x), (y)), #x " <= " #y, __VA_ARGS__)
#define CAFFE_ENFORCE_LT_WITH_CALLER(x, y, ...) \
CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER(Less((x), (y)), #x " < " #y, __VA_ARGS__)
#define CAFFE_ENFORCE_GE_WITH_CALLER(x, y, ...) \
CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER(GreaterEquals((x), (y)), #x " >= " #y, __VA_ARGS__)
#define CAFFE_ENFORCE_GT_WITH_CALLER(x, y, ...) \
CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER(Greater((x), (y)), #x " > " #y, __VA_ARGS__)
} // namespace caffe2
#endif // CAFFE2_CORE_LOGGING_H_