VirtualBox

source: vbox/trunk/src/VBox/Storage/testcase/tstVDIo.cpp@ 35653

Last change on this file since 35653 was 35597, checked in by vboxsync, 14 years ago

Storage/tstVDIo: Build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.6 KB
Line 
1/** @file
2 *
3 * VBox HDD container test utility - I/O replay.
4 */
5
6/*
7 * Copyright (C) 2011 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#define LOGGROUP LOGGROUP_DEFAULT
18#include <VBox/vd.h>
19#include <VBox/err.h>
20#include <VBox/log.h>
21#include <iprt/asm.h>
22#include <iprt/string.h>
23#include <iprt/stream.h>
24#include <iprt/mem.h>
25#include <iprt/initterm.h>
26#include <iprt/getopt.h>
27#include <iprt/list.h>
28#include <iprt/ctype.h>
29#include <iprt/semaphore.h>
30#include <iprt/thread.h>
31
32#include "VDMemDisk.h"
33#include "VDIoBackendMem.h"
34#include "VDIoRnd.h"
35
36/**
37 * A virtual file backed by memory.
38 */
39typedef struct VDFILE
40{
41 /** Pointer to the next file. */
42 RTLISTNODE Node;
43 /** Name of the file. */
44 char *pszName;
45 /** Memory file baking the file. */
46 PVDMEMDISK pMemDisk;
47 /** Completion callback of the VD layer. */
48 PFNVDCOMPLETED pfnComplete;
49} VDFILE, *PVDFILE;
50
51/**
52 * Global VD test state.
53 */
54typedef struct VDTESTGLOB
55{
56 /** HDD handle to operate on. */
57 PVBOXHDD pVD;
58 /** Head of the active file list. */
59 RTLISTNODE ListFiles;
60 /** Memory I/O backend. */
61 PVDIOBACKENDMEM pIoBackend;
62 /** Error interface. */
63 VDINTERFACE VDIError;
64 /** Error interface callbacks. */
65 VDINTERFACEERROR VDIErrorCallbacks;
66 /** Pointer to the per disk interface list. */
67 PVDINTERFACE pInterfacesDisk;
68 /** I/O interface. */
69 VDINTERFACE VDIIo;
70 /** I/O interface callbacks. */
71 VDINTERFACEIO VDIIoCallbacks;
72 /** Pointer to the per image interface list. */
73 PVDINTERFACE pInterfacesImages;
74 /** Physical CHS Geometry. */
75 VDGEOMETRY PhysGeom;
76 /** Logical CHS geometry. */
77 VDGEOMETRY LogicalGeom;
78 /** I/O RNG handle. */
79 PVDIORND pIoRnd;
80} VDTESTGLOB, *PVDTESTGLOB;
81
82/**
83 * Transfer direction.
84 */
85typedef enum VDIOREQTXDIR
86{
87 VDIOREQTXDIR_READ = 0,
88 VDIOREQTXDIR_WRITE,
89 VDIOREQTXDIR_FLUSH
90} VDIOREQTXDIR;
91
92/**
93 * I/O request.
94 */
95typedef struct VDIOREQ
96{
97 /** Transfer type. */
98 VDIOREQTXDIR enmTxDir;
99 /** slot index. */
100 unsigned idx;
101 /** Start offset. */
102 uint64_t off;
103 /** Size to transfer. */
104 size_t cbReq;
105 /** S/G Buffer */
106 RTSGBUF SgBuf;
107 /** Data segment */
108 RTSGSEG DataSeg;
109 /** Flag whether the request is outstanding or not. */
110 volatile bool fOutstanding;
111} VDIOREQ, *PVDIOREQ;
112
113/**
114 * I/O test data.
115 */
116typedef struct VDIOTEST
117{
118 /** Start offset. */
119 uint64_t offStart;
120 /** End offset. */
121 uint64_t offEnd;
122 /** Flag whether random or sequential access is wanted */
123 bool fRandomAccess;
124 /** Block size. */
125 size_t cbBlkIo;
126 /** Number of bytes to transfer. */
127 size_t cbIo;
128 unsigned uWriteChance;
129 PVDIORND pIoRnd;
130 uint64_t offNext;
131} VDIOTEST, *PVDIOTEST;
132
133/**
134 * Argument types.
135 */
136typedef enum VDSCRIPTARGTYPE
137{
138 /** Argument is a string. */
139 VDSCRIPTARGTYPE_STRING = 0,
140 /** Argument is a 64bit unsigned number. */
141 VDSCRIPTARGTYPE_UNSIGNED_NUMBER,
142 /** Argument is a 64bit signed number. */
143 VDSCRIPTARGTYPE_SIGNED_NUMBER,
144 /** Arugment is a unsigned 64bit range */
145 VDSCRIPTARGTYPE_UNSIGNED_RANGE,
146 /** Arugment is a boolean. */
147 VDSCRIPTARGTYPE_BOOL
148} VDSCRIPTARGTYPE;
149
150/**
151 * Script argument.
152 */
153typedef struct VDSCRIPTARG
154{
155 /** Argument identifier. */
156 char chId;
157 /** Type of the argument. */
158 VDSCRIPTARGTYPE enmType;
159 /** Type depndent data. */
160 union
161 {
162 /** String. */
163 const char *pcszString;
164 /** Bool. */
165 bool fFlag;
166 /** unsigned number. */
167 uint64_t u64;
168 /** Signed number. */
169 int64_t i64;
170 /** Unsigned range. */
171 struct
172 {
173 uint64_t Start;
174 uint64_t End;
175 } Range;
176 } u;
177} VDSCRIPTARG, *PVDSCRIPTARG;
178
179/** Script action handler. */
180typedef DECLCALLBACK(int) FNVDSCRIPTACTION(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
181/** Pointer to a script action handler. */
182typedef FNVDSCRIPTACTION *PFNVDSCRIPTACTION;
183
184/**
185 * Script argument descriptor.
186 */
187typedef struct VDSCRIPTARGDESC
188{
189 /** Name of the arugment. */
190 const char *pcszName;
191 /** Identifier for the argument. */
192 char chId;
193 /** Type of the argument. */
194 VDSCRIPTARGTYPE enmType;
195 /** Flags */
196 uint32_t fFlags;
197} VDSCRIPTARGDESC, *PVDSCRIPTARGDESC;
198/** Pointer to a const script argument descriptor. */
199typedef const VDSCRIPTARGDESC *PCVDSCRIPTARGDESC;
200
201/** Flag whether the argument is mandatory. */
202#define VDSCRIPTARGDESC_FLAG_MANDATORY RT_BIT(0)
203/** Flag whether the number can have a size suffix (K|M|G) */
204#define VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX RT_BIT(1)
205
206/**
207 * Script action.
208 */
209typedef struct VDSCRIPTACTION
210{
211 /** Action name. */
212 const char *pcszAction;
213 /** Pointer to the arguments. */
214 const PCVDSCRIPTARGDESC paArgDesc;
215 /** Number of arugments in the array. */
216 unsigned cArgDescs;
217 /** Pointer to the action handler. */
218 PFNVDSCRIPTACTION pfnHandler;
219} VDSCRIPTACTION, *PVDSCRIPTACTION;
220
221typedef const VDSCRIPTACTION *PCVDSCRIPTACTION;
222
223static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
224static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
225static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
226static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
227static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
228static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
229static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
230static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
231static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
232
233/* create action */
234const VDSCRIPTARGDESC g_aArgCreate[] =
235{
236 /* pcszName chId enmType fFlags */
237 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
238 {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
239 {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
240 {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
241};
242
243/* open action */
244const VDSCRIPTARGDESC g_aArgOpen[] =
245{
246 /* pcszName chId enmType fFlags */
247 {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
248 {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
249};
250
251/* write action */
252const VDSCRIPTARGDESC g_aArgIo[] =
253{
254 /* pcszName chId enmType fFlags */
255 {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0},
256 {"max-reqs", 'l', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0},
257 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
258 {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
259 {"blocksize", 'b', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
260 {"off", 'o', VDSCRIPTARGTYPE_UNSIGNED_RANGE, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
261 {"reads", 'r', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
262 {"writes", 'w', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}
263};
264
265/* flush action */
266const VDSCRIPTARGDESC g_aArgFlush[] =
267{
268 /* pcszName chId enmType fFlags */
269 {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0}
270};
271
272/* merge action */
273const VDSCRIPTARGDESC g_aArgMerge[] =
274{
275 /* pcszName chId enmType fFlags */
276 {"forward", 'f', VDSCRIPTARGTYPE_BOOL, VDSCRIPTARGDESC_FLAG_MANDATORY}
277};
278
279/* close action */
280const VDSCRIPTARGDESC g_aArgClose[] =
281{
282 /* pcszName chId enmType fFlags */
283 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
284 {"delete", 'd', VDSCRIPTARGTYPE_BOOL, VDSCRIPTARGDESC_FLAG_MANDATORY}
285};
286
287/* I/O RNG create action */
288const VDSCRIPTARGDESC g_aArgIoRngCreate[] =
289{
290 /* pcszName chId enmType fFlags */
291 {"size", 'd', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
292 {"seed", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}
293};
294
295/* Sleep */
296const VDSCRIPTARGDESC g_aArgSleep[] =
297{
298 /* pcszName chId enmType fFlags */
299 {"time", 't', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
300};
301
302const VDSCRIPTACTION g_aScriptActions[] =
303{
304 /* pcszAction paArgDesc cArgDescs pfnHandler */
305 {"create", g_aArgCreate, RT_ELEMENTS(g_aArgCreate), vdScriptHandlerCreate},
306 {"open", g_aArgOpen, RT_ELEMENTS(g_aArgOpen), vdScriptHandlerOpen},
307 {"io", g_aArgIo, RT_ELEMENTS(g_aArgIo), vdScriptHandlerIo},
308 {"flush", g_aArgFlush, RT_ELEMENTS(g_aArgFlush), vdScriptHandlerFlush},
309 {"close", g_aArgClose, RT_ELEMENTS(g_aArgClose), vdScriptHandlerClose},
310 {"merge", g_aArgMerge, RT_ELEMENTS(g_aArgMerge), vdScriptHandlerMerge},
311 {"iorngcreate", g_aArgIoRngCreate, RT_ELEMENTS(g_aArgIoRngCreate), vdScriptHandlerIoRngCreate},
312 {"iorngdestroy", NULL, 0, vdScriptHandlerIoRngDestroy},
313 {"sleep", g_aArgSleep, RT_ELEMENTS(g_aArgSleep), vdScriptHandlerSleep},
314};
315
316const unsigned g_cScriptActions = RT_ELEMENTS(g_aScriptActions);
317
318static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
319 const char *pszFormat, va_list va)
320{
321 RTPrintf("tstVD: Error %Rrc at %s:%u (%s): ", rc, RT_SRC_POS_ARGS);
322 RTPrintfV(pszFormat, va);
323 RTPrintf("\n");
324}
325
326static int tstVDMessage(void *pvUser, const char *pszFormat, va_list va)
327{
328 RTPrintf("tstVD: ");
329 RTPrintfV(pszFormat, va);
330 return VINF_SUCCESS;
331}
332
333static int tstVDIoTestInit(PVDIOTEST pIoTest, PVDTESTGLOB pGlob, bool fRandomAcc, size_t cbIo,
334 size_t cbBlkSize, uint64_t offStart, uint64_t offEnd,
335 unsigned uWriteChance, unsigned uReadChance);
336static bool tstVDIoTestRunning(PVDIOTEST pIoTest);
337static bool tstVDIoTestReqOutstanding(PVDIOREQ pIoReq);
338static int tstVDIoTestReqInit(PVDIOTEST pIoTest, PVDIOREQ pIoReq);
339static void tstVDIoTestReqComplete(void *pvUser1, void *pvUser2, int rcReq);
340
341static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
342{
343 int rc = VINF_SUCCESS;
344 uint64_t cbSize = 0;
345 const char *pcszBackend = NULL;
346 const char *pcszImage = NULL;
347 bool fBase = false;
348
349 for (unsigned i = 0; i < cScriptArgs; i++)
350 {
351 switch (paScriptArgs[i].chId)
352 {
353 case 'm':
354 {
355 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "base"))
356 fBase = true;
357 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "diff"))
358 fBase = false;
359 else
360 {
361 RTPrintf("Invalid image mode '%s' given\n", paScriptArgs[i].u.pcszString);
362 rc = VERR_INVALID_PARAMETER;
363 }
364 break;
365 }
366 case 'n':
367 {
368 pcszImage = paScriptArgs[i].u.pcszString;
369 break;
370 }
371 case 'b':
372 {
373 pcszBackend = paScriptArgs[i].u.pcszString;
374 break;
375 }
376 case 's':
377 {
378 cbSize = paScriptArgs[i].u.u64;
379 break;
380 }
381 default:
382 AssertMsgFailed(("Invalid argument given!\n"));
383 }
384
385 if (RT_FAILURE(rc))
386 break;
387 }
388
389 if (RT_SUCCESS(rc))
390 {
391 if (fBase)
392 rc = VDCreateBase(pGlob->pVD, pcszBackend, pcszImage, cbSize, 0, NULL,
393 &pGlob->PhysGeom, &pGlob->LogicalGeom,
394 NULL, VD_OPEN_FLAGS_ASYNC_IO, pGlob->pInterfacesImages, NULL);
395 else
396 rc = VDCreateDiff(pGlob->pVD, pcszBackend, pcszImage, 0, NULL, NULL, NULL, VD_OPEN_FLAGS_ASYNC_IO,
397 pGlob->pInterfacesImages, NULL);
398 }
399
400 return rc;
401}
402
403static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
404{
405 int rc = VINF_SUCCESS;
406 const char *pcszBackend = NULL;
407 const char *pcszImage = NULL;
408
409 for (unsigned i = 0; i < cScriptArgs; i++)
410 {
411 switch (paScriptArgs[i].chId)
412 {
413 case 'n':
414 {
415 pcszImage = paScriptArgs[i].u.pcszString;
416 break;
417 }
418 case 'b':
419 {
420 pcszBackend = paScriptArgs[i].u.pcszString;
421 break;
422 }
423 default:
424 AssertMsgFailed(("Invalid argument given!\n"));
425 }
426
427 if (RT_FAILURE(rc))
428 break;
429 }
430
431 if (RT_SUCCESS(rc))
432 {
433 rc = VDOpen(pGlob->pVD, pcszBackend, pcszImage, VD_OPEN_FLAGS_ASYNC_IO, pGlob->pInterfacesImages);
434 }
435
436 return rc;
437}
438
439static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
440{
441 int rc = VINF_SUCCESS;
442 bool fAsync = false;
443 bool fRandomAcc = false;
444 uint64_t cbIo = 0;
445 uint64_t cbBlkSize = 0;
446 bool fDataProviderRnd = false;
447 bool fPrintStats = false;
448 uint64_t offStart = 0;
449 uint64_t offEnd = 0;
450 unsigned cMaxReqs = 0;
451 uint8_t uWriteChance = 0;
452 uint8_t uReadChance = 0;
453
454 offEnd = VDGetSize(pGlob->pVD, VD_LAST_IMAGE);
455 if (offEnd == 0)
456 return VERR_INVALID_STATE;
457 cbIo = offEnd;
458
459 for (unsigned i = 0; i < cScriptArgs; i++)
460 {
461 switch (paScriptArgs[i].chId)
462 {
463 case 'a':
464 {
465 fAsync = paScriptArgs[i].u.fFlag;
466 break;
467 }
468 case 'l':
469 {
470 cMaxReqs = paScriptArgs[i].u.u64;
471 break;
472 }
473 case 'm':
474 {
475 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "seq"))
476 fRandomAcc = false;
477 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "rnd"))
478 fRandomAcc = true;
479 else
480 {
481 RTPrintf("Invalid access mode '%s'\n", paScriptArgs[i].u.pcszString);
482 rc = VERR_INVALID_PARAMETER;
483 }
484 break;
485 }
486 case 's':
487 {
488 cbIo = paScriptArgs[i].u.u64;
489 break;
490 }
491 case 'b':
492 {
493 cbBlkSize = paScriptArgs[i].u.u64;
494 break;
495 }
496 case 'o':
497 {
498 offStart = paScriptArgs[i].u.Range.Start;
499 offEnd = paScriptArgs[i].u.Range.End;
500 break;
501 }
502 case 'r':
503 {
504 uReadChance = (uint8_t)paScriptArgs[i].u.u64;
505 break;
506 }
507 case 'w':
508 {
509 uWriteChance = (uint8_t)paScriptArgs[i].u.u64;
510 break;
511 }
512 default:
513 AssertMsgFailed(("Invalid argument given!\n"));
514 }
515
516 if (RT_FAILURE(rc))
517 break;
518 }
519
520 if (RT_SUCCESS(rc))
521 {
522 VDIOTEST IoTest;
523
524 rc = tstVDIoTestInit(&IoTest, pGlob, fRandomAcc, cbIo, cbBlkSize, offStart, offEnd, uWriteChance, uReadChance);
525 if (RT_SUCCESS(rc))
526 {
527 PVDIOREQ paIoReq = NULL;
528 unsigned cMaxTasksOutstanding = fAsync ? cMaxReqs : 1;
529 RTSEMEVENT EventSem;
530
531 rc = RTSemEventCreate(&EventSem);
532 paIoReq = (PVDIOREQ)RTMemAllocZ(cMaxTasksOutstanding * sizeof(VDIOREQ));
533 if (paIoReq && RT_SUCCESS(rc))
534 {
535 for (unsigned i = 0; i < cMaxTasksOutstanding; i++)
536 paIoReq[i].idx = i;
537
538 while (tstVDIoTestRunning(&IoTest))
539 {
540 bool fTasksOutstanding = false;
541 unsigned idx = 0;
542
543 /* Submit all idling requests. */
544 while ( idx < cMaxTasksOutstanding
545 && tstVDIoTestRunning(&IoTest))
546 {
547 if (!tstVDIoTestReqOutstanding(&paIoReq[idx]))
548 {
549 rc = tstVDIoTestReqInit(&IoTest, &paIoReq[idx]);
550 AssertRC(rc);
551
552 if (RT_SUCCESS(rc))
553 {
554 if (!fAsync)
555 {
556 switch (paIoReq[idx].enmTxDir)
557 {
558 case VDIOREQTXDIR_READ:
559 {
560 rc = VDRead(pGlob->pVD, paIoReq[idx].off, paIoReq[idx].DataSeg.pvSeg, paIoReq[idx].cbReq);
561 RTMemFree(paIoReq[idx].DataSeg.pvSeg);
562 break;
563 }
564 case VDIOREQTXDIR_WRITE:
565 {
566 rc = VDWrite(pGlob->pVD, paIoReq[idx].off, paIoReq[idx].DataSeg.pvSeg, paIoReq[idx].cbReq);
567 break;
568 }
569 case VDIOREQTXDIR_FLUSH:
570 {
571 rc = VDFlush(pGlob->pVD);
572 break;
573 }
574 }
575 if (RT_SUCCESS(rc))
576 idx++;
577 }
578 else
579 {
580 switch (paIoReq[idx].enmTxDir)
581 {
582 case VDIOREQTXDIR_READ:
583 {
584 rc = VDAsyncRead(pGlob->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
585 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
586 if (rc == VINF_VD_ASYNC_IO_FINISHED)
587 RTMemFree(paIoReq[idx].DataSeg.pvSeg);
588 break;
589 }
590 case VDIOREQTXDIR_WRITE:
591 {
592 rc = VDAsyncWrite(pGlob->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
593 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
594 break;
595 }
596 case VDIOREQTXDIR_FLUSH:
597 {
598 rc = VDAsyncFlush(pGlob->pVD, tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
599 break;
600 }
601 }
602
603 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
604 {
605 idx++;
606 fTasksOutstanding = true;
607 rc = VINF_SUCCESS;
608 }
609 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
610 {
611 ASMAtomicXchgBool(&paIoReq[idx].fOutstanding, false);
612 rc = VINF_SUCCESS;
613 }
614 }
615
616 if (RT_FAILURE(rc))
617 RTPrintf("Error submitting task %u rc=%Rrc\n", paIoReq[idx].idx, rc);
618 }
619 }
620 }
621
622 /* Wait for a request to complete. */
623 if ( fAsync
624 && fTasksOutstanding)
625 {
626 rc = RTSemEventWait(EventSem, RT_INDEFINITE_WAIT);
627 AssertRC(rc);
628 }
629 }
630
631 /* Cleanup, wait for all tasks to complete. */
632 while (fAsync)
633 {
634 unsigned idx = 0;
635 bool fAllIdle = true;
636
637 while (idx < cMaxTasksOutstanding)
638 {
639 if (tstVDIoTestReqOutstanding(&paIoReq[idx]))
640 {
641 fAllIdle = false;
642 break;
643 }
644 idx++;
645 }
646
647 if (!fAllIdle)
648 {
649 rc = RTSemEventWait(EventSem, 100);
650 Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT);
651 }
652 else
653 break;
654 }
655
656 RTSemEventDestroy(EventSem);
657 RTMemFree(paIoReq);
658 }
659 else
660 rc = VERR_NO_MEMORY;
661 }
662 }
663
664 return rc;
665}
666
667static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
668{
669 int rc = VINF_SUCCESS;
670 bool fAsync = false;
671
672 if (cScriptArgs == 1 && paScriptArgs[0].chId == 'a')
673 fAsync = paScriptArgs[0].u.fFlag;
674
675 if (fAsync)
676 {
677 /** @todo */
678 rc = VERR_NOT_IMPLEMENTED;
679 }
680 else
681 rc = VDFlush(pGlob->pVD);
682
683 return rc;
684}
685
686static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
687{
688 return VERR_NOT_IMPLEMENTED;
689}
690
691static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
692{
693 int rc = VINF_SUCCESS;
694 bool fAll = false;
695 bool fDelete = false;
696
697 for (unsigned i = 0; i < cScriptArgs; i++)
698 {
699 switch (paScriptArgs[i].chId)
700 {
701 case 'm':
702 {
703 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "all"))
704 fAll = true;
705 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "single"))
706 fAll = false;
707 else
708 {
709 RTPrintf("Invalid mode '%s' given\n", paScriptArgs[i].u.pcszString);
710 rc = VERR_INVALID_PARAMETER;
711 }
712 break;
713 }
714 case 'd':
715 {
716 fDelete = paScriptArgs[i].u.fFlag;
717 break;
718 }
719 default:
720 AssertMsgFailed(("Invalid argument given!\n"));
721 }
722
723 if (RT_FAILURE(rc))
724 break;
725 }
726
727 if ( RT_SUCCESS(rc)
728 && fAll
729 && fDelete)
730 {
731 RTPrintf("mode=all doesn't work with delete=yes\n");
732 rc = VERR_INVALID_PARAMETER;
733 }
734
735 if (RT_SUCCESS(rc))
736 {
737 if (fAll)
738 rc = VDCloseAll(pGlob->pVD);
739 else
740 rc = VDClose(pGlob->pVD, fDelete);
741 }
742 return rc;
743}
744
745
746static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
747{
748 int rc = VINF_SUCCESS;
749 size_t cbPattern = 0;
750 uint64_t uSeed = 0;
751
752 for (unsigned i = 0; i < cScriptArgs; i++)
753 {
754 switch (paScriptArgs[i].chId)
755 {
756 case 'd':
757 {
758 cbPattern = paScriptArgs[i].u.u64;
759 break;
760 }
761 case 's':
762 {
763 uSeed = paScriptArgs[i].u.u64;
764 break;
765 }
766 default:
767 AssertMsgFailed(("Invalid argument given!\n"));
768 }
769 }
770
771 if (pGlob->pIoRnd)
772 {
773 RTPrintf("I/O RNG already exists\n");
774 rc = VERR_INVALID_STATE;
775 }
776 else
777 rc = VDIoRndCreate(&pGlob->pIoRnd, cbPattern, uSeed);
778
779 return rc;
780}
781
782static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
783{
784 if (pGlob->pIoRnd)
785 {
786 VDIoRndDestroy(pGlob->pIoRnd);
787 pGlob->pIoRnd = NULL;
788 }
789 else
790 RTPrintf("WARNING: No I/O RNG active, faulty script. Continuing\n");
791
792 return VINF_SUCCESS;
793}
794
795static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
796{
797 int rc = VINF_SUCCESS;
798 uint64_t cMillies = 0;
799
800 for (unsigned i = 0; i < cScriptArgs; i++)
801 {
802 switch (paScriptArgs[i].chId)
803 {
804 case 't':
805 {
806 cMillies = paScriptArgs[i].u.u64;
807 break;
808 }
809 default:
810 AssertMsgFailed(("Invalid argument given!\n"));
811 }
812 }
813
814 rc = RTThreadSleep(cMillies);
815 return rc;
816}
817
818static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
819 uint32_t fOpen,
820 PFNVDCOMPLETED pfnCompleted,
821 void **ppStorage)
822{
823 int rc = VINF_SUCCESS;
824 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
825 bool fFound = false;
826
827 /* Check if the file exists. */
828 PVDFILE pIt = NULL;
829 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
830 {
831 if (!RTStrCmp(pIt->pszName, pszLocation))
832 {
833 fFound = true;
834 break;
835 }
836 }
837
838 if (fFound && pIt->pfnComplete)
839 rc = VERR_FILE_LOCK_FAILED;
840 else if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE)
841 {
842 /* If the file exists delete the memory disk. */
843 if (fFound)
844 rc = VDMemDiskSetSize(pIt->pMemDisk, 0);
845 else
846 {
847 /* Create completey new. */
848 pIt = (PVDFILE)RTMemAllocZ(sizeof(VDFILE));
849 if (pIt)
850 {
851 pIt->pfnComplete = pfnCompleted;
852 pIt->pszName = RTStrDup(pszLocation);
853
854 if (pIt->pszName)
855 {
856 rc = VDMemDiskCreate(&pIt->pMemDisk, 0);
857 }
858 else
859 rc = VERR_NO_MEMORY;
860
861 if (RT_FAILURE(rc))
862 {
863 if (pIt->pszName)
864 RTStrFree(pIt->pszName);
865 RTMemFree(pIt);
866 }
867 }
868 else
869 rc = VERR_NO_MEMORY;
870
871 RTListAppend(&pGlob->ListFiles, &pIt->Node);
872 }
873 }
874 else if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN)
875 {
876 if (!fFound)
877 rc = VERR_FILE_NOT_FOUND;
878 else
879 pIt->pfnComplete = pfnCompleted;
880 }
881 else
882 rc = VERR_INVALID_PARAMETER;
883
884 if (RT_SUCCESS(rc))
885 {
886 AssertPtr(pIt);
887 *ppStorage = pIt;
888 }
889
890 return rc;
891}
892
893static DECLCALLBACK(int) tstVDIoFileClose(void *pvUser, void *pStorage)
894{
895 PVDFILE pFile = (PVDFILE)pStorage;
896
897 /* Mark as not busy. */
898 pFile->pfnComplete = NULL;
899 return VINF_SUCCESS;
900}
901
902static DECLCALLBACK(int) tstVDIoFileDelete(void *pvUser, const char *pcszFilename)
903{
904 int rc = VINF_SUCCESS;
905 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
906 bool fFound = false;
907
908 /* Check if the file exists. */
909 PVDFILE pIt = NULL;
910 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
911 {
912 if (!RTStrCmp(pIt->pszName, pcszFilename))
913 {
914 fFound = true;
915 break;
916 }
917 }
918
919 if (fFound)
920 {
921 RTListNodeRemove(&pIt->Node);
922 VDMemDiskDestroy(pIt->pMemDisk);
923 RTStrFree(pIt->pszName);
924 RTMemFree(pIt);
925 }
926 else
927 rc = VERR_FILE_NOT_FOUND;
928
929 return rc;
930}
931
932static DECLCALLBACK(int) tstVDIoFileMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
933{
934 int rc = VINF_SUCCESS;
935 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
936 bool fFound = false;
937
938 /* Check if the file exists. */
939 PVDFILE pIt = NULL;
940 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
941 {
942 if (!RTStrCmp(pIt->pszName, pcszSrc))
943 {
944 fFound = true;
945 break;
946 }
947 }
948
949 if (fFound)
950 {
951 char *pszNew = RTStrDup(pcszDst);
952 if (pszNew)
953 {
954 RTStrFree(pIt->pszName);
955 pIt->pszName = pszNew;
956 }
957 else
958 rc = VERR_NO_MEMORY;
959 }
960 else
961 rc = VERR_FILE_NOT_FOUND;
962
963 return rc;
964}
965
966static DECLCALLBACK(int) tstVDIoFileGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
967{
968 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
969
970 *pcbFreeSpace = ~0ULL; /** @todo: Implement */
971 return VINF_SUCCESS;
972}
973
974static DECLCALLBACK(int) tstVDIoFileGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
975{
976 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
977
978 /** @todo: Implement */
979 return VINF_SUCCESS;
980}
981
982static DECLCALLBACK(int) tstVDIoFileGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
983{
984 PVDFILE pFile = (PVDFILE)pStorage;
985
986 return VDMemDiskGetSize(pFile->pMemDisk, pcbSize);
987}
988
989static DECLCALLBACK(int) tstVDIoFileSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
990{
991 PVDFILE pFile = (PVDFILE)pStorage;
992
993 return VDMemDiskSetSize(pFile->pMemDisk, cbSize);
994}
995
996static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
997 const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
998{
999 int rc = VINF_SUCCESS;
1000 PVDFILE pFile = (PVDFILE)pStorage;
1001
1002 RTSGBUF SgBuf;
1003 RTSGSEG Seg;
1004
1005 Seg.pvSeg = (void *)pvBuffer;
1006 Seg.cbSeg = cbBuffer;
1007 RTSgBufInit(&SgBuf, &Seg, 1);
1008 rc = VDMemDiskWrite(pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
1009 if (RT_SUCCESS(rc) && pcbWritten)
1010 *pcbWritten = cbBuffer;
1011
1012 return rc;
1013}
1014
1015static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
1016 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
1017{
1018 int rc = VINF_SUCCESS;
1019 PVDFILE pFile = (PVDFILE)pStorage;
1020
1021 RTSGBUF SgBuf;
1022 RTSGSEG Seg;
1023
1024 Seg.pvSeg = pvBuffer;
1025 Seg.cbSeg = cbBuffer;
1026 RTSgBufInit(&SgBuf, &Seg, 1);
1027 rc = VDMemDiskRead(pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
1028 if (RT_SUCCESS(rc) && pcbRead)
1029 *pcbRead = cbBuffer;
1030
1031 return rc;
1032}
1033
1034static DECLCALLBACK(int) tstVDIoFileFlushSync(void *pvUser, void *pStorage)
1035{
1036 /* nothing to do. */
1037 return VINF_SUCCESS;
1038}
1039
1040static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
1041 PCRTSGSEG paSegments, size_t cSegments,
1042 size_t cbRead, void *pvCompletion,
1043 void **ppTask)
1044{
1045 int rc = VINF_SUCCESS;
1046 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1047 PVDFILE pFile = (PVDFILE)pStorage;
1048
1049 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pFile->pMemDisk, VDIOTXDIR_READ, uOffset,
1050 cbRead, paSegments, cSegments, pFile->pfnComplete, pvCompletion);
1051 if (RT_SUCCESS(rc))
1052 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1053
1054 return rc;
1055}
1056
1057static DECLCALLBACK(int) tstVDIoFileWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
1058 PCRTSGSEG paSegments, size_t cSegments,
1059 size_t cbWrite, void *pvCompletion,
1060 void **ppTask)
1061{
1062 int rc = VINF_SUCCESS;
1063 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1064 PVDFILE pFile = (PVDFILE)pStorage;
1065
1066 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pFile->pMemDisk, VDIOTXDIR_WRITE, uOffset,
1067 cbWrite, paSegments, cSegments, pFile->pfnComplete, pvCompletion);
1068 if (RT_SUCCESS(rc))
1069 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1070
1071 return rc;
1072}
1073
1074static DECLCALLBACK(int) tstVDIoFileFlushAsync(void *pvUser, void *pStorage, void *pvCompletion,
1075 void **ppTask)
1076{
1077 int rc = VINF_SUCCESS;
1078 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1079 PVDFILE pFile = (PVDFILE)pStorage;
1080
1081 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pFile->pMemDisk, VDIOTXDIR_FLUSH, 0,
1082 0, NULL, 0, pFile->pfnComplete, pvCompletion);
1083 if (RT_SUCCESS(rc))
1084 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1085
1086 return rc;
1087}
1088
1089static int tstVDIoTestInit(PVDIOTEST pIoTest, PVDTESTGLOB pGlob, bool fRandomAcc, size_t cbIo,
1090 size_t cbBlkSize, uint64_t offStart, uint64_t offEnd,
1091 unsigned uWriteChance, unsigned uReadChance)
1092{
1093 pIoTest->fRandomAccess = fRandomAcc;
1094 pIoTest->cbIo = cbIo;
1095 pIoTest->cbBlkIo = cbBlkSize;
1096 pIoTest->offStart = offStart;
1097 pIoTest->offEnd = offEnd;
1098 pIoTest->uWriteChance = uWriteChance;
1099 pIoTest->pIoRnd = pGlob->pIoRnd;
1100 pIoTest->offNext = pIoTest->offEnd < pIoTest->offStart ? pIoTest->offEnd - cbBlkSize : 0;
1101 return VINF_SUCCESS;
1102}
1103
1104static bool tstVDIoTestRunning(PVDIOTEST pIoTest)
1105{
1106 return pIoTest->cbIo > 0;
1107}
1108
1109static bool tstVDIoTestReqOutstanding(PVDIOREQ pIoReq)
1110{
1111 return pIoReq->fOutstanding;
1112}
1113
1114/**
1115 * Returns true with the given chance in percent.
1116 *
1117 * @returns true or false
1118 * @param iPercentage The percentage of the chance to return true.
1119 */
1120static bool tstVDIoTestIsTrue(PVDIOTEST pIoTest, int iPercentage)
1121{
1122 int uRnd = VDIoRndGetU32Ex(pIoTest->pIoRnd, 0, 100);
1123
1124 return (uRnd <= iPercentage); /* This should be enough for our purpose */
1125}
1126
1127static int tstVDIoTestReqInit(PVDIOTEST pIoTest, PVDIOREQ pIoReq)
1128{
1129 int rc = VINF_SUCCESS;
1130
1131 if (pIoTest->cbIo)
1132 {
1133 /* Read or Write? */
1134 pIoReq->enmTxDir = tstVDIoTestIsTrue(pIoTest, pIoTest->uWriteChance) ? VDIOREQTXDIR_WRITE : VDIOREQTXDIR_READ;
1135 pIoReq->cbReq = RT_MIN(pIoTest->cbBlkIo, pIoTest->cbIo);
1136 pIoTest->cbIo -= pIoReq->cbReq;
1137 pIoReq->DataSeg.cbSeg = pIoReq->cbReq;
1138 pIoReq->off = pIoTest->offNext;
1139
1140 if (pIoReq->enmTxDir == VDIOREQTXDIR_WRITE)
1141 {
1142 rc = VDIoRndGetBuffer(pIoTest->pIoRnd, &pIoReq->DataSeg.pvSeg, pIoReq->cbReq);
1143 AssertRC(rc);
1144 }
1145 else
1146 {
1147 /* Read */
1148 pIoReq->DataSeg.pvSeg = RTMemAlloc(pIoReq->cbReq);
1149 if (!pIoReq->DataSeg.pvSeg)
1150 rc = VERR_NO_MEMORY;
1151 }
1152
1153 if (RT_SUCCESS(rc))
1154 {
1155 RTSgBufInit(&pIoReq->SgBuf, &pIoReq->DataSeg, 1);
1156
1157 if (pIoTest->fRandomAccess)
1158 {
1159 /** @todo */
1160 }
1161 else
1162 {
1163 pIoTest->offNext = pIoTest->offEnd < pIoTest->offStart
1164 ? RT_MAX(pIoTest->offEnd, pIoTest->offNext - pIoTest->cbBlkIo)
1165 : RT_MIN(pIoTest->offEnd, pIoTest->offNext + pIoTest->cbBlkIo);
1166 }
1167 pIoReq->fOutstanding = true;
1168 }
1169 }
1170 else
1171 rc = VERR_ACCESS_DENIED;
1172
1173 return rc;
1174}
1175
1176static void tstVDIoTestReqComplete(void *pvUser1, void *pvUser2, int rcReq)
1177{
1178 PVDIOREQ pIoReq = (PVDIOREQ)pvUser1;
1179 RTSEMEVENT hEventSem = (RTSEMEVENT)pvUser2;
1180
1181 ASMAtomicXchgBool(&pIoReq->fOutstanding, false);
1182 RTSemEventSignal(hEventSem);
1183 return;
1184}
1185
1186/**
1187 * Skips the characters until the given character is reached.
1188 *
1189 * @returns Start of the string with the given character
1190 * or NULL if the string ended before.
1191 *
1192 * @param psz The string to skip.
1193 * @param ch The character.
1194 */
1195static char *tstVDIoScriptSkipUntil(char *psz, char ch)
1196{
1197 while ( *psz != '\0'
1198 && *psz != ch)
1199 psz++;
1200
1201 return psz;
1202}
1203
1204/**
1205 * Skips the spaces of the current string.
1206 *
1207 * @returns Start of the string with a non space character
1208 * or NULL if the string ended before.
1209 *
1210 * @param psz The string to skip.
1211 */
1212static char *tstVDIoScriptSkipSpace(char *psz)
1213{
1214 while ( *psz != '\0'
1215 && RT_C_IS_SPACE(*psz))
1216 psz++;
1217
1218 return psz;
1219}
1220
1221/**
1222 * Skips all characters until a space is reached of the current
1223 * string.
1224 *
1225 * @returns Start of the string with a space character
1226 * or NULL if the string ended before.
1227 *
1228 * @param psz The string to skip.
1229 */
1230static char *tstVDIoScriptSkipNonSpace(char *psz)
1231{
1232 while ( *psz != '\0'
1233 && !RT_C_IS_SPACE(*psz))
1234 psz++;
1235
1236 return psz;
1237}
1238
1239/**
1240 * Parses one argument name, value pair.
1241 *
1242 * @returns IPRT status code.
1243 *
1244 * @param pVDScriptAction Script action.
1245 * @param pcszName Argument name.
1246 * @param pcszValue Argument value.
1247 * @param pScriptArg Where to fill in the parsed
1248 * argument.
1249 * @param pfMandatory Where to store whether the argument
1250 * is mandatory.
1251 */
1252static int tstVDIoScriptArgumentParse(PCVDSCRIPTACTION pVDScriptAction, const char *pcszName,
1253 const char *pcszValue, PVDSCRIPTARG pScriptArg, bool *pfMandatory)
1254{
1255 int rc = VERR_NOT_FOUND;
1256
1257 for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
1258 {
1259 if (!RTStrCmp(pVDScriptAction->paArgDesc[i].pcszName, pcszName))
1260 {
1261 rc = VINF_SUCCESS;
1262
1263 switch (pVDScriptAction->paArgDesc[i].enmType)
1264 {
1265 case VDSCRIPTARGTYPE_BOOL:
1266 {
1267 pScriptArg->enmType = VDSCRIPTARGTYPE_BOOL;
1268 if (!RTStrICmp(pcszValue, "yes") || !RTStrICmp(pcszValue, "on"))
1269 pScriptArg->u.fFlag = true;
1270 else if (!RTStrICmp(pcszValue, "no") || !RTStrICmp(pcszValue, "off"))
1271 pScriptArg->u.fFlag = false;
1272 else
1273 {
1274 RTPrintf("Boolean argument malformed '%s'\n", pcszValue);
1275 rc = VERR_INVALID_PARAMETER;
1276 }
1277 break;
1278 }
1279 case VDSCRIPTARGTYPE_SIGNED_NUMBER:
1280 {
1281 pScriptArg->enmType = VDSCRIPTARGTYPE_SIGNED_NUMBER;
1282 AssertMsgFailed(("todo\n"));
1283 break;
1284 }
1285 case VDSCRIPTARGTYPE_STRING:
1286 {
1287 pScriptArg->enmType = VDSCRIPTARGTYPE_STRING;
1288 pScriptArg->u.pcszString = pcszValue;
1289 break;
1290 }
1291 case VDSCRIPTARGTYPE_UNSIGNED_NUMBER:
1292 {
1293 char *pszSuffix = NULL;
1294
1295 pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_NUMBER;
1296 rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.u64);
1297 if (rc == VWRN_TRAILING_CHARS)
1298 {
1299 switch (*pszSuffix)
1300 {
1301 case 'k':
1302 case 'K':
1303 {
1304 pScriptArg->u.u64 *= _1K;
1305 break;
1306 }
1307 case 'm':
1308 case 'M':
1309 {
1310 pScriptArg->u.u64 *= _1M;
1311 break;
1312 }
1313 case 'g':
1314 case 'G':
1315 {
1316 pScriptArg->u.u64 *= _1G;
1317 break;
1318 }
1319 default:
1320 {
1321 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
1322 rc = VERR_INVALID_PARAMETER;
1323 }
1324 }
1325 if (rc != VERR_INVALID_PARAMETER)
1326 rc = VINF_SUCCESS;
1327 }
1328
1329 break;
1330 }
1331 case VDSCRIPTARGTYPE_UNSIGNED_RANGE:
1332 {
1333 char *pszSuffix = NULL;
1334
1335 pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_RANGE;
1336 rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.Range.Start);
1337 if (rc == VWRN_TRAILING_CHARS)
1338 {
1339 if (*pszSuffix != '-')
1340 {
1341 switch (*pszSuffix)
1342 {
1343 case 'k':
1344 case 'K':
1345 {
1346 pScriptArg->u.u64 *= _1K;
1347 break;
1348 }
1349 case 'm':
1350 case 'M':
1351 {
1352 pScriptArg->u.u64 *= _1M;
1353 break;
1354 }
1355 case 'g':
1356 case 'G':
1357 {
1358 pScriptArg->u.u64 *= _1G;
1359 break;
1360 }
1361 default:
1362 {
1363 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
1364 rc = VERR_INVALID_PARAMETER;
1365 }
1366 }
1367 }
1368
1369 if (*pszSuffix == '-')
1370 {
1371 pszSuffix++;
1372 rc = RTStrToUInt64Ex(pszSuffix, &pszSuffix, 10, &pScriptArg->u.Range.End);
1373 if (rc == VWRN_TRAILING_CHARS)
1374 {
1375 switch (*pszSuffix)
1376 {
1377 case 'k':
1378 case 'K':
1379 {
1380 pScriptArg->u.Range.End *= _1K;
1381 break;
1382 }
1383 case 'm':
1384 case 'M':
1385 {
1386 pScriptArg->u.Range.End *= _1M;
1387 break;
1388 }
1389 case 'g':
1390 case 'G':
1391 {
1392 pScriptArg->u.Range.End *= _1G;
1393 break;
1394 }
1395 default:
1396 {
1397 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
1398 rc = VERR_INVALID_PARAMETER;
1399 }
1400 }
1401 }
1402 }
1403 else
1404 rc = VERR_INVALID_PARAMETER;
1405 }
1406 else
1407 rc = VERR_INVALID_PARAMETER;
1408
1409 if (rc == VERR_INVALID_PARAMETER)
1410 RTPrintf("Invalid range format\n");
1411 break;
1412 }
1413 default:
1414 AssertMsgFailed(("Invalid script argument type\n"));
1415 }
1416
1417 if (RT_SUCCESS(rc))
1418 {
1419 pScriptArg->chId = pVDScriptAction->paArgDesc[i].chId;
1420 *pfMandatory = !!(pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY);
1421 }
1422 break;
1423 }
1424 }
1425
1426 if (rc == VERR_NOT_FOUND)
1427 RTPrintf("Argument '%s' not found\n", pcszName);
1428
1429 return rc;
1430}
1431
1432/**
1433 * Parses the arguments of a action in the script.
1434 *
1435 * @returns IPRT status code.
1436 *
1437 * @param psz Argument string.
1438 * @param pVDScriptAction The script action to parses
1439 * arguments for.
1440 * @param paScriptArgs Where to store the arguments.
1441 * @param pcScriptArgs Where to store the actual number of
1442 * arguments parsed.
1443 */
1444static int tstVDIoScriptArgumentListParse(char *psz, PCVDSCRIPTACTION pVDScriptAction, PVDSCRIPTARG paScriptArgs, unsigned *pcScriptArgs)
1445{
1446 int rc = VINF_SUCCESS;
1447 unsigned cMandatoryArgsReq = 0;
1448 unsigned cScriptArgs = 0;
1449
1450 /* Count the number of mandatory arguments first. */
1451 for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
1452 if (pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY)
1453 cMandatoryArgsReq++;
1454
1455 /* One argument is given in the form name=value. */
1456 *pcScriptArgs = 0;
1457
1458 while ( psz
1459 && *psz != '\0')
1460 {
1461 const char *pcszName = psz;
1462
1463 psz = tstVDIoScriptSkipUntil(psz, '=');
1464 if (psz != '\0')
1465 {
1466 *psz = '\0'; /* Overwrite */
1467 psz++;
1468 const char *pcszValue = psz;
1469
1470 psz = tstVDIoScriptSkipNonSpace(psz);
1471 if (psz != '\0')
1472 {
1473 *psz = '\0'; /* Overwrite */
1474 psz++;
1475 psz = tstVDIoScriptSkipSpace(psz);
1476
1477 /* We have the name and value pair now. */
1478 bool fMandatory;
1479 rc = tstVDIoScriptArgumentParse(pVDScriptAction, pcszName, pcszValue, &paScriptArgs[cScriptArgs], &fMandatory);
1480 if (RT_SUCCESS(rc))
1481 {
1482 if (fMandatory)
1483 cMandatoryArgsReq--;
1484 cScriptArgs++;
1485 }
1486 }
1487 else
1488 {
1489 RTPrintf("Value missing for argument '%s'\n", pcszName);
1490 rc = VERR_INVALID_STATE;
1491 break;
1492 }
1493 }
1494 else
1495 {
1496 RTPrintf("Argument in invalid form\n");
1497 rc = VERR_INVALID_STATE;
1498 break;
1499 }
1500 }
1501
1502 if ( RT_SUCCESS(rc)
1503 && cMandatoryArgsReq)
1504 {
1505 /* No arguments anymore but there are still mandatory arguments left. */
1506 RTPrintf("There are %u arguments missing for script action '%s\n", pVDScriptAction->pcszAction);
1507 rc = VERR_INVALID_STATE;
1508 }
1509
1510 if (RT_SUCCESS(rc))
1511 *pcScriptArgs = cScriptArgs;
1512
1513 return rc;
1514}
1515
1516/**
1517 * Executes the script pointed to by the given stream.
1518 *
1519 * @returns IPRT status code.
1520 *
1521 * @param pStrm The stream handle of the script.
1522 * @param pGlob Global test data.
1523 */
1524static int tstVDIoScriptExecute(PRTSTREAM pStrm, PVDTESTGLOB pGlob)
1525{
1526 int rc = VINF_SUCCESS;
1527 char abBuffer[0x1000]; /* Current assumption that a line is never longer than 4096 bytes. */
1528 PVDSCRIPTARG paScriptArgs = NULL;
1529 unsigned cScriptArgsMax = 0;
1530
1531 do
1532 {
1533 memset(abBuffer, 0, sizeof(abBuffer));
1534 rc = RTStrmGetLine(pStrm, abBuffer, sizeof(abBuffer));
1535 if (RT_SUCCESS(rc))
1536 {
1537 const char *pcszAction = NULL;
1538 char *psz = abBuffer;
1539
1540 /* Skip space */
1541 psz = tstVDIoScriptSkipSpace(psz);
1542 if (psz != '\0')
1543 {
1544 PCVDSCRIPTACTION pVDScriptAction = NULL;
1545
1546 /* Get the action name. */
1547 pcszAction = psz;
1548
1549 psz = tstVDIoScriptSkipNonSpace(psz);
1550 if (psz != '\0')
1551 {
1552 Assert(RT_C_IS_SPACE(*psz));
1553 *psz++ = '\0';
1554 }
1555
1556 /* Find the action. */
1557 for (unsigned i = 0; i < g_cScriptActions; i++)
1558 {
1559 if (!RTStrCmp(pcszAction, g_aScriptActions[i].pcszAction))
1560 {
1561 pVDScriptAction = &g_aScriptActions[i];
1562 break;
1563 }
1564 }
1565
1566 if (pVDScriptAction)
1567 {
1568 /* Parse arguments. */
1569 if (cScriptArgsMax < pVDScriptAction->cArgDescs)
1570 {
1571 /* Increase arguments array. */
1572 if (paScriptArgs)
1573 RTMemFree(paScriptArgs);
1574
1575 cScriptArgsMax = pVDScriptAction->cArgDescs;
1576 paScriptArgs = (PVDSCRIPTARG)RTMemAllocZ(cScriptArgsMax * sizeof(VDSCRIPTARG));
1577 }
1578
1579 if (paScriptArgs)
1580 {
1581 unsigned cScriptArgs;
1582
1583 rc = tstVDIoScriptArgumentListParse(psz, pVDScriptAction, paScriptArgs, &cScriptArgs);
1584 if (RT_SUCCESS(rc))
1585 {
1586 /* Execute the handler. */
1587 rc = pVDScriptAction->pfnHandler(pGlob, paScriptArgs, cScriptArgs);
1588 }
1589 }
1590 else
1591 {
1592 RTPrintf("Out of memory while allocating argument array for script action %s\n", pcszAction);
1593 rc = VERR_NO_MEMORY;
1594 }
1595 }
1596 else
1597 {
1598 RTPrintf("Script action %s is not known\n", pcszAction);
1599 rc = VERR_NOT_FOUND;
1600 }
1601 }
1602 else
1603 {
1604 RTPrintf("Missing action name\n");
1605 rc = VERR_INVALID_STATE;
1606 }
1607 }
1608 } while(RT_SUCCESS(rc));
1609
1610 if (rc == VERR_EOF)
1611 {
1612 RTPrintf("Successfully executed I/O script\n");
1613 rc = VINF_SUCCESS;
1614 }
1615 return rc;
1616}
1617
1618/**
1619 * Executes the given I/O script.
1620 *
1621 * @returns nothing.
1622 *
1623 * @param pcszFilename The script to execute.
1624 */
1625static void tstVDIoScriptRun(const char *pcszFilename)
1626{
1627 int rc = VINF_SUCCESS;
1628 PRTSTREAM pScriptStrm; /**< Stream of the script file. */
1629 VDTESTGLOB GlobTest; /**< Global test data. */
1630
1631 memset(&GlobTest, 0, sizeof(VDTESTGLOB));
1632 RTListInit(&GlobTest.ListFiles);
1633
1634 rc = RTStrmOpen(pcszFilename, "r", &pScriptStrm);
1635 if (RT_SUCCESS(rc))
1636 {
1637 /* Init global test data. */
1638 GlobTest.VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
1639 GlobTest.VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
1640 GlobTest.VDIErrorCallbacks.pfnError = tstVDError;
1641 GlobTest.VDIErrorCallbacks.pfnMessage = tstVDMessage;
1642
1643 rc = VDInterfaceAdd(&GlobTest.VDIError, "tstVDIo_VDIError", VDINTERFACETYPE_ERROR,
1644 &GlobTest.VDIErrorCallbacks, NULL, &GlobTest.pInterfacesDisk);
1645 AssertRC(rc);
1646
1647 GlobTest.VDIIoCallbacks.cbSize = sizeof(VDINTERFACEIO);
1648 GlobTest.VDIIoCallbacks.enmInterface = VDINTERFACETYPE_IO;
1649 GlobTest.VDIIoCallbacks.pfnOpen = tstVDIoFileOpen;
1650 GlobTest.VDIIoCallbacks.pfnClose = tstVDIoFileClose;
1651 GlobTest.VDIIoCallbacks.pfnDelete = tstVDIoFileDelete;
1652 GlobTest.VDIIoCallbacks.pfnMove = tstVDIoFileMove;
1653 GlobTest.VDIIoCallbacks.pfnGetFreeSpace = tstVDIoFileGetFreeSpace;
1654 GlobTest.VDIIoCallbacks.pfnGetModificationTime = tstVDIoFileGetModificationTime;
1655 GlobTest.VDIIoCallbacks.pfnGetSize = tstVDIoFileGetSize;
1656 GlobTest.VDIIoCallbacks.pfnSetSize = tstVDIoFileSetSize;
1657 GlobTest.VDIIoCallbacks.pfnWriteSync = tstVDIoFileWriteSync;
1658 GlobTest.VDIIoCallbacks.pfnReadSync = tstVDIoFileReadSync;
1659 GlobTest.VDIIoCallbacks.pfnFlushSync = tstVDIoFileFlushSync;
1660 GlobTest.VDIIoCallbacks.pfnReadAsync = tstVDIoFileReadAsync;
1661 GlobTest.VDIIoCallbacks.pfnWriteAsync = tstVDIoFileWriteAsync;
1662 GlobTest.VDIIoCallbacks.pfnFlushAsync = tstVDIoFileFlushAsync;
1663
1664 rc = VDInterfaceAdd(&GlobTest.VDIIo, "tstVDIo_VDIIo", VDINTERFACETYPE_IO,
1665 &GlobTest.VDIIoCallbacks, &GlobTest, &GlobTest.pInterfacesImages);
1666 AssertRC(rc);
1667
1668 /* Init I/O backend. */
1669 rc = VDIoBackendMemCreate(&GlobTest.pIoBackend);
1670 if (RT_SUCCESS(rc))
1671 {
1672 rc = VDCreate(GlobTest.pInterfacesDisk, VDTYPE_HDD, &GlobTest.pVD);
1673 if (RT_SUCCESS(rc))
1674 {
1675 /* Execute the script. */
1676 rc = tstVDIoScriptExecute(pScriptStrm, &GlobTest);
1677 if (RT_FAILURE(rc))
1678 {
1679 RTPrintf("Executing the script stream failed rc=%Rrc\n", rc);
1680 }
1681 }
1682 else
1683 RTPrintf("Failed to create disk container rc=%Rrc\n", rc);
1684 VDIoBackendMemDestroy(GlobTest.pIoBackend);
1685 }
1686 else
1687 RTPrintf("Creating the I/O backend failed rc=%Rrc\n");
1688
1689 RTStrmClose(pScriptStrm);
1690 }
1691 else
1692 RTPrintf("Opening script failed rc=%Rrc\n", rc);
1693}
1694
1695/**
1696 * Shows help message.
1697 */
1698static void printUsage(void)
1699{
1700 RTPrintf("Usage:\n"
1701 "--script <filename> Script to execute\n"
1702 "--replay <filename> Log to replay (not implemented yet)\n");
1703}
1704
1705static const RTGETOPTDEF g_aOptions[] =
1706{
1707 { "--script", 's', RTGETOPT_REQ_STRING },
1708 { "--replay", 'r', RTGETOPT_REQ_STRING },
1709};
1710
1711int main(int argc, char *argv[])
1712{
1713 RTR3Init();
1714 int rc;
1715 RTGETOPTUNION ValueUnion;
1716 RTGETOPTSTATE GetState;
1717 char c;
1718
1719 if (argc != 3)
1720 {
1721 printUsage();
1722 return RTEXITCODE_FAILURE;
1723 }
1724
1725 rc = VDInit();
1726 if (RT_FAILURE(rc))
1727 return RTEXITCODE_FAILURE;
1728
1729 RTGetOptInit(&GetState, argc, argv, g_aOptions,
1730 RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1731
1732 while ( RT_SUCCESS(rc)
1733 && (c = RTGetOpt(&GetState, &ValueUnion)))
1734 {
1735 switch (c)
1736 {
1737 case 's':
1738 tstVDIoScriptRun(ValueUnion.psz);
1739 break;
1740 case 'r':
1741 RTPrintf("Replaying I/O logs is not implemented yet\n");
1742 break;
1743 default:
1744 printUsage();
1745 }
1746 }
1747
1748 rc = VDShutdown();
1749 if (RT_FAILURE(rc))
1750 RTPrintf("tstVDIo: unloading backends failed! rc=%Rrc\n", rc);
1751
1752 return RTEXITCODE_SUCCESS;
1753}
1754
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette