VirtualBox

source: vbox/trunk/src/VBox/Storage/testcase/tstVDSnap.cpp@ 57358

Last change on this file since 57358 was 57358, checked in by vboxsync, 10 years ago

*: scm cleanup run.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 KB
Line 
1/** @file
2 *
3 * Snapshot VBox HDD container test utility.
4 */
5
6/*
7 * Copyright (C) 2010-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
18#include <VBox/vd.h>
19#include <VBox/err.h>
20#include <VBox/log.h>
21#include <iprt/asm.h>
22#include <iprt/dir.h>
23#include <iprt/string.h>
24#include <iprt/stream.h>
25#include <iprt/file.h>
26#include <iprt/mem.h>
27#include <iprt/initterm.h>
28#include <iprt/rand.h>
29
30/**
31 * A VD snapshot test.
32 */
33typedef struct VDSNAPTEST
34{
35 /** Backend to use */
36 const char *pcszBackend;
37 /** Base image name */
38 const char *pcszBaseImage;
39 /** Diff image ending */
40 const char *pcszDiffSuff;
41 /** Number of iterations before the test exits */
42 uint32_t cIterations;
43 /** Test pattern size */
44 size_t cbTestPattern;
45 /** Minimum number of disk segments */
46 uint32_t cDiskSegsMin;
47 /** Miaximum number of disk segments */
48 uint32_t cDiskSegsMax;
49 /** Minimum number of diffs needed before a merge
50 * operation can occur */
51 unsigned cDiffsMinBeforeMerge;
52 /** Chance to get create instead of a merge operation */
53 uint32_t uCreateDiffChance;
54 /** Chance to change a segment after a diff was created */
55 uint32_t uChangeSegChance;
56 /** Numer of allocated blocks in the base image in percent */
57 uint32_t uAllocatedBlocks;
58 /** Merge direction */
59 bool fForward;
60} VDSNAPTEST, *PVDSNAPTEST;
61
62/**
63 * Structure defining a disk segment.
64 */
65typedef struct VDDISKSEG
66{
67 /** Start offset in the disk. */
68 uint64_t off;
69 /** Size of the segment. */
70 uint64_t cbSeg;
71 /** Pointer to the start of the data in the test pattern used for the segment. */
72 uint8_t *pbData;
73 /** Pointer to the data for a diff write */
74 uint8_t *pbDataDiff;
75} VDDISKSEG, *PVDDISKSEG;
76
77
78/*********************************************************************************************************************************
79* Global Variables *
80*********************************************************************************************************************************/
81/** The error count. */
82unsigned g_cErrors = 0;
83/** Global RNG state. */
84RTRAND g_hRand;
85
86static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
87 const char *pszFormat, va_list va)
88{
89 g_cErrors++;
90 RTPrintf("tstVD: Error %Rrc at %s:%u (%s): ", rc, RT_SRC_POS_ARGS);
91 RTPrintfV(pszFormat, va);
92 RTPrintf("\n");
93}
94
95static int tstVDMessage(void *pvUser, const char *pszFormat, va_list va)
96{
97 RTPrintf("tstVD: ");
98 RTPrintfV(pszFormat, va);
99 return VINF_SUCCESS;
100}
101
102/**
103 * Returns true with the given chance in percent.
104 *
105 * @returns true or false
106 * @param iPercentage The percentage of the chance to return true.
107 */
108static bool tstVDSnapIsTrue(int iPercentage)
109{
110 int uRnd = RTRandAdvU32Ex(g_hRand, 0, 100);
111
112 return (uRnd <= iPercentage); /* This should be enough for our purpose */
113}
114
115static void tstVDSnapSegmentsDice(PVDSNAPTEST pTest, PVDDISKSEG paDiskSeg, uint32_t cDiskSegments,
116 uint8_t *pbTestPattern, size_t cbTestPattern)
117{
118 for (uint32_t i = 0; i < cDiskSegments; i++)
119 {
120 /* Do we want to change the current segment? */
121 if (tstVDSnapIsTrue(pTest->uChangeSegChance))
122 paDiskSeg[i].pbDataDiff = pbTestPattern + RT_ALIGN_64(RTRandAdvU64Ex(g_hRand, 0, cbTestPattern - paDiskSeg[i].cbSeg - 512), 512);
123 }
124}
125
126static int tstVDSnapWrite(PVBOXHDD pVD, PVDDISKSEG paDiskSegments,
127 uint32_t cDiskSegments, uint64_t cbDisk, bool fInit)
128{
129 int rc = VINF_SUCCESS;
130
131 for (uint32_t i = 0; i < cDiskSegments; i++)
132 {
133 if (fInit || paDiskSegments[i].pbDataDiff)
134 {
135 size_t cbWrite = paDiskSegments[i].cbSeg;
136 uint64_t off = paDiskSegments[i].off;
137 uint8_t *pbData = fInit
138 ? paDiskSegments[i].pbData
139 : paDiskSegments[i].pbDataDiff;
140
141 if (pbData)
142 {
143 rc = VDWrite(pVD, off, pbData, cbWrite);
144 if (RT_FAILURE(rc))
145 return rc;
146 }
147 }
148 }
149
150 return rc;
151}
152
153static int tstVDSnapReadVerify(PVBOXHDD pVD, PVDDISKSEG paDiskSegments, uint32_t cDiskSegments, uint64_t cbDisk)
154{
155 int rc = VINF_SUCCESS;
156 uint8_t *pbBuf = (uint8_t *)RTMemAlloc(_1M);
157
158 for (uint32_t i = 0; i < cDiskSegments; i++)
159 {
160 size_t cbRead = paDiskSegments[i].cbSeg;
161 uint64_t off = paDiskSegments[i].off;
162 uint8_t *pbCmp = paDiskSegments[i].pbData;
163
164 Assert(!paDiskSegments[i].pbDataDiff);
165
166 while (cbRead)
167 {
168 size_t cbToRead = RT_MIN(cbRead, _1M);
169
170 rc = VDRead(pVD, off, pbBuf, cbToRead);
171 if (RT_FAILURE(rc))
172 return rc;
173
174 if (pbCmp)
175 {
176 if (memcmp(pbCmp, pbBuf, cbToRead))
177 {
178 for (unsigned iCmp = 0; iCmp < cbToRead; iCmp++)
179 {
180 if (pbCmp[iCmp] != pbBuf[iCmp])
181 {
182 RTPrintf("Unexpected data at %llu expected %#x got %#x\n", off+iCmp, pbCmp[iCmp], pbBuf[iCmp]);
183 break;
184 }
185 }
186 return VERR_INTERNAL_ERROR;
187 }
188 }
189 else
190 {
191 /* Verify that the block is 0 */
192 for (unsigned iCmp = 0; iCmp < cbToRead; iCmp++)
193 {
194 if (pbBuf[iCmp] != 0)
195 {
196 RTPrintf("Zero block contains data at %llu\n", off+iCmp);
197 return VERR_INTERNAL_ERROR;
198 }
199 }
200 }
201
202 cbRead -= cbToRead;
203 off += cbToRead;
204
205 if (pbCmp)
206 pbCmp += cbToRead;
207 }
208 }
209
210 RTMemFree(pbBuf);
211
212 return rc;
213}
214
215static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
216{
217 int rc;
218 PVBOXHDD pVD = NULL;
219 VDGEOMETRY PCHS = { 0, 0, 0 };
220 VDGEOMETRY LCHS = { 0, 0, 0 };
221 PVDINTERFACE pVDIfs = NULL;
222 VDINTERFACEERROR VDIfError;
223
224 /** Buffer storing the random test pattern. */
225 uint8_t *pbTestPattern = NULL;
226 /** Number of disk segments */
227 uint32_t cDiskSegments;
228 /** Array of disk segments */
229 PVDDISKSEG paDiskSeg = NULL;
230 unsigned cDiffs = 0;
231 unsigned idDiff = 0; /* Diff ID counter for the filename */
232
233 /* Delete all images from a previous run. */
234 RTFileDelete(pTest->pcszBaseImage);
235 for (unsigned i = 0; i < pTest->cIterations; i++)
236 {
237 char *pszDiffFilename = NULL;
238
239 rc = RTStrAPrintf(&pszDiffFilename, "tstVDSnapDiff%u.%s", i, pTest->pcszDiffSuff);
240 if (RT_SUCCESS(rc))
241 {
242 if (RTFileExists(pszDiffFilename))
243 RTFileDelete(pszDiffFilename);
244 RTStrFree(pszDiffFilename);
245 }
246 }
247
248 /* Create the virtual disk test data */
249 pbTestPattern = (uint8_t *)RTMemAlloc(pTest->cbTestPattern);
250
251 RTRandAdvBytes(g_hRand, pbTestPattern, pTest->cbTestPattern);
252 cDiskSegments = RTRandAdvU32Ex(g_hRand, pTest->cDiskSegsMin, pTest->cDiskSegsMax);
253
254 uint64_t cbDisk = 0;
255
256 paDiskSeg = (PVDDISKSEG)RTMemAllocZ(cDiskSegments * sizeof(VDDISKSEG));
257 if (!paDiskSeg)
258 {
259 RTPrintf("Failed to allocate memory for random disk segments\n");
260 g_cErrors++;
261 return VERR_NO_MEMORY;
262 }
263
264 for (unsigned i = 0; i < cDiskSegments; i++)
265 {
266 paDiskSeg[i].off = cbDisk;
267 paDiskSeg[i].cbSeg = RT_ALIGN_64(RTRandAdvU64Ex(g_hRand, 512, pTest->cbTestPattern), 512);
268 if (tstVDSnapIsTrue(pTest->uAllocatedBlocks))
269 paDiskSeg[i].pbData = pbTestPattern + RT_ALIGN_64(RTRandAdvU64Ex(g_hRand, 0, pTest->cbTestPattern - paDiskSeg[i].cbSeg - 512), 512);
270 else
271 paDiskSeg[i].pbData = NULL; /* Not allocated initially */
272 cbDisk += paDiskSeg[i].cbSeg;
273 }
274
275 RTPrintf("Disk size is %llu bytes\n", cbDisk);
276
277#define CHECK(str) \
278 do \
279 { \
280 RTPrintf("%s rc=%Rrc\n", str, rc); \
281 if (RT_FAILURE(rc)) \
282 { \
283 if (pbTestPattern) \
284 RTMemFree(pbTestPattern); \
285 if (paDiskSeg) \
286 RTMemFree(paDiskSeg); \
287 VDDestroy(pVD); \
288 g_cErrors++; \
289 return rc; \
290 } \
291 } while (0)
292
293#define CHECK_BREAK(str) \
294 do \
295 { \
296 RTPrintf("%s rc=%Rrc\n", str, rc); \
297 if (RT_FAILURE(rc)) \
298 { \
299 g_cErrors++; \
300 break; \
301 } \
302 } while (0)
303
304 /* Create error interface. */
305 /* Create error interface. */
306 VDIfError.pfnError = tstVDError;
307 VDIfError.pfnMessage = tstVDMessage;
308
309 rc = VDInterfaceAdd(&VDIfError.Core, "tstVD_Error", VDINTERFACETYPE_ERROR,
310 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
311 AssertRC(rc);
312
313
314 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pVD);
315 CHECK("VDCreate()");
316
317 rc = VDCreateBase(pVD, pTest->pcszBackend, pTest->pcszBaseImage, cbDisk,
318 VD_IMAGE_FLAGS_NONE, "Test image",
319 &PCHS, &LCHS, NULL, VD_OPEN_FLAGS_NORMAL,
320 NULL, NULL);
321 CHECK("VDCreateBase()");
322
323 bool fInit = true;
324 uint32_t cIteration = 0;
325
326 /* Do the real work now */
327 while ( RT_SUCCESS(rc)
328 && cIteration < pTest->cIterations)
329 {
330 /* Write */
331 rc = tstVDSnapWrite(pVD, paDiskSeg, cDiskSegments, cbDisk, fInit);
332 CHECK_BREAK("tstVDSnapWrite()");
333
334 fInit = false;
335
336 /* Write returned, do we want to create a new diff or merge them? */
337 bool fCreate = cDiffs < pTest->cDiffsMinBeforeMerge
338 ? true
339 : tstVDSnapIsTrue(pTest->uCreateDiffChance);
340
341 if (fCreate)
342 {
343 char *pszDiffFilename = NULL;
344
345 RTStrAPrintf(&pszDiffFilename, "tstVDSnapDiff%u.%s", idDiff, pTest->pcszDiffSuff);
346 CHECK("RTStrAPrintf()");
347 idDiff++;
348 cDiffs++;
349
350 rc = VDCreateDiff(pVD, pTest->pcszBackend, pszDiffFilename,
351 VD_IMAGE_FLAGS_NONE, "Test diff image", NULL, NULL,
352 VD_OPEN_FLAGS_NORMAL, NULL, NULL);
353 CHECK_BREAK("VDCreateDiff()");
354
355 RTStrFree(pszDiffFilename);
356 VDDumpImages(pVD);
357
358 /* Change data */
359 tstVDSnapSegmentsDice(pTest, paDiskSeg, cDiskSegments, pbTestPattern, pTest->cbTestPattern);
360 }
361 else
362 {
363 uint32_t uStartMerge = RTRandAdvU32Ex(g_hRand, 1, cDiffs - 1);
364 uint32_t uEndMerge = RTRandAdvU32Ex(g_hRand, uStartMerge + 1, cDiffs);
365 RTPrintf("Merging %u diffs from %u to %u...\n",
366 uEndMerge - uStartMerge,
367 uStartMerge,
368 uEndMerge);
369 if (pTest->fForward)
370 rc = VDMerge(pVD, uStartMerge, uEndMerge, NULL);
371 else
372 rc = VDMerge(pVD, uEndMerge, uStartMerge, NULL);
373 CHECK_BREAK("VDMerge()");
374
375 cDiffs -= uEndMerge - uStartMerge;
376
377 VDDumpImages(pVD);
378
379 /* Go through the disk segments and reset pointers. */
380 for (uint32_t i = 0; i < cDiskSegments; i++)
381 {
382 if (paDiskSeg[i].pbDataDiff)
383 {
384 paDiskSeg[i].pbData = paDiskSeg[i].pbDataDiff;
385 paDiskSeg[i].pbDataDiff = NULL;
386 }
387 }
388
389 /* Now compare the result with our test pattern */
390 rc = tstVDSnapReadVerify(pVD, paDiskSeg, cDiskSegments, cbDisk);
391 CHECK_BREAK("tstVDSnapReadVerify()");
392 }
393 cIteration++;
394 }
395
396 VDDumpImages(pVD);
397
398 VDDestroy(pVD);
399 if (paDiskSeg)
400 RTMemFree(paDiskSeg);
401 if (pbTestPattern)
402 RTMemFree(pbTestPattern);
403
404 RTFileDelete(pTest->pcszBaseImage);
405 for (unsigned i = 0; i < idDiff; i++)
406 {
407 char *pszDiffFilename = NULL;
408
409 RTStrAPrintf(&pszDiffFilename, "tstVDSnapDiff%u.%s", i, pTest->pcszDiffSuff);
410 RTFileDelete(pszDiffFilename);
411 RTStrFree(pszDiffFilename);
412 }
413#undef CHECK
414 return rc;
415}
416
417int main(int argc, char *argv[])
418{
419 RTR3InitExe(argc, &argv, 0);
420 int rc;
421 VDSNAPTEST Test;
422
423 RTPrintf("tstVDSnap: TESTING...\n");
424
425 rc = RTRandAdvCreateParkMiller(&g_hRand);
426 if (RT_FAILURE(rc))
427 {
428 RTPrintf("tstVDSnap: Creating RNG failed rc=%Rrc\n", rc);
429 return 1;
430 }
431
432 RTRandAdvSeed(g_hRand, 0x12345678);
433
434 Test.pcszBackend = "vmdk";
435 Test.pcszBaseImage = "tstVDSnapBase.vmdk";
436 Test.pcszDiffSuff = "vmdk";
437 Test.cIterations = 30;
438 Test.cbTestPattern = 10 * _1M;
439 Test.cDiskSegsMin = 10;
440 Test.cDiskSegsMax = 50;
441 Test.cDiffsMinBeforeMerge = 5;
442 Test.uCreateDiffChance = 50; /* % */
443 Test.uChangeSegChance = 50; /* % */
444 Test.uAllocatedBlocks = 50; /* 50% allocated */
445 Test.fForward = true;
446 tstVDOpenCreateWriteMerge(&Test);
447
448 /* Same test with backwards merge */
449 Test.fForward = false;
450 tstVDOpenCreateWriteMerge(&Test);
451
452 rc = VDShutdown();
453 if (RT_FAILURE(rc))
454 {
455 RTPrintf("tstVDSnap: unloading backends failed! rc=%Rrc\n", rc);
456 g_cErrors++;
457 }
458 /*
459 * Summary
460 */
461 if (!g_cErrors)
462 RTPrintf("tstVDSnap: SUCCESS\n");
463 else
464 RTPrintf("tstVDSnap: FAILURE - %d errors\n", g_cErrors);
465
466 RTRandAdvDestroy(g_hRand);
467
468 return !!g_cErrors;
469}
470
Note: See TracBrowser for help on using the repository browser.

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