Skip to content

Commit

Permalink
Mark the event_err() functions as __attribute__((noreturn))
Browse files Browse the repository at this point in the history
This attribute tells gcc (and anything else that understands gcc
attributes) that the functions will never return control, and helps
the optimizer a little.  With luck, it will also tell
less-than-full-program dataflow analysis tools that they don't need to
worry about any code path that involves calling one of these functions
and then returning.

This patch also forces event_exit() to always exit, no matter what the
user-supplied fatal_callback does.  This means that the old unit tests
for the event_err* functions don't work any more, since they assume it
is safe to call event_err* if you've given it a bogus fatal_callback
that doesn't exit.  Instead, we have to make the unit tests fork
before calling event_err(), and have the main unit test process wait
for the event_err() test to exit with a sane exit code.  On unix,
that's trivial.  On windows, let's not bother and just assume that
event_err* works.
  • Loading branch information
nmathewson committed May 13, 2010
1 parent dfb75ab commit 33bbbed
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 30 deletions.
8 changes: 5 additions & 3 deletions log-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,19 @@

#ifdef __GNUC__
#define EV_CHECK_FMT(a,b) __attribute__((format(printf, a, b)))
#define EV_NORETURN __attribute__((noreturn))
#else
#define EV_CHECK_FMT(a,b)
#define EV_NORETURN
#endif

#define _EVENT_ERR_ABORT 0xdeaddead

void event_err(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3);
void event_err(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3) EV_NORETURN;
void event_warn(const char *fmt, ...) EV_CHECK_FMT(1,2);
void event_sock_err(int eval, evutil_socket_t sock, const char *fmt, ...) EV_CHECK_FMT(3,4);
void event_sock_err(int eval, evutil_socket_t sock, const char *fmt, ...) EV_CHECK_FMT(3,4) EV_NORETURN;
void event_sock_warn(evutil_socket_t sock, const char *fmt, ...) EV_CHECK_FMT(2,3);
void event_errx(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3);
void event_errx(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3) EV_NORETURN;
void event_warnx(const char *fmt, ...) EV_CHECK_FMT(1,2);
void event_msgx(const char *fmt, ...) EV_CHECK_FMT(1,2);
void _event_debugx(const char *fmt, ...) EV_CHECK_FMT(1,2);
Expand Down
6 changes: 4 additions & 2 deletions log.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
static void _warn_helper(int severity, const char *errstr, const char *fmt,
va_list ap);
static void event_log(int severity, const char *msg);
static void event_exit(int errcode) EV_NORETURN;

static event_fatal_cb fatal_fn = NULL;

Expand All @@ -71,9 +72,10 @@ event_set_fatal_callback(event_fatal_cb cb)
static void
event_exit(int errcode)
{
if (fatal_fn)
if (fatal_fn) {
fatal_fn(errcode);
else if (errcode == _EVENT_ERR_ABORT)
exit(errcode); /* should never be reached */
} else if (errcode == _EVENT_ERR_ABORT)
abort();
else
exit(errcode);
Expand Down
98 changes: 73 additions & 25 deletions test/regress_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,15 +423,68 @@ logfn(int severity, const char *msg)
logmsg = strdup(msg);
}

static int exited = 0;
static int exitcode = 0;
static int fatal_want_severity = 0;
static const char *fatal_want_message = NULL;
static void
fatalfn(int c)
fatalfn(int exitcode)
{
exited = 1;
exitcode = c;
if (logsev != fatal_want_severity ||
!logmsg ||
strcmp(logmsg, fatal_want_message))
exit(0);
else
exit(exitcode);
}

#ifndef WIN32
#define CAN_CHECK_ERR
static void
check_error_logging(void (*fn)(void), int wantexitcode,
int wantseverity, const char *wantmsg)
{
pid_t pid;
int status = 0, exitcode;
fatal_want_severity = wantseverity;
fatal_want_message = wantmsg;
if ((pid = fork()) == 0) {
/* child process */
fn();
exit(0); /* should be unreachable. */
} else {
wait(&status);
exitcode = WEXITSTATUS(status);
tt_int_op(wantexitcode, ==, exitcode);
}
end:
;
}

