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[])
return 1;
if (strcmp(argv[1], "depends") == 0) {
printf("ccan/str\n");
printf("ccan/container_of\n");
printf("ccan/check_type\n");
return 0;
}
......
/* Licensed under BSD-MIT - see LICENSE file for details */
#ifndef CCAN_LIST_H
#define CCAN_LIST_H
//#define CCAN_LIST_DEBUG 1
#include <stdbool.h>
#include <assert.h>
#include <ccan/str/str.h>
#include <ccan/container_of/container_of.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,
const char *abortstr);
#define LIST_LOC __FILE__ ":" stringify(__LINE__)
#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
#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
/**
......@@ -155,13 +158,16 @@ static inline void list_head_init(struct list_head *h)
* list_add(&parent->children, &child->list);
* 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->prev = &h->n;
h->n.next->prev = 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);
* 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->prev = h->n.prev;
h->n.prev->next = 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:
* 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;
}
/**
* 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.
* @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);
* 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->prev->next = n->next;
#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);
*/
#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 = 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");
*/
#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, \
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");
*/
#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, \
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));
* 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. */
to->n.prev = from_tail;
......@@ -481,11 +518,13 @@ static inline void list_append_list(struct list_head *to,
* assert(list_empty(&parent->children));
* 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. */
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);
*/
#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; \
i = list_node_to_off_(list_node_from_off_((void *)i, (off))->next, \
(off)))
......@@ -550,7 +590,8 @@ static inline void list_prepend_list(struct list_head *to,
* printf("Name: %s\n", child->name);
*/
#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, \
(off)); \
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