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