debug: don't rely on format for output

debug is relied upon by a great deal of other units so it's very
difficult to include other popular headers like format.hpp without
triggering cyclic dependencies.
This commit is contained in:
Danny Robson 2018-05-10 12:44:03 +10:00
parent 06e29ed136
commit 4116442e40
4 changed files with 214 additions and 234 deletions

View File

@ -11,13 +11,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
* Copyright 2015-2018 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_ALLOC_ARENA_HPP
#define __UTIL_ALLOC_ARENA_HPP
#ifndef CRUFT_UTIL_ALLOC_ARENA_HPP
#define CRUFT_UTIL_ALLOC_ARENA_HPP
#include "../memory/deleter.hpp"
#include "../cast.hpp"
#include <memory>
#include <utility>

439
debug.hpp
View File

@ -14,8 +14,7 @@
* Copyright 2010-2017 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __DEBUG_HPP
#define __DEBUG_HPP
#pragma once
//#include "maths.hpp" // XXX: See notes at the end of file for maths.hpp inclusion
#include <cmath>
@ -23,7 +22,8 @@
#include <stdexcept>
#include <string>
#include <thread>
#include <iostream>
#include <iosfwd>
#include <sstream>
///////////////////////////////////////////////////////////////////////////////
@ -47,6 +47,7 @@
/// refer to variables which do not always exist, and current compiler
/// implementations are a little picky here.
#ifndef NDEBUG
#include <iostream>
#define DEBUG_ONLY(X) do { X; } while (0)
#else
#define DEBUG_ONLY(X) do { ; } while (0)
@ -62,26 +63,34 @@
///////////////////////////////////////////////////////////////////////////////
#define TRACE { \
DEBUG_ONLY ( \
std::clog << "tid: " << std::this_thread::get_id () << "; " << __FILE__ << ":" << __func__ << ":" << __LINE__ << std::endl; \
); \
#define TRACE { \
DEBUG_ONLY ( \
std::cerr << "tid: " << std::this_thread::get_id () \
<< "; " << __FILE__ \
<< ":" << __func__ \
<< ":" << __LINE__ \
<< '\n'; \
); \
}
#define WARN(C) do { \
DEBUG_ONLY ( \
if (C) { \
std::cerr << __FILE__ << ":" << __func__ << ":" << __LINE__ << ", " << #C << std::endl; \
} \
); \
#define WARN(C) do { \
DEBUG_ONLY ( \
if (C) { \
std::cerr << __FILE__ \
<< ":" << __func__ \
<< ":" << __LINE__ \
<< ", " << #C \
<< '\n'; \
} \
); \
} while (0)
#define WARN_RETURN(CONDITION,VALUE) do { \
if (const auto& __warn_return = (CONDITION); !!__warn_return) { \
if constexpr (debug_enabled) { \
std::clog << __FILE__ << ':' \
std::cerr << __FILE__ << ':' \
<< __LINE__ << ':' \
<< __PRETTY_FUNCTION__ << "; " \
<< #CONDITION << '\n'; \
@ -96,7 +105,7 @@
#define RETURN_UNLESS(VALUE,CONDITION) do { \
if (const auto &__return_unless = (CONDITION); !__return_unless) { \
if constexpr (debug_enabled) { \
std::clog << __FILE__ << ':' \
std::cerr << __FILE__ << ':' \
<< __LINE__ << ':' \
<< __PRETTY_FUNCTION__ << "; " \
<< #CONDITION << '\n'; \
@ -108,14 +117,6 @@
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define _CHECK_PANIC(FMT,...) do { \
panic ("%s:%s:%i:%s\n" FMT, \
PACKAGE, __FILE__, __LINE__, __func__, \
__VA_ARGS__); \
} while(0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK(C) do { \
DEBUG_ONLY ( \
@ -142,176 +143,156 @@
const auto &__b = (B); \
\
if (!::util::almost_equal (__a, __b)) { \
_CHECK_PANIC("expected equality\n" \
"__a: %s is %!\n" \
"__b: %s is %!\n", \
#A, __a, \
#B, __b); \
std::cerr << "expected equality\n" \
"__a: " #A " is " << __a << "\n" \
"__b: " #B " is " << __b << "\n"; \
} \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_LT(A,B) do { \
DEBUG_ONLY ( \
const auto &__a = (A); \
const auto &__b = (B); \
\
if (__a >= __b) { \
_CHECK_PANIC("expected less than\n" \
"__a: %s is %!\n" \
"__b: %s is %!\n", \
#A, __a, \
#B, __b); \
}; \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_LE(A,B) do { \
DEBUG_ONLY ( \
const auto &__a = (A); \
const auto &__b = (B); \
\
if (__a > __b) { \
_CHECK_PANIC("expected less than or equal\n" \
"__a: %s is %!\n" \
"__b: %s is %!\n", \
#A, __a, \
#B, __b); \
} \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_GT(A,B) do { \
DEBUG_ONLY ( \
const auto &__a = (A); \
const auto &__b = (B); \
\
if (__a <= __b) { \
_CHECK_PANIC ("expected greater than\n" \
"__a: %s is %!\n" \
"__b: %s is %!\n", \
#A, __a, \
#B, __b); \
} \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_GE(A,B) do { \
DEBUG_ONLY ( \
const auto &__a = (A); \
const auto &__b = (B); \
\
if (__a < __b) { \
_CHECK_PANIC ("expected greater or equal\n" \
"__a: %s is %!\n" \
"__b: %s is %!\n", \
#A, __a, \
#B, __b); \
}; \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_LIMIT(V,L,H) do { \
DEBUG_ONLY ( \
const auto &__v = (V); \
const auto &__l = (L); \
const auto &__h = (H); \
\
if (__v < __l || __v > __h) { \
_CHECK_PANIC ("expected limit\n" \
"__l: %s is %!\n" \
"__h: %s is %!\n" \
"__v: %s is %!\n", \
#L, __l, \
#H, __h, \
#V, __v); \
}; \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_NEQ(A,B) do { \
DEBUG_ONLY( \
const auto &__a = (A); \
const auto &__b = (B); \
\
if (::util::almost_equal (__a, __b)) { \
_CHECK_PANIC ("expected inequality\n" \
"__a: %s is %!\n" \
"__b: %s is %!\n", \
#A, __a, \
#B, __b); \
}; \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_ZERO(A) do { \
DEBUG_ONLY ( \
const auto &__a = (A); \
\
if (!::util::almost_zero (__a)) { \
_CHECK_PANIC ("expected zero\n" \
"__a: %s is %!\n" \
#A, __a); \
}; \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_NEZ(A) do { \
DEBUG_ONLY ( \
const auto &__a = (A); \
\
if (::util::exactly_zero (__a)) \
_CHECK_PANIC ("expected non-zero\n" \
"__a: %s is %!", \
#A, __a); \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_MOD(V,M) do { \
#define CHECK_LT(A,B) do { \
DEBUG_ONLY ( \
const auto &__check_mod_v = (V); \
const auto &__check_mod_m = (M); \
const auto &__a = (A); \
const auto &__b = (B); \
\
if (!::util::exactly_zero (__check_mod_v % __check_mod_m)) \
_CHECK_PANIC ("expected zero modulus\n" \
"__v: %s is %!\n" \
"__m: %s is %!", \
#V, __check_mod_v, \
#M, __check_mod_m); \
if (__a >= __b) { \
std::cerr << "expected less than\n" \
"__a: " << #A << " is " << __a << "\n" \
"__b: " << #B << " is " << __b << "\n"; \
}; \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_LE(A,B) do { \
DEBUG_ONLY ( \
const auto &__a = (A); \
const auto &__b = (B); \
\
if (__a > __b) { \
std::cerr << "expected less than or equal\n" \
"__a: " << #A << " is " << __a << "\n" \
"__b: " << #B << " is " << __b << "\n"; \
} \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_GT(A,B) do { \
DEBUG_ONLY ( \
const auto &__a = (A); \
const auto &__b = (B); \
\
if (__a <= __b) { \
std::cerr << "expected greater than\n" \
"__a: " << #A << " is " << __a << "\n" \
"__b: " << #B << " is " << __b << "\n"; \
} \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_GE(A,B) do { \
DEBUG_ONLY ( \
const auto &__a = (A); \
const auto &__b = (B); \
\
if (__a < __b) { \
std::cerr << "expected greater or equal\n" \
"__a: " << #A << " is " << __a << "\n" \
"__b: " << #B << " is " << __b << "\n"; \
}; \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_LIMIT(V,L,H) do { \
DEBUG_ONLY ( \
const auto &__v = (V); \
const auto &__l = (L); \
const auto &__h = (H); \
\
if (__v < __l || __v > __h) { \
std::cerr << "expected limit\n" \
"__l: " << #L << " is " << +__l << "\n" \
"__h: " << #H << " is " << +__h << "\n" \
"__v: " << #V << " is " << +__v << "\n"; \
}; \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_NEQ(A,B) do { \
DEBUG_ONLY( \
const auto &__a = (A); \
const auto &__b = (B); \
\
if (::util::almost_equal (__a, __b)) { \
std::cerr << "expected inequality\n" \
"__a: " << #A << " is " << __a << "\n" \
"__b: " << #B << " is " << __b << "\n"; \
}; \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_ZERO(A) do { \
DEBUG_ONLY ( \
const auto &__a = (A); \
\
if (!::util::almost_zero (__a)) { \
std::cerr << "expected zero\n" \
"__a: " << #A << " is " << __a << "\n"; \
}; \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_NEZ(A) do { \
DEBUG_ONLY ( \
const auto &__a = (A); \
\
if (::util::exactly_zero (__a)) \
std::cerr << "expected non-zero\n" \
"__a: " << #A << " is " << __a << '\n'; \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_MOD(V,M) do { \
DEBUG_ONLY ( \
const auto &__check_mod_v = (V); \
const auto &__check_mod_m = (M); \
\
if (!::util::exactly_zero (__check_mod_v % __check_mod_m)) \
std::cerr << "expected zero modulus\n" \
"__v: " << #V << " is " << __check_mod_v << "\n" \
"__m: " << #M << " is " << __check_mod_m << "\n"; \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#if defined(ENABLE_DEBUGGING)
#define CHECK_ENUM(C, ...) do { \
const auto &__c = (C); \
const auto &__e = { __VA_ARGS__ }; \
\
if (std::find (std::cbegin (__e), \
std::cend (__e), \
__c) == std::end (__e)) { \
_CHECK_PANIC("expect enum\n" \
"__c: %s is %!", \
#C, __c); \
} \
#define CHECK_ENUM(C, ...) do { \
const auto &__c = (C); \
const auto &__e = { __VA_ARGS__ }; \
\
if (std::find (std::cbegin (__e), \
std::cend (__e), \
__c) == std::end (__e)) { \
std::cerr << "expect enum\n" \
"__c: " << #C << " is " << __c << '\n'; \
} \
} while (0)
#else
#define CHECK_ENUM(C,...)
@ -319,13 +300,12 @@
#if !defined(NDEBUG)
#define CHECK_FINITE(V) do { \
const auto &__v = (V); \
if (!std::isfinite (__v)) { \
_CHECK_PANIC ("expected finite value\n" \
"__v: %! is %!", \
#V, __v); \
} \
#define CHECK_FINITE(V) do { \
const auto &__v = (V); \
if (!std::isfinite (__v)) { \
std::cerr << "expected finite value\n" \
"__v: " << #V << " is " << __v << '\n'; \
} \
} while (0)
#else
#define CHECK_FINITE(V,...)
@ -335,34 +315,32 @@
///////////////////////////////////////////////////////////////////////////////
#define CHECK_THROWS(E,C) do { \
DEBUG_ONLY ( \
bool caught = false; \
\
try \
{ C; } \
catch (E const&) \
{ caught = true; } \
\
if (!caught) \
_CHECK_PANIC ("expected exception: %s", #E); \
); \
#define CHECK_THROWS(E,C) do { \
DEBUG_ONLY ( \
bool caught = false; \
\
try \
{ C; } \
catch (E const&) \
{ caught = true; } \
\
if (!caught) \
std::cerr << "expected exception: " << #E << '\n' \
); \
} while (0)
///////////////////////////////////////////////////////////////////////////////
#define CHECK_NOTHROW(C) do { \
DEBUG_ONLY ( \
try { \
C; \
} catch (const std::exception &e) { \
_CHECK_PANIC ("unexpected exception: %s", \
e.what ()); \
} catch (...) { \
_CHECK_PANIC ("unexpected exception: %s", \
"unknown"); \
} \
); \
#define CHECK_NOTHROW(C) do { \
DEBUG_ONLY ( \
try { \
C; \
} catch (const std::exception &e) { \
std::cerr << "unexpected exception: " << e.what () << '\n'; \
} catch (...) { \
std::cerr << "unexpected exception: unknown\n"; \
} \
); \
} while (0)
@ -434,23 +412,14 @@ void breakpoint (void);
///////////////////////////////////////////////////////////////////////////////
#include "log.hpp"
#include "backtrace.hpp"
namespace util::debug::detail {
void panic [[noreturn]] (const char *msg);
void
panic [[noreturn]] (const char *msg);
template <typename ...Args, size_t N>
void panic [[noreturn]] (const char (&fmt)[N], const Args& ...args)
{
LOG_EMERGENCY (fmt, args...);
LOG_EMERGENCY ("%!", ::debug::backtrace ());
breakpoint ();
abort ();
}
void not_implemented [[noreturn]] (const char *msg);
void unreachable [[noreturn]] (const char *msg);
}
@ -465,11 +434,10 @@ panic [[noreturn]] (const char *msg)
}
template <typename ...Args, size_t N>
constexpr void
panic [[noreturn]] (const char (&fmt)[N], const Args&... args)
inline void
panic [[noreturn]] (const std::string &msg)
{
util::debug::detail::panic (fmt, args...);
panic (msg.c_str ());
}
@ -530,11 +498,23 @@ unreachable [[noreturn]] (void)
///////////////////////////////////////////////////////////////////////////////
/// report a fatal error induced by an unhandled value, especially in switch
/// statements. will almost invariably abort the application.
namespace util::debug::detail {
template <typename T>
void
unhandled [[noreturn]] (T &&t) noexcept
{
std::ostringstream os;
os << "unhandled value: " << t << '\n';
::panic (os.str ());
}
}
template <typename T>
constexpr void
unhandled [[noreturn]] (T &&t) noexcept
{
panic ("unhandled value %!", std::forward<T> (t));
util::debug::detail::unhandled (std::forward<T> (t));
}
@ -648,6 +628,3 @@ namespace util::debug {
// but maths.hpp might be using CHECK_ macros so we must include maths.hpp
// after we define the CHECK_ macros so the preprocessor can resolve them.
#include "maths.hpp"
#endif // __DEBUG_HPP

View File

@ -19,6 +19,7 @@
#include "version.hpp"
#include "maths.hpp"
#include <cstring>
#include <stdexcept>

View File

@ -18,6 +18,7 @@
#ifndef CRUFT_UTIL_VIEW_HPP
#define CRUFT_UTIL_VIEW_HPP
#include "cast.hpp"
#include "types/traits.hpp"
#include "maths.hpp"