Skip to content

Commit

Permalink
Merge branch 'simpler-assert'
Browse files Browse the repository at this point in the history
* simpler-assert:
  ASE: utils: use diag_prefix() for printouts
  ASE: backtrace.*: remove old code
  ASE: cxxaux.hh: add ASE_ASSERT_UNREACHED() and ASE_ASSERT()
  ASE: cxxaux.hh: add assertion_fatal() and bool assertion_failed_fatal
  ASE: cxxaux.cc: add assertion_fatal(), handle $ASE_DEBUG assertion debug keys
	Check and implement "breakpoint", "backtrace" and "fatal-warnings" in $ASE_DEBUG

Signed-off-by: Tim Janik <[email protected]>
  • Loading branch information
tim-janik committed Jan 22, 2024
2 parents a26757d + c5ca06b commit 25b6611
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 247 deletions.
116 changes: 0 additions & 116 deletions ase/backtrace.cc

This file was deleted.

44 changes: 0 additions & 44 deletions ase/backtrace.hh

This file was deleted.

135 changes: 111 additions & 24 deletions ase/cxxaux.cc
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
// This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
#include "cxxaux.hh"
#include "utils.hh" // ase_fatal_warnings
#include "backtrace.hh"
#include <cxxabi.h> // abi::__cxa_demangle
#include <signal.h>
#include <fcntl.h>
#include <cstring>

