Spaces:
Runtime error
Runtime error
/* | |
* Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. | |
* | |
* Licensed 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. | |
*/ | |
using Severity = nvinfer1::ILogger::Severity; | |
class LogStreamConsumerBuffer : public std::stringbuf | |
{ | |
public: | |
LogStreamConsumerBuffer(std::ostream& stream, const std::string& prefix, bool shouldLog) | |
: mOutput(stream) | |
, mPrefix(prefix) | |
, mShouldLog(shouldLog) | |
{ | |
} | |
LogStreamConsumerBuffer(LogStreamConsumerBuffer&& other) | |
: mOutput(other.mOutput) | |
{ | |
} | |
~LogStreamConsumerBuffer() | |
{ | |
// std::streambuf::pbase() gives a pointer to the beginning of the buffered part of the output sequence | |
// std::streambuf::pptr() gives a pointer to the current position of the output sequence | |
// if the pointer to the beginning is not equal to the pointer to the current position, | |
// call putOutput() to log the output to the stream | |
if (pbase() != pptr()) | |
{ | |
putOutput(); | |
} | |
} | |
// synchronizes the stream buffer and returns 0 on success | |
// synchronizing the stream buffer consists of inserting the buffer contents into the stream, | |
// resetting the buffer and flushing the stream | |
virtual int sync() | |
{ | |
putOutput(); | |
return 0; | |
} | |
void putOutput() | |
{ | |
if (mShouldLog) | |
{ | |
// prepend timestamp | |
std::time_t timestamp = std::time(nullptr); | |
tm* tm_local = std::localtime(×tamp); | |
std::cout << "["; | |
std::cout << std::setw(2) << std::setfill('0') << 1 + tm_local->tm_mon << "/"; | |
std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_mday << "/"; | |
std::cout << std::setw(4) << std::setfill('0') << 1900 + tm_local->tm_year << "-"; | |
std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_hour << ":"; | |
std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_min << ":"; | |
std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_sec << "] "; | |
// std::stringbuf::str() gets the string contents of the buffer | |
// insert the buffer contents pre-appended by the appropriate prefix into the stream | |
mOutput << mPrefix << str(); | |
// set the buffer to empty | |
str(""); | |
// flush the stream | |
mOutput.flush(); | |
} | |
} | |
void setShouldLog(bool shouldLog) | |
{ | |
mShouldLog = shouldLog; | |
} | |
private: | |
std::ostream& mOutput; | |
std::string mPrefix; | |
bool mShouldLog; | |
}; | |
//! | |
//! \class LogStreamConsumerBase | |
//! \brief Convenience object used to initialize LogStreamConsumerBuffer before std::ostream in LogStreamConsumer | |
//! | |
class LogStreamConsumerBase | |
{ | |
public: | |
LogStreamConsumerBase(std::ostream& stream, const std::string& prefix, bool shouldLog) | |
: mBuffer(stream, prefix, shouldLog) | |
{ | |
} | |
protected: | |
LogStreamConsumerBuffer mBuffer; | |
}; | |
//! | |
//! \class LogStreamConsumer | |
//! \brief Convenience object used to facilitate use of C++ stream syntax when logging messages. | |
//! Order of base classes is LogStreamConsumerBase and then std::ostream. | |
//! This is because the LogStreamConsumerBase class is used to initialize the LogStreamConsumerBuffer member field | |
//! in LogStreamConsumer and then the address of the buffer is passed to std::ostream. | |
//! This is necessary to prevent the address of an uninitialized buffer from being passed to std::ostream. | |
//! Please do not change the order of the parent classes. | |
//! | |
class LogStreamConsumer : protected LogStreamConsumerBase, public std::ostream | |
{ | |
public: | |
//! \brief Creates a LogStreamConsumer which logs messages with level severity. | |
//! Reportable severity determines if the messages are severe enough to be logged. | |
LogStreamConsumer(Severity reportableSeverity, Severity severity) | |
: LogStreamConsumerBase(severityOstream(severity), severityPrefix(severity), severity <= reportableSeverity) | |
, std::ostream(&mBuffer) // links the stream buffer with the stream | |
, mShouldLog(severity <= reportableSeverity) | |
, mSeverity(severity) | |
{ | |
} | |
LogStreamConsumer(LogStreamConsumer&& other) | |
: LogStreamConsumerBase(severityOstream(other.mSeverity), severityPrefix(other.mSeverity), other.mShouldLog) | |
, std::ostream(&mBuffer) // links the stream buffer with the stream | |
, mShouldLog(other.mShouldLog) | |
, mSeverity(other.mSeverity) | |
{ | |
} | |
void setReportableSeverity(Severity reportableSeverity) | |
{ | |
mShouldLog = mSeverity <= reportableSeverity; | |
mBuffer.setShouldLog(mShouldLog); | |
} | |
private: | |
static std::ostream& severityOstream(Severity severity) | |
{ | |
return severity >= Severity::kINFO ? std::cout : std::cerr; | |
} | |
static std::string severityPrefix(Severity severity) | |
{ | |
switch (severity) | |
{ | |
case Severity::kINTERNAL_ERROR: return "[F] "; | |
case Severity::kERROR: return "[E] "; | |
case Severity::kWARNING: return "[W] "; | |
case Severity::kINFO: return "[I] "; | |
case Severity::kVERBOSE: return "[V] "; | |
default: assert(0); return ""; | |
} | |
} | |
bool mShouldLog; | |
Severity mSeverity; | |
}; | |
//! \class Logger | |
//! | |
//! \brief Class which manages logging of TensorRT tools and samples | |
//! | |
//! \details This class provides a common interface for TensorRT tools and samples to log information to the console, | |
//! and supports logging two types of messages: | |
//! | |
//! - Debugging messages with an associated severity (info, warning, error, or internal error/fatal) | |
//! - Test pass/fail messages | |
//! | |
//! The advantage of having all samples use this class for logging as opposed to emitting directly to stdout/stderr is | |
//! that the logic for controlling the verbosity and formatting of sample output is centralized in one location. | |
//! | |
//! In the future, this class could be extended to support dumping test results to a file in some standard format | |
//! (for example, JUnit XML), and providing additional metadata (e.g. timing the duration of a test run). | |
//! | |
//! TODO: For backwards compatibility with existing samples, this class inherits directly from the nvinfer1::ILogger | |
//! interface, which is problematic since there isn't a clean separation between messages coming from the TensorRT | |
//! library and messages coming from the sample. | |
//! | |
//! In the future (once all samples are updated to use Logger::getTRTLogger() to access the ILogger) we can refactor the | |
//! class to eliminate the inheritance and instead make the nvinfer1::ILogger implementation a member of the Logger | |
//! object. | |
class Logger : public nvinfer1::ILogger | |
{ | |
public: | |
Logger(Severity severity = Severity::kWARNING) | |
: mReportableSeverity(severity) | |
{ | |
} | |
//! | |
//! \enum TestResult | |
//! \brief Represents the state of a given test | |
//! | |
enum class TestResult | |
{ | |
kRUNNING, //!< The test is running | |
kPASSED, //!< The test passed | |
kFAILED, //!< The test failed | |
kWAIVED //!< The test was waived | |
}; | |
//! | |
//! \brief Forward-compatible method for retrieving the nvinfer::ILogger associated with this Logger | |
//! \return The nvinfer1::ILogger associated with this Logger | |
//! | |
//! TODO Once all samples are updated to use this method to register the logger with TensorRT, | |
//! we can eliminate the inheritance of Logger from ILogger | |
//! | |
nvinfer1::ILogger& getTRTLogger() | |
{ | |
return *this; | |
} | |
//! | |
//! \brief Implementation of the nvinfer1::ILogger::log() virtual method | |
//! | |
//! Note samples should not be calling this function directly; it will eventually go away once we eliminate the | |
//! inheritance from nvinfer1::ILogger | |
//! | |
void log(Severity severity, const char* msg) override | |
{ | |
LogStreamConsumer(mReportableSeverity, severity) << "[TRT] " << std::string(msg) << std::endl; | |
} | |
//! | |
//! \brief Method for controlling the verbosity of logging output | |
//! | |
//! \param severity The logger will only emit messages that have severity of this level or higher. | |
//! | |
void setReportableSeverity(Severity severity) | |
{ | |
mReportableSeverity = severity; | |
} | |
//! | |
//! \brief Opaque handle that holds logging information for a particular test | |
//! | |
//! This object is an opaque handle to information used by the Logger to print test results. | |
//! The sample must call Logger::defineTest() in order to obtain a TestAtom that can be used | |
//! with Logger::reportTest{Start,End}(). | |
//! | |
class TestAtom | |
{ | |
public: | |
TestAtom(TestAtom&&) = default; | |
private: | |
friend class Logger; | |
TestAtom(bool started, const std::string& name, const std::string& cmdline) | |
: mStarted(started) | |
, mName(name) | |
, mCmdline(cmdline) | |
{ | |
} | |
bool mStarted; | |
std::string mName; | |
std::string mCmdline; | |
}; | |
//! | |
//! \brief Define a test for logging | |
//! | |
//! \param[in] name The name of the test. This should be a string starting with | |
//! "TensorRT" and containing dot-separated strings containing | |
//! the characters [A-Za-z0-9_]. | |
//! For example, "TensorRT.sample_googlenet" | |
//! \param[in] cmdline The command line used to reproduce the test | |
// | |
//! \return a TestAtom that can be used in Logger::reportTest{Start,End}(). | |
//! | |
static TestAtom defineTest(const std::string& name, const std::string& cmdline) | |
{ | |
return TestAtom(false, name, cmdline); | |
} | |
//! | |
//! \brief A convenience overloaded version of defineTest() that accepts an array of command-line arguments | |
//! as input | |
//! | |
//! \param[in] name The name of the test | |
//! \param[in] argc The number of command-line arguments | |
//! \param[in] argv The array of command-line arguments (given as C strings) | |
//! | |
//! \return a TestAtom that can be used in Logger::reportTest{Start,End}(). | |
static TestAtom defineTest(const std::string& name, int argc, char const* const* argv) | |
{ | |
auto cmdline = genCmdlineString(argc, argv); | |
return defineTest(name, cmdline); | |
} | |
//! | |
//! \brief Report that a test has started. | |
//! | |
//! \pre reportTestStart() has not been called yet for the given testAtom | |
//! | |
//! \param[in] testAtom The handle to the test that has started | |
//! | |
static void reportTestStart(TestAtom& testAtom) | |
{ | |
reportTestResult(testAtom, TestResult::kRUNNING); | |
assert(!testAtom.mStarted); | |
testAtom.mStarted = true; | |
} | |
//! | |
//! \brief Report that a test has ended. | |
//! | |
//! \pre reportTestStart() has been called for the given testAtom | |
//! | |
//! \param[in] testAtom The handle to the test that has ended | |
//! \param[in] result The result of the test. Should be one of TestResult::kPASSED, | |
//! TestResult::kFAILED, TestResult::kWAIVED | |
//! | |
static void reportTestEnd(const TestAtom& testAtom, TestResult result) | |
{ | |
assert(result != TestResult::kRUNNING); | |
assert(testAtom.mStarted); | |
reportTestResult(testAtom, result); | |
} | |
static int reportPass(const TestAtom& testAtom) | |
{ | |
reportTestEnd(testAtom, TestResult::kPASSED); | |
return EXIT_SUCCESS; | |
} | |
static int reportFail(const TestAtom& testAtom) | |
{ | |
reportTestEnd(testAtom, TestResult::kFAILED); | |
return EXIT_FAILURE; | |
} | |
static int reportWaive(const TestAtom& testAtom) | |
{ | |
reportTestEnd(testAtom, TestResult::kWAIVED); | |
return EXIT_SUCCESS; | |
} | |
static int reportTest(const TestAtom& testAtom, bool pass) | |
{ | |
return pass ? reportPass(testAtom) : reportFail(testAtom); | |
} | |
Severity getReportableSeverity() const | |
{ | |
return mReportableSeverity; | |
} | |
private: | |
//! | |
//! \brief returns an appropriate string for prefixing a log message with the given severity | |
//! | |
static const char* severityPrefix(Severity severity) | |
{ | |
switch (severity) | |
{ | |
case Severity::kINTERNAL_ERROR: return "[F] "; | |
case Severity::kERROR: return "[E] "; | |
case Severity::kWARNING: return "[W] "; | |
case Severity::kINFO: return "[I] "; | |
case Severity::kVERBOSE: return "[V] "; | |
default: assert(0); return ""; | |
} | |
} | |
//! | |
//! \brief returns an appropriate string for prefixing a test result message with the given result | |
//! | |
static const char* testResultString(TestResult result) | |
{ | |
switch (result) | |
{ | |
case TestResult::kRUNNING: return "RUNNING"; | |
case TestResult::kPASSED: return "PASSED"; | |
case TestResult::kFAILED: return "FAILED"; | |
case TestResult::kWAIVED: return "WAIVED"; | |
default: assert(0); return ""; | |
} | |
} | |
//! | |
//! \brief returns an appropriate output stream (cout or cerr) to use with the given severity | |
//! | |
static std::ostream& severityOstream(Severity severity) | |
{ | |
return severity >= Severity::kINFO ? std::cout : std::cerr; | |
} | |
//! | |
//! \brief method that implements logging test results | |
//! | |
static void reportTestResult(const TestAtom& testAtom, TestResult result) | |
{ | |
severityOstream(Severity::kINFO) << "&&&& " << testResultString(result) << " " << testAtom.mName << " # " | |
<< testAtom.mCmdline << std::endl; | |
} | |
//! | |
//! \brief generate a command line string from the given (argc, argv) values | |
//! | |
static std::string genCmdlineString(int argc, char const* const* argv) | |
{ | |
std::stringstream ss; | |
for (int i = 0; i < argc; i++) | |
{ | |
if (i > 0) | |
ss << " "; | |
ss << argv[i]; | |
} | |
return ss.str(); | |
} | |
Severity mReportableSeverity; | |
}; | |
namespace | |
{ | |
//! | |
//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kVERBOSE | |
//! | |
//! Example usage: | |
//! | |
//! LOG_VERBOSE(logger) << "hello world" << std::endl; | |
//! | |
inline LogStreamConsumer LOG_VERBOSE(const Logger& logger) | |
{ | |
return LogStreamConsumer(logger.getReportableSeverity(), Severity::kVERBOSE); | |
} | |
//! | |
//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kINFO | |
//! | |
//! Example usage: | |
//! | |
//! LOG_INFO(logger) << "hello world" << std::endl; | |
//! | |
inline LogStreamConsumer LOG_INFO(const Logger& logger) | |
{ | |
return LogStreamConsumer(logger.getReportableSeverity(), Severity::kINFO); | |
} | |
//! | |
//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kWARNING | |
//! | |
//! Example usage: | |
//! | |
//! LOG_WARN(logger) << "hello world" << std::endl; | |
//! | |
inline LogStreamConsumer LOG_WARN(const Logger& logger) | |
{ | |
return LogStreamConsumer(logger.getReportableSeverity(), Severity::kWARNING); | |
} | |
//! | |
//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kERROR | |
//! | |
//! Example usage: | |
//! | |
//! LOG_ERROR(logger) << "hello world" << std::endl; | |
//! | |
inline LogStreamConsumer LOG_ERROR(const Logger& logger) | |
{ | |
return LogStreamConsumer(logger.getReportableSeverity(), Severity::kERROR); | |
} | |
//! | |
//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kINTERNAL_ERROR | |
// ("fatal" severity) | |
//! | |
//! Example usage: | |
//! | |
//! LOG_FATAL(logger) << "hello world" << std::endl; | |
//! | |
inline LogStreamConsumer LOG_FATAL(const Logger& logger) | |
{ | |
return LogStreamConsumer(logger.getReportableSeverity(), Severity::kINTERNAL_ERROR); | |
} | |
} // anonymous namespace | |