modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/lsm1/lsm-test/lsmtest_io.c (about)

     1  
     2  /*
     3  ** SUMMARY
     4  **
     5  **   This file implements the 'io' subcommand of the test program. It is used
     6  **   for testing the performance of various combinations of write() and fsync()
     7  **   system calls. All operations occur on a single file, which may or may not
     8  **   exist when a test is started.
     9  **
    10  **   A test consists of a series of commands. Each command is either a write
    11  **   or an fsync. A write is specified as "<amount>@<offset>", where <amount>
    12  **   is the amount of data written, and <offset> is the offset of the file
    13  **   to write to. An <amount> or an <offset> is specified as an integer number
    14  **   of bytes. Or, if postfixed with a "K", "M" or "G", an integer number of
    15  **   KB, MB or GB, respectively. An fsync is simply "S". All commands are
    16  **   case-insensitive.
    17  **
    18  **   Example test program:
    19  **
    20  **        2M@6M 1492K@4M S 4096@4K S
    21  **
    22  **   This program writes 2 MB of data starting at the offset 6MB offset of
    23  **   the file, followed by 1492 KB of data written at the 4MB offset of the
    24  **   file, followed by a call to fsync(), a write of 4KB of data at byte
    25  **   offset 4096, and finally another call to fsync().
    26  **
    27  **   Commands may either be specified on the command line (one command per
    28  **   command line argument) or read from stdin. Commands read from stdin
    29  **   must be separated by white-space.
    30  **
    31  ** COMMAND LINE INVOCATION
    32  **
    33  **   The sub-command implemented in this file must be invoked with at least
    34  **   two arguments - the path to the file to write to and the page-size to
    35  **   use for writing. If there are more than two arguments, then each
    36  **   subsequent argument is assumed to be a test command. If there are exactly
    37  **   two arguments, the test commands are read from stdin.
    38  **
    39  **   A write command does not result in a single call to system call write().
    40  **   Instead, the specified region is written sequentially using one or
    41  **   more calls to write(), each of which writes not more than one page of
    42  **   data. For example, if the page-size is 4KB, the command "2M@6M" results
    43  **   in 512 calls to write(), each of which writes 4KB of data.
    44  **
    45  ** EXAMPLES
    46  **
    47  **   Two equivalent examples:
    48  **
    49  **     $ lsmtest io testfile.db 4KB 2M@6M 1492K@4M S 4096@4K S
    50  **     3544K written in 129 ms
    51  **     $ echo "2M@6M 1492K@4M S 4096@4K S" | lsmtest io testfile.db 4096 
    52  **     3544K written in 127 ms
    53  **
    54  */
    55  
    56  #include "lsmtest.h"
    57  
    58  typedef struct IoContext IoContext;
    59  
    60  struct IoContext {
    61    int fd;
    62    int nWrite;
    63  };
    64  
    65  /*
    66  ** As isspace(3)
    67  */
    68  static int safe_isspace(char c){
    69    if( c&0x80) return 0;
    70    return isspace(c);
    71  }
    72  
    73  /*
    74  ** As isdigit(3)
    75  */
    76  static int safe_isdigit(char c){
    77    if( c&0x80) return 0;
    78    return isdigit(c);
    79  }
    80  
    81  static i64 getNextSize(char *zIn, char **pzOut, int *pRc){
    82    i64 iRet = 0;
    83    if( *pRc==0 ){
    84      char *z = zIn;
    85  
    86      if( !safe_isdigit(*z) ){
    87        *pRc = 1;
    88        return 0;
    89      }
    90  
    91      /* Process digits */
    92      while( safe_isdigit(*z) ){
    93        iRet = iRet*10 + (*z - '0');
    94        z++;
    95      }
    96  
    97      /* Process suffix */
    98      switch( *z ){
    99        case 'k': case 'K':
   100          iRet = iRet * 1024;
   101          z++;
   102          break;
   103  
   104        case 'm': case 'M':
   105          iRet = iRet * 1024 * 1024;
   106          z++;
   107          break;
   108  
   109        case 'g': case 'G':
   110          iRet = iRet * 1024 * 1024 * 1024;
   111          z++;
   112          break;
   113      }
   114  
   115      if( pzOut ) *pzOut = z;
   116    }
   117    return iRet;
   118  }
   119  
   120  static int doOneCmd(
   121    IoContext *pCtx,
   122    u8 *aData,
   123    int pgsz,
   124    char *zCmd,
   125    char **pzOut
   126  ){
   127    char c;
   128    char *z = zCmd;
   129  
   130    while( safe_isspace(*z) ) z++;
   131    c = *z;
   132  
   133    if( c==0 ){
   134      if( pzOut ) *pzOut = z;
   135      return 0;
   136    }
   137  
   138    if( c=='s' || c=='S' ){
   139      if( pzOut ) *pzOut = &z[1];
   140      return fdatasync(pCtx->fd);
   141    }
   142  
   143    if( safe_isdigit(c) ){
   144      i64 iOff = 0;
   145      int nByte = 0;
   146      int rc = 0;
   147      int nPg;
   148      int iPg;
   149  
   150      nByte = (int)getNextSize(z, &z, &rc);
   151      if( rc || *z!='@' ) goto bad_command;
   152      z++;
   153      iOff = getNextSize(z, &z, &rc);
   154      if( rc || (safe_isspace(*z)==0 && *z!='\0') ) goto bad_command;
   155      if( pzOut ) *pzOut = z;
   156  
   157      nPg = (nByte+pgsz-1) / pgsz;
   158      lseek(pCtx->fd, (off_t)iOff, SEEK_SET);
   159      for(iPg=0; iPg<nPg; iPg++){
   160        write(pCtx->fd, aData, pgsz);
   161      }
   162      pCtx->nWrite += nByte/1024;
   163  
   164      return 0;
   165    }
   166  
   167   bad_command:
   168    testPrintError("unrecognized command: %s", zCmd);
   169    return 1;
   170  }
   171  
   172  static int readStdin(char **pzOut){
   173    int nAlloc = 128;
   174    char *zOut = 0;
   175    int nOut = 0;
   176  
   177    while( !feof(stdin) ){
   178      int nRead;
   179  
   180      nAlloc = nAlloc*2;
   181      zOut = realloc(zOut, nAlloc);
   182      nRead = fread(&zOut[nOut], 1, nAlloc-nOut-1, stdin);
   183  
   184      if( nRead==0 ) break;
   185      nOut += nRead;
   186      zOut[nOut] = '\0';
   187    }
   188  
   189    *pzOut = zOut;
   190    return 0;
   191  }
   192  
   193  int do_io(int nArg, char **azArg){
   194    IoContext ctx;
   195    int pgsz;
   196    char *zFile;
   197    char *zPgsz;
   198    int i;
   199    int rc = 0;
   200  
   201    char *zStdin = 0;
   202    char *z;
   203  
   204    u8 *aData;
   205  
   206    memset(&ctx, 0, sizeof(IoContext));
   207    if( nArg<2 ){
   208      testPrintUsage("FILE PGSZ ?CMD-1 ...?");
   209      return -1;
   210    }
   211    zFile = azArg[0];
   212    zPgsz = azArg[1];
   213  
   214    pgsz = (int)getNextSize(zPgsz, 0, &rc);
   215    if( pgsz<=0 ){
   216      testPrintError("Ridiculous page size: %d", pgsz);
   217      return -1;
   218    }
   219    aData = malloc(pgsz);
   220    memset(aData, 0x77, pgsz);
   221  
   222    ctx.fd = open(zFile, O_RDWR|O_CREAT|_O_BINARY, 0644);
   223    if( ctx.fd<0 ){
   224      perror("open: ");
   225      return -1;
   226    }
   227  
   228    if( nArg==2 ){
   229      readStdin(&zStdin);
   230      testTimeInit();
   231      z = zStdin;
   232      while( *z && rc==0 ){
   233        rc = doOneCmd(&ctx, aData, pgsz, z, &z);
   234      }
   235    }else{
   236      testTimeInit();
   237      for(i=2; i<nArg; i++){
   238        rc = doOneCmd(&ctx, aData, pgsz, azArg[i], 0);
   239      }
   240    }
   241  
   242    printf("%dK written in %d ms\n", ctx.nWrite, testTimeGet());
   243  
   244    free(zStdin);
   245    close(ctx.fd);
   246  
   247    return 0;
   248  }