VirtualBox

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

Last change on this file since 86215 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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