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 | |
186 | namespace tvm { |
187 | namespace 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 | */ |
193 | TVM_DLL std::string Backtrace(); |
194 | |
195 | /*! \brief Base error type for TVM. Wraps a string message. */ |
196 | class 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 | */ |
214 | class 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 | */ |
227 | class 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 */ |
281 | namespace 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 | */ |
297 | TVM_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 | */ |
304 | class 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 | */ |
327 | class 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 | */ |
349 | class 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 | */ |
384 | class 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". |
405 | class 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. */ |
414 | class 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 |
469 | inline 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 | */ |
496 | inline 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 | */ |
505 | class 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. */ |
521 | using 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 | */ |
528 | class 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 | |
538 | constexpr 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 | |
545 | template <typename X, typename Y> |
546 | std::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" |
568 | TVM_CHECK_FUNC(_LT, <) |
569 | TVM_CHECK_FUNC(_GT, >) |
570 | TVM_CHECK_FUNC(_LE, <=) |
571 | TVM_CHECK_FUNC(_GE, >=) |
572 | TVM_CHECK_FUNC(_EQ, ==) |
573 | TVM_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 |
716 | using runtime::Error; |
717 | using runtime::InternalError; |
718 | |
719 | } // namespace tvm |
720 | #endif // TVM_RUNTIME_LOGGING_H_ |
721 | |