test_malloc_caps.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. /*
  2. Tests for the capabilities-based memory allocator.
  3. */
  4. #include <esp_types.h>
  5. #include <stdio.h>
  6. #include "unity.h"
  7. #include "esp_attr.h"
  8. #include "esp_heap_caps.h"
  9. #include "spi_flash_mmap.h"
  10. #include "esp_memory_utils.h"
  11. #include "esp_private/spi_flash_os.h"
  12. #include <stdlib.h>
  13. #include <sys/param.h>
  14. #if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2)
  15. //IDF-5167
  16. #ifndef CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
  17. TEST_CASE("Capabilities allocator test", "[heap]")
  18. {
  19. char *m1, *m2[10];
  20. int x;
  21. size_t free8start, free32start, free8, free32;
  22. /* It's important we printf() something before we take the empty heap sizes,
  23. as the first printf() in a task allocates heap resources... */
  24. printf("Testing capabilities allocator...\n");
  25. free8start = heap_caps_get_free_size(MALLOC_CAP_8BIT);
  26. free32start = heap_caps_get_free_size(MALLOC_CAP_32BIT);
  27. printf("Free 8bit-capable memory (start): %dK, 32-bit capable memory %dK\n", free8start, free32start);
  28. TEST_ASSERT(free32start >= free8start);
  29. printf("Allocating 10K of 8-bit capable RAM\n");
  30. m1= heap_caps_malloc(10*1024, MALLOC_CAP_8BIT);
  31. printf("--> %p\n", m1);
  32. free8 = heap_caps_get_free_size(MALLOC_CAP_8BIT);
  33. free32 = heap_caps_get_free_size(MALLOC_CAP_32BIT);
  34. printf("Free 8bit-capable memory (both reduced): %dK, 32-bit capable memory %dK\n", free8, free32);
  35. //Both should have gone down by 10K; 8bit capable ram is also 32-bit capable
  36. TEST_ASSERT(free8<=(free8start-10*1024));
  37. TEST_ASSERT(free32<=(free32start-10*1024));
  38. //Assume we got DRAM back
  39. TEST_ASSERT((((int)m1)&0xFF000000)==0x3F000000);
  40. free(m1);
  41. //The goal here is to allocate from IRAM. Since there is no external IRAM (yet)
  42. //the following gives size of IRAM-only (not D/IRAM) memory.
  43. size_t free_iram = heap_caps_get_free_size(MALLOC_CAP_INTERNAL) -
  44. heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
  45. size_t alloc32 = MIN(free_iram / 2, 10*1024) & (~3);
  46. if(free_iram) {
  47. printf("Freeing; allocating %u bytes of 32K-capable RAM\n", alloc32);
  48. m1 = heap_caps_malloc(alloc32, MALLOC_CAP_32BIT);
  49. printf("--> %p\n", m1);
  50. //Check that we got IRAM back
  51. TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000);
  52. free8 = heap_caps_get_free_size(MALLOC_CAP_8BIT);
  53. free32 = heap_caps_get_free_size(MALLOC_CAP_32BIT);
  54. printf("Free 8bit-capable memory (after 32-bit): %dK, 32-bit capable memory %dK\n", free8, free32);
  55. //Only 32-bit should have gone down by alloc32: 32-bit isn't necessarily 8bit capable
  56. TEST_ASSERT(free32<=(free32start-alloc32));
  57. TEST_ASSERT(free8==free8start);
  58. free(m1);
  59. } else {
  60. printf("This platform has no 32-bit only capable RAM, jumping to next test \n");
  61. }
  62. printf("Allocating impossible caps\n");
  63. m1= heap_caps_malloc(10*1024, MALLOC_CAP_8BIT|MALLOC_CAP_EXEC);
  64. printf("--> %p\n", m1);
  65. TEST_ASSERT(m1==NULL);
  66. if(free_iram) {
  67. printf("Testing changeover iram -> dram");
  68. // priorities will exhaust IRAM first, then start allocating from DRAM
  69. for (x=0; x<10; x++) {
  70. m2[x]= heap_caps_malloc(alloc32, MALLOC_CAP_32BIT);
  71. printf("--> %p\n", m2[x]);
  72. }
  73. TEST_ASSERT((((int)m2[0])&0xFF000000)==0x40000000);
  74. TEST_ASSERT((((int)m2[9])&0xFF000000)==0x3F000000);
  75. } else {
  76. printf("This platform has no IRAM-only so changeover will never occur, jumping to next test\n");
  77. }
  78. printf("Test if allocating executable code still gives IRAM, even with dedicated IRAM region depleted\n");
  79. if(free_iram) {
  80. // (the allocation should come from D/IRAM)
  81. free_iram = heap_caps_get_free_size(MALLOC_CAP_EXEC);
  82. m1= heap_caps_malloc(MIN(free_iram / 2, 10*1024), MALLOC_CAP_EXEC);
  83. printf("--> %p\n", m1);
  84. TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000);
  85. for (x=0; x<10; x++) free(m2[x]);
  86. } else {
  87. // (the allocation should come from D/IRAM)
  88. free_iram = heap_caps_get_free_size(MALLOC_CAP_EXEC);
  89. m1= heap_caps_malloc(MIN(free_iram / 2, 10*1024), MALLOC_CAP_EXEC);
  90. printf("--> %p\n", m1);
  91. TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000);
  92. }
  93. free(m1);
  94. printf("Done.\n");
  95. }
  96. #endif
  97. #endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2)
  98. #ifdef CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY
  99. TEST_CASE("IRAM_8BIT capability test", "[heap]")
  100. {
  101. uint8_t *ptr;
  102. size_t free_size, free_size32, largest_free_size;
  103. /* need to print something as first printf allocates some heap */
  104. printf("IRAM_8BIT capability test\n");
  105. free_size = heap_caps_get_free_size(MALLOC_CAP_IRAM_8BIT);
  106. free_size32 = heap_caps_get_free_size(MALLOC_CAP_32BIT);
  107. largest_free_size = heap_caps_get_largest_free_block(MALLOC_CAP_IRAM_8BIT);
  108. ptr = heap_caps_malloc(largest_free_size, MALLOC_CAP_IRAM_8BIT);
  109. TEST_ASSERT((((int)ptr)&0xFF000000)==0x40000000);
  110. /* As the heap allocator may present an overhead for allocated blocks,
  111. * we need to check that the free heap size is now smaller or equal to the former free size. */
  112. TEST_ASSERT(heap_caps_get_free_size(MALLOC_CAP_IRAM_8BIT) <= (free_size - heap_caps_get_allocated_size(ptr)));
  113. TEST_ASSERT(heap_caps_get_free_size(MALLOC_CAP_32BIT) <= (free_size32 - heap_caps_get_allocated_size(ptr)));
  114. free(ptr);
  115. }
  116. #endif
  117. TEST_CASE("heap_caps metadata test", "[heap]")
  118. {
  119. /* need to print something as first printf allocates some heap */
  120. printf("heap_caps metadata test\n");
  121. heap_caps_print_heap_info(MALLOC_CAP_8BIT);
  122. multi_heap_info_t original;
  123. heap_caps_get_info(&original, MALLOC_CAP_8BIT);
  124. void *b = heap_caps_malloc(original.largest_free_block, MALLOC_CAP_8BIT);
  125. TEST_ASSERT_NOT_NULL(b);
  126. printf("After allocating %d bytes:\n", original.largest_free_block);
  127. heap_caps_print_heap_info(MALLOC_CAP_8BIT);
  128. multi_heap_info_t after;
  129. heap_caps_get_info(&after, MALLOC_CAP_8BIT);
  130. TEST_ASSERT(after.largest_free_block <= original.largest_free_block);
  131. TEST_ASSERT(after.total_free_bytes <= original.total_free_bytes);
  132. free(b);
  133. heap_caps_get_info(&after, MALLOC_CAP_8BIT);
  134. printf("\n\n After test, heap status:\n");
  135. heap_caps_print_heap_info(MALLOC_CAP_8BIT);
  136. /* Allow some leeway here, because LWIP sometimes allocates up to 144 bytes in the background
  137. as part of timer management.
  138. */
  139. TEST_ASSERT_INT32_WITHIN(200, after.total_free_bytes, original.total_free_bytes);
  140. TEST_ASSERT_INT32_WITHIN(200, after.largest_free_block, original.largest_free_block);
  141. TEST_ASSERT(after.minimum_free_bytes < original.total_free_bytes);
  142. }
  143. #if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2)
  144. //IDF-5167
  145. /* Small function runs from IRAM to check that malloc/free/realloc
  146. all work OK when cache is disabled...
  147. */
  148. static IRAM_ATTR __attribute__((noinline)) bool iram_malloc_test(void)
  149. {
  150. spi_flash_guard_get()->start(); // Disables flash cache
  151. bool result = true;
  152. void *x = heap_caps_malloc(64, MALLOC_CAP_EXEC);
  153. result = result && (x != NULL);
  154. void *y = heap_caps_realloc(x, 32, MALLOC_CAP_EXEC);
  155. result = result && (y != NULL);
  156. heap_caps_free(y);
  157. spi_flash_guard_get()->end(); // Re-enables flash cache
  158. return result;
  159. }
  160. TEST_CASE("heap_caps_xxx functions work with flash cache disabled", "[heap]")
  161. {
  162. TEST_ASSERT( iram_malloc_test() );
  163. }
  164. #endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2)
  165. #ifdef CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS
  166. TEST_CASE("When enabled, allocation operation failure generates an abort", "[heap][reset=abort,SW_CPU_RESET]")
  167. {
  168. const size_t stupid_allocation_size = (128 * 1024 * 1024);
  169. void *ptr = heap_caps_malloc(stupid_allocation_size, MALLOC_CAP_DEFAULT);
  170. (void)ptr;
  171. TEST_FAIL_MESSAGE("should not be reached");
  172. }
  173. #endif
  174. static bool called_user_failed_hook = false;
  175. void heap_caps_alloc_failed_hook(size_t requested_size, uint32_t caps, const char *function_name)
  176. {
  177. printf("%s was called but failed to allocate %d bytes with 0x%X capabilities. \n",function_name, requested_size, caps);
  178. called_user_failed_hook = true;
  179. }
  180. TEST_CASE("user provided alloc failed hook must be called when allocation fails", "[heap]")
  181. {
  182. TEST_ASSERT(heap_caps_register_failed_alloc_callback(heap_caps_alloc_failed_hook) == ESP_OK);
  183. const size_t stupid_allocation_size = (128 * 1024 * 1024);
  184. void *ptr = heap_caps_malloc(stupid_allocation_size, MALLOC_CAP_DEFAULT);
  185. TEST_ASSERT(called_user_failed_hook != false);
  186. called_user_failed_hook = false;
  187. ptr = heap_caps_realloc(ptr, stupid_allocation_size, MALLOC_CAP_DEFAULT);
  188. TEST_ASSERT(called_user_failed_hook != false);
  189. called_user_failed_hook = false;
  190. ptr = heap_caps_aligned_alloc(0x200, stupid_allocation_size, MALLOC_CAP_DEFAULT);
  191. TEST_ASSERT(called_user_failed_hook != false);
  192. (void)ptr;
  193. }
  194. TEST_CASE("allocation with invalid capability should also trigger the alloc failed hook", "[heap]")
  195. {
  196. const size_t allocation_size = 64;
  197. const uint32_t invalid_cap = MALLOC_CAP_INVALID;
  198. TEST_ASSERT(heap_caps_register_failed_alloc_callback(heap_caps_alloc_failed_hook) == ESP_OK);
  199. called_user_failed_hook = false;
  200. void *ptr = heap_caps_malloc(allocation_size, invalid_cap);
  201. TEST_ASSERT(called_user_failed_hook != false);
  202. called_user_failed_hook = false;
  203. ptr = heap_caps_realloc(ptr, allocation_size, invalid_cap);
  204. TEST_ASSERT(called_user_failed_hook != false);
  205. called_user_failed_hook = false;
  206. ptr = heap_caps_aligned_alloc(0x200, allocation_size, invalid_cap);
  207. TEST_ASSERT(called_user_failed_hook != false);
  208. (void)ptr;
  209. }
  210. #ifdef CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP
  211. /**
  212. * In MR 16031, the priority of RTC memory has been adjusted to the lowest.
  213. * RTC memory will not be consumed a lot during the startup process.
  214. */
  215. TEST_CASE("RTC memory shoule be lowest priority and its free size should be big enough", "[heap]")
  216. {
  217. const size_t allocation_size = 1024 * 4;
  218. void *ptr = NULL;
  219. size_t free_size = 0;
  220. ptr = heap_caps_malloc(allocation_size, MALLOC_CAP_DEFAULT);
  221. TEST_ASSERT_NOT_NULL(ptr);
  222. TEST_ASSERT(!esp_ptr_in_rtc_dram_fast(ptr));
  223. free_size = heap_caps_get_free_size(MALLOC_CAP_RTCRAM);
  224. TEST_ASSERT_GREATER_OR_EQUAL(1024 * 4, free_size);
  225. free(ptr);
  226. }
  227. #endif