#ifdef __FreeBSD__
#include <stdlib.h>
#include <malloc_np.h>
#else
#include <malloc.h>
#endif
#include <sys/wait.h>
#include "unittest.h"
#define NBUFS 16
static size_t
get_rand_size(void)
{
return sizeof(int) + 64 * (rand() % 100);
}
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]);
int nfork = atoi(argv[2]);
int nthread = atoi(argv[3]);
UT_ASSERT(nfork >= 0);
UT_ASSERT(nthread >= 0);
os_thread_t thread[nthread];
int first_child = 0;
int **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 (int i = 0; i < nfork; i++) {
for (int j = 0; j < NBUFS; j++) {
int 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 (int j = 0; j < NBUFS; j++) {
*bufs[i * NBUFS + j] = ((unsigned)pids2[i] << 16) + j;
}
if (pids1[i]) {
for (int t = 0; t < nthread; ++t) {
PTHREAD_JOIN(&thread[t], NULL);
free(thread_sizes[t]);
}
free(thread_sizes);
} else {
first_child = i + 1;
}
for (int ii = 0; ii < i; ii++) {
for (int j = 0; j < NBUFS; j++) {
UT_ASSERTeq(*bufs[ii * NBUFS + j],
((unsigned)pids2[ii] << 16) + j);
}
}
}
for (int 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);
}
}