1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <inttypes.h>
6 #include <stdatomic.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11
12 #include <pthread.h>
13
14 #include <zircon/syscalls.h>
15 #include <zircon/types.h>
16
17 #define TICKS 0
18
19 #define USE_PTHREAD_MUTEXES 0
20 #define USE_SPINLOCKS 0
21 #define USE_FUTEXES 1
22
23 // malloc may behave differently for larger allocations (e.g., using mmap).
24 // Using up to 512k allocations will likely trigger this behavior.
25 #define LARGE_MALLOC 0
26 #define LARGE_MALLOC_SIZE (512 * 1024)
27 #define SMALL_MALLOC_SIZE 1024
28 #if LARGE_MALLOC
29 #define MALLOC_SIZE LARGE_MALLOC_SIZE
30 #else
31 #define MALLOC_SIZE SMALL_MALLOC_SIZE
32 #endif
33
34 static atomic_int xlock = ATOMIC_VAR_INIT(0);
35
_lock(atomic_int * lock)36 static void _lock(atomic_int* lock) {
37 while (atomic_exchange(lock, 1) != 0)
38 ;
39 }
_unlock(atomic_int * lock)40 static void _unlock(atomic_int* lock) {
41 atomic_store(lock, 0);
42 }
43
_ftxlock(atomic_int * lock)44 static void _ftxlock(atomic_int* lock) {
45 while (atomic_exchange(lock, 1) != 0) {
46 zx_futex_wait(lock, 1, ZX_HANDLE_INVALID, ZX_TIME_INFINITE);
47 }
48 }
_ftxunlock(atomic_int * lock)49 static void _ftxunlock(atomic_int* lock) {
50 atomic_store(lock, 0);
51 zx_futex_wake(lock, 1);
52 }
53
54 #if USE_PTHREAD_MUTEXES
55 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
56 #define LOCK() \
57 do { \
58 if (info->lock) \
59 pthread_mutex_lock(&lock); \
60 } while (0)
61 #define UNLOCK() \
62 do { \
63 if (info->lock) \
64 pthread_mutex_unlock(&lock); \
65 } while (0)
66 #endif
67
68 #if USE_SPINLOCKS
69 #define LOCK() \
70 do { \
71 if (info->lock) \
72 _lock(&xlock); \
73 } while (0)
74 #define UNLOCK() \
75 do { \
76 if (info->lock) \
77 _unlock(&xlock); \
78 } while (0)
79 #endif
80
81 #if USE_FUTEXES
82 #define LOCK() \
83 do { \
84 if (info->lock) \
85 _ftxlock(&xlock); \
86 } while (0)
87 #define UNLOCK() \
88 do { \
89 if (info->lock) \
90 _ftxunlock(&xlock); \
91 } while (0)
92 #endif
93
94 #define THREADS 8
95 #define BUCKETS 16
96
97 typedef struct info {
98 pthread_t t;
99 int n;
100 atomic_int lock;
101 int size[BUCKETS];
102 void* bucket[BUCKETS];
103 } info_t;
104
rnum(int m)105 int rnum(int m) {
106 return (random() & 0x7FFFFFFFU) % m;
107 }
108
blaster(void * arg)109 void* blaster(void* arg) {
110 info_t* info = arg;
111 #if TICKS
112 int tick = rnum(5000);
113 #endif
114
115 for (;;) {
116 #if TICKS
117 tick++;
118 if (tick == 10000) {
119 printf("(%d)\n", info->n);
120 tick = rnum(5000);
121 }
122 #endif
123 int n = rnum(BUCKETS);
124 if (info->bucket[n] == NULL) {
125 allocnew:
126 info->size[n] = 7 + rnum(MALLOC_SIZE);
127 LOCK();
128 info->bucket[n] = malloc(info->size[n]);
129 UNLOCK();
130 if (info->bucket[n] == NULL) {
131 printf("blaster %d malloc failed %d\n", info->n, n);
132 __builtin_trap();
133 }
134 memset(info->bucket[n], info->n * n, info->size[n]);
135 } else {
136 int sz = info->size[n];
137 uint8_t* x = info->bucket[n];
138 int val = n * info->n;
139 for (int i = 0; i < sz; i++) {
140 if (x[i] != val) {
141 printf("blaster %d bad bucket %d\n", info->n, n);
142 __builtin_trap();
143 }
144 }
145 if (rnum(1000) < 750) {
146 LOCK();
147 free(info->bucket[n]);
148 UNLOCK();
149 goto allocnew;
150 } else {
151 memset(x, val, sz);
152 }
153 }
154 }
155
156 return NULL;
157 }
158
heapblaster(int count,int locking)159 int heapblaster(int count, int locking) {
160 info_t info[THREADS] = {};
161 if (count < 1)
162 count = 1;
163 if (count >= THREADS)
164 count = THREADS;
165 printf("heapblaster: starting %d threads... (%s)\n",
166 count, locking ? "locking" : "not locking");
167 for (int n = 0; n < count; n++) {
168 info[n].lock = locking;
169 info[n].n = n;
170 if (count == 1) {
171 blaster(info + n);
172 return 0;
173 } else {
174 pthread_create(&info[n].t, NULL, blaster, info + n);
175 }
176 }
177 for (;;)
178 sleep(1000);
179 return 0;
180 }
181
182 static uint8_t data[65534];
183
writespam(int opt)184 int writespam(int opt) {
185 zx_handle_t p[2];
186 zx_status_t r;
187 uint64_t count = 0;
188
189 if ((r = zx_channel_create(0, p, p + 1)) < 0) {
190 printf("cleanup-test: channel create 0 failed: %d\n", r);
191 return -1;
192 }
193
194 printf("evil-tests: about to spam data into a channel\n");
195 for (;;) {
196 count++;
197 if ((r = zx_channel_write(p[0], 0, data, sizeof(data), NULL, 0)) < 0) {
198 printf("evil-tests: SUCCESS, writespammer error %d after only %" PRIu64 " writes\n", r, count);
199 return 0;
200 }
201 if ((count % 1000) == 0) {
202 printf("evil-tests: wrote %" PRIu64 " messages (%" PRIu64 " bytes).\n", count, count * sizeof(data));
203 }
204 }
205 if (opt == 0) {
206 printf("evil-tests: closing the channel (full of messages)\n");
207 zx_handle_close(p[0]);
208 zx_handle_close(p[1]);
209 } else {
210 printf("evil-tests: leaving the channel open (full of messages)\n");
211 }
212 return 0;
213 }
214
handlespam(void)215 int handlespam(void) {
216 zx_handle_t p[2];
217 uint64_t count = 0;
218
219 printf("evil-tests: about to create all the handles\n");
220 for (;;) {
221 zx_status_t status;
222 if ((status = zx_channel_create(0, p, p + 1)) < 0) {
223 printf("evil-tests: SUCCESS, channel create failed %d after %" PRIu64 " created\n", status, count);
224 return 0;
225 }
226 count++;
227 if ((count % 1000) == 0) {
228 printf("evil-tests: created %" PRIu64 " channels\n", count);
229 }
230 }
231 return 0;
232 }
233
nanospam(void)234 int nanospam(void) {
235 for (;;) {
236 zx_nanosleep(1);
237 }
238 }
239
main(int argc,char ** argv)240 int main(int argc, char** argv) {
241 if (argc < 2) {
242 printf(
243 "usage: evil-tests spam1 spam writes into channel\n"
244 " evil-tests spam2 spam writes, don't close channel after\n"
245 " evil-tests spam3 spam handle creation\n"
246 " evil-tests nano spam nanosleep\n"
247 " evil-tests heap1 <n> heap stress test, locking\n"
248 " evil-tests heap2 <n> heap stress test, no locking\n");
249 return -1;
250 } else if (!strcmp(argv[1], "spam1")) {
251 return writespam(0);
252 } else if (!strcmp(argv[1], "spam2")) {
253 return writespam(1);
254 } else if (!strcmp(argv[1], "spam3")) {
255 return handlespam();
256 } else if (!strcmp(argv[1], "nano")) {
257 return nanospam();
258 } else if (!strcmp(argv[1], "heap1")) {
259 int n = (argc > 2) ? strtoul(argv[2], 0, 10) : THREADS;
260 return heapblaster(n, 1);
261 } else if (!strcmp(argv[1], "heap2")) {
262 int n = (argc > 2) ? strtoul(argv[2], 0, 10) : THREADS;
263 return heapblaster(n, 0);
264 } else {
265 printf("unknown sub-command '%s'\n", argv[1]);
266 return -1;
267 }
268 return 0;
269 }
270