Commit ecf907f7 authored by Rusty Russell's avatar Rusty Russell

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: default avatarStewart Smith <stewart@linux.vnet.ibm.com>
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent b989e06c
...@@ -62,7 +62,9 @@ int main(int argc, char *argv[]) ...@@ -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;
} }
......
/* 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); ...@@ -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(h, loc) list_check((h), loc)
#define list_debug_node(n) list_check_node((n), __func__) #define list_debug_node(n, loc) list_check_node((n), loc)
#else #else
#define list_debug(h) (h) #define list_debug(h, loc) (h)
#define list_debug_node(n) (n) #define list_debug_node(n, loc) (n)
#endif #endif
/** /**
...@@ -155,13 +158,16 @@ static inline void list_head_init(struct list_head *h) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, #define list_append_list(t, f) list_append_list_(t, f, \
struct list_head *from) __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 *from_tail = list_debug(from, abortstr)->n.prev;
struct list_node *to_tail = list_debug(to)->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, ...@@ -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, #define list_prepend_list(t, f) list_prepend_list_(t, f, LIST_LOC)
struct list_head *from) 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 *from_tail = list_debug(from, abortstr)->n.prev;
struct list_node *to_head = list_debug(to)->n.next; 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, ...@@ -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, ...@@ -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; \
......
/* 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();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment