From: Ben Pfaff Date: Thu, 17 Jan 2013 00:03:03 +0000 (-0800) Subject: util: Introduce ovs_assert macro. X-Git-Tag: sliver-openvswitch-1.9.90-3~8^2~24 X-Git-Url: http://git.onelab.eu/?p=sliver-openvswitch.git;a=commitdiff_plain;h=4749f73d12c844b318af7f45cf45e1acac9f7c08 util: Introduce ovs_assert macro. An occasionally significant problem with the standard "assert" macro is that it writes the failure message to stderr. In our daemons, stderr is generally redirected to /dev/null. It's more useful to write the failure message to the log, which is what the new ovs_assert macro introduced in this patch does. Signed-off-by: Ben Pfaff Acked-by: Ethan Jackson --- diff --git a/lib/compiler.h b/lib/compiler.h index 27612a74a..760389d58 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -26,6 +26,8 @@ #define ALWAYS_INLINE __attribute__((always_inline)) #define WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) #define SENTINEL(N) __attribute__((sentinel(N))) +#define OVS_LIKELY(CONDITION) __builtin_expect(!!(CONDITION), 1) +#define OVS_UNLIKELY(CONDITION) __builtin_expect(!!(CONDITION), 0) #else #define NO_RETURN #define OVS_UNUSED @@ -35,6 +37,8 @@ #define ALWAYS_INLINE #define WARN_UNUSED_RESULT #define SENTINEL(N) +#define OVS_LIKELY(CONDITION) (!!(CONDITION)) +#define OVS_UNLIKELY(CONDITION) (!!(CONDITION)) #endif /* ISO C says that a C implementation may choose any integer type for an enum diff --git a/lib/util.c b/lib/util.c index c90d556c3..83d3ff802 100644 --- a/lib/util.c +++ b/lib/util.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ #include #include "util.h" -#include #include #include #include @@ -45,6 +44,30 @@ const char *subprogram_name = ""; /* --version option output. */ static char *program_version; +void +ovs_assert_failure(const char *where, const char *function, + const char *condition) +{ + /* Prevent an infinite loop (or stack overflow) in case VLOG_ABORT happens + * to trigger an assertion failure of its own. */ + static int reentry = 0; + + switch (reentry++) { + case 0: + VLOG_ABORT("%s: assertion %s failed in %s()", + where, condition, function); + NOT_REACHED(); + + case 1: + fprintf(stderr, "%s: assertion %s failed in %s()", + where, condition, function); + abort(); + + default: + abort(); + } +} + void out_of_memory(void) { @@ -756,7 +779,7 @@ english_list_delimiter(size_t index, size_t total) int log_2_floor(uint32_t n) { - assert(n); + ovs_assert(n); #if !defined(UINT_MAX) || !defined(UINT32_MAX) #error "Someone screwed up the #includes." diff --git a/lib/util.h b/lib/util.h index 70f36919a..b944ec793 100644 --- a/lib/util.h +++ b/lib/util.h @@ -62,6 +62,17 @@ #define BUILD_ASSERT_DECL_GCCONLY(EXPR) ((void) 0) #endif +/* Like the standard assert macro, except: + * + * - Writes the failure message to the log. + * + * - Not affected by NDEBUG. */ +#define ovs_assert(CONDITION) \ + if (!OVS_LIKELY(CONDITION)) { \ + ovs_assert_failure(SOURCE_LOCATOR, __func__, #CONDITION); \ + } +void ovs_assert_failure(const char *, const char *, const char *) NO_RETURN; + /* Casts 'pointer' to 'type' and issues a compiler warning if the cast changes * anything other than an outermost "const" or "volatile" qualifier. * diff --git a/tests/library.at b/tests/library.at index 0765c3f3f..55adcfbf2 100644 --- a/tests/library.at +++ b/tests/library.at @@ -140,3 +140,22 @@ mkdir 01234567890123456789012345678901234567890123456789012345678901234567890123 cd 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 AT_CHECK([test-unix-socket ../012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/socket socket]) AT_CLEANUP + +AT_SETUP([ovs_assert]) +OVS_LOGDIR=`pwd`; export OVS_LOGDIR +AT_CHECK([test-util -voff -vfile:info '-vPATTERN:file:%c|%p|%m' --log-file assert || kill -l $?], + [0], [ABRT +], [stderr]) + +AT_CHECK([sed 's/\(opened log file\) .*/\1/ +s/|[[^|]]*: /|/' test-util.log], [0], [dnl +vlog|INFO|opened log file +util|EMER|assertion false failed in test_assert() +]) + +AT_CHECK([sed 's/.*: // +1q' stderr], [0], + [assertion false failed in test_assert() +]) + +AT_CLEANUP diff --git a/tests/test-util.c b/tests/test-util.c index b98fc79aa..3eecc7a4c 100644 --- a/tests/test-util.c +++ b/tests/test-util.c @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -25,6 +26,7 @@ #include "command-line.h" #include "random.h" #include "util.h" +#include "vlog.h" #undef NDEBUG #include @@ -335,6 +337,12 @@ test_follow_symlinks(int argc, char *argv[]) free(target); } } + +static void +test_assert(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + ovs_assert(false); +} static const struct command commands[] = { {"ctz", 0, 0, test_ctz}, @@ -345,13 +353,46 @@ static const struct command commands[] = { {"bitwise_one", 0, 0, test_bitwise_one}, {"bitwise_is_all_zeros", 0, 0, test_bitwise_is_all_zeros}, {"follow-symlinks", 1, INT_MAX, test_follow_symlinks}, + {"assert", 0, 0, test_assert}, {NULL, 0, 0, NULL}, }; +static void +parse_options(int argc, char *argv[]) +{ + enum { + VLOG_OPTION_ENUMS + }; + static struct option long_options[] = { + VLOG_LONG_OPTIONS, + {NULL, 0, NULL, 0}, + }; + char *short_options = long_options_to_short_options(long_options); + + for (;;) { + int c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c == -1) { + break; + } + + switch (c) { + VLOG_OPTION_HANDLERS + + case '?': + exit(EXIT_FAILURE); + + default: + abort(); + } + } + free(short_options); +} + int main(int argc, char *argv[]) { set_program_name(argv[0]); - run_command(argc - 1, argv + 1, commands); + parse_options(argc, argv); + run_command(argc - optind, argv + optind, commands); return 0; }