blob: 70e0fb5aabbc2a88df04e697f560335e8e36a477 [file] [log] [blame]
/*
* Copyright 2016 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "compile_time_macros.h"
#include "console.h"
#include "link_defs.h"
#include "shared_mem.h"
#include "test_util.h"
/*
* Total size of memory in the malloc pool (shared between free and allocated
* buffers.
*/
static int total_size;
/*
* Number of randomized allocation/free attempts, large enough to execute all
* branches in the malloc/free module.
*/
static int counter = 500000;
/*
* A good random number generator approximation. Guaranteed to generate the
* same sequence on all test runs.
*/
static uint32_t next = 127;
static uint32_t myrand(void)
{
next = next * 1103515245 + 12345;
return ((uint32_t)(next/65536) % 32768);
}
/* Keep track of buffers allocated by the test function. */
static struct {
void *buf;
size_t buffer_size;
} allocations[12]; /* Up to 12 buffers could be allocated concurrently. */
/*
* Verify that allocated and free buffers do not overlap, and that our and
* malloc's ideas of the number of allocated buffers match.
*/
static int check_for_overlaps(void)
{
int i;
int allocation_match;
int allocations_count, allocated_count;
allocations_count = allocated_count = 0;
for (i = 0; i < ARRAY_SIZE(allocations); i++) {
struct shm_buffer *allocced_buf;
if (!allocations[i].buf)
continue;
/*
* Indication of finding the allocated buffer in internal
* malloc structures.
*/
allocation_match = 0;
/* number of buffers allocated by the test program. */
allocations_count++;
/*
* Number of allocated buffers malloc knows about, calculated
* multiple times to keep things simple.
*/
allocated_count = 0;
for (allocced_buf = allocced_buf_chain;
allocced_buf;
allocced_buf = allocced_buf->next_buffer) {
int allocated_size, allocation_size;
allocated_count++;
if (allocations[i].buf != (allocced_buf + 1))
continue;
allocated_size = allocced_buf->buffer_size;
allocation_size = allocations[i].buffer_size;
/*
* Verify that size requested by the allocator matches
* the value used by malloc, i.e. does not exceed the
* allocated size and is no less than two buffer
* structures lower (which can happen when the
* requested size was rounded up to cover gaps smaller
* than the buffer header structure size).
*/
if ((allocation_size > allocated_size) ||
((allocated_size - allocation_size) >=
(2 * sizeof(struct shm_buffer) + sizeof(int)))) {
ccprintf("inconsistency: allocated (size %d)"
" allocation %d(size %d)\n",
allocated_size, i, allocation_size);
return 0;
}
if (allocation_match++) {
ccprintf("inconsistency: duplicated match\n");
return 0;
}
}
if (!allocation_match) {
ccprintf("missing match %pP!\n", allocations[i].buf);
return 0;
}
}
if (allocations_count != allocated_count) {
ccprintf("count mismatch (%d != %d)!\n",
allocations_count, allocated_count);
return 0;
}
return 1;
}
/*
* Verify that shared memory is in a consistent state, i.e. that there is no
* overlaps between allocated and free buffers, and that all memory is
* accounted for (is either allocated or available).
*/
static int shmem_is_ok(int line)
{
int count = 0;
int running_size = 0;
struct shm_buffer *pbuf = free_buf_chain;
if (pbuf && pbuf->prev_buffer) {
ccprintf("Bad free buffer list start %pP\n", pbuf);
goto bailout;
}
while (pbuf) {
struct shm_buffer *top;
running_size += pbuf->buffer_size;
if (count++ > 100)
goto bailout; /* Is there a loop? */
top = (struct shm_buffer *)((uintptr_t)pbuf +
pbuf->buffer_size);
if (pbuf->next_buffer) {
if (top >= pbuf->next_buffer) {
ccprintf("%s:%d"
" - inconsistent buffer size at %pP\n",
__func__, __LINE__, pbuf);
goto bailout;
}
if (pbuf->next_buffer->prev_buffer != pbuf) {
ccprintf("%s:%d"
" - inconsistent next buffer at %pP\n",
__func__, __LINE__, pbuf);
goto bailout;
}
}
pbuf = pbuf->next_buffer;
}
if (pbuf) { /* Must be a loop. */
ccprintf("Too many buffers in the chain\n");
goto bailout;
}
/* Make sure there were at least 5 buffers allocated at one point. */
if (count > 5)
set_map_bit(1 << 24);
/* Add allocated sizes. */
for (pbuf = allocced_buf_chain; pbuf; pbuf = pbuf->next_buffer)
running_size += pbuf->buffer_size;
if (total_size) {
if (total_size != running_size)
goto bailout;
} else {
/* Remember total size for future reference. */
total_size = running_size;
}
if (!check_for_overlaps())
goto bailout;
return 1;
bailout:
ccprintf("Line %d, counter %d. The list has been corrupted, "
"total size %d, running size %d\n",
line, counter, total_size, running_size);
return 0;
}
/*
* Bitmap used to keep track of branches taken by malloc/free routines. Once
* all bits in the 0..(MAX_MASK_BIT - 1) range are set, consider the test
* completed.
*/
static uint32_t test_map;
void run_test(int argc, char **argv)
{
int index;
const int shmem_size = shared_mem_size();
while (counter--) {
char *shptr;
uint32_t r_data;
r_data = myrand();
if (!(counter % 50000))
ccprintf("%d\n", counter);
/*
* If all bits we care about are set in the map - the test is
* over.
*/
if ((test_map & ALL_PATHS_MASK) == ALL_PATHS_MASK) {
if (test_map & ~ALL_PATHS_MASK) {
ccprintf("Unexpected mask bits set: %x"
", counter %d\n",
test_map & ~ALL_PATHS_MASK,
counter);
test_fail();
return;
}
ccprintf("Done testing, counter at %d\n", counter);
test_pass();
return;
}
/* Pick a random allocation entry. */
index = r_data % ARRAY_SIZE(allocations);
if (allocations[index].buf) {
/*
* If there is a buffer associated with the entry -
* release it.
*/
shared_mem_release(allocations[index].buf);
allocations[index].buf = 0;
if (!shmem_is_ok(__LINE__)) {
test_fail();
return;
}
} else {
size_t alloc_size = r_data % (shmem_size);
/*
* If the allocation entry is empty - allocate a
* buffer of a random size up to max shared memory.
*/
if (shared_mem_acquire(alloc_size, &shptr) ==
EC_SUCCESS) {
allocations[index].buf = (void *) shptr;
allocations[index].buffer_size = alloc_size;
/*
* Make sure every allocated byte is
* modified.
*/
while (alloc_size--)
shptr[alloc_size] =
shptr[alloc_size] ^ 0xff;
if (!shmem_is_ok(__LINE__)) {
test_fail();
return;
}
}
}
}
/*
* The test is over, free all still allcated buffers, if any. Keep
* verifying memory consistency after each free() invocation.
*/
for (index = 0; index < ARRAY_SIZE(allocations); index++)
if (allocations[index].buf) {
shared_mem_release(allocations[index].buf);
allocations[index].buf = NULL;
if (!shmem_is_ok(__LINE__)) {
test_fail();
return;
}
}
ccprintf("Did not pass all paths, map %x != %x\n",
test_map, ALL_PATHS_MASK);
test_fail();
}
void set_map_bit(uint32_t mask)
{
test_map |= mask;
}