namespace Ase {

VirtualBase::~VirtualBase() noexcept
{}

/** Demangle a std::typeinfo.name() string into a proper C++ type name.
* This function uses abi::__cxa_demangle() from <cxxabi.h> to demangle C++ type names,
* which works for g++, libstdc++, clang++, libc++.
*/
String
string_demangle_cxx (const char *mangled_identifier)
string_demangle_cxx (const char *mangled_identifier) noexcept
{
int status = 0;
char *malloced_result = abi::__cxa_demangle (mangled_identifier, NULL, NULL, &status);
Expand All @@ -22,34 +24,119 @@ string_demangle_cxx (const char *mangled_identifier)
return result;
}

/// Find GDB and construct command line
static std::string
backtrace_command()
{
bool allow_ptrace = false;
#ifdef __linux__
const char *const ptrace_scope = "/proc/sys/kernel/yama/ptrace_scope";
int fd = open (ptrace_scope, 0);
char b[8] = { 0 };
if (read (fd, b, 8) > 0)
allow_ptrace = b[0] == '0';
close (fd);
#else
allow_ptrace = true;
#endif
const char *const usr_bin_gdb = "/usr/bin/gdb";
if (!allow_ptrace || access (usr_bin_gdb, X_OK) != 0)
return "";
char cmd[1024];
snprintf (cmd, sizeof (cmd),
"%s -q -n -p %u --batch "
"-iex 'set auto-load python-scripts off' "
"-iex 'set script-extension off' "
"-ex 'set print address off' "
// "-ex 'set print frame-arguments none' "
"-ex 'thread apply all backtrace 21' " // max frames
">&2 2>/dev/null",
usr_bin_gdb, gettid());
return cmd;
}

/// Quick boolean check for a colon separated key in a haystack.
static bool
has_debug_key (const char *const debugkeys, const char *const key)
{
if (!debugkeys) return false;
const auto l = strlen (key);
const auto d = strstr (debugkeys, key);
return d && (d == debugkeys || d[-1] == ':') && (d[l] == 0 || d[l] == ':');
}

/// Global flag to force aborting on assertion warnings.
bool assertion_failed_fatal = false;

/// Print a debug message via assertion_failed() and abort the program.
void
assertion_fatal (const char *msg, const char *file, int line, const char *func) noexcept
{
assertion_failed_fatal = true;
assertion_failed (msg, file, line, func);
for (;;)
abort();
}

static void assertion_abort (const char *msg, const char *file, int line, const char *func) noexcept ASE_NORETURN;

/// Print instructive message, handle "breakpoint", "backtrace" and "fatal-warnings" in $ASE_DEBUG.
void
assertion_failed (const char *msg, const char *file, int line, const char *func) noexcept
{
std::string m;
if (file && line > 0 && func)
fprintf (stderr, "%s:%u:%s: ", file, line, func);
m += std::string (file) + ":" + std::to_string (unsigned (line)) + ":" + func + ": ";
else if (file && line > 0)
fprintf (stderr, "%s:%u: ", file, line);
else if (file)
fprintf (stderr, "%s: ", file);
m += std::string (file) + ":" + std::to_string (unsigned (line)) + ": ";
else if (file || func)
m += std::string (file ? file : func) + ": ";
if (!msg || !msg[0])
fputs ("state unreachable\n", stderr);
else
{
fputs ("assertion failed: ", stderr);
fputs (msg, stderr);
if (msg[0] && msg[strlen (msg) - 1] != '\n')
fputc ('\n', stderr);
m += "assertion unreachable\n";
else {
if (line >= 0)
m += "assertion failed: ";
m += msg;
if (!m.empty() && m.back() != '\n')
m += "\n";
}
fflush (stdout); // preserve output ordering
fputs (m.c_str(), stderr);
fflush (stderr); // some platforms (_WIN32) don't properly flush on '\n'
const char *const d = getenv ("ASE_DEBUG");
if (!assertion_failed_fatal && has_debug_key (d, "fatal-warnings"))
assertion_failed_fatal = true;
if (assertion_failed_fatal || has_debug_key (d, "breakpoint")) {
#if (defined __i386__ || defined __x86_64__)
__asm__ __volatile__ ("int $03");
#else
__builtin_trap();
#endif
} else if (has_debug_key (d, "backtrace")) {
const std::string gdb_cmd = backtrace_command();
if (!gdb_cmd.empty()) {
const auto res = system (gdb_cmd.c_str());
(void) res;
}
fflush (stderr);
if (debug_key_enabled ("backtrace"))
ASE_PRINT_BACKTRACE (__FILE__, __LINE__, __func__);
else if (debug_key_enabled ("break"))
breakpoint();
if (ase_fatal_warnings)
return breakpoint();
}
if (assertion_failed_fatal)
assertion_abort (msg, file, line, func);
}

VirtualBase::~VirtualBase()
{}
} // Ase

#undef NDEBUG // enable __GLIBC__ __assert_fail()
#include <cassert>
namespace Ase {
static void
assertion_abort (const char *msg, const char *file, int line, const char *func) noexcept
{
#if defined (_ASSERT_H_DECLS) && defined(__GLIBC__)
// abort via GLIBC if possible, which allows 'print __abort_msg->msg' from apport/gdb
__assert_fail (msg && msg[0] ? msg : "assertion unreachable\n", file, line, func);
#else
for (;;)
abort();
#endif
}
} // Ase
15 changes: 12 additions & 3 deletions ase/cxxaux.hh
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,13 @@ using VoidF = std::function<void()>;
#define ASE_ASSERT_RETURN(expr, ...) do { if (expr) [[likely]] break; ::Ase::assertion_failed (#expr); return __VA_ARGS__; } while (0)

/// Return from the current function and issue an assertion warning.
#define ASE_ASSERT_RETURN_UNREACHED(...) do { ::Ase::assertion_failed (""); return __VA_ARGS__; } while (0)
#define ASE_ASSERT_RETURN_UNREACHED(...) do { ::Ase::assertion_failed (nullptr); return __VA_ARGS__; } while (0)

/// Abort and issue an assertion error.
#define ASE_ASSERT_UNREACHED(...) do { ::Ase::assertion_fatal (nullptr); } while (0)

/// Issue an assertion warning if `expr` evaluates to false.
#define ASE_ASSERT(expr) do { if (expr) [[likely]] break; ::Ase::assertion_fatal (#expr); } while (0)

/// Issue an assertion warning if `expr` evaluates to false.
#define ASE_ASSERT_WARN(expr) do { if (expr) [[likely]] break; ::Ase::assertion_failed (#expr); } while (0)
Expand Down Expand Up @@ -174,7 +180,7 @@ divmod (T dividend, T divisor, T *reminderp)
}

/// Demangle identifier via libcc.
std::string string_demangle_cxx (const char *mangled_identifier);
std::string string_demangle_cxx (const char *mangled_identifier) noexcept;

/// Provide demangled stringified name for type `T`.
template<class T> ASE_PURE static inline String
Expand All @@ -201,13 +207,16 @@ unalias_ptr (T *ptr)
/// Common base type to allow casting between polymorphic classes.
struct VirtualBase {
protected:
virtual ~VirtualBase() = 0;
virtual ~VirtualBase() noexcept = 0;
};
using VirtualBaseP = std::shared_ptr<VirtualBase>;

/// Issue a warning about an assertion error.
void assertion_failed (const char *msg = nullptr, const char *file = __builtin_FILE(),
int line = __builtin_LINE(), const char *func = __builtin_FUNCTION()) noexcept;
void assertion_fatal (const char *msg = nullptr, const char *file = __builtin_FILE(),
int line = __builtin_LINE(), const char *func = __builtin_FUNCTION()) noexcept ASE_NORETURN;
extern bool assertion_failed_fatal;

/// Test string equality at compile time.
extern inline constexpr bool
Expand Down
Loading

0 comments on commit 25b6611

Please sign in to comment.