Как-то так. Вычисляет размер максимально большого свободного блока и суммарный размер первых нескольких больших блоков, что даёт аппроксимацию свободного объёма памяти в целом и сведения о фрагментации. Работает с любой IDE, любым компилятором, любой ОС, любой библиотекой... где есть malloc. Выполняется относительно медленно. Ограничение в MAX_SIZE нужно для того, чтобы в среде linux не глючил valgrind, не срабатывали лимиты (ulimit) и linux не выделял бы бесконечное количество памяти. В системе без виртуальной памяти ограничение не нужно.
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include <assert.h>
#include <stdio.h>
struct s_memfree {
        size_t maxsize;    // макс. размер свободного блока
        size_t minfree;    // объём свобнодной памяти равен или больше minfree
        unsigned nblocks;  // число блоков в minfree
};
#ifndef TEST_MEMFREE
#define LIMIT 16        /* usually ok for embedded platform */
#define MALLOC malloc
#define FREE free
#else /* TEST_MEMFREE */
#define LIMIT 16
void *MALLOC(size_t n);
void FREE(void *p);
#endif
/* because of OS limits, etc... */
#if SIZE_MAX <= (64*1024*1024/LIMIT)
#define MAX_SIZE SIZE_MAX
#else
#define MAX_SIZE (64*1024*1024/LIMIT)
#endif
/* allocate block with maximum possible size and return pointer 
 * to it (block contains it's size), return NULL if block can't be allocated */
static size_t *maxblock(size_t max)
{
uint_fast8_t count=0;
size_t bs, min=sizeof(size_t);
size_t *p=NULL;
        while (max > min) {
                if (p!=NULL) FREE(p);
                bs=((unsigned long)max+min)/2+1;
                p=MALLOC(bs);
                if (p==NULL) max=bs-1;
                else *p=bs, min=bs;
                count++;
                assert(count < 48);  /* log2(2^32) */
        }
        if (p==NULL) p=MALLOC(min);
        if (p!=NULL) *p=min;
        return p;
}
/* compute summary size of free blocks with `max' size limit,
 * number of iterations limited by `limit' value */
static size_t minfree(size_t max, unsigned limit, unsigned *nblk)
{
size_t s, *p;
        p=maxblock(max);
        if (p==NULL) return 0;
        if (nblk!=NULL) (*nblk)++;
        if (limit>0) s=minfree(*p, limit-1, nblk);
                else s=0;
        s+=*p;
        FREE(p);
        return s;
}
// возвращает размер свободного блока максимального размера
size_t maxfree(void)
{
        size_t s, *p=maxblock(MAX_SIZE);
        if (p==NULL) return 0;
        s=*p;
        FREE(p);
        return s;
}
// возвращает минимальный объём свободной памяти и заполняет структуру (см. выше)
size_t memfree(struct s_memfree *r)
{
struct s_memfree my;
size_t *p;
        if (r==NULL) r=&my;
        r->maxsize=0, r->minfree=0, r->nblocks=0;
        p=maxblock(MAX_SIZE);
        if (p==NULL)
                return 0;
        r->maxsize=*p;
        r->nblocks++;
        r->minfree=r->maxsize+minfree(r->maxsize, LIMIT, &r->nblocks);
        FREE(p);
        return r->minfree;
}
 
[ZX]