blob: dde2399c2d5668d42a4dafc11eaec98f98ee47cc [file] [edit]
/*
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright © 2020 Keith Packard
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define _DEFAULT_SOURCE
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#define NUM_MALLOC 128
#define MAX_ALLOC 1024
static uint8_t *blocks[NUM_MALLOC];
static size_t block_size[NUM_MALLOC];
static uint8_t block_data[NUM_MALLOC];
static int order[NUM_MALLOC];
static int
randint(int max)
{
/* not evenly distributed, but it's good enough */
return random() % max;
}
static void
shuffle_order(void)
{
int i;
for (i = 0; i < NUM_MALLOC; i++)
order[i] = i;
for (i = 0; i < NUM_MALLOC - 1; i++) {
int j = randint(NUM_MALLOC - 1 - i) + i + 1;
int t = order[i];
order[i] = order[j];
order[j] = t;
}
}
static void
fill_block(int i)
{
block_data[i] = randint(256);
if (blocks[i])
memset(blocks[i], block_data[i], block_size[i]);
}
static void
reset_block(int i)
{
blocks[i] = NULL;
block_data[i] = 0;
block_size[i] = 0;
}
static void
reset_blocks(void)
{
int i;
for (i = 0; i < NUM_MALLOC; i++)
reset_block(i);
}
static int
check_block(char *which, int i)
{
size_t size = block_size[i];
uint8_t data = block_data[i];
uint8_t *block = blocks[i];
size_t j;
if (!block)
return 0;
for (j = 0; j < size; j++)
if (block[j] != data) {
printf("%s: wrong data in block %d at %u\n",
which, i, (unsigned) j);
return 1;
}
return 0;
}
static int
check_blocks(char *which)
{
int i;
int result = 0;
for (i = 0; i < NUM_MALLOC; i++)
result += check_block(which, i);
return result;
}
static int
check_malloc(size_t in_use)
{
int result = 0;
struct mallinfo info = mallinfo();
#ifdef _NANO_MALLOC
if (info.arena < info.fordblks + in_use) {
printf("non-free bytes in arena %zu free %zu\n", info.arena, info.fordblks);
result++;
}
if (in_use == 0 && info.ordblks != 1) {
printf("%zd blocks free\n", info.ordblks);
result++;
}
if (info.uordblks < in_use) {
printf("expected at least %zu in use (%zu)\n", in_use, info.uordblks);
result++;
}
if (in_use == 0 && info.uordblks != 0) {
printf("expected all free but %zu still reported in use\n", info.uordblks);
result++;
}
#endif
return result;
}
#ifdef _NANO_MALLOC
extern size_t __malloc_minsize, __malloc_align, __malloc_head;
#endif
int
main(void)
{
int loops;
int result = 0;
for (loops = 0; loops < 10; loops++)
{
long i;
size_t in_use;
reset_blocks();
in_use = 0;
/* Test slowly increasing size of a block using realloc */
for (i = 0; i < NUM_MALLOC; i++) {
result += check_block("grow", 0);
block_size[0] += randint(MAX_ALLOC);
in_use = block_size[0];
blocks[0] = realloc(blocks[0], block_size[0]);
fill_block(0);
}
result += check_block("grow done", 0);
result += check_malloc(in_use);
free(blocks[0]);
in_use = 0;
/* Make sure everything is free */
result += check_malloc(in_use);
reset_blocks();
#pragma GCC diagnostic push
#ifndef __clang__
#pragma GCC diagnostic ignored "-Walloc-size-larger-than=PTRDIFF_MAX"
#endif
/* Test huge malloc sizes */
for (i = sizeof(size_t) * 8 - 2; i < (long) (sizeof(size_t) * 8); i++) {
blocks[0] = malloc((size_t) 1 << i);
if (blocks[0])
free(blocks[0]);
}
in_use = 0;
/* Make sure everything is free */
result += check_malloc(in_use);
reset_blocks();
/* Test allocating negative amounts */
for (i = -1; i >= -128; i--) {
blocks[0] = malloc((size_t) i);
#pragma GCC diagnostic pop
if (blocks[0]) {
printf("malloc size %ld succeeded\n", i);
result++;
free(blocks[0]);
}
}
in_use = 0;
/* Make sure everything is free */
result += check_malloc(in_use);
reset_blocks();
/* Test allocating random chunks */
for (i = 0; i < NUM_MALLOC; i++) {
size_t size = randint(MAX_ALLOC);
block_size[i] = size;
blocks[i] = malloc(size);
in_use += size;
fill_block(i);
}
result += check_blocks("malloc");
result += check_malloc(in_use);
/* Realloc them to new random sizes in a random order
*/
shuffle_order();
for (i = 0; i < NUM_MALLOC; i++) {
size_t size = randint(MAX_ALLOC);
int j = order[i];
in_use -= block_size[j];
blocks[j] = realloc(blocks[j], size);
block_size[j] = size;
in_use += size;
fill_block(j);
}
result += check_blocks("realloc");
result += check_malloc(in_use);
shuffle_order();
for (i = 0; i < NUM_MALLOC; i++) {
int j = order[i];
check_block("realloc block", j);
free(blocks[j]);
in_use -= block_size[j];
}
result += check_malloc(in_use);
if (in_use != 0) {
printf("malloc stress accounting error\n");
result++;
}
reset_blocks();
for (i = 0; i < NUM_MALLOC; i++) {
size_t size = randint(MAX_ALLOC);
size_t align = 1 << (3 + randint(7));
block_size[i] = size;
blocks[i] = memalign(align, size);
if ((uintptr_t) blocks[i] & (align - 1)) {
printf("unaligned block returned %p align %zu size %zu\n",
blocks[i], align, size);
result++;
}
fill_block(i);
in_use += size;
}
result += check_blocks("memalign");
result += check_malloc(in_use);
shuffle_order();
for (i = 0; i < NUM_MALLOC; i++) {
int j = order[i];
check_block("free", j);
free(blocks[j]);
in_use -= block_size[j];
}
result += check_malloc(in_use);
if (in_use != 0) {
printf("malloc stress accounting error\n");
result++;
}
}
return result;
}