commit - ea4bdc8528c8b6c2824909734eb5e82c06464332
commit + cde79ded160470db64ff8e2874550691b7234fbb
blob - /dev/null
blob + b806473dfc5f68226768849179e9cee0860d3ec2 (mode 644)
--- /dev/null
+++ tests/tools/postmark-1_5.c
+/*
+Written by Jeffrey Katcher under contract to Network Appliance.
+Copyright (C) 1997-2001
+Network Appliance, Inc.
+
+This code has been successfully compiled and run by Network
+Appliance on various platforms, including Solaris 2 on an Ultra-170,
+and Windows NT on a Compaq ProLiant. However, this PostMark source
+code is distributed under the Artistic License appended to the end
+of this file. As such, no support is provided. However, please report
+any errors to the author, Jeffrey Katcher <katcher@netcom.com>, or to
+Andy Watson <watson@netapp.com>.
+
+Versions:
+1.00 - Original release - 8/17/97
+
+1.01 - Fixed endless loop on EOF,
+ Divide by zero when file_size_high=file_size_low - 10/29/97
+ (Thanks to Chuck Murnane)
+
+1.1 - Added new commands to distribute work across multiple directories
+ and/or file systems and multiple work subdirectories.
+
+ Changed set location command (+,-) to allow file systems & weights
+ Added set subdirectories command and code to distribute work across
+ multiple subdirectories
+ Added file redirect to show and run commands
+ Improved help system - 4/8/98
+
+1.11 - Fixed unfortunate problem where read_file opens in append mode thus
+ avoiding actual reads. (Thanks to Kent Peacock)
+
+1.12 - Changed bytes read and written to float. Hopefully this will avoid
+ overflow when very large file sizes are used.
+
+1.13 - Added terse report option allowing results to be easily included in
+ other things. (Thanks to Walter Wong)
+ Also tweaked help code to allow partial matches
+
+1.14 - Automatically stop run if work files are depleted
+
+1.5 - It was pointed out by many (most recently Michael Flaster) that the
+ pseudo-random number generator was more pseudo than random. After
+ a review of the literature and extensive benchmarking, I've replaced
+ the previous PRNG with the Mersenne Twister. While an excellent PRNG,
+ it retains much of the performance of the previous implementation.
+ URL: http://www.math.keio.ac.jp/~matumoto/emt.html
+ Also changed MB definition to 1024KB, tweaked show command
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <fcntl.h>
+
+#ifdef _WIN32
+#include <io.h>
+#include <direct.h>
+
+#define GETWD(x) getcwd(x,MAX_LINE)
+#define MKDIR(x) mkdir(x)
+#define SEPARATOR "\\"
+#else
+extern char *getwd();
+
+#define GETWD(x) getwd(x)
+#define MKDIR(x) mkdir(x,0700)
+#define SEPARATOR "/"
+#endif
+
+#define MAX_LINE 255
+#define MAX_FILENAME 80
+
+#define KILOBYTE 1024
+#define MEGABYTE (KILOBYTE*KILOBYTE)
+
+#define PROMPT "pm>"
+
+typedef struct { /* ADT for table of CLI commands */
+ char *name; /* name of command */
+ int (*func)(); /* pointer to callback function */
+ char *help; /* descriptive help string */
+} cmd;
+
+extern int cli_set_size();
+extern int cli_set_number();
+extern int cli_set_seed();
+extern int cli_set_transactions();
+extern int cli_set_location();
+extern int cli_set_subdirs();
+extern int cli_set_read();
+extern int cli_set_write();
+extern int cli_set_buffering();
+extern int cli_set_bias_read();
+extern int cli_set_bias_create();
+extern int cli_set_report();
+
+extern int cli_run();
+extern int cli_show();
+extern int cli_help();
+extern int cli_quit();
+
+cmd command_list[]={ /* table of CLI commands */
+ {"set size",cli_set_size,"Sets low and high bounds of files"},
+ {"set number",cli_set_number,"Sets number of simultaneous files"},
+ {"set seed",cli_set_seed,"Sets seed for random number generator"},
+ {"set transactions",cli_set_transactions,"Sets number of transactions"},
+ {"set location",cli_set_location,"Sets location of working files"},
+ {"set subdirectories",cli_set_subdirs,"Sets number of subdirectories"},
+ {"set read",cli_set_read,"Sets read block size"},
+ {"set write",cli_set_write,"Sets write block size"},
+ {"set buffering",cli_set_buffering,"Sets usage of buffered I/O"},
+ {"set bias read",cli_set_bias_read,
+ "Sets the chance of choosing read over append"},
+ {"set bias create",cli_set_bias_create,
+ "Sets the chance of choosing create over delete"},
+ {"set report",cli_set_report,"Choose verbose or terse report format"},
+ {"run",cli_run,"Runs one iteration of benchmark"},
+ {"show",cli_show,"Displays current configuration"},
+ {"help",cli_help,"Prints out available commands"},
+ {"quit",cli_quit,"Exit program"},
+ NULL
+};
+
+extern void verbose_report();
+extern void terse_report();
+void (*reports[])()={verbose_report,terse_report};
+
+/* Counters */
+int files_created; /* number of files created */
+int files_deleted; /* number of files deleted */
+int files_read; /* number of files read */
+int files_appended; /* number of files appended */
+float bytes_written; /* number of bytes written to files */
+float bytes_read; /* number of bytes read from files */
+
+/* Configurable Parameters */
+int file_size_low=500;
+int file_size_high=10000; /* file size: fixed or random within range */
+int simultaneous=500; /* simultaneous files */
+int seed=42; /* random number generator seed */
+int transactions=500; /* number of transactions */
+int subdirectories=0; /* Number of subdirectories */
+int read_block_size=512; /* I/O block sizes */
+int write_block_size=512;
+int bias_read=5; /* chance of picking read over append */
+int bias_create=5; /* chance of picking create over delete */
+int buffered_io=1; /* use C library buffered I/O */
+int report=0; /* 0=verbose, 1=terse report format */
+
+/* Working Storage */
+char *file_source; /* pointer to buffer of random text */
+
+typedef struct {
+ char name[MAX_FILENAME+1]; /* name of individual file */
+ int size; /* current size of file, 0 = unused file slot */
+} file_entry;
+
+file_entry *file_table; /* table of files in use */
+int file_allocated; /* pointer to last allocated slot in file_table */
+
+typedef struct file_system_struct {
+ file_entry system;
+ struct file_system_struct *next,*prev;
+} file_system;
+
+file_system *file_systems; /* table of file systems/directories to use */
+int file_system_weight; /* sum of weights for all file systems */
+int file_system_count; /* number of configured file systems */
+char **location_index; /* weighted index of file systems */
+
+char *read_buffer; /* temporary space for reading file data into */
+
+#define RND(x) ((x>0)?(genrand() % (x)):0)
+extern unsigned long genrand();
+extern void sgenrand();
+
+/* converts integer values to byte/kilobyte/megabyte strings */
+char *scale(i)
+int i;
+{
+ static char buffer[MAX_LINE]; /* storage for current conversion */
+
+ if (i/MEGABYTE)
+ sprintf(buffer,"%.2f megabytes",(float)i/MEGABYTE);
+ else
+ if (i/KILOBYTE)
+ sprintf(buffer,"%.2f kilobytes",(float)i/KILOBYTE);
+ else
+ sprintf(buffer,"%d bytes",i);
+
+ return(buffer);
+}
+
+/* converts float values to byte/kilobyte/megabyte strings */
+char *scalef(i)
+float i;
+{
+ static char buffer[MAX_LINE]; /* storage for current conversion */
+
+ if (i/(float)MEGABYTE>1)
+ sprintf(buffer,"%.2f megabytes",i/(float)MEGABYTE);
+ else
+ if (i/(float)KILOBYTE)
+ sprintf(buffer,"%.2f kilobytes",i/(float)KILOBYTE);
+ else
+ sprintf(buffer,"%f bytes",i);
+
+ return(buffer);
+}
+
+/* UI callback for 'set size' command - sets range of file sizes */
+int cli_set_size(param)
+char *param; /* remainder of command line */
+{
+ char *token;
+ int size;
+
+ if (param && (size=atoi(param))>0)
+ {
+ file_size_low=size;
+ if ((token=strchr(param,' ')) && (size=atoi(token))>0 &&
+ size>=file_size_low)
+ file_size_high=size;
+ else
+ file_size_high=file_size_low;
+ }
+ else
+ fprintf(stderr,"Error: no file size low or high bounds specified\n");
+
+ return(1);
+}
+
+/* UI callback for 'set number' command - sets number of files to create */
+int cli_set_number(param)
+char *param; /* remainder of command line */
+{
+ int size;
+
+ if (param && (size=atoi(param))>0)
+ simultaneous=size;
+ else
+ fprintf(stderr,"Error: no file number specified\n");
+
+ return(1);
+}
+
+/* UI callback for 'set seed' command - initial value for random number gen */
+int cli_set_seed(param)
+char *param; /* remainder of command line */
+{
+ int size;
+
+ if (param && (size=atoi(param))>0)
+ seed=size;
+ else
+ fprintf(stderr,"Error: no random number seed specified\n");
+
+ return(1);
+}
+
+/* UI callback for 'set transactions' - configure number of transactions */
+int cli_set_transactions(param)
+char *param; /* remainder of command line */
+{
+ int size;
+
+ if (param && (size=atoi(param))>0)
+ transactions=size;
+ else
+ fprintf(stderr,"Error: no transactions specified\n");
+
+ return(1);
+}
+
+int parse_weight(params)
+char *params;
+{
+ int weight=1;
+ char *split;
+
+ if (split=strrchr(params,' '))
+ {
+ *split='\0';
+ if ((weight=atoi(split+1))<=0)
+ {
+ fprintf(stderr,"Error: ignoring invalid weight '%s'\n",split+1);
+ weight=1;
+ }
+ }
+
+ return(weight);
+}
+
+void add_location(params,weight)
+char *params;
+int weight;
+{
+ file_system *new_file_system;
+
+ if (new_file_system=(file_system *)calloc(1,sizeof(file_system)))
+ {
+ strcpy(new_file_system->system.name,params);
+ new_file_system->system.size=weight;
+
+ if (file_systems)
+ {
+ new_file_system->prev=file_systems->prev;
+ file_systems->prev->next=new_file_system;
+ file_systems->prev=new_file_system;
+ }
+ else
+ {
+ new_file_system->prev=new_file_system;
+ file_systems=new_file_system;
+ }
+
+ file_system_weight+=weight;
+ file_system_count++;
+ }
+}
+
+void delete_location(loc_name)
+char *loc_name;
+{
+ file_system *traverse;
+
+ for (traverse=file_systems; traverse; traverse=traverse->next)
+ if (!strcmp(traverse->system.name,loc_name))
+ {
+ file_system_weight-=traverse->system.size;
+ file_system_count--;
+
+ if (file_systems->prev==file_systems)
+ {
+ free(file_systems);
+ file_systems=NULL;
+ }
+ else
+ {
+ if (file_systems->prev==traverse)
+ file_systems->prev=traverse->prev;
+
+ if (traverse==file_systems)
+ file_systems=file_systems->next;
+ else
+ traverse->prev->next=traverse->next;
+
+ if (traverse->next)
+ traverse->next->prev=traverse->prev;
+
+ free(traverse);
+ }
+
+ break;
+ }
+
+ if (!traverse)
+ fprintf(stderr,"Error: cannot find location '%s'\n",loc_name);
+}
+
+void delete_locations()
+{
+ file_system *next;
+
+ while (file_systems)
+ {
+ next=file_systems->next;
+ free(file_systems);
+ file_systems=next;
+ }
+
+ file_system_weight=0;
+ file_system_count=0;
+}
+
+/* UI callback for 'set location' - configure current working directory */
+int cli_set_location(param)
+char *param; /* remainder of command line */
+{
+ if (param)
+ {
+ switch (*param)
+ {
+ case '+': /* add location to list */
+ add_location(param+1,parse_weight(param+1));
+ break;
+
+ case '-': /* remove location from list */
+ delete_location(param+1);
+ break;
+
+ default:
+ delete_locations();
+ add_location(param,parse_weight(param));
+ }
+ }
+ else
+ fprintf(stderr,"Error: no directory name specified\n");
+
+ return(1);
+}
+
+/* UI callback for 'set subdirectories' - configure number of subdirectories */
+int cli_set_subdirs(param)
+char *param; /* remainder of command line */
+{
+ int subdirs;
+
+ if (param && (subdirs=atoi(param))>=0)
+ subdirectories=subdirs;
+ else
+ fprintf(stderr,"Error: invalid number of subdirectories specified\n");
+
+ return(1);
+}
+
+/* UI callback for 'set read' - configure read block size (integer) */
+int cli_set_read(param)
+char *param; /* remainder of command line */
+{
+ int size;
+
+ if (param && (size=atoi(param))>0)
+ read_block_size=size;
+ else
+ fprintf(stderr,"Error: no block size specified\n");
+
+ return(1);
+}
+
+/* UI callback for 'set write' - configure write block size (integer) */
+int cli_set_write(param)
+char *param; /* remainder of command line */
+{
+ int size;
+
+ if (param && (size=atoi(param))>0)
+ write_block_size=size;
+ else
+ fprintf(stderr,"Error: no block size specified\n");
+
+ return(1);
+}
+
+/* UI callback for 'set buffering' - sets buffering mode on or off
+ - true = buffered I/O (default), false = raw I/O */
+int cli_set_buffering(param)
+char *param; /* remainder of command line */
+{
+ if (param && (!strcmp(param,"true") || !strcmp(param,"false")))
+ buffered_io=(!strcmp(param,"true"))?1:0;
+ else
+ fprintf(stderr,"Error: no buffering mode (true/false) specified\n");
+
+ return(1);
+}
+
+/* UI callback for 'set bias read' - sets probability of read vs. append */
+int cli_set_bias_read(param)
+char *param; /* remainder of command line */
+{
+ int value;
+
+ if (param && (value=atoi(param))>=-1 && value<=10)
+ bias_read=value;
+ else
+ fprintf(stderr,
+ "Error: no bias specified (0-10 for greater chance,-1 to disable)\n");
+
+ return(1);
+}
+
+/* UI callback for 'set bias create' - sets probability of create vs. delete */
+int cli_set_bias_create(param)
+char *param; /* remainder of command line */
+{
+ int value;
+
+ if (param && (value=atoi(param))>=-1 && value<=10)
+ bias_create=value;
+ else
+ fprintf(stderr,
+ "Error: no bias specified (0-10 for greater chance,-1 to disable)\n");
+
+ return(1);
+}
+
+/* UI callback for 'set report' - chooses verbose or terse report formats */
+int cli_set_report(param)
+char *param; /* remainder of command line */
+{
+ int match=0;
+
+ if (param)
+ {
+ if (!strcmp(param,"verbose"))
+ report=0;
+ else
+ if (!strcmp(param,"terse"))
+ report=1;
+ else
+ match=-1;
+ }
+
+ if (!param || match==-1)
+ fprintf(stderr,"Error: either 'verbose' or 'terse' required\n");
+
+ return(1);
+}
+
+/* populate file source buffer with 'size' bytes of readable randomness */
+char *initialize_file_source(size)
+int size; /* number of bytes of junk to create */
+{
+ char *new_source;
+ int i;
+
+ if ((new_source=(char *)malloc(size))==NULL) /* allocate buffer */
+ fprintf(stderr,"Error: failed to allocate source file of size %d\n",size);
+ else
+ for (i=0; i<size; i++) /* file buffer with junk */
+ new_source[i]=32+RND(95);
+
+ return(new_source);
+}
+
+/* returns differences in times -
+ 1 second is the minimum to avoid divide by zero errors */
+time_t diff_time(t1,t0)
+time_t t1;
+time_t t0;
+{
+ return((t1-=t0)?t1:1);
+}
+
+/* prints out results from running transactions */
+void verbose_report(fp,end_time,start_time,t_end_time,t_start_time,deleted)
+FILE *fp;
+time_t end_time,start_time,t_end_time,t_start_time; /* timers from run */
+int deleted; /* files deleted back-to-back */
+{
+ time_t elapsed,t_elapsed;
+ int interval;
+
+ elapsed=diff_time(end_time,start_time);
+ t_elapsed=diff_time(t_end_time,t_start_time);
+
+ fprintf(fp,"Time:\n");
+ fprintf(fp,"\t%d seconds total\n",elapsed);
+ fprintf(fp,"\t%d seconds of transactions (%d per second)\n",t_elapsed,
+ transactions/t_elapsed);
+
+ fprintf(fp,"\nFiles:\n");
+ fprintf(fp,"\t%d created (%d per second)\n",files_created,
+ files_created/elapsed);
+
+ interval=diff_time(t_start_time,start_time);
+ fprintf(fp,"\t\tCreation alone: %d files (%d per second)\n",simultaneous,
+ simultaneous/interval);
+ fprintf(fp,"\t\tMixed with transactions: %d files (%d per second)\n",
+ files_created-simultaneous,(files_created-simultaneous)/t_elapsed);
+ fprintf(fp,"\t%d read (%d per second)\n",files_read,files_read/t_elapsed);
+ fprintf(fp,"\t%d appended (%d per second)\n",files_appended,
+ files_appended/t_elapsed);
+ fprintf(fp,"\t%d deleted (%d per second)\n",files_created,
+ files_created/elapsed);
+
+ interval=diff_time(end_time,t_end_time);
+ fprintf(fp,"\t\tDeletion alone: %d files (%d per second)\n",deleted,
+ deleted/interval);
+ fprintf(fp,"\t\tMixed with transactions: %d files (%d per second)\n",
+ files_deleted-deleted,(files_deleted-deleted)/t_elapsed);
+
+ fprintf(fp,"\nData:\n");
+ fprintf(fp,"\t%s read ",scalef(bytes_read));
+ fprintf(fp,"(%s per second)\n",scalef(bytes_read/(float)elapsed));
+ fprintf(fp,"\t%s written ",scalef(bytes_written));
+ fprintf(fp,"(%s per second)\n",scalef(bytes_written/(float)elapsed));
+}
+
+void terse_report(fp,end_time,start_time,t_end_time,t_start_time,deleted)
+FILE *fp;
+time_t end_time,start_time,t_end_time,t_start_time; /* timers from run */
+int deleted; /* files deleted back-to-back */
+{
+ time_t elapsed,t_elapsed;
+ int interval;
+
+ elapsed=diff_time(end_time,start_time);
+ t_elapsed=diff_time(t_end_time,t_start_time);
+ interval=diff_time(t_start_time,start_time);
+
+ fprintf(fp,"%d %d %.2f ", elapsed, t_elapsed,
+ (float)transactions/t_elapsed);
+ fprintf(fp, "%.2f %.2f %.2f ", (float)files_created/elapsed,
+ (float)simultaneous/interval,
+ (float)(files_created-simultaneous)/t_elapsed);
+ fprintf(fp, "%.2f %.2f ", (float)files_read/t_elapsed,
+ (float)files_appended/t_elapsed);
+ fprintf(fp, "%.2f %.2f %.2f ", (float)files_created/elapsed,
+ (float)deleted/interval,
+ (float)(files_deleted-deleted)/t_elapsed);
+ fprintf(fp, "%.2f %.2f\n", (float)bytes_read/elapsed,
+ (float)bytes_written/elapsed);
+}
+
+/* returns file_table entry of unallocated file
+ - if not at end of table, then return next entry
+ - else search table for gaps */
+int find_free_file()
+{
+ int i;
+
+ if (file_allocated<simultaneous<<1 && file_table[file_allocated].size==0)
+ return(file_allocated++);
+ else /* search entire table for holes */
+ for (i=0; i<simultaneous<<1; i++)
+ if (file_table[i].size==0)
+ {
+ file_allocated=i;
+ return(file_allocated++);
+ }
+
+ return(-1); /* return -1 only if no free files found */
+}
+
+/* write 'size' bytes to file 'fd' using unbuffered I/O */
+void write_blocks(fd,size)
+int fd;
+int size; /* bytes to write to file */
+{
+ int offset=0; /* offset into file */
+ int i;
+
+ /* write even blocks */
+ for (i=size; i>=write_block_size;
+ i-=write_block_size,offset+=write_block_size)
+ write(fd,file_source+offset,write_block_size);
+
+ write(fd,file_source+offset,i); /* write remainder */
+
+ bytes_written+=size; /* update counter */
+}
+
+/* write 'size' bytes to file 'fp' using buffered I/O */
+void fwrite_blocks(fp,size)
+FILE *fp;
+int size; /* bytes to write to file */
+{
+ int offset=0; /* offset into file */
+ int i;
+
+ /* write even blocks */
+ for (i=size; i>=write_block_size;
+ i-=write_block_size,offset+=write_block_size)
+ fwrite(file_source+offset,write_block_size,1,fp);
+
+ fwrite(file_source+offset,i,1,fp); /* write remainder */
+
+ bytes_written+=size; /* update counter */
+}
+
+void create_file_name(dest)
+char *dest;
+{
+ char conversion[MAX_LINE+1];
+
+ *dest='\0';
+ if (file_system_count)
+ {
+ strcat(dest,
+ location_index[(file_system_count==1)?0:RND(file_system_weight)]);
+ strcat(dest,SEPARATOR);
+ }
+
+ if (subdirectories>1)
+ {
+ sprintf(conversion,"s%d%s",RND(subdirectories),SEPARATOR);
+ strcat(dest,conversion);
+ }
+
+ sprintf(conversion,"%d",++files_created);
+ strcat(dest,conversion);
+}
+
+/* creates new file of specified length and fills it with data */
+void create_file(buffered)
+int buffered; /* 1=buffered I/O (default), 0=unbuffered I/O */
+{
+ FILE *fp=NULL;
+ int fd=-1;
+ int free_file; /* file_table slot for new file */
+
+ if ((free_file=find_free_file())!=-1) /* if file space is available */
+ { /* decide on name and initial length */
+ create_file_name(file_table[free_file].name);
+
+ file_table[free_file].size=
+ file_size_low+RND(file_size_high-file_size_low);
+
+ if (buffered)
+ fp=fopen(file_table[free_file].name,"w");
+ else
+ fd=open(file_table[free_file].name,O_RDWR|O_CREAT,0644);
+
+ if (fp || fd!=-1)
+ {
+ if (buffered)
+ {
+ fwrite_blocks(fp,file_table[free_file].size);
+ fclose(fp);
+ }
+ else
+ {
+ write_blocks(fd,file_table[free_file].size);
+ close(fd);
+ }
+ }
+ else
+ fprintf(stderr,"Error: cannot open '%s' for writing\n",
+ file_table[free_file].name);
+ }
+}
+
+/* deletes specified file from disk and file_table */
+void delete_file(number)
+int number;
+{
+ if (file_table[number].size)
+ {
+ if (remove(file_table[number].name))
+ fprintf(stderr,"Error: Cannot delete '%s'\n",file_table[number].name);
+ else
+ { /* reset entry in file_table and update counter */
+ file_table[number].size=0;
+ files_deleted++;
+ }
+ }
+}
+
+/* reads entire specified file into temporary buffer */
+void read_file(number,buffered)
+int number; /* number of file to read (from file_table) */
+int buffered; /* 1=buffered I/O (default), 0=unbuffered I/O */
+{
+ FILE *fp=NULL;
+ int fd=-1;
+ int i;
+
+ if (buffered)
+ fp=fopen(file_table[number].name,"r");
+ else
+ fd=open(file_table[number].name,O_RDONLY,0644);
+
+ if (fp || fd!=-1)
+ { /* read as many blocks as possible then read the remainder */
+ if (buffered)
+ {
+ for (i=file_table[number].size; i>=read_block_size; i-=read_block_size)
+ fread(read_buffer,read_block_size,1,fp);
+
+ fread(read_buffer,i,1,fp);
+
+ fclose(fp);
+ }
+ else
+ {
+ for (i=file_table[number].size; i>=read_block_size; i-=read_block_size)
+ read(fd,read_buffer,read_block_size);
+
+ read(fd,read_buffer,i);
+
+ close(fd);
+ }
+
+ /* increment counters to record transaction */
+ bytes_read+=file_table[number].size;
+ files_read++;
+ }
+ else
+ fprintf(stderr,"Error: cannot open '%s' for reading\n",
+ file_table[number].name);
+}
+
+/* appends random data to a chosen file up to the maximum configured length */
+void append_file(number,buffered)
+int number; /* number of file (from file_table) to append date to */
+int buffered; /* 1=buffered I/O (default), 0=unbuffered I/O */
+{
+ FILE *fp=NULL;
+ int fd=-1;
+ int block; /* size of data to append */
+
+ if (file_table[number].size<file_size_high)
+ {
+ if (buffered)
+ fp=fopen(file_table[number].name,"a");
+ else
+ fd=open(file_table[number].name,O_RDWR|O_APPEND,0644);
+
+ if ((fp || fd!=-1) && file_table[number].size<file_size_high)
+ {
+ block=RND(file_size_high-file_table[number].size)+1;
+
+ if (buffered)
+ {
+ fwrite_blocks(fp,block);
+ fclose(fp);
+ }
+ else
+ {
+ write_blocks(fd,block);
+ close(fd);
+ }
+
+ file_table[number].size+=block;
+ files_appended++;
+ }
+ else
+ fprintf(stderr,"Error: cannot open '%s' for append\n",
+ file_table[number].name);
+ }
+}
+
+/* finds and returns the offset of a file that is in use from the file_table */
+int find_used_file() /* only called after files are created */
+{
+ int used_file;
+
+ while (file_table[used_file=RND(simultaneous<<1)].size==0)
+ ;
+
+ return(used_file);
+}
+
+/* reset global counters - done before each test run */
+void reset_counters()
+{
+ files_created=0;
+ files_deleted=0;
+ files_read=0;
+ files_appended=0;
+ bytes_written=0;
+ bytes_read=0;
+}
+
+/* perform the configured number of file transactions
+ - a transaction consisted of either a read or append and either a
+ create or delete all chosen at random */
+int run_transactions(buffered)
+int buffered; /* 1=buffered I/O (default), 0=unbuffered I/O */
+{
+ int percent; /* one tenth of the specified transactions */
+ int i;
+
+ percent=transactions/10;
+ for (i=0; i<transactions; i++)
+ {
+ if (files_created==files_deleted)
+ {
+ printf("out of files!\n");
+ printf("For this workload, either increase the number of files or\n");
+ printf("decrease the number of transactions.\n");
+ break;
+ }
+
+ if (bias_read!=-1) /* if read/append not locked out... */
+ {
+ if (RND(10)<bias_read) /* read file */
+ read_file(find_used_file(),buffered);
+ else /* append file */
+ append_file(find_used_file(),buffered);
+ }
+
+ if (bias_create!=-1) /* if create/delete not locked out... */
+ {
+ if (RND(10)<bias_create) /* create file */
+ create_file(buffered);
+ else /* delete file */
+ delete_file(find_used_file());
+ }
+
+ if ((i % percent)==0) /* if another tenth of the work is done...*/
+ {
+ putchar('.'); /* print progress indicator */
+ fflush(stdout);
+ }
+ }
+
+ return(transactions-i);
+}
+
+char **build_location_index(list,weight)
+file_system *list;
+int weight;
+{
+ char **index;
+ int count;
+ int i=0;
+
+ if ((index=(char **)calloc(1,weight*sizeof(char *)))==NULL)
+ fprintf(stderr,"Error: cannot build weighted index of locations\n");
+ else
+ for (; list; list=list->next)
+ for (count=0; count<list->system.size; count++)
+ index[i++]=list->system.name;
+
+ return(index);
+}
+
+void create_subdirectories(dir_list,base_dir,subdirs)
+file_system *dir_list;
+char *base_dir;
+int subdirs;
+{
+ char dir_name[MAX_LINE+1]; /* buffer holding subdirectory names */
+ char save_dir[MAX_LINE+1];
+ int i;
+
+ if (dir_list)
+ {
+ for (; dir_list; dir_list=dir_list->next)
+ create_subdirectories(NULL,dir_list->system.name,subdirs);
+ }
+ else
+ {
+ if (base_dir)
+ sprintf(save_dir,"%s%s",base_dir,SEPARATOR);
+ else
+ *save_dir='\0';
+
+ for (i=0; i<subdirs; i++)
+ {
+ sprintf(dir_name,"%ss%d",save_dir,i);
+ MKDIR(dir_name);
+ }
+ }
+}
+
+void delete_subdirectories(dir_list,base_dir,subdirs)
+file_system *dir_list;
+char *base_dir;
+int subdirs;
+{
+ char dir_name[MAX_LINE+1]; /* buffer holding subdirectory names */
+ char save_dir[MAX_LINE+1];
+ int i;
+
+ if (dir_list)
+ {
+ for (; dir_list; dir_list=dir_list->next)
+ delete_subdirectories(NULL,dir_list->system.name,subdirs);
+ }
+ else
+ {
+ if (base_dir)
+ sprintf(save_dir,"%s%s",base_dir,SEPARATOR);
+ else
+ *save_dir='\0';
+
+ for (i=0; i<subdirs; i++)
+ {
+ sprintf(dir_name,"%ss%d",save_dir,i);
+ rmdir(dir_name);
+ }
+ }
+}
+
+/* CLI callback for 'run' - benchmark execution loop */
+int cli_run(param) /* none */
+char *param; /* unused */
+{
+ time_t start_time,t_start_time,t_end_time,end_time; /* elapsed timers */
+ int delete_base; /* snapshot of deleted files counter */
+ FILE *fp=NULL; /* file descriptor for directing output */
+ int incomplete;
+ int i; /* generic iterator */
+
+ reset_counters(); /* reset counters before each run */
+
+ sgenrand(seed); /* initialize random number generator */
+
+ /* allocate file space and fill with junk */
+ file_source=initialize_file_source(file_size_high<<1);
+
+ /* allocate read buffer */
+ read_buffer=(char *)malloc(read_block_size);
+
+ /* allocate table of files at 2 x simultaneous files */
+ file_allocated=0;
+ if ((file_table=(file_entry *)calloc(simultaneous<<1,sizeof(file_entry)))==
+ NULL)
+ fprintf(stderr,"Error: Failed to allocate table for %d files\n",
+ simultaneous<<1);
+
+ if (file_system_count>0)
+ location_index=build_location_index(file_systems,file_system_weight);
+
+ /* create subdirectories if necessary */
+ if (subdirectories>1)
+ {
+ printf("Creating subdirectories...");
+ fflush(stdout);
+ create_subdirectories(file_systems,NULL,subdirectories);
+ printf("Done\n");
+ }
+
+ time(&start_time); /* store start time */
+
+ /* create files in specified directory until simultaneous number */
+ printf("Creating files...");
+ fflush(stdout);
+ for (i=0; i<simultaneous; i++)
+ create_file(buffered_io);
+ printf("Done\n");
+
+ printf("Performing transactions");
+ fflush(stdout);
+ time(&t_start_time);
+ incomplete=run_transactions(buffered_io);
+ time(&t_end_time);
+ if (!incomplete)
+ printf("Done\n");
+
+ /* delete remaining files */
+ printf("Deleting files...");
+ fflush(stdout);
+ delete_base=files_deleted;
+ for (i=0; i<simultaneous<<1; i++)
+ delete_file(i);
+ printf("Done\n");
+
+ /* print end time and difference, transaction numbers */
+ time(&end_time);
+
+ /* delete previously created subdirectories */
+ if (subdirectories>1)
+ {
+ printf("Deleting subdirectories...");
+ fflush(stdout);
+ delete_subdirectories(file_systems,NULL,subdirectories);
+ printf("Done\n");
+ }
+
+ if (location_index)
+ {
+ free(location_index);
+ location_index=NULL;
+ }
+
+ if (param)
+ if ((fp=fopen(param,"a"))==NULL)
+ fprintf(stderr,"Error: Cannot direct output to file '%s'\n",param);
+
+ if (!fp)
+ fp=stdout;
+
+ if (!incomplete)
+ reports[report](fp,end_time,start_time,t_end_time,t_start_time,
+ files_deleted-delete_base);
+
+ if (param && fp!=stdout)
+ fclose(fp);
+
+ /* free resources allocated for this run */
+ free(file_table);
+ free(read_buffer);
+ free(file_source);
+
+ return(1); /* return 1 unless exit requested, then return 0 */
+}
+
+/* CLI callback for 'show' - print values of configuration variables */
+int cli_show(param)
+char *param; /* optional: name of output file */
+{
+ char current_dir[MAX_LINE+1]; /* buffer containing working directory */
+ file_system *traverse;
+ FILE *fp=NULL; /* file descriptor for directing output */
+
+ if (param)
+ if ((fp=fopen(param,"a"))==NULL)
+ fprintf(stderr,"Error: Cannot direct output to file '%s'\n",param);
+
+ if (!fp)
+ fp=stdout;
+
+ fprintf(fp,"Current configuration is:\n");
+ fprintf(fp,"The base number of files is %d\n",simultaneous);
+ fprintf(fp,"Transactions: %d\n",transactions);
+
+ if (file_size_low!=file_size_high)
+ {
+ fprintf(fp,"Files range between %s ",scale(file_size_low));
+ fprintf(fp,"and %s in size\n",scale(file_size_high));
+ }
+ else
+ fprintf(fp,"Files are %s in size\n",scale(file_size_low));
+
+ fprintf(fp,"Working director%s: %s\n",(file_system_count>1)?"ies":"y",
+ (file_system_count==0)?GETWD(current_dir):"");
+
+ for (traverse=file_systems; traverse; traverse=traverse->next)
+ printf("\t%s (weight=%d)\n",traverse->system.name,traverse->system.size);
+
+ if (subdirectories>0)
+ fprintf(fp,"%d subdirector%s will be used\n",subdirectories,
+ (subdirectories==1)?"y":"ies");
+
+ fprintf(fp,"Block sizes are: read=%s, ",scale(read_block_size));
+ fprintf(fp,"write=%s\n",scale(write_block_size));
+ fprintf(fp,"Biases are: read/append=%d, create/delete=%d\n",bias_read,
+ bias_create);
+ fprintf(fp,"%ssing Unix buffered file I/O\n",buffered_io?"U":"Not u");
+ fprintf(fp,"Random number generator seed is %d\n",seed);
+
+ fprintf(fp,"Report format is %s.\n",report?"terse":"verbose");
+
+ if (param && fp!=stdout)
+ fclose(fp);
+
+ return(1); /* return 1 unless exit requested, then return 0 */
+}
+
+/* CLI callback for 'quit' - returns 0 causing UI to exit */
+int cli_quit(param) /* none */
+char *param; /* unused */
+{
+ return(0); /* return 1 unless exit requested, then return 0 */
+}
+
+/* CLI callback for 'help' - prints help strings from command_list */
+int cli_help(param)
+char *param; /* optional: specific command to get help for */
+{
+ int n=0; /* number of matching items */
+ int i; /* traversal variable for command table */
+ int len;
+
+ if (param && (len=strlen(param))>0) /* if a command is specified... */
+ for (i=0; command_list[i].name; i++) /* walk command table */
+ if (!strncmp(command_list[i].name,param,len))
+ {
+ printf("%s - %s\n",command_list[i].name,command_list[i].help);
+ n++;
+ }
+
+ if (!param || !n)
+ for (i=0; command_list[i].name; i++) /* traverse command table */
+ printf("%s - %s\n",command_list[i].name,command_list[i].help);
+
+ return(1); /* return 1 unless exit requested, then return 0 */
+}
+
+/* read CLI line from user, translate aliases if any, return fgets status */
+char *cli_read_line(buffer,size)
+char *buffer; /* empty input line */
+int size;
+{
+ char *result;
+
+ printf("%s",PROMPT); /* print prompt */
+ fflush(stdout); /* force prompt to print */
+ if (result=fgets(buffer,size,stdin)) /* read line safely */
+ {
+ buffer[strlen(buffer)-1]='\0'; /* delete final CR */
+ if (!strcmp(buffer,"?")) /* translate aliases */
+ strcpy(buffer,"help");
+ if (!strcmp(buffer,"exit"))
+ strcpy(buffer,"quit");
+ }
+
+ return(result); /* return success of fgets */
+}
+
+/* parse CLI input line */
+int cli_parse_line(buffer)
+char *buffer; /* line of user input */
+{
+ int result=1; /* default return status */
+ int len; /* length of parsed command */
+ int i; /* traversal variable for command table */
+
+ if (*buffer=='!') /* check for shell escape */
+ system((strlen(buffer)>1)?buffer+1:getenv("SHELL"));
+ else
+ {
+ for (i=0; command_list[i].name; i++) /* walk command table */
+ if (!strncmp(command_list[i].name,buffer,
+ len=strlen(command_list[i].name)))
+ { /* if command matches... */
+ result=(command_list[i].func)
+ (((int)strlen(buffer)>len)?buffer+len+1:NULL);
+ break; /* call function and pass remainder of line as parameter */
+ }
+
+ if (!command_list[i].name) /* if no commands were called... */
+ printf("Eh?\n"); /* tribute to Canadian diction */
+ }
+
+ return(result); /* return 1 unless exit requested, then return 0 */
+}
+
+/* read config file if present and process it line by line
+ - if 'quit' is in file then function returns 0 */
+int read_config_file(filename,buffer)
+char *filename; /* file name of config file */
+char *buffer; /* temp storage for each line read from file */
+{
+ int result=1; /* default exit value - proceed with UI */
+ FILE *fp;
+
+ if (fp=fopen(filename,"r")) /* open config file */
+ {
+ printf("Reading configuration from file '%s'\n",filename);
+ while (fgets(buffer,MAX_LINE,fp) && result) /* read lines until 'quit' */
+ {
+ buffer[strlen(buffer)-1]='\0'; /* delete final CR */
+ result=cli_parse_line(buffer); /* process line as typed in */
+ }
+
+ fclose(fp);
+ }
+
+ return(result);
+}
+
+/* main function - reads config files then enters get line/parse line loop */
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ char buffer[MAX_LINE+1]; /* storage for input command line */
+
+ printf("PostMark v1.5 : 3/27/01\n");
+ if (read_config_file((argc==2)?argv[1]:".pmrc",buffer))
+ while (cli_read_line(buffer,MAX_LINE) && cli_parse_line(buffer))
+ ;
+}
+
+/*
+
+ The "Artistic License"
+
+ Preamble
+
+The intent of this document is to state the conditions under which a
+Package may be copied, such that the Copyright Holder maintains some
+semblance of artistic control over the development of the package,
+while giving the users of the package the right to use and distribute
+the Package in a more-or-less customary fashion, plus the right to make
+reasonable modifications.
+
+Definitions:
+
+ "Package" refers to the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection of files
+ created through textual modification.
+
+ "Standard Version" refers to such a Package if it has not been
+ modified, or has been modified in accordance with the wishes
+ of the Copyright Holder as specified below.
+
+ "Copyright Holder" is whoever is named in the copyright or
+ copyrights for the package.
+
+ "You" is you, if you're thinking about copying or distributing
+ this Package.
+
+ "Reasonable copying fee" is whatever you can justify on the
+ basis of media cost, duplication charges, time of people involved,
+ and so on. (You will not be required to justify it to the
+ Copyright Holder, but only to the computing community at large
+ as a market that must bear the fee.)
+
+ "Freely Available" means that no fee is charged for the item
+ itself, though there may be fees involved in handling the item.
+ It also means that recipients of the item may redistribute it
+ under the same conditions they received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you
+duplicate all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications
+derived from the Public Domain or from the Copyright Holder. A Package
+modified in such a way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+that you insert a prominent notice in each changed file stating how and
+when you changed that file, and provided that you do at least ONE of the
+following:
+
+ a) place your modifications in the Public Domain or otherwise make them
+ Freely Available, such as by posting said modifications to Usenet or
+ an equivalent medium, or placing the modifications on a major archive
+ site such as uunet.uu.net, or by allowing the Copyright Holder to include
+ your modifications in the Standard Version of the Package.
+
+ b) use the modified Package only within your corporation or organization.
+
+ c) rename any non-standard executables so the names do not conflict
+ with standard executables, which must also be provided, and provide
+ a separate manual page for each non-standard executable that clearly
+ documents how it differs from the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or
+executable form, provided that you do at least ONE of the following:
+
+ a) distribute a Standard Version of the executables and library files,
+ together with instructions (in the manual page or equivalent) on where
+ to get the Standard Version.
+
+ b) accompany the distribution with the machine-readable source of
+ the Package with your modifications.
+
+ c) give non-standard executables non-standard names, and clearly
+ document the differences in manual pages (or equivalent), together
+ with instructions on where to get the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this
+Package. You may charge any fee you choose for support of this
+Package. You may not charge a fee for this Package itself. However,
+you may distribute this Package in aggregate with other (possibly
+commercial) programs as part of a larger (possibly commercial) software
+distribution provided that you do not advertise this Package as a
+product of your own. You may embed this Package's interpreter within
+an executable of yours (by linking); this shall be construed as a mere
+form of aggregation, provided that the complete Standard Version of the
+interpreter is so embedded.
+
+6. The scripts and library files supplied as input to or produced as
+output from the programs of this Package do not automatically fall
+under the copyright of this Package, but belong to whomever generated
+them, and may be sold commercially, and may be aggregated with this
+Package. If such scripts or library files are aggregated with this
+Package via the so-called "undump" or "unexec" methods of producing a
+binary executable image, then distribution of such an image shall
+neither be construed as a distribution of this Package nor shall it
+fall under the restrictions of Paragraphs 3 and 4, provided that you do
+not represent such an executable image as a Standard Version of this
+Package.
+
+7. C subroutines (or comparably compiled subroutines in other
+languages) supplied by you and linked into this Package in order to
+emulate subroutines and variables of the language defined by this
+Package shall not be considered part of this Package, but are the
+equivalent of input as in Paragraph 6, provided these subroutines do
+not change the language in any way that would cause it to fail the
+regression tests for the language.
+
+8. Aggregation of this Package with a commercial distribution is always
+permitted provided that the use of this Package is embedded; that is,
+when no overt attempt is made to make this Package's interfaces visible
+to the end user of the commercial distribution. Such use shall not be
+construed as a distribution of this Package.
+
+9. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+ The End
+
+*/
+
+
+/* A C-program for MT19937: Integer version (1999/10/28) */
+/* genrand() generates one pseudorandom unsigned integer (32bit) */
+/* which is uniformly distributed among 0 to 2^32-1 for each */
+/* call. sgenrand(seed) sets initial values to the working area */
+/* of 624 words. Before genrand(), sgenrand(seed) must be */
+/* called once. (seed is any 32-bit integer.) */
+/* Coded by Takuji Nishimura, considering the suggestions by */
+/* Topher Cooper and Marc Rieffel in July-Aug. 1997. */
+
+/* This library is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU Library General Public */
+/* License as published by the Free Software Foundation; either */
+/* version 2 of the License, or (at your option) any later */
+/* version. */
+/* This library 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 Library General Public License for more details. */
+/* You should have received a copy of the GNU Library General */
+/* Public License along with this library; if not, write to the */
+/* Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */
+/* 02111-1307 USA */
+
+/* Copyright (C) 1997, 1999 Makoto Matsumoto and Takuji Nishimura. */
+/* Any feedback is very welcome. For any question, comments, */
+/* see http://www.math.keio.ac.jp/matumoto/emt.html or email */
+/* matumoto@math.keio.ac.jp */
+
+/* REFERENCE */
+/* M. Matsumoto and T. Nishimura, */
+/* "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform */
+/* Pseudo-Random Number Generator", */
+/* ACM Transactions on Modeling and Computer Simulation, */
+/* Vol. 8, No. 1, January 1998, pp 3--30. */
+
+/* Period parameters */
+#define N 624
+#define M 397
+#define MATRIX_A 0x9908b0df /* constant vector a */
+#define UPPER_MASK 0x80000000 /* most significant w-r bits */
+#define LOWER_MASK 0x7fffffff /* least significant r bits */
+
+/* Tempering parameters */
+#define TEMPERING_MASK_B 0x9d2c5680
+#define TEMPERING_MASK_C 0xefc60000
+#define TEMPERING_SHIFT_U(y) (y >> 11)
+#define TEMPERING_SHIFT_S(y) (y << 7)
+#define TEMPERING_SHIFT_T(y) (y << 15)
+#define TEMPERING_SHIFT_L(y) (y >> 18)
+
+static unsigned long mt[N]; /* the array for the state vector */
+static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
+
+/* Initializing the array with a seed */
+void
+sgenrand(seed)
+ unsigned long seed;
+{
+ int i;
+
+ for (i=0;i<N;i++) {
+ mt[i] = seed & 0xffff0000;
+ seed = 69069 * seed + 1;
+ mt[i] |= (seed & 0xffff0000) >> 16;
+ seed = 69069 * seed + 1;
+ }
+ mti = N;
+}
+
+/* Initialization by "sgenrand()" is an example. Theoretically, */
+/* there are 2^19937-1 possible states as an intial state. */
+/* This function allows to choose any of 2^19937-1 ones. */
+/* Essential bits in "seed_array[]" is following 19937 bits: */
+/* (seed_array[0]&UPPER_MASK), seed_array[1], ..., seed_array[N-1]. */
+/* (seed_array[0]&LOWER_MASK) is discarded. */
+/* Theoretically, */
+/* (seed_array[0]&UPPER_MASK), seed_array[1], ..., seed_array[N-1] */
+/* can take any values except all zeros. */
+void
+lsgenrand(seed_array)
+ unsigned long seed_array[];
+ /* the length of seed_array[] must be at least N */
+{
+ int i;
+
+ for (i=0;i<N;i++)
+ mt[i] = seed_array[i];
+ mti=N;
+}
+
+unsigned long
+genrand()
+{
+ unsigned long y;
+ static unsigned long mag01[2]={0x0, MATRIX_A};
+ /* mag01[x] = x * MATRIX_A for x=0,1 */
+
+ if (mti >= N) { /* generate N words at one time */
+ int kk;
+
+ if (mti == N+1) /* if sgenrand() has not been called, */
+ sgenrand(4357); /* a default initial seed is used */
+
+ for (kk=0;kk<N-M;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
+ }
+ for (;kk<N-1;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1];
+ }
+ y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
+ mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1];
+
+ mti = 0;
+ }
+
+ y = mt[mti++];
+ y ^= TEMPERING_SHIFT_U(y);
+ y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
+ y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
+ y ^= TEMPERING_SHIFT_L(y);
+
+ return y;
+}