Browse Source

Enhance CCAN_LIST_DEBUG to report original caller

Simpler reimplementation of SS's patch; just plumb file and line through
inline functions in header.  We add a new check, which actually tests
these, and fix _info which missed ccan/check_type as a dependency.

Based-on-the-true-story-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Rusty Russell 12 years ago
parent
commit
ecf907f7e6
3 changed files with 127 additions and 25 deletions
  1. 2 0
      ccan/list/_info
  2. 66 25
      ccan/list/list.h
  3. 59 0
      ccan/list/test/run-CCAN_LIST_DEBUG.c

+ 2 - 0
ccan/list/_info

@@ -62,7 +62,9 @@ int main(int argc, char *argv[])
 		return 1;
 		return 1;
 
 
 	if (strcmp(argv[1], "depends") == 0) {
 	if (strcmp(argv[1], "depends") == 0) {
+		printf("ccan/str\n");
 		printf("ccan/container_of\n");
 		printf("ccan/container_of\n");
+		printf("ccan/check_type\n");
 		return 0;
 		return 0;
 	}
 	}
 
 

+ 66 - 25
ccan/list/list.h

@@ -1,8 +1,10 @@
 /* Licensed under BSD-MIT - see LICENSE file for details */
 /* Licensed under BSD-MIT - see LICENSE file for details */
 #ifndef CCAN_LIST_H
 #ifndef CCAN_LIST_H
 #define CCAN_LIST_H
 #define CCAN_LIST_H
+//#define CCAN_LIST_DEBUG 1
 #include <stdbool.h>
 #include <stdbool.h>
 #include <assert.h>
 #include <assert.h>
+#include <ccan/str/str.h>
 #include <ccan/container_of/container_of.h>
 #include <ccan/container_of/container_of.h>
 #include <ccan/check_type/check_type.h>
 #include <ccan/check_type/check_type.h>
 
 
@@ -88,12 +90,13 @@ struct list_head *list_check(const struct list_head *h, const char *abortstr);
 struct list_node *list_check_node(const struct list_node *n,
 struct list_node *list_check_node(const struct list_node *n,
 				  const char *abortstr);
 				  const char *abortstr);
 
 
+#define LIST_LOC __FILE__  ":" stringify(__LINE__)
 #ifdef CCAN_LIST_DEBUG
 #ifdef CCAN_LIST_DEBUG
-#define list_debug(h) list_check((h), __func__)
-#define list_debug_node(n) list_check_node((n), __func__)
+#define list_debug(h, loc) list_check((h), loc)
+#define list_debug_node(n, loc) list_check_node((n), loc)
 #else
 #else
-#define list_debug(h) (h)
-#define list_debug_node(n) (n)
+#define list_debug(h, loc) (h)
+#define list_debug_node(n, loc) (n)
 #endif
 #endif
 
 
 /**
 /**
@@ -155,13 +158,16 @@ static inline void list_head_init(struct list_head *h)
  *	list_add(&parent->children, &child->list);
  *	list_add(&parent->children, &child->list);
  *	parent->num_children++;
  *	parent->num_children++;
  */
  */
-static inline void list_add(struct list_head *h, struct list_node *n)
+#define list_add(h, n) list_add_(h, n, LIST_LOC)
+static inline void list_add_(struct list_head *h,
+			     struct list_node *n,
+			     const char *abortstr)
 {
 {
 	n->next = h->n.next;
 	n->next = h->n.next;
 	n->prev = &h->n;
 	n->prev = &h->n;
 	h->n.next->prev = n;
 	h->n.next->prev = n;
 	h->n.next = n;
 	h->n.next = n;
-	(void)list_debug(h);
+	(void)list_debug(h, abortstr);
 }
 }
 
 
 /**
 /**
@@ -174,13 +180,16 @@ static inline void list_add(struct list_head *h, struct list_node *n)
  *	list_add_tail(&parent->children, &child->list);
  *	list_add_tail(&parent->children, &child->list);
  *	parent->num_children++;
  *	parent->num_children++;
  */
  */
-static inline void list_add_tail(struct list_head *h, struct list_node *n)
+#define list_add_tail(h, n) list_add_tail_(h, n, LIST_LOC)
+static inline void list_add_tail_(struct list_head *h,
+				  struct list_node *n,
+				  const char *abortstr)
 {
 {
 	n->next = &h->n;
 	n->next = &h->n;
 	n->prev = h->n.prev;
 	n->prev = h->n.prev;
 	h->n.prev->next = n;
 	h->n.prev->next = n;
 	h->n.prev = n;
 	h->n.prev = n;
-	(void)list_debug(h);
+	(void)list_debug(h, abortstr);
 }
 }
 
 
 /**
 /**
@@ -192,12 +201,34 @@ static inline void list_add_tail(struct list_head *h, struct list_node *n)
  * Example:
  * Example:
  *	assert(list_empty(&parent->children) == (parent->num_children == 0));
  *	assert(list_empty(&parent->children) == (parent->num_children == 0));
  */
  */
