Page MenuHomeFreeBSD

D49973.id154401.diff
No OneTemporary

D49973.id154401.diff

diff --git a/share/man/man3/queue.3 b/share/man/man3/queue.3
--- a/share/man/man3/queue.3
+++ b/share/man/man3/queue.3
@@ -1390,49 +1390,87 @@
TAILQ_INIT(&head);
.Ed
.Sh DIAGNOSTICS
-When debugging
-.Nm queue(3) ,
-it can be useful to trace queue changes.
-To enable tracing, define the macro
-.Va QUEUE_MACRO_DEBUG_TRACE
-at compile time.
+.Nm queue(3)
+provides several diagnostic and debugging facilities.
.Pp
-It can also be useful to trash pointers that have been unlinked from a queue,
-to detect use after removal.
-To enable pointer trashing, define the macro
-.Va QUEUE_MACRO_DEBUG_TRASH
-at compile time.
-The macro
-.Fn QMD_IS_TRASHED "void *ptr"
-returns true if
-.Fa ptr
-has been trashed by the
-.Va QUEUE_MACRO_DEBUG_TRASH
-option.
+Check code that performs basic integrity and API conformance checks is
+automatically inserted when using queue macros in the kernel if compiling it
+with
+.Va INVARIANTS .
+One can request insertion or elision of check code by respectively defining one
+of the macros
+.Va QUEUE_MACRO_DEBUG_ASSERTIONS
+or
+.Va QUEUE_MACRO_NO_DEBUG_ASSERTIONS
+before first inclusion of
+.In sys/queue.h .
+When check code encounters an anomaly, it panics the kernel or aborts the
+program.
+To this end, in the kernel or in
+.Va _STANDALONE
+builds, it by default calls
+.Fn panic ,
+while in userland builds it prints the diagnostic message on
+.Dv stderr
+and then calls
+.Fn abort .
+These behaviors can be overriden by defining a custom
+.Fn QMD_PANIC
+macro before first inclusion of
+.In sys/queue.h .
+The diagnostic messages automatically include the source file, line and function
+where the failing check occured.
+This behavior can be overriden by defining a custom
+.Fn QMD_ASSERT
+macro before first inclusion of
+.In sys/queue.h .
.Pp
-In the kernel (with
-.Va INVARIANTS
-enabled), the
+The
.Fn SLIST_REMOVE_PREVPTR
macro is available to aid debugging:
.Bl -hang -offset indent
.It Fn SLIST_REMOVE_PREVPTR "TYPE **prev" "TYPE *elm" "SLIST_ENTRY NAME"
.Pp
-Removes
+Removes element
.Fa elm ,
which must directly follow the element whose
.Va &SLIST_NEXT()
is
.Fa prev ,
-from the SLIST.
-This macro validates that
+from the list.
+This macro may insert, under conditions detailed above, check code that
+validates that
.Fa elm
-follows
+indeed follows
.Fa prev
-in
-.Va INVARIANTS
-mode.
+in the list
+.Po
+through the
+.Fn QMD_SLIST_CHECK_PREVPTR
+macro
+.Pc .
.El
+.Pp
+When debugging, it can be useful to trace queue changes.
+To enable tracing, define the macro
+.Va QUEUE_MACRO_DEBUG_TRACE .
+Note that, at the moment, only macros for regular tail queues have been
+instrumented.
+.Pp
+It can also be useful to trash pointers that have been unlinked from a queue,
+to detect use after removal.
+To enable pointer trashing, define the macro
+.Va QUEUE_MACRO_DEBUG_TRASH
+at compile time.
+Note that, at the moment, only a limited number of macros have been
+instrumented.
+The macro
+.Fn QMD_IS_TRASHED "void *ptr"
+returns true if
+.Fa ptr
+has been trashed by the
+.Va QUEUE_MACRO_DEBUG_TRASH
+option.
.Sh SEE ALSO
.Xr arb 3 ,
.Xr tree 3
diff --git a/sys/sys/queue.h b/sys/sys/queue.h
--- a/sys/sys/queue.h
+++ b/sys/sys/queue.h
@@ -116,11 +116,10 @@
*
*/
#ifdef QUEUE_MACRO_DEBUG
-#warn Use QUEUE_MACRO_DEBUG_TRACE and/or QUEUE_MACRO_DEBUG_TRASH
+#warn Use QUEUE_MACRO_DEBUG_xxx instead (TRACE, TRASH and/or ASSERTIONS)
#define QUEUE_MACRO_DEBUG_TRACE
#define QUEUE_MACRO_DEBUG_TRASH
#endif
-
#ifdef QUEUE_MACRO_DEBUG_TRACE
/* Store the last 2 places the queue element or head was altered */
struct qm_trace {
@@ -164,6 +163,62 @@
#define QMD_IS_TRASHED(x) 0
#endif /* QUEUE_MACRO_DEBUG_TRASH */
+#if defined(QUEUE_MACRO_DEBUG_ASSERTIONS) && \
+ defined(QUEUE_MACRO_NO_DEBUG_ASSERTIONS)
+#error Both QUEUE_MACRO_DEBUG_ASSERTIONS and QUEUE_MACRO_NO_DEBUG_ASSERTIONS defined
+#endif
+
+/*
+ * Automatically define QUEUE_MACRO_DEBUG_ASSERTIONS when compiling the kernel
+ * with INVARIANTS, if not already defined and not prevented by presence of
+ * QUEUE_MACRO_NO_DEBUG_ASSERTIONS.
+ */
+#if !defined(QUEUE_MACRO_DEBUG_ASSERTIONS) && \
+ !defined(QUEUE_MACRO_NO_DEBUG_ASSERTIONS) && \
+ (defined(_KERNEL) && defined(INVARIANTS))
+#define QUEUE_MACRO_DEBUG_ASSERTIONS
+#endif
+
+/*
+ * If queue assertions are enabled, provide default definitions for QMD_PANIC()
+ * and QMD_ASSERT() if undefined.
+ */
+#ifdef QUEUE_MACRO_DEBUG_ASSERTIONS
+#ifndef QMD_PANIC
+#if defined(_KERNEL) || defined(_STANDALONE)
+/*
+ * On _STANDALONE, either <stand.h> or the headers using <sys/queue.h> provide
+ * a declaration or macro for panic().
+ */
+#ifdef _KERNEL
+#include <sys/kassert.h>
+#endif
+#define QMD_PANIC(fmt, ...) do { \
+ panic(fmt, ##__VA_ARGS__); \
+} while (0)
+#else /* !(_KERNEL || _STANDALONE) */
+#include <stdio.h>
+#include <stdlib.h>
+#define QMD_PANIC(fmt, ...) do { \
+ fprintf(stderr, fmt "\n", ##__VA_ARGS__); \
+ abort(); \
+} while (0)
+#endif /* _KERNEL || _STANDALONE */
+#endif /* !QMD_PANIC */
+
+#ifndef QMD_ASSERT
+#define QMD_ASSERT(expression, fmt, ...) do { \
+ if (!(expression)) \
+ QMD_PANIC("%s:%u: %s: " fmt, \
+ __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
+} while (0)
+#endif /* !QMD_ASSERT */
+#else /* !QUEUE_MACRO_DEBUG_ASSERTIONS */
+#undef QMD_ASSERT
+#define QMD_ASSERT(test, fmt, ...) do {} while (0)
+#endif /* QUEUE_MACRO_DEBUG_ASSERTIONS */
+
+
#ifdef __cplusplus
/*
* In C++ there can be structure lists and class lists:
@@ -176,6 +231,7 @@
/*
* Singly-linked List declarations.
*/
+
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
@@ -202,27 +258,19 @@
/*
* Singly-linked List functions.
*/
-#if (defined(_KERNEL) && defined(INVARIANTS))
-#define QMD_SLIST_CHECK_PREVPTR(prevp, elm) do { \
- if (*(prevp) != (elm)) \
- panic("Bad prevptr *(%p) == %p != %p", \
- (prevp), *(prevp), (elm)); \
-} while (0)
-#define SLIST_ASSERT_EMPTY(head) do { \
- if (!SLIST_EMPTY((head))) \
- panic("%s: slist %p is not empty", __func__, (head)); \
-} while (0)
+#define QMD_SLIST_CHECK_PREVPTR(prevp, elm) \
+ QMD_ASSERT(*(prevp) == (elm), \
+ "Bad prevptr *(%p) == %p != %p", \
+ (prevp), *(prevp), (elm))
-#define SLIST_ASSERT_NONEMPTY(head) do { \
- if (SLIST_EMPTY((head))) \
- panic("%s: slist %p is empty", __func__, (head)); \
-} while (0)
-#else
-#define QMD_SLIST_CHECK_PREVPTR(prevp, elm)
-#define SLIST_ASSERT_EMPTY(head)
-#define SLIST_ASSERT_NONEMPTY(head)
-#endif
+#define SLIST_ASSERT_EMPTY(head) \
+ QMD_ASSERT(SLIST_EMPTY((head)), \
+ "slist %p is not empty", (head))
+
+#define SLIST_ASSERT_NONEMPTY(head) \
+ QMD_ASSERT(!SLIST_EMPTY((head)), \
+ "slist %p is empty", (head))
#define SLIST_CONCAT(head1, head2, type, field) do { \
QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head1); \
@@ -333,6 +381,7 @@
/*
* Singly-linked Tail queue declarations.
*/
+
#define STAILQ_HEAD(name, type) \
struct name { \
struct type *stqh_first;/* first element */ \
@@ -361,46 +410,36 @@
/*
* Singly-linked Tail queue functions.
*/
-#if (defined(_KERNEL) && defined(INVARIANTS))
+
/*
* QMD_STAILQ_CHECK_EMPTY(STAILQ_HEAD *head)
*
* Validates that the stailq head's pointer to the last element's next pointer
* actually points to the head's first element pointer field.
*/
-#define QMD_STAILQ_CHECK_EMPTY(head) do { \
- if ((head)->stqh_last != &(head)->stqh_first) \
- panic("Empty stailq %p->stqh_last is %p, not head's " \
- "first field address", (head), (head)->stqh_last); \
-} while (0)
+#define QMD_STAILQ_CHECK_EMPTY(head) \
+ QMD_ASSERT((head)->stqh_last == &(head)->stqh_first, \
+ "Empty stailq %p->stqh_last is %p, " \
+ "not head's first field address", \
+ (head), (head)->stqh_last)
/*
* QMD_STAILQ_CHECK_TAIL(STAILQ_HEAD *head)
*
* Validates that the stailq's last element's next pointer is NULL.
*/
-#define QMD_STAILQ_CHECK_TAIL(head) do { \
- if (*(head)->stqh_last != NULL) \
- panic("Stailq %p last element's next pointer is %p, " \
- "not NULL", (head), *(head)->stqh_last); \
-} while (0)
-
-#define STAILQ_ASSERT_EMPTY(head) do { \
- if (!STAILQ_EMPTY((head))) \
- panic("%s: stailq %p is not empty", __func__, (head)); \
-} while (0)
+#define QMD_STAILQ_CHECK_TAIL(head) \
+ QMD_ASSERT(*(head)->stqh_last == NULL, \
+ "Stailq %p last element's next pointer is " \
+ "%p, not NULL", (head), *(head)->stqh_last)
-#define STAILQ_ASSERT_NONEMPTY(head) do { \
- if (STAILQ_EMPTY((head))) \
- panic("%s: stailq %p is empty", __func__, (head)); \
-} while (0)
+#define STAILQ_ASSERT_EMPTY(head) \
+ QMD_ASSERT(STAILQ_EMPTY((head)), \
+ "stailq %p is not empty", (head))
-#else
-#define QMD_STAILQ_CHECK_EMPTY(head)
-#define QMD_STAILQ_CHECK_TAIL(head)
-#define STAILQ_ASSERT_EMPTY(head)
-#define STAILQ_ASSERT_NONEMPTY(head)
-#endif /* _KERNEL && INVARIANTS */
+#define STAILQ_ASSERT_NONEMPTY(head) \
+ QMD_ASSERT(!STAILQ_EMPTY((head)), \
+ "stailq %p is empty", (head))
#define STAILQ_CONCAT(head1, head2) do { \
if (!STAILQ_EMPTY((head2))) { \
@@ -531,6 +570,7 @@
/*
* List declarations.
*/
+
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
@@ -560,19 +600,18 @@
* List functions.
*/
-#if (defined(_KERNEL) && defined(INVARIANTS))
/*
* QMD_LIST_CHECK_HEAD(LIST_HEAD *head, LIST_ENTRY NAME)
*
* If the list is non-empty, validates that the first element of the list
* points back at 'head.'
*/
-#define QMD_LIST_CHECK_HEAD(head, field) do { \
- if (LIST_FIRST((head)) != NULL && \
- LIST_FIRST((head))->field.le_prev != \
- &LIST_FIRST((head))) \
- panic("Bad list head %p first->prev != head", (head)); \
-} while (0)
+#define QMD_LIST_CHECK_HEAD(head, field) \
+ QMD_ASSERT(LIST_FIRST((head)) == NULL || \
+ LIST_FIRST((head))->field.le_prev == \
+ &LIST_FIRST((head)), \
+ "Bad list head %p first->prev != head", \
+ (head))
/*
* QMD_LIST_CHECK_NEXT(TYPE *elm, LIST_ENTRY NAME)
@@ -580,39 +619,28 @@
* If an element follows 'elm' in the list, validates that the next element
* points back at 'elm.'
*/
-#define QMD_LIST_CHECK_NEXT(elm, field) do { \
- if (LIST_NEXT((elm), field) != NULL && \
- LIST_NEXT((elm), field)->field.le_prev != \
- &((elm)->field.le_next)) \
- panic("Bad link elm %p next->prev != elm", (elm)); \
-} while (0)
+#define QMD_LIST_CHECK_NEXT(elm, field) \
+ QMD_ASSERT(LIST_NEXT((elm), field) == NULL || \
+ LIST_NEXT((elm), field)->field.le_prev == \
+ &((elm)->field.le_next), \
+ "Bad link elm %p next->prev != elm", (elm))
/*
* QMD_LIST_CHECK_PREV(TYPE *elm, LIST_ENTRY NAME)
*
* Validates that the previous element (or head of the list) points to 'elm.'
*/
-#define QMD_LIST_CHECK_PREV(elm, field) do { \
- if (*(elm)->field.le_prev != (elm)) \
- panic("Bad link elm %p prev->next != elm", (elm)); \
-} while (0)
+#define QMD_LIST_CHECK_PREV(elm, field) \
+ QMD_ASSERT(*(elm)->field.le_prev == (elm), \
+ "Bad link elm %p prev->next != elm", (elm))
-#define LIST_ASSERT_EMPTY(head) do { \
- if (!LIST_EMPTY((head))) \
- panic("%s: list %p is not empty", __func__, (head)); \
-} while (0)
+#define LIST_ASSERT_EMPTY(head) \
+ QMD_ASSERT(LIST_EMPTY((head)), \
+ "list %p is not empty", (head))
-#define LIST_ASSERT_NONEMPTY(head) do { \
- if (LIST_EMPTY((head))) \
- panic("%s: list %p is empty", __func__, (head)); \
-} while (0)
-#else
-#define QMD_LIST_CHECK_HEAD(head, field)
-#define QMD_LIST_CHECK_NEXT(elm, field)
-#define QMD_LIST_CHECK_PREV(elm, field)
-#define LIST_ASSERT_EMPTY(head)
-#define LIST_ASSERT_NONEMPTY(head)
-#endif /* (_KERNEL && INVARIANTS) */
+#define LIST_ASSERT_NONEMPTY(head) \
+ QMD_ASSERT(!LIST_EMPTY((head)), \
+ "list %p is empty", (head))
#define LIST_CONCAT(head1, head2, type, field) do { \
QUEUE_TYPEOF(type) *curelm = LIST_FIRST(head1); \
@@ -753,6 +781,7 @@
/*
* Tail queue declarations.
*/
+
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
@@ -787,29 +816,29 @@
/*
* Tail queue functions.
*/
-#if (defined(_KERNEL) && defined(INVARIANTS))
+
/*
* QMD_TAILQ_CHECK_HEAD(TAILQ_HEAD *head, TAILQ_ENTRY NAME)
*
* If the tailq is non-empty, validates that the first element of the tailq
* points back at 'head.'
*/
-#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
- if (!TAILQ_EMPTY(head) && \
- TAILQ_FIRST((head))->field.tqe_prev != \
- &TAILQ_FIRST((head))) \
- panic("Bad tailq head %p first->prev != head", (head)); \
-} while (0)
+#define QMD_TAILQ_CHECK_HEAD(head, field) \
+ QMD_ASSERT(TAILQ_EMPTY(head) || \
+ TAILQ_FIRST((head))->field.tqe_prev == \
+ &TAILQ_FIRST((head)), \
+ "Bad tailq head %p first->prev != head", \
+ (head))
/*
* QMD_TAILQ_CHECK_TAIL(TAILQ_HEAD *head, TAILQ_ENTRY NAME)
*
* Validates that the tail of the tailq is a pointer to pointer to NULL.
*/
-#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
- if (*(head)->tqh_last != NULL) \
- panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
-} while (0)
+#define QMD_TAILQ_CHECK_TAIL(head, field) \
+ QMD_ASSERT(*(head)->tqh_last == NULL, \
+ "Bad tailq NEXT(%p->tqh_last) != NULL", \
+ (head))
/*
* QMD_TAILQ_CHECK_NEXT(TYPE *elm, TAILQ_ENTRY NAME)
@@ -817,40 +846,28 @@
* If an element follows 'elm' in the tailq, validates that the next element
* points back at 'elm.'
*/
-#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
- if (TAILQ_NEXT((elm), field) != NULL && \
- TAILQ_NEXT((elm), field)->field.tqe_prev != \
- &((elm)->field.tqe_next)) \
- panic("Bad link elm %p next->prev != elm", (elm)); \
-} while (0)
+#define QMD_TAILQ_CHECK_NEXT(elm, field) \
+ QMD_ASSERT(TAILQ_NEXT((elm), field) == NULL || \
+ TAILQ_NEXT((elm), field)->field.tqe_prev == \
+ &((elm)->field.tqe_next), \
+ "Bad link elm %p next->prev != elm", (elm))
/*
* QMD_TAILQ_CHECK_PREV(TYPE *elm, TAILQ_ENTRY NAME)
*
* Validates that the previous element (or head of the tailq) points to 'elm.'
*/
-#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
- if (*(elm)->field.tqe_prev != (elm)) \
- panic("Bad link elm %p prev->next != elm", (elm)); \
-} while (0)
+#define QMD_TAILQ_CHECK_PREV(elm, field) \
+ QMD_ASSERT(*(elm)->field.tqe_prev == (elm), \
+ "Bad link elm %p prev->next != elm", (elm))
-#define TAILQ_ASSERT_EMPTY(head) do { \
- if (!TAILQ_EMPTY((head))) \
- panic("%s: tailq %p is not empty", __func__, (head)); \
-} while (0)
+#define TAILQ_ASSERT_EMPTY(head) \
+ QMD_ASSERT(TAILQ_EMPTY((head)), \
+ "tailq %p is not empty", (head))
-#define TAILQ_ASSERT_NONEMPTY(head) do { \
- if (TAILQ_EMPTY((head))) \
- panic("%s: tailq %p is empty", __func__, (head)); \
-} while (0)
-#else
-#define QMD_TAILQ_CHECK_HEAD(head, field)
-#define QMD_TAILQ_CHECK_TAIL(head, headname)
-#define QMD_TAILQ_CHECK_NEXT(elm, field)
-#define QMD_TAILQ_CHECK_PREV(elm, field)
-#define TAILQ_ASSERT_EMPTY(head)
-#define TAILQ_ASSERT_NONEMPTY(head)
-#endif /* (_KERNEL && INVARIANTS) */
+#define TAILQ_ASSERT_NONEMPTY(head) \
+ QMD_ASSERT(!TAILQ_EMPTY((head)), \
+ "tailq %p is empty", (head))
#define TAILQ_CONCAT(head1, head2, field) do { \
if (!TAILQ_EMPTY(head2)) { \

File Metadata

Mime Type
text/plain
Expires
Mon, Oct 5, 8:19 PM (14 h, 43 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40257949
Default Alt Text
D49973.id154401.diff (15 KB)

Event Timeline