tstVDIo.cpp revision 078c5b8c5c814755aab943bf62cadef7e91c419c
/** @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 "VDMemDisk.h"
#include "VDIoBackendMem.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. */
/** Completion callback of the VD layer. */
/**
* Global VD test state.
*/
typedef struct VDTESTGLOB
{
/** HDD handle to operate on. */
/** Head of the active file 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. */
/** Physical CHS Geometry. */
/** Logical CHS geometry. */
} VDTESTGLOB, *PVDTESTGLOB;
/**
* 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) vdScriptHandlerClose(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 */
};
/* write 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 */
};
/* close action */
const VDSCRIPTARGDESC g_aArgClose[] =
{
/* pcszName chId enmType fFlags */
};
const VDSCRIPTACTION g_aScriptActions[] =
{
/* pcszAction paArgDesc cArgDescs pfnHandler */
};
{
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;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'm':
{
fBase = true;
fBase = false;
else
{
}
break;
}
case 'n':
{
break;
}
case 'b':
{
break;
}
case 's':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
if (RT_FAILURE(rc))
break;
}
if (RT_SUCCESS(rc))
{
if (fBase)
else
}
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
const char *pcszBackend = NULL;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'n':
{
break;
}
case 'b':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
if (RT_FAILURE(rc))
break;
}
if (RT_SUCCESS(rc))
{
}
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;
uint8_t uReadChance = 0;
if (offEnd == 0)
return VERR_INVALID_STATE;
for (unsigned i = 0; i < cScriptArgs; i++)
{
switch (paScriptArgs[i].chId)
{
case 'a':
{
break;
}
case 'l':
{
break;
}
case 'm':
{
fRandomAcc = false;
fRandomAcc = true;
else
{
}
break;
}
case 's':
{
break;
}
case 'b':
{
break;
}
case 'o':
{
break;
}
case 'r':
{
break;
}
case 'w':
{
break;
}
default:
AssertMsgFailed(("Invalid argument given!\n"));
}
if (RT_FAILURE(rc))
break;
}
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
int rc = VINF_SUCCESS;
bool fAsync = false;
if (fAsync)
{
/** @todo */
}
else
return rc;
}
static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
{
return VERR_NOT_IMPLEMENTED;
}
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 'm':
{
fAll = true;
fAll = false;
else
{
}
break;
}
case 'd':
{
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 (fAll)
else
}
return rc;
}
void **ppStorage)
{
int rc = VINF_SUCCESS;
bool fFound = false;
/* Check if the file exists. */
{
{
fFound = true;
break;
}
}
else if (fOpen & RTFILE_O_CREATE)
{
/* 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;
}
}
else if (fOpen & RTFILE_O_OPEN)
{
if (!fFound)
else
}
else
if (RT_SUCCESS(rc))
{
}
return rc;
}
{
/* Mark as not busy. */
return VINF_SUCCESS;
}
{
int rc = VINF_SUCCESS;
bool fFound = false;
/* 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;
if (RT_SUCCESS(rc))
return rc;
}
void **ppTask)
{
int rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
return rc;
}
void **ppTask)
{
int rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
return rc;
}
/**
* 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;
}
/**
* 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 (*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
&& *psz != '\0')
{
if (psz != '\0')
{
psz++;
if (psz != '\0')
{
psz++;
/* We have the name and value pair now. */
bool fMandatory;
rc = tstVDIoScriptArgumentParse(pVDScriptAction, pcszName, pcszValue, &paScriptArgs[cScriptArgs], &fMandatory);
if (RT_SUCCESS(rc))
{
if (fMandatory)
cScriptArgs++;
}
}
else
{
break;
}
}
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 (psz != '\0')
{
/* Get the action name. */
pcszAction = psz;
if (psz != '\0')
{
*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
{
RTPrintf("Missing action name\n");
}
}
} while(RT_SUCCESS(rc));
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))
{
if (RT_SUCCESS(rc))
{
/* Execute the script. */
if (RT_FAILURE(rc))
{
}
}
else
}
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;
}