blob: 75493e442c4af357e9266e2bb8336d61dd315483 [file] [log] [blame]
// Copyright 2016 The Fuchsia 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 <inttypes.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#define TICKS 0
#define USE_PTHREAD_MUTEXES 0
#define USE_SPINLOCKS 0
#define USE_FUTEXES 1
// malloc may behave differently for larger allocations (e.g., using mmap).
// Using up to 512k allocations will likely trigger this behavior.
#define LARGE_MALLOC 0
#define LARGE_MALLOC_SIZE (512 * 1024)
#define SMALL_MALLOC_SIZE 1024
#if LARGE_MALLOC
#define MALLOC_SIZE LARGE_MALLOC_SIZE
#else
#define MALLOC_SIZE SMALL_MALLOC_SIZE
#endif
static atomic_int xlock = ATOMIC_VAR_INIT(0);
static void _lock(atomic_int* lock) {
while (atomic_exchange(lock, 1) != 0)
;
}
static void _unlock(atomic_int* lock) {
atomic_store(lock, 0);
}
static void _ftxlock(atomic_int* lock) {
while (atomic_exchange(lock, 1) != 0) {
zx_futex_wait(lock, 1, ZX_HANDLE_INVALID, ZX_TIME_INFINITE);
}
}
static void _ftxunlock(atomic_int* lock) {
atomic_store(lock, 0);
zx_futex_wake(lock, 1);
}
#if USE_PTHREAD_MUTEXES
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
#define LOCK() \
do { \
if (info->lock) \
pthread_mutex_lock(&lock); \
} while (0)
#define UNLOCK() \
do { \
if (info->lock) \
pthread_mutex_unlock(&lock); \
} while (0)
#endif
#if USE_SPINLOCKS
#define LOCK() \
do { \
if (info->lock) \
_lock(&xlock); \
} while (0)
#define UNLOCK() \
do { \
if (info->lock) \
_unlock(&xlock); \
} while (0)
#endif
#if USE_FUTEXES
#define LOCK() \
do { \
if (info->lock) \
_ftxlock(&xlock); \
} while (0)
#define UNLOCK() \
do { \
if (info->lock) \
_ftxunlock(&xlock); \
} while (0)
#endif
#define THREADS 8
#define BUCKETS 16
typedef struct info {
pthread_t t;
int n;
atomic_int lock;
int size[BUCKETS];
void* bucket[BUCKETS];
} info_t;
int rnum(int m) {
uint32_t data;
zx_cprng_draw((void*)&data, sizeof(data));
return (data & 0x7FFFFFFFU) % m;
}
void* blaster(void* arg) {
info_t* info = arg;
#if TICKS
int tick = rnum(5000);
#endif
for (;;) {
#if TICKS
tick++;
if (tick == 10000) {
printf("(%d)\n", info->n);
tick = rnum(5000);
}
#endif
int n = rnum(BUCKETS);
if (info->bucket[n] == NULL) {
allocnew:
info->size[n] = 7 + rnum(MALLOC_SIZE);
LOCK();
info->bucket[n] = malloc(info->size[n]);
UNLOCK();
if (info->bucket[n] == NULL) {
printf("blaster %d malloc failed %d\n", info->n, n);
__builtin_trap();
}
memset(info->bucket[n], info->n * n, info->size[n]);
} else {
int sz = info->size[n];
uint8_t* x = info->bucket[n];
int val = n * info->n;
for (int i = 0; i < sz; i++) {
if (x[i] != val) {
printf("blaster %d bad bucket %d\n", info->n, n);
__builtin_trap();
}
}
if (rnum(1000) < 750) {
LOCK();
free(info->bucket[n]);
UNLOCK();
goto allocnew;
} else {
memset(x, val, sz);
}
}
}
return NULL;
}
int heapblaster(int count, int locking) {
info_t info[THREADS] = {};
if (count < 1)
count = 1;
if (count >= THREADS)
count = THREADS;
printf("heapblaster: starting %d threads... (%s)\n",
count, locking ? "locking" : "not locking");
for (int n = 0; n < count; n++) {
info[n].lock = locking;
info[n].n = n;
if (count == 1) {
blaster(info + n);
return 0;
} else {
pthread_create(&info[n].t, NULL, blaster, info + n);
}
}
for (;;)
sleep(1000);
return 0;
}
static uint8_t data[65534];
int writespam(int opt) {
zx_handle_t p[2];
zx_status_t r;
uint64_t count = 0;
if ((r = zx_channel_create(0, p, p + 1)) < 0) {
printf("cleanup-test: channel create 0 failed: %d\n", r);
return -1;
}
printf("evil-tests: about to spam data into a channel\n");
for (;;) {
count++;
if ((r = zx_channel_write(p[0], 0, data, sizeof(data), NULL, 0)) < 0) {
printf("evil-tests: SUCCESS, writespammer error %d after only %" PRIu64 " writes\n", r, count);
return 0;
}
if ((count % 1000) == 0) {
printf("evil-tests: wrote %" PRIu64 " messages (%" PRIu64 " bytes).\n", count, count * sizeof(data));
}
}
if (opt == 0) {
printf("evil-tests: closing the channel (full of messages)\n");
zx_handle_close(p[0]);
zx_handle_close(p[1]);
} else {
printf("evil-tests: leaving the channel open (full of messages)\n");
}
return 0;
}
int handlespam(void) {
zx_handle_t p[2];
uint64_t count = 0;
printf("evil-tests: about to create all the handles\n");
for (;;) {
zx_status_t status;
if ((status = zx_channel_create(0, p, p + 1)) < 0) {
printf("evil-tests: SUCCESS, channel create failed %d after %" PRIu64 " created\n", status, count);
return 0;
}
count++;
if ((count % 1000) == 0) {
printf("evil-tests: created %" PRIu64 " channels\n", count);
}
}
return 0;
}
int nanospam(void) {
for (;;) {
zx_nanosleep(1);
}
}
int main(int argc, char** argv) {
if (argc < 2) {
printf(
"usage: evil-tests spam1 spam writes into channel\n"
" evil-tests spam2 spam writes, don't close channel after\n"
" evil-tests spam3 spam handle creation\n"
" evil-tests nano spam nanosleep\n"
" evil-tests heap1 <n> heap stress test, locking\n"
" evil-tests heap2 <n> heap stress test, no locking\n");
return -1;
} else if (!strcmp(argv[1], "spam1")) {
return writespam(0);
} else if (!strcmp(argv[1], "spam2")) {
return writespam(1);
} else if (!strcmp(argv[1], "spam3")) {
return handlespam();
} else if (!strcmp(argv[1], "nano")) {
return nanospam();
} else if (!strcmp(argv[1], "heap1")) {
int n = (argc > 2) ? strtoul(argv[2], 0, 10) : THREADS;
return heapblaster(n, 1);
} else if (!strcmp(argv[1], "heap2")) {
int n = (argc > 2) ? strtoul(argv[2], 0, 10) : THREADS;
return heapblaster(n, 0);
} else {
printf("unknown sub-command '%s'\n", argv[1]);
return -1;
}
return 0;
}