tralloc

A C library that adds size information to memory allocations.
git clone git://src.gearsix.net/tralloc
Log | Files | Refs | Atom | README

commit cdd8a8b1eb1e87c02b6809a9dbab7d69299737f4
parent ec743e370d6ad71463930e5e744d0e54f80ebb97
Author: gearsix <gearsix@tuta.io>
Date:   Mon, 13 Sep 2021 13:11:57 +0100

first release tidyup

- renamed to "htrack"
- renamed functions accordingly
- minor bugfixes to heapsiz tracking
- added comment documentation
- added TODO.txt
- added a very rudimentary test file

Diffstat:
AREADME.md | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ATODO.txt | 21+++++++++++++++++++++
Ahtrack.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahtrack.h | 36++++++++++++++++++++++++++++++++++++
Dstdlibw.c | 81-------------------------------------------------------------------------------
Dstdlibw.h | 54------------------------------------------------------
Atest.c | 50++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 282 insertions(+), 135 deletions(-)

diff --git a/README.md b/README.md @@ -0,0 +1,108 @@ + +# htrack + +An ANSI-C libray for tracking heap-allocated memory. + +## Overview + +This library provides several functions that act as wrappers +around the _stdlib_ memory allocation functions to track and +provide information on the number of heap-allocated bytes for +individual pointers and overall. + +It's implemented with functions the act as _stdlib_ wrappers so +that it can sit ontop of whatever _stdlib_ implementation you're +using. The downside of this is that it's unable to track any +allocations made without these wrappers. This is acceptable since +it's intended to be used for tracking allocations made by yourself. + +There are also a few usability pitfalls: if you call some of the +functions on pointers that **have not** been allocated by htrack +allocation functions, then it'll cause a memory error. +This behaviour is consistent with stdlib though. Make sure to read +function documentation. + +## Goals + +- Track the total number of heap-allocated bytes. +- Track number of bytes allocated for individual pointers. + +## Guidelines + +- **Do not** call `htrack_realloc()`, `htrack_allocsiz()` or +`htrack_free` on a pointer not allocated by one of the htrack stdlib +wrapper functions (same way you wouldn't call free on a pointer you +didn't allocate with stdlib). Otherwise you'll probably run into a +segfault. +- Don't expect htrack to track memory allocated outside of it's own +htrack functions. +- You should be able to use the _htrack stdlib wrappers_ just as you +would use their stdlib counterparts without error. + +## API + +**htrack_siz** + + size_t htrack_siz(); + +Returns the number of bytes heap-allocated by _htrack memory allocation_ +functions. + +**htrack_limit** + + size_t htrack_limit(); + +Returns the maximum limit set on `htrack_siz`. +If 0 is returned, there is no limit (returns 0 by default). + +**htrack_setlimit** + + size_t htrack_setlimit(size_t n); + +Sets the maximum limit on `htrack_siz`. +If this limit is reached, then htrack alloc functions will fail and +return NULL. +Returns `n` if successful and 0 if unsuccessful. + +**htrack_allocsiz** + + size_t htrack_allocsiz(void *ptr); + +Returns number of bytes allocated for `ptr`. + +> `ptr` **must** be a _htrack allocated pointer_. + +### stdlib wrappers + +These functions behave exactly the same and their stdlib counterparts. +The only difference is that they will also add the memory overhead +required to track the number of allocated bytes for each pointer +(which `sizeof(size_t)` per-pointer) and increment/decrement the +tracked heapsiz accordingly. +This overhead is stored at index [-1] of the returned pointers and +used by functions within the library for tracking the heapsiz. + +**htrack_malloc** + + void *htrack_malloc(size_t n); + +**htrack_calloc** + + void *htrack_calloc(size_t num, size_t n); + +**htrack_realloc** + + void *htrack_realloc(void *ptr, size_t n); + +> `ptr` **must** be a pointer allocated by htrack. + +**htrack_free** + + void htrack_free(void *ptr); + +> `ptr` **must** be a pointer allocated by htrack. + +## Authors + +- gearsix + diff --git a/TODO.txt b/TODO.txt @@ -0,0 +1,21 @@ +LEGEND +------ + +[x] = done; +[~] = in-progress; +[-] = won't do; +[ ] = not started; + +(!) = Priority; +(W) = Will do; +(S) = Should do; +(C) = Could do; +(N) = Won't do; +(?) = Needs investigating; + +TODO +---- +- [x] (N) Handle user errors (e.g. calling `htrack_allocsiz` on pointer not allocated by htrack). + The stdlib doesn't do this for `free` when you call it on a pointer not malloc'd. +- [ ] (S) Find a better way to handle errors that assert(...) + This was a semi-temporary handler for development. diff --git a/htrack.c b/htrack.c @@ -0,0 +1,67 @@ +#include "htrack.h" + +static size_t heapsiz = 0, heaplim = 0; + +size_t htrack_siz() +{ + return heapsiz; +} + +size_t htrack_limit() +{ + return heaplim; +} + +size_t htrack_setlimit(size_t n) +{ + assert(heapsiz <= n); + return (heaplim = n); +} + +size_t htrack_allocsiz(void *ptr) +{ + return ((size_t *)ptr)[-1]; +} + +void *htrack_malloc(size_t n) +{ + size_t *ptr; + assert(n); + n += sizeof(size_t); + if (heaplim > 0) assert(heapsiz + n <= heaplim); + ptr = (size_t *)malloc(n); + heapsiz += n; + ptr[0] = n; + return &ptr[1]; +} + +void *htrack_calloc(size_t num, size_t n) +{ + size_t *ptr, psiz; + assert(n && num); + psiz = (num * n) + sizeof(size_t); + if (heaplim > 0) assert(heapsiz + psiz <= heaplim); + ptr = (size_t *)calloc(1, psiz); + ptr[0] = psiz; + return &ptr[1]; +} + +void *htrack_realloc(void *ptr, size_t n) +{ + size_t *ret, m = htrack_allocsiz(ptr); + n += sizeof(size_t); + if (heaplim > 0) assert(heapsiz + (-m + n) <= heaplim); + ret = (size_t *)realloc((size_t *)ptr-1, n); + assert(ret != NULL); + heapsiz += (-m + n); + ret[0] = n; + return &ret[1]; +} + +void htrack_free(void *ptr) +{ + size_t n = ((size_t *)ptr)[-1]; + if (heaplim > 0) assert(heapsiz - n >= heaplim); + heapsiz -= n; + free((size_t *)ptr-1); +} diff --git a/htrack.h b/htrack.h @@ -0,0 +1,36 @@ +/** + * See README.md for documentation +**/ + +#ifndef STDLIBW +#define STDLIBW + +#include <stdlib.h> +#include <assert.h> + +/* num. bytes heap-allocated by htrack */ +size_t htrack_siz(); + +/* the max. number of heap-allocated bytes htrack will allow */ +size_t htrack_limit(); + +size_t htrack_setlimit(size_t n); + +/* The number of bytes allocated for `ptr`. + * ptr MUST be allocated by htrack */ +size_t htrack_allocsiz(void *ptr); + +/** + * stdlib wrappers - behave the same as their stdlib counterparts +**/ +void *htrack_malloc(size_t n); + +void *htrack_calloc(size_t num, size_t n); + +/* ptr MUST be allocated by htrack */ +void *htrack_realloc(void *ptr, size_t n); + +/* ptr MUST be allocated by htrack */ +void htrack_free(void *ptr); + +#endif diff --git a/stdlibw.c b/stdlibw.c @@ -1,81 +0,0 @@ -#include "stdlibw.h" - -/** _heap - * main function for tracking allocated heap memory using a - * `static size_t`. Only tracked memory allocated with other - * _stdlibw_ functions -**/ -static size_t _heap(char op, size_t n) -{ - static size_t siz = 0, lim = 0; - switch (op) { - case 0: break; - case '!': return (lim = n); - case '?': return lim; - case '+': - if (lim > 0) assert(siz + n <= lim); - siz += n; - break; - case '-': - if (lim > 0) assert(siz - n >= lim); - siz -= n; - break; - default: /* invalid op */ - assert(1); - } - return siz; -} - -size_t heapsiz() -{ - return _heap(0, 0); -} - -size_t heaplim() -{ - return _heap('?', 0); -} - -size_t limheap(size_t n) -{ - return _heap('!', 0); -} - -size_t sizeofw(void *ptr) -{ - return ((size_t *)ptr)[-1]; -} - -void *mallocw(size_t siz) -{ - size_t *ptr; - assert(siz); - ptr = (size_t *)malloc(siz += sizeof(size_t)); - _heap('+', siz); - ptr[0] = siz; - return &ptr[1]; -} - -void *callocw(size_t num, size_t siz) -{ - size_t *ptr; - assert(siz && (size_t)-1/num); - ptr = (size_t *)mallocw(num * siz); - return ptr; -} - -void *realloc(void *ptr, size_t siz) -{ - size_t *ret, psiz = sizeofw(ptr); - assert(siz > psiz); - ret = mallocw(siz); - while (--psiz > 0) ret[psiz] = ((size_t *)ptr)[psiz]; - freew(ptr); - return ret; -} - -void freew(void *ptr) -{ - _heap('-', ((size_t *)ptr)[-1]); - free((size_t *)ptr-1); -} diff --git a/stdlibw.h b/stdlibw.h @@ -1,54 +0,0 @@ -#ifndef STDLIBW -#define STDLIBW - -#include <stdlib.h> -#include <assert.h> - -/** heapsiz - heap size? - * returns the currently tracked `heapsiz` -**/ -size_t heapsiz(); - -/** heaplim - heapsiz limit? - * returns the current limit set on `heapsiz`. - * if 0 is returned, there is no limit. -**/ -size_t heaplim(); - -/** limheap - limit heapsiz - * set the limit on `heapsiz` to `siz`, returns the set limit. - * if the returned value is not `n`, then the call failed. -**/ -size_t limheap(size_t n); - -/** - * sizeofw - sizeof (wrapper) - * returns the number of allocated bytes for `ptr`. - * will fail if called on a pointer not allocated by stdlibw. -**/ -size_t sizeofw(void *ptr); - -/** mallocw - malloc (wrapper) - * malloc (sizeof(size_t) + siz) bytes and return [1] of that pointer. - * [0] is used to store `siz` for other stdlibw operations. - * `(siz + sizeof(size_t))` will be added to tracked `heapsiz`. -**/ -void *mallocw(size_t siz); - -/** callocw - calloc (wrapper) - * see `mallocw()`. Duplicate, but for `calloc`. -**/ -void *callocw(size_t num, size_t siz); - -/** reallocw - realloc (wrapper) - * `mallocw()` a new pointer, copy `ptr` contents to it and `freew()` - * ptr. -**/ -void *reallocw(void *ptr, size_t siz); - -/** freew - free (wrapper) - * free a pointer allocated by a *stdlibw* alloc function. -**/ -void freew(void *ptr); - -#endif diff --git a/test.c b/test.c @@ -0,0 +1,50 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "htrack.h" + +void fail(const char *msg) +{ + fprintf(stderr, msg); + exit(EXIT_FAILURE); +} + +int main (int argc, char *argv[]) +{ + size_t siz; + char *str; + + printf("htrack_siz: %lu\n", htrack_siz()); + if (htrack_siz() != 0) fail("initial htrack_siz not 0\n"); + printf("htrack_limit: %lu\n", htrack_limit()); + if (htrack_limit() != 0) fail("initial htrack_setlimit not 0\n"); + + siz = 6; + str = htrack_malloc(siz); + printf("str = '%s', %zu bytes\n", str, htrack_allocsiz(str)); + if (str == NULL) fail("htrack_malloc failed\n"); + if (htrack_allocsiz(str) != (siz + sizeof(size_t))) fail("invalid htrack_allocsiz value\n"); + strcpy(str, "foobar"); + if (strcmp(str, "foobar") != 0) fail("strcpy failed\n"); + printf("str = '%s', %zu bytes\n", str, htrack_allocsiz(str)); + + siz = 12; + str = htrack_realloc(str, siz); + printf("str = '%s', %zu bytes\n", str, htrack_allocsiz(str)); + if (str == NULL) fail("htrack_realloc failed\n"); + if (htrack_allocsiz(str) != (siz + sizeof(size_t))) fail("invalid htrack_allocsiz value\n"); + strcpy(str, "foobarfoobar"); + if (strcmp(str, "foobarfoobar") != 0) fail("strcpy failed\n"); + printf("str = '%s', %zu bytes\n", str, htrack_allocsiz(str)); + + htrack_free(str); + + siz = 6; + htrack_setlimit(10); + str = htrack_calloc(2, siz); /* should cause assertion failure */ + printf("str = '%s', %zu bytes\n", str, htrack_allocsiz(str)); + htrack_free(str); + + + return EXIT_SUCCESS; +}