util: Introduce ovs_assert macro.
authorBen Pfaff <blp@nicira.com>
Thu, 17 Jan 2013 00:03:03 +0000 (16:03 -0800)
committerBen Pfaff <blp@nicira.com>
Thu, 17 Jan 2013 00:03:03 +0000 (16:03 -0800)
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 <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
lib/compiler.h
lib/util.c
lib/util.h
tests/library.at
tests/test-util.c

index 27612a7..760389d 100644 (file)
@@ -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
index c90d556..83d3ff8 100644 (file)
@@ -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 <config.h>
 #include "util.h"
-#include <assert.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdarg.h>
@@ -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."
index 70f3691..b944ec7 100644 (file)
 #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.
  *
index 0765c3f..55adcfb 100644 (file)
@@ -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
index b98fc79..3eecc7a 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <config.h>
 
+#include <getopt.h>
 #include <inttypes.h>
 #include <limits.h>
 #include <stdio.h>
@@ -25,6 +26,7 @@
 #include "command-line.h"
 #include "random.h"
 #include "util.h"
+#include "vlog.h"
 
 #undef NDEBUG
 #include <assert.h>
@@ -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);
+}
 \f
 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;
 }