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 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 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 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 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 130 static void walk_graph(void *graph) 131 { 132 void **curr = graph; 133 134 while (1) { 135 curr = *curr; 136 speed++; 137 } 138 } 139 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