-static inline bool list_empty(const struct list_head *h)
+#define list_empty(h) list_empty_(h, LIST_LOC)
+static inline bool list_empty_(const struct list_head *h, const char* abortstr)
 {
 {
-	(void)list_debug(h);
+	(void)list_debug(h, abortstr);
 	return h->n.next == &h->n;
 	return h->n.next == &h->n;
 }
 }
 
 
+/**
+ * list_empty_nodebug - is a list empty (and don't perform debug checks)?
+ * @h: the list_head
+ *
+ * If the list is empty, returns true.
+ * This differs from list_empty() in that if CCAN_LIST_DEBUG is set it
+ * will NOT perform debug checks. Only use this function if you REALLY
+ * know what you're doing.
+ *
+ * Example:
+ *	assert(list_empty_nodebug(&parent->children) == (parent->num_children == 0));
+ */
+#ifndef CCAN_LIST_DEBUG
+#define list_empty_nodebug(h) list_empty(h)
+#else
+static inline bool list_empty_nodebug(const struct list_head *h)
+{
+	return h->n.next == &h->n;
+}
+#endif
+
 /**
 /**
  * list_del - delete an entry from an (unknown) linked list.
  * list_del - delete an entry from an (unknown) linked list.
  * @n: the list_node to delete from the list.
  * @n: the list_node to delete from the list.
@@ -212,9 +243,10 @@ static inline bool list_empty(const struct list_head *h)
  *	list_del(&child->list);
  *	list_del(&child->list);
  *	parent->num_children--;
  *	parent->num_children--;
  */
  */
