| // 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; |
| } |