static void
errx_fn(void)
{
event_errx(2, "Fatal error; too many kumquats (%d)", 5);
}

static void
err_fn(void)
{
errno = ENOENT;
event_err(5,"Couldn't open %s", "/very/bad/file");
}

static void
sock_err_fn(void)
{
evutil_socket_t fd = socket(AF_INET, SOCK_STREAM, 0);
#ifdef WIN32
EVUTIL_SET_SOCKET_ERROR(WSAEWOULDBLOCK);
#else
errno = EAGAIN;
#endif
event_sock_err(20, fd, "Unhappy socket");
}
#endif

static void
test_evutil_log(void *ptr)
{
Expand All @@ -441,7 +494,7 @@ test_evutil_log(void *ptr)
event_set_log_callback(logfn);
event_set_fatal_callback(fatalfn);
#define RESET() do { \
logsev = exited = exitcode = 0; \
logsev = 0; \
if (logmsg) free(logmsg); \
logmsg = NULL; \
} while (0)
Expand All @@ -451,19 +504,22 @@ test_evutil_log(void *ptr)
tt_str_op(logmsg,==,msg); \
} while (0)

event_errx(2, "Fatal error; too many kumquats (%d)", 5);
LOGEQ(_EVENT_LOG_ERR, "Fatal error; too many kumquats (5)");
tt_int_op(exitcode,==,2);
#ifdef CAN_CHECK_ERR
/* We need to disable these tests for now. Previously, the logging
* module didn't enforce the requirement that a fatal callback
* actually exit. Now, it exits no matter what, so if we wan to
* reinstate these tests, we'll need to fork for each one. */
check_error_logging(errx_fn, 2, _EVENT_LOG_ERR,
"Fatal error; too many kumquats (5)");
RESET();
#endif

event_warnx("Far too many %s (%d)", "wombats", 99);
LOGEQ(_EVENT_LOG_WARN, "Far too many wombats (99)");
tt_int_op(exited,==,0);
RESET();

event_msgx("Connecting lime to coconut");
LOGEQ(_EVENT_LOG_MSG, "Connecting lime to coconut");
tt_int_op(exited,==,0);
RESET();

event_debug(("A millisecond passed! We should log that!"));
Expand All @@ -481,16 +537,14 @@ test_evutil_log(void *ptr)
evutil_snprintf(buf, sizeof(buf),
"Couldn't open /bad/file: %s",strerror(ENOENT));
LOGEQ(_EVENT_LOG_WARN,buf);
tt_int_op(exited, ==, 0);
RESET();

errno = ENOENT;
event_err(5,"Couldn't open %s", "/very/bad/file");
#ifdef CAN_CHECK_ERR
evutil_snprintf(buf, sizeof(buf),
"Couldn't open /very/bad/file: %s",strerror(ENOENT));
LOGEQ(_EVENT_LOG_ERR,buf);
tt_int_op(exitcode, ==, 5);
check_error_logging(err_fn, 5, _EVENT_LOG_ERR, buf);
RESET();
#endif

/* Try with a socket errno. */
fd = socket(AF_INET, SOCK_STREAM, 0);
Expand All @@ -506,18 +560,12 @@ test_evutil_log(void *ptr)
#endif
event_sock_warn(fd, "Unhappy socket");
LOGEQ(_EVENT_LOG_WARN, buf);
tt_int_op(exited,==,0);
RESET();

#ifdef WIN32
EVUTIL_SET_SOCKET_ERROR(WSAEWOULDBLOCK);
#else
errno = EAGAIN;
#endif
event_sock_err(200, fd, "Unhappy socket");
LOGEQ(_EVENT_LOG_ERR, buf);
tt_int_op(exitcode,==,200);
#ifdef CAN_CHECK_ERR
check_error_logging(sock_err_fn, 20, _EVENT_LOG_ERR, buf);
RESET();
#endif

#undef RESET
#undef LOGEQ
Expand Down

0 comments on commit 33bbbed

Please sign in to comment.