Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F174635657
D49973.id154401.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
D49973.id154401.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D49973: queue(3): Debug macros: Finer control knobs, userland support
Attached
Detach File
Event Timeline
Log In to Comment