/*
* Copyright 2015-2018, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * 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
* OWNER 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.
*/
/*
* vmmalloc_fork.c -- unit test for libvmmalloc fork() support
*
* usage: vmmalloc_fork [c|e] <nfork> <nthread>
*/
#ifdef __FreeBSD__
#include <stdlib.h>
#include <malloc_np.h>
#else
#include <malloc.h>
#endif
#include <sys/wait.h>
#include "unittest.h"
#define NBUFS 16
/*
* get_rand_size -- returns random size of allocation
*/
static size_t
get_rand_size(void)
{
return sizeof(int) + 64 * ((unsigned)rand() % 100);
}
/*
* do_test -- thread callback
*
* This function is called in separate thread and the main thread
* forks a child processes. Please be aware that any locks held in this
* function may potentially cause a deadlock.
*
* For example using rand() in this function may cause a deadlock because
* it grabs an internal lock.
*/
static void *
do_test(void *arg)
{
int **bufs = malloc(NBUFS * sizeof(void *));
UT_ASSERTne(bufs, NULL);
size_t *sizes = (size_t *)arg;
UT_ASSERTne(sizes, NULL);
for (int j = 0; j < NBUFS; j++) {
bufs[j] = malloc(sizes[j]);
UT_ASSERTne(bufs[j], NULL);
}
for (int j = 0; j < NBUFS; j++) {
UT_ASSERT(malloc_usable_size(bufs[j]) >= sizes[j]);
free(bufs[j]);
}
free(bufs);
return NULL;
}
int
main(int argc, char *argv[])
{
if (argc == 4 && argv[3][0] == 't') {
exit(0);
}
START(argc, argv, "vmmalloc_fork");
if (argc < 4)
UT_FATAL("usage: %s [c|e] <nfork> <nthread>", argv[0]);
unsigned nfork = ATOU(argv[2]);
unsigned nthread = ATOU(argv[3]);
os_thread_t thread[nthread];
unsigned first_child = 0;
unsigned **bufs = malloc(nfork * NBUFS * sizeof(void *));
UT_ASSERTne(bufs, NULL);
size_t *sizes = malloc(nfork * NBUFS * sizeof(size_t));
UT_ASSERTne(sizes, NULL);
int *pids1 = malloc(nfork * sizeof(pid_t));
UT_ASSERTne(pids1, NULL);
int *pids2 = malloc(nfork * sizeof(pid_t));
UT_ASSERTne(pids2, NULL);
for (unsigned i = 0; i < nfork; i++) {
for (unsigned j = 0; j < NBUFS; j++) {
unsigned idx = i * NBUFS + j;
sizes[idx] = get_rand_size();
bufs[idx] = malloc(sizes[idx]);
UT_ASSERTne(bufs[idx], NULL);
UT_ASSERT(malloc_usable_size(bufs[idx]) >= sizes[idx]);
}
size_t **thread_sizes = malloc(sizeof(size_t *) * nthread);
UT_ASSERTne(thread_sizes, NULL);
for (int t = 0; t < nthread; ++t) {
thread_sizes[t] = malloc(NBUFS * sizeof(size_t));
UT_ASSERTne(thread_sizes[t], NULL);
for (int j = 0; j < NBUFS; j++)
thread_sizes[t][j] = get_rand_size();
}
for (int t = 0; t < nthread; ++t) {
PTHREAD_CREATE(&thread[t], NULL,
do_test, thread_sizes[t]);
}
pids1[i] = fork();
if (pids1[i] == -1)
UT_OUT("fork failed");
UT_ASSERTne(pids1[i], -1);
if (pids1[i] == 0 && argv[1][0] == 'e' && i == nfork - 1) {
int fd = os_open("/dev/null", O_RDWR, S_IWUSR);
int res = dup2(fd, 1);
UT_ASSERTne(res, -1);
os_close(fd);
execl("/bin/echo", "/bin/echo", "Hello world!", NULL);
}
pids2[i] = getpid();
for (unsigned j = 0; j < NBUFS; j++) {
*bufs[i * NBUFS + j] = ((unsigned)pids2[i] << 16) + j;
}
if (pids1[i]) {
/* parent */
for (int t = 0; t < nthread; ++t) {
PTHREAD_JOIN(&thread[t], NULL);
free(thread_sizes[t]);
}
free(thread_sizes);
} else {
/* child */
first_child = i + 1;
}
for (unsigned ii = 0; ii < i; ii++) {
for (unsigned j = 0; j < NBUFS; j++) {
UT_ASSERTeq(*bufs[ii * NBUFS + j],
((unsigned)pids2[ii] << 16) + j);
}
}
}
for (unsigned i = first_child; i < nfork; i++) {
int status;
waitpid(pids1[i], &status, 0);
UT_ASSERT(WIFEXITED(status));
UT_ASSERTeq(WEXITSTATUS(status), 0);
}
free(pids1);
free(pids2);
for (int i = 0; i < nfork; i++) {
for (int j = 0; j < NBUFS; j++) {
int idx = i * NBUFS + j;
UT_ASSERT(malloc_usable_size(bufs[idx]) >= sizes[idx]);
free(bufs[idx]);
}
}
free(sizes);
free(bufs);
if (first_child == 0) {
DONE(NULL);
}
}