Commit 47e384c5 authored by Lucian Grijincu's avatar Lucian Grijincu Committed by Facebook GitHub Bot

folly: SafeAssert: variadic macros to enable more detailed error reporting from signal handlers

Summary:
`folly::Symbolizer` uses `FOLLY_SAFE_CHECK` -- which is signal-safe, but it only says "this thing broke".

I plan to changes these to add more details about what exactly broke:
from:
   FOLLY_SAFE_CHECK(die.abbr.tag == DW_TAG_compile_unit, "expecting compile unit entry");
to
   FOLLY_SAFE_CHECK(die.abbr.tag == DW_TAG_compile_unit, "expecting compile unit entry, found: ", die.abbr.tag);

Reviewed By: yfeldblum

Differential Revision: D25992552

fbshipit-source-id: ae678b45606afc519704767844a973d499c60c73
parent 9b6c06fa
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include <folly/portability/Unistd.h> #include <folly/portability/Unistd.h>
namespace folly { namespace folly {
namespace detail {
namespace { namespace {
...@@ -442,35 +441,40 @@ constexpr std::pair<int, const char*> errors[] = { ...@@ -442,35 +441,40 @@ constexpr std::pair<int, const char*> errors[] = {
}; };
#undef FOLLY_DETAIL_ERROR #undef FOLLY_DETAIL_ERROR
} // namespace
namespace signal_safe {
void writeStderr(const char* s, size_t len) { void writeStderr(const char* s, size_t len) {
fileutil_detail::wrapFull(write, STDERR_FILENO, const_cast<char*>(s), len); fileutil_detail::wrapFull(write, STDERR_FILENO, const_cast<char*>(s), len);
} }
void writeStderr(const char* s) { void writeStderr(const char* s) {
writeStderr(s, strlen(s)); writeStderr(s, strlen(s));
} }
void flushStderr() { void flushStderr() {
fileutil_detail::wrapNoInt(fsync, STDERR_FILENO); fileutil_detail::wrapNoInt(fsync, STDERR_FILENO);
} }
} // namespace void writeStderr(unsigned long x) {
void assertionFailure(
const char* expr,
const char* msg,
const char* file,
unsigned int line,
const char* function,
int error) {
char buf[to_ascii_size_max_decimal<uint64_t>()]; char buf[to_ascii_size_max_decimal<uint64_t>()];
writeStderr(buf, to_ascii_decimal(buf, x));
}
void writeErrBegin(const char* expr, const char* msg) {
writeStderr("\n\nAssertion failure: "); writeStderr("\n\nAssertion failure: ");
writeStderr(expr + 1, strlen(expr) - 2); writeStderr(expr + 1, strlen(expr) - 2);
writeStderr("\nMessage: "); writeStderr("\nMessage: ");
writeStderr(msg); writeStderr(msg);
}
[[noreturn]] void writeErrEnd(
const char* file, unsigned int line, const char* function, int error) {
writeStderr("\nFile: "); writeStderr("\nFile: ");
writeStderr(file); writeStderr(file);
writeStderr("\nLine: "); writeStderr("\nLine: ");
writeStderr(buf, to_ascii_decimal(buf, line)); writeStderr(line);
writeStderr("\nFunction: "); writeStderr("\nFunction: ");
writeStderr(function); writeStderr(function);
if (error) { if (error) {
...@@ -478,7 +482,7 @@ void assertionFailure( ...@@ -478,7 +482,7 @@ void assertionFailure(
// the symbolic constant is necessary since actual numbers may vary // the symbolic constant is necessary since actual numbers may vary
// for simplicity, do not attempt to mimic strerror printing descriptions // for simplicity, do not attempt to mimic strerror printing descriptions
writeStderr("\nError: "); writeStderr("\nError: ");
writeStderr(buf, to_ascii_decimal(buf, error)); writeStderr(error);
writeStderr(" ("); writeStderr(" (");
// the list is not required to be sorted; but the program is about to die // the list is not required to be sorted; but the program is about to die
auto const pred = [=](auto const e) { return e.first == error; }; auto const pred = [=](auto const e) { return e.first == error; };
...@@ -491,5 +495,5 @@ void assertionFailure( ...@@ -491,5 +495,5 @@ void assertionFailure(
abort(); abort();
} }
} // namespace detail } // namespace signal_safe
} // namespace folly } // namespace folly
...@@ -21,47 +21,53 @@ ...@@ -21,47 +21,53 @@
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/Preprocessor.h> #include <folly/Preprocessor.h>
#define FOLLY_SAFE_CHECK_IMPL(expr, expr_s, msg, error) \ #define FOLLY_SAFE_CHECK_IMPL(expr, expr_s, msg, error, ...) \
((expr) ? static_cast<void>(0) \ ((expr) ? static_cast<void>(0) \
: ::folly::detail::assertionFailure( \ : (::folly::signal_safe::writeErrBegin( \
FOLLY_PP_STRINGIZE(expr_s), \ FOLLY_PP_STRINGIZE(expr_s), (msg)), \
(msg), \ ::folly::signal_safe::writeStderrPack(__VA_ARGS__), \
__FILE__, \ ::folly::signal_safe::writeErrEnd( \
__LINE__, \ __FILE__, __LINE__, __PRETTY_FUNCTION__, error)))
__PRETTY_FUNCTION__, \
error))
/** /**
* Verify that the expression is true. If not, prints an error message * Verify that the expression is true. If not, prints an error message
* (containing msg) to stderr and abort()s. Just like CHECK(), but only * (containing msg) to stderr and abort()s. Just like CHECK(), but only
* logs to stderr and only does async-signal-safe calls. * logs to stderr and only does async-signal-safe calls.
*/ */
#define FOLLY_SAFE_CHECK(expr, msg) \ #define FOLLY_SAFE_CHECK(expr, msg, ...) \
FOLLY_SAFE_CHECK_IMPL((expr), (expr), (msg), 0) FOLLY_SAFE_CHECK_IMPL((expr), (expr), (msg), 0, __VA_ARGS__);
/** /**
* In debug mode, verify that the expression is true. Otherwise, do nothing * In debug mode, verify that the expression is true. Otherwise, do nothing
* (do not even evaluate expr). Just like DCHECK(), but only logs to stderr and * (do not even evaluate expr). Just like DCHECK(), but only logs to stderr and
* only does async-signal-safe calls. * only does async-signal-safe calls.
*/ */
#define FOLLY_SAFE_DCHECK(expr, msg) \ #define FOLLY_SAFE_DCHECK(expr, msg, ...) \
FOLLY_SAFE_CHECK_IMPL(!::folly::kIsDebug || (expr), (expr), (msg), 0) FOLLY_SAFE_CHECK_IMPL( \
!::folly::kIsDebug || (expr), (expr), (msg), 0, __VA_ARGS__)
/** /**
* Like FOLLY_SAFE_CHECK, but also prints errno. * Like FOLLY_SAFE_CHECK, but also prints errno.
*/ */
#define FOLLY_SAFE_PCHECK(expr, msg) \ #define FOLLY_SAFE_PCHECK(expr, msg, ...) \
FOLLY_SAFE_CHECK_IMPL((expr), (expr), (msg), errno) FOLLY_SAFE_CHECK_IMPL((expr), (expr), (msg), errno, __VA_ARGS__)
namespace folly { namespace folly {
namespace detail { namespace signal_safe {
[[noreturn]] void assertionFailure( void writeStderr(const char*);
const char* expr, void writeStderr(unsigned long int);
const char* msg,
const char* file, template <typename... A>
unsigned int line, void writeStderrPack(A... a) {
const char* function, // NOTE: C++14-compatible version of comma fold expression.
int error); using _ = int[];
} // namespace detail void(_{0, (writeStderr(a), 0)...});
}
void writeErrBegin(const char* expr, const char* msg);
[[noreturn]] void writeErrEnd(
const char* file, unsigned int line, const char* function, int error);
} // namespace signal_safe
} // namespace folly } // namespace folly
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment