1
2 /*
3 * Cheesy program to create a "graph" of nodes, spawn threads and
4 * walk the graph.
5 */
6
7 /*
8 * Copyright (C) 2003-2006 IBM
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of the
13 * License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23 * 02111-1307, USA.
24 */
25 #define _GNU_SOURCE
26 #include <sys/mman.h>
27 #include <malloc.h>
28 #include <sys/sysinfo.h>
29 #include <limits.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <signal.h>
36 #include <sys/time.h>
37 #include <strings.h>
38 #include <time.h>
39 //#define __USE_GNU
40 #include <sched.h>
41
42 static int seed_random(void);
43 static void populate_graph(void *region, unsigned long long node_count);
44 static void alarm_func(int signum);
45 static void print_help(const char *name);
46
47 static struct timeval last;
48 volatile unsigned long speed = 0;
49 static unsigned long report_interval = 30;
50
51 /*
52 * A quick note: each graph "node" consists of some pointer off to another
53 * part of the graph array.
54 */
55
print_help(const char * name)56 static void print_help(const char *name)
57 {
58 printf
59 ("Usage: %s [-p num_threads] [-d ram_divisor | -n num_nodes] [-s report_intrvl] [-a add_intrvl] [-t]\n",
60 name);
61 printf
62 ("-d ram_divisor: Use (total_ram / ram_divisor) as a graph (16).\n");
63 printf("-p num_threads: Start up some number of threads (1).\n");
64 printf("-n num_nodes: Create a graph with some number of nodes.\n");
65 printf("-s report_intvl Seconds between speed reports (30).\n");
66 printf("-a add_intrvl: Seconds between adding children (never).\n");
67 #ifdef __cpu_set_t_defined
68 printf
69 ("-t: Assign each process to its own processor (no).\n");
70 #else
71 printf("-t: Not enabled because you need kernel 2.5.8+.\n");
72 #endif
73 }
74
populate_graph(void * graph,unsigned long long node_count)75 static void populate_graph(void *graph, unsigned long long node_count)
76 {
77 unsigned long i;
78 void **ptr;
79 unsigned long gunk;
80
81 seed_random();
82
83 /* Each cell of the array points to another place in the array. */
84 for (i = 0, ptr = graph; i < node_count; i++, ptr++) {
85 gunk = (node_count - 1) * (rand() / (RAND_MAX + 1.0));
86 *ptr = (void *)(graph + (gunk * sizeof(void *)));
87 }
88 }
89
seed_random(void)90 static int seed_random(void)
91 {
92 int fp;
93 long seed;
94
95 fp = open("/dev/urandom", O_RDONLY);
96 if (fp < 0) {
97 perror("/dev/urandom");
98 return 0;
99 }
100
101 if (read(fp, &seed, sizeof(seed)) != sizeof(seed)) {
102 perror("read random seed");
103 return 0;
104 }
105
106 close(fp);
107 srand(seed);
108
109 return 1;
110 }
111
alarm_func(int signum)112 static void alarm_func(int signum)
113 {
114 struct timeval now;
115 float time;
116
117 gettimeofday(&now, NULL);
118 time = (now.tv_usec + (now.tv_sec * 1000000))
119 - (last.tv_usec + (last.tv_sec * 1000000));
120 time /= 1000000;
121
122 printf("%d: %.0f nodes/sec.\n", getpid(), speed / time);
123 fflush(stdout);
124 speed = 0;
125 last = now;
126
127 alarm(report_interval);
128 }
129
walk_graph(void * graph)130 static void walk_graph(void *graph)
131 {
132 void **curr = graph;
133
134 while (1) {
135 curr = *curr;
136 speed++;
137 }
138 }
139
main(int argc,char * argv[])140 int main(int argc, char *argv[])
141 {
142 unsigned long long num_nodes, ram_size;
143 unsigned long num_forks = 1;
144 struct sysinfo info;
145 void *shm;
146 int *cond;
147 struct sigaction zig;
148 int c, add_wait = -1, is_parent = 1;
149 #ifdef __cpu_set_t_defined
150 int affinity = 0;
151 cpu_set_t my_cpu_mask;
152 #endif
153
154 /* By default we'll use 1/16th of total RAM, rounded
155 * down to the nearest page. */
156 if (sysinfo(&info) != 0) {
157 perror("sysinfo");
158 return 1;
159 }
160
161 ram_size = info.totalram / 16;
162 ram_size = ram_size & ~(getpagesize() - 1);
163 num_nodes = ram_size / sizeof(void *);
164
165 /* Parse command line args */
166 while ((c = getopt(argc, argv, "a:p:n:d:s:t")) != -1) {
167 switch (c) {
168 case 'p':
169 num_forks = atoi(optarg);
170 break;
171 case 'd':
172 ram_size = info.totalram / atoi(optarg);
173 ram_size = ram_size & ~(getpagesize() - 1);
174 num_nodes = ram_size / sizeof(void *);
175 break;
176 case 'n':
177 num_nodes = atoi(optarg);
178 ram_size = num_nodes * sizeof(void *);
179 break;
180 case 's':
181 report_interval = atoi(optarg);
182 break;
183 case 'a':
184 add_wait = atoi(optarg);
185 break;
186 #ifdef __cpu_set_t_defined
187 case 't':
188 affinity = 1;
189 break;
190 #endif
191 default:
192 print_help(argv[0]);
193 return 0;
194 }
195 }
196
197 /* Will we exceed half the address space size? Use 1/4 of it at most. */
198 if (ram_size > ((unsigned long long)1 << ((sizeof(void *) * 8) - 1))) {
199 printf
200 ("Was going to use %lluKB (%llu nodes) but that's too big.\n",
201 ram_size / 1024, num_nodes);
202 ram_size = ((unsigned long long)1 << (sizeof(void *) * 8));
203 ram_size /= 4;
204 num_nodes = ram_size / sizeof(void *);
205 printf("Clamping to %lluKB (%llu nodes) instead.\n",
206 ram_size / 1024, num_nodes);
207 }
208
209 /* Talk about what we're going to do. */
210 printf("Going to use %lluKB (%llu nodes).\n", ram_size / 1024,
211 num_nodes);
212
213 /* Make a shared anonymous map of the RAM */
214 shm = mmap(NULL, ram_size, PROT_READ | PROT_WRITE,
215 MAP_SHARED | MAP_ANONYMOUS, 0, 0);
216 if (shm == MAP_FAILED) {
217 perror("mmap");
218 return 2;
219 }
220 printf("mmap region: %p (%llu nodes)\n", shm, num_nodes);
221
222 /* Create an SHM condition variable. Bogus, I know... */
223 cond = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
224 MAP_SHARED | MAP_ANONYMOUS, 0, 0);
225 if (cond == MAP_FAILED) {
226 perror("mmap");
227 return 4;
228 }
229 *cond = 1;
230
231 /* Create a "graph" by populating it with random pointers. */
232 printf("Populating nodes...");
233 fflush(stdout);
234 populate_graph(shm, num_nodes);
235 printf("done.\n");
236
237 printf("Creating %lu processes with reports every %lu seconds \
238 and %d seconds between adding children.\n", num_forks, report_interval, add_wait);
239
240 /* Fork off separate processes. The shared region is shared
241 * across all children. If we only wanted one thread, we shouldn't
242 * fork anything. Note that the "cond" mmap is a really crappy
243 * condition variable kludge that works well enough for HERE ONLY. */
244 for (c = (add_wait >= 0 ? 0 : 1); c < num_forks; c++) {
245 /* Child should wait for the condition and then break. */
246 if (!fork()) {
247 #ifdef __cpu_set_t_defined
248 if (affinity) {
249 CPU_ZERO(&my_cpu_mask);
250 CPU_SET(c, &my_cpu_mask);
251 if (0 !=
252 sched_setaffinity(0, sizeof(cpu_set_t),
253 &my_cpu_mask)) {
254 perror("sched_setaffinity");
255 }
256 }
257 #endif
258
259 is_parent = 0;
260 while (*cond) {
261 usleep(10000);
262 }
263 break;
264 }
265 }
266 if (is_parent) {
267 #ifdef __cpu_set_t_defined
268 if (affinity) {
269 CPU_ZERO(&my_cpu_mask);
270 CPU_SET(0, &my_cpu_mask);
271 if (0 !=
272 sched_setaffinity(0, sizeof(cpu_set_t),
273 &my_cpu_mask)) {
274 perror("sched_setaffinity");
275 }
276 }
277 #endif
278 printf("All threads created. Launching!\n");
279 *cond = 0;
280 }
281
282 /* now start the work */
283 if (!is_parent) {
284 start_thread:
285 /* Set up the alarm handler to print speed info. */
286 memset(&zig, 0x00, sizeof(zig));
287 zig.sa_handler = alarm_func;
288 sigaction(SIGALRM, &zig, NULL);
289 gettimeofday(&last, NULL);
290 alarm(report_interval);
291
292 /* Walk the graph. */
293 walk_graph(shm);
294
295 /* This function never returns */
296 } else {
297 /* Start the ramp-up. The children will never die,
298 * so we don't need to wait() for 'em.
299 */
300 while (add_wait != -1) {
301 sleep(add_wait);
302 if (fork() == 0) {
303 /* goto is cheesy, but works. */
304 goto start_thread;
305 } else {
306 printf("Added thread.\n");
307 }
308 }
309 goto start_thread;
310 }
311
312 return 0;
313 }
314