1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20/*!
21 * \file tvm/runtime/logging.h
22 * \brief logging utilities
23 *
24 * We define our own CHECK and LOG macros to replace those from dmlc-core.
25 * These macros are then injected into dmlc-core via the
26 * DMLC_USE_LOGGING_LIBRARY define. dmlc-core will #include this file wherever
27 * it needs logging.
28 */
29#ifndef TVM_RUNTIME_LOGGING_H_
30#define TVM_RUNTIME_LOGGING_H_
31
32#include <dmlc/common.h>
33#include <dmlc/thread_local.h>
34#include <tvm/runtime/c_runtime_api.h>
35
36#include <ctime>
37#include <iomanip>
38#include <iostream>
39#include <memory>
40#include <sstream>
41#include <string>
42#include <unordered_map>
43#include <vector>
44
45/*!
46 * \brief Macro helper to force a function not to be inlined.
47 * It is only used in places that we know not inlining is good,
48 * e.g. some logging functions.
49 */
50#if defined(_MSC_VER)
51#define TVM_NO_INLINE __declspec(noinline)
52#else
53#define TVM_NO_INLINE __attribute__((noinline))
54#endif
55
56/*!
57 * \brief Macro helper to force a function to be inlined.
58 * It is only used in places that we know inline is important,
59 * e.g. some template expansion cases.
60 */
61#ifdef _MSC_VER
62#define TVM_ALWAYS_INLINE __forceinline
63#else
64#define TVM_ALWAYS_INLINE inline __attribute__((always_inline))
65#endif
66
67/*!
68 * \brief Macro helper for exception throwing.
69 */
70#define TVM_THROW_EXCEPTION noexcept(false)
71
72/*!
73 * \brief Whether or not enable backtrace logging during a
74 * fatal error.
75 *
76 * \note TVM won't depend on LIBBACKTRACE or other exec_info
77 * library when this option is disabled.
78 */
79#ifndef TVM_LOG_STACK_TRACE
80#define TVM_LOG_STACK_TRACE 1
81#endif
82
83/*!
84 * \brief Whether or not use libbacktrace library
85 * for getting backtrace information
86 */
87#ifndef TVM_USE_LIBBACKTRACE
88#define TVM_USE_LIBBACKTRACE 0
89#endif
90
91/*!
92 * \brief Whether or not customize the logging output.
93 * If log customize is enabled, the user must implement
94 * tvm::runtime::detail::LogFatalImpl and tvm::runtime::detail::LogMessageImpl.
95 */
96#ifndef TVM_LOG_CUSTOMIZE
97#define TVM_LOG_CUSTOMIZE 0
98#endif
99
100// a technique that enables overriding macro names on the number of parameters. This is used
101// to define other macros below
102#define GET_MACRO(_1, _2, _3, _4, _5, NAME, ...) NAME
103
104/*!
105 * \brief COND_X calls COND_X_N where N is the number of parameters passed to COND_X
106 * X can be any of CHECK_GE, CHECK_EQ, CHECK, or LOG COND_X (but not COND_X_N)
107 * are supposed to be used outside this file.
108 * The first parameter of COND_X (and therefore, COND_X_N), which we call 'quit_on_assert',
109 * is a boolean. The rest of the parameters of COND_X is the same as the parameters of X.
110 * quit_on_assert determines the overall behavior of COND_X. If it's true COND_X
111 * quits the program on assertion failure. If it's false, then it moves on and somehow reports
112 * the assertion failure back to the macro caller in an appropriate manner (e.g, 'return false'
113 * in a function, or 'continue' or 'break' in a loop)
114 * The default behavior when quit_on_assertion is false, is to 'return false'. If this is not
115 * desirable, the macro caller can pass one more last parameter to COND_X to tell COND_X what
116 * to do when when quit_on_assertion is false and the assertion fails.
117 *
118 * Rationale: These macros were designed to implement functions that have two behaviors
119 * in a concise way. Those behaviors are quitting on assertion failures, or trying to
120 * move on from assertion failures. Note that these macros hide lots of control flow in them,
121 * and therefore, makes the logic of the whole code slightly harder to understand. However,
122 * in pieces of code that use these macros frequently, it will significantly shorten the
123 * amount of code needed to be read, and we won't need to clutter the main logic of the
124 * function by repetitive control flow structure. The first problem
125 * mentioned will be improved over time as the developer gets used to the macro.
126 *
127 * Here is an example of how to use it
128 * \code
129 * bool f(..., bool quit_on_assertion) {
130 * int a = 0, b = 0;
131 * ...
132 * a = ...
133 * b = ...
134 * // if quit_on_assertion is true, if a==b, continue, otherwise quit.
135 * // if quit_on_assertion is false, if a==b, continue, otherwise 'return false'
136 * // (default behaviour)
137 * COND_CHECK_EQ(quit_on_assertion, a, b) << "some error message when quiting"
138 * ...
139 * for (int i = 0; i < N; i++) {
140 * a = ...
141 * b = ...
142 * // if quit_on_assertion is true, if a==b, continue, otherwise quit.
143 * // if quit_on_assertion is false, if a==b, continue, otherwise 'break'
144 * // (non-default behaviour, therefore, has to be explicitly specified)
145 * COND_CHECK_EQ(quit_on_assertion, a, b, break) << "some error message when quiting"
146 * }
147 * }
148 * \endcode
149 */
150#define COND_CHECK_GE(...) \
151 GET_MACRO(__VA_ARGS__, COND_CHECK_GE_5, COND_CHECK_GE_4, COND_CHECK_GE_3)(__VA_ARGS__)
152#define COND_CHECK_EQ(...) \
153 GET_MACRO(__VA_ARGS__, COND_CHECK_EQ_5, COND_CHECK_EQ_4, COND_CHECK_EQ_3)(__VA_ARGS__)
154#define COND_CHECK(...) \
155 GET_MACRO(__VA_ARGS__, COND_CHECK_5, COND_CHECK_4, COND_CHECK_3, COND_CHECK_2)(__VA_ARGS__)
156#define COND_LOG(...) \
157 GET_MACRO(__VA_ARGS__, COND_LOG_5, COND_LOG_4, COND_LOG_3, COND_LOG_2)(__VA_ARGS__)
158
159// Not supposed to be used by users directly.
160#define COND_CHECK_OP(quit_on_assert, x, y, what, op) \
161 if (!quit_on_assert) { \
162 if (!((x)op(y))) what; \
163 } else /* NOLINT(*) */ \
164 CHECK_##op(x, y)
165
166#define COND_CHECK_EQ_4(quit_on_assert, x, y, what) COND_CHECK_OP(quit_on_assert, x, y, what, ==)
167#define COND_CHECK_GE_4(quit_on_assert, x, y, what) COND_CHECK_OP(quit_on_assert, x, y, what, >=)
168
169#define COND_CHECK_3(quit_on_assert, x, what) \
170 if (!quit_on_assert) { \
171 if (!(x)) what; \
172 } else /* NOLINT(*) */ \
173 CHECK(x)
174
175#define COND_LOG_3(quit_on_assert, x, what) \
176 if (!quit_on_assert) { \
177 what; \
178 } else /* NOLINT(*) */ \
179 LOG(x)
180
181#define COND_CHECK_EQ_3(quit_on_assert, x, y) COND_CHECK_EQ_4(quit_on_assert, x, y, return false)
182#define COND_CHECK_GE_3(quit_on_assert, x, y) COND_CHECK_GE_4(quit_on_assert, x, y, return false)
183#define COND_CHECK_2(quit_on_assert, x) COND_CHECK_3(quit_on_assert, x, return false)
184#define COND_LOG_2(quit_on_assert, x) COND_LOG_3(quit_on_assert, x, return false)
185
186namespace tvm {
187namespace runtime {
188
189/*!
190 * \brief Generate a backtrace when called.
191 * \return A multiline string of the backtrace. There will be either one or two lines per frame.
192 */
193TVM_DLL std::string Backtrace();
194
195/*! \brief Base error type for TVM. Wraps a string message. */
196class Error : public ::dmlc::Error { // for backwards compatibility
197 public:
198 /*!
199 * \brief Construct an error.
200 * \param s The message to be displayed with the error.
201 */
202 explicit Error(const std::string& s) : ::dmlc::Error(s) {}
203};
204
205/*!
206 * \brief Error message already set in frontend env.
207 *
208 * This error can be thrown by EnvCheckSignals to indicate
209 * that there is an error set in the frontend environment(e.g.
210 * python interpreter). The TVM FFI should catch this error
211 * and return a proper code tell the frontend caller about
212 * this fact.
213 */
214class EnvErrorAlreadySet : public ::dmlc::Error {
215 public:
216 /*!
217 * \brief Construct an error.
218 * \param s The message to be displayed with the error.
219 */
220 explicit EnvErrorAlreadySet(const std::string& s) : ::dmlc::Error(s) {}
221};
222
223/*!
224 * \brief Error type for errors from CHECK, ICHECK, and LOG(FATAL). This error
225 * contains a backtrace of where it occurred.
226 */
227class InternalError : public Error {
228 public:
229 /*! \brief Construct an error. Not recommended to use directly. Instead use LOG(FATAL).
230 *
231 * \param file The file where the error occurred.
232 * \param lineno The line number where the error occurred.
233 * \param message The error message to display.
234 * \param time The time at which the error occurred. This should be in local time.
235 * \param backtrace Backtrace from when the error occurred.
236 */
237 InternalError(std::string file, int lineno, std::string message,
238 std::time_t time = std::time(nullptr), std::string backtrace = Backtrace())
239 : Error(""),
240 file_(file),
241 lineno_(lineno),
242 message_(message),
243 time_(time),
244 backtrace_(backtrace) {
245 std::ostringstream s;
246 // XXX: Do not change this format, otherwise all error handling in python will break (because it
247 // parses the message to reconstruct the error type).
248 // TODO(tkonolige): Convert errors to Objects, so we can avoid the mess of formatting/parsing
249 // error messages correctly.
250 s << "[" << std::put_time(std::localtime(&time), "%H:%M:%S") << "] " << file << ":" << lineno
251 << ": " << message << std::endl;
252 if (backtrace.size() > 0) {
253 s << backtrace << std::endl;
254 }
255 full_message_ = s.str();
256 }
257 /*! \return The file in which the error occurred. */
258 const std::string& file() const { return file_; }
259 /*! \return The message associated with this error. */
260 const std::string& message() const { return message_; }
261 /*! \return Formatted error message including file, linenumber, backtrace, and message. */
262 const std::string& full_message() const { return full_message_; }
263 /*! \return The backtrace from where this error occurred. */
264 const std::string& backtrace() const { return backtrace_; }
265 /*! \return The time at which this error occurred. */
266 const std::time_t& time() const { return time_; }
267 /*! \return The line number at which this error occurred. */
268 int lineno() const { return lineno_; }
269 virtual const char* what() const noexcept { return full_message_.c_str(); }
270
271 private:
272 std::string file_;
273 int lineno_;
274 std::string message_;
275 std::time_t time_;
276 std::string backtrace_;
277 std::string full_message_; // holds the full error string
278};
279
280/*! \brief Internal implementation */
281namespace detail {
282// Provide support for customized logging.
283#if TVM_LOG_CUSTOMIZE
284/*!
285 * \brief Custom implementations of LogFatal.
286 *
287 * \sa TVM_LOG_CUSTOMIZE
288 */
289[[noreturn]] TVM_DLL void LogFatalImpl(const std::string& file, int lineno,
290 const std::string& message);
291
292/*!
293 * \brief Custom implementations of LogMessage.
294 *
295 * \sa TVM_LOG_CUSTOMIZE
296 */
297TVM_DLL void LogMessageImpl(const std::string& file, int lineno, int level,
298 const std::string& message);
299
300/*!
301 * \brief Class to accumulate an error message and throw it. Do not use
302 * directly, instead use LOG(FATAL).
303 */
304class LogFatal {
305 public:
306 LogFatal(const std::string& file, int lineno) : file_(file), lineno_(lineno) {}
307#ifdef _MSC_VER
308#pragma disagnostic push
309#pragma warning(disable : 4722)
310#endif
311 [[noreturn]] ~LogFatal() TVM_THROW_EXCEPTION { LogFatalImpl(file_, lineno_, stream_.str()); }
312#ifdef _MSC_VER
313#pragma disagnostic pop
314#endif
315 std::ostringstream& stream() { return stream_; }
316
317 private:
318 std::ostringstream stream_;
319 std::string file_;
320 int lineno_;
321};
322
323/*!
324 * \brief Class to accumulate an log message. Do not use directly, instead use
325 * LOG(INFO), LOG(WARNING), LOG(ERROR).
326 */
327class LogMessage {
328 public:
329 LogMessage(const std::string& file, int lineno, int level)
330 : file_(file), lineno_(lineno), level_(level) {}
331 ~LogMessage() { LogMessageImpl(file_, lineno_, level_, stream_.str()); }
332 std::ostringstream& stream() { return stream_; }
333
334 private:
335 std::string file_;
336 int lineno_;
337 int level_;
338 std::ostringstream stream_;
339};
340
341#else
342
343/*!
344 * \brief Class to accumulate an error message and throw it. Do not use
345 * directly, instead use LOG(FATAL).
346 * \note The `LogFatal` class is designed to be an empty class to reduce stack size usage.
347 * To play this trick, we use the thread-local storage to store its internal data.
348 */
349class LogFatal {
350 public:
351 TVM_NO_INLINE LogFatal(const char* file, int lineno) { GetEntry().Init(file, lineno); }
352#ifdef _MSC_VER
353#pragma disagnostic push
354#pragma warning(disable : 4722)
355#endif
356 [[noreturn]] ~LogFatal() TVM_THROW_EXCEPTION { GetEntry().Finalize(); }
357#ifdef _MSC_VER
358#pragma disagnostic pop
359#endif
360 std::ostringstream& stream() { return GetEntry().stream_; }
361
362 private:
363 struct Entry {
364 void Init(const char* file, int lineno) {
365 this->stream_.str("");
366 this->file_ = file;
367 this->lineno_ = lineno;
368 }
369 [[noreturn]] TVM_NO_INLINE dmlc::Error Finalize() {
370 throw InternalError(file_, lineno_, stream_.str());
371 }
372 std::ostringstream stream_;
373 std::string file_;
374 int lineno_;
375 };
376
377 TVM_DLL TVM_NO_INLINE static Entry& GetEntry();
378};
379
380/*!
381 * \brief Class to accumulate an log message. Do not use directly, instead use
382 * LOG(INFO), LOG(WARNING), LOG(ERROR).
383 */
384class LogMessage {
385 public:
386 LogMessage(const std::string& file, int lineno, int level) {
387 std::time_t t = std::time(nullptr);
388 stream_ << "[" << std::put_time(std::localtime(&t), "%H:%M:%S") << "] " << file << ":" << lineno
389 << level_strings_[level];
390 }
391 TVM_NO_INLINE ~LogMessage() { std::cerr << stream_.str() << std::endl; }
392 std::ostringstream& stream() { return stream_; }
393
394 private:
395 std::ostringstream stream_;
396 TVM_DLL static const char* level_strings_[];
397};
398
399#endif
400
401// Below is from dmlc-core
402// This class is used to explicitly ignore values in the conditional
403// logging macros. This avoids compiler warnings like "value computed
404// is not used" and "statement has no effect".
405class LogMessageVoidify {
406 public:
407 LogMessageVoidify() {}
408 // This has to be an operator with a precedence lower than << but
409 // higher than "?:". See its usage.
410 void operator&(std::ostream&) {}
411};
412
413/*! \brief Captures the state of the \p TVM_LOG_DEBUG environment flag. */
414class TvmLogDebugSettings {
415 public:
416 /*!
417 * \brief Parses the \p TVM_LOG_DEBUG environment flag as per the specification given by
418 * \p DebugLoggingEnabled and \p VerboseLoggingEnabled, and caches the result.
419 */
420 inline static const TvmLogDebugSettings& FromFlag() {
421 // Parse and cache the verbosity level map.
422 static const auto* settings =
423 new TvmLogDebugSettings(TvmLogDebugSettings::ParseSpec(std::getenv("TVM_LOG_DEBUG")));
424 return *settings;
425 }
426
427 /*!
428 * \brief Parses \p opt_spec as per specification for \p TVM_LOG_DEBUG given by
429 * \p DebugLoggingEnabled and \p VerboseLoggingEnabled. Throws if specification is ill-formed.
430 */
431 static TvmLogDebugSettings ParseSpec(const char* opt_spec);
432
433 /*!
434 * \brief Implements \p VerboseLoggingEnabled below w.r.t. the already parsed \p TVM_LOG_DEBUG
435 * environment variable.
436 */
437 inline bool VerboseEnabled(const char* opt_filename, int level) const {
438 if (opt_filename == nullptr || level < 0 || vlog_level_map_.empty()) {
439 return false;
440 }
441 return VerboseEnabledImpl(opt_filename, level);
442 }
443
444 /*! \brief Returns true if \p DLOG statements should be executed. */
445 bool dlog_enabled() const { return dlog_enabled_; }
446
447 private:
448 // Slow path for VerboseEnabled.
449 bool VerboseEnabledImpl(const std::string& filename, int level) const;
450
451 /*! \brief If true, DLOG statements are enabled. */
452 bool dlog_enabled_ = false;
453 /*!
454 * \brief A map from canonicalized filenames to the maximum VLOG verbosity level for that file.
455 * May also contain the 'wildcard' entry \p "DEFAULT" representing the level for all other files.
456 */
457 std::unordered_map<std::string, int> vlog_level_map_;
458};
459
460/*!
461 * \brief Returns true if a DLOG statement is enabled by the \p TVM_LOG_DEBUG environment
462 * variable. Requires:
463 * \code
464 * TVM_LOG_DEBUG=1
465 * \endcode
466 * or a valid setting as described by \p VerboseLoggingEnabled below.
467 */
468// Also from dmlc-core
469inline bool DebugLoggingEnabled() {
470 static int state = 0;
471 if (state == 0) {
472 state = TvmLogDebugSettings::FromFlag().dlog_enabled() ? 1 : -1;
473 }
474 return state == 1;
475}
476
477/*!
478 * \brief Returns true if a VLOG statement in \p filename is enabled by the \p TVM_LOG_DEBUG
479 * environment variable for logging at verbosity \p level. Levels should be non-negative.
480 *
481 * Filenames are canonicalized to be w.r.t. the src/ dir of the TVM tree. (VLOG's should not
482 * appear under include/).
483 *
484 * To enable file \p relay/foo.cc up to level 2 and \p ir/bar.cc for level 0 only set:
485 * \code
486 * TVM_LOG_DEBUG="relay/foo.cc=2,ir/bar.cc=0"
487 * \endcode
488 *
489 * To enable all files up to level 3 but disable \p ir/bar.cc set:
490 * \code
491 * TVM_LOG_DEBUG="DEFAULT=2,ir/bar.cc=-1"
492 * \endcode
493 *
494 * Any of these settings will also enable DLOG statements.
495 */
496inline bool VerboseLoggingEnabled(const char* opt_filename, int level) {
497 return TvmLogDebugSettings::FromFlag().VerboseEnabled(opt_filename, level);
498}
499
500/*!
501 * \brief A stack of VLOG context messages.
502 *
503 * For use by \p VLOG_CONTEXT macro only.
504 */
505class VLogContext {
506 public:
507 void Push(std::stringstream* stream) { context_stack_.push_back(stream); }
508 void Pop() {
509 if (!context_stack_.empty()) {
510 context_stack_.pop_back();
511 }
512 }
513
514 std::string str() const;
515
516 private:
517 std::vector<std::stringstream*> context_stack_;
518};
519
520/*! \brief Thread local \p VLogContext for tracking a stack of VLOG context messages. */
521using ThreadLocalVLogContext = dmlc::ThreadLocalStore<VLogContext>;
522
523/*!
524 * \brief A RAII class to push/pos a VLOG context message onto the thread-local stack.
525 *
526 * For use by \p VLOG_CONTEXT macro only.
527 */
528class VLogContextEntry {
529 public:
530 VLogContextEntry() { ThreadLocalVLogContext::Get()->Push(&sstream_); }
531 ~VLogContextEntry() { ThreadLocalVLogContext::Get()->Pop(); }
532 std::ostream& stream() { return sstream_; }
533
534 private:
535 std::stringstream sstream_;
536};
537
538constexpr const char* kTVM_INTERNAL_ERROR_MESSAGE =
539 "\n"
540 "---------------------------------------------------------------\n"
541 "An error occurred during the execution of TVM.\n"
542 "For more information, please see: https://tvm.apache.org/docs/errors.html\n"
543 "---------------------------------------------------------------\n";
544
545template <typename X, typename Y>
546std::unique_ptr<std::string> LogCheckFormat(const X& x, const Y& y) {
547 std::ostringstream os;
548 os << " (" << x << " vs. " << y << ") "; // CHECK_XX(x, y) requires x and y can be serialized to
549 // string. Use CHECK(x OP y) otherwise.
550 return std::make_unique<std::string>(os.str());
551}
552
553// Inline _Pragma in macros does not work reliably on old version of MSVC and
554// GCC. We wrap all comparisons in a function so that we can use #pragma to
555// silence bad comparison warnings.
556#define TVM_CHECK_FUNC(name, op) \
557 template <typename X, typename Y> \
558 TVM_ALWAYS_INLINE std::unique_ptr<std::string> LogCheck##name(const X& x, const Y& y) { \
559 if (x op y) return nullptr; \
560 return LogCheckFormat(x, y); \
561 } \
562 TVM_ALWAYS_INLINE std::unique_ptr<std::string> LogCheck##name(int x, int y) { \
563 return LogCheck##name<int, int>(x, y); \
564 }
565
566#pragma GCC diagnostic push
567#pragma GCC diagnostic ignored "-Wsign-compare"
568TVM_CHECK_FUNC(_LT, <)
569TVM_CHECK_FUNC(_GT, >)
570TVM_CHECK_FUNC(_LE, <=)
571TVM_CHECK_FUNC(_GE, >=)
572TVM_CHECK_FUNC(_EQ, ==)
573TVM_CHECK_FUNC(_NE, !=)
574#pragma GCC diagnostic pop
575
576} // namespace detail
577
578#define TVM_LOG_LEVEL_DEBUG 0
579#define TVM_LOG_LEVEL_INFO 1
580#define TVM_LOG_LEVEL_WARNING 2
581#define TVM_LOG_LEVEL_ERROR 3
582#define TVM_LOG_LEVEL_FATAL 4
583#define LOG(level) LOG_##level
584#define LOG_DEBUG \
585 ::tvm::runtime::detail::LogMessage(__FILE__, __LINE__, TVM_LOG_LEVEL_DEBUG).stream()
586#define LOG_FATAL ::tvm::runtime::detail::LogFatal(__FILE__, __LINE__).stream()
587#define LOG_INFO ::tvm::runtime::detail::LogMessage(__FILE__, __LINE__, TVM_LOG_LEVEL_INFO).stream()
588#define LOG_ERROR \
589 ::tvm::runtime::detail::LogMessage(__FILE__, __LINE__, TVM_LOG_LEVEL_ERROR).stream()
590#define LOG_WARNING \
591 ::tvm::runtime::detail::LogMessage(__FILE__, __LINE__, TVM_LOG_LEVEL_WARNING).stream()
592
593#define TVM_CHECK_BINARY_OP(name, op, x, y) \
594 if (auto __tvm__log__err = ::tvm::runtime::detail::LogCheck##name(x, y)) \
595 ::tvm::runtime::detail::LogFatal(__FILE__, __LINE__).stream() \
596 << "Check failed: " << #x " " #op " " #y << *__tvm__log__err << ": "
597
598#define CHECK(x) \
599 if (!(x)) \
600 ::tvm::runtime::detail::LogFatal(__FILE__, __LINE__).stream() \
601 << "Check failed: (" #x << ") is false: "
602
603#define CHECK_LT(x, y) TVM_CHECK_BINARY_OP(_LT, <, x, y)
604#define CHECK_GT(x, y) TVM_CHECK_BINARY_OP(_GT, >, x, y)
605#define CHECK_LE(x, y) TVM_CHECK_BINARY_OP(_LE, <=, x, y)
606#define CHECK_GE(x, y) TVM_CHECK_BINARY_OP(_GE, >=, x, y)
607#define CHECK_EQ(x, y) TVM_CHECK_BINARY_OP(_EQ, ==, x, y)
608#define CHECK_NE(x, y) TVM_CHECK_BINARY_OP(_NE, !=, x, y)
609#define CHECK_NOTNULL(x) \
610 ((x) == nullptr ? ::tvm::runtime::detail::LogFatal(__FILE__, __LINE__).stream() \
611 << "Check not null: " #x << ' ', \
612 (x) : (x)) // NOLINT(*)
613
614#define LOG_IF(severity, condition) \
615 !(condition) ? (void)0 : ::tvm::runtime::detail::LogMessageVoidify() & LOG(severity)
616
617#if TVM_LOG_DEBUG
618
619#define LOG_DFATAL LOG_FATAL
620#define DFATAL FATAL
621#define DLOG(severity) LOG_IF(severity, ::tvm::runtime::detail::DebugLoggingEnabled())
622#define DLOG_IF(severity, condition) \
623 LOG_IF(severity, ::tvm::runtime::detail::DebugLoggingEnabled() && (condition))
624
625/*!
626 * \brief If the \p TVM_LOG_DEBUG build flag is enabled, push a context message onto an internal
627 * stack. All VLOG messages will include this stack in their prefix to help with debugging. E.g.:
628 * \code
629 * VLOG_CONTEXT << "my context";
630 * VLOG(1) << "my log message";
631 * \endcode
632 * Thread safe. No-op with no execution overhead if the \p TVM_LOG_DEBUG build flag is not enabled.
633 */
634#define VLOG_CONTEXT \
635 ::tvm::runtime::detail::VLogContextEntry vlog_entry_; \
636 vlog_entry_.stream()
637
638#else
639
640#define LOG_DFATAL LOG_ERROR
641#define DFATAL ERROR
642#define DLOG(severity) true ? (void)0 : ::tvm::runtime::detail::LogMessageVoidify() & LOG(severity)
643#define DLOG_IF(severity, condition) \
644 (true || !(condition)) ? (void)0 : ::tvm::runtime::detail::LogMessageVoidify() & LOG(severity)
645#define VLOG_CONTEXT true ? (void)0 : ::tvm::runtime::detail::LogMessageVoidify() & LOG(INFO)
646
647#endif
648
649/*!
650 * \brief If the \p TVM_LOG_DEBUG build flag is enabled, and the containing file has been enabled
651 * at \p level or greater in the \p TVM_LOG_DEBUG environment variable, then log a message at
652 * \p INFO severity.
653 *
654 * See \p VerboseLoggingEnabled for the format of the \p TVM_LOG_DEBUG environment variable.
655 * Thread safe. No-op with no execution overhead if the \p TVM_LOG_DEBUG build flag is not enabled.
656 * No-op with some execution overhead if the \p TVM_LOG_DEBUG build flag is enabled but the
657 * containing file is not enabled.
658 */
659#define VLOG(level) \
660 DLOG_IF(INFO, ::tvm::runtime::detail::VerboseLoggingEnabled(__FILE__, (level))) \
661 << ::tvm::runtime::detail::ThreadLocalVLogContext::Get()->str()
662
663#if TVM_LOG_DEBUG
664#define DCHECK(x) CHECK(x)
665#define DCHECK_LT(x, y) CHECK((x) < (y))
666#define DCHECK_GT(x, y) CHECK((x) > (y))
667#define DCHECK_LE(x, y) CHECK((x) <= (y))
668#define DCHECK_GE(x, y) CHECK((x) >= (y))
669#define DCHECK_EQ(x, y) CHECK((x) == (y))
670#define DCHECK_NE(x, y) CHECK((x) != (y))
671#else
672#define DCHECK(x) \
673 while (false) CHECK(x)
674#define DCHECK_LT(x, y) \
675 while (false) CHECK((x) < (y))
676#define DCHECK_GT(x, y) \
677 while (false) CHECK((x) > (y))
678#define DCHECK_LE(x, y) \
679 while (false) CHECK((x) <= (y))
680#define DCHECK_GE(x, y) \
681 while (false) CHECK((x) >= (y))
682#define DCHECK_EQ(x, y) \
683 while (false) CHECK((x) == (y))
684#define DCHECK_NE(x, y) \
685 while (false) CHECK((x) != (y))
686#endif
687
688#define TVM_ICHECK_INDENT " "
689
690#define ICHECK_BINARY_OP(name, op, x, y) \
691 if (auto __tvm__log__err = ::tvm::runtime::detail::LogCheck##name(x, y)) \
692 ::tvm::runtime::detail::LogFatal(__FILE__, __LINE__).stream() \
693 << ::tvm::runtime::detail::kTVM_INTERNAL_ERROR_MESSAGE << std::endl \
694 << TVM_ICHECK_INDENT << "Check failed: " << #x " " #op " " #y << *__tvm__log__err << ": "
695
696#define ICHECK(x) \
697 if (!(x)) \
698 ::tvm::runtime::detail::LogFatal(__FILE__, __LINE__).stream() \
699 << ::tvm::runtime::detail::kTVM_INTERNAL_ERROR_MESSAGE << TVM_ICHECK_INDENT \
700 << "Check failed: (" #x << ") is false: "
701
702#define ICHECK_LT(x, y) ICHECK_BINARY_OP(_LT, <, x, y)
703#define ICHECK_GT(x, y) ICHECK_BINARY_OP(_GT, >, x, y)
704#define ICHECK_LE(x, y) ICHECK_BINARY_OP(_LE, <=, x, y)
705#define ICHECK_GE(x, y) ICHECK_BINARY_OP(_GE, >=, x, y)
706#define ICHECK_EQ(x, y) ICHECK_BINARY_OP(_EQ, ==, x, y)
707#define ICHECK_NE(x, y) ICHECK_BINARY_OP(_NE, !=, x, y)
708#define ICHECK_NOTNULL(x) \
709 ((x) == nullptr ? ::tvm::runtime::detail::LogFatal(__FILE__, __LINE__).stream() \
710 << ::tvm::runtime::detail::kTVM_INTERNAL_ERROR_MESSAGE \
711 << TVM_ICHECK_INDENT << "Check not null: " #x << ' ', \
712 (x) : (x)) // NOLINT(*)
713
714} // namespace runtime
715// Re-export error types
716using runtime::Error;
717using runtime::InternalError;
718
719} // namespace tvm
720#endif // TVM_RUNTIME_LOGGING_H_
721