VirtualBox

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

Last change on this file since 62482 was 62482, checked in by vboxsync, 8 years ago

(C) 2016

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