take: add labels when CCAN_TAKE_DEBUG set, return in taken_any().
authorRusty Russell <rusty@rustcorp.com.au>
Wed, 15 Mar 2017 03:40:08 +0000 (14:10 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Wed, 15 Mar 2017 03:57:33 +0000 (14:27 +1030)
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ccan/take/_info
ccan/take/take.c
ccan/take/take.h
ccan/take/test/run-debug.c [new file with mode: 0644]

index fcb4f318a36ad77fdf1e7e8440de463b4f2d51cf..d013838a14916e76a34ff3353f33740cb9f33b08 100644 (file)
@@ -53,6 +53,7 @@ int main(int argc, char *argv[])
 
        if (strcmp(argv[1], "depends") == 0) {
                printf("ccan/likely\n");
+               printf("ccan/str\n");
                return 0;
        }
 
index 73e5c29d206fcf5a5cf23e7b6f8d4836a3d06fc4..c628aac0dc97ff663db216e4f05a14b8cfd38bbf 100644 (file)
@@ -1,16 +1,22 @@
 /* CC0 (Public domain) - see LICENSE file for details */
 #include <ccan/take/take.h>
 #include <ccan/likely/likely.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 static const void **takenarr;
+static const char **labelarr;
 static size_t max_taken, num_taken;
 static size_t allocfail;
 static void (*allocfailfn)(const void *p);
 
-void *take_(const void *p)
+void *take_(const void *p, const char *label)
 {
+       /* Overallocate: it's better than risking calloc returning NULL! */
+       if (unlikely(label && !labelarr))
+               labelarr = calloc(max_taken+1, sizeof(*labelarr));
+
        if (unlikely(num_taken == max_taken)) {
                const void **new;
 
@@ -25,9 +31,16 @@ void *take_(const void *p)
                        return (void *)p;
                }
                takenarr = new;
+               /* Once labelarr is set, we maintain it. */
+               if (labelarr)
+                       labelarr = realloc(labelarr,
+                                          sizeof(*labelarr) * (max_taken+1));
                max_taken++;
        }
+       if (unlikely(labelarr))
+               labelarr[num_taken] = label;
        takenarr[num_taken++] = p;
+
        return (void *)p;
 }
 
@@ -68,9 +81,23 @@ bool is_taken(const void *p)
        return find_taken(p) > 0;
 }
 
-bool taken_any(void)
+const char *taken_any(void)
 {
-       return num_taken != 0;
+       static char pointer_buf[32];
+
+       if (num_taken == 0)
+               return NULL;
+
+       /* We're *allowed* to have some with labels, some without. */
+       if (labelarr) {
+               size_t i;
+               for (i = 0; i < num_taken; i++)
+                       if (labelarr[i])
+                               return labelarr[i];
+       }
+
+       sprintf(pointer_buf, "%p", takenarr[0]);
+       return pointer_buf;
 }
 
 void take_cleanup(void)
@@ -78,6 +105,8 @@ void take_cleanup(void)
        max_taken = num_taken = 0;
        free(takenarr);
        takenarr = NULL;
+       free(labelarr);
+       labelarr = NULL;
 }
 
 void take_allocfail(void (*fn)(const void *p))
index b6ac4a9f074a4b0d0269ce81ee59d2375148b9af..e0db2902021cff390f46b3330069ecb1b3dd4453 100644 (file)
@@ -3,6 +3,13 @@
 #define CCAN_TAKE_H
 #include "config.h"
 #include <stdbool.h>
+#include <ccan/str/str.h>
+
+#ifdef CCAN_TAKE_DEBUG
+#define TAKE_LABEL(p) __FILE__ ":" stringify(__LINE__) ":" stringify(p)
+#else
+#define TAKE_LABEL(p) NULL
+#endif
 
 /**
  * take - record a pointer to be consumed by the function its handed to.
@@ -12,7 +19,7 @@
  * which is extremely useful for chaining functions.  It works on
  * NULL, for pass-through error handling.
  */
-#define take(p) (take_typeof(p) take_((p)))
+#define take(p) (take_typeof(p) take_((p), TAKE_LABEL(p)))
 
 /**
  * taken - check (and un-take) a pointer was passed with take()
@@ -60,7 +67,9 @@ bool is_taken(const void *p);
 /**
  * taken_any - are there any taken pointers?
  *
- * Mainly useful for debugging take() leaks.
+ * Mainly useful for debugging take() leaks.  With CCAN_TAKE_DEBUG, returns
+ * the label where the pointer was passed to take(), otherwise returns
+ * a static char buffer with the pointer value in it.  NULL if none are taken.
  *
  * Example:
  *     static void cleanup(void)
@@ -68,7 +77,7 @@ bool is_taken(const void *p);
  *             assert(!taken_any());
  *     }
  */
-bool taken_any(void);
+const char *taken_any(void);
 
 /**
  * take_cleanup - remove all taken pointers from list.
@@ -112,5 +121,5 @@ void take_allocfail(void (*fn)(const void *p));
 #define take_typeof(ptr)
 #endif
 
-void *take_(const void *p);
+void *take_(const void *p, const char *label);
 #endif /* CCAN_TAKE_H */
diff --git a/ccan/take/test/run-debug.c b/ccan/take/test/run-debug.c
new file mode 100644 (file)
index 0000000..a9dda6e
--- /dev/null
@@ -0,0 +1,34 @@
+#include <stdlib.h>
+#include <stdbool.h>
+
+#define CCAN_TAKE_DEBUG 1
+#include <ccan/take/take.h>
+#include <ccan/take/take.c>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+       const char *p = "hi";
+
+       plan_tests(14);
+
+       /* We can take NULL. */
+       ok1(take(NULL) == NULL);
+       ok1(is_taken(NULL));
+       ok1(strstr(taken_any(), "run-debug.c:16:"));
+       ok1(taken(NULL)); /* Undoes take() */
+       ok1(!is_taken(NULL));
+       ok1(!taken(NULL));
+       ok1(!taken_any());
+
+       /* We can take a real pointer. */
+       ok1(take(p) == p);
+       ok1(is_taken(p));
+       ok1(strends(taken_any(), "run-debug.c:25:p"));
+       ok1(taken(p)); /* Undoes take() */
+       ok1(!is_taken(p));
+       ok1(!taken(p));
+       ok1(!taken_any());
+
+       return exit_status();
+}