306 lines
7.1 KiB
C
306 lines
7.1 KiB
C
/*
|
|
* 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 %p!\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 %p\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 %p\n",
|
|
__func__, __LINE__, pbuf);
|
|
goto bailout;
|
|
}
|
|
if (pbuf->next_buffer->prev_buffer != pbuf) {
|
|
ccprintf("%s:%d"
|
|
" - inconsistent next buffer at %p\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(void)
|
|
{
|
|
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;
|
|
}
|