Moving logging from caffe2 to c10. (#12881)

Summary:
Pull Request resolved: https://github.com/pytorch/pytorch/pull/12881

TSIA. This should not change any functionality.

Remaining work:
- change the build script to deprecate use of CAFFE2_USE_MINIMAL_GOOGLE_GLOG and use a C10 macro instead.
- Unify the exception name (EnforceNotMet -> Error)
- Unify the logging and warning APIs (like AT_WARNING)

Reviewed By: dzhulgakov

Differential Revision: D10441597

fbshipit-source-id: 4784dc0cd5af83dacb10c4952a2d1d7236b3f14d
This commit is contained in:
Yangqing Jia
2018-10-19 20:19:48 -07:00
committed by Facebook Github Bot
parent d120b9af5a
commit 7dbb38e856
9 changed files with 461 additions and 408 deletions

View File

@ -39,6 +39,10 @@ if (${USE_GLOG})
target_link_libraries(c10 PUBLIC glog::glog)
endif()
if (ANDROID)
target_link_libraries(c10 PRIVATE log)
endif()
target_include_directories(
c10 PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../>

View File

@ -52,4 +52,11 @@
// the c10 namespace but not any nontrivial files.
namespace c10 {} // namespace c10
// C10_NORETURN
#if defined(_MSC_VER)
#define C10_NORETURN __declspec(noreturn)
#else
#define C10_NORETURN __attribute__((noreturn))
#endif
#endif // C10_MACROS_MACROS_H_

View File

@ -1,9 +1,13 @@
#include <algorithm>
#include "caffe2/core/logging.h"
#include <gtest/gtest.h>
#include "c10/util/Logging.h"
namespace caffe2 {
namespace c10_test {
using std::set;
using std::string;
using std::vector;
TEST(LoggingTest, TestEnforceTrue) {
// This should just work.
@ -17,7 +21,7 @@ TEST(LoggingTest, TestEnforceFalse) {
CAFFE_ENFORCE(false, "This throws.");
// This should never be triggered.
ADD_FAILURE();
} catch (const EnforceNotMet&) {
} catch (const ::c10::Error&) {
}
std::swap(FLAGS_caffe2_use_fatal_for_enforce, kFalse);
}
@ -29,7 +33,7 @@ TEST(LoggingTest, TestEnforceEquals) {
CAFFE_ENFORCE_THAT(Equals(++x, ++y));
// This should never be triggered.
ADD_FAILURE();
} catch (const EnforceNotMet& err) {
} catch (const ::c10::Error& err) {
EXPECT_NE(err.msg().find("5 vs 6"), string::npos);
}
@ -45,11 +49,11 @@ TEST(LoggingTest, EnforceShowcase) {
int one = 1;
int two = 2;
int three = 3;
#define WRAP_AND_PRINT(exp) \
try { \
exp; \
} catch (const EnforceNotMet&) { \
/* EnforceNotMet already does LOG(ERROR) */ \
#define WRAP_AND_PRINT(exp) \
try { \
exp; \
} catch (const ::c10::Error&) { \
/* ::c10::Error already does LOG(ERROR) */ \
}
WRAP_AND_PRINT(CAFFE_ENFORCE_EQ(one, two));
WRAP_AND_PRINT(CAFFE_ENFORCE_NE(one * 2, two));
@ -82,4 +86,4 @@ TEST(LoggingDeathTest, TestEnforceUsingFatal) {
}
#endif
} // namespace caffe2
} // namespace c10_test

View File

@ -1,5 +1,5 @@
#include "caffe2/core/logging.h"
#include "caffe2/core/flags.h"
#include "c10/util/Logging.h"
#include "c10/util/Flags.h"
#include <algorithm>
#include <cstring>
@ -14,7 +14,7 @@ C10_DEFINE_bool(
"If set true, when CAFFE_ENFORCE is not met, abort instead "
"of throwing an exception.");
namespace caffe2 {
namespace c10 {
namespace enforce_detail {
/* implicit */ EnforceFailMessage::EnforceFailMessage(std::string&& msg) {
msg_ = new std::string(std::move(msg));
@ -45,9 +45,9 @@ void ThrowEnforceNotMet(
throw e;
}
} // namespace caffe2
} // namespace c10
#ifdef C10_USE_GFLAGS
#if defined(C10_USE_GFLAGS) && defined(C10_USE_GLOG)
// When GLOG depends on GFLAGS, these variables are being defined in GLOG
// directly via the GFLAGS definition, so we will use DECLARE_* to declare
// them, and use them in Caffe2.
@ -57,25 +57,28 @@ DECLARE_int32(minloglevel);
DECLARE_int32(v);
// GLOG's logtostderr value
DECLARE_bool(logtostderr);
#elif !CAFFE2_MOBILE && !__APPLE__ && !defined(_WIN32)
// Declare our own versions of the above flags so we don't error out
// when they are passed into Caffe2.
C10_DEFINE_int(minloglevel, 0, "Equivalent to glog minloglevel");
C10_DEFINE_int(v, 0, "Equivalent to glog verbose");
C10_DEFINE_bool(logtostderr, false, "Equivalent to glog logtostderr");
#endif // C10_USE_GFLAGS
#endif // defined(C10_USE_GFLAGS) && defined(C10_USE_GLOG)
#ifdef CAFFE2_USE_GOOGLE_GLOG
#if !defined(C10_USE_GLOG)
// This backward compatibility flags are in order to deal with cases where
// Caffe2 are not built with glog, but some init flags still pass in these
// flags. They may go away in the future.
C10_DEFINE_int32(minloglevel, 0, "Equivalent to glog minloglevel");
C10_DEFINE_int32(v, 0, "Equivalent to glog verbose");
C10_DEFINE_bool(logtostderr, false, "Equivalent to glog logtostderr");
#endif // !defined(c10_USE_GLOG)
#ifdef C10_USE_GLOG
// Provide easy access to the above variables, regardless whether GLOG is
// dependent on GFLAGS or not. Note that the namespace (fLI, fLB) is actually
// consistent between GLOG and GFLAGS, so we can do the below declaration
// consistently.
namespace caffe2 {
namespace c10 {
using fLB::FLAGS_logtostderr;
using fLI::FLAGS_minloglevel;
using fLI::FLAGS_v;
using fLB::FLAGS_logtostderr;
} // namespace caffe2
} // namespace c10
C10_DEFINE_int(
caffe2_log_level,
@ -89,13 +92,13 @@ C10_DEFINE_int(
namespace google {
namespace glog_internal_namespace_ {
bool IsGoogleLoggingInitialized();
} // namespace glog_internal_namespace_
} // namespace google
} // namespace glog_internal_namespace_
} // namespace google
namespace caffe2 {
namespace c10 {
bool InitCaffeLogging(int* argc, char** argv) {
if (*argc == 0) return true;
if (*argc == 0)
return true;
#if !defined(_MSC_VER)
// This trick can only be used on UNIX platforms
if (!::google::glog_internal_namespace_::IsGoogleLoggingInitialized())
@ -103,7 +106,7 @@ bool InitCaffeLogging(int* argc, char** argv) {
{
::google::InitGoogleLogging(argv[0]);
#if !defined(_MSC_VER)
// This is never defined on Windows
// This is never defined on Windows
::google::InstallFailureSignalHandler();
#endif
}
@ -129,9 +132,9 @@ void ShowLogInfoToStderr() {
FLAGS_logtostderr = 1;
FLAGS_minloglevel = std::min(FLAGS_minloglevel, google::GLOG_INFO);
}
} // namespace caffe2
} // namespace c10
#else // !CAFFE2_USE_GOOGLE_GLOG
#else // !C10_USE_GLOG
#ifdef ANDROID
#include <android/log.h>
@ -142,11 +145,12 @@ C10_DEFINE_int(
ERROR,
"The minimum log level that caffe2 will output.");
namespace caffe2 {
namespace c10 {
bool InitCaffeLogging(int* argc, char** argv) {
// When doing InitCaffeLogging, we will assume that caffe's flag paser has
// already finished.
if (*argc == 0) return true;
if (*argc == 0)
return true;
if (!c10::CommandLineFlagsHasBeenParsed()) {
std::cerr << "InitCaffeLogging() has to be called after "
"c10::ParseCommandLineFlags. Modify your program to make sure "
@ -162,24 +166,23 @@ bool InitCaffeLogging(int* argc, char** argv) {
return true;
}
void UpdateLoggingLevelsFromFlags() {
}
void UpdateLoggingLevelsFromFlags() {}
void ShowLogInfoToStderr() {
FLAGS_caffe2_log_level = INFO;
}
MessageLogger::MessageLogger(const char *file, int line, int severity)
: severity_(severity) {
MessageLogger::MessageLogger(const char* file, int line, int severity)
: severity_(severity) {
if (severity_ < FLAGS_caffe2_log_level) {
// Nothing needs to be logged.
return;
}
#ifdef ANDROID
tag_ = "native";
#else // !ANDROID
#else // !ANDROID
tag_ = "";
#endif // ANDROID
#endif // ANDROID
/*
time_t rawtime;
struct tm * timeinfo;
@ -210,12 +213,12 @@ MessageLogger::~MessageLogger() {
stream_ << "\n";
#ifdef ANDROID
static const int android_log_levels[] = {
ANDROID_LOG_FATAL, // LOG_FATAL
ANDROID_LOG_ERROR, // LOG_ERROR
ANDROID_LOG_WARN, // LOG_WARNING
ANDROID_LOG_INFO, // LOG_INFO
ANDROID_LOG_DEBUG, // VLOG(1)
ANDROID_LOG_VERBOSE, // VLOG(2) .. VLOG(N)
ANDROID_LOG_FATAL, // LOG_FATAL
ANDROID_LOG_ERROR, // LOG_ERROR
ANDROID_LOG_WARN, // LOG_WARNING
ANDROID_LOG_INFO, // LOG_INFO
ANDROID_LOG_DEBUG, // VLOG(1)
ANDROID_LOG_VERBOSE, // VLOG(2) .. VLOG(N)
};
int android_level_index = FATAL - std::min(FATAL, severity_);
int level = android_log_levels[std::min(android_level_index, 5)];
@ -225,7 +228,7 @@ MessageLogger::~MessageLogger() {
if (severity_ == FATAL) {
__android_log_print(ANDROID_LOG_FATAL, tag_, "terminating.\n");
}
#else // !ANDROID
#else // !ANDROID
if (severity_ >= FLAGS_caffe2_log_level) {
// If not building on Android, log all output to std::cerr.
std::cerr << stream_.str();
@ -236,12 +239,12 @@ MessageLogger::~MessageLogger() {
std::cerr << std::flush;
}
}
#endif // ANDROID
#endif // ANDROID
if (severity_ == FATAL) {
DealWithFatal();
}
}
} // namespace caffe2
} // namespace c10
#endif // !CAFFE2_USE_GOOGLE_GLOG
#endif // !C10_USE_GLOG

246
c10/util/Logging.h Normal file
View File

@ -0,0 +1,246 @@
#ifndef C10_UTIL_LOGGING_H_
#define C10_UTIL_LOGGING_H_
#include <climits>
#include <exception>
#include <functional>
#include <limits>
#include <sstream>
#include "c10/macros/Macros.h"
#include "c10/util/Exception.h"
#include "c10/util/Flags.h"
#include "c10/util/StringUtil.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 C10_USE_GLOG
#include "c10/util/logging_is_google_glog.h"
#else // !C10_USE_GLOG
#include "c10/util/logging_is_not_google_glog.h"
#endif // C10_USE_GLOG
C10_DECLARE_int(caffe2_log_level);
C10_DECLARE_bool(caffe2_use_fatal_for_enforce);
namespace c10 {
using std::string;
// Functions that we use for initialization.
C10_API bool InitCaffeLogging(int* argc, char** argv);
C10_API void UpdateLoggingLevelsFromFlags();
C10_API C10_NORETURN void ThrowEnforceNotMet(
const char* file,
const int line,
const char* condition,
const std::string& msg,
const void* caller = nullptr);
constexpr bool IsUsingGoogleLogging() {
#ifdef C10_USE_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.
*/
C10_API void ShowLogInfoToStderr();
C10_API void SetStackTraceFetcher(std::function<string(void)> fetcher);
using EnforceNotMet = ::c10::Error;
#define CAFFE_ENFORCE(condition, ...) \
do { \
if (!(condition)) { \
::c10::ThrowEnforceNotMet( \
__FILE__, __LINE__, #condition, ::c10::str(__VA_ARGS__)); \
} \
} while (false)
#define CAFFE_ENFORCE_WITH_CALLER(condition, ...) \
do { \
if (!(condition)) { \
::c10::ThrowEnforceNotMet( \
__FILE__, __LINE__, #condition, ::c10::str(__VA_ARGS__), this); \
} \
} while (false)
#define CAFFE_THROW(...) \
::c10::ThrowEnforceNotMet(__FILE__, __LINE__, "", ::c10::str(__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<int64_t>& shape) {
* if (shape.size() == 1) { return EnforceOK(); }
* return c10::str("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 C10_API EnforceOK {};
class C10_API 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);
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 = ::c10::str(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 c10::str(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 ::c10::enforce_detail; \
const EnforceFailMessage& CAFFE_ENFORCE_THAT_IMPL_r_ = (condition); \
if (CAFFE_ENFORCE_THAT_IMPL_r_.bad()) { \
::c10::ThrowEnforceNotMet( \
__FILE__, \
__LINE__, \
expr, \
CAFFE_ENFORCE_THAT_IMPL_r_.get_message_and_free( \
::c10::str(__VA_ARGS__))); \
} \
} while (false)
#define CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER(condition, expr, ...) \
do { \
using namespace ::c10::enforce_detail; \
const EnforceFailMessage& CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER_r_ = \
(condition); \
if (CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER_r_.bad()) { \
::c10::ThrowEnforceNotMet( \
__FILE__, \
__LINE__, \
expr, \
CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER_r_.get_message_and_free( \
::c10::str(__VA_ARGS__)), \
this); \
} \
} while (false)
} // namespace enforce_detail
#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 c10
#endif // C10_UTIL_LOGGING_H_

View File

@ -0,0 +1,52 @@
#ifndef C10_UTIL_LOGGING_IS_GOOGLE_GLOG_H_
#define C10_UTIL_LOGGING_IS_GOOGLE_GLOG_H_
#include <map>
#include <set>
#include <vector>
#include <iomanip> // because some of the caffe2 code uses e.g. std::setw
// Using google glog. For glog 0.3.2 versions, stl_logging.h needs to be before
// logging.h to actually use stl_logging. Because template magic.
// In addition, we do not do stl logging in .cu files because nvcc does not like
// it. Some mobile platforms do not like stl_logging, so we add an
// overload in that case as well.
#ifdef __CUDACC__
#include <cuda.h>
#endif
#if !defined(__CUDACC__) && !defined(CAFFE2_USE_MINIMAL_GOOGLE_GLOG)
#include <glog/stl_logging.h>
// Old versions of glog don't declare this using declaration, so help
// them out. Fortunately, C++ won't complain if you declare the same
// using declaration multiple times.
namespace std {
using ::operator<<;
}
#else // !defined(__CUDACC__) && !defined(CAFFE2_USE_MINIMAL_GOOGLE_GLOG)
// In the cudacc compiler scenario, we will simply ignore the container
// printout feature. Basically we need to register a fake overload for
// vector/string - here, we just ignore the entries in the logs.
namespace std {
#define INSTANTIATE_FOR_CONTAINER(container) \
template <class... Types> \
ostream& operator<<(ostream& out, const container<Types...>&) { \
return out; \
}
INSTANTIATE_FOR_CONTAINER(vector)
INSTANTIATE_FOR_CONTAINER(map)
INSTANTIATE_FOR_CONTAINER(set)
#undef INSTANTIATE_FOR_CONTAINER
} // namespace std
#endif
#include <glog/logging.h>
#endif // C10_UTIL_LOGGING_IS_GOOGLE_GLOG_H_

View File

@ -1,42 +1,47 @@
#ifndef CAFFE2_CORE_LOGGING_IS_NOT_GOOGLE_GLOG_H_
#define CAFFE2_CORE_LOGGING_IS_NOT_GOOGLE_GLOG_H_
#ifndef C10_UTIL_LOGGING_IS_NOT_GOOGLE_GLOG_H_
#define C10_UTIL_LOGGING_IS_NOT_GOOGLE_GLOG_H_
#include <chrono>
#include <climits>
#include <ctime>
#include <iomanip>
#include <string>
#include <fstream>
#include <iomanip>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include "caffe2/core/flags.h"
#include "c10/util/Flags.h"
// Log severity level constants.
const int FATAL = 3;
const int FATAL = 3;
#if !defined(_MSC_VER) || !defined(ERROR)
// Windows defines the ERROR macro already, and as a result we will
// simply use that one. The downside is that one will now mix LOG(INFO)
// and LOG(ERROR) because ERROR is defined to be zero. Anyway, the
// recommended way is to use glog so fixing this is a low-pri item.
const int ERROR = 2;
const int ERROR = 2;
#endif
const int WARNING = 1;
const int INFO = 0;
const int INFO = 0;
const char CAFFE2_SEVERITY_PREFIX[] = "FEWIV";
namespace caffe2 {
class CAFFE2_API MessageLogger {
namespace c10 {
class C10_API MessageLogger {
public:
MessageLogger(const char *file, int line, int severity);
MessageLogger(const char* file, int line, int severity);
~MessageLogger();
// Return the stream associated with the logger object.
std::stringstream &stream() { return stream_; }
std::stringstream& stream() {
return stream_;
}
private:
// When there is a fatal log, we simply abort.
void DealWithFatal() { abort(); }
void DealWithFatal() {
abort();
}
const char* tag_;
std::stringstream stream_;
@ -46,24 +51,24 @@ class CAFFE2_API MessageLogger {
// This class is used to explicitly ignore values in the conditional
// logging macros. This avoids compiler warnings like "value computed
// is not used" and "statement has no effect".
class CAFFE2_API LoggerVoidify {
class C10_API LoggerVoidify {
public:
LoggerVoidify() { }
LoggerVoidify() {}
// This has to be an operator with a precedence lower than << but
// higher than ?:
void operator&(const std::ostream &s) { }
void operator&(const std::ostream& s) {}
};
// Log a message and terminate.
template<class T>
void LogMessageFatal(const char *file, int line, const T &message) {
template <class T>
void LogMessageFatal(const char* file, int line, const T& message) {
MessageLogger(file, line, FATAL).stream() << message;
}
// Helpers for CHECK_NOTNULL(). Two are necessary to support both raw pointers
// and smart pointers.
template <typename T>
T& CheckNotNullCommon(const char *file, int line, const char *names, T& t) {
T& CheckNotNullCommon(const char* file, int line, const char* names, T& t) {
if (t == nullptr) {
LogMessageFatal(file, line, std::string(names));
}
@ -71,55 +76,56 @@ T& CheckNotNullCommon(const char *file, int line, const char *names, T& t) {
}
template <typename T>
T* CheckNotNull(const char *file, int line, const char *names, T* t) {
T* CheckNotNull(const char* file, int line, const char* names, T* t) {
return CheckNotNullCommon(file, line, names, t);
}
template <typename T>
T& CheckNotNull(const char *file, int line, const char *names, T& t) {
T& CheckNotNull(const char* file, int line, const char* names, T& t) {
return CheckNotNullCommon(file, line, names, t);
}
} // namespace caffe2
} // namespace c10
// ---------------------- Logging Macro definitions --------------------------
static_assert(CAFFE2_LOG_THRESHOLD <= FATAL,
"CAFFE2_LOG_THRESHOLD should at most be FATAL.");
static_assert(
CAFFE2_LOG_THRESHOLD <= FATAL,
"CAFFE2_LOG_THRESHOLD should at most be FATAL.");
// If n is under the compile time caffe log threshold, The _CAFFE_LOG(n)
// should not generate anything in optimized code.
#define LOG(n) \
#define LOG(n) \
if (n >= CAFFE2_LOG_THRESHOLD) \
::caffe2::MessageLogger((char*)__FILE__, __LINE__, n).stream()
::c10::MessageLogger((char*)__FILE__, __LINE__, n).stream()
#define VLOG(n) LOG((-n))
#define LOG_IF(n, condition) \
if (n >= CAFFE2_LOG_THRESHOLD && (condition)) \
::caffe2::MessageLogger((char*)__FILE__, __LINE__, n).stream()
::c10::MessageLogger((char*)__FILE__, __LINE__, n).stream()
#define VLOG_IF(n, condition) LOG_IF((-n), (condition))
#define VLOG_IS_ON(verboselevel) (CAFFE2_LOG_THRESHOLD <= -(verboselevel))
// Log only if condition is met. Otherwise evaluates to void.
#define FATAL_IF(condition) \
condition ? (void) 0 : ::caffe2::LoggerVoidify() & \
::caffe2::MessageLogger((char*)__FILE__, __LINE__, FATAL).stream()
#define FATAL_IF(condition) \
condition ? (void)0 \
: ::c10::LoggerVoidify() & \
::c10::MessageLogger((char*)__FILE__, __LINE__, FATAL).stream()
// Check for a given boolean condition.
#define CHECK(condition) FATAL_IF(condition) \
<< "Check failed: " #condition " "
#define CHECK(condition) FATAL_IF(condition) << "Check failed: " #condition " "
#ifndef NDEBUG
// Debug only version of CHECK
#define DCHECK(condition) FATAL_IF(condition) \
<< "Check failed: " #condition " "
#define DCHECK(condition) FATAL_IF(condition) << "Check failed: " #condition " "
#else
// Optimized version - generates no code.
#define DCHECK(condition) if(false) CHECK(condition)
#endif // NDEBUG
#define DCHECK(condition) \
if (false) \
CHECK(condition)
#endif // NDEBUG
#define CHECK_OP(val1, val2, op) FATAL_IF((val1 op val2)) \
<< "Check failed: " #val1 " " #op " " #val2 " "
#define CHECK_OP(val1, val2, op) \
FATAL_IF((val1 op val2)) << "Check failed: " #val1 " " #op " " #val2 " "
// Check_op macro definitions
#define CHECK_EQ(val1, val2) CHECK_OP(val1, val2, ==)
@ -137,30 +143,44 @@ static_assert(CAFFE2_LOG_THRESHOLD <= FATAL,
#define DCHECK_LT(val1, val2) CHECK_OP(val1, val2, <)
#define DCHECK_GE(val1, val2) CHECK_OP(val1, val2, >=)
#define DCHECK_GT(val1, val2) CHECK_OP(val1, val2, >)
#else // !NDEBUG
#else // !NDEBUG
// These versions generate no code in optimized mode.
#define DCHECK_EQ(val1, val2) if(false) CHECK_OP(val1, val2, ==)
#define DCHECK_NE(val1, val2) if(false) CHECK_OP(val1, val2, !=)
#define DCHECK_LE(val1, val2) if(false) CHECK_OP(val1, val2, <=)
#define DCHECK_LT(val1, val2) if(false) CHECK_OP(val1, val2, <)
#define DCHECK_GE(val1, val2) if(false) CHECK_OP(val1, val2, >=)
#define DCHECK_GT(val1, val2) if(false) CHECK_OP(val1, val2, >)
#endif // NDEBUG
#define DCHECK_EQ(val1, val2) \
if (false) \
CHECK_OP(val1, val2, ==)
#define DCHECK_NE(val1, val2) \
if (false) \
CHECK_OP(val1, val2, !=)
#define DCHECK_LE(val1, val2) \
if (false) \
CHECK_OP(val1, val2, <=)
#define DCHECK_LT(val1, val2) \
if (false) \
CHECK_OP(val1, val2, <)
#define DCHECK_GE(val1, val2) \
if (false) \
CHECK_OP(val1, val2, >=)
#define DCHECK_GT(val1, val2) \
if (false) \
CHECK_OP(val1, val2, >)
#endif // NDEBUG
// Check that a pointer is not null.
#define CHECK_NOTNULL(val) \
::caffe2::CheckNotNull( \
::c10::CheckNotNull( \
__FILE__, __LINE__, "Check failed: '" #val "' Must be non NULL", (val))
#ifndef NDEBUG
// Debug only version of CHECK_NOTNULL
#define DCHECK_NOTNULL(val) \
::caffe2::CheckNotNull( \
::c10::CheckNotNull( \
__FILE__, __LINE__, "Check failed: '" #val "' Must be non NULL", (val))
#else // !NDEBUG
#else // !NDEBUG
// Optimized version - generates no code.
#define DCHECK_NOTNULL(val) if (false) CHECK_NOTNULL(val)
#endif // NDEBUG
#define DCHECK_NOTNULL(val) \
if (false) \
CHECK_NOTNULL(val)
#endif // NDEBUG
// ---------------------- Support for std objects --------------------------
// These are adapted from glog to support a limited set of logging capability
@ -170,50 +190,51 @@ namespace std {
// Forward declare these two, and define them after all the container streams
// operators so that we can recurse from pair -> container -> container -> pair
// properly.
template<class First, class Second>
std::ostream& operator<<(
std::ostream& out, const std::pair<First, Second>& p);
template <class First, class Second>
std::ostream& operator<<(std::ostream& out, const std::pair<First, Second>& p);
} // namespace std
namespace caffe2 {
namespace c10 {
template <class Iter>
void PrintSequence(std::ostream& ss, Iter begin, Iter end);
} // namespace caffe2
} // namespace c10
namespace std {
#define INSTANTIATE_FOR_CONTAINER(container) \
template <class... Types> \
std::ostream& operator<<( \
std::ostream& out, const container<Types...>& seq) { \
caffe2::PrintSequence(out, seq.begin(), seq.end()); \
return out; \
}
#define INSTANTIATE_FOR_CONTAINER(container) \
template <class... Types> \
std::ostream& operator<<( \
std::ostream& out, const container<Types...>& seq) { \
c10::PrintSequence(out, seq.begin(), seq.end()); \
return out; \
}
INSTANTIATE_FOR_CONTAINER(std::vector)
INSTANTIATE_FOR_CONTAINER(std::map)
INSTANTIATE_FOR_CONTAINER(std::set)
#undef INSTANTIATE_FOR_CONTAINER
template<class First, class Second>
template <class First, class Second>
inline std::ostream& operator<<(
std::ostream& out, const std::pair<First, Second>& p) {
std::ostream& out,
const std::pair<First, Second>& p) {
out << '(' << p.first << ", " << p.second << ')';
return out;
}
} // namespace std
namespace caffe2 {
namespace c10 {
template <class Iter>
inline void PrintSequence(std::ostream& out, Iter begin, Iter end) {
// Output at most 100 elements -- appropriate if used for logging.
for (int i = 0; begin != end && i < 100; ++i, ++begin) {
if (i > 0) out << ' ';
if (i > 0)
out << ' ';
out << *begin;
}
if (begin != end) {
out << " ...";
}
}
} // namespace caffe2
} // namespace c10
#endif // CAFFE2_CORE_LOGGING_IS_NOT_GOOGLE_GLOG_H_
#endif // C10_UTIL_LOGGING_IS_NOT_GOOGLE_GLOG_H_

View File

@ -1,238 +1,3 @@
#ifndef CAFFE2_CORE_LOGGING_H_
#define CAFFE2_CORE_LOGGING_H_
#include <climits>
#include <exception>
#include <functional>
#include <limits>
#include <sstream>
#include <c10/util/Exception.h>
#include "c10/util/StringUtil.h"
#pragma once
#include "c10/util/Logging.h"
#include "caffe2/core/common.h"
#include "caffe2/core/flags.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
C10_DECLARE_int(caffe2_log_level);
C10_DECLARE_bool(caffe2_use_fatal_for_enforce);
namespace caffe2 {
// Functions that we use for initialization.
CAFFE2_API bool InitCaffeLogging(int* argc, char** argv);
CAFFE2_API void UpdateLoggingLevelsFromFlags();
CAFFE2_API CAFFE2_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.
*/
CAFFE2_API void ShowLogInfoToStderr();
CAFFE2_API void SetStackTraceFetcher(std::function<string(void)> fetcher);
using EnforceNotMet = ::c10::Error;
#define CAFFE_ENFORCE(condition, ...) \
do { \
if (!(condition)) { \
::caffe2::ThrowEnforceNotMet( \
__FILE__, __LINE__, #condition, ::c10::str(__VA_ARGS__)); \
} \
} while (false)
#define CAFFE_ENFORCE_WITH_CALLER(condition, ...) \
do { \
if (!(condition)) { \
::caffe2::ThrowEnforceNotMet( \
__FILE__, __LINE__, #condition, ::c10::str(__VA_ARGS__), this); \
} \
} while (false)
#define CAFFE_THROW(...) \
::caffe2::ThrowEnforceNotMet(__FILE__, __LINE__, "", ::c10::str(__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<int64_t>& shape) {
* if (shape.size() == 1) { return EnforceOK(); }
* return c10::str("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 CAFFE2_API EnforceOK {};
class CAFFE2_API 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);
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 = ::c10::str(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 c10::str(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( \
::c10::str(__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( \
::c10::str(__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_

View File

@ -1,49 +0,0 @@
#ifndef CAFFE2_CORE_LOGGING_IS_GOOGLE_GLOG_H_
#define CAFFE2_CORE_LOGGING_IS_GOOGLE_GLOG_H_
#include <iomanip> // because some of the caffe2 code uses e.g. std::setw
// Using google glog. For glog 0.3.2 versions, stl_logging.h needs to be before
// logging.h to actually use stl_logging. Because template magic.
// In addition, we do not do stl logging in .cu files because nvcc does not like
// it. Some mobile platforms do not like stl_logging, so we add an
// overload in that case as well.
#ifdef __CUDACC__
#include <cuda.h>
#endif
#if !defined(__CUDACC__) && !defined(CAFFE2_USE_MINIMAL_GOOGLE_GLOG)
#include <glog/stl_logging.h>
// Old versions of glog don't declare this using declaration, so help
// them out. Fortunately, C++ won't complain if you declare the same
// using declaration multiple times.
namespace std {
using ::operator<<;
}
#else // !defined(__CUDACC__) && !defined(CAFFE2_USE_MINIMAL_GOOGLE_GLOG)
// here, we need to register a fake overload for vector/string - here,
// we just ignore the entries in the logs.
namespace std
{
#define INSTANTIATE_FOR_CONTAINER(container) \
template <class... Types> \
ostream& operator<<(ostream& out, const container<Types...>&) { \
return out; \
}
INSTANTIATE_FOR_CONTAINER(vector)
INSTANTIATE_FOR_CONTAINER(map)
INSTANTIATE_FOR_CONTAINER(set)
#undef INSTANTIATE_FOR_CONTAINER
}
#endif
#include <glog/logging.h>
#endif // CAFFE2_CORE_LOGGING_IS_GOOGLE_GLOG_H_