tstVDIo.cpp revision 48a15610b4cfea625302a67f21b3f46215c59aa4
/* $Id$ */
/** @file
*
* VBox HDD container test utility - I/O replay.
*/
/*
* Copyright (C) 2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#define LOGGROUP LOGGROUP_DEFAULT
#include <iprt/initterm.h>
#include <iprt/semaphore.h>
#include <iprt/critsect.h>
#include "VDMemDisk.h"
#include "VDIoBackendMem.h"
#include "VDIoRnd.h"
/**
* A virtual file backed by memory.
*/
typedef struct VDFILE
{
/** Pointer to the next file. */
/** Name of the file. */
char *pszName;
/** Memory file baking the file. */
/** Flag whether the file is read locked. */
bool fReadLock;
/** Flag whether the file is write locked. */
bool fWriteLock;
/**
* VD storage object.
*/
typedef struct VDSTORAGE
{
/** Pointer to the file. */
/** Completion callback of the VD layer. */
} VDSTORAGE, *PVDSTORAGE;
/**
* A virtual disk.
*/
typedef struct VDDISK
{
/** List node. */
/** Name of the disk handle for identification. */
char *pszName;
/** HDD handle to operate on. */
/** Memory disk used for data verification. */
/** Critical section to serialize access to the memory disk. */
/** Physical CHS Geometry. */
/** Logical CHS geometry. */
/**
* A data buffer with a pattern.
*/
typedef struct VDPATTERN
{
/** List node. */
/** Name of the pattern. */
char *pszName;
/** Size of the pattern. */
/** Pointer to the buffer containing the pattern. */
void *pvPattern;
} VDPATTERN, *PVDPATTERN;
/**
* Global VD test state.
*/
typedef struct VDTESTGLOB
{
/** List of active virtual disks. */
/** Head of the active file list. */
/** Head of the pattern list. */
/** Memory I/O backend. */
/** Error interface. */
/** Error interface callbacks. */
/** Pointer to the per disk interface list. */
/** I/O interface. */
/** I/O interface callbacks. */
/** Pointer to the per image interface list. */
/** I/O RNG handle. */
} VDTESTGLOB, *PVDTESTGLOB;
/**
* Transfer direction.
*/
typedef enum VDIOREQTXDIR
{
VDIOREQTXDIR_READ = 0,
} VDIOREQTXDIR;
/**
* I/O request.
*/
typedef struct VDIOREQ
{
/** Transfer type. */
/** slot index. */
unsigned idx;
/** Start offset. */
/** Size to transfer. */
/** S/G Buffer */
/** Data segment */
/** Flag whether the request is outstanding or not. */
volatile bool fOutstanding;
/** Buffer to use for reads. */
void *pvBufRead;
/** Opaque user data. */
void *pvUser;
/**
* I/O test data.
*/
typedef struct VDIOTEST
{
/** Start offset. */
/** End offset. */
/** Flag whether random or sequential access is wanted */
bool fRandomAccess;
/** Block size. */
/** Number of bytes to transfer. */
/** Chance in percent to get a write. */
unsigned uWriteChance;
/** Pointer to the I/O data generator. */
/** Pointer to the data pattern to use. */
/** Data dependent on the I/O mode (sequential or random). */
union
{
/** Next offset for sequential access. */
/** Data for random acess. */
struct
{
/** Number of valid entries in the bitmap. */
/** Pointer to the bitmap marking accessed blocks. */
/** Number of unaccessed blocks. */
} Rnd;
} u;
/**
* Argument types.
*/
typedef enum VDSCRIPTARGTYPE
{
/** Argument is a string. */
/** Argument is a 64bit unsigned number. */
/** Argument is a 64bit signed number. */
/** Arugment is a unsigned 64bit range */
/** Arugment is a boolean. */
/**
* Script argument.
*/
typedef struct VDSCRIPTARG
{
/** Argument identifier. */
char chId;
/** Type of the argument. */
/** Type depndent data. */
union
{
/** String. */
const char *pcszString;
/** Bool. */
bool fFlag;
/** unsigned number. */
/** Signed number. */
/** Unsigned range. */
struct
{
} Range;
} u;
} VDSCRIPTARG, *PVDSCRIPTARG;
/** Script action handler. */
typedef DECLCALLBACK(int) FNVDSCRIPTACTION(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
/** Pointer to a script action handler. */
typedef FNVDSCRIPTACTION *PFNVDSCRIPTACTION;
/**
* Script argument descriptor.
*/
typedef struct VDSCRIPTARGDESC
{
/** Name of the arugment. */
const char *pcszName;
/** Identifier for the argument. */
char chId;
/** Type of the argument. */
/** Flags */
/** Pointer to a const script argument descriptor. */
typedef const VDSCRIPTARGDESC *PCVDSCRIPTARGDESC;
/** Flag whether the argument is mandatory. */
#define VDSCRIPTARGDESC_FLAG_MANDATORY RT_BIT(0)
/** Flag whether the number can have a size suffix (K|M|G) */
/**
* Script action.
*/
typedef struct VDSCRIPTACTION
{
/** Action name. */
const char *pcszAction;
/** Pointer to the arguments. */
const PCVDSCRIPTARGDESC paArgDesc;
/** Number of arugments in the array. */
unsigned cArgDescs;
/** Pointer to the action handler. */
typedef const VDSCRIPTACTION *PCVDSCRIPTACTION;
static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerCompact(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
/* create action */
const VDSCRIPTARGDESC g_aArgCreate[] =
{
/* pcszName chId enmType fFlags */
{"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX}
};
/* open action */
const VDSCRIPTARGDESC g_aArgOpen[] =
{
/* pcszName chId enmType fFlags */
};
/* I/O action */
const VDSCRIPTARGDESC g_aArgIo[] =
{
/* pcszName chId enmType fFlags */
{"blocksize", 'b', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
};
/* flush action */
const VDSCRIPTARGDESC g_aArgFlush[] =
{
/* pcszName chId enmType fFlags */
};
/* merge action */
const VDSCRIPTARGDESC g_aArgMerge[] =
{
/* pcszName chId enmType fFlags */
};
/* Compact a disk */
const VDSCRIPTARGDESC g_aArgCompact[] =
{
/* pcszName chId enmType fFlags */
};
/* close action */
const VDSCRIPTARGDESC g_aArgClose[] =
{
/* pcszName chId enmType fFlags */
};
/* I/O RNG create action */
const VDSCRIPTARGDESC g_aArgIoRngCreate[] =
{
/* pcszName chId enmType fFlags */
{"size", 'd', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
};
/* I/O pattern create action */
{
/* pcszName chId enmType fFlags */
{"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
};
/* I/O pattern create action */
const VDSCRIPTARGDESC g_aArgIoPatternCreateFromFile[] =
{
/* pcszName chId enmType fFlags */
};
/* I/O pattern destroy action */
const VDSCRIPTARGDESC g_aArgIoPatternDestroy[] =
{
/* pcszName chId enmType fFlags */
};
/* Sleep */
const VDSCRIPTARGDESC g_aArgSleep[] =
{
/* pcszName chId enmType fFlags */
};
/* Dump memory file */
const VDSCRIPTARGDESC g_aArgDumpFile[] =
{
/* pcszName chId enmType fFlags */
};
/* Create virtual disk handle */
const VDSCRIPTARGDESC g_aArgCreateDisk[] =
{
/* pcszName chId enmType fFlags */
};
/* Create virtual disk handle */
const VDSCRIPTARGDESC g_aArgDestroyDisk[] =
{
/* pcszName chId enmType fFlags */
};
/* Compare virtual disks */
const VDSCRIPTARGDESC g_aArgCompareDisks[] =
{
/* pcszName chId enmType fFlags */
};
/* Dump disk info */
const VDSCRIPTARGDESC g_aArgDumpDiskInfo[] =
{
/* pcszName chId enmType fFlags */
};
/* Print message */
const VDSCRIPTARGDESC g_aArgPrintMsg[] =
{
/* pcszName chId enmType fFlags */
};
const VDSCRIPTACTION g_aScriptActions[] =
{
/* pcszAction paArgDesc cArgDescs pfnHandler */
{"iopatterncreatefromnumber", g_aArgIoPatternCreateFromNumber, RT_ELEMENTS(g_aArgIoPatternCreateFromNumber), vdScriptHandlerIoPatternCreateFromNumber},
{"iopatterncreatefromfile", g_aArgIoPatternCreateFromFile, RT_ELEMENTS(g_aArgIoPatternCreateFromFile), vdScriptHandlerIoPatternCreateFromFile},
{"iopatterndestroy", g_aArgIoPatternDestroy, RT_ELEMENTS(g_aArgIoPatternDestroy), vdScriptHandlerIoPatternDestroy},
};
{
RTPrintf("\n");
}
{
RTPrintf("tstVD: ");
return VINF_SUCCESS;
}
static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
const char *pcszBackend = NULL;
bool fBase = false;
bool fDynamic = true;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'd':
{
break;
}
case 'm':
{
fBase = true;
fBase = false;
else
{
}
break;
}
case 'n':
{
break;
}
case 'b':
{
break;
}
case 's':
{
break;
}
case 't':
{
fDynamic = false;
fDynamic = true;
else
{
}
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
if (RT_FAILURE(rc))
break;
}
if (RT_SUCCESS(rc))
{
if (pDisk)
{
unsigned fImageFlags = VD_IMAGE_FLAGS_NONE;
if (!fDynamic)
if (fBase)
else
rc = VDCreateDiff(pDisk->pVD, pcszBackend, pcszImage, fImageFlags, NULL, NULL, NULL, VD_OPEN_FLAGS_ASYNC_IO,
}
else
rc = VERR_NOT_FOUND;
}
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
const char *pcszBackend = NULL;
bool fShareable = false;
bool fReadonly = false;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'd':
{
break;
}
case 'n':
{
break;
}
case 'b':
{
break;
}
case 's':
{
break;
}
case 'r':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
if (RT_FAILURE(rc))
break;
}
if (RT_SUCCESS(rc))
{
if (pDisk)
{
unsigned fOpenFlags = VD_OPEN_FLAGS_ASYNC_IO;
if (fShareable)
if (fReadonly)
}
else
rc = VERR_NOT_FOUND;
}
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
bool fAsync = false;
bool fRandomAcc = false;
bool fDataProviderRnd = false;
bool fPrintStats = false;
unsigned cMaxReqs = 0;
uint8_t uWriteChance = 0;
const char *pcszPattern = NULL;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'd':
{
break;
}
case 'a':
{
break;
}
case 'l':
{
break;
}
case 'm':
{
fRandomAcc = false;
fRandomAcc = true;
else
{
}
break;
}
case 's':
{
break;
}
case 'b':
{
break;
}
case 'o':
{
break;
}
case 'w':
{
break;
}
case 'p':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
if (RT_FAILURE(rc))
break;
}
if (RT_SUCCESS(rc))
{
if (!pDisk)
rc = VERR_NOT_FOUND;
}
if (RT_SUCCESS(rc))
{
/* Set defaults if not set by the user. */
{
if (offEnd == 0)
return VERR_INVALID_STATE;
}
if (!cbIo)
}
if ( RT_SUCCESS(rc)
&& pcszPattern)
{
if (!pPattern)
rc = VERR_NOT_FOUND;
}
if (RT_SUCCESS(rc))
{
rc = tstVDIoTestInit(&IoTest, pGlob, fRandomAcc, cbIo, cbBlkSize, offStart, offEnd, uWriteChance, pPattern);
if (RT_SUCCESS(rc))
{
{
/* Init requests. */
for (unsigned i = 0; i < cMaxTasksOutstanding; i++)
{
{
rc = VERR_NO_MEMORY;
break;
}
}
while ( tstVDIoTestRunning(&IoTest)
&& RT_SUCCESS(rc))
{
bool fTasksOutstanding = false;
unsigned idx = 0;
/* Submit all idling requests. */
while ( idx < cMaxTasksOutstanding
&& tstVDIoTestRunning(&IoTest))
{
{
if (RT_SUCCESS(rc))
{
if (!fAsync)
{
{
case VDIOREQTXDIR_READ:
{
if (RT_SUCCESS(rc)
&& pDisk->pMemDiskVerify)
{
{
}
}
break;
}
case VDIOREQTXDIR_WRITE:
{
if (RT_SUCCESS(rc)
&& pDisk->pMemDiskVerify)
{
}
break;
}
case VDIOREQTXDIR_FLUSH:
{
break;
}
}
if (RT_SUCCESS(rc))
idx++;
}
else
{
{
case VDIOREQTXDIR_READ:
{
break;
}
case VDIOREQTXDIR_WRITE:
{
break;
}
case VDIOREQTXDIR_FLUSH:
{
break;
}
}
if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
{
idx++;
fTasksOutstanding = true;
rc = VINF_SUCCESS;
}
else if (rc == VINF_VD_ASYNC_IO_FINISHED)
{
{
case VDIOREQTXDIR_READ:
{
if (pDisk->pMemDiskVerify)
{
{
}
}
}
case VDIOREQTXDIR_WRITE:
{
if (pDisk->pMemDiskVerify)
{
}
break;
}
case VDIOREQTXDIR_FLUSH:
break;
}
if (rc != VERR_INVALID_STATE)
rc = VINF_SUCCESS;
}
}
if (RT_FAILURE(rc))
}
}
}
/* Wait for a request to complete. */
if ( fAsync
{
}
}
/* Cleanup, wait for all tasks to complete. */
while (fAsync)
{
unsigned idx = 0;
bool fAllIdle = true;
while (idx < cMaxTasksOutstanding)
{
{
fAllIdle = false;
break;
}
idx++;
}
if (!fAllIdle)
{
}
else
break;
}
}
else
rc = VERR_NO_MEMORY;
}
}
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
bool fAsync = false;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'd':
{
break;
}
case 'a':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
if (RT_FAILURE(rc))
break;
}
if (RT_SUCCESS(rc))
{
if (!pDisk)
rc = VERR_NOT_FOUND;
else if (fAsync)
{
/** @todo */
}
else
}
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
unsigned nImageFrom = 0;
unsigned nImageTo = 0;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'd':
{
break;
}
case 'f':
{
break;
}
case 't':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
if (RT_FAILURE(rc))
break;
}
if (RT_SUCCESS(rc))
{
if (!pDisk)
rc = VERR_NOT_FOUND;
else
{
/** @todo: Provide progress interface to test that cancelation
* doesn't corrupt the data.
*/
}
}
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerCompact(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
unsigned nImage = 0;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'd':
{
break;
}
case 'i':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
if (RT_FAILURE(rc))
break;
}
if (RT_SUCCESS(rc))
{
if (!pDisk)
rc = VERR_NOT_FOUND;
else
{
/** @todo: Provide progress interface to test that cancelation
* doesn't corrupt the data.
*/
}
}
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
bool fAll = false;
bool fDelete = false;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'd':
{
break;
}
case 'm':
{
fAll = true;
fAll = false;
else
{
}
break;
}
case 'r':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
if (RT_FAILURE(rc))
break;
}
if ( RT_SUCCESS(rc)
&& fAll
&& fDelete)
{
RTPrintf("mode=all doesn't work with delete=yes\n");
}
if (RT_SUCCESS(rc))
{
if (pDisk)
{
if (fAll)
else
}
else
rc = VERR_NOT_FOUND;
}
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
const char *pcszSeeder = NULL;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'd':
{
break;
}
case 's':
{
break;
}
case 'm':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
}
{
RTPrintf("I/O RNG already exists\n");
}
else
{
uint64_t uSeedToUse = 0;
uSeedToUse = uSeed;
{
if (RT_SUCCESS(rc))
{
}
}
if (RT_SUCCESS(rc))
}
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
{
}
else
RTPrintf("WARNING: No I/O RNG active, faulty script. Continuing\n");
return VINF_SUCCESS;
}
static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
uint64_t u64Pattern = 0;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'n':
{
break;
}
case 's':
{
break;
}
case 'p':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
}
if (!pPattern)
{
if (pPattern)
{
/* Fill the buffer. */
{
}
}
else
rc = VERR_NO_MEMORY;
}
else
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'n':
{
break;
}
case 'f':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
}
if (!pPattern)
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
if (pPattern)
{
if (RT_SUCCESS(rc))
else
{
}
}
else
rc = VERR_NO_MEMORY;
}
}
}
else
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'n':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
}
if (pPattern)
{
}
else
rc = VERR_NOT_FOUND;
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 't':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
}
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
const char *pcszPathToDump = NULL;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'f':
{
break;
}
case 'p':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
}
/* Check for the file. */
bool fFound = false;
{
{
fFound = true;
break;
}
}
if (fFound)
{
}
else
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
bool fVerify = false;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'n':
{
break;
}
case 'v':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
}
if (pDisk)
else
{
if (pDisk)
{
{
rc = VINF_SUCCESS;
if (fVerify)
{
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
}
}
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
else
{
if (fVerify)
{
}
}
}
}
else
rc = VERR_NO_MEMORY;
if (RT_FAILURE(rc))
}
else
rc = VERR_NO_MEMORY;
}
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'n':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
}
if (pDisk)
{
if (pDisk->pMemDiskVerify)
{
}
}
else
rc = VERR_NOT_FOUND;
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case '1':
{
break;
}
case '2':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
}
{
{
else
{
{
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
{
{
break;
}
}
else
{
break;
}
}
}
}
else
{
if (pbBuf1)
if (pbBuf2)
rc = VERR_NO_MEMORY;
}
}
else
rc = VERR_NOT_FOUND;
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'd':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
}
if (pDisk)
else
rc = VERR_NOT_FOUND;
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
return VINF_SUCCESS;
}
void **ppStorage)
{
int rc = VINF_SUCCESS;
bool fFound = false;
/*
* Some backends use ./ for paths, strip it.
* @todo: Implement proper directory support for the
* memory filesystem.
*/
&& *pszLocation == '.'
pszLocation += 2;
/* Check if the file exists. */
{
{
fFound = true;
break;
}
}
{
/* If the file exists delete the memory disk. */
if (fFound)
else
{
/* Create completey new. */
if (pIt)
{
{
}
else
rc = VERR_NO_MEMORY;
if (RT_FAILURE(rc))
{
}
}
else
rc = VERR_NO_MEMORY;
}
}
{
if (!fFound)
}
else
if (RT_SUCCESS(rc))
{
if (!pStorage)
rc = VERR_NO_MEMORY;
}
return rc;
}
{
return VINF_SUCCESS;
}
{
int rc = VINF_SUCCESS;
bool fFound = false;
/*
* Some backends use ./ for paths, strip it.
* @todo: Implement proper directory support for the
* memory filesystem.
*/
&& *pcszFilename == '.'
pcszFilename += 2;
/* Check if the file exists. */
{
{
fFound = true;
break;
}
}
if (fFound)
{
}
else
return rc;
}
static DECLCALLBACK(int) tstVDIoFileMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
{
int rc = VINF_SUCCESS;
bool fFound = false;
/* Check if the file exists. */
{
{
fFound = true;
break;
}
}
if (fFound)
{
if (pszNew)
{
}
else
rc = VERR_NO_MEMORY;
}
else
return rc;
}
static DECLCALLBACK(int) tstVDIoFileGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
{
return VINF_SUCCESS;
}
static DECLCALLBACK(int) tstVDIoFileGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
{
/** @todo: Implement */
return VINF_SUCCESS;
}
{
}
{
}
{
int rc = VINF_SUCCESS;
*pcbWritten = cbBuffer;
return rc;
}
{
int rc = VINF_SUCCESS;
return rc;
}
{
/* nothing to do. */
return VINF_SUCCESS;
}
void **ppTask)
{
int rc = VINF_SUCCESS;
rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_READ, uOffset,
if (RT_SUCCESS(rc))
return rc;
}
void **ppTask)
{
int rc = VINF_SUCCESS;
rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_WRITE, uOffset,
if (RT_SUCCESS(rc))
return rc;
}
void **ppTask)
{
int rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
return rc;
}
{
int rc = VINF_SUCCESS;
if (fRandomAcc)
{
? 1
: 0));
rc = VERR_NO_MEMORY;
}
else
return rc;
}
{
if (pIoTest->fRandomAccess)
}
{
}
{
return pIoReq->fOutstanding;
}
/**
* Returns true with the given chance in percent.
*
* @returns true or false
* @param iPercentage The percentage of the chance to return true.
*/
{
}
{
int rc = VINF_SUCCESS;
{
/* Read or Write? */
pIoReq->enmTxDir = tstVDIoTestIsTrue(pIoTest, pIoTest->uWriteChance) ? VDIOREQTXDIR_WRITE : VDIOREQTXDIR_READ;
{
else
}
else
{
/* Read */
}
if (RT_SUCCESS(rc))
{
if (pIoTest->fRandomAccess)
{
int idx = -1;
/* In case this is the last request we don't need to search further. */
{
int idxIo;
/*
* If the bit is marked free use it, otherwise search for the next free bit
* and if that doesn't work use the first free bit.
*/
{
if (idxIo != -1)
}
else
}
{
/* New round, clear everything. */
}
else
}
else
{
{
}
else
{
? 0
}
}
pIoReq->fOutstanding = true;
}
}
else
return rc;
}
{
if (pDisk->pMemDiskVerify)
{
{
case VDIOREQTXDIR_READ:
{
}
case VDIOREQTXDIR_WRITE:
{
break;
}
case VDIOREQTXDIR_FLUSH:
break;
}
}
return;
}
/**
* Returns the disk handle by name or NULL if not found
*
* @returns Disk handle or NULL if the disk could not be found.
*
* @param pGlob Global test state.
* @param pcszDisk Name of the disk to get.
*/
{
bool fFound = false;
{
{
fFound = true;
break;
}
}
}
/**
* Returns the I/O pattern handle by name of NULL if not found.
*
* @returns I/O pattern handle or NULL if the pattern could not be found.
*
* @param pGlob Global test state.
* @param pcszName Name of the pattern.
*/
{
bool fFound = false;
{
{
fFound = true;
break;
}
}
}
/**
* Creates a new pattern with the given name and an
* allocated pattern buffer.
*
* @returns Pointer to a new pattern buffer or NULL on failure.
* @param pcszName Name of the pattern.
* @param cbPattern Size of the pattern buffer.
*/
{
{
}
else
{
if (pPattern)
if (pszName)
if (pvPattern)
}
return pPattern;
}
{
return VERR_INVALID_PARAMETER;
return VINF_SUCCESS;
}
/**
* Skips the characters until the given character is reached.
*
* @returns Start of the string with the given character
* or NULL if the string ended before.
*
* @param psz The string to skip.
* @param ch The character.
*/
{
while ( *psz != '\0'
psz++;
return psz;
}
/**
* Skips the spaces of the current string.
*
* @returns Start of the string with a non space character
* or NULL if the string ended before.
*
* @param psz The string to skip.
*/
static char *tstVDIoScriptSkipSpace(char *psz)
{
while ( *psz != '\0'
&& RT_C_IS_SPACE(*psz))
psz++;
return psz;
}
/**
* Skips all characters until a space is reached of the current
* string.
*
* @returns Start of the string with a space character
* or NULL if the string ended before.
*
* @param psz The string to skip.
*/
static char *tstVDIoScriptSkipNonSpace(char *psz)
{
while ( *psz != '\0'
&& !RT_C_IS_SPACE(*psz))
psz++;
return psz;
}
/**
* Returns true if the first character of the given string
* contains a character marking a line end (comment or \0
* terminator).
*
* @returns true if the line contains no more characters of
* interest and false otherwise.
*
* @param psz The string to check for.
*/
static bool tstVDIoIsLineEnd(const char *psz)
{
}
/**
* Parses one argument name, value pair.
*
* @returns IPRT status code.
*
* @param pVDScriptAction Script action.
* @param pcszName Argument name.
* @param pcszValue Argument value.
* @param pScriptArg Where to fill in the parsed
* argument.
* @param pfMandatory Where to store whether the argument
* is mandatory.
*/
{
int rc = VERR_NOT_FOUND;
for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
{
{
rc = VINF_SUCCESS;
{
case VDSCRIPTARGTYPE_BOOL:
{
pScriptArg->u.fFlag = true;
pScriptArg->u.fFlag = false;
else
{
}
break;
}
{
AssertMsgFailed(("todo\n"));
break;
}
case VDSCRIPTARGTYPE_STRING:
{
break;
}
{
if (rc == VWRN_TRAILING_CHARS)
{
switch (*pszSuffix)
{
case 'k':
case 'K':
{
break;
}
case 'm':
case 'M':
{
break;
}
case 'g':
case 'G':
{
break;
}
default:
{
}
}
if (rc != VERR_INVALID_PARAMETER)
rc = VINF_SUCCESS;
}
break;
}
{
if (rc == VWRN_TRAILING_CHARS)
{
if (*pszSuffix != '-')
{
switch (*pszSuffix)
{
case 'k':
case 'K':
{
break;
}
case 'm':
case 'M':
{
break;
}
case 'g':
case 'G':
{
break;
}
default:
{
}
}
if (RT_SUCCESS(rc))
pszSuffix++;
}
if (*pszSuffix == '-')
{
pszSuffix++;
if (rc == VWRN_TRAILING_CHARS)
{
switch (*pszSuffix)
{
case 'k':
case 'K':
{
break;
}
case 'm':
case 'M':
{
break;
}
case 'g':
case 'G':
{
break;
}
default:
{
}
}
}
}
else
}
else
if (rc == VERR_INVALID_PARAMETER)
RTPrintf("Invalid range format\n");
break;
}
default:
AssertMsgFailed(("Invalid script argument type\n"));
}
if (RT_SUCCESS(rc))
{
}
break;
}
}
if (rc == VERR_NOT_FOUND)
return rc;
}
/**
* Parses the arguments of a action in the script.
*
* @returns IPRT status code.
*
* @param psz Argument string.
* @param pVDScriptAction The script action to parses
* arguments for.
* @param paScriptArgs Where to store the arguments.
* @param pcScriptArgs Where to store the actual number of
* arguments parsed.
*/
static int tstVDIoScriptArgumentListParse(char *psz, PCVDSCRIPTACTION pVDScriptAction, PVDSCRIPTARG paScriptArgs, unsigned *pcScriptArgs)
{
int rc = VINF_SUCCESS;
unsigned cMandatoryArgsReq = 0;
unsigned cScriptArgs = 0;
/* Count the number of mandatory arguments first. */
for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
/* One argument is given in the form name=value. */
*pcScriptArgs = 0;
while ( psz
&& !tstVDIoIsLineEnd(psz))
{
if (!tstVDIoIsLineEnd(psz))
{
psz++;
if (!tstVDIoIsLineEnd(psz))
{
psz++;
}
if (*pcszValue == '\0')
{
break;
}
/* We have the name and value pair now. */
bool fMandatory = false; /* Shut up gcc */
rc = tstVDIoScriptArgumentParse(pVDScriptAction, pcszName, pcszValue, &paScriptArgs[cScriptArgs], &fMandatory);
if (RT_SUCCESS(rc))
{
if (fMandatory)
cScriptArgs++;
}
}
else
{
RTPrintf("Argument in invalid form\n");
break;
}
}
if ( RT_SUCCESS(rc)
{
/* No arguments anymore but there are still mandatory arguments left. */
}
if (RT_SUCCESS(rc))
return rc;
}
/**
* Executes the script pointed to by the given stream.
*
* @returns IPRT status code.
*
* @param pStrm The stream handle of the script.
* @param pGlob Global test data.
*/
{
int rc = VINF_SUCCESS;
unsigned cScriptArgsMax = 0;
do
{
if (RT_SUCCESS(rc))
{
const char *pcszAction = NULL;
/* Skip space */
if (!tstVDIoIsLineEnd(psz))
{
/* Get the action name. */
pcszAction = psz;
if (!tstVDIoIsLineEnd(psz))
{
*psz++ = '\0';
}
/* Find the action. */
for (unsigned i = 0; i < g_cScriptActions; i++)
{
{
pVDScriptAction = &g_aScriptActions[i];
break;
}
}
if (pVDScriptAction)
{
/* Parse arguments. */
{
/* Increase arguments array. */
if (paScriptArgs)
}
if (paScriptArgs)
{
unsigned cScriptArgs;
if (RT_SUCCESS(rc))
{
/* Execute the handler. */
}
}
else
{
rc = VERR_NO_MEMORY;
}
}
else
{
rc = VERR_NOT_FOUND;
}
}
/* else empty line, just continue */
}
} while(RT_SUCCESS(rc));
{
RTPrintf("Successfully executed I/O script\n");
rc = VINF_SUCCESS;
}
return rc;
}
/**
* Executes the given I/O script.
*
* @returns nothing.
*
* @param pcszFilename The script to execute.
*/
static void tstVDIoScriptRun(const char *pcszFilename)
{
int rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
/* Init global test data. */
/* Init I/O backend. */
if (RT_SUCCESS(rc))
{
/* Execute the script. */
if (RT_FAILURE(rc))
{
}
}
else
RTPrintf("Creating the I/O backend failed rc=%Rrc\n");
}
else
}
/**
* Shows help message.
*/
static void printUsage(void)
{
RTPrintf("Usage:\n"
"--script <filename> Script to execute\n"
"--replay <filename> Log to replay (not implemented yet)\n");
}
static const RTGETOPTDEF g_aOptions[] =
{
};
{
RTR3Init();
int rc;
char c;
if (argc != 3)
{
printUsage();
return RTEXITCODE_FAILURE;
}
if (RT_FAILURE(rc))
return RTEXITCODE_FAILURE;
while ( RT_SUCCESS(rc)
{
switch (c)
{
case 's':
break;
case 'r':
RTPrintf("Replaying I/O logs is not implemented yet\n");
break;
default:
printUsage();
}
}
rc = VDShutdown();
if (RT_FAILURE(rc))
return RTEXITCODE_SUCCESS;
}