#include #include #include #include #include static uintptr_t expected_malloc_alignment(size_t size) { /* TODO use ffs? */ if (size >= 16) { return sizeof(void*) * 2; } else if (size >= 8) { return 8; } else if (size >= 4) { return 4; } else if (size >= 2) { return 2; } else { return 1; } } TEST(memorytest, malloc_alignment) { for (int size = 2; size < 256; size++) { const uintptr_t alignment_mask = expected_malloc_alignment(size) - 1; void* ptr1 = malloc(size); void* ptr2 = malloc(size / 2); /* Try to shake up the alignment. */ void* ptr3 = malloc(size); ASSERT_EQ(0, (uintptr_t)ptr1 & alignment_mask, "size %d / align %ld\n", size, alignment_mask + 1); ASSERT_EQ(0, (uintptr_t)ptr3 & alignment_mask, "size %d / align %ld\n", size, alignment_mask + 1); free(ptr3); free(ptr2); free(ptr1); } test_abort:; } #define MEMORYTEST_CONCURRENT_ALLOCATION_THREADS 64 struct memorytest_alloc_thread_arg { size_t alloc_size; int count; atomic_int threads_done; atomic_int chunks_allocated; atomic_bool go; }; int memorytest_alloc_thread(void* _arg) { struct memorytest_alloc_thread_arg* arg = _arg; void** ptrs; int i; int ret; ptrs = calloc(arg->count, sizeof(*ptrs)); if (!ptrs) { return ERR_NO_MEMORY; } /* Busy-wait until control thread says go. */ while (!atomic_load(&arg->go)) { } for (i = 0; i < arg->count; i++) { ptrs[i] = malloc(arg->alloc_size); if (!ptrs[i]) { ret = ERR_NO_MEMORY; goto err_malloc; } atomic_fetch_add(&arg->chunks_allocated, 1); } ret = 0; err_malloc: atomic_fetch_add(&arg->threads_done, 1); while (atomic_load(&arg->threads_done) != MEMORYTEST_CONCURRENT_ALLOCATION_THREADS) { thread_sleep(10); } while (i-- > 0) { free(ptrs[i]); } free(ptrs); return ret; } TEST(memorytest, concurrent_allocation) { /* * Test concurrent allocation by creating many threads. If this test is * as the first test after boot, it will test the behavior while growing * the heap. Assuming the heap implemention never shrinks the heap, * additional test runs will not exercise this path. */ struct memorytest_alloc_thread_arg thread_arg = { .alloc_size = PAGE_SIZE / 4 * 3, /* ~1 page after heap overhead */ .count = 8, .threads_done = 0, .chunks_allocated = 0, .go = false, }; struct thread* thread[MEMORYTEST_CONCURRENT_ALLOCATION_THREADS]; for (size_t i = 0; i < countof(thread); i++) { thread[i] = thread_create("memorytest", memorytest_alloc_thread, &thread_arg, HIGH_PRIORITY - 1, DEFAULT_STACK_SIZE); ASSERT_NE(0, thread[i]); } for (size_t i = 0; i < countof(thread); i++) { ASSERT_EQ(0, thread_resume(thread[i])); } /* Wait for test threads to start and migrate to other cpus */ thread_sleep(100); atomic_store(&thread_arg.go, true); for (size_t i = 0; i < countof(thread); i++) { int retcode; ASSERT_EQ(0, thread_join(thread[i], &retcode, INFINITE_TIME)); EXPECT_EQ(0, retcode, "Chunks allocated: %d\n", atomic_load(&thread_arg.chunks_allocated)); } test_abort:; } PORT_TEST(memorytest, "com.android.kernel.memorytest");