Files
nvidia--tensorrt/samples/common/safeErrorRecorder.h
T
wehub-resource-sync c8a779b1bb
Docker Image CI / build-ubuntu2004 (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:55 +08:00

295 lines
9.1 KiB
C++

/*
* SPDX-FileCopyrightText: Copyright (c) 1993-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#ifndef SAFE_ERROR_RECORDER_H
#define SAFE_ERROR_RECORDER_H
#include "NvInferSafeRecorder.h"
#include "safeCommon.h"
#include <algorithm>
#include <array>
#include <atomic>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <exception>
#include <iostream>
#include <memory>
#include <mutex>
#include <string_view>
#if ENABLE_NVLOG
#include <nvos_s3_tegra_log.h>
#endif // ENABLE_NVLOG
namespace sample
{
using namespace nvinfer2::safe;
namespace detail
{
//! Copy the contents of a std::string_view into a buffer, truncating if it doesn't fit, and always null-terminating the
//! result (unless dst == nullptr or dstSize == 0). Returns the number of bytes written, including the null terminator.
//! Behavior is undefined if dstSize < 0.
//! Behavior is undefined if dst == nullpltr but dstSize > 0.
inline int64_t truncatingCopyAsCString(std::string_view const src, AsciiChar* const dst, int64_t const dstSize)
{
SAFE_ASSERT(0 <= dstSize);
SAFE_ASSERT(dst != nullptr || dstSize == 0);
if (dstSize == 0)
{
return 0;
}
SAFE_ASSERT(0 < dstSize); //< We at least have room for a null terminator.
auto const toWrite = std::min(static_cast<int64_t>(src.size()), dstSize - 1);
std::copy_n(src.data(), toWrite, dst);
dst[toWrite] = '\0';
return toWrite + 1;
}
//! Copy the contents of a std::string_view into a fixed-size buffer, null terminated (unless the buffer has zero size)
//! and return it.
template <typename TArray>
[[nodiscard]] constexpr TArray truncatedCopyAsCString(std::string_view const src)
{
TArray result{};
// Expecting TArray to be a std::array or similar, constructing to its size and having a static size:
SAFE_ASSERT(std::tuple_size<TArray>::value == result.size());
truncatingCopyAsCString(src, result.data(), result.size());
return result;
}
#if ENABLE_NVLOG
//! \return a severity number corresponding to an `nvinfer2::safe::Severity`.
//! Unlisted enumerators map to `NVOS_LOG_SEVERITY_INFO`
[[nodiscard]] constexpr uint8_t toNvOsLogSeverity(nvinfer2::safe::Severity sev)
{
switch (sev)
{
case nvinfer2::safe::Severity::kINFO: return NVOS_LOG_SEVERITY_INFO;
case nvinfer2::safe::Severity::kWARNING: return NVOS_LOG_SEVERITY_WARNING;
case nvinfer2::safe::Severity::kVERBOSE: return NVOS_LOG_SEVERITY_DEBUG1;
default: return NVOS_LOG_SEVERITY_INFO;
}
}
#endif // ENABLE_NVLOG
} // namespace detail
//! The SampleSafeRecorder implementation of the ISafeRecorder interface.
class SampleSafeRecorder : public ISafeRecorder
{
using DescHolder = std::array<AsciiChar, ISafeRecorder::kMAX_SAFE_DESC_LENGTH + 1U>;
using errorPair = std::pair<ErrorCode, DescHolder>;
public:
SampleSafeRecorder(nvinfer2::safe::Severity severity = nvinfer2::safe::Severity::kINFO, int32_t index = -1,
char const* filename = "TRTErrors.log")
: ISafeRecorder(severity, index)
{
fatalErrorLogFile = fopen(filename, "w+");
if (fatalErrorLogFile == nullptr)
{
std::cerr << "Failed to open error log file: " << filename << std::endl;
}
}
virtual ~SampleSafeRecorder() noexcept
{
if (mRefCount != 0)
{
reportError(ErrorCode::kINTERNAL_ERROR, "Non-zero reference count for recorder upon deallocation.");
}
}
int32_t getNbErrors() const noexcept final
{
return nbErrors;
}
ErrorCode getErrorCode(int32_t errorIdx) const noexcept final
{
return invalidIndexCheck(errorIdx) ? ErrorCode::kINVALID_ARGUMENT : (*this)[errorIdx].first;
};
ErrorDesc getErrorDesc(int32_t errorIdx) const noexcept final
{
return invalidIndexCheck(errorIdx) ? "ErrorIdx is out of range." : (*this)[errorIdx].second.data();
}
bool hasOverflowed() const noexcept final
{
return (nbErrors >= kMAX_NB_ERRORS);
}
int32_t getMaxNbErrors() const
{
return kMAX_NB_ERRORS;
}
// Empty the errorStack.
void clear() noexcept final
{
try
{
// grab a lock so that there is no addition while clearing.
std::lock_guard<std::mutex> guard(mStackLock);
nbErrors = 0;
}
catch (std::exception const& e)
{
#if ENABLE_NVLOG
NvOsDebugPrintStr(NVOS_LOG_CODE_START, NVOS_LOG_SEVERITY_ERROR, e.what());
#else
std::cerr << "Internal Error: " << e.what() << std::endl;
#endif // ENABLE_NVLOG
}
};
//! Simple helper function that checks if the error stack is empty.
bool empty() const noexcept
{
return (nbErrors == 0);
}
bool reportError(ErrorCode val, ErrorDesc desc) noexcept final
{
try
{
std::string_view const descView = desc; //< This implicitly calls strlen once.
std::cerr << descView << std::endl;
DescHolder descArr = detail::truncatedCopyAsCString<DescHolder>(descView);
std::lock_guard<std::mutex> guard(mStackLock);
#if ENABLE_NVLOG
NvOsDebugPrintStrInt(
NVOS_LOG_CODE_START, NVOS_LOG_SEVERITY_ERROR, descArr.data(), static_cast<int32_t>(val));
#else
// Only write to the array if there's space available
if (nbErrors < kMAX_NB_ERRORS)
{
mErrorStack.at(nbErrors) = errorPair(val, descArr);
nbErrors++;
}
#endif // ENABLE_NVLOG
}
catch (std::exception const& e)
{
#if ENABLE_NVLOG
NvOsDebugPrintStr(NVOS_LOG_CODE_START, NVOS_LOG_SEVERITY_ERROR, e.what());
#else
// `std::ofstream` uses heap allocation which is not allowed for safe samples
// Hence, C functions are used here to write data to file.
if (fatalErrorLogFile != nullptr)
{
setbuf(fatalErrorLogFile, NULL);
fwrite(e.what(), strlen(e.what()), 1, fatalErrorLogFile);
fwrite("\n", 1, 1, fatalErrorLogFile);
fflush(fatalErrorLogFile);
}
std::cerr << e.what() << std::endl;
#endif // ENABLE_NVLOG
}
// All errors are considered fatal.
return true;
}
bool reportInfo(ErrorDesc desc) noexcept final
{
return reportIfSevere(nvinfer2::safe::Severity::kINFO, desc);
}
bool reportWarn(ErrorDesc desc) noexcept final
{
return reportIfSevere(nvinfer2::safe::Severity::kWARNING, desc);
}
bool reportVerbose(ErrorDesc desc) noexcept final
{
return reportIfSevere(nvinfer2::safe::Severity::kVERBOSE, desc);
}
bool reportDebug(ErrorDesc desc) noexcept final
{
return reportIfSevere(nvinfer2::safe::Severity::kDEBUG, desc);
}
// Atomically increment or decrement the ref counter.
RefCount incRefCount() noexcept final
{
return ++mRefCount;
}
RefCount decRefCount() noexcept final
{
return --mRefCount;
}
private:
// Simple helper functions.
errorPair const& operator[](int32_t index) const noexcept
{
return mErrorStack[index];
}
bool invalidIndexCheck(int32_t index) const noexcept
{
return index >= nbErrors;
}
bool reportIfSevere(nvinfer2::safe::Severity msgSev, ErrorDesc desc) noexcept
{
#if ENABLE_NVLOG
if (mSeverity >= msgSev)
{
auto const severity = detail::toNvOsLogSeverity(msgSev);
std::lock_guard<std::mutex> guard(mStackLock);
NvOsDebugPrintStr(NVOS_LOG_CODE_START, severity, desc);
return true;
}
return false;
#else
if (mSeverity >= msgSev)
{
std::lock_guard<std::mutex> guard(mStackLock);
std::cout << desc << std::endl;
return true;
}
return false;
#endif // ENABLE_NVLOG
}
// Used to store the logs that are Fatal
FILE* fatalErrorLogFile;
// Mutex to hold when locking mErrorStack for thread safety.
std::mutex mStackLock;
// Reference count of the class. Destruction of the class when mRefCount
// is not zero causes undefined behavior.
std::atomic<int32_t> mRefCount{0};
// Number of errors that occurred so far.
int32_t nbErrors{0};
// Maximum number of errors that can be stored.
static constexpr int32_t kMAX_NB_ERRORS = 10;
// The error stack that holds the errors recorded by TensorRT.
std::array<errorPair, kMAX_NB_ERRORS> mErrorStack;
}; // class SampleRecorder
} // namespace sample
#endif // SAFE_ERROR_RECORDER_H