1 /*!
2  *  Copyright (c) 2015 by Contributors
3  * \file logging.h
4  * \brief defines logging macros of dmlc
5  *  allows use of GLOG, fall back to internal
6  *  implementation when disabled
7  */
8 #ifndef DMLC_LOGGING_H_
9 #define DMLC_LOGGING_H_
10 #include <cstdio>
11 #include <cstdlib>
12 #include <string>
13 #include <vector>
14 #include <stdexcept>
15 #include <memory>
16 #include "./base.h"
17 
18 #if DMLC_LOG_STACK_TRACE
19 #include <cxxabi.h>
20 #include <sstream>
21 #include DMLC_EXECINFO_H
22 #endif
23 
24 namespace dmlc {
25 /*!
26  * \brief exception class that will be thrown by
27  *  default logger if DMLC_LOG_FATAL_THROW == 1
28  */
29 struct Error : public std::runtime_error {
30   /*!
31    * \brief constructor
32    * \param s the error message
33    */
ErrorError34   explicit Error(const std::string &s) : std::runtime_error(s) {}
35 };
36 
37 #if DMLC_LOG_STACK_TRACE
Demangle(char const * msg_str)38 inline std::string Demangle(char const *msg_str) {
39   using std::string;
40   string msg(msg_str);
41   size_t symbol_start = string::npos;
42   size_t symbol_end = string::npos;
43   if ( ((symbol_start = msg.find("_Z")) != string::npos)
44        && (symbol_end = msg.find_first_of(" +", symbol_start)) ) {
45     string left_of_symbol(msg, 0, symbol_start);
46     string symbol(msg, symbol_start, symbol_end - symbol_start);
47     string right_of_symbol(msg, symbol_end);
48 
49     int status = 0;
50     size_t length = string::npos;
51     std::unique_ptr<char, void (*)(void *__ptr)> demangled_symbol =
52         {abi::__cxa_demangle(symbol.c_str(), 0, &length, &status), &std::free};
53     if (demangled_symbol && status == 0 && length > 0) {
54       string symbol_str(demangled_symbol.get());
55       std::ostringstream os;
56       os << left_of_symbol << symbol_str << right_of_symbol;
57       return os.str();
58     }
59   }
60   return string(msg_str);
61 }
62 
63 // By default skip the first frame because
64 // that belongs to ~LogMessageFatal
65 inline std::string StackTrace(
66     size_t start_frame = 1,
67     const size_t stack_size = DMLC_LOG_STACK_TRACE_SIZE) {
68   using std::string;
69   std::ostringstream stacktrace_os;
70   std::vector<void*> stack(stack_size);
71   int nframes = backtrace(stack.data(), static_cast<int>(stack_size));
72   stacktrace_os << "Stack trace:\n";
73   char **msgs = backtrace_symbols(stack.data(), nframes);
74   if (msgs != nullptr) {
75     for (int frameno = start_frame; frameno < nframes; ++frameno) {
76       string msg = dmlc::Demangle(msgs[frameno]);
77       stacktrace_os << "  [bt] (" << frameno - start_frame << ") " << msg << "\n";
78     }
79   }
80   free(msgs);
81   string stack_trace = stacktrace_os.str();
82   return stack_trace;
83 }
84 
85 #else  // DMLC_LOG_STACK_TRACE is off
86 
demangle(char const * msg_str)87 inline std::string demangle(char const* msg_str) {
88   return std::string();
89 }
90 
91 inline std::string StackTrace(size_t start_frame = 1,
92                               const size_t stack_size = 0) {
93   return std::string("Stack trace not available when "
94   "DMLC_LOG_STACK_TRACE is disabled at compile time.");
95 }
96 
97 #endif  // DMLC_LOG_STACK_TRACE
98 }  // namespace dmlc
99 
100 #if DMLC_USE_GLOG
101 #include <glog/logging.h>
102 
103 namespace dmlc {
104 /*!
105  * \brief optionally redirect to google's init log
106  * \param argv0 The arguments.
107  */
InitLogging(const char * argv0)108 inline void InitLogging(const char* argv0) {
109   google::InitGoogleLogging(argv0);
110 }
111 }  // namespace dmlc
112 
113 #else
114 // use a light version of glog
115 #include <assert.h>
116 #include <iostream>
117 #include <sstream>
118 #include <ctime>
119 
120 #if defined(_MSC_VER)
121 #pragma warning(disable : 4722)
122 #pragma warning(disable : 4068)
123 #endif
124 
125 namespace dmlc {
InitLogging(const char *)126 inline void InitLogging(const char*) {
127   // DO NOTHING
128 }
129 
130 // get debug option from env variable.
DebugLoggingEnabled()131 inline bool DebugLoggingEnabled() {
132   static int state = 0;
133   if (state == 0) {
134     if (auto var = std::getenv("DMLC_LOG_DEBUG")) {
135       if (std::string(var) == "1") {
136         state = 1;
137       } else {
138         state = -1;
139       }
140     } else {
141       // by default hide debug logging.
142       state = -1;
143     }
144   }
145   return state == 1;
146 }
147 
148 class LogCheckError {
149  public:
LogCheckError()150   LogCheckError() : str(nullptr) {}
LogCheckError(const std::string & str_)151   explicit LogCheckError(const std::string& str_) : str(new std::string(str_)) {}
152   LogCheckError(const LogCheckError& other) = delete;
LogCheckError(LogCheckError && other)153   LogCheckError(LogCheckError&& other) : str(other.str) {
154     other.str = nullptr;
155   }
~LogCheckError()156   ~LogCheckError() { if (str != nullptr) delete str; }
157   operator bool() const { return str != nullptr; }
158   LogCheckError& operator=(const LogCheckError& other) = delete;
159   LogCheckError& operator=(LogCheckError&& other) = delete;
160   std::string* str;
161 };
162 
163 #ifndef DMLC_GLOG_DEFINED
164 
165 #ifndef _LIBCPP_SGX_NO_IOSTREAMS
166 #define DEFINE_CHECK_FUNC(name, op)                               \
167   template <typename X, typename Y>                               \
168   inline LogCheckError LogCheck##name(const X& x, const Y& y) {   \
169     if (x op y) return LogCheckError();                           \
170     std::ostringstream os;                                        \
171     os << " (" << x << " vs. " << y << ") ";  /* CHECK_XX(x, y) requires x and y can be serialized to string. Use CHECK(x OP y) otherwise. NOLINT(*) */ \
172     return LogCheckError(os.str());                               \
173   }                                                               \
174   inline LogCheckError LogCheck##name(int x, int y) {             \
175     return LogCheck##name<int, int>(x, y);                        \
176   }
177 #else
178 #define DEFINE_CHECK_FUNC(name, op)                               \
179   template <typename X, typename Y>                               \
180   inline LogCheckError LogCheck##name(const X& x, const Y& y) {   \
181     if (x op y) return LogCheckError();                           \
182     return LogCheckError("Error.");                               \
183   }                                                               \
184   inline LogCheckError LogCheck##name(int x, int y) {             \
185     return LogCheck##name<int, int>(x, y);                        \
186   }
187 #endif
188 
189 #define CHECK_BINARY_OP(name, op, x, y)                               \
190   if (dmlc::LogCheckError _check_err = dmlc::LogCheck##name(x, y))    \
191     dmlc::LogMessageFatal(__FILE__, __LINE__).stream()                \
192       << "Check failed: " << #x " " #op " " #y << *(_check_err.str) << ": "
193 
194 #pragma GCC diagnostic push
195 #pragma GCC diagnostic ignored "-Wsign-compare"
196 DEFINE_CHECK_FUNC(_LT, <)
197 DEFINE_CHECK_FUNC(_GT, >)
198 DEFINE_CHECK_FUNC(_LE, <=)
199 DEFINE_CHECK_FUNC(_GE, >=)
200 DEFINE_CHECK_FUNC(_EQ, ==)
201 DEFINE_CHECK_FUNC(_NE, !=)
202 #pragma GCC diagnostic pop
203 
204 // Always-on checking
205 #define CHECK(x)                                           \
206   if (!(x))                                                \
207     dmlc::LogMessageFatal(__FILE__, __LINE__).stream()     \
208       << "Check failed: " #x << ": "
209 #define CHECK_LT(x, y) CHECK_BINARY_OP(_LT, <, x, y)
210 #define CHECK_GT(x, y) CHECK_BINARY_OP(_GT, >, x, y)
211 #define CHECK_LE(x, y) CHECK_BINARY_OP(_LE, <=, x, y)
212 #define CHECK_GE(x, y) CHECK_BINARY_OP(_GE, >=, x, y)
213 #define CHECK_EQ(x, y) CHECK_BINARY_OP(_EQ, ==, x, y)
214 #define CHECK_NE(x, y) CHECK_BINARY_OP(_NE, !=, x, y)
215 #define CHECK_NOTNULL(x) \
216   ((x) == NULL ? dmlc::LogMessageFatal(__FILE__, __LINE__).stream() << "Check  notnull: "  #x << ' ', (x) : (x)) // NOLINT(*)
217 
218 // Debug-only checking.
219 #if DMLC_LOG_DEBUG
220 #define DCHECK(x) \
221   while (false) CHECK(x)
222 #define DCHECK_LT(x, y) \
223   while (false) CHECK((x) < (y))
224 #define DCHECK_GT(x, y) \
225   while (false) CHECK((x) > (y))
226 #define DCHECK_LE(x, y) \
227   while (false) CHECK((x) <= (y))
228 #define DCHECK_GE(x, y) \
229   while (false) CHECK((x) >= (y))
230 #define DCHECK_EQ(x, y) \
231   while (false) CHECK((x) == (y))
232 #define DCHECK_NE(x, y) \
233   while (false) CHECK((x) != (y))
234 #else
235 #define DCHECK(x) CHECK(x)
236 #define DCHECK_LT(x, y) CHECK((x) < (y))
237 #define DCHECK_GT(x, y) CHECK((x) > (y))
238 #define DCHECK_LE(x, y) CHECK((x) <= (y))
239 #define DCHECK_GE(x, y) CHECK((x) >= (y))
240 #define DCHECK_EQ(x, y) CHECK((x) == (y))
241 #define DCHECK_NE(x, y) CHECK((x) != (y))
242 #endif  // DMLC_LOG_DEBUG
243 
244 #if DMLC_LOG_CUSTOMIZE
245 #define LOG_INFO dmlc::CustomLogMessage(__FILE__, __LINE__)
246 #else
247 #define LOG_INFO dmlc::LogMessage(__FILE__, __LINE__)
248 #endif
249 #define LOG_ERROR LOG_INFO
250 #define LOG_WARNING LOG_INFO
251 #define LOG_FATAL dmlc::LogMessageFatal(__FILE__, __LINE__)
252 #define LOG_QFATAL LOG_FATAL
253 
254 // Poor man version of VLOG
255 #define VLOG(x) LOG_INFO.stream()
256 
257 #define LOG(severity) LOG_##severity.stream()
258 #define LG LOG_INFO.stream()
259 #define LOG_IF(severity, condition) \
260   !(condition) ? (void)0 : dmlc::LogMessageVoidify() & LOG(severity)
261 
262 #if DMLC_LOG_DEBUG
263 
264 #define LOG_DFATAL LOG_FATAL
265 #define DFATAL FATAL
266 #define DLOG(severity) LOG_IF(severity, ::dmlc::DebugLoggingEnabled())
267 #define DLOG_IF(severity, condition) LOG_IF(severity, ::dmlc::DebugLoggingEnabled() && (condition))
268 
269 #else
270 
271 #define LOG_DFATAL LOG_ERROR
272 #define DFATAL ERROR
273 #define DLOG(severity) true ? (void)0 : dmlc::LogMessageVoidify() & LOG(severity)
274 #define DLOG_IF(severity, condition) \
275   (true || !(condition)) ? (void)0 : dmlc::LogMessageVoidify() & LOG(severity)
276 #endif
277 
278 // Poor man version of LOG_EVERY_N
279 #define LOG_EVERY_N(severity, n) LOG(severity)
280 
281 #endif  // DMLC_GLOG_DEFINED
282 
283 class DateLogger {
284  public:
DateLogger()285   DateLogger() {
286 #if defined(_MSC_VER)
287     _tzset();
288 #endif
289   }
HumanDate()290   const char* HumanDate() {
291 #ifndef _LIBCPP_SGX_CONFIG
292 #if defined(_MSC_VER)
293     _strtime_s(buffer_, sizeof(buffer_));
294 #else
295     time_t time_value = time(NULL);
296     struct tm *pnow;
297 #if !defined(_WIN32)
298     struct tm now;
299     pnow = localtime_r(&time_value, &now);
300 #else
301     pnow = localtime(&time_value);  // NOLINT(*)
302 #endif
303     snprintf(buffer_, sizeof(buffer_), "%02d:%02d:%02d",
304              pnow->tm_hour, pnow->tm_min, pnow->tm_sec);
305 #endif
306 #endif  // _LIBCPP_SGX_CONFIG
307     return buffer_;
308   }
309 
310  private:
311   char buffer_[9];
312 };
313 
314 #ifndef _LIBCPP_SGX_NO_IOSTREAMS
315 class LogMessage {
316  public:
LogMessage(const char * file,int line)317   LogMessage(const char* file, int line)
318       :
319 #ifdef __ANDROID__
320         log_stream_(std::cout)
321 #else
322         log_stream_(std::cerr)
323 #endif
324   {
325     log_stream_ << "[" << pretty_date_.HumanDate() << "] " << file << ":"
326                 << line << ": ";
327   }
~LogMessage()328   ~LogMessage() { log_stream_ << '\n'; }
stream()329   std::ostream& stream() { return log_stream_; }
330 
331  protected:
332   std::ostream& log_stream_;
333 
334  private:
335   DateLogger pretty_date_;
336   LogMessage(const LogMessage&);
337   void operator=(const LogMessage&);
338 };
339 
340 // customized logger that can allow user to define where to log the message.
341 class CustomLogMessage {
342  public:
CustomLogMessage(const char * file,int line)343   CustomLogMessage(const char* file, int line) {
344     log_stream_ << "[" << DateLogger().HumanDate() << "] " << file << ":"
345                 << line << ": ";
346   }
~CustomLogMessage()347   ~CustomLogMessage() {
348     Log(log_stream_.str());
349   }
stream()350   std::ostream& stream() { return log_stream_; }
351   /*!
352    * \brief customized logging of the message.
353    * This function won't be implemented by libdmlc
354    * \param msg The message to be logged.
355    */
356   static void Log(const std::string& msg);
357 
358  private:
359   std::ostringstream log_stream_;
360 };
361 #else
362 class DummyOStream {
363  public:
364   template <typename T>
365   DummyOStream& operator<<(T _) { return *this; }
str()366   inline std::string str() { return ""; }
367 };
368 class LogMessage {
369  public:
LogMessage(const char * file,int line)370   LogMessage(const char* file, int line) : log_stream_() {}
stream()371   DummyOStream& stream() { return log_stream_; }
372 
373  protected:
374   DummyOStream log_stream_;
375 
376  private:
377   LogMessage(const LogMessage&);
378   void operator=(const LogMessage&);
379 };
380 #endif
381 
382 
383 #if defined(_LIBCPP_SGX_NO_IOSTREAMS)
384 class LogMessageFatal : public LogMessage {
385  public:
LogMessageFatal(const char * file,int line)386   LogMessageFatal(const char* file, int line) : LogMessage(file, line) {}
~LogMessageFatal()387   ~LogMessageFatal() {
388     abort();
389   }
390  private:
391   LogMessageFatal(const LogMessageFatal&);
392   void operator=(const LogMessageFatal&);
393 };
394 #elif DMLC_LOG_FATAL_THROW == 0
395 class LogMessageFatal : public LogMessage {
396  public:
LogMessageFatal(const char * file,int line)397   LogMessageFatal(const char* file, int line) : LogMessage(file, line) {}
~LogMessageFatal()398   ~LogMessageFatal() {
399     log_stream_ << "\n" << StackTrace() << "\n";
400     abort();
401   }
402 
403  private:
404   LogMessageFatal(const LogMessageFatal&);
405   void operator=(const LogMessageFatal&);
406 };
407 #else
408 class LogMessageFatal {
409  public:
LogMessageFatal(const char * file,int line)410   LogMessageFatal(const char* file, int line) {
411     log_stream_ << "[" << pretty_date_.HumanDate() << "] " << file << ":"
412                 << line << ": ";
413   }
stream()414   std::ostringstream &stream() { return log_stream_; }
~LogMessageFatal()415   ~LogMessageFatal() DMLC_THROW_EXCEPTION {
416 #if DMLC_LOG_STACK_TRACE
417     log_stream_ << "\n" << StackTrace() << "\n";
418 #endif
419 
420     // throwing out of destructor is evil
421     // hopefully we can do it here
422     // also log the message before throw
423 #if DMLC_LOG_BEFORE_THROW
424     LOG(ERROR) << log_stream_.str();
425 #endif
426     throw Error(log_stream_.str());
427   }
428 
429  private:
430   std::ostringstream log_stream_;
431   DateLogger pretty_date_;
432   LogMessageFatal(const LogMessageFatal&);
433   void operator=(const LogMessageFatal&);
434 };
435 #endif
436 
437 // This class is used to explicitly ignore values in the conditional
438 // logging macros.  This avoids compiler warnings like "value computed
439 // is not used" and "statement has no effect".
440 class LogMessageVoidify {
441  public:
LogMessageVoidify()442   LogMessageVoidify() {}
443   // This has to be an operator with a precedence lower than << but
444   // higher than "?:". See its usage.
445 #if !defined(_LIBCPP_SGX_NO_IOSTREAMS)
446   void operator&(std::ostream&) {}
447 #endif
448 };
449 
450 }  // namespace dmlc
451 
452 #endif
453 #endif  // DMLC_LOGGING_H_
454