test_multi_heap.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. #include "catch.hpp"
  2. #include "multi_heap.h"
  3. #include "../multi_heap_config.h"
  4. #include <string.h>
  5. #include <assert.h>
  6. static void *__malloc__(size_t bytes)
  7. {
  8. return malloc(bytes);
  9. }
  10. static void __free__(void *ptr)
  11. {
  12. free(ptr);
  13. }
  14. /* Insurance against accidentally using libc heap functions in tests */
  15. #undef free
  16. #define free #error
  17. #undef malloc
  18. #define malloc #error
  19. #undef calloc
  20. #define calloc #error
  21. #undef realloc
  22. #define realloc #error
  23. TEST_CASE("multi_heap simple allocations", "[multi_heap]")
  24. {
  25. uint8_t small_heap[4 * 1024];
  26. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  27. size_t test_alloc_size = (multi_heap_free_size(heap) + 4) / 2;
  28. printf("New heap:\n");
  29. multi_heap_dump(heap);
  30. printf("*********************\n");
  31. uint8_t *buf = (uint8_t *)multi_heap_malloc(heap, test_alloc_size);
  32. printf("small_heap %p buf %p\n", small_heap, buf);
  33. REQUIRE( buf != NULL );
  34. REQUIRE((intptr_t)buf >= (intptr_t)small_heap);
  35. REQUIRE( (intptr_t)buf < (intptr_t)(small_heap + sizeof(small_heap)));
  36. REQUIRE( multi_heap_get_allocated_size(heap, buf) >= test_alloc_size );
  37. REQUIRE( multi_heap_get_allocated_size(heap, buf) < test_alloc_size + 16);
  38. memset(buf, 0xEE, test_alloc_size);
  39. REQUIRE( multi_heap_malloc(heap, test_alloc_size) == NULL );
  40. multi_heap_free(heap, buf);
  41. printf("Empty?\n");
  42. multi_heap_dump(heap);
  43. printf("*********************\n");
  44. /* Now there should be space for another allocation */
  45. buf = (uint8_t *)multi_heap_malloc(heap, test_alloc_size);
  46. REQUIRE( buf != NULL );
  47. multi_heap_free(heap, buf);
  48. REQUIRE( multi_heap_free_size(heap) > multi_heap_minimum_free_size(heap) );
  49. }
  50. TEST_CASE("multi_heap fragmentation", "[multi_heap]")
  51. {
  52. uint8_t small_heap[4 * 1024];
  53. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  54. const size_t alloc_size = 128;
  55. void *p[4];
  56. for (int i = 0; i < 4; i++) {
  57. multi_heap_dump(heap);
  58. REQUIRE( multi_heap_check(heap, true) );
  59. p[i] = multi_heap_malloc(heap, alloc_size);
  60. printf("%d = %p ****->\n", i, p[i]);
  61. multi_heap_dump(heap);
  62. REQUIRE( p[i] != NULL );
  63. }
  64. printf("allocated %p %p %p %p\n", p[0], p[1], p[2], p[3]);
  65. REQUIRE( multi_heap_malloc(heap, alloc_size * 5) == NULL ); /* no room to allocate 5*alloc_size now */
  66. printf("4 allocations:\n");
  67. multi_heap_dump(heap);
  68. printf("****************\n");
  69. multi_heap_free(heap, p[0]);
  70. multi_heap_free(heap, p[1]);
  71. multi_heap_free(heap, p[3]);
  72. printf("1 allocations:\n");
  73. multi_heap_dump(heap);
  74. printf("****************\n");
  75. void *big = multi_heap_malloc(heap, alloc_size * 3);
  76. //Blocks in TLSF are organized in different form, so this makes no sense
  77. multi_heap_free(heap, big);
  78. multi_heap_free(heap, p[2]);
  79. printf("0 allocations:\n");
  80. multi_heap_dump(heap);
  81. printf("****************\n");
  82. big = multi_heap_malloc(heap, alloc_size * 2);
  83. //Blocks in TLSF are organized in different form, so this makes no sense
  84. multi_heap_free(heap, big);
  85. }
  86. /* Test that malloc/free does not leave free space fragmented */
  87. TEST_CASE("multi_heap defrag", "[multi_heap]")
  88. {
  89. void *p[4];
  90. uint8_t small_heap[4 * 1024];
  91. multi_heap_info_t info, info2;
  92. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  93. printf("0 ---\n");
  94. multi_heap_dump(heap);
  95. REQUIRE( multi_heap_check(heap, true) );
  96. multi_heap_get_info(heap, &info);
  97. REQUIRE( 0 == info.allocated_blocks );
  98. REQUIRE( 1 == info.free_blocks );
  99. printf("1 ---\n");
  100. p[0] = multi_heap_malloc(heap, 128);
  101. p[1] = multi_heap_malloc(heap, 32);
  102. multi_heap_dump(heap);
  103. REQUIRE( multi_heap_check(heap, true) );
  104. printf("2 ---\n");
  105. multi_heap_free(heap, p[0]);
  106. p[2] = multi_heap_malloc(heap, 64);
  107. multi_heap_dump(heap);
  108. REQUIRE( p[2] == p[0] );
  109. REQUIRE( multi_heap_check(heap, true) );
  110. printf("3 ---\n");
  111. multi_heap_free(heap, p[2]);
  112. p[3] = multi_heap_malloc(heap, 32);
  113. multi_heap_dump(heap);
  114. REQUIRE( p[3] == p[0] );
  115. REQUIRE( multi_heap_check(heap, true) );
  116. multi_heap_get_info(heap, &info2);
  117. REQUIRE( 2 == info2.allocated_blocks );
  118. REQUIRE( 2 == info2.free_blocks );
  119. multi_heap_free(heap, p[0]);
  120. multi_heap_free(heap, p[1]);
  121. multi_heap_get_info(heap, &info2);
  122. REQUIRE( 0 == info2.allocated_blocks );
  123. REQUIRE( 1 == info2.free_blocks );
  124. REQUIRE( info.total_free_bytes == info2.total_free_bytes );
  125. }
  126. /* Test that malloc/free does not leave free space fragmented
  127. Note: With fancy poisoning, realloc is implemented as malloc-copy-free and this test does not apply.
  128. */
  129. #ifndef MULTI_HEAP_POISONING_SLOW
  130. TEST_CASE("multi_heap defrag realloc", "[multi_heap]")
  131. {
  132. void *p[4];
  133. uint8_t small_heap[4 * 1024];
  134. multi_heap_info_t info, info2;
  135. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  136. printf("0 ---\n");
  137. multi_heap_dump(heap);
  138. REQUIRE( multi_heap_check(heap, true) );
  139. multi_heap_get_info(heap, &info);
  140. REQUIRE( 0 == info.allocated_blocks );
  141. REQUIRE( 1 == info.free_blocks );
  142. printf("1 ---\n");
  143. p[0] = multi_heap_malloc(heap, 128);
  144. p[1] = multi_heap_malloc(heap, 32);
  145. multi_heap_dump(heap);
  146. REQUIRE( multi_heap_check(heap, true) );
  147. printf("2 ---\n");
  148. p[2] = multi_heap_realloc(heap, p[0], 64);
  149. multi_heap_dump(heap);
  150. REQUIRE( p[2] == p[0] );
  151. REQUIRE( multi_heap_check(heap, true) );
  152. printf("3 ---\n");
  153. p[3] = multi_heap_realloc(heap, p[2], 32);
  154. multi_heap_dump(heap);
  155. REQUIRE( p[3] == p[0] );
  156. REQUIRE( multi_heap_check(heap, true) );
  157. multi_heap_get_info(heap, &info2);
  158. REQUIRE( 2 == info2.allocated_blocks );
  159. REQUIRE( 2 == info2.free_blocks );
  160. multi_heap_free(heap, p[0]);
  161. multi_heap_free(heap, p[1]);
  162. multi_heap_get_info(heap, &info2);
  163. REQUIRE( 0 == info2.allocated_blocks );
  164. REQUIRE( 1 == info2.free_blocks );
  165. REQUIRE( info.total_free_bytes == info2.total_free_bytes );
  166. }
  167. #endif
  168. void multi_heap_allocation_impl(int heap_size)
  169. {
  170. uint8_t *big_heap = (uint8_t *) __malloc__(2*heap_size);
  171. const int NUM_POINTERS = 64;
  172. printf("Running multi-allocation test with heap_size %d...\n", heap_size);
  173. REQUIRE( big_heap );
  174. multi_heap_handle_t heap = multi_heap_register(big_heap, heap_size);
  175. void *p[NUM_POINTERS] = { 0 };
  176. size_t s[NUM_POINTERS] = { 0 };
  177. const size_t initial_free = multi_heap_free_size(heap);
  178. const int ITERATIONS = 10000;
  179. for (int i = 0; i < ITERATIONS; i++) {
  180. /* check all pointers allocated so far are valid inside big_heap */
  181. for (int j = 0; j < NUM_POINTERS; j++) {
  182. if (p[j] != NULL) {
  183. }
  184. }
  185. uint8_t n = rand() % NUM_POINTERS;
  186. if (rand() % 4 == 0) {
  187. /* 1 in 4 iterations, try to realloc the buffer instead
  188. of using malloc/free
  189. */
  190. size_t new_size = rand() % 1024;
  191. void *new_p = multi_heap_realloc(heap, p[n], new_size);
  192. printf("realloc %p -> %p (%zu -> %zu)\n", p[n], new_p, s[n], new_size);
  193. multi_heap_check(heap, true);
  194. if (new_size == 0 || new_p != NULL) {
  195. p[n] = new_p;
  196. s[n] = new_size;
  197. if (new_size > 0) {
  198. REQUIRE( p[n] >= big_heap );
  199. REQUIRE( p[n] < big_heap + heap_size );
  200. memset(p[n], n, new_size);
  201. }
  202. }
  203. continue;
  204. }
  205. if (p[n] != NULL) {
  206. if (s[n] > 0) {
  207. /* Verify pre-existing contents of p[n] */
  208. uint8_t compare[s[n]];
  209. memset(compare, n, s[n]);
  210. /*REQUIRE*/assert( memcmp(compare, p[n], s[n]) == 0 );
  211. }
  212. REQUIRE( multi_heap_check(heap, true) );
  213. multi_heap_free(heap, p[n]);
  214. printf("freed %p (%zu)\n", p[n], s[n]);
  215. if (!multi_heap_check(heap, true)) {
  216. printf("FAILED iteration %d after freeing %p\n", i, p[n]);
  217. multi_heap_dump(heap);
  218. REQUIRE(0);
  219. }
  220. }
  221. s[n] = rand() % 1024;
  222. REQUIRE( multi_heap_check(heap, true) );
  223. p[n] = multi_heap_malloc(heap, s[n]);
  224. printf("malloc %p (%zu)\n", p[n], s[n]);
  225. if (p[n] != NULL) {
  226. REQUIRE( p[n] >= big_heap );
  227. REQUIRE( p[n] < big_heap + heap_size );
  228. }
  229. if (!multi_heap_check(heap, true)) {
  230. printf("FAILED iteration %d after mallocing %p (%zu bytes)\n", i, p[n], s[n]);
  231. multi_heap_dump(heap);
  232. REQUIRE(0);
  233. }
  234. if (p[n] != NULL) {
  235. memset(p[n], n, s[n]);
  236. }
  237. }
  238. for (int i = 0; i < NUM_POINTERS; i++) {
  239. multi_heap_free(heap, p[i]);
  240. if (!multi_heap_check(heap, true)) {
  241. printf("FAILED during cleanup after freeing %p\n", p[i]);
  242. multi_heap_dump(heap);
  243. REQUIRE(0);
  244. }
  245. }
  246. REQUIRE( initial_free == multi_heap_free_size(heap) );
  247. __free__(big_heap);
  248. }
  249. TEST_CASE("multi_heap many random allocations", "[multi_heap]")
  250. {
  251. size_t poolsize[] = { 15, 255, 4095, 8191 };
  252. for (size_t i = 0; i < sizeof(poolsize)/sizeof(size_t); i++) {
  253. multi_heap_allocation_impl(poolsize[i] * 1024);
  254. }
  255. }
  256. TEST_CASE("multi_heap_get_info() function", "[multi_heap]")
  257. {
  258. uint8_t heapdata[4 * 1024];
  259. multi_heap_handle_t heap = multi_heap_register(heapdata, sizeof(heapdata));
  260. multi_heap_info_t before, after, freed;
  261. multi_heap_get_info(heap, &before);
  262. 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",
  263. before.total_free_bytes,
  264. before.total_allocated_bytes,
  265. before.largest_free_block,
  266. before.minimum_free_bytes,
  267. before.allocated_blocks,
  268. before.free_blocks,
  269. before.total_blocks);
  270. REQUIRE( 0 == before.allocated_blocks );
  271. REQUIRE( 0 == before.total_allocated_bytes );
  272. REQUIRE( before.total_free_bytes == before.minimum_free_bytes );
  273. void *x = multi_heap_malloc(heap, 32);
  274. multi_heap_get_info(heap, &after);
  275. 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",
  276. after.total_free_bytes,
  277. after.total_allocated_bytes,
  278. after.largest_free_block,
  279. after.minimum_free_bytes,
  280. after.allocated_blocks,
  281. after.free_blocks,
  282. after.total_blocks);
  283. REQUIRE( 1 == after.allocated_blocks );
  284. REQUIRE( 32 == after.total_allocated_bytes );
  285. REQUIRE( after.minimum_free_bytes < before.minimum_free_bytes);
  286. REQUIRE( after.minimum_free_bytes > 0 );
  287. multi_heap_free(heap, x);
  288. multi_heap_get_info(heap, &freed);
  289. 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",
  290. freed.total_free_bytes,
  291. freed.total_allocated_bytes,
  292. freed.largest_free_block,
  293. freed.minimum_free_bytes,
  294. freed.allocated_blocks,
  295. freed.free_blocks,
  296. freed.total_blocks);
  297. REQUIRE( 0 == freed.allocated_blocks );
  298. REQUIRE( 0 == freed.total_allocated_bytes );
  299. REQUIRE( before.total_free_bytes == freed.total_free_bytes );
  300. REQUIRE( after.minimum_free_bytes == freed.minimum_free_bytes );
  301. }
  302. TEST_CASE("multi_heap minimum-size allocations", "[multi_heap]")
  303. {
  304. uint8_t heapdata[4096];
  305. void *p[sizeof(heapdata) / sizeof(void *)] = {NULL};
  306. const size_t NUM_P = sizeof(p) / sizeof(void *);
  307. size_t allocated_size = 0;
  308. multi_heap_handle_t heap = multi_heap_register(heapdata, sizeof(heapdata));
  309. size_t before_free = multi_heap_free_size(heap);
  310. size_t i;
  311. for (i = 0; i < NUM_P; i++) {
  312. //TLSF minimum block size is 4 bytes
  313. p[i] = multi_heap_malloc(heap, 1);
  314. if (p[i] == NULL) {
  315. break;
  316. }
  317. }
  318. REQUIRE( i < NUM_P); // Should have run out of heap before we ran out of pointers
  319. printf("Allocated %zu minimum size chunks\n", i);
  320. REQUIRE(multi_heap_free_size(heap) < before_free);
  321. multi_heap_check(heap, true);
  322. /* Free in random order */
  323. bool has_allocations = true;
  324. while (has_allocations) {
  325. i = rand() % NUM_P;
  326. multi_heap_free(heap, p[i]);
  327. p[i] = NULL;
  328. multi_heap_check(heap, true);
  329. has_allocations = false;
  330. for (i = 0; i < NUM_P && !has_allocations; i++) {
  331. has_allocations = (p[i] != NULL);
  332. }
  333. }
  334. /* all freed! */
  335. REQUIRE( before_free == multi_heap_free_size(heap) );
  336. }
  337. TEST_CASE("multi_heap_realloc()", "[multi_heap]")
  338. {
  339. const uint32_t PATTERN = 0xABABDADA;
  340. uint8_t small_heap[4 * 1024];
  341. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  342. uint32_t *a = (uint32_t *)multi_heap_malloc(heap, 64);
  343. uint32_t *b = (uint32_t *)multi_heap_malloc(heap, 32);
  344. REQUIRE( a != NULL );
  345. REQUIRE( b != NULL );
  346. REQUIRE( b > a); /* 'b' takes the block after 'a' */
  347. *a = PATTERN;
  348. uint32_t *c = (uint32_t *)multi_heap_realloc(heap, a, 72);
  349. REQUIRE( multi_heap_check(heap, true));
  350. REQUIRE( c != NULL );
  351. REQUIRE( c > b ); /* 'a' moves, 'c' takes the block after 'b' */
  352. REQUIRE( *c == PATTERN );
  353. #ifndef MULTI_HEAP_POISONING_SLOW
  354. // "Slow" poisoning implementation doesn't reallocate in place, so these
  355. // test will fail...
  356. uint32_t *d = (uint32_t *)multi_heap_realloc(heap, c, 36);
  357. REQUIRE( multi_heap_check(heap, true) );
  358. REQUIRE( c == d ); /* 'c' block should be shrunk in-place */
  359. REQUIRE( *d == PATTERN);
  360. uint32_t *e = (uint32_t *)multi_heap_malloc(heap, 64);
  361. REQUIRE( multi_heap_check(heap, true));
  362. REQUIRE( a == e ); /* 'e' takes the block formerly occupied by 'a' */
  363. multi_heap_free(heap, d);
  364. uint32_t *f = (uint32_t *)multi_heap_realloc(heap, b, 64);
  365. REQUIRE( multi_heap_check(heap, true) );
  366. REQUIRE( f == b ); /* 'b' should be extended in-place, over space formerly occupied by 'd' */
  367. #ifdef MULTI_HEAP_POISONING
  368. #define TOO_MUCH 7420 + 1
  369. #else
  370. #define TOO_MUCH 7420 + 1
  371. #endif
  372. /* not enough contiguous space left in the heap */
  373. uint32_t *g = (uint32_t *)multi_heap_realloc(heap, e, TOO_MUCH);
  374. REQUIRE( g == NULL );
  375. multi_heap_free(heap, f);
  376. /* try again */
  377. g = (uint32_t *)multi_heap_realloc(heap, e, 128);
  378. REQUIRE( multi_heap_check(heap, true) );
  379. REQUIRE( e == g ); /* 'g' extends 'e' in place, into the space formerly held by 'f' */
  380. #endif
  381. }
  382. // TLSF only accepts heaps aligned to 4-byte boundary so
  383. // only aligned allocation tests make sense.
  384. TEST_CASE("multi_heap aligned allocations", "[multi_heap]")
  385. {
  386. uint8_t test_heap[4 * 1024];
  387. multi_heap_handle_t heap = multi_heap_register(test_heap, sizeof(test_heap));
  388. uint32_t aligments = 0; // starts from alignment by 4-byte boundary
  389. size_t old_size = multi_heap_free_size(heap);
  390. size_t leakage = 1024;
  391. printf("[ALIGNED_ALLOC] heap_size before: %d \n", old_size);
  392. printf("New heap:\n");
  393. multi_heap_dump(heap);
  394. printf("*********************\n");
  395. for(;aligments <= 256; aligments++) {
  396. //Use some stupid size value to test correct alignment even in strange
  397. //memory layout objects:
  398. uint8_t *buf = (uint8_t *)multi_heap_aligned_alloc(heap, (aligments + 137), aligments );
  399. if(((aligments & (aligments - 1)) != 0) || (!aligments)) {
  400. REQUIRE( buf == NULL );
  401. } else {
  402. REQUIRE( buf != NULL );
  403. REQUIRE((intptr_t)buf >= (intptr_t)test_heap);
  404. REQUIRE((intptr_t)buf < (intptr_t)(test_heap + sizeof(test_heap)));
  405. printf("[ALIGNED_ALLOC] alignment required: %u \n", aligments);
  406. printf("[ALIGNED_ALLOC] address of allocated memory: %p \n\n", (void *)buf);
  407. //Address of obtained block must be aligned with selected value
  408. REQUIRE(((intptr_t)buf & (aligments - 1)) == 0);
  409. //Write some data, if it corrupts memory probably the heap
  410. //canary verification will fail:
  411. memset(buf, 0xA5, (aligments + 137));
  412. multi_heap_free(heap, buf);
  413. }
  414. }
  415. printf("[ALIGNED_ALLOC] heap_size after: %d \n", multi_heap_free_size(heap));
  416. REQUIRE((old_size - multi_heap_free_size(heap)) <= leakage);
  417. }