summaryrefslogtreecommitdiffstats
path: root/ext/lsm1/lsm-test/lsmtest_io.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:28:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:28:19 +0000
commit18657a960e125336f704ea058e25c27bd3900dcb (patch)
tree17b438b680ed45a996d7b59951e6aa34023783f2 /ext/lsm1/lsm-test/lsmtest_io.c
parentInitial commit. (diff)
downloadsqlite3-18657a960e125336f704ea058e25c27bd3900dcb.tar.xz
sqlite3-18657a960e125336f704ea058e25c27bd3900dcb.zip
Adding upstream version 3.40.1.upstream/3.40.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ext/lsm1/lsm-test/lsmtest_io.c')
-rw-r--r--ext/lsm1/lsm-test/lsmtest_io.c248
1 files changed, 248 insertions, 0 deletions
diff --git a/ext/lsm1/lsm-test/lsmtest_io.c b/ext/lsm1/lsm-test/lsmtest_io.c
new file mode 100644
index 0000000..7aa5d10
--- /dev/null
+++ b/ext/lsm1/lsm-test/lsmtest_io.c
@@ -0,0 +1,248 @@
+
+/*
+** SUMMARY
+**
+** This file implements the 'io' subcommand of the test program. It is used
+** for testing the performance of various combinations of write() and fsync()
+** system calls. All operations occur on a single file, which may or may not
+** exist when a test is started.
+**
+** A test consists of a series of commands. Each command is either a write
+** or an fsync. A write is specified as "<amount>@<offset>", where <amount>
+** is the amount of data written, and <offset> is the offset of the file
+** to write to. An <amount> or an <offset> is specified as an integer number
+** of bytes. Or, if postfixed with a "K", "M" or "G", an integer number of
+** KB, MB or GB, respectively. An fsync is simply "S". All commands are
+** case-insensitive.
+**
+** Example test program:
+**
+** 2M@6M 1492K@4M S 4096@4K S
+**
+** This program writes 2 MB of data starting at the offset 6MB offset of
+** the file, followed by 1492 KB of data written at the 4MB offset of the
+** file, followed by a call to fsync(), a write of 4KB of data at byte
+** offset 4096, and finally another call to fsync().
+**
+** Commands may either be specified on the command line (one command per
+** command line argument) or read from stdin. Commands read from stdin
+** must be separated by white-space.
+**
+** COMMAND LINE INVOCATION
+**
+** The sub-command implemented in this file must be invoked with at least
+** two arguments - the path to the file to write to and the page-size to
+** use for writing. If there are more than two arguments, then each
+** subsequent argument is assumed to be a test command. If there are exactly
+** two arguments, the test commands are read from stdin.
+**
+** A write command does not result in a single call to system call write().
+** Instead, the specified region is written sequentially using one or
+** more calls to write(), each of which writes not more than one page of
+** data. For example, if the page-size is 4KB, the command "2M@6M" results
+** in 512 calls to write(), each of which writes 4KB of data.
+**
+** EXAMPLES
+**
+** Two equivalent examples:
+**
+** $ lsmtest io testfile.db 4KB 2M@6M 1492K@4M S 4096@4K S
+** 3544K written in 129 ms
+** $ echo "2M@6M 1492K@4M S 4096@4K S" | lsmtest io testfile.db 4096
+** 3544K written in 127 ms
+**
+*/
+
+#include "lsmtest.h"
+
+typedef struct IoContext IoContext;
+
+struct IoContext {
+ int fd;
+ int nWrite;
+};
+
+/*
+** As isspace(3)
+*/
+static int safe_isspace(char c){
+ if( c&0x80) return 0;
+ return isspace(c);
+}
+
+/*
+** As isdigit(3)
+*/
+static int safe_isdigit(char c){
+ if( c&0x80) return 0;
+ return isdigit(c);
+}
+
+static i64 getNextSize(char *zIn, char **pzOut, int *pRc){
+ i64 iRet = 0;
+ if( *pRc==0 ){
+ char *z = zIn;
+
+ if( !safe_isdigit(*z) ){
+ *pRc = 1;
+ return 0;
+ }
+
+ /* Process digits */
+ while( safe_isdigit(*z) ){
+ iRet = iRet*10 + (*z - '0');
+ z++;
+ }
+
+ /* Process suffix */
+ switch( *z ){
+ case 'k': case 'K':
+ iRet = iRet * 1024;
+ z++;
+ break;
+
+ case 'm': case 'M':
+ iRet = iRet * 1024 * 1024;
+ z++;
+ break;
+
+ case 'g': case 'G':
+ iRet = iRet * 1024 * 1024 * 1024;
+ z++;
+ break;
+ }
+
+ if( pzOut ) *pzOut = z;
+ }
+ return iRet;
+}
+
+static int doOneCmd(
+ IoContext *pCtx,
+ u8 *aData,
+ int pgsz,
+ char *zCmd,
+ char **pzOut
+){
+ char c;
+ char *z = zCmd;
+
+ while( safe_isspace(*z) ) z++;
+ c = *z;
+
+ if( c==0 ){
+ if( pzOut ) *pzOut = z;
+ return 0;
+ }
+
+ if( c=='s' || c=='S' ){
+ if( pzOut ) *pzOut = &z[1];
+ return fdatasync(pCtx->fd);
+ }
+
+ if( safe_isdigit(c) ){
+ i64 iOff = 0;
+ int nByte = 0;
+ int rc = 0;
+ int nPg;
+ int iPg;
+
+ nByte = (int)getNextSize(z, &z, &rc);
+ if( rc || *z!='@' ) goto bad_command;
+ z++;
+ iOff = getNextSize(z, &z, &rc);
+ if( rc || (safe_isspace(*z)==0 && *z!='\0') ) goto bad_command;
+ if( pzOut ) *pzOut = z;
+
+ nPg = (nByte+pgsz-1) / pgsz;
+ lseek(pCtx->fd, (off_t)iOff, SEEK_SET);
+ for(iPg=0; iPg<nPg; iPg++){
+ write(pCtx->fd, aData, pgsz);
+ }
+ pCtx->nWrite += nByte/1024;
+
+ return 0;
+ }
+
+ bad_command:
+ testPrintError("unrecognized command: %s", zCmd);
+ return 1;
+}
+
+static int readStdin(char **pzOut){
+ int nAlloc = 128;
+ char *zOut = 0;
+ int nOut = 0;
+
+ while( !feof(stdin) ){
+ int nRead;
+
+ nAlloc = nAlloc*2;
+ zOut = realloc(zOut, nAlloc);
+ nRead = fread(&zOut[nOut], 1, nAlloc-nOut-1, stdin);
+
+ if( nRead==0 ) break;
+ nOut += nRead;
+ zOut[nOut] = '\0';
+ }
+
+ *pzOut = zOut;
+ return 0;
+}
+
+int do_io(int nArg, char **azArg){
+ IoContext ctx;
+ int pgsz;
+ char *zFile;
+ char *zPgsz;
+ int i;
+ int rc = 0;
+
+ char *zStdin = 0;
+ char *z;
+
+ u8 *aData;
+
+ memset(&ctx, 0, sizeof(IoContext));
+ if( nArg<2 ){
+ testPrintUsage("FILE PGSZ ?CMD-1 ...?");
+ return -1;
+ }
+ zFile = azArg[0];
+ zPgsz = azArg[1];
+
+ pgsz = (int)getNextSize(zPgsz, 0, &rc);
+ if( pgsz<=0 ){
+ testPrintError("Ridiculous page size: %d", pgsz);
+ return -1;
+ }
+ aData = malloc(pgsz);
+ memset(aData, 0x77, pgsz);
+
+ ctx.fd = open(zFile, O_RDWR|O_CREAT|_O_BINARY, 0644);
+ if( ctx.fd<0 ){
+ perror("open: ");
+ return -1;
+ }
+
+ if( nArg==2 ){
+ readStdin(&zStdin);
+ testTimeInit();
+ z = zStdin;
+ while( *z && rc==0 ){
+ rc = doOneCmd(&ctx, aData, pgsz, z, &z);
+ }
+ }else{
+ testTimeInit();
+ for(i=2; i<nArg; i++){
+ rc = doOneCmd(&ctx, aData, pgsz, azArg[i], 0);
+ }
+ }
+
+ printf("%dK written in %d ms\n", ctx.nWrite, testTimeGet());
+
+ free(zStdin);
+ close(ctx.fd);
+
+ return 0;
+}