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