test_multi_heap.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  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 * 5) == NULL ); /* no room to allocate 5*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 that malloc/free does not leave free space fragmented */
  79. TEST_CASE("multi_heap defrag", "[multi_heap]")
  80. {
  81. void *p[4];
  82. uint8_t small_heap[512];
  83. multi_heap_info_t info, info2;
  84. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  85. printf("0 ---\n");
  86. multi_heap_dump(heap);
  87. REQUIRE( multi_heap_check(heap, true) );
  88. multi_heap_get_info(heap, &info);
  89. REQUIRE( 0 == info.allocated_blocks );
  90. REQUIRE( 1 == info.free_blocks );
  91. printf("1 ---\n");
  92. p[0] = multi_heap_malloc(heap, 128);
  93. p[1] = multi_heap_malloc(heap, 32);
  94. multi_heap_dump(heap);
  95. REQUIRE( multi_heap_check(heap, true) );
  96. printf("2 ---\n");
  97. multi_heap_free(heap, p[0]);
  98. p[2] = multi_heap_malloc(heap, 64);
  99. multi_heap_dump(heap);
  100. REQUIRE( p[2] == p[0] );
  101. REQUIRE( multi_heap_check(heap, true) );
  102. printf("3 ---\n");
  103. multi_heap_free(heap, p[2]);
  104. p[3] = multi_heap_malloc(heap, 32);
  105. multi_heap_dump(heap);
  106. REQUIRE( p[3] == p[0] );
  107. REQUIRE( multi_heap_check(heap, true) );
  108. multi_heap_get_info(heap, &info2);
  109. REQUIRE( 2 == info2.allocated_blocks );
  110. REQUIRE( 2 == info2.free_blocks );
  111. multi_heap_free(heap, p[0]);
  112. multi_heap_free(heap, p[1]);
  113. multi_heap_get_info(heap, &info2);
  114. REQUIRE( 0 == info2.allocated_blocks );
  115. REQUIRE( 1 == info2.free_blocks );
  116. REQUIRE( info.total_free_bytes == info2.total_free_bytes );
  117. }
  118. /* Test that malloc/free does not leave free space fragmented
  119. Note: With fancy poisoning, realloc is implemented as malloc-copy-free and this test does not apply.
  120. */
  121. #ifndef MULTI_HEAP_POISONING_SLOW
  122. TEST_CASE("multi_heap defrag realloc", "[multi_heap]")
  123. {
  124. void *p[4];
  125. uint8_t small_heap[512];
  126. multi_heap_info_t info, info2;
  127. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  128. printf("0 ---\n");
  129. multi_heap_dump(heap);
  130. REQUIRE( multi_heap_check(heap, true) );
  131. multi_heap_get_info(heap, &info);
  132. REQUIRE( 0 == info.allocated_blocks );
  133. REQUIRE( 1 == info.free_blocks );
  134. printf("1 ---\n");
  135. p[0] = multi_heap_malloc(heap, 128);
  136. p[1] = multi_heap_malloc(heap, 32);
  137. multi_heap_dump(heap);
  138. REQUIRE( multi_heap_check(heap, true) );
  139. printf("2 ---\n");
  140. p[2] = multi_heap_realloc(heap, p[0], 64);
  141. multi_heap_dump(heap);
  142. REQUIRE( p[2] == p[0] );
  143. REQUIRE( multi_heap_check(heap, true) );
  144. printf("3 ---\n");
  145. p[3] = multi_heap_realloc(heap, p[2], 32);
  146. multi_heap_dump(heap);
  147. REQUIRE( p[3] == p[0] );
  148. REQUIRE( multi_heap_check(heap, true) );
  149. multi_heap_get_info(heap, &info2);
  150. REQUIRE( 2 == info2.allocated_blocks );
  151. REQUIRE( 2 == info2.free_blocks );
  152. multi_heap_free(heap, p[0]);
  153. multi_heap_free(heap, p[1]);
  154. multi_heap_get_info(heap, &info2);
  155. REQUIRE( 0 == info2.allocated_blocks );
  156. REQUIRE( 1 == info2.free_blocks );
  157. REQUIRE( info.total_free_bytes == info2.total_free_bytes );
  158. }
  159. #endif
  160. TEST_CASE("multi_heap many random allocations", "[multi_heap]")
  161. {
  162. uint8_t big_heap[1024];
  163. const int NUM_POINTERS = 64;
  164. printf("Running multi-allocation test...\n");
  165. void *p[NUM_POINTERS] = { 0 };
  166. size_t s[NUM_POINTERS] = { 0 };
  167. multi_heap_handle_t heap = multi_heap_register(big_heap, sizeof(big_heap));
  168. const size_t initial_free = multi_heap_free_size(heap);
  169. const int ITERATIONS = 100000;
  170. for (int i = 0; i < ITERATIONS; i++) {
  171. /* check all pointers allocated so far are valid inside big_heap */
  172. for (int j = 0; j < NUM_POINTERS; j++) {
  173. if (p[j] != NULL) {
  174. }
  175. }
  176. uint8_t n = rand() % NUM_POINTERS;
  177. if (rand() % 4 == 0) {
  178. /* 1 in 4 iterations, try to realloc the buffer instead
  179. of using malloc/free
  180. */
  181. size_t new_size = rand() % 1024;
  182. void *new_p = multi_heap_realloc(heap, p[n], new_size);
  183. printf("realloc %p -> %p (%zu -> %zu)\n", p[n], new_p, s[n], new_size);
  184. multi_heap_check(heap, true);
  185. if (new_size == 0 || new_p != NULL) {
  186. p[n] = new_p;
  187. s[n] = new_size;
  188. if (new_size > 0) {
  189. REQUIRE( p[n] >= big_heap );
  190. REQUIRE( p[n] < big_heap + sizeof(big_heap) );
  191. memset(p[n], n, new_size);
  192. }
  193. }
  194. continue;
  195. }
  196. if (p[n] != NULL) {
  197. if (s[n] > 0) {
  198. /* Verify pre-existing contents of p[n] */
  199. uint8_t compare[s[n]];
  200. memset(compare, n, s[n]);
  201. /*REQUIRE*/assert( memcmp(compare, p[n], s[n]) == 0 );
  202. }
  203. REQUIRE( multi_heap_check(heap, true) );
  204. multi_heap_free(heap, p[n]);
  205. printf("freed %p (%zu)\n", p[n], s[n]);
  206. if (!multi_heap_check(heap, true)) {
  207. printf("FAILED iteration %d after freeing %p\n", i, p[n]);
  208. multi_heap_dump(heap);
  209. REQUIRE(0);
  210. }
  211. }
  212. s[n] = rand() % 1024;
  213. REQUIRE( multi_heap_check(heap, true) );
  214. p[n] = multi_heap_malloc(heap, s[n]);
  215. printf("malloc %p (%zu)\n", p[n], s[n]);
  216. if (p[n] != NULL) {
  217. REQUIRE( p[n] >= big_heap );
  218. REQUIRE( p[n] < big_heap + sizeof(big_heap) );
  219. }
  220. if (!multi_heap_check(heap, true)) {
  221. printf("FAILED iteration %d after mallocing %p (%zu bytes)\n", i, p[n], s[n]);
  222. multi_heap_dump(heap);
  223. REQUIRE(0);
  224. }
  225. if (p[n] != NULL) {
  226. memset(p[n], n, s[n]);
  227. }
  228. }
  229. for (int i = 0; i < NUM_POINTERS; i++) {
  230. multi_heap_free(heap, p[i]);
  231. if (!multi_heap_check(heap, true)) {
  232. printf("FAILED during cleanup after freeing %p\n", p[i]);
  233. multi_heap_dump(heap);
  234. REQUIRE(0);
  235. }
  236. }
  237. REQUIRE( initial_free == multi_heap_free_size(heap) );
  238. }
  239. TEST_CASE("multi_heap_get_info() function", "[multi_heap]")
  240. {
  241. uint8_t heapdata[256];
  242. multi_heap_handle_t heap = multi_heap_register(heapdata, sizeof(heapdata));
  243. multi_heap_info_t before, after, freed;
  244. multi_heap_get_info(heap, &before);
  245. 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",
  246. before.total_free_bytes,
  247. before.total_allocated_bytes,
  248. before.largest_free_block,
  249. before.minimum_free_bytes,
  250. before.allocated_blocks,
  251. before.free_blocks,
  252. before.total_blocks);
  253. REQUIRE( 0 == before.allocated_blocks );
  254. REQUIRE( 0 == before.total_allocated_bytes );
  255. REQUIRE( before.total_free_bytes == before.minimum_free_bytes );
  256. void *x = multi_heap_malloc(heap, 32);
  257. multi_heap_get_info(heap, &after);
  258. 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",
  259. after.total_free_bytes,
  260. after.total_allocated_bytes,
  261. after.largest_free_block,
  262. after.minimum_free_bytes,
  263. after.allocated_blocks,
  264. after.free_blocks,
  265. after.total_blocks);
  266. REQUIRE( 1 == after.allocated_blocks );
  267. REQUIRE( 32 == after.total_allocated_bytes );
  268. REQUIRE( after.minimum_free_bytes < before.minimum_free_bytes);
  269. REQUIRE( after.minimum_free_bytes > 0 );
  270. multi_heap_free(heap, x);
  271. multi_heap_get_info(heap, &freed);
  272. 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",
  273. freed.total_free_bytes,
  274. freed.total_allocated_bytes,
  275. freed.largest_free_block,
  276. freed.minimum_free_bytes,
  277. freed.allocated_blocks,
  278. freed.free_blocks,
  279. freed.total_blocks);
  280. REQUIRE( 0 == freed.allocated_blocks );
  281. REQUIRE( 0 == freed.total_allocated_bytes );
  282. REQUIRE( before.total_free_bytes == freed.total_free_bytes );
  283. REQUIRE( after.minimum_free_bytes == freed.minimum_free_bytes );
  284. }
  285. TEST_CASE("multi_heap minimum-size allocations", "[multi_heap]")
  286. {
  287. uint8_t heapdata[16384];
  288. void *p[sizeof(heapdata) / sizeof(void *)];
  289. const size_t NUM_P = sizeof(p) / sizeof(void *);
  290. multi_heap_handle_t heap = multi_heap_register(heapdata, sizeof(heapdata));
  291. size_t before_free = multi_heap_free_size(heap);
  292. size_t i;
  293. for (i = 0; i < NUM_P; i++) {
  294. p[i] = multi_heap_malloc(heap, 1);
  295. if (p[i] == NULL) {
  296. break;
  297. }
  298. }
  299. REQUIRE( i < NUM_P); // Should have run out of heap before we ran out of pointers
  300. printf("Allocated %zu minimum size chunks\n", i);
  301. REQUIRE( 0 == multi_heap_free_size(heap) );
  302. multi_heap_check(heap, true);
  303. /* Free in random order */
  304. bool has_allocations = true;
  305. while (has_allocations) {
  306. i = rand() % NUM_P;
  307. multi_heap_free(heap, p[i]);
  308. p[i] = NULL;
  309. multi_heap_check(heap, true);
  310. has_allocations = false;
  311. for (i = 0; i < NUM_P && !has_allocations; i++) {
  312. has_allocations = (p[i] != NULL);
  313. }
  314. }
  315. /* all freed! */
  316. REQUIRE( before_free == multi_heap_free_size(heap) );
  317. }
  318. TEST_CASE("multi_heap_realloc()", "[multi_heap]")
  319. {
  320. const uint32_t PATTERN = 0xABABDADA;
  321. uint8_t small_heap[300];
  322. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  323. uint32_t *a = (uint32_t *)multi_heap_malloc(heap, 64);
  324. uint32_t *b = (uint32_t *)multi_heap_malloc(heap, 32);
  325. REQUIRE( a != NULL );
  326. REQUIRE( b != NULL );
  327. REQUIRE( b > a); /* 'b' takes the block after 'a' */
  328. *a = PATTERN;
  329. uint32_t *c = (uint32_t *)multi_heap_realloc(heap, a, 72);
  330. REQUIRE( multi_heap_check(heap, true));
  331. REQUIRE( c != NULL );
  332. REQUIRE( c > b ); /* 'a' moves, 'c' takes the block after 'b' */
  333. REQUIRE( *c == PATTERN );
  334. #ifndef MULTI_HEAP_POISONING_SLOW
  335. // "Slow" poisoning implementation doesn't reallocate in place, so these
  336. // test will fail...
  337. uint32_t *d = (uint32_t *)multi_heap_realloc(heap, c, 36);
  338. REQUIRE( multi_heap_check(heap, true) );
  339. REQUIRE( c == d ); /* 'c' block should be shrunk in-place */
  340. REQUIRE( *d == PATTERN);
  341. uint32_t *e = (uint32_t *)multi_heap_malloc(heap, 64);
  342. REQUIRE( multi_heap_check(heap, true));
  343. REQUIRE( a == e ); /* 'e' takes the block formerly occupied by 'a' */
  344. multi_heap_free(heap, d);
  345. uint32_t *f = (uint32_t *)multi_heap_realloc(heap, b, 64);
  346. REQUIRE( multi_heap_check(heap, true) );
  347. REQUIRE( f == b ); /* 'b' should be extended in-place, over space formerly occupied by 'd' */
  348. #ifdef MULTI_HEAP_POISONING
  349. #define TOO_MUCH 92 + 1
  350. #else
  351. #define TOO_MUCH 128 + 1
  352. #endif
  353. /* not enough contiguous space left in the heap */
  354. uint32_t *g = (uint32_t *)multi_heap_realloc(heap, e, TOO_MUCH);
  355. REQUIRE( g == NULL );
  356. multi_heap_free(heap, f);
  357. /* try again */
  358. g = (uint32_t *)multi_heap_realloc(heap, e, 128);
  359. REQUIRE( multi_heap_check(heap, true) );
  360. REQUIRE( e == g ); /* 'g' extends 'e' in place, into the space formerly held by 'f' */
  361. #endif
  362. }
  363. TEST_CASE("corrupt heap block", "[multi_heap]")
  364. {
  365. uint8_t small_heap[256];
  366. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  367. void *a = multi_heap_malloc(heap, 32);
  368. REQUIRE( multi_heap_check(heap, true) );
  369. memset(a, 0xEE, 64);
  370. REQUIRE( !multi_heap_check(heap, true) );
  371. }
  372. TEST_CASE("unaligned heaps", "[multi_heap]")
  373. {
  374. const size_t CHUNK_LEN = 256;
  375. const size_t CANARY_LEN = 16;
  376. const uint8_t CANARY_BYTE = 0x3E;
  377. uint8_t heap_chunk[CHUNK_LEN + CANARY_LEN * 2];
  378. /* Put some canary bytes before and after the bytes we intend to use for
  379. the heap, make sure they aren't ever overwritten */
  380. memset(heap_chunk, CANARY_BYTE, CANARY_LEN);
  381. memset(heap_chunk + CANARY_LEN + CHUNK_LEN, CANARY_BYTE, CANARY_LEN);
  382. for (int i = 0; i < 8; i++) {
  383. printf("Testing with offset %d\n", i);
  384. multi_heap_handle_t heap = multi_heap_register(heap_chunk + CANARY_LEN + i, CHUNK_LEN - i);
  385. multi_heap_info_t info;
  386. REQUIRE( multi_heap_check(heap, true) );
  387. multi_heap_get_info(heap, &info);
  388. REQUIRE( info.total_free_bytes > CHUNK_LEN - 64 - i );
  389. REQUIRE( info.largest_free_block > CHUNK_LEN - 64 - i );
  390. void *a = multi_heap_malloc(heap, info.largest_free_block);
  391. REQUIRE( a != NULL );
  392. memset(a, 0xAA, info.largest_free_block);
  393. REQUIRE( multi_heap_check(heap, true) );
  394. multi_heap_free(heap, a);
  395. REQUIRE( multi_heap_check(heap, true) );
  396. for (unsigned j = 0; j < CANARY_LEN; j++) { // check canaries
  397. REQUIRE( heap_chunk[j] == CANARY_BYTE );
  398. REQUIRE( heap_chunk[CHUNK_LEN + CANARY_LEN + j] == CANARY_BYTE );
  399. }
  400. }
  401. }
  402. TEST_CASE("multi_heap aligned allocations", "[multi_heap]")
  403. {
  404. uint8_t test_heap[1024 * 1024];
  405. multi_heap_handle_t heap = multi_heap_register(test_heap, sizeof(test_heap));
  406. uint32_t aligments = 0; // starts from alignment by 4-byte boundary
  407. size_t old_size = multi_heap_free_size(heap);
  408. size_t leakage = 1024;
  409. printf("[ALIGNED_ALLOC] heap_size before: %d \n", old_size);
  410. printf("New heap:\n");
  411. multi_heap_dump(heap);
  412. printf("*********************\n");
  413. for(;aligments < 500 * 1024; aligments++) {
  414. //Use some stupid size value to test correct alignment even in strange
  415. //memory layout objects:
  416. uint8_t *buf = (uint8_t *)multi_heap_aligned_alloc(heap, (aligments + 137), aligments );
  417. if(((aligments & (aligments - 1)) != 0) || (!aligments)) {
  418. REQUIRE( buf == NULL );
  419. //printf("[ALIGNED_ALLOC] alignment: %u is not a power of two, don't allow allocation \n", aligments);
  420. } else {
  421. REQUIRE( buf != NULL );
  422. REQUIRE((intptr_t)buf >= (intptr_t)test_heap);
  423. REQUIRE((intptr_t)buf < (intptr_t)(test_heap + sizeof(test_heap)));
  424. printf("[ALIGNED_ALLOC] alignment required: %u \n", aligments);
  425. //printf("[ALIGNED_ALLOC] allocated size: %d \n", multi_heap_get_allocated_size(heap, buf));
  426. printf("[ALIGNED_ALLOC] address of allocated memory: %p \n\n", (void *)buf);
  427. //Address of obtained block must be aligned with selected value
  428. if((aligments & 0x03) == 0) {
  429. //Alignment is a multiple of four:
  430. REQUIRE(((intptr_t)buf & 0x03) == 0);
  431. } else {
  432. //Exotic alignments:
  433. REQUIRE(((intptr_t)buf & (aligments - 1)) == 0);
  434. }
  435. //Write some data, if it corrupts memory probably the heap
  436. //canary verification will fail:
  437. memset(buf, 0xA5, (aligments + 137));
  438. multi_heap_aligned_free(heap, buf);
  439. }
  440. }
  441. printf("[ALIGNED_ALLOC] heap_size after: %d \n", multi_heap_free_size(heap));
  442. REQUIRE((old_size - multi_heap_free_size(heap)) <= leakage);
  443. }