commit 50eb88c8687e6bc8a1144aa2bfe6eb0d05c414f1
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
Diffstat:
A | README.md | | | 108 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | TODO.txt | | | 21 | +++++++++++++++++++++ |
A | htrack.c | | | 67 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | htrack.h | | | 36 | ++++++++++++++++++++++++++++++++++++ |
D | stdlibw.c | | | 81 | ------------------------------------------------------------------------------- |
D | stdlibw.h | | | 54 | ------------------------------------------------------ |
A | test.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;
+}