Module: xenomai-3
Branch: wip/heapmem
Commit: ebfcb36320e00906fd2bf4b8058bb33620788547
URL:    
http://git.xenomai.org/?p=xenomai-3.git;a=commit;h=ebfcb36320e00906fd2bf4b8058bb33620788547

Author: Philippe Gerum <r...@xenomai.org>
Date:   Sun Apr 22 18:20:04 2018 +0200

testsuite/smokey: add test suite for memory allocators

---

 configure.ac                                |    4 +
 testsuite/smokey/Makefile.am                |   17 +-
 testsuite/smokey/memcheck/Makefile.am       |    8 +
 testsuite/smokey/memcheck/memcheck.c        |  884 +++++++++++++++++++++++++++
 testsuite/smokey/memcheck/memcheck.h        |   65 ++
 testsuite/smokey/memory-heapmem/Makefile.am |    9 +
 testsuite/smokey/memory-heapmem/heapmem.c   |   51 ++
 testsuite/smokey/memory-pshared/Makefile.am |    9 +
 testsuite/smokey/memory-pshared/pshared.c   |  121 ++++
 testsuite/smokey/memory-tlsf/Makefile.am    |   10 +
 testsuite/smokey/memory-tlsf/tlsf.c         |  123 ++++
 11 files changed, 1300 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index ec2d7cb..c0ef1df 100644
