test_multi_heap.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. #include "catch.hpp"
  2. #include "multi_heap.h"
  3. #include "../multi_heap_config.h"
  4. #include <string.h>
  5. #include <assert.h>
  6. /* Insurance against accidentally using libc heap functions in tests */
  7. #undef free
  8. #define free #error
  9. #undef malloc
  10. #define malloc #error
  11. #undef calloc
  12. #define calloc #error
  13. #undef realloc
  14. #define realloc #error
  15. TEST_CASE("multi_heap simple allocations", "[multi_heap]")
  16. {
  17. uint8_t small_heap[128];
  18. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  19. size_t test_alloc_size = (multi_heap_free_size(heap) + 4) / 2;
  20. printf("New heap:\n");
  21. multi_heap_dump(heap);
  22. printf("*********************\n");
  23. uint8_t *buf = (uint8_t *)multi_heap_malloc(heap, test_alloc_size);
  24. printf("small_heap %p buf %p\n", small_heap, buf);
  25. REQUIRE( buf != NULL );
  26. REQUIRE((intptr_t)buf >= (intptr_t)small_heap);
  27. REQUIRE( (intptr_t)buf < (intptr_t)(small_heap + sizeof(small_heap)));
  28. REQUIRE( multi_heap_get_allocated_size(heap, buf) >= test_alloc_size );
  29. REQUIRE( multi_heap_get_allocated_size(heap, buf) < test_alloc_size + 16);
  30. memset(buf, 0xEE, test_alloc_size);
  31. REQUIRE( multi_heap_malloc(heap, test_alloc_size) == NULL );
  32. multi_heap_free(heap, buf);
  33. printf("Empty?\n");
  34. multi_heap_dump(heap);
  35. printf("*********************\n");
  36. /* Now there should be space for another allocation */
  37. buf = (uint8_t *)multi_heap_malloc(heap, test_alloc_size);
  38. REQUIRE( buf != NULL );
  39. multi_heap_free(heap, buf);
  40. REQUIRE( multi_heap_free_size(heap) > multi_heap_minimum_free_size(heap) );
  41. }
  42. TEST_CASE("multi_heap fragmentation", "[multi_heap]")
  43. {
  44. uint8_t small_heap[256];
  45. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  46. const size_t alloc_size = 24;
  47. void *p[4];
  48. for (int i = 0; i < 4; i++) {
  49. multi_heap_dump(heap);
  50. REQUIRE( multi_heap_check(heap, true) );
  51. p[i] = multi_heap_malloc(heap, alloc_size);
  52. printf("%d = %p ****->\n", i, p[i]);
  53. multi_heap_dump(heap);
  54. REQUIRE( p[i] != NULL );
  55. }
  56. printf("allocated %p %p %p %p\n", p[0], p[1], p[2], p[3]);
  57. REQUIRE( multi_heap_malloc(heap, alloc_size * 3) == NULL ); /* no room to allocate 3*alloc_size now */
  58. printf("4 allocations:\n");
  59. multi_heap_dump(heap);
  60. printf("****************\n");
  61. multi_heap_free(heap, p[0]);
  62. multi_heap_free(heap, p[1]);
  63. multi_heap_free(heap, p[3]);
  64. printf("1 allocations:\n");
  65. multi_heap_dump(heap);
  66. printf("****************\n");
  67. void *big = multi_heap_malloc(heap, alloc_size * 3);
  68. REQUIRE( p[3] == big ); /* big should go where p[3] was freed from */
  69. multi_heap_free(heap, big);
  70. multi_heap_free(heap, p[2]);
  71. printf("0 allocations:\n");
  72. multi_heap_dump(heap);
  73. printf("****************\n");
  74. big = multi_heap_malloc(heap, alloc_size * 2);
  75. REQUIRE( p[0] == big ); /* big should now go where p[0] was freed from */
  76. multi_heap_free(heap, big);
  77. }
  78. TEST_CASE("multi_heap many random allocations", "[multi_heap]")
  79. {
  80. uint8_t big_heap[1024];
  81. const int NUM_POINTERS = 64;
  82. printf("Running multi-allocation test...\n");
  83. void *p[NUM_POINTERS] = { 0 };
  84. size_t s[NUM_POINTERS] = { 0 };
  85. multi_heap_handle_t heap = multi_heap_register(big_heap, sizeof(big_heap));
  86. const size_t initial_free = multi_heap_free_size(heap);
  87. const int ITERATIONS = 100000;
  88. for (int i = 0; i < ITERATIONS; i++) {
  89. /* check all pointers allocated so far are valid inside big_heap */
  90. for (int j = 0; j < NUM_POINTERS; j++) {
  91. if (p[j] != NULL) {
  92. }
  93. }
  94. uint8_t n = rand() % NUM_POINTERS;
  95. if (rand() % 4 == 0) {
  96. /* 1 in 4 iterations, try to realloc the buffer instead
  97. of using malloc/free
  98. */
  99. size_t new_size = rand() % 1024;
  100. void *new_p = multi_heap_realloc(heap, p[n], new_size);
  101. printf("realloc %p -> %p (%zu -> %zu)\n", p[n], new_p, s[n], new_size);
  102. multi_heap_check(heap, true);
  103. if (new_size == 0 || new_p != NULL) {
  104. p[n] = new_p;
  105. s[n] = new_size;
  106. if (new_size > 0) {
  107. REQUIRE( p[n] >= big_heap );
  108. REQUIRE( p[n] < big_heap + sizeof(big_heap) );
  109. memset(p[n], n, new_size);
  110. }
  111. }
  112. continue;
  113. }
  114. if (p[n] != NULL) {
  115. if (s[n] > 0) {
  116. /* Verify pre-existing contents of p[n] */
  117. uint8_t compare[s[n]];
  118. memset(compare, n, s[n]);
  119. /*REQUIRE*/assert( memcmp(compare, p[n], s[n]) == 0 );
  120. }
  121. REQUIRE( multi_heap_check(heap, true) );
  122. multi_heap_free(heap, p[n]);
  123. printf("freed %p (%zu)\n", p[n], s[n]);
  124. if (!multi_heap_check(heap, true)) {
  125. printf("FAILED iteration %d after freeing %p\n", i, p[n]);
  126. multi_heap_dump(heap);
  127. REQUIRE(0);
  128. }
  129. }
  130. s[n] = rand() % 1024;
  131. REQUIRE( multi_heap_check(heap, true) );
  132. p[n] = multi_heap_malloc(heap, s[n]);
  133. printf("malloc %p (%zu)\n", p[n], s[n]);
  134. if (p[n] != NULL) {
  135. REQUIRE( p[n] >= big_heap );
  136. REQUIRE( p[n] < big_heap + sizeof(big_heap) );
  137. }
  138. if (!multi_heap_check(heap, true)) {
  139. printf("FAILED iteration %d after mallocing %p (%zu bytes)\n", i, p[n], s[n]);
  140. multi_heap_dump(heap);
  141. REQUIRE(0);
  142. }
  143. if (p[n] != NULL) {
  144. memset(p[n], n, s[n]);
  145. }
  146. }
  147. for (int i = 0; i < NUM_POINTERS; i++) {
  148. multi_heap_free(heap, p[i]);
  149. if (!multi_heap_check(heap, true)) {
  150. printf("FAILED during cleanup after freeing %p\n", p[i]);
  151. multi_heap_dump(heap);
  152. REQUIRE(0);
  153. }
  154. }
  155. REQUIRE( initial_free == multi_heap_free_size(heap) );
  156. }
  157. TEST_CASE("multi_heap_get_info() function", "[multi_heap]")
  158. {
  159. uint8_t heapdata[256];
  160. multi_heap_handle_t heap = multi_heap_register(heapdata, sizeof(heapdata));
  161. multi_heap_info_t before, after, freed;
  162. multi_heap_get_info(heap, &before);
  163. printf("before: total_free_bytes %zu\ntotal_allocated_bytes %zu\nlargest_free_block %zu\nminimum_free_bytes %zu\nallocated_blocks %zu\nfree_blocks %zu\ntotal_blocks %zu\n",
  164. before.total_free_bytes,
  165. before.total_allocated_bytes,
  166. before.largest_free_block,
  167. before.minimum_free_bytes,
  168. before.allocated_blocks,
  169. before.free_blocks,
  170. before.total_blocks);
  171. REQUIRE( 0 == before.allocated_blocks );
  172. REQUIRE( 0 == before.total_allocated_bytes );
  173. REQUIRE( before.total_free_bytes == before.minimum_free_bytes );
  174. void *x = multi_heap_malloc(heap, 32);
  175. multi_heap_get_info(heap, &after);
  176. printf("after: total_free_bytes %zu\ntotal_allocated_bytes %zu\nlargest_free_block %zu\nminimum_free_bytes %zu\nallocated_blocks %zu\nfree_blocks %zu\ntotal_blocks %zu\n",
  177. after.total_free_bytes,
  178. after.total_allocated_bytes,
  179. after.largest_free_block,
  180. after.minimum_free_bytes,
  181. after.allocated_blocks,
  182. after.free_blocks,
  183. after.total_blocks);
  184. REQUIRE( 1 == after.allocated_blocks );
  185. REQUIRE( 32 == after.total_allocated_bytes );
  186. REQUIRE( after.minimum_free_bytes < before.minimum_free_bytes);
  187. REQUIRE( after.minimum_free_bytes > 0 );
  188. multi_heap_free(heap, x);
  189. multi_heap_get_info(heap, &freed);
  190. printf("freed: total_free_bytes %zu\ntotal_allocated_bytes %zu\nlargest_free_block %zu\nminimum_free_bytes %zu\nallocated_blocks %zu\nfree_blocks %zu\ntotal_blocks %zu\n",
  191. freed.total_free_bytes,
  192. freed.total_allocated_bytes,
  193. freed.largest_free_block,
  194. freed.minimum_free_bytes,
  195. freed.allocated_blocks,
  196. freed.free_blocks,
  197. freed.total_blocks);
  198. REQUIRE( 0 == freed.allocated_blocks );
  199. REQUIRE( 0 == freed.total_allocated_bytes );
  200. REQUIRE( before.total_free_bytes == freed.total_free_bytes );
  201. REQUIRE( after.minimum_free_bytes == freed.minimum_free_bytes );
  202. }
  203. TEST_CASE("multi_heap minimum-size allocations", "[multi_heap]")
  204. {
  205. uint8_t heapdata[16384];
  206. void *p[sizeof(heapdata) / sizeof(void *)];
  207. const size_t NUM_P = sizeof(p) / sizeof(void *);
  208. multi_heap_handle_t heap = multi_heap_register(heapdata, sizeof(heapdata));
  209. size_t before_free = multi_heap_free_size(heap);
  210. size_t i;
  211. for (i = 0; i < NUM_P; i++) {
  212. p[i] = multi_heap_malloc(heap, 1);
  213. if (p[i] == NULL) {
  214. break;
  215. }
  216. }
  217. REQUIRE( i < NUM_P); // Should have run out of heap before we ran out of pointers
  218. printf("Allocated %zu minimum size chunks\n", i);
  219. REQUIRE( 0 == multi_heap_free_size(heap) );
  220. multi_heap_check(heap, true);
  221. /* Free in random order */
  222. bool has_allocations = true;
  223. while (has_allocations) {
  224. i = rand() % NUM_P;
  225. multi_heap_free(heap, p[i]);
  226. p[i] = NULL;
  227. multi_heap_check(heap, true);
  228. has_allocations = false;
  229. for (i = 0; i < NUM_P && !has_allocations; i++) {
  230. has_allocations = (p[i] != NULL);
  231. }
  232. }
  233. /* all freed! */
  234. REQUIRE( before_free == multi_heap_free_size(heap) );
  235. }
  236. TEST_CASE("multi_heap_realloc()", "[multi_heap]")
  237. {
  238. const uint32_t PATTERN = 0xABABDADA;
  239. uint8_t small_heap[300];
  240. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  241. uint32_t *a = (uint32_t *)multi_heap_malloc(heap, 64);
  242. uint32_t *b = (uint32_t *)multi_heap_malloc(heap, 32);
  243. REQUIRE( a != NULL );
  244. REQUIRE( b != NULL );
  245. REQUIRE( b > a); /* 'b' takes the block after 'a' */
  246. *a = PATTERN;
  247. uint32_t *c = (uint32_t *)multi_heap_realloc(heap, a, 72);
  248. REQUIRE( multi_heap_check(heap, true));
  249. REQUIRE( c != NULL );
  250. REQUIRE( c > b ); /* 'a' moves, 'c' takes the block after 'b' */
  251. REQUIRE( *c == PATTERN );
  252. #ifndef MULTI_HEAP_POISONING_SLOW
  253. // "Slow" poisoning implementation doesn't reallocate in place, so these
  254. // test will fail...
  255. uint32_t *d = (uint32_t *)multi_heap_realloc(heap, c, 36);
  256. REQUIRE( multi_heap_check(heap, true) );
  257. REQUIRE( c == d ); /* 'c' block should be shrunk in-place */
  258. REQUIRE( *d == PATTERN);
  259. uint32_t *e = (uint32_t *)multi_heap_malloc(heap, 64);
  260. REQUIRE( multi_heap_check(heap, true));
  261. REQUIRE( a == e ); /* 'e' takes the block formerly occupied by 'a' */
  262. multi_heap_free(heap, d);
  263. uint32_t *f = (uint32_t *)multi_heap_realloc(heap, b, 64);
  264. REQUIRE( multi_heap_check(heap, true) );
  265. REQUIRE( f == b ); /* 'b' should be extended in-place, over space formerly occupied by 'd' */
  266. uint32_t *g = (uint32_t *)multi_heap_realloc(heap, e, 128); /* not enough contiguous space left in the heap */
  267. REQUIRE( g == NULL );
  268. multi_heap_free(heap, f);
  269. /* try again */
  270. g = (uint32_t *)multi_heap_realloc(heap, e, 128);
  271. REQUIRE( multi_heap_check(heap, true) );
  272. REQUIRE( e == g ); /* 'g' extends 'e' in place, into the space formerly held by 'f' */
  273. #endif
  274. }
  275. TEST_CASE("corrupt heap block", "[multi_heap]")
  276. {
  277. uint8_t small_heap[256];
  278. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  279. void *a = multi_heap_malloc(heap, 32);
  280. REQUIRE( multi_heap_check(heap, true) );
  281. memset(a, 0xEE, 64);
  282. REQUIRE( !multi_heap_check(heap, true) );
  283. }