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 }