VirtualBox

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

Last change on this file since 28855 was 28800, checked in by vboxsync, 14 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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

© 2023 Oracle
ContactPrivacy policyTerms of Use