Home | History | Annotate | Download | only in ramsnake
      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