-static inline void list_del(struct list_node *n)
+#define list_del(n) list_del_(n, LIST_LOC)
+static inline void list_del_(struct list_node *n, const char* abortstr)
 {
 {
-	(void)list_debug_node(n);
+	(void)list_debug_node(n, abortstr);
 	n->next->prev = n->prev;
 	n->next->prev = n->prev;
 	n->prev->next = n->next;
 	n->prev->next = n->next;
 #ifdef CCAN_LIST_DEBUG
 #ifdef CCAN_LIST_DEBUG
@@ -374,7 +406,7 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
  *		printf("Name: %s\n", child->name);
  *		printf("Name: %s\n", child->name);
  */
  */
 #define list_for_each_rev(h, i, member)					\
 #define list_for_each_rev(h, i, member)					\
-	for (i = container_of_var(list_debug(h)->n.prev, i, member);	\
+	for (i = container_of_var(list_debug(h,	LIST_LOC)->n.prev, i, member); \
 	     &i->member != &(h)->n;					\
 	     &i->member != &(h)->n;					\
 	     i = container_of_var(i->member.prev, i, member))
 	     i = container_of_var(i->member.prev, i, member))
 
 
@@ -414,7 +446,8 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
  *		printf("No second child!\n");
  *		printf("No second child!\n");
  */
  */
 #define list_next(h, i, member)						\
 #define list_next(h, i, member)						\
-	((list_typeof(i))list_entry_or_null(list_debug(h),		\
+	((list_typeof(i))list_entry_or_null(list_debug(h,		\
+					    __FILE__ ":" stringify(__LINE__)), \
 					    (i)->member.next,		\
 					    (i)->member.next,		\
 					    list_off_var_((i), member)))
 					    list_off_var_((i), member)))
 
 
@@ -432,7 +465,8 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
  *		printf("Can't go back to first child?!\n");
  *		printf("Can't go back to first child?!\n");
  */
  */
 #define list_prev(h, i, member)						\
 #define list_prev(h, i, member)						\
-	((list_typeof(i))list_entry_or_null(list_debug(h),		\
+	((list_typeof(i))list_entry_or_null(list_debug(h,		\
+					    __FILE__ ":" stringify(__LINE__)), \
 					    (i)->member.prev,		\
 					    (i)->member.prev,		\
 					    list_off_var_((i), member)))
 					    list_off_var_((i), member)))
 
 
@@ -451,11 +485,14 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
  *	assert(list_empty(&parent->children));
  *	assert(list_empty(&parent->children));
  *	parent->num_children = 0;
  *	parent->num_children = 0;
  */
  */
-static inline void list_append_list(struct list_head *to,
-				    struct list_head *from)
+#define list_append_list(t, f) list_append_list_(t, f,			\
+				   __FILE__ ":" stringify(__LINE__))
+static inline void list_append_list_(struct list_head *to,
+				     struct list_head *from,
+				     const char *abortstr)
 {
 {
-	struct list_node *from_tail = list_debug(from)->n.prev;
-	struct list_node *to_tail = list_debug(to)->n.prev;
+	struct list_node *from_tail = list_debug(from, abortstr)->n.prev;
+	struct list_node *to_tail = list_debug(to, abortstr)->n.prev;
 
 
 	/* Sew in head and entire list. */
 	/* Sew in head and entire list. */
 	to->n.prev = from_tail;
 	to->n.prev = from_tail;
@@ -481,11 +518,13 @@ static inline void list_append_list(struct list_head *to,
  *	assert(list_empty(&parent->children));
  *	assert(list_empty(&parent->children));
  *	parent->num_children = 0;
  *	parent->num_children = 0;
  */
  */
-static inline void list_prepend_list(struct list_head *to,
-				     struct list_head *from)
+#define list_prepend_list(t, f) list_prepend_list_(t, f, LIST_LOC)
+static inline void list_prepend_list_(struct list_head *to,
+				      struct list_head *from,
+				      const char *abortstr)
 {
 {
-	struct list_node *from_tail = list_debug(from)->n.prev;
-	struct list_node *to_head = list_debug(to)->n.next;
+	struct list_node *from_tail = list_debug(from, abortstr)->n.prev;
+	struct list_node *to_head = list_debug(to, abortstr)->n.next;
 
 
 	/* Sew in head and entire list. */
 	/* Sew in head and entire list. */
 	to->n.next = &from->n;
 	to->n.next = &from->n;
@@ -528,7 +567,8 @@ static inline void list_prepend_list(struct list_head *to,
  *		printf("Name: %s\n", child->name);
  *		printf("Name: %s\n", child->name);
  */
  */
 #define list_for_each_off(h, i, off)                                    \
 #define list_for_each_off(h, i, off)                                    \
-  for (i = list_node_to_off_(list_debug(h)->n.next, (off));             \
+	for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.next,	\
+				   (off));				\
        list_node_from_off_((void *)i, (off)) != &(h)->n;                \
        list_node_from_off_((void *)i, (off)) != &(h)->n;                \
        i = list_node_to_off_(list_node_from_off_((void *)i, (off))->next, \
        i = list_node_to_off_(list_node_from_off_((void *)i, (off))->next, \
                              (off)))
                              (off)))
@@ -550,7 +590,8 @@ static inline void list_prepend_list(struct list_head *to,
  *		printf("Name: %s\n", child->name);
  *		printf("Name: %s\n", child->name);
  */
  */
 #define list_for_each_safe_off(h, i, nxt, off)                          \
 #define list_for_each_safe_off(h, i, nxt, off)                          \
-  for (i = list_node_to_off_(list_debug(h)->n.next, (off)),             \
+	for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.next,	\
+				   (off)),				\
          nxt = list_node_to_off_(list_node_from_off_(i, (off))->next,   \
          nxt = list_node_to_off_(list_node_from_off_(i, (off))->next,   \
                                  (off));                                \
                                  (off));                                \
        list_node_from_off_(i, (off)) != &(h)->n;                        \
        list_node_from_off_(i, (off)) != &(h)->n;                        \

+ 59 - 0
ccan/list/test/run-CCAN_LIST_DEBUG.c

@@ -0,0 +1,59 @@
+/* Check that CCAN_LIST_DEBUG works */
+#include <setjmp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <err.h>
+
+/* We don't actually want it to exit... */
+static jmp_buf aborted;
+#define abort() longjmp(aborted, 1)
+
+#define fprintf my_fprintf
+static char printf_buffer[1000];
+
+static int my_fprintf(FILE *stream, const char *format, ...)
+{
+	va_list ap;
+	int ret;
+	va_start(ap, format);
+	ret = vsprintf(printf_buffer, format, ap);
+	va_end(ap);
+	return ret;
+}
+
+#define CCAN_LIST_DEBUG 1
+#include <ccan/list/list.h>
+#include <ccan/tap/tap.h>
+#include <ccan/list/list.c>
+
+int main(int argc, char *argv[])
+{
+	struct list_head list;
+	struct list_node n1;
+	char expect[100];
+
+	plan_tests(2);
+	/* Empty list. */
+	list.n.next = &list.n;
+	list.n.prev = &list.n;
+	ok1(list_check(&list, NULL) == &list);
+
+	/* Bad back ptr */
+	list.n.prev = &n1;
+
+	/* Aborting version. */
+	sprintf(expect, "run-CCAN_LIST_DEBUG.c:50: prev corrupt in node %p (0) of %p\n",
+		&list, &list);
+	if (setjmp(aborted) == 0) {
+		assert(list_empty(&list));
+		fail("list_empty on empty with bad back ptr didn't fail!");
+	} else {
+		/* __FILE__ might give full path. */
+		int prep = strlen(printf_buffer) - strlen(expect);
+		ok1(prep >= 0 && strcmp(printf_buffer + prep, expect) == 0);
+	}
+
+	return exit_status();
+}