Files
filament/libs/utils/src/Panic.cpp

363 lines
9.0 KiB
C++

/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.
*/
#include <utils/Panic.h>
#include "ostream_.h"
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cstdlib>
#include <cstring>
#include <mutex>
#include <new>
#include <string>
#include <string_view>
#include <utility>
namespace utils {
// ------------------------------------------------------------------------------------------------
class UserPanicHandler {
struct CallBack {
Panic::PanicHandlerCallback handler = nullptr;
void* user = nullptr;
void call(Panic const& panic) const {
if (UTILS_UNLIKELY(handler)) {
handler(user, panic);
}
}
};
mutable std::mutex mLock{};
CallBack mCallBack{};
CallBack getCallback() const noexcept {
std::lock_guard const lock(mLock);
return mCallBack;
}
public:
static UserPanicHandler& get() noexcept {
static UserPanicHandler data;
return data;
}
void call(Panic const& panic) const {
getCallback().call(panic);
}
void set(Panic::PanicHandlerCallback handler, void* user) noexcept {
std::lock_guard const lock(mLock);
mCallBack = { handler, user };
}
};
// ------------------------------------------------------------------------------------------------
UTILS_NOINLINE
static std::string sprintfToString(const char* format, va_list args) noexcept {
std::string s;
va_list tmp;
va_copy(tmp, args);
int n = vsnprintf(nullptr, 0, format, tmp);
va_end(tmp);
if (n >= 0) {
++n; // for the nul-terminating char
char* const buf = new(std::nothrow) char[n];
if (buf) {
vsnprintf(buf, size_t(n), format, args);
s.assign(buf);
delete [] buf;
}
}
return s;
}
static inline std::string sprintfToString(const char* format, ...) noexcept {
va_list args;
va_start(args, format);
std::string const s{ sprintfToString(format, args) };
va_end(args);
return s;
}
static std::string buildPanicString(
std::string_view const& msg, const char* function, int line,
const char* file, const char* reason) {
#ifndef NDEBUG
return sprintfToString("%.*s\nin %s:%d\nin file %s\nreason: %s",
msg.size(), msg.data(), function, line, file, reason);
#else
return sprintfToString("%.*s\nin %s:%d\nreason: %s",
msg.size(), msg.data(), function, line, reason);
#endif
}
// ------------------------------------------------------------------------------------------------
Panic::~Panic() noexcept = default;
void Panic::setPanicHandler(PanicHandlerCallback handler, void* user) noexcept {
UserPanicHandler::get().set(handler, user);
}
// ------------------------------------------------------------------------------------------------
template<typename T>
TPanic<T>::TPanic(const char* function, const char* file, int line, char const* literal,
std::string reason)
: mFile(file),
mFunction(function),
mLine(line),
mLiteral(literal),
mReason(std::move(reason)) {
mCallstack.update(1);
mWhat = buildPanicString(T::type, mFunction, mLine, mFile, mReason.c_str());
}
template<typename T>
TPanic<T>::~TPanic() = default;
template<typename T>
const char* TPanic<T>::what() const noexcept {
return mWhat.c_str();
}
template<typename T>
const char* TPanic<T>::getType() const noexcept {
return T::type;
}
template<typename T>
const char* TPanic<T>::getReason() const noexcept {
return mReason.c_str();
}
template<typename T>
const char* TPanic<T>::getReasonLiteral() const noexcept {
return mLiteral.c_str();
}
template<typename T>
const char* TPanic<T>::getFunction() const noexcept {
return mFunction;
}
template<typename T>
const char* TPanic<T>::getFile() const noexcept {
return mFile;
}
template<typename T>
int TPanic<T>::getLine() const noexcept {
return mLine;
}
template<typename T>
const CallStack& TPanic<T>::getCallStack() const noexcept {
return mCallstack;
}
template<typename T>
void TPanic<T>::log() const noexcept {
slog.e << what() << io::endl;
slog.e << mCallstack << io::endl;
}
UTILS_ALWAYS_INLINE
inline static const char* formatFile(char const* file) noexcept {
const char * p = std::strstr(file, "filament/");
return p ? p : file;
}
template<typename T>
void TPanic<T>::panic(char const* function, char const* file, int line, char const* literal,
const char* format, ...) {
va_list args;
va_start(args, format);
std::string reason{ sprintfToString(format, args) };
va_end(args);
panic(function, file, line, literal, std::move(reason));
}
template<typename T>
void TPanic<T>::panic(char const* function, char const* file, int line, char const* literal,
std::string reason) {
if (reason.empty()) {
reason = literal;
}
T e(function, formatFile(file), line, literal, std::move(reason));
// always log the Panic at the point it is detected
e.log();
// Call the user provided handler
UserPanicHandler::get().call(e);
// if exceptions are enabled, throw now.
#ifdef __EXCEPTIONS
throw e;
#endif
// and finally abort if we somehow get here
std::abort();
}
// -----------------------------------------------------------------------------------------------
namespace details {
void panicLog(char const* function, char const* file, int line, const char* format, ...) noexcept {
va_list args;
va_start(args, format);
std::string const reason{ sprintfToString(format, args) };
va_end(args);
std::string const msg = buildPanicString("PanicLog",
function, line, file, reason.c_str());
slog.e << msg << io::endl;
slog.e << CallStack::unwind(1) << io::endl;
}
PanicStream::PanicStream(
char const* function,
char const* file,
int line,
char const* condition) noexcept
: mFunction(function), mFile(file), mLine(line), mLiteral(condition) {
}
PanicStream::~PanicStream() = default;
PanicStream& PanicStream::operator<<(short value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned short value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(char value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned char value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(int value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned int value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(long value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned long value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(long long int value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned long long int value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(float value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(double value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(long double value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(bool value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(void const* value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(char const* value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned char const* value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(std::string const& value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(std::string_view const& value) noexcept {
mStream << value;
return *this;
}
} // namespace details
// -----------------------------------------------------------------------------------------------
template class UTILS_PUBLIC TPanic<PreconditionPanic>;
template class UTILS_PUBLIC TPanic<PostconditionPanic>;
template class UTILS_PUBLIC TPanic<ArithmeticPanic>;
} // namespace utils