Files
wehub-resource-sync 26446540fa
Lint / lint (push) Waiting to run
CI / MacOS (push) Waiting to run
CI / Windows (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

403 lines
13 KiB
C++

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*!
* \file tvm/runtime/logging.h
* \brief logging utilities
*
* We use the following facilities from tvm/ffi/error.h for
* error handling and checking:
*
* - TVM_FFI_THROW(ErrorKind) << "msg";
* - TVM_FFI_CHECK(cond, ErrorKind) << "msg";
* - TVM_FFI_CHECK_EQ(x, y, ErrorKind) << "msg";
* - TVM_FFI_ICHECK(x) << "msg"; // InternalError
* - TVM_FFI_ICHECK_EQ(x, y) << "msg";
* - TVM_FFI_DCHECK(x) << "msg"; // Debug-only InternalError
*
* LOG(INFO), LOG(WARNING), LOG(ERROR) are kept for logging.
* LOG(FATAL) is kept for completeness, it throws InternalError.
*/
#ifndef TVM_RUNTIME_LOGGING_H_
#define TVM_RUNTIME_LOGGING_H_
#include <tvm/ffi/error.h>
#include <tvm/runtime/base.h>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
/*!
* \brief Whether or not customize the logging output.
* If log customize is enabled, the user must implement
* tvm::runtime::detail::LogFatalImpl and tvm::runtime::detail::LogMessageImpl.
*/
#ifndef TVM_LOG_CUSTOMIZE
#define TVM_LOG_CUSTOMIZE 0
#endif
namespace tvm {
namespace runtime {
/*! \brief Internal implementation */
namespace detail {
// Provide support for customized logging.
#if TVM_LOG_CUSTOMIZE
/*!
* \brief Custom implementations of LogFatal.
*
* \sa TVM_LOG_CUSTOMIZE
*/
[[noreturn]] TVM_RUNTIME_DLL void LogFatalImpl(const std::string& file, int lineno,
const std::string& message);
/*!
* \brief Custom implementations of LogMessage.
*
* \sa TVM_LOG_CUSTOMIZE
*/
TVM_RUNTIME_DLL void LogMessageImpl(const std::string& file, int lineno, int level,
const std::string& message);
/*!
* \brief Class to accumulate an error message and throw it. Do not use
* directly, instead use LOG(FATAL).
*/
class LogFatal {
public:
LogFatal(const std::string& file, int lineno) : file_(file), lineno_(lineno) {}
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4722)
#endif
[[noreturn]] ~LogFatal() noexcept(false) { LogFatalImpl(file_, lineno_, stream_.str()); }
#ifdef _MSC_VER
#pragma warning(pop)
#endif
std::ostringstream& stream() { return stream_; }
private:
std::ostringstream stream_;
std::string file_;
int lineno_;
};
/*!
* \brief Class to accumulate an log message. Do not use directly, instead use
* LOG(INFO), LOG(WARNING), LOG(ERROR).
*/
class LogMessage {
public:
LogMessage(const std::string& file, int lineno, int level)
: file_(file), lineno_(lineno), level_(level) {}
~LogMessage() { LogMessageImpl(file_, lineno_, level_, stream_.str()); }
std::ostringstream& stream() { return stream_; }
private:
std::string file_;
int lineno_;
int level_;
std::ostringstream stream_;
};
#else
/*!
* \brief Class to accumulate an error message and throw it. Do not use
* directly, instead use LOG(FATAL).
* \note The `LogFatal` class is designed to be an empty class to reduce stack size usage.
* To play this trick, we use the thread-local storage to store its internal data.
*/
class LogFatal {
public:
TVM_FFI_NO_INLINE LogFatal(const char* file, int lineno) { GetEntry().Init(file, lineno); }
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4722)
#endif
[[noreturn]] ~LogFatal() noexcept(false) {
GetEntry().Finalize();
throw;
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
std::ostringstream& stream() { return GetEntry().stream_; }
private:
struct Entry {
void Init(const char* file, int lineno) {
this->stream_.str("");
this->file_ = file;
this->lineno_ = lineno;
}
[[noreturn]] TVM_FFI_NO_INLINE ffi::Error Finalize() noexcept(false) {
ffi::Error error("InternalError", stream_.str(),
TVMFFIBacktrace(file_.c_str(), lineno_, "", 0));
throw error;
}
std::ostringstream stream_;
std::string file_;
int lineno_;
};
TVM_FFI_NO_INLINE TVM_RUNTIME_DLL static Entry& GetEntry();
};
/*!
* \brief Class to accumulate an log message. Do not use directly, instead use
* LOG(INFO), LOG(WARNING), LOG(ERROR).
*/
class LogMessage {
public:
LogMessage(const std::string& file, int lineno, int level) {
// Use inline constexpr to avoid ODR-issues with hidden static members
// when libtvm_runtime.so is built with -fvisibility=hidden.
static constexpr const char* kLevelStrings[] = {
": Debug: ", // TVM_LOG_LEVEL_DEBUG
": ", // TVM_LOG_LEVEL_INFO
": Warning: ", // TVM_LOG_LEVEL_WARNING
": Error: ", // TVM_LOG_LEVEL_ERROR
};
std::time_t t = std::time(nullptr);
stream_ << "[" << std::put_time(std::localtime(&t), "%H:%M:%S") << "] " << file << ":" << lineno
<< kLevelStrings[level];
}
TVM_FFI_NO_INLINE ~LogMessage() { std::cerr << stream_.str() << std::endl; }
std::ostringstream& stream() { return stream_; }
private:
std::ostringstream stream_;
};
#endif
// This class is used to explicitly ignore values in the conditional
// logging macros. This avoids compiler warnings like "value computed
// is not used" and "statement has no effect".
class LogMessageVoidify {
public:
LogMessageVoidify() {}
// This has to be an operator with a precedence lower than << but
// higher than "?:". See its usage.
void operator&(std::ostream&) {}
};
/*! \brief Captures the state of the \p TVM_LOG_DEBUG environment flag. */
class TVM_RUNTIME_DLL TvmLogDebugSettings {
public:
/*!
* \brief Parses the \p TVM_LOG_DEBUG environment flag as per the specification given by
* \p DebugLoggingEnabled and \p VerboseLoggingEnabled, and caches the result.
*/
inline static const TvmLogDebugSettings& FromFlag() {
// Parse and cache the verbosity level map.
static const auto* settings =
new TvmLogDebugSettings(TvmLogDebugSettings::ParseSpec(std::getenv("TVM_LOG_DEBUG")));
return *settings;
}
/*!
* \brief Parses \p opt_spec as per specification for \p TVM_LOG_DEBUG given by
* \p DebugLoggingEnabled and \p VerboseLoggingEnabled. Throws if specification is ill-formed.
*/
static TvmLogDebugSettings ParseSpec(const char* opt_spec);
/*!
* \brief Implements \p VerboseLoggingEnabled below w.r.t. the already parsed \p TVM_LOG_DEBUG
* environment variable.
*/
inline bool VerboseEnabled(const char* opt_filename, int level) const {
if (opt_filename == nullptr || level < 0 || vlog_level_map_.empty()) {
return false;
}
return VerboseEnabledImpl(opt_filename, level);
}
/*! \brief Returns true if \p DLOG statements should be executed. */
bool dlog_enabled() const { return dlog_enabled_; }
private:
// Slow path for VerboseEnabled.
bool VerboseEnabledImpl(const std::string& filename, int level) const;
/*! \brief If true, DLOG statements are enabled. */
bool dlog_enabled_ = false;
/*!
* \brief A map from canonicalized filenames to the maximum VLOG verbosity level for that file.
* May also contain the 'wildcard' entry \p "DEFAULT" representing the level for all other files.
*/
std::unordered_map<std::string, int> vlog_level_map_;
};
/*!
* \brief Returns true if a DLOG statement is enabled by the \p TVM_LOG_DEBUG environment
* variable. Requires:
* \code
* TVM_LOG_DEBUG=1
* \endcode
* or a valid setting as described by \p VerboseLoggingEnabled below.
*/
inline bool DebugLoggingEnabled() {
static int state = 0;
if (state == 0) {
state = TvmLogDebugSettings::FromFlag().dlog_enabled() ? 1 : -1;
}
return state == 1;
}
/*!
* \brief Returns true if a VLOG statement in \p filename is enabled by the \p TVM_LOG_DEBUG
* environment variable for logging at verbosity \p level. Levels should be non-negative.
*
* Filenames are canonicalized to be w.r.t. the src/ dir of the TVM tree. (VLOG's should not
* appear under include/).
*
* To enable file \p ir/bar.cc for level 0 only set:
* \code
* TVM_LOG_DEBUG="ir/bar.cc=0"
* \endcode
*
* To enable all files up to level 3 but disable \p ir/bar.cc set:
* \code
* TVM_LOG_DEBUG="DEFAULT=2,ir/bar.cc=-1"
* \endcode
*
* Any of these settings will also enable DLOG statements.
*/
inline bool VerboseLoggingEnabled(const char* opt_filename, int level) {
return TvmLogDebugSettings::FromFlag().VerboseEnabled(opt_filename, level);
}
/*!
* \brief A stack of VLOG context messages.
*
* For use by \p VLOG_CONTEXT macro only.
*/
class VLogContext {
public:
void Push(std::stringstream* stream) { context_stack_.push_back(stream); }
void Pop() {
if (!context_stack_.empty()) {
context_stack_.pop_back();
}
}
std::string str() const;
private:
std::vector<std::stringstream*> context_stack_;
};
/*! \brief Get thread local \p VLogContext for tracking a stack of VLOG context messages. */
inline VLogContext* ThreadLocalVLogContext() {
static thread_local VLogContext inst;
return &inst;
}
/*!
* \brief A RAII class to push/pos a VLOG context message onto the thread-local stack.
*
* For use by \p VLOG_CONTEXT macro only.
*/
class VLogContextEntry {
public:
VLogContextEntry() { ThreadLocalVLogContext()->Push(&sstream_); }
~VLogContextEntry() { ThreadLocalVLogContext()->Pop(); }
std::ostream& stream() { return sstream_; }
private:
std::stringstream sstream_;
};
} // namespace detail
#define TVM_LOG_LEVEL_DEBUG 0
#define TVM_LOG_LEVEL_INFO 1
#define TVM_LOG_LEVEL_WARNING 2
#define TVM_LOG_LEVEL_ERROR 3
#define TVM_LOG_LEVEL_FATAL 4
#define LOG(level) LOG_##level
#define LOG_DEBUG \
::tvm::runtime::detail::LogMessage(__FILE__, __LINE__, TVM_LOG_LEVEL_DEBUG).stream()
#define LOG_FATAL ::tvm::runtime::detail::LogFatal(__FILE__, __LINE__).stream()
#define LOG_INFO ::tvm::runtime::detail::LogMessage(__FILE__, __LINE__, TVM_LOG_LEVEL_INFO).stream()
#define LOG_ERROR \
::tvm::runtime::detail::LogMessage(__FILE__, __LINE__, TVM_LOG_LEVEL_ERROR).stream()
#define LOG_WARNING \
::tvm::runtime::detail::LogMessage(__FILE__, __LINE__, TVM_LOG_LEVEL_WARNING).stream()
#define LOG_IF(severity, condition) \
!(condition) ? (void)0 : ::tvm::runtime::detail::LogMessageVoidify() & LOG(severity)
#if TVM_LOG_DEBUG
#define LOG_DFATAL LOG_FATAL
#define DFATAL FATAL
#define DLOG(severity) LOG_IF(severity, ::tvm::runtime::detail::DebugLoggingEnabled())
#define DLOG_IF(severity, condition) \
LOG_IF(severity, ::tvm::runtime::detail::DebugLoggingEnabled() && (condition))
/*!
* \brief If the \p TVM_LOG_DEBUG build flag is enabled, push a context message onto an internal
* stack. All VLOG messages will include this stack in their prefix to help with debugging. E.g.:
* \code
* VLOG_CONTEXT << "my context";
* VLOG(1) << "my log message";
* \endcode
* Thread safe. No-op with no execution overhead if the \p TVM_LOG_DEBUG build flag is not enabled.
*/
#define VLOG_CONTEXT \
::tvm::runtime::detail::VLogContextEntry vlog_entry_; \
vlog_entry_.stream()
#else
#define LOG_DFATAL LOG_ERROR
#define DFATAL ERROR
#define DLOG(severity) true ? (void)0 : ::tvm::runtime::detail::LogMessageVoidify() & LOG(severity)
#define DLOG_IF(severity, condition) \
(true || !(condition)) ? (void)0 : ::tvm::runtime::detail::LogMessageVoidify() & LOG(severity)
#define VLOG_CONTEXT true ? (void)0 : ::tvm::runtime::detail::LogMessageVoidify() & LOG(INFO)
#endif
/*!
* \brief If the \p TVM_LOG_DEBUG build flag is enabled, and the containing file has been enabled
* at \p level or greater in the \p TVM_LOG_DEBUG environment variable, then log a message at
* \p INFO severity.
*
* See \p VerboseLoggingEnabled for the format of the \p TVM_LOG_DEBUG environment variable.
* Thread safe. No-op with no execution overhead if the \p TVM_LOG_DEBUG build flag is not enabled.
* No-op with some execution overhead if the \p TVM_LOG_DEBUG build flag is enabled but the
* containing file is not enabled.
*/
#define VLOG(level) \
DLOG_IF(INFO, ::tvm::runtime::detail::VerboseLoggingEnabled(__FILE__, (level))) \
<< ::tvm::runtime::detail::ThreadLocalVLogContext()->str()
} // namespace runtime
} // namespace tvm
#endif // TVM_RUNTIME_LOGGING_H_