Exemple #1
0
void close_connection(struct connection *c)
{
  if(c->read == 0 && c->keepalive) {
    /* server has legitiamately shut down an idle keep alive request */
    good--;  /* connection never happend */
  } 
  else {
    if(good==1) {
      /* first time here */
      doclen = c->bread;
    } else if (c->bread!=doclen) { 
      bad++; 
      err_length++; 
    }
    
    /* save out time */
    if(done < requests) {
      struct data s;
      gettimeofday(&c->done,0);
      s.read = c->read;
      s.ctime = timedif(c->connect, c->start);
      s.time = timedif(c->done, c->start);
      stats[done++] = s;
    }
  }

  close(c->fd);
  FD_CLR(c->fd, &readbits);
  FD_CLR(c->fd, &writebits);

  /* connect again */
  start_connect(c); 
  return;
}
Exemple #2
0
void output_results()
{
  int timetaken;
  
  gettimeofday(&endtime,0);
  timetaken = timedif(endtime, start);
  
  printf("\n---\n");
  printf("Server:                 %s\n", server_name);
  printf("Document Length:        %d\n", doclen);  
  printf("Concurency Level:       %d\n", concurrency);
  printf("Time taken for tests:   %d.%03d seconds\n", 
	 timetaken/1000, timetaken%1000);
  printf("Complete requests:      %d\n", done);
  printf("Failed requests:        %d\n", bad);
  if(bad) printf("   (Connect: %d, Length: %d, Exceptions: %d)\n",
		 err_conn, err_length, err_except);
  if(keepalive) printf("Keep-Alive requests:    %d\n", doneka);
  printf("Bytes transfered:       %d\n", totalread);
  printf("HTML transfered:        %d\n", totalbread);
  
  /* avoid divide by zero */
  if(timetaken) {
    printf("Requests per seconds:   %.2f\n", 1000*(float)(done)/timetaken);
    printf("Transfer rate:          %.2f kb/s\n", 
	   (float)(totalread)/timetaken);
  }

  {
    /* work out connection times */
    int i;
    int totalcon=0, total=0;
    int mincon=9999999, mintot=999999;
    int maxcon=0, maxtot=0;

    for(i=0; i<requests; i++) {
      struct data s = stats[i];
      mincon = min(mincon, s.ctime);
      mintot = min(mintot, s.time);
      maxcon = max(maxcon, s.ctime);
      maxtot = max(maxtot, s.time);
      totalcon += s.ctime;
      total += s.time;
    }
    printf("\nConnnection Times (ms)\n");
    printf("           min   avg   max\n");
    printf("Connect: %5d %5d %5d\n",mincon, totalcon/requests, maxcon );
    printf("Total:   %5d %5d %5d\n", mintot, total/requests, maxtot);
    printf("---\n\n");
  }

  exit(0);
}
Exemple #3
0
int test() 
{
  struct timeval timeout, now;
  fd_set sel_read, sel_except, sel_write;
  int i;
  
  {
    /* get server information */
    struct hostent *he;
    he = gethostbyname(machine);
    if (!he) err("gethostbyname");
    server.sin_family      = he->h_addrtype;
    server.sin_port        = htons(port);
    server.sin_addr.s_addr = ((unsigned long *)(he->h_addr_list[0]))[0];
  }

  con = malloc(concurrency*sizeof(struct connection));
  memset(con,0,concurrency*sizeof(struct connection));
  
  stats = malloc(requests * sizeof(struct data));

  FD_ZERO(&readbits);
  FD_ZERO(&writebits);

  /* setup request */
  sprintf(request,"GET %s HTTP/1.0\r\nUser-Agent: ZeusBench/1.0\r\n"
	  "%sHost: %s\r\nAccept: */*\r\n\r\n", file, 
	  keepalive?"Connection: Keep-Alive\r\n":"", machine );
    
  reqlen = strlen(request);

  /* ok - lets start */
  gettimeofday(&start,0);

  /* initialise lots of requests */
  for(i=0; i<concurrency; i++) start_connect(&con[i]);

  while(done<requests) {
    int n;
    /* setup bit arrays */
    memcpy(&sel_except, &readbits, sizeof(readbits));
    memcpy(&sel_read, &readbits, sizeof(readbits));
    memcpy(&sel_write, &writebits, sizeof(readbits));

    /* check for time limit expiry */
    gettimeofday(&now,0);
    if(tlimit && timedif(now,start) > (tlimit*1000)) {
      requests=done;   /* so stats are correct */
      output_results();
    }

    /* Timeout of 30 seconds. */
    timeout.tv_sec=30; timeout.tv_usec=0;
    n=select(256, &sel_read, &sel_write, &sel_except, &timeout);
    if(!n) {
      printf("\nServer timed out\n\n");
      exit(1);
    }
    if(n<1) err("select");

    for(i=0; i<concurrency; i++) {
      int s = con[i].fd;
      if(FD_ISSET(s, &sel_except)) {
	bad++; 
	err_except++;
	start_connect(&con[i]);
	continue;
      }
      if(FD_ISSET(s, &sel_read)) read_connection(&con[i]);
      if(FD_ISSET(s, &sel_write)) write_request(&con[i]);
    }
    if(done>=requests) output_results();
  }
  return 0;
}
Exemple #4
0
void read_connection(struct connection *c)
{
  int r;
  
  r=read(c->fd,buffer,sizeof(buffer));
  if(r==0 || (r<0 && errno!=EAGAIN)) {
    good++;
    close_connection(c);
    return;
  } 
  
  if(r<0 && errno==EAGAIN) return;

  c->read += r;
  totalread += r;
  
  if(!c->gotheader) {
    char *s;
    int l=4;
    int space = CBUFFSIZE - c->cbx - 1; /* -1 to allow for 0 terminator */
    int tocopy = (space<r)?space:r;
    memcpy(c->cbuff+c->cbx, buffer, space);
    c->cbx += tocopy; space -= tocopy;
    c->cbuff[c->cbx] = 0; /* terminate for benefit of strstr */
    s = strstr(c->cbuff, "\r\n\r\n");
    /* this next line is so that we talk to NCSA 1.5 which blatantly breaks 
       the http specifaction */
    if(!s) { s = strstr(c->cbuff,"\n\n"); l=2; }

    if(!s) {
       /* read rest next time */
      if(space) 
	return;
      else {
	/* header is in invalid or too big - close connection */
	close(c->fd);
	if(bad++>10) {
	  printf("\nTest aborted after 10 failures\n\n");
	  exit(1);
	} 
	FD_CLR(c->fd, &writebits);
	start_connect(c);
      }	
    }
    else {
      /* have full header */
      if(!good) {
	/* this is first time, extract some interesting info */
	char *p, *q;
	p = strstr(c->cbuff, "Server:");
	q = server_name;
	if(p) { p+=8; while(*p>32) *q++ = *p++; }
	*q = 0;
      }
	
      c->gotheader = 1;
      *s = 0; /* terminate at end of header */
      if(keepalive && 
	 (strstr(c->cbuff, "Keep-Alive") 
	  || strstr(c->cbuff, "keep-alive")))  /* for benefit of MSIIS */
	{
	char *cl;
	cl = strstr(c->cbuff, "Content-Length:");
	/* for cacky servers like NCSA which break the spec and send a 
	   lower case 'l' */
	if(!cl) cl = strstr(c->cbuff, "Content-length:");
	if(cl) {
	  c->keepalive=1;
	  c->length = atoi(cl+16);
	}
      }
      c->bread += c->cbx - (s+l-c->cbuff) + r-tocopy;
      totalbread += c->bread;
    }
  }  
  else {
    /* outside header, everything we have read is entity body */
    c->bread += r;
    totalbread += r;
  }

  if(c->keepalive && (c->bread >= c->length)) {
    /* finished a keep-alive connection */
    good++; doneka++;
    /* save out time */
    if(good==1) {
      /* first time here */
      doclen = c->bread;
    } else if(c->bread!=doclen) { bad++; err_length++; }
    if(done < requests) {
      struct data s;
      gettimeofday(&c->done,0);
      s.read = c->read;
      s.ctime = timedif(c->connect, c->start);
      s.time = timedif(c->done, c->start);
      stats[done++] = s;
    }
    c->keepalive = 0; c->length = 0; c->gotheader=0; c->cbx = 0;
    c->read = c->bread = 0;
    write_request(c);
    c->start = c->connect; /* zero connect time with keep-alive */
  }
}
Exemple #5
0
static void
test(struct global * registry) {
    struct timeval timeout, now;
    fd_set sel_read, sel_except, sel_write;
    int i;

    registry->con = calloc(registry->concurrency, sizeof(struct connection));
    memset(registry->con, 0, registry->concurrency * sizeof(struct connection));

#ifdef AB_DEBUG
    printf("AB_DEBUG: start of test()\n");
#endif

    for (i = 0; i < registry->concurrency; i++) {
        registry->con[i].url = registry->ready_to_run_queue[i].url;
        registry->con[i].run = registry->ready_to_run_queue[i].run;
        registry->con[i].state = STATE_READY;
        registry->con[i].thread = registry->ready_to_run_queue[i].thread;
    }

#ifdef AB_DEBUG
    printf("AB_DEBUG: test() - stage 1\n");
#endif

    registry->stats = calloc(registry->number_of_urls, sizeof(struct data *));
    for (i = 0; i < registry->number_of_runs; i++) {
        int j;
        for (j = registry->position[i]; j < registry->position[i+1]; j++)
            registry->stats[j] = calloc(registry->repeats[i], sizeof(struct data));
    }

#ifdef AB_DEBUG
    printf("AB_DEBUG: test() - stage 2\n");
#endif

    FD_ZERO(&registry->readbits);
    FD_ZERO(&registry->writebits);

#ifdef AB_DEBUG
    printf("AB_DEBUG: test() - stage 3\n");
#endif

    /* ok - lets start */
    gettimeofday(&registry->starttime, 0);

#ifdef AB_DEBUG
    printf("AB_DEBUG: test() - stage 4\n");
#endif

    /* initialise lots of requests */

    registry->head = registry->concurrency;
    for (i = 0; i < registry->concurrency; i++)
        start_connect(registry, &registry->con[i]);

#ifdef AB_DEBUG
    printf("AB_DEBUG: test() - stage 5\n");
#endif

    while (registry->done < registry->need_to_be_done) {
        int n;

#ifdef AB_DEBUG
        printf("AB_DEBUG: test() - stage 5.1, registry->done = %d\n", registry->done);
#endif

        /* setup bit arrays */
        memcpy(&sel_except, &registry->readbits, sizeof(registry->readbits));
        memcpy(&sel_read, &registry->readbits, sizeof(registry->readbits));
        memcpy(&sel_write, &registry->writebits, sizeof(registry->writebits));

#ifdef AB_DEBUG
        printf("AB_DEBUG: test() - stage 5.2, registry->done = %d\n", registry->done);
#endif

        /* Timeout of 30 seconds, or minimum time limit specified by config. */
        timeout.tv_sec = registry->min_tlimit.tv_sec;
        timeout.tv_usec = registry->min_tlimit.tv_usec;
        n = select(FD_SETSIZE, &sel_read, &sel_write, &sel_except, &timeout);
#ifdef AB_DEBUG
        printf("AB_DEBUG: test() - stage 5.3, registry->done = %d\n", registry->done);
#endif
        if (!n)
            myerr(registry->warn_and_error, "Server timed out");
        if (n < 1)
            myerr(registry->warn_and_error, "Select error.");
#ifdef AB_DEBUG
        printf("AB_DEBUG: test() - stage 5.4, registry->done = %d\n", registry->done);
#endif
        /* check for time limit expiry */
        gettimeofday(&now, 0);
        if (registry->tlimit &&
            timedif(now, registry->starttime) > (registry->tlimit * 1000)) {
            char *warn = malloc(256 * sizeof(char));
            sprintf(warn, "Global time limit reached (%.2f sec), premature exit", registry->tlimit);
            myerr(registry->warn_and_error, warn);
            free(warn);
            registry->need_to_be_done = registry->done;        /* break out of loop */
        }

        for (i = 0; i < registry->concurrency; i++) {
            int s = registry->con[i].fd;
#ifdef AB_DEBUG
            printf("AB_DEBUG: test() - stage 5.5, registry->done = %d, i = %d\n", registry->done, i);
#endif
            if (registry->started[registry->con[i].url]
                > registry->finished[registry->con[i].url]) {
                struct connection * c = &registry->con[i];
                struct timeval url_now;

                /* check for per-url time limit expiry */
                gettimeofday(&url_now, 0);

#ifdef AB_DEBUG
                printf("AB_DEBUG: test() - stage 5.5.4, Time taken for current request = %d ms; Per-url time limit = %.4f sec; for run %d, url %d\n", timedif(url_now, c->start_time), registry->url_tlimit[c->url], c->run, c->url - registry->position[c->run]);
                printf("AB_DEBUG: test() - stage 5.5.5, registry->done = %d, i = %d\n", registry->done, i);
#endif
                if (registry->url_tlimit[c->url] &&
                    timedif(url_now, c->start_time) > (registry->url_tlimit[c->url] * 1000)) {
                    char *warn = malloc(256 * sizeof(char));
#ifdef AB_DEBUG
                    printf("AB_DEBUG: test() - stage 5.5.5.3, registry->done = %d, i = %d\n", registry->done, i);
#endif
                    sprintf(warn, "Per-url time limit reached (%.3f sec) for run %d, url %d, iteration %d; connection closed prematurely", registry->url_tlimit[c->url], c->run, c->url - registry->position[c->run], c->thread);
                    myerr(registry->warn_and_error, warn);
                    free(warn);

                    registry->failed[c->url]++;
                    close_connection(registry, c);
                    continue;
                }
            }

            if (registry->con[i].state == STATE_DONE)
                continue;
#ifdef AB_DEBUG
            printf("AB_DEBUG: test() - stage 5.6, registry->done = %d, i = %d\n", registry->done, i);
#endif
            if (FD_ISSET(s, &sel_except)) {
                registry->failed[registry->con[i].url]++;
                start_connect(registry, &registry->con[i]);
                continue;
            }
#ifdef AB_DEBUG
            printf("AB_DEBUG: test() - stage 5.7, registry->done = %d, i = %d\n", registry->done, i);
#endif
            if (FD_ISSET(s, &sel_read)) {
                read_connection(registry, &registry->con[i]);
                continue;
            }
#ifdef AB_DEBUG
            printf("AB_DEBUG: test() - stage 5.8, registry->done = %d, i = %d\n", registry->done, i);
#endif
            if (FD_ISSET(s, &sel_write))
                write_request(registry, &registry->con[i]);        
#ifdef AB_DEBUG
            printf("AB_DEBUG: test() - stage 5.9, registry->done = %d, i = %d\n", registry->done, i);
#endif
        }
    }

#ifdef AB_DEBUG
    printf("AB_DEBUG: test() - stage 6\n");
#endif

    gettimeofday(&registry->endtime, 0);
    if (strlen(registry->warn_and_error) == 28)
        myerr(registry->warn_and_error, "None.\n");
    else myerr(registry->warn_and_error, "Done.\n");
}
Exemple #6
0
/* ------------------------------------------------------------------------ */
void time_span_out ()
{
	int i;										/* counter */
	struct data_hdr temp_hdr;	/* data header */
	struct time end_time;		/* block ending time */
	unsigned int offset;		/* local computations */
	double duration;

	struct type52 *save_channel_ptr = current_channel;
	struct twindow *twin;

/*  
 * if output is intended for a seed volume, update the blockette info 74
 * only...
 *
 */
	if (Seed_flag)
	{
		update_type74(data_hdr);
	
		data_hdr->nsamples = 0;

		return;
	}
/*                +=======================================+                */
/*================|          Look for Time Spans          |================*/
/*                +=======================================+                */
	if (read_summary_flag)
	{
		char start[23];
		char end[23];
		struct time endtime;
		struct time starttime;

		duration = 
		   ((double)(data_hdr->nsamples)*TIME_PRECISION)/data_hdr->sample_rate;

		endtime = timeadd_double(data_hdr->time, duration);

		
#if 0

		sprintf(start, "%d,%d,%d:%d:%d.%d", 
					data_hdr->time.year,
					data_hdr->time.day,
					data_hdr->time.hour,
					data_hdr->time.minute,
					data_hdr->time.second,
					data_hdr->time.fracsec);

                sprintf(end, "%d,%d,%d:%d:%d.%d", 
                                        endtime.year,
                                        endtime.day, 
                                        endtime.hour, 
                                        endtime.minute,
                                        endtime.second, 
                                        endtime.fracsec); 

		if (input_file_type == DISK_DEVICE)
			/* if from disk drive, already scanned the
			 * block 74s for matching data, just use
			 * what was scanned previously
			 */
			twin = get_twin_from_summary();

		else
			
			/* tape drive seed volume, we must scan the
			 * summary file for "clipping"
			 */
			twin = scan_summary_file(data_hdr->station,
					  	 data_hdr->channel,
					  	 data_hdr->network,
						 data_hdr->location,
						 data_hdr->time,
						 endtime);
					  

		if (twin == NULL)
		{
			fprintf(stderr, "Error - time_span_out(): Unable to locate timespan in the summary file!\nStation: %s;Channel: %s\n for timespan %s to %s \n",
					data_hdr->station,
					data_hdr->channel,
					start, end);

			fprintf(stderr, "Ignoring summary file for this timespan.\n");

			output_data(data_hdr, 0);

			data_hdr->nsamples = 0;

			return;

		}

		timecvt(&starttime, twin->time_start);
		timecvt(&endtime, twin->time_end);
        
		start_time_point = &starttime;
		end_time_point = &endtime;

 
		start_time_count = end_time_count = 1;

#endif

		set_event(&(data_hdr->time), &endtime);
 
	}

	
	/* if user didn't enter any time spans, output all data */
	if (start_time_count == 0)
	{
		output_data(data_hdr, 0);
		data_hdr->nsamples = 0;

		return;
	}
	/* look through time span list as entered by the user */
	for (i=0;i<start_time_count;i++)
	{
		current_channel = save_channel_ptr;

		/* copy current data header */
		temp_hdr = *data_hdr;

		/* is span start equal to start of data? */
		if (timecmp (start_time_point[i],data_hdr->time) == 0)
		{
			/* is there a corresponding end time? */
			if (i < end_time_count)
			{
				/* use current data start time */
				/* calc number of samples */

				duration = timedif(temp_hdr.time,end_time_point[i]);

				/* took this out of timedif */
				if (duration < 0)
					duration = 0;

				temp_hdr.nsamples = (int) (((duration*temp_hdr.sample_rate)/TIME_PRECISION)+.999);

				/* make sure it's not more than available data */
				if (temp_hdr.nsamples > data_hdr->nsamples) temp_hdr.nsamples = data_hdr->nsamples;

				output_data(&temp_hdr, 0);

			}
			else 
			{
				output_data(data_hdr, 0);
			}
		}
		/* is span start less than start of data? */
		else if (timecmp (start_time_point[i],data_hdr->time) < 0)
		{
			/* is there a corresponding end time? */
			if (i < end_time_count)
			{
				if (timecmp (end_time_point[i],data_hdr->time) > 0)
				{
					/* use current data start time */
					/* calc number of samples */

					duration = timedif(temp_hdr.time,end_time_point[i]);

                                	/* took this out of timedif */
                                	if (duration < 0)
                                        	duration = 0;

					temp_hdr.nsamples = (int) (((duration*temp_hdr.sample_rate)/TIME_PRECISION)+.999);

					/* make sure it's not more than available data */
					if (end_time_point[i].year == 9999) temp_hdr.nsamples = data_hdr->nsamples;

					if (temp_hdr.nsamples > data_hdr->nsamples) temp_hdr.nsamples = data_hdr->nsamples;
					output_data(&temp_hdr, 0);
				}
			}
			else
			{
				output_data(data_hdr, 0);
			}
	
		}
		/* span start must be greater than start of data */
		else
		{
			/* figure out data ending time */
			if (data_hdr->sample_rate != 0)
				duration = (((double)data_hdr->nsamples * TIME_PRECISION) / data_hdr->sample_rate);

			else 
				duration = 0;

			end_time = timeadd_double (data_hdr->time, duration);

			/* is span start before end of data? */
			if (timecmp(start_time_point[i],end_time) < 0)
			{
				/* calc new start time and sample offset*/
				duration = timedif(temp_hdr.time,start_time_point[i]);

				/* took this out of timedif */
				if (duration < 0)
					duration = 0;

				offset = (int) ((duration * data_hdr->sample_rate)/TIME_PRECISION);

				/* changed to float to avoid overflow - CL */
				duration = 
					((float) offset * TIME_PRECISION) / 
						data_hdr->sample_rate;

				temp_hdr.time = timeadd_double (data_hdr->time, duration);
				/* calc number of samples */
				if (i < end_time_count)
				{
					if (end_time_point[i].year != 9999)
						duration = timedif(temp_hdr.time,end_time_point[i]);
					else
						duration = timedif(temp_hdr.time,end_time);

				}
				else 
					duration = timedif(temp_hdr.time,end_time);

				/* took this out of timedif */
				if (duration < 0)
					duration = 0;

				temp_hdr.nsamples = (int)(((duration*temp_hdr.sample_rate)/TIME_PRECISION)+.999);

				/* make sure it's not more than available data */
				if ((temp_hdr.nsamples+offset) > data_hdr->nsamples)
					temp_hdr.nsamples = data_hdr->nsamples - offset;

				output_data(&temp_hdr, offset);

			}
		}
	}

	data_hdr->nsamples = 0;

	if (read_summary_flag)
		/* will avoid unnecessary free()s as these weren't malloced */
                start_time_count = end_time_count = 0;
		
	
/*                +=======================================+                */
/*================|                clean-up               |================*/
/*                +=======================================+                */

}
Exemple #7
0
void scheduler(memcached_server_st *servers, conclusions_st *conclusion)
{
  unsigned int x;
  unsigned int actual_loaded= 0; /* Fix warning */
  memcached_st *memc;

  struct timeval start_time, end_time;
  pthread_t mainthread;            /* Thread descriptor */
  pthread_attr_t attr;          /* Thread attributes */
  pairs_st *pairs= NULL;

  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr,
                              PTHREAD_CREATE_DETACHED);

  memc= memcached_create(NULL);
  memcached_server_push(memc, servers);

  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, opt_binary);
  
  if (opt_flush)
    flush_all(memc);
  if (opt_createial_load)
    pairs= load_create_data(memc, opt_createial_load, &actual_loaded);

  /* We set this after we have loaded */
  {
    if (opt_non_blocking_io)
      memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, 1);
    if (opt_tcp_nodelay)
      memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, 1);
  }


  pthread_mutex_lock(&counter_mutex);
  thread_counter= 0;

  pthread_mutex_lock(&sleeper_mutex);
  master_wakeup= 1;
  pthread_mutex_unlock(&sleeper_mutex);

  for (x= 0; x < opt_concurrency; x++)
  {
    thread_context_st *context;
    context= (thread_context_st *)malloc(sizeof(thread_context_st));
    memset(context, 0, sizeof(thread_context_st));

    context->memc= memcached_clone(NULL, memc);
    context->test= opt_test;

    context->initial_pairs= pairs;
    context->initial_number= actual_loaded;

    if (opt_test == SET_TEST)
    {
      context->execute_pairs= pairs_generate(opt_execute_number, 400);
      context->execute_number= opt_execute_number;
    }

    /* now you create the thread */
    if (pthread_create(&mainthread, &attr, run_task,
                       (void *)context) != 0)
    {
      fprintf(stderr,"Could not create thread\n");
      exit(1);
    }
    thread_counter++;
  }

  pthread_mutex_unlock(&counter_mutex);
  pthread_attr_destroy(&attr);

  pthread_mutex_lock(&sleeper_mutex);
  master_wakeup= 0;
  pthread_mutex_unlock(&sleeper_mutex);
  pthread_cond_broadcast(&sleep_threshhold);

  gettimeofday(&start_time, NULL);
  /*
    We loop until we know that all children have cleaned up.
  */
  pthread_mutex_lock(&counter_mutex);
  while (thread_counter)
  {
    struct timespec abstime;

    memset(&abstime, 0, sizeof(struct timespec));
    abstime.tv_sec= 1;

    pthread_cond_timedwait(&count_threshhold, &counter_mutex, &abstime);
  }
  pthread_mutex_unlock(&counter_mutex);

  gettimeofday(&end_time, NULL);

  conclusion->load_time= timedif(end_time, start_time);
  conclusion->read_time= timedif(end_time, start_time);
  pairs_free(pairs);
  memcached_free(memc);
}
Exemple #8
0
int main(int argc, char *argv[])
{
  test_return_t return_code;
  unsigned int x;
  char *collection_to_run= NULL;
  char *wildcard= NULL;
  world_st world;
  collection_st *collection;
  collection_st *next;
  void *world_ptr;

  world_stats_st stats;

#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
  if (sasl_client_init(NULL) != SASL_OK)
  {
     fprintf(stderr, "Failed to initialize sasl library!\n");
     return 1;
  }
#endif

  memset(&stats, 0, sizeof(stats));
  memset(&world, 0, sizeof(world));
  get_world(&world);

  collection= init_world(&world);

  if (world.create)
  {
    test_return_t error;
    world_ptr= world.create(&error);
    if (error != TEST_SUCCESS)
      exit(1);
  }
  else
  {
    world_ptr= NULL;
  }

  if (argc > 1)
    collection_to_run= argv[1];

  if (argc == 3)
    wildcard= argv[2];

  for (next= collection; next->name; next++)
  {
    test_return_t collection_rc= TEST_SUCCESS;
    test_st *run;
    bool failed= false;
    bool skipped= false;

    run= next->tests;
    if (collection_to_run && fnmatch(collection_to_run, next->name, 0))
      continue;

    stats.collection_total++;

    collection_rc= world.collection.startup(world_ptr);

    if (collection_rc != TEST_SUCCESS)
      goto skip_pre;

    if (next->pre)
    {
      collection_rc= world.runner->pre(next->pre, world_ptr);
    }

skip_pre:
    switch (collection_rc)
    {
      case TEST_SUCCESS:
        fprintf(stderr, "\n%s\n\n", next->name);
        break;
      case TEST_FAILURE:
        fprintf(stderr, "\n%s [ failed ]\n\n", next->name);
        stats.collection_failed++;
        goto cleanup;
      case TEST_SKIPPED:
        fprintf(stderr, "\n%s [ skipping ]\n\n", next->name);
        stats.collection_skipped++;
        goto cleanup;
      case TEST_MEMORY_ALLOCATION_FAILURE:
      case TEST_MAXIMUM_RETURN:
      default:
        assert(0);
        break;
    }


    for (x= 0; run->name; run++)
    {
      struct timeval start_time, end_time;
      long int load_time= 0;

      if (wildcard && fnmatch(wildcard, run->name, 0))
        continue;

      fprintf(stderr, "Testing %s", run->name);

      if (world.test.startup)
      {
        world.test.startup(world_ptr);
      }

      if (run->requires_flush && world.test.flush)
      {
        world.test.flush(world_ptr);
      }

      if (world.test.pre_run)
      {
        world.test.pre_run(world_ptr);
      }


      // Runner code
      {
#if 0
        if (next->pre && world.runner->pre)
        {
          return_code= world.runner->pre(next->pre, world_ptr);

          if (return_code != TEST_SUCCESS)
          {
            goto error;
          }
        }
#endif

        gettimeofday(&start_time, NULL);
        return_code= world.runner->run(run->test_fn, world_ptr);
        gettimeofday(&end_time, NULL);
        load_time= timedif(end_time, start_time);

#if 0
        if (next->post && world.runner->post)
        {
          (void) world.runner->post(next->post, world_ptr);
        }
#endif
      }

      if (world.test.post_run)
      {
        world.test.post_run(world_ptr);
      }

      stats.total++;

      fprintf(stderr, "\t\t\t\t\t");

      switch (return_code)
      {
      case TEST_SUCCESS:
        fprintf(stderr, "%ld.%03ld ", load_time / 1000, load_time % 1000);
        stats.success++;
        break;
      case TEST_FAILURE:
        stats.failed++;
        failed= true;
        break;
      case TEST_SKIPPED:
        stats.skipped++;
        skipped= true;
        break;
      case TEST_MEMORY_ALLOCATION_FAILURE:
        fprintf(stderr, "Exhausted memory, quitting\n");
        abort();
      case TEST_MAXIMUM_RETURN:
      default:
        assert(0); // Coding error.
        break;
      }

      fprintf(stderr, "[ %s ]\n", test_strerror(return_code));

      if (world.test.on_error)
      {
        test_return_t rc;
        rc= world.test.on_error(return_code, world_ptr);

        if (rc != TEST_SUCCESS)
          break;
      }
    }

    if (next->post && world.runner->post)
    {
      (void) world.runner->post(next->post, world_ptr);
    }

    if (! failed && ! skipped)
    {
      stats.collection_success++;
    }
cleanup:

    world.collection.shutdown(world_ptr);
  }

  if (stats.collection_failed || stats.collection_skipped)
  {
    fprintf(stderr, "Some test failures and/or skipped test occurred.\n\n");
  }
  else
  {
    fprintf(stderr, "All tests completed successfully\n\n");
  }

  if (world.destroy)
  {
    test_return_t error;
    error= world.destroy(world_ptr);

    if (error != TEST_SUCCESS)
    {
      fprintf(stderr, "Failure during shutdown.\n");
      stats.failed++; // We do this to make our exit code return 1
    }
  }

  world_stats_print(&stats);

#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
  sasl_done();
#endif

  return stats.failed == 0 ? 0 : 1;
}