VirtualBox

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

Last change on this file since 27797 was 27797, checked in by vboxsync, 15 years ago

misc compiler warning fixes, comment typos and other minor cleanups

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