blob: e651258aecc41e48484fd48a98f4b93750c93b5b [file] [log] [blame]
/*
* Cheesy program to create a "graph" of nodes, spawn threads and
* walk the graph.
*/
/*
* Copyright (C) 2003-2006 IBM
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#define _GNU_SOURCE
#include <sys/mman.h>
#include <malloc.h>
#include <sys/sysinfo.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <strings.h>
#include <time.h>
//#define __USE_GNU
#include <sched.h>
static int seed_random(void);
static void populate_graph(void *region, unsigned long long node_count);
static void alarm_func(int signum);
static void print_help(const char *name);
static struct timeval last;
volatile unsigned long speed = 0;
static unsigned long report_interval = 30;
/*
* A quick note: each graph "node" consists of some pointer off to another
* part of the graph array.
*/
static void print_help(const char *name) {
printf("Usage: %s [-p num_threads] [-d ram_divisor | -n num_nodes] [-s report_intrvl] [-a add_intrvl] [-t]\n",
name);
printf("-d ram_divisor: Use (total_ram / ram_divisor) as a graph (16).\n");
printf("-p num_threads: Start up some number of threads (1).\n");
printf("-n num_nodes: Create a graph with some number of nodes.\n");
printf("-s report_intvl Seconds between speed reports (30).\n");
printf("-a add_intrvl: Seconds between adding children (never).\n");
#ifdef __cpu_set_t_defined
printf("-t: Assign each process to its own processor (no).\n");
#else
printf("-t: Not enabled because you need kernel 2.5.8+.\n");
#endif
}
static void populate_graph(void *graph, unsigned long long node_count) {
unsigned long i;
void **ptr;
unsigned long gunk;
seed_random();
/* Each cell of the array points to another place in the array. */
for (i = 0, ptr = graph; i < node_count; i++, ptr++) {
gunk = (node_count - 1) * (rand() / (RAND_MAX + 1.0));
*ptr = (void *)(graph + (gunk * sizeof(void *)));
}
}
static int seed_random(void) {
int fp;
long seed;
fp = open("/dev/urandom", O_RDONLY);
if (fp < 0) {
perror("/dev/urandom");
return 0;
}
if (read(fp, &seed, sizeof(seed)) != sizeof(seed)) {
perror("read random seed");
return 0;
}
close(fp);
srand(seed);
return 1;
}
static void alarm_func(int signum) {
struct timeval now;
float time;
gettimeofday(&now, NULL);
time = (now.tv_usec + (now.tv_sec * 1000000))
- (last.tv_usec + (last.tv_sec * 1000000));
time /= 1000000;
printf("%d: %.0f nodes/sec.\n", getpid(), speed / time);
fflush(stdout);
speed = 0;
last = now;
alarm(report_interval);
}
static void walk_graph(void *graph) {
void **curr = graph;
while (1) {
curr = *curr;
speed++;
}
}
int main(int argc, char *argv[]) {
unsigned long long num_nodes, ram_size;
unsigned long num_forks = 1;
struct sysinfo info;
void *shm;
int *cond;
struct sigaction zig;
int c, add_wait = -1, is_parent = 1;
#ifdef __cpu_set_t_defined
int affinity = 0;
cpu_set_t my_cpu_mask;
#endif
/* By default we'll use 1/16th of total RAM, rounded
* down to the nearest page. */
if (sysinfo(&info) != 0) {
perror("sysinfo");
return 1;
}
ram_size = info.totalram / 16;
ram_size = ram_size & ~(getpagesize() - 1);
num_nodes = ram_size / sizeof(void *);
/* Parse command line args */
while ((c = getopt(argc, argv, "a:p:n:d:s:t")) != -1) {
switch (c) {
case 'p':
num_forks = atoi(optarg);
break;
case 'd':
ram_size = info.totalram / atoi(optarg);
ram_size = ram_size & ~(getpagesize() - 1);
num_nodes = ram_size / sizeof(void *);
break;
case 'n':
num_nodes = atoi(optarg);
ram_size = num_nodes * sizeof(void *);
break;
case 's':
report_interval = atoi(optarg);
break;
case 'a':
add_wait = atoi(optarg);
break;
#ifdef __cpu_set_t_defined
case 't':
affinity = 1;
break;
#endif
default:
print_help(argv[0]);
return 0;
}
}
/* Will we exceed half the address space size? Use 1/4 of it at most. */
if (ram_size > ((unsigned long long)1 << ((sizeof(void *) * 8) - 1))) {
printf("Was going to use %lluKB (%llu nodes) but that's too big.\n",
ram_size / 1024, num_nodes);
ram_size = ((unsigned long long)1 << (sizeof(void *) * 8));
ram_size /= 4;
num_nodes = ram_size / sizeof(void *);
printf("Clamping to %lluKB (%llu nodes) instead.\n",
ram_size / 1024, num_nodes);
}
/* Talk about what we're going to do. */
printf("Going to use %lluKB (%llu nodes).\n", ram_size / 1024,
num_nodes);
/* Make a shared anonymous map of the RAM */
shm = mmap(NULL, ram_size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, 0, 0);
if (shm == MAP_FAILED) {
perror("mmap");
return 2;
}
printf("mmap region: %p (%llu nodes)\n", shm, num_nodes);
/* Create an SHM condition variable. Bogus, I know... */
cond = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, 0, 0);
if (cond == MAP_FAILED) {
perror("mmap");
return 4;
}
*cond = 1;
/* Create a "graph" by populating it with random pointers. */
printf("Populating nodes...");
fflush(stdout);
populate_graph(shm, num_nodes);
printf("done.\n");
printf("Creating %lu processes with reports every %lu seconds \
and %d seconds between adding children.\n",
num_forks, report_interval, add_wait);
/* Fork off separate processes. The shared region is shared
* across all children. If we only wanted one thread, we shouldn't
* fork anything. Note that the "cond" mmap is a really crappy
* condition variable kludge that works well enough for HERE ONLY. */
for (c = (add_wait >= 0 ? 0 : 1); c < num_forks; c++) {
/* Child should wait for the condition and then break. */
if (!fork()) {
#ifdef __cpu_set_t_defined
if (affinity) {
CPU_ZERO(&my_cpu_mask);
CPU_SET(c, &my_cpu_mask);
if (0 != sched_setaffinity(0,sizeof(cpu_set_t), &my_cpu_mask)) {
perror("sched_setaffinity");
}
}
#endif
is_parent = 0;
while (*cond) {
usleep(10000);
}
break;
}
}
if (is_parent) {
#ifdef __cpu_set_t_defined
if (affinity) {
CPU_ZERO(&my_cpu_mask);
CPU_SET(0, &my_cpu_mask);
if (0 != sched_setaffinity(0,sizeof(cpu_set_t), &my_cpu_mask)) {
perror("sched_setaffinity");
}
}
#endif
printf("All threads created. Launching!\n");
*cond = 0;
}
/* now start the work */
if (!is_parent) {
start_thread:
/* Set up the alarm handler to print speed info. */
memset(&zig, 0x00, sizeof(zig));
zig.sa_handler = alarm_func;
sigaction(SIGALRM, &zig, NULL);
gettimeofday(&last, NULL);
alarm(report_interval);
/* Walk the graph. */
walk_graph(shm);
/* This function never returns */
} else {
/* Start the ramp-up. The children will never die,
* so we don't need to wait() for 'em.
*/
while (add_wait != -1) {
sleep(add_wait);
if (fork() == 0) {
/* goto is cheesy, but works. */
goto start_thread;
} else {
printf("Added thread.\n");
}
}
goto start_thread;
}
return 0;
}