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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
* *
* Copyright 2015 Danny Robson <danny@nerdcruft.net> * Copyright 2015-2018 Danny Robson <danny@nerdcruft.net>
*/ */
#ifndef __UTIL_ALLOC_ARENA_HPP #ifndef CRUFT_UTIL_ALLOC_ARENA_HPP
#define __UTIL_ALLOC_ARENA_HPP #define CRUFT_UTIL_ALLOC_ARENA_HPP
#include "../memory/deleter.hpp" #include "../memory/deleter.hpp"
#include "../cast.hpp"
#include <memory> #include <memory>
#include <utility> #include <utility>

167
debug.hpp
View File

@ -14,8 +14,7 @@
* Copyright 2010-2017 Danny Robson <danny@nerdcruft.net> * Copyright 2010-2017 Danny Robson <danny@nerdcruft.net>
*/ */
#ifndef __DEBUG_HPP #pragma once
#define __DEBUG_HPP
//#include "maths.hpp" // XXX: See notes at the end of file for maths.hpp inclusion //#include "maths.hpp" // XXX: See notes at the end of file for maths.hpp inclusion
#include <cmath> #include <cmath>
@ -23,7 +22,8 @@
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <thread> #include <thread>
#include <iostream> #include <iosfwd>
#include <sstream>
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -47,6 +47,7 @@
/// refer to variables which do not always exist, and current compiler /// refer to variables which do not always exist, and current compiler
/// implementations are a little picky here. /// implementations are a little picky here.
#ifndef NDEBUG #ifndef NDEBUG
#include <iostream>
#define DEBUG_ONLY(X) do { X; } while (0) #define DEBUG_ONLY(X) do { X; } while (0)
#else #else
#define DEBUG_ONLY(X) do { ; } while (0) #define DEBUG_ONLY(X) do { ; } while (0)
@ -64,7 +65,11 @@
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define TRACE { \ #define TRACE { \
DEBUG_ONLY ( \ DEBUG_ONLY ( \
std::clog << "tid: " << std::this_thread::get_id () << "; " << __FILE__ << ":" << __func__ << ":" << __LINE__ << std::endl; \ std::cerr << "tid: " << std::this_thread::get_id () \
<< "; " << __FILE__ \
<< ":" << __func__ \
<< ":" << __LINE__ \
<< '\n'; \
); \ ); \
} }
@ -72,7 +77,11 @@
#define WARN(C) do { \ #define WARN(C) do { \
DEBUG_ONLY ( \ DEBUG_ONLY ( \
if (C) { \ if (C) { \
std::cerr << __FILE__ << ":" << __func__ << ":" << __LINE__ << ", " << #C << std::endl; \ std::cerr << __FILE__ \
<< ":" << __func__ \
<< ":" << __LINE__ \
<< ", " << #C \
<< '\n'; \
} \ } \
); \ ); \
} while (0) } while (0)
@ -81,7 +90,7 @@
#define WARN_RETURN(CONDITION,VALUE) do { \ #define WARN_RETURN(CONDITION,VALUE) do { \
if (const auto& __warn_return = (CONDITION); !!__warn_return) { \ if (const auto& __warn_return = (CONDITION); !!__warn_return) { \
if constexpr (debug_enabled) { \ if constexpr (debug_enabled) { \
std::clog << __FILE__ << ':' \ std::cerr << __FILE__ << ':' \
<< __LINE__ << ':' \ << __LINE__ << ':' \
<< __PRETTY_FUNCTION__ << "; " \ << __PRETTY_FUNCTION__ << "; " \
<< #CONDITION << '\n'; \ << #CONDITION << '\n'; \
@ -96,7 +105,7 @@
#define RETURN_UNLESS(VALUE,CONDITION) do { \ #define RETURN_UNLESS(VALUE,CONDITION) do { \
if (const auto &__return_unless = (CONDITION); !__return_unless) { \ if (const auto &__return_unless = (CONDITION); !__return_unless) { \
if constexpr (debug_enabled) { \ if constexpr (debug_enabled) { \
std::clog << __FILE__ << ':' \ std::cerr << __FILE__ << ':' \
<< __LINE__ << ':' \ << __LINE__ << ':' \
<< __PRETTY_FUNCTION__ << "; " \ << __PRETTY_FUNCTION__ << "; " \
<< #CONDITION << '\n'; \ << #CONDITION << '\n'; \
@ -108,14 +117,6 @@
} while (0) } 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 { \ #define CHECK(C) do { \
DEBUG_ONLY ( \ DEBUG_ONLY ( \
@ -142,11 +143,9 @@
const auto &__b = (B); \ const auto &__b = (B); \
\ \
if (!::util::almost_equal (__a, __b)) { \ if (!::util::almost_equal (__a, __b)) { \
_CHECK_PANIC("expected equality\n" \ std::cerr << "expected equality\n" \
"__a: %s is %!\n" \ "__a: " #A " is " << __a << "\n" \
"__b: %s is %!\n", \ "__b: " #B " is " << __b << "\n"; \
#A, __a, \
#B, __b); \
} \ } \
); \ ); \
} while (0) } while (0)
@ -159,11 +158,9 @@
const auto &__b = (B); \ const auto &__b = (B); \
\ \
if (__a >= __b) { \ if (__a >= __b) { \
_CHECK_PANIC("expected less than\n" \ std::cerr << "expected less than\n" \
"__a: %s is %!\n" \ "__a: " << #A << " is " << __a << "\n" \
"__b: %s is %!\n", \ "__b: " << #B << " is " << __b << "\n"; \
#A, __a, \
#B, __b); \
}; \ }; \
); \ ); \
} while (0) } while (0)
@ -176,11 +173,9 @@
const auto &__b = (B); \ const auto &__b = (B); \
\ \
if (__a > __b) { \ if (__a > __b) { \
_CHECK_PANIC("expected less than or equal\n" \ std::cerr << "expected less than or equal\n" \
"__a: %s is %!\n" \ "__a: " << #A << " is " << __a << "\n" \
"__b: %s is %!\n", \ "__b: " << #B << " is " << __b << "\n"; \
#A, __a, \
#B, __b); \
} \ } \
); \ ); \
} while (0) } while (0)
@ -193,11 +188,9 @@
const auto &__b = (B); \ const auto &__b = (B); \
\ \
if (__a <= __b) { \ if (__a <= __b) { \
_CHECK_PANIC ("expected greater than\n" \ std::cerr << "expected greater than\n" \
"__a: %s is %!\n" \ "__a: " << #A << " is " << __a << "\n" \
"__b: %s is %!\n", \ "__b: " << #B << " is " << __b << "\n"; \
#A, __a, \
#B, __b); \
} \ } \
); \ ); \
} while (0) } while (0)
@ -210,11 +203,9 @@
const auto &__b = (B); \ const auto &__b = (B); \
\ \
if (__a < __b) { \ if (__a < __b) { \
_CHECK_PANIC ("expected greater or equal\n" \ std::cerr << "expected greater or equal\n" \
"__a: %s is %!\n" \ "__a: " << #A << " is " << __a << "\n" \
"__b: %s is %!\n", \ "__b: " << #B << " is " << __b << "\n"; \
#A, __a, \
#B, __b); \
}; \ }; \
); \ ); \
} while (0) } while (0)
@ -228,13 +219,10 @@
const auto &__h = (H); \ const auto &__h = (H); \
\ \
if (__v < __l || __v > __h) { \ if (__v < __l || __v > __h) { \
_CHECK_PANIC ("expected limit\n" \ std::cerr << "expected limit\n" \
"__l: %s is %!\n" \ "__l: " << #L << " is " << +__l << "\n" \
"__h: %s is %!\n" \ "__h: " << #H << " is " << +__h << "\n" \
"__v: %s is %!\n", \ "__v: " << #V << " is " << +__v << "\n"; \
#L, __l, \
#H, __h, \
#V, __v); \
}; \ }; \
); \ ); \
} while (0) } while (0)
@ -246,11 +234,9 @@
const auto &__b = (B); \ const auto &__b = (B); \
\ \
if (::util::almost_equal (__a, __b)) { \ if (::util::almost_equal (__a, __b)) { \
_CHECK_PANIC ("expected inequality\n" \ std::cerr << "expected inequality\n" \
"__a: %s is %!\n" \ "__a: " << #A << " is " << __a << "\n" \
"__b: %s is %!\n", \ "__b: " << #B << " is " << __b << "\n"; \
#A, __a, \
#B, __b); \
}; \ }; \
); \ ); \
} while (0) } while (0)
@ -262,9 +248,8 @@
const auto &__a = (A); \ const auto &__a = (A); \
\ \
if (!::util::almost_zero (__a)) { \ if (!::util::almost_zero (__a)) { \
_CHECK_PANIC ("expected zero\n" \ std::cerr << "expected zero\n" \
"__a: %s is %!\n" \ "__a: " << #A << " is " << __a << "\n"; \
#A, __a); \
}; \ }; \
); \ ); \
} while (0) } while (0)
@ -276,9 +261,8 @@
const auto &__a = (A); \ const auto &__a = (A); \
\ \
if (::util::exactly_zero (__a)) \ if (::util::exactly_zero (__a)) \
_CHECK_PANIC ("expected non-zero\n" \ std::cerr << "expected non-zero\n" \
"__a: %s is %!", \ "__a: " << #A << " is " << __a << '\n'; \
#A, __a); \
); \ ); \
} while (0) } while (0)
@ -290,11 +274,9 @@
const auto &__check_mod_m = (M); \ const auto &__check_mod_m = (M); \
\ \
if (!::util::exactly_zero (__check_mod_v % __check_mod_m)) \ if (!::util::exactly_zero (__check_mod_v % __check_mod_m)) \
_CHECK_PANIC ("expected zero modulus\n" \ std::cerr << "expected zero modulus\n" \
"__v: %s is %!\n" \ "__v: " << #V << " is " << __check_mod_v << "\n" \
"__m: %s is %!", \ "__m: " << #M << " is " << __check_mod_m << "\n"; \
#V, __check_mod_v, \
#M, __check_mod_m); \
); \ ); \
} while (0) } while (0)
@ -308,9 +290,8 @@
if (std::find (std::cbegin (__e), \ if (std::find (std::cbegin (__e), \
std::cend (__e), \ std::cend (__e), \
__c) == std::end (__e)) { \ __c) == std::end (__e)) { \
_CHECK_PANIC("expect enum\n" \ std::cerr << "expect enum\n" \
"__c: %s is %!", \ "__c: " << #C << " is " << __c << '\n'; \
#C, __c); \
} \ } \
} while (0) } while (0)
#else #else
@ -322,9 +303,8 @@
#define CHECK_FINITE(V) do { \ #define CHECK_FINITE(V) do { \
const auto &__v = (V); \ const auto &__v = (V); \
if (!std::isfinite (__v)) { \ if (!std::isfinite (__v)) { \
_CHECK_PANIC ("expected finite value\n" \ std::cerr << "expected finite value\n" \
"__v: %! is %!", \ "__v: " << #V << " is " << __v << '\n'; \
#V, __v); \
} \ } \
} while (0) } while (0)
#else #else
@ -345,7 +325,7 @@
{ caught = true; } \ { caught = true; } \
\ \
if (!caught) \ if (!caught) \
_CHECK_PANIC ("expected exception: %s", #E); \ std::cerr << "expected exception: " << #E << '\n' \
); \ ); \
} while (0) } while (0)
@ -356,11 +336,9 @@
try { \ try { \
C; \ C; \
} catch (const std::exception &e) { \ } catch (const std::exception &e) { \
_CHECK_PANIC ("unexpected exception: %s", \ std::cerr << "unexpected exception: " << e.what () << '\n'; \
e.what ()); \
} catch (...) { \ } catch (...) { \
_CHECK_PANIC ("unexpected exception: %s", \ std::cerr << "unexpected exception: unknown\n"; \
"unknown"); \
} \ } \
); \ ); \
} while (0) } while (0)
@ -434,23 +412,14 @@ void breakpoint (void);
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#include "log.hpp"
#include "backtrace.hpp"
namespace util::debug::detail { 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 not_implemented [[noreturn]] (const char *msg);
void unreachable [[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> inline void
constexpr void panic [[noreturn]] (const std::string &msg)
panic [[noreturn]] (const char (&fmt)[N], const Args&... args)
{ {
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 /// report a fatal error induced by an unhandled value, especially in switch
/// statements. will almost invariably abort the application. /// 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> template <typename T>
constexpr void constexpr void
unhandled [[noreturn]] (T &&t) noexcept 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 // 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. // after we define the CHECK_ macros so the preprocessor can resolve them.
#include "maths.hpp" #include "maths.hpp"
#endif // __DEBUG_HPP

View File

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

View File

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