blob: af80f3f041706f97f16dcc16ec7cb968ed017e64 [file] [log] [blame]
/*
* Copyright (c) 2013 The Native Client 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 <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define WSIZE sizeof(uint64_t)
#include "gtest/gtest.h"
namespace {
class MallocCallocReallocFreeTests : public ::testing::Test {
protected:
MallocCallocReallocFreeTests() {
// You can do set-up work for each test here.
}
~MallocCallocReallocFreeTests() override {
}
void SetUp() override {
}
void TearDown() override {
}
};
} //namespace
/*
* Generate a deterministic sequence of numbers.
* Use the Hailstone sequence, just for fun.
*/
uint64_t next_seq(uint64_t num) {
if (num & 1) {
return 3 * num + 1;
} else {
return num / 2;
}
}
/*
* Initialize the memory with a deterministic sequence. Given numwords = N,
* the initialized memory will look like this (words):
* [N, s1, s2, s3 ...]
* s1 <- (addr % 1000). s2 <- next_seq(s1). s3 <- next_seq(s2), etc...
*/
void init_memory(void *addr, uint32_t numwords) {
ASSERT_GE(numwords, (uint32_t)1);
uint64_t *wordaddr = (uint64_t*)addr;
wordaddr[0] = numwords;
for (uint32_t i = 1; i < numwords; i++) {
wordaddr[i] = (i == 1) ? (((uint64_t)addr) % 1000)
: next_seq(wordaddr[i - 1]);
}
}
/* Verify that memory is initialized as expected from init_memory */
void verify_memory(void *addr) {
ASSERT_NE(addr, nullptr);
uint64_t *wordaddr = (uint64_t*)addr;
uint64_t numwords = wordaddr[0];
ASSERT_GE(numwords, (uint64_t)1);
for (uint32_t i = 1; i < numwords; i++) {
if ((i == 1 && wordaddr[1] == ((uint64_t)addr % 1000)) ||
wordaddr[i] == next_seq(wordaddr[i - 1])) {
/* Good! */
} else {
ASSERT_TRUE(false) << "Memory verification error. Buffer starting at "
<< addr <<", index " << i << ": " << wordaddr[i];
}
}
}
/*
* Runs an allocation test using the specified allocator.
*/
typedef enum {
USE_MALLOC, USE_CALLOC, USE_REALLOC
} WhichAlloc;
void run_allocation_test(WhichAlloc wa, uint32_t numallocs) {
void **table = (void **)calloc(numallocs, sizeof(void*));
for (uint32_t i = 0; i < numallocs; i++) {
/* Random allocation size */
uint32_t numwords = 1 + rand() % (2 << 15);
/* Allocate the memory and save the pointer in table */
void *addr = 0;
if (wa == USE_MALLOC) {
addr = malloc(WSIZE * numwords);
} else if (wa == USE_CALLOC) {
addr = calloc(numwords, WSIZE);
} else if (wa == USE_REALLOC) {
addr = realloc(0, WSIZE * numwords);
}
ASSERT_NE(addr, nullptr);
table[i] = addr;
/* Initialize allocated memory */
init_memory(addr, numwords);
}
/* To stir things up, allocate, init and release some memory */
#define NSTIR 10
void *stir_table[NSTIR];
for (int i = 0; i < NSTIR; ++i) {
uint32_t numwords = 1 + rand() % (2 << 18);
void *addr = malloc(WSIZE * numwords);
init_memory(addr, numwords);
stir_table[i] = addr;
}
for (int i = 0; i < NSTIR; ++i) {
free(stir_table[i]);
}
/* When all was allocated, verify that all memory looks as expected */
for (uint32_t i = 0; i < numallocs; i++) {
verify_memory(table[i]);
}
/* Finally, free all allocated memory */
for (uint i = 0; i < numallocs; i++) {
free(table[i]);
}
free(table);
}
/* A volatile pointer to thwart optimizations in some tests */
volatile void* volatile vaddr;
TEST_F(MallocCallocReallocFreeTests, TestMallocCallocReallocFree) {
unsigned seed = time(0);
srand(seed);
printf("Random seed = %u\n", seed);
/*
* Main allocation tests: loop-run run_allocation_test for the different ways
* to allocate memory (with malloc, calloc and realloc).
*/
const uint32_t NUMALLOCS = 20;
const uint32_t NUMRUNS = 10;
for (uint32_t i = 0; i < NUMRUNS; i++) {
run_allocation_test(USE_MALLOC, NUMALLOCS);
run_allocation_test(USE_CALLOC, NUMALLOCS);
run_allocation_test(USE_REALLOC, NUMALLOCS);
}
/*
* Test that calloc zeroes the memory properly.
*/
uint32_t zero = 0;
void *m = calloc(4, 1);
ASSERT_EQ(memcmp(m, &zero, 4), 0);
free(m);
m = calloc(1, 1);
ASSERT_EQ(memcmp(m, &zero, 1), 0);
free(m);
/*
* Test that realloc does the right thing, leaving the old contents in place.
*/
uint32_t nwords = 100;
m = malloc(nwords * WSIZE);
init_memory(m, nwords);
/* Save a copy of m aside, to compare later. We can't just use verify_memory
* on the reallocated buffer because its location can change.
*/
void *m2 = malloc(nwords * WSIZE);
memcpy(m2, m, nwords * WSIZE);
m = realloc(m, 3 * nwords * WSIZE);
ASSERT_EQ(memcmp(m, m2, nwords * WSIZE), 0);
free(m);
free(m2);
/*
* Testing corner cases. In many of these our only test is that nothing
* crashes, but that's also something.
*/
free(NULL);
vaddr = malloc(0);
free((void*)vaddr);
vaddr = calloc(0, 4);
free((void*)vaddr);
vaddr = realloc(NULL, 0);
free((void*)vaddr);
vaddr = malloc(7);
vaddr = realloc((void*)vaddr, 0);
}