--- a/configure.ac
+++ b/configure.ac
@@ -931,6 +931,10 @@ AC_CONFIG_FILES([ \
        testsuite/smokey/timerfd/Makefile \
        testsuite/smokey/tsc/Makefile \
        testsuite/smokey/leaks/Makefile \
+       testsuite/smokey/memcheck/Makefile \
+       testsuite/smokey/memory-heapmem/Makefile \
+       testsuite/smokey/memory-tlsf/Makefile \
+       testsuite/smokey/memory-pshared/Makefile \
        testsuite/smokey/fpu-stress/Makefile \
        testsuite/smokey/net_udp/Makefile \
        testsuite/smokey/net_packet_dgram/Makefile \
diff --git a/testsuite/smokey/Makefile.am b/testsuite/smokey/Makefile.am
index c6fe70b..d7a71fe 100644
--- a/testsuite/smokey/Makefile.am
+++ b/testsuite/smokey/Makefile.am
@@ -5,6 +5,10 @@ CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
 
 smokey_SOURCES = main.c
 
+# Make sure to list modules from the most dependent to the
+# least. e.g. net_common should appear after all net_* modules,
+# memcheck should appear after all heapmem-* modules.
+
 COBALT_SUBDIRS =       \
        arith           \
        bufp            \
@@ -12,6 +16,9 @@ COBALT_SUBDIRS =      \
        fpu-stress      \
        iddp            \
        leaks           \
+       memory-heapmem  \
+       memory-tlsf     \
+       memcheck        \
        net_packet_dgram\
        net_packet_raw  \
        net_udp         \
@@ -31,11 +38,19 @@ COBALT_SUBDIRS =    \
        vdso-access     \
        xddp
 
+if XENO_PSHARED
+COBALT_SUBDIRS += memory-pshared
+endif
+
 if CONFIG_XENO_LIBS_DLOPEN
 COBALT_SUBDIRS += dlopen
 endif
 
-MERCURY_SUBDIRS =
+MERCURY_SUBDIRS = memory-heapmem memory-tlsf
+if XENO_PSHARED
+MERCURY_SUBDIRS += memory-pshared
+endif
+MERCURY_SUBDIRS += memcheck
 
 DIST_SUBDIRS = $(COBALT_SUBDIRS) $(MERCURY_SUBDIRS)
 
diff --git a/testsuite/smokey/memcheck/Makefile.am 
b/testsuite/smokey/memcheck/Makefile.am
new file mode 100644
index 0000000..482314a
--- /dev/null
+++ b/testsuite/smokey/memcheck/Makefile.am
@@ -0,0 +1,8 @@
+noinst_LIBRARIES = libmemcheck.a
+noinst_HEADERS = memcheck.h
+
+AM_CPPFLAGS =                  \
+       @XENO_USER_CFLAGS@      \
+       -I$(top_srcdir)/include
+
+libmemcheck_a_SOURCES = memcheck.c
diff --git a/testsuite/smokey/memcheck/memcheck.c 
b/testsuite/smokey/memcheck/memcheck.c
new file mode 100644
index 0000000..3f18477
--- /dev/null
+++ b/testsuite/smokey/memcheck/memcheck.c
@@ -0,0 +1,884 @@
+/*
+ * Copyright (C) 2018 Philippe Gerum <r...@xenomai.org>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#include <time.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sched.h>
+#include <pthread.h>
+#include <boilerplate/time.h>
+#include "memcheck.h"
+
+enum pattern {
+       alphabet_series,
+       digit_series,
+       binary_series,
+};
+
+struct chunk {
+       void *ptr;
+       enum pattern pattern;
+};
+
+struct runstats {
+       size_t heap_size;
+       size_t user_size;
+       size_t block_size;
+       int nrblocks;
+       long alloc_avg_ns;
+       long alloc_max_ns;
+       long free_avg_ns;
+       long free_max_ns;
+       int flags;
+       double overhead;
+       double fragmentation;
+       struct runstats *next;
+};
+
+static struct runstats *statistics;
+
+static int nrstats;
+
+static int max_results = 4;
+
+static inline long diff_ts(struct timespec *left, struct timespec *right)
+{
+       return (long long)(left->tv_sec - right->tv_sec) * ONE_BILLION
+               + left->tv_nsec - right->tv_nsec;
+}
+
+static inline void swap(void *left, void *right, const size_t size)
+{
+       char trans[size];
+
+       memcpy(trans, left, size);
+       memcpy(left, right, size);
+       memcpy(right, trans, size);
+}
+
+static void random_shuffle(void *vbase, size_t nmemb, const size_t size)
+{
+       struct {
+               char x[size];
+       } __attribute__((packed)) *base = vbase;
+       unsigned int j, k;
+       double u;
+
+       for(j = nmemb; j > 0; j--) {
+               u = (double)random() / RAND_MAX;
+               k = (unsigned int)(j * u) + 1;
+               if (j == k)
+                       continue;
+               swap(&base[j - 1], &base[k - 1], size);
+       }
+}
+
+/* Reverse sort, high values first. */
+
+#define compare_values(l, r)                   \
+       ({                                      \
+               typeof(l) _l = (l);             \
+               typeof(r) _r = (r);             \
+               (_l > _r) - (_l < _r);          \
+       })
+
+static int sort_by_heap_size(const void *l, const void *r)
+{
+       const struct runstats *ls = l, *rs = r;
+
+       return compare_values(rs->heap_size, ls->heap_size);
+}
+
+static int sort_by_alloc_time(const void *l, const void *r)
+{
+       const struct runstats *ls = l, *rs = r;
+
+       return compare_values(rs->alloc_max_ns, ls->alloc_max_ns);
+}
+
+static int sort_by_free_time(const void *l, const void *r)
+{
+       const struct runstats *ls = l, *rs = r;
+
+       return compare_values(rs->free_max_ns, ls->free_max_ns);
+}
+
+static int sort_by_frag(const void *l, const void *r)
+{
+       const struct runstats *ls = l, *rs = r;
+
+       return compare_values(rs->fragmentation, ls->fragmentation);
+}
+
+static int sort_by_overhead(const void *l, const void *r)
+{
+       const struct runstats *ls = l, *rs = r;
+
+       return compare_values(rs->overhead, ls->overhead);
+}
+
+static inline const char *get_debug_state(void)
+{
+#if defined(CONFIG_XENO_DEBUG_FULL)
+       return "\n(CAUTION: full debug enabled)";
+#elif defined(CONFIG_XENO_DEBUG)
+       return "\n(debug partially enabled)";
+#else
+       return "";
+#endif
+}
+
+#ifdef CONFIG_XENO_COBALT
+
+#include <sys/cobalt.h>
+
+static inline void breathe(int loops)
+{
+       struct timespec idle = {
+               .tv_sec = 0,
+               .tv_nsec = 1000000,
+       };
+
+       /*
+        * There is not rt throttling over Cobalt, so we may need to
+        * keep the host kernel breathing by napping during the test
+        * sequences.
+        */
+       if ((loops % 10000) == 0)
+               __RT(clock_nanosleep(CLOCK_MONOTONIC, 0, &idle, NULL));
+}
+
+static inline void harden(void)
+{
+       cobalt_thread_harden();
+}
+
+#else
+
+static inline void breathe(int loops) { }
+
+static inline void harden(void) { }
+
+#endif
+
+static void __dump_stats(struct memcheck_descriptor *md,
+                        struct runstats *stats,
+                        int (*sortfn)(const void *l, const void *r),
+                        int nr, const char *key)
+{
+       struct runstats *p;
+       int n;
+
+       qsort(stats, nrstats, sizeof(*p), sortfn);
+
+       smokey_trace("\nsorted by: max %s\n%8s  %7s  %7s  %5s  %5s  %5s  %5s   
%5s  %5s  %s",
+                    key, "HEAPSZ", "BLOCKSZ", "NRBLKS", "AVG-A",
+                    "AVG-F", "MAX-A", "MAX-F", "OVRH%", "FRAG%", "FLAGS");
+
+       for (n = 0; n < nr; n++) {
+               p = stats + n;
+               smokey_trace("%7zuk  %7zu%s  %6d  %5.1f  %5.1f  %5.1f  %5.1f   
%4.1f  %5.1f   %s%s%s",
+                            p->heap_size / 1024,
+                            p->block_size < 1024 ? p->block_size : 
p->block_size / 1024,
+                            p->block_size < 1024 ? " " : "k",
+                            p->nrblocks,
+                            (double)p->alloc_avg_ns/1000.0,
+                            (double)p->free_avg_ns/1000.0,
+                            (double)p->alloc_max_ns/1000.0,
+                            (double)p->free_max_ns/1000.0,
+                            p->overhead,
+                            p->fragmentation,
+                            p->alloc_avg_ns == 0 && p->free_avg_ns == 0 ? 
"FAILED " : "",
+                            p->flags & MEMCHECK_SHUFFLE ? "+shuffle " : "",
+                            p->flags & MEMCHECK_REALLOC ? "+realloc" : "");
+       }
+
+       if (nr < nrstats)
+               smokey_trace("  ... (%d results following) ...", nrstats - nr);
+}
+
+static int dump_stats(struct memcheck_descriptor *md, const char *title)
+{
+       long worst_alloc_max = 0, worst_free_max = 0;
+       double overhead_sum = 0.0, frag_sum = 0.0;
+       long max_alloc_sum = 0, max_free_sum = 0;
+       long avg_alloc_sum = 0, avg_free_sum = 0;
+       struct runstats *stats, *p, *next;
+       int n;
+
+       stats = __STD(malloc(sizeof(*p) * nrstats));
+       if (stats == NULL) {
+               smokey_warning("failed allocating memory");
+               return -ENOMEM;
+       }
+
+       for (n = 0, p = statistics; n < nrstats; n++, p = p->next)
+               stats[n] = *p;
+
+       smokey_trace("\n[%s] ON '%s'%s\n",
+                    title, md->name, get_debug_state());
+
+       smokey_trace("HEAPSZ    test heap size");
+       smokey_trace("BLOCKSZ   tested block size");
+       smokey_trace("NRBLKS    number of blocks allocatable in heap");
+       smokey_trace("AVG-A     average time to allocate block (us)");
+       smokey_trace("AVG-F     average time to free block (us)");
+       smokey_trace("MAX-A     max time to allocate block (us)");
+       smokey_trace("MAX-F     max time to free block (us)");
+       smokey_trace("OVRH%     overhead");
+       smokey_trace("FRAG%     external fragmentation");
+       smokey_trace("FLAGS     +shuffle: randomized free");
+       smokey_trace("          +realloc: measure after initial alloc/free pass 
(hot heap)");
+
+       if (max_results > 0) {
+               if (max_results > nrstats)
+                       max_results = nrstats;
+               __dump_stats(md, stats, sort_by_alloc_time, max_results, "alloc 
time");
+               __dump_stats(md, stats, sort_by_free_time, max_results, "free 
time");
+               __dump_stats(md, stats, sort_by_overhead, max_results, 
"overhead");
+               __dump_stats(md, stats, sort_by_frag, max_results, 
"fragmentation");
+       } else if (max_results < 0)
+               __dump_stats(md, stats, sort_by_heap_size, nrstats, "heap 
size");
+
+       __STD(free(stats));
+
+       for (p = statistics; p; p = next) {
+               max_alloc_sum += p->alloc_max_ns;
+               max_free_sum += p->free_max_ns;
+               avg_alloc_sum += p->alloc_avg_ns;
+               avg_free_sum += p->free_avg_ns;
+               overhead_sum += p->overhead;
+               frag_sum += p->fragmentation;
+               if (p->alloc_max_ns > worst_alloc_max)
+                       worst_alloc_max = p->alloc_max_ns;
+               if (p->free_max_ns > worst_free_max)
+                       worst_free_max = p->free_max_ns;
+               next = p->next;
+               __STD(free(p));
+       }
+
+       smokey_trace("\noverall:");
+       smokey_trace("  worst alloc time: %.1f (us)",
+                    (double)worst_alloc_max / 1000.0);
+       smokey_trace("  worst free time: %.1f (us)",
+                    (double)worst_free_max / 1000.0);
+       smokey_trace("  average of max. alloc times: %.1f (us)",
+                    (double)max_alloc_sum / nrstats / 1000.0);
+       smokey_trace("  average of max. free times: %.1f (us)",
+                    (double)max_free_sum / nrstats / 1000.0);
+       smokey_trace("  average alloc time: %.1f (us)",
+                    (double)avg_alloc_sum / nrstats / 1000.0);
+       smokey_trace("  average free time: %.1f (us)",
+                    (double)avg_free_sum / nrstats / 1000.0);
+       smokey_trace("  average overhead: %.1f%%",
+                    (double)overhead_sum / nrstats);
+       smokey_trace("  average fragmentation: %.1f%%",
+                    (double)frag_sum / nrstats);
+
+       statistics = NULL;
+       nrstats = 0;
+
+       return 0;
+}
+
+static void fill_pattern(char *p, size_t size, enum pattern pat)
+{
+       unsigned int val, count;
+
+       switch (pat) {
+       case alphabet_series:
+               val = 'a';
+               count = 26;
+               break;
+       case digit_series:
+               val = '0';
+               count = 10;
+               break;
+       default:
+               val = 0;
+               count = 255;
+               break;
+       }
+
+       while (size-- > 0) {
+               *p++ = (char)(val % count);
+               val++;
+       }
+}
+
+static int check_pattern(const char *p, size_t size, enum pattern pat)
+{
+       unsigned int val, count;
+
+       switch (pat) {
+       case alphabet_series:
+               val = 'a';
+               count = 26;
+               break;
+       case digit_series:
+               val = '0';
+               count = 10;
+               break;
+       default:
+               val = 0;
+               count = 255;
+               break;
+       }
+
+       while (size-- > 0) {
+               if (*p++ != (char)(val % count))
+                       return 0;
+               val++;
+       }
+
+       return 1;
+}
+
+static size_t find_largest_free(struct memcheck_descriptor *md,
+                               size_t free_size, size_t block_size)
+{
+       void *p;
+
+       for (;;) {
+               p = md->alloc(md->heap, free_size);
+               if (p) {
+                       md->free(md->heap, p);
+                       break;
+               }
+               if (free_size <= block_size)
+                       break;
+               free_size -= block_size;
+       }
+
+       return free_size;
+}
+
+static int test_seq(struct memcheck_descriptor *md,
+                   size_t heap_size, size_t block_size, int flags)
+{
+       long alloc_sum_ns, alloc_avg_ns, free_sum_ns, free_avg_ns,
+               alloc_max_ns, free_max_ns, d;
+       size_t arena_size, user_size, largest_free, freed;
+       int ret, n, k, maxblocks, nrblocks;
+       struct timespec start, end;
+       struct runstats *stats;
+       struct chunk *chunks;
+       bool done_frag;
+       void *mem, *p;
+       double frag;
+
+       arena_size = md->get_arena_size(heap_size);
+       if (arena_size == 0) {
+               smokey_trace("cannot get arena size for heap size %zu",
+                            heap_size);
+               return -ENOMEM;
+       }
+       
+       maxblocks = heap_size / block_size;
+
+       mem = __STD(malloc(arena_size));
+       if (mem == NULL)
+               return -ENOMEM;
+
+       ret = md->init(md->heap, mem, arena_size);
+       if (ret) {
+               smokey_trace("cannot init heap with arena size %zu",
+                            arena_size);
+               goto out;
+       }
+
+       chunks = calloc(sizeof(*chunks), maxblocks);
+       if (chunks == NULL) {
+               ret = -ENOMEM;
+               goto no_chunks;
+       }
+
+       if (md->get_usable_size(md->heap) != heap_size) {
+               smokey_trace("memory size inconsistency (%zu / %zu bytes)",
+                            heap_size, md->get_usable_size(md->heap));
+               goto bad;
+       }
+
+       user_size = 0;
+       alloc_avg_ns = 0;
+       free_avg_ns = 0;
+       alloc_max_ns = 0;
+       free_max_ns = 0;
+       frag = 0.0;
+
+       /*
+        * With Cobalt, make sure to run in primary mode before the
+        * first allocation call takes place, not to charge any switch
+        * time to the allocator.
+        */
+       harden();
+       for (n = 0, alloc_sum_ns = 0; ; n++) {
+               __RT(clock_gettime(CLOCK_MONOTONIC, &start));
+               p = md->alloc(md->heap, block_size);
+               __RT(clock_gettime(CLOCK_MONOTONIC, &end));
+               d = diff_ts(&end, &start);
+               if (d > alloc_max_ns)
+                       alloc_max_ns = d;
+               alloc_sum_ns += d;
+               if (p == NULL)
+                       break;
+               user_size += block_size;
+               if (n >= maxblocks) {
+                       smokey_trace("too many blocks fetched"
+                                    " (heap=%zu, block=%zu, "
+                                    "got more than %d blocks)",
+                                    heap_size, block_size, maxblocks);
+                       goto bad;
+               }
+               chunks[n].ptr = p;
+               if (flags & MEMCHECK_PATTERN) {
+                       chunks[n].pattern = (enum pattern)(random() % 3);
+                       fill_pattern(chunks[n].ptr, block_size, 
chunks[n].pattern);
+               }
+               breathe(n);
+       }
+
+       nrblocks = n;
+       if (nrblocks == 0)
+               goto do_stats;
+
+       if ((flags & MEMCHECK_ZEROOVRD) && nrblocks != maxblocks) {
+               smokey_trace("too few blocks fetched, unexpected overhead"
+                            " (heap=%zu, block=%zu, "
+                            "got %d, less than %d blocks)",
+                            heap_size, block_size, nrblocks, maxblocks);
+               goto bad;
+       }
+
+       breathe(0);
+
+       /* Make sure we did not trash any busy block while allocating. */
+       if (flags & MEMCHECK_PATTERN) {
+               for (n = 0; n < nrblocks; n++) {
+                       if (!check_pattern(chunks[n].ptr, block_size,
+                                          chunks[n].pattern)) {
+                               smokey_trace("corrupted block #%d on alloc"
+                                            " sequence (pattern %d)",
+                                            n, chunks[n].pattern);
+                               goto bad;
+                       }
+                       breathe(n);
+               }
+       }
+       
+       if (flags & MEMCHECK_SHUFFLE)
+               random_shuffle(chunks, nrblocks, sizeof(*chunks));
+
+       /*
+        * Release all blocks.
+        */
+       harden();
+       for (n = 0, free_sum_ns = 0, freed = 0, done_frag = false;
+            n < nrblocks; n++) {
+               __RT(clock_gettime(CLOCK_MONOTONIC, &start));
+               ret = md->free(md->heap, chunks[n].ptr);
+               __RT(clock_gettime(CLOCK_MONOTONIC, &end));
+               if (ret) {
+                       smokey_trace("failed to free block %p "
+                                    "(heap=%zu, block=%zu)",
+                                    chunks[n].ptr, heap_size, block_size);
+                       goto bad;
+               }
+               d = diff_ts(&end, &start);
+               if (d > free_max_ns)
+                       free_max_ns = d;
+               free_sum_ns += d;
+               chunks[n].ptr = NULL;
+               /* Make sure we did not trash busy blocks while freeing. */
+               if (flags & MEMCHECK_PATTERN) {
+                       for (k = 0; k < nrblocks; k++) {
+                               if (chunks[k].ptr &&
+                                   !check_pattern(chunks[k].ptr, block_size,
+                                                  chunks[k].pattern)) {
+                                       smokey_trace("corrupted block #%d on 
release"
+                                                    " sequence (pattern %d)",
+                                                    k, chunks[k].pattern);
+                                       goto bad;
+                               }
+                       }
+               }
+               freed += block_size;
+               /*
+                * Get a sense of the fragmentation for the tested
+                * allocation pattern, heap and block sizes when half
+                * of the usable heap size should be available to us.
+                * NOTE: user_size excludes the overhead, this is
+                * actually what we managed to get from the current
+                * heap out of the allocation loop.
+                */
+               if (!done_frag && freed >= user_size / 2) {
+                       /* Calculate the external fragmentation. */
+                       largest_free = find_largest_free(md, freed, block_size);
+                       frag = (1.0 - ((double)largest_free / freed)) * 100.0;
+                       done_frag = true;
+               }
+               breathe(n);
+       }
+
+       /*
+        * If the deallocation mechanism is broken, we might not be
+        * able to reproduce the same allocation pattern with the same
+        * outcome, check this.
+        */
+       if (flags & MEMCHECK_REALLOC) {
+               for (n = 0, alloc_max_ns = alloc_sum_ns = 0; ; n++) {
+                       __RT(clock_gettime(CLOCK_MONOTONIC, &start));
+                       p = md->alloc(md->heap, block_size);
+                       __RT(clock_gettime(CLOCK_MONOTONIC, &end));
+                       d = diff_ts(&end, &start);
+                       if (d > alloc_max_ns)
+                               alloc_max_ns = d;
+                       alloc_sum_ns += d;
+                       if (p == NULL)
+                               break;
+                       if (n >= maxblocks) {
+                               smokey_trace("too many blocks fetched during 
realloc"
+                                            " (heap=%zu, block=%zu, "
+                                            "got more than %d blocks)",
+                                            heap_size, block_size, maxblocks);
+                               goto bad;
+                       }
+                       chunks[n].ptr = p;
+                       breathe(n);
+               }
+               if (n != nrblocks) {
+                       smokey_trace("inconsistent block count fetched during 
realloc"
+                                    " (heap=%zu, block=%zu, "
+                                    "got %d blocks vs %d during alloc)",
+                                    heap_size, block_size, n, nrblocks);
+                       goto bad;
+               }
+               for (n = 0, free_max_ns = free_sum_ns = 0; n < nrblocks; n++) {
+                       __RT(clock_gettime(CLOCK_MONOTONIC, &start));
+                       ret = md->free(md->heap, chunks[n].ptr);
+                       __RT(clock_gettime(CLOCK_MONOTONIC, &end));
+                       if (ret) {
+                               smokey_trace("failed to free block %p during 
realloc"
+                                            "(heap=%zu, block=%zu)",
+                                            chunks[n].ptr, heap_size, 
block_size);
+                               goto bad;
+                       }
+                       d = diff_ts(&end, &start);
+                       if (d > free_max_ns)
+                               free_max_ns = d;
+                       free_sum_ns += d;
+                       breathe(n);
+               }
+       }
+
+       alloc_avg_ns = alloc_sum_ns / nrblocks;
+       free_avg_ns = free_sum_ns / nrblocks;
+
+       if ((flags & MEMCHECK_ZEROOVRD) && heap_size != user_size) {
+               smokey_trace("unexpected overhead reported");
+               goto bad;
+       }
+
+       if (md->get_used_size(md->heap) > 0) {
+               smokey_trace("memory leakage reported: %zu bytes missing",
+                            md->get_used_size(md->heap));
+               goto bad;
+       }
+               
+       /*
+        * Don't report stats when running a pattern check, timings
+        * are affected.
+        */
+do_stats:
+       breathe(0);
+       ret = 0;
+       if (!(flags & MEMCHECK_PATTERN)) {
+               stats = __STD(malloc(sizeof(*stats)));
+               if (stats == NULL) {
+                       smokey_warning("failed allocating memory");
+                       ret = -ENOMEM;
+                       goto oom;
+               }
+               stats->heap_size = heap_size;
+               stats->user_size = user_size;
+               stats->block_size = block_size;
+               stats->nrblocks = nrblocks;
+               stats->alloc_avg_ns = alloc_avg_ns;
+               stats->alloc_max_ns = alloc_max_ns;
+               stats->free_avg_ns = free_avg_ns;
+               stats->free_max_ns = free_max_ns;
+               stats->overhead = 100.0 - (user_size * 100.0 / heap_size);
+               stats->fragmentation = frag;
+               stats->flags = flags;
+               stats->next = statistics;
+               statistics = stats;
+               nrstats++;
+       }
+
+done:
+       __STD(free(chunks));
+no_chunks:
+       md->destroy(md->heap);
+out:
+       if (ret)
+               smokey_trace("** '%s' FAILED(overhead %s, %sshuffle, %scheck, 
%srealloc): heapsz=%zuk, "
+                            "blocksz=%zu, overhead=%zu (%.1f%%)",
+                            md->name,
+                            flags & MEMCHECK_ZEROOVRD ? "disallowed" : 
"allowed",
+                            flags & MEMCHECK_SHUFFLE ? "" : "no ",
+                            flags & MEMCHECK_PATTERN ? "" : "no ",
+                            flags & MEMCHECK_REALLOC ? "" : "no ",
+                            heap_size / 1024, block_size,
+                            arena_size - heap_size,
+                            (arena_size * 100.0 / heap_size) - 100.0);
+oom:
+       __STD(free(mem));
+
+       return ret;
+bad:
+       ret = -EPROTO;
+       goto done;
+}
+
+static inline int test_flags(struct memcheck_descriptor *md, int flags)
+{
+       return md->valid_flags & flags;
+}
+
+int memcheck_run(struct memcheck_descriptor *md,
+                struct smokey_test *t,
+                int argc, char *const argv[])
+{
+       struct timespec idle = { .tv_sec = 0, .tv_nsec = 1000000 };
+       size_t heap_size, block_size;
+       struct sched_param param;
+       cpu_set_t affinity;
+       unsigned long seed;
+       int ret, runs;
+       time_t now;
+       void *p;
+
+       /* Populate the malloc arena early to limit mode switching. */
+       p = __STD(malloc(2 * 1024 * 1024));
+       __STD(free(p));
+
+       smokey_parse_args(t, argc, argv);
+       
+       if (smokey_arg_isset(t, "seq_heap_size"))
+               md->seq_max_heap_size = smokey_arg_size(t, "seq_heap_size");
+       
+       if (smokey_arg_isset(t, "random_alloc_rounds"))
+               md->random_rounds = smokey_arg_int(t, "random_alloc_rounds");
+       
+       if (smokey_arg_isset(t, "pattern_heap_size"))
+               md->pattern_heap_size = smokey_arg_size(t, "pattern_heap_size");
+       
+       if (smokey_arg_isset(t, "pattern_check_rounds"))
+               md->pattern_rounds = smokey_arg_int(t, "pattern_check_rounds");
+
+       if (smokey_arg_isset(t, "max_results"))
+               max_results = smokey_arg_int(t, "max_results");
+
+       now = time(NULL);
+       seed = (unsigned long)now * getpid();
+       srandom(seed);
+
+       smokey_trace("== memcheck started at %s", ctime(&now));
+       smokey_trace("     seq_heap_size=%zuk", md->seq_max_heap_size / 1024);
+       smokey_trace("     random_alloc_rounds=%d", md->random_rounds);
+       smokey_trace("     pattern_heap_size=%zuk", md->pattern_heap_size / 
1024);
+       smokey_trace("     pattern_check_rounds=%d", md->pattern_rounds);
+       
+       CPU_ZERO(&affinity);
+       CPU_SET(0, &affinity);
+       ret = sched_setaffinity(0, sizeof(affinity), &affinity);
+       if (ret) {
+               smokey_warning("failed setting CPU affinity");
+               return -ret;
+       }
+
+       /* This switches to real-time mode over Cobalt. */
+       param.sched_priority = 1;
+       pthread_setschedparam(pthread_self(), SCHED_FIFO, &param);
+
+       /*
+        * Create a series of heaps of increasing size, allocating
+        * then freeing all blocks sequentially from them, ^2 block
+        * sizes up to half of the heap size. Test multiple patterns:
+        *
+        * - alloc -> free_in_alloc_order
+        * - alloc -> free_in_alloc_order -> (re)alloc
+        * - alloc -> free_in_random_order
+        * - alloc -> free_in_random_order -> (re)alloc
+        */
+       for (heap_size = md->seq_min_heap_size;
+            heap_size < md->seq_max_heap_size; heap_size <<= 1) {
+               for (block_size = 16;
+                    block_size < heap_size / 2; block_size <<= 1) {
+                       ret = test_seq(md, heap_size, block_size,
+                                      test_flags(md, MEMCHECK_ZEROOVRD));
+                       if (ret) {
+                               smokey_trace("failed with %zuk heap, "
+                                            "%zu-byte block (pow2)",
+                                            heap_size / 1024, block_size);
+                               return ret;
+                       }
+               }
+               for (block_size = 16;
+                    block_size < heap_size / 2; block_size <<= 1) {
+                       ret = test_seq(md, heap_size, block_size,
+                              test_flags(md, 
MEMCHECK_ZEROOVRD|MEMCHECK_REALLOC));
+                       if (ret) {
+                               smokey_trace("failed with %zuk heap, "
+                                            "%zu-byte block (pow2, realloc)",
+                                            heap_size / 1024, block_size);
+                               return ret;
+                       }
+               }
+               for (block_size = 16;
+                    block_size < heap_size / 2; block_size <<= 1) {
+                       ret = test_seq(md, heap_size, block_size,
+                              test_flags(md, 
MEMCHECK_ZEROOVRD|MEMCHECK_SHUFFLE));
+                       if (ret) {
+                               smokey_trace("failed with %zuk heap, "
+                                            "%zu-byte block (pow2, shuffle)",
+                                            heap_size / 1024, block_size);
+                               return ret;
+                       }
+               }
+               for (block_size = 16;
+                    block_size < heap_size / 2; block_size <<= 1) {
+                       ret = test_seq(md, heap_size, block_size,
+                              test_flags(md, 
MEMCHECK_ZEROOVRD|MEMCHECK_REALLOC|MEMCHECK_SHUFFLE));
+                       if (ret) {
+                               smokey_trace("failed with %zuk heap, "
+                                            "%zu-byte block (pow2, shuffle, 
realloc)",
+                                            heap_size / 1024, block_size);
+                               return ret;
+                       }
+               }
+       }
+
+       ret = dump_stats(md, "SEQUENTIAL ALLOC->FREE, ^2 BLOCK SIZES");
+       if (ret)
+               return ret;
+
+       /*
+        * Create a series of heaps of increasing size, allocating
+        * then freeing all blocks sequentially from them, random
+        * block sizes. Test multiple patterns as previously with ^2
+        * block sizes.
+        */
+       for (heap_size = md->seq_min_heap_size;
+            heap_size < md->seq_max_heap_size; heap_size <<= 1) {
+               for (runs = 0; runs < md->random_rounds; runs++) {
+                       block_size = (random() % heap_size) ?: 1;
+                       ret = test_seq(md, heap_size, block_size, 0);
+                       if (ret) {
+                               smokey_trace("failed with %zuk heap, "
+                                            "%zu-byte block (random)",
+                                            heap_size / 1024, block_size);
+                               return ret;
+                       }
+               }
+       }
+       
+       for (heap_size = md->seq_min_heap_size;
+            heap_size < md->seq_max_heap_size; heap_size <<= 1) {
+               for (runs = 0; runs < md->random_rounds; runs++) {
+                       block_size = (random() % (heap_size / 2)) ?: 1;
+                       ret = test_seq(md, heap_size, block_size,
+                                      test_flags(md, MEMCHECK_REALLOC));
+                       if (ret) {
+                               smokey_trace("failed with %zuk heap, "
+                                            "%zu-byte block (random, realloc)",
+                                            heap_size / 1024, block_size);
+                               return ret;
+                       }
+               }
+       }
+       
+       for (heap_size = md->seq_min_heap_size;
+            heap_size < md->seq_max_heap_size; heap_size <<= 1) {
+               for (runs = 0; runs < md->random_rounds; runs++) {
+                       block_size = (random() % (heap_size / 2)) ?: 1;
+                       ret = test_seq(md, heap_size, block_size,
+                                      test_flags(md, MEMCHECK_SHUFFLE));
+                       if (ret) {
+                               smokey_trace("failed with %zuk heap, "
+                                            "%zu-byte block (random, shuffle)",
+                                            heap_size / 1024, block_size);
+                               return ret;
+                       }
+               }
+       }
+       
+       for (heap_size = md->seq_min_heap_size;
+            heap_size < md->seq_max_heap_size; heap_size <<= 1) {
+               for (runs = 0; runs < md->random_rounds; runs++) {
+                       block_size = (random() % (heap_size / 2)) ?: 1;
+                       ret = test_seq(md, heap_size, block_size,
+                              test_flags(md, 
MEMCHECK_REALLOC|MEMCHECK_SHUFFLE));
+                       if (ret) {
+                               smokey_trace("failed with %zuk heap, "
+                                            "%zu-byte block (random, shuffle, 
realloc)",
+                                            heap_size / 1024, block_size);
+                               return ret;
+                       }
+               }
+       }
+
+       ret = dump_stats(md, "SEQUENTIAL ALLOC->FREE, RANDOM BLOCK SIZES");
+       if (ret)
+               return ret;
+
+       smokey_trace("\n(running the pattern check test for '%s'"
+                    " -- this may take some time)", md->name);
+
+       for (runs = 0; runs < md->pattern_rounds; runs++) {
+               /*
+                * pattern check is CPU-consuming and tends to starve
+                * the host kernel from CPU for too long, relax a bit
+                * between loops.
+                */
+               __RT(clock_nanosleep(CLOCK_MONOTONIC, 0, &idle, NULL));
+               block_size = (random() % (heap_size / 2)) ?: 1;
+               ret = test_seq(md, md->pattern_heap_size, block_size,
+                              test_flags(md, 
MEMCHECK_SHUFFLE|MEMCHECK_PATTERN));
+               if (ret) {
+                       smokey_trace("failed with %zuk heap, "
+                                    "%zu-byte block (random, shuffle, check)",
+                                    md->pattern_heap_size / 1024, block_size);
+                       return ret;
+               }
+       }
+       
+       now = time(NULL);
+       smokey_trace("\n== memcheck finished at %s", ctime(&now));
+
+       return ret;
+}
+
+#ifdef CONFIG_XENO_COBALT
+
+#include <cobalt/tunables.h>
+
+static int memcheck_tune(void)
+{
+       set_config_tunable(print_buffer_size, 512 * 1024);
+
+       return 0;
+}
+
+static struct setup_descriptor memcheck_setup = {
+       .name = "memcheck",
+       .tune = memcheck_tune,
+};
+
+user_setup_call(memcheck_setup);
+
+#endif
diff --git a/testsuite/smokey/memcheck/memcheck.h 
b/testsuite/smokey/memcheck/memcheck.h
new file mode 100644
index 0000000..881d22d
--- /dev/null
+++ b/testsuite/smokey/memcheck/memcheck.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 Philippe Gerum <r...@xenomai.org>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef SMOKEY_MEMCHECK_H
+#define SMOKEY_MEMCHECK_H
+
+#include <sys/types.h>
+#include <boilerplate/ancillaries.h>
+#include <smokey/smokey.h>
+
+#define MEMCHECK_ZEROOVRD   1
+#define MEMCHECK_SHUFFLE    2
+#define MEMCHECK_PATTERN    4
+#define MEMCHECK_REALLOC    8
+#define MEMCHECK_ALL_FLAGS  0xf
+
+struct memcheck_descriptor {
+       const char *name;
+       int (*init)(void *heap, void *mem, size_t heap_size);
+       void (*destroy)(void *heap);
+       void *(*alloc)(void *heap, size_t size);
+       int (*free)(void *heap, void *block);
+       size_t (*get_used_size)(void *heap);
+       size_t (*get_usable_size)(void *heap);
+       size_t (*get_arena_size)(size_t heap_size);
+       size_t seq_min_heap_size;
+       size_t seq_max_heap_size;
+       int random_rounds;
+       size_t pattern_heap_size;
+       int pattern_rounds;
+       void *heap;
+       int valid_flags;
+};
+
+#define HEAP_INIT_T(__p)    ((int (*)(void *heap, void *mem, size_t 
size))(__p))
+#define HEAP_DESTROY_T(__p) ((void (*)(void *heap))(__p))
+#define HEAP_ALLOC_T(__p)   ((void *(*)(void *heap, size_t size))(__p))
+#define HEAP_FREE_T(__p)    ((int (*)(void *heap, void *block))(__p))
+#define HEAP_USED_T(__p)    ((size_t (*)(void *heap))(__p))
+#define HEAP_USABLE_T(__p)  ((size_t (*)(void *heap))(__p))
+
+#define MEMCHECK_ARGS                                  \
+       SMOKEY_ARGLIST(                                 \
+               SMOKEY_SIZE(seq_heap_size),             \
+               SMOKEY_SIZE(pattern_heap_size),         \
+               SMOKEY_INT(random_alloc_rounds),        \
+               SMOKEY_INT(pattern_check_rounds),       \
+               SMOKEY_INT(max_results),                \
+       )
+  
+#define MEMCHECK_HELP_STRINGS                                          \
+       "\tseq_heap_size=<size[K|M|G]>\tmax. heap size for sequential alloc 
tests\n" \
+       "\tpattern_heap_size=<size[K|M|G]>\tmax. heap size for pattern check 
test\n" \
+       "\trandom_alloc_rounds=<N>\t\t# of rounds of random-size allocations\n" 
\
+       "\tpattern_check_rounds=<N>\t# of rounds of pattern check tests\n" \
+       "\tmax_results=<N>\t# of result lines (worst-case first, -1=all)\n" \
+       "\tSet --verbose=2 for detailed runtime statistics.\n"
+
+int memcheck_run(struct memcheck_descriptor *md,
+                struct smokey_test *t,
+                int argc, char *const argv[]);
+
+#endif /* SMOKEY_MEMCHECK_H */
diff --git a/testsuite/smokey/memory-heapmem/Makefile.am 
b/testsuite/smokey/memory-heapmem/Makefile.am
new file mode 100644
index 0000000..35c4a94
--- /dev/null
+++ b/testsuite/smokey/memory-heapmem/Makefile.am
@@ -0,0 +1,9 @@
+
+noinst_LIBRARIES = libmemory-heapmem.a
+
+libmemory_heapmem_a_SOURCES = heapmem.c
+
+libmemory_heapmem_a_CPPFLAGS =         \
+       @XENO_USER_CFLAGS@      \
+       -I$(srcdir)/..          \
+       -I$(top_srcdir)/include
diff --git a/testsuite/smokey/memory-heapmem/heapmem.c 
b/testsuite/smokey/memory-heapmem/heapmem.c
new file mode 100644
index 0000000..1ce7b4e
--- /dev/null
+++ b/testsuite/smokey/memory-heapmem/heapmem.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 Philippe Gerum <r...@xenomai.org>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#include <boilerplate/heapmem.h>
+#include "memcheck/memcheck.h"
+
+smokey_test_plugin(memory_heapmem,
+                  MEMCHECK_ARGS,
+                  "Check for the heapmem allocator sanity.\n"
+                  MEMCHECK_HELP_STRINGS
+       );
+
+#define MIN_HEAP_SIZE  8192
+#define MAX_HEAP_SIZE  (1024 * 1024 * 2)
+#define RANDOM_ROUNDS  1024
+
+#define PATTERN_HEAP_SIZE  (128*1024)
+#define PATTERN_ROUNDS     128
+
+static struct heap_memory heap;
+
+static size_t get_arena_size(size_t heap_size)
+{
+       return HEAPMEM_ARENA_SIZE(heap_size);
+}
+
+static struct memcheck_descriptor heapmem_descriptor = {
+       .name = "heapmem",
+       .init = HEAP_INIT_T(heapmem_init),
+       .destroy = HEAP_DESTROY_T(heapmem_destroy),
+       .alloc = HEAP_ALLOC_T(heapmem_alloc),
+       .free = HEAP_FREE_T(heapmem_free),
+       .get_usable_size = HEAP_USABLE_T(heapmem_usable_size),
+       .get_used_size = HEAP_USED_T(heapmem_used_size),
+       .seq_min_heap_size = MIN_HEAP_SIZE,
+       .seq_max_heap_size = MAX_HEAP_SIZE,
+       .random_rounds = RANDOM_ROUNDS,
+       .pattern_heap_size = PATTERN_HEAP_SIZE,
+       .pattern_rounds = PATTERN_ROUNDS,
+       .heap = &heap,
+       .get_arena_size = get_arena_size,
+       .valid_flags = MEMCHECK_ALL_FLAGS,
+};
+
+static int run_memory_heapmem(struct smokey_test *t,
+                             int argc, char *const argv[])
+{
+       return memcheck_run(&heapmem_descriptor, t, argc, argv);
+}
diff --git a/testsuite/smokey/memory-pshared/Makefile.am 
b/testsuite/smokey/memory-pshared/Makefile.am
new file mode 100644
index 0000000..59df783
--- /dev/null
+++ b/testsuite/smokey/memory-pshared/Makefile.am
@@ -0,0 +1,9 @@
+
+noinst_LIBRARIES = libmemory-pshared.a
+
+libmemory_pshared_a_SOURCES = pshared.c
+
+libmemory_pshared_a_CPPFLAGS =                 \
+       @XENO_USER_CFLAGS@              \
+       -I$(srcdir)/..                  \
+       -I$(top_srcdir)/include
diff --git a/testsuite/smokey/memory-pshared/pshared.c 
b/testsuite/smokey/memory-pshared/pshared.c
new file mode 100644
index 0000000..584b301
--- /dev/null
+++ b/testsuite/smokey/memory-pshared/pshared.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 Philippe Gerum <r...@xenomai.org>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#include <xenomai/init.h>
+#include <xenomai/tunables.h>
+#include <copperplate/heapobj.h>
+#include "memcheck/memcheck.h"
+
+smokey_test_plugin(memory_pshared,
+                  MEMCHECK_ARGS,
+                  "Check for the pshared allocator sanity.\n"
+                  MEMCHECK_HELP_STRINGS
+       );
+
+#define MIN_HEAP_SIZE  8192
+#define MAX_HEAP_SIZE  (1024 * 1024 * 2)
+#define RANDOM_ROUNDS  128
+
+#define PATTERN_HEAP_SIZE  (128*1024)
+#define PATTERN_ROUNDS     128
+
+static struct heapobj heap;
+
+static int do_pshared_init(void *heap, void *mem, size_t arena_size)
+{
+       /* mem is ignored, pshared uses its own memory. */
+       return heapobj_init(heap, "memcheck", arena_size);
+}
+
+static void do_pshared_destroy(void *heap)
+{
+       heapobj_destroy(heap);
+}
+
+static void *do_pshared_alloc(void *heap, size_t size)
+{
+       return heapobj_alloc(heap, size);
+}
+
+static int do_pshared_free(void *heap, void *block)
+{
+       heapobj_free(heap, block);
+
+       return 0;       /* Hope for the best. */
+}
+
+static size_t do_pshared_used_size(void *heap)
+{
+       return heapobj_inquire(heap);
+}
+
+static size_t do_pshared_usable_size(void *heap)
+{
+       return heapobj_get_size(heap);
+}
+
+static size_t do_pshared_arena_size(size_t heap_size)
+{
+       struct heapobj h;
+       size_t overhead;
+       int ret;
+
+       ret = heapobj_init(&h, "memcheck", heap_size);
+       if (ret)
+               return 0;
+
+       overhead = heap_size - heapobj_get_size(&h);
+       heapobj_destroy(&h);
+
+       /*
+        * pshared must have no external overhead, since
+        * heapobj_init() allocates the memory it needs.  Make sure
+        * this assumption is correct for any tested size.
+        */
+       return overhead == 0 ? heap_size : 0;
+}
+
+static struct memcheck_descriptor pshared_descriptor = {
+       .name = "pshared",
+       .init = HEAP_INIT_T(do_pshared_init),
+       .destroy = HEAP_DESTROY_T(do_pshared_destroy),
+       .alloc = HEAP_ALLOC_T(do_pshared_alloc),
+       .free = HEAP_FREE_T(do_pshared_free),
+       .get_usable_size = HEAP_USABLE_T(do_pshared_usable_size),
+       .get_used_size = HEAP_USED_T(do_pshared_used_size),
+       .get_arena_size = do_pshared_arena_size,
+       .seq_min_heap_size = MIN_HEAP_SIZE,
+       .seq_max_heap_size = MAX_HEAP_SIZE,
+       .random_rounds = RANDOM_ROUNDS,
+       .pattern_heap_size = PATTERN_HEAP_SIZE,
+       .pattern_rounds = PATTERN_ROUNDS,
+       /* heapobj-pshared has overgead even for ^2 sizes, can't check for 
ZEROOVRD. */
+       .valid_flags = MEMCHECK_ALL_FLAGS & ~MEMCHECK_ZEROOVRD,
+       .heap = &heap,
+};
+
+static int run_memory_pshared(struct smokey_test *t,
+                             int argc, char *const argv[])
+{
+       return memcheck_run(&pshared_descriptor, t, argc, argv);
+}
+
+static int memcheck_pshared_tune(void)
+{
+       /*
+        * We create test pools from the main one: make sure the
+        * latter is large enough.
+        */
+       set_config_tunable(mem_pool_size, MAX_HEAP_SIZE + 1024 * 1024);
+
+       return 0;
+}
+
+static struct setup_descriptor memcheck_pshared_setup = {
+       .name = "memcheck_pshared",
+       .tune = memcheck_pshared_tune,
+};
+
+user_setup_call(memcheck_pshared_setup);
diff --git a/testsuite/smokey/memory-tlsf/Makefile.am 
b/testsuite/smokey/memory-tlsf/Makefile.am
new file mode 100644
index 0000000..901e6fd
--- /dev/null
+++ b/testsuite/smokey/memory-tlsf/Makefile.am
@@ -0,0 +1,10 @@
+
+noinst_LIBRARIES = libmemory-tlsf.a
+
+libmemory_tlsf_a_SOURCES = tlsf.c
+
+libmemory_tlsf_a_CPPFLAGS =            \
+       @XENO_USER_CFLAGS@              \
+       -I$(top_srcdir)/lib/boilerplate \
+       -I$(srcdir)/..                  \
+       -I$(top_srcdir)/include
diff --git a/testsuite/smokey/memory-tlsf/tlsf.c 
b/testsuite/smokey/memory-tlsf/tlsf.c
new file mode 100644
index 0000000..5de9007
--- /dev/null
+++ b/testsuite/smokey/memory-tlsf/tlsf.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 Philippe Gerum <r...@xenomai.org>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#include <tlsf/tlsf.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include "memcheck/memcheck.h"
+
+smokey_test_plugin(memory_tlsf,
+                  MEMCHECK_ARGS,
+                  "Check for the TLSF allocator sanity.\n"
+                  MEMCHECK_HELP_STRINGS
+       );
+
+#define MIN_HEAP_SIZE  8192
+#define MAX_HEAP_SIZE  (1024 * 1024 * 2)
+#define RANDOM_ROUNDS  1024
+
+#define PATTERN_HEAP_SIZE  (128*1024)
+#define PATTERN_ROUNDS     128
+
+static struct memcheck_descriptor tlsf_descriptor;
+
+static pthread_mutex_t tlsf_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static size_t overhead;
+
+static size_t test_pool_size; /* TLSF does not save this information. */
+
+static int do_tlsf_init(void *dummy, void *mem, size_t pool_size)
+{
+       tlsf_descriptor.heap = mem;
+       return init_memory_pool(pool_size, mem) == -1L ? -ENOMEM : 0;
+}
+
+static void do_tlsf_destroy(void *pool)
+{
+       destroy_memory_pool(pool);
+}
+
+static void *do_tlsf_alloc(void *pool, size_t size)
+{
+       void *p;
+
+       pthread_mutex_lock(&tlsf_lock);
+       p = malloc_ex(size, pool);
+       pthread_mutex_unlock(&tlsf_lock);
+
+       return p;
+}
+
+static int do_tlsf_free(void *pool, void *block)
+{
+       pthread_mutex_lock(&tlsf_lock);
+       free_ex(block, pool);
+       pthread_mutex_unlock(&tlsf_lock);
+
+       return 0;       /* Yeah, well... */
+}
+
+static size_t do_tlsf_used_size(void *pool)
+{
+       /* Do not count the overhead memory for the TLSF header. */
+       return get_used_size(pool) - overhead;
+}
+
+static size_t do_tlsf_usable_size(void *pool)
+{
+       return test_pool_size;
+}
+
+static size_t do_tlsf_arena_size(size_t pool_size)
+{
+       size_t available_size;
+       void *pool;
+
+       /*
+        * The area size is the total amount of memory some allocator
+        * may need for managing a heap, including its metadata. We
+        * need to figure out how much memory overhead TLSF has for a
+        * given pool size, which we add to the ideal pool_size for
+        * determining the arena size.
+        */
+       test_pool_size = pool_size;
+       pool = __STD(malloc(pool_size));
+       available_size = init_memory_pool(pool_size, pool);
+       if (available_size == (size_t)-1) {
+               __STD(free(pool));
+               return 0;
+       }
+
+       destroy_memory_pool(pool);
+       overhead = pool_size - available_size;
+       __STD(free(pool));
+
+       return pool_size + overhead;
+}
+
+static struct memcheck_descriptor tlsf_descriptor = {
+       .name = "tlsf",
+       .init = HEAP_INIT_T(do_tlsf_init),
+       .destroy = HEAP_DESTROY_T(do_tlsf_destroy),
+       .alloc = HEAP_ALLOC_T(do_tlsf_alloc),
+       .free = HEAP_FREE_T(do_tlsf_free),
+       .get_usable_size = HEAP_USABLE_T(do_tlsf_usable_size),
+       .get_used_size = HEAP_USED_T(do_tlsf_used_size),
+       .get_arena_size = do_tlsf_arena_size,
+       .seq_min_heap_size = MIN_HEAP_SIZE,
+       .seq_max_heap_size = MAX_HEAP_SIZE,
+       .random_rounds = RANDOM_ROUNDS,
+       .pattern_heap_size = PATTERN_HEAP_SIZE,
+       .pattern_rounds = PATTERN_ROUNDS,
+       /* TLSF always has overhead, can't check for ZEROOVRD. */
+       .valid_flags = MEMCHECK_ALL_FLAGS & ~MEMCHECK_ZEROOVRD,
+};
+
+static int run_memory_tlsf(struct smokey_test *t,
+                          int argc, char *const argv[])
+{
+       return memcheck_run(&tlsf_descriptor, t, argc, argv);
+}


_______________________________________________
Xenomai-git mailing list
Xenomai-git@xenomai.org
https://xenomai.org/mailman/listinfo/xenomai-git

Reply via email to