VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/nt/ntFlushVirtualMemory.cpp@ 95818

Last change on this file since 95818 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.2 KB
Line 
1/* $Id: ntFlushVirtualMemory.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * Memory mapped files testcase - NT.
4 */
5
6/*
7 * Copyright (C) 2007-2022 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/nt/nt.h>
32
33#include <iprt/alloca.h>
34#include <iprt/file.h>
35#include <iprt/getopt.h>
36#include <iprt/initterm.h>
37#include <iprt/mem.h>
38#include <iprt/message.h>
39#include <iprt/path.h>
40#include <iprt/stream.h>
41#include <iprt/string.h>
42#include <iprt/thread.h>
43#include <iprt/err.h>
44#include <iprt/x86.h>
45
46
47/*********************************************************************************************************************************
48* Defined Constants And Macros *
49*********************************************************************************************************************************/
50/** Create page signature. */
51#define MAKE_PAGE_SIGNATURE(a_iPage) ((a_iPage) | UINT32_C(0x42000000) )
52
53/** Number history entries on the page. */
54#define NUM_ROUND_HISTORY 16
55
56
57/*********************************************************************************************************************************
58* Global Variables *
59*********************************************************************************************************************************/
60/** How chatty we should be. */
61static uint32_t g_cVerbosity = 0;
62
63
64/**
65 * Checks if the on-disk file matches our expectations.
66 *
67 * @returns IPRT status code, fully bitched.
68 * @param pszFilename The name of the file.
69 * @param pu32BufChk Buffer to read the file into
70 * @param pu32BufOrg Expected file content.
71 * @param cbBuf The buffer size.
72 * @param iRound The update round.
73 */
74static int CheckFile(const char *pszFilename, uint32_t *pu32BufChk, uint32_t const *pu32BufOrg, size_t cbBuf, uint32_t iRound)
75{
76 /*
77 * Open and read the file into memory.
78 */
79 HANDLE hFile;
80 int rc = RTNtPathOpen(pszFilename,
81 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
82 FILE_ATTRIBUTE_NORMAL,
83 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
84 FILE_OPEN,
85 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NO_INTERMEDIATE_BUFFERING,
86 OBJ_CASE_INSENSITIVE,
87 &hFile,
88 NULL);
89 if (RT_SUCCESS(rc))
90 {
91 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
92 NTSTATUS rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*pfnApc*/, NULL /*pvApcCtx*/,
93 &Ios, pu32BufChk, (ULONG)cbBuf, NULL /*poffFile*/, NULL /*pvKey*/);
94 if (NT_SUCCESS(rcNt) || Ios.Information != cbBuf)
95 {
96 /*
97 * See if the content of the file matches our expectations.
98 */
99 if (memcmp(pu32BufChk, pu32BufOrg, cbBuf) == 0)
100 { /* matches - likely */ }
101 else
102 {
103 RTMsgError("Round %u: Buffer mismatch!\n", iRound);
104
105 /* Try figure where the differences are. */
106 size_t const cPages = cbBuf / X86_PAGE_SIZE;
107 size_t const cItemsPerPage = X86_PAGE_SIZE / sizeof(pu32BufOrg[0]);
108 for (uint32_t iPage = 0; iPage < cPages; iPage++)
109 for (uint32_t iItem = 0; iItem < cItemsPerPage; iItem++)
110 {
111 uint32_t uValue = pu32BufChk[iPage * cItemsPerPage + iItem];
112 uint32_t uExpected = pu32BufOrg[iPage * cItemsPerPage + iItem];
113 if (uValue != uExpected)
114 RTMsgError("Round %u: page #%u, index #%u: %#x, expected %#x\n",
115 iRound, iPage, iItem, uValue, uExpected);
116 }
117
118 rc = VERR_MISMATCH;
119 }
120 }
121 else if (NT_SUCCESS(rcNt))
122 {
123 RTMsgError("Round %u: NtReadFile return %zu bytes intead of %zu!\n", iRound, Ios.Information, cbBuf);
124 rc = VERR_READ_ERROR;
125 }
126 else
127 {
128 RTMsgError("Round %u: NtReadFile(%#x) failed: %#x (%#x)\n", iRound, cbBuf, rcNt, Ios.Status);
129 rc = RTErrConvertFromNtStatus(rcNt);
130 }
131
132 /*
133 * Close the file and return.
134 */
135 rcNt = NtClose(hFile);
136 if (!NT_SUCCESS(rcNt))
137 {
138 RTMsgError("Round %u: NtCloseFile() failed: %#x\n", iRound, rcNt);
139 rc = RTErrConvertFromNtStatus(rcNt);
140 }
141 }
142 else
143 RTMsgError("Round %u: RTNtPathOpen() failed: %Rrc\n", iRound, rc);
144 return rc;
145}
146
147
148/**
149 * Manually checks whether the buffer matches up to our expectations.
150 *
151 * @returns IPRT status code, fully bitched.
152 * @param pu32Buf The buffer/mapping to check.
153 * @param cbBuf The buffer size.
154 * @param iRound The update round.
155 * @param cFlushesLeft Number of flushes left in the round.
156 */
157static int CheckBuffer(uint32_t const *pu32Buf, size_t cbBuf, uint32_t iRound, uint32_t cFlushesLeft)
158{
159 size_t const cPages = cbBuf / X86_PAGE_SIZE;
160 size_t const cItemsPerPage = X86_PAGE_SIZE / sizeof(pu32Buf[0]);
161 size_t const offPage = iRound & (NUM_ROUND_HISTORY - 1);
162 uint32_t const uValue = iRound | (cFlushesLeft << 20);
163//RTPrintf("debug: CheckBuffer: %p %u/%u\n", pu32Buf, iRound, cFlushesLeft);
164
165 for (uint32_t iPage = 0; iPage < cPages; iPage++)
166 {
167 uint32_t uActual = pu32Buf[iPage * cItemsPerPage + offPage];
168 if (uActual != uValue)
169 {
170 RTMsgError("Round %u/%u: page #%u: last entry is corrupted: %#x, expected %#x\n",
171 iRound, cFlushesLeft, iPage, uActual, uValue);
172 return VERR_MISMATCH;
173 }
174
175 uActual = pu32Buf[iPage * cItemsPerPage + cItemsPerPage - 1];
176 if (uActual != MAKE_PAGE_SIGNATURE(iPage))
177 {
178 RTMsgError("Round %u/%u: page #%u magic corrupted: %#x, expected %#x\n",
179 iRound, cFlushesLeft, iPage, uActual, MAKE_PAGE_SIGNATURE(iPage));
180 return VERR_INVALID_MAGIC;
181 }
182 }
183
184 /*
185 * Check previous rounds.
186 */
187 for (uint32_t cRoundsAgo = 1; cRoundsAgo < NUM_ROUND_HISTORY - 1 && cRoundsAgo <= iRound; cRoundsAgo++)
188 {
189 uint32_t iOldRound = iRound - cRoundsAgo;
190 size_t const offOldPage = iOldRound & (NUM_ROUND_HISTORY - 1);
191 for (uint32_t iPage = 0; iPage < cPages; iPage++)
192 {
193 uint32_t uActual = pu32Buf[iPage * cItemsPerPage + offOldPage];
194 if (uActual != iOldRound)
195 {
196 RTMsgError("Round %u/%u: page #%u: entry from %u rounds ago is corrupted: %#x, expected %#x\n",
197 iRound, cFlushesLeft, iPage, cRoundsAgo, uActual, uValue);
198 return VERR_MISMATCH;
199 }
200 }
201 }
202
203 return VINF_SUCCESS;
204}
205
206
207/**
208 * Updates the buffer.
209 *
210 * @param pu32Buf The buffer/mapping to update.
211 * @param cbBuf The buffer size.
212 * @param iRound The update round.
213 * @param cFlushesLeft Number of flushes left in this round.
214 */
215static void UpdateBuffer(uint32_t *pu32Buf, size_t cbBuf, uint32_t iRound, uint32_t cFlushesLeft)
216{
217 size_t const cPages = cbBuf / X86_PAGE_SIZE;
218 size_t const cItemsPerPage = X86_PAGE_SIZE / sizeof(pu32Buf[0]);
219 size_t const offPage = iRound & (NUM_ROUND_HISTORY - 1);
220 uint32_t const uValue = iRound | (cFlushesLeft << 20);
221//RTPrintf("debug: UpdateBuffer: %p %u/%u\n", pu32Buf, iRound, cFlushesLeft);
222
223 for (uint32_t iPage = 0; iPage < cPages; iPage++)
224 pu32Buf[iPage * cItemsPerPage + offPage] = uValue;
225}
226
227
228
229/**
230 * Modifies the file via memory mapping.
231 *
232 * @returns IPRT status code, fully bitched.
233 * @param pszFilename The file we're using as a test bed.
234 * @param pu32BufOrg The sane copy of the file that gets updated in
235 * parallel.
236 * @param cbBuf The size of the file and bufer.
237 * @param iRound The current round number.
238 * @param fCheckFirst Whether to read from the mapping the mapping
239 * before dirtying it the first time around.
240 * @param fCheckAfterFlush Whether to read from the mapping the mapping
241 * before dirtying it after a flush.
242 * @param cFlushes How many times we modify the mapping and flush
243 * it before one final modification and unmapping.
244 * @param fLargePages Whether to use large pages.
245 */
246static int MakeModifications(const char *pszFilename, uint32_t *pu32BufOrg, size_t cbBuf, uint32_t iRound,
247 bool fCheckFirst, bool fCheckAfterFlush, uint32_t cFlushes, bool fLargePages)
248{
249
250 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
251 int rc = RTNtPathOpen(pszFilename,
252 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
253 FILE_ATTRIBUTE_NORMAL,
254 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
255 FILE_OPEN,
256 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NO_INTERMEDIATE_BUFFERING,
257 OBJ_CASE_INSENSITIVE,
258 &hFile,
259 NULL);
260 if (RT_SUCCESS(rc))
261 {
262 HANDLE hSection;
263 NTSTATUS rcNt = NtCreateSection(&hSection,
264 SECTION_ALL_ACCESS,
265 NULL, /*pObjAttrs*/
266 NULL, /*pcbMax*/
267 PAGE_READWRITE,
268 SEC_COMMIT,
269 hFile);
270 NtClose(hFile);
271 if (NT_SUCCESS(rcNt))
272 {
273 PVOID pvMapping = NULL;
274 SIZE_T cbMapping = 0;
275 rcNt = NtMapViewOfSection(hSection, NtCurrentProcess(),
276 &pvMapping,
277 0, /* ZeroBits */
278 0, /* CommitSize */
279 NULL, /* SectionOffset */
280 &cbMapping,
281 ViewUnmap,
282 fLargePages ? MEM_LARGE_PAGES : 0,
283 PAGE_READWRITE);
284 if (NT_SUCCESS(rcNt))
285 {
286 /*
287 * Make the modifications.
288 */
289 if (g_cVerbosity >= 2)
290 RTPrintf("debug: pvMapping=%p LB %#x\n", pvMapping, cbBuf);
291
292 for (uint32_t iInner = 0;; iInner++)
293 {
294 if (iInner ? fCheckAfterFlush : fCheckFirst)
295 {
296 if (iInner == 0)
297 rc = CheckBuffer((uint32_t *)pvMapping, cbBuf, iRound - 1, 0);
298 else
299 rc = CheckBuffer((uint32_t *)pvMapping, cbBuf, iRound, cFlushes - iInner + 1);
300 if (RT_FAILURE(rc))
301 {
302 RTMsgError("Round %u/%u: NtUnmapViewOfSection failed: %#x\n", iRound, rcNt);
303 break;
304 }
305 }
306
307 UpdateBuffer((uint32_t *)pvMapping, cbBuf, iRound, cFlushes - iInner);
308 UpdateBuffer(pu32BufOrg, cbBuf, iRound, cFlushes - iInner);
309
310 if (iInner >= cFlushes)
311 break;
312
313 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
314 SIZE_T cbBuf2 = cbBuf;
315 PVOID pvMapping2 = pvMapping;
316 rcNt = NtFlushVirtualMemory(NtCurrentProcess(), &pvMapping2, &cbBuf2, &Ios);
317 if (!NT_SUCCESS(rcNt))
318 {
319 RTMsgError("Round %u: NtFlushVirtualMemory failed: %#x\n", iRound, rcNt);
320 rc = RTErrConvertFromNtStatus(rcNt);
321 break;
322 }
323 }
324
325 /*
326 * Cleanup.
327 */
328 rcNt = NtUnmapViewOfSection(NtCurrentProcess(), pvMapping);
329 if (!NT_SUCCESS(rcNt))
330 {
331 RTMsgError("Round %u: NtUnmapViewOfSection failed: %#x\n", iRound, rcNt);
332 rc = RTErrConvertFromNtStatus(rcNt);
333 }
334 }
335 else
336 {
337 RTMsgError("Round %u: NtMapViewOfSection failed: %#x\n", iRound, rcNt);
338 rc = RTErrConvertFromNtStatus(rcNt);
339 }
340
341 rcNt = NtClose(hSection);
342 if (!NT_SUCCESS(rcNt))
343 {
344 RTMsgError("Round %u: NtClose(hSection) failed: %#x\n", iRound, rcNt);
345 rc = RTErrConvertFromNtStatus(rcNt);
346 }
347 }
348 else
349 {
350 RTMsgError("Round %u: NtCreateSection failed: %#x\n", iRound, rcNt);
351 rc = RTErrConvertFromNtStatus(rcNt);
352 }
353 }
354 else
355 RTMsgError("Round %u: Error opening file '%s' for memory mapping: %Rrc\n", iRound, pszFilename, rc);
356 return rc;
357}
358
359
360int main(int argc, char **argv)
361{
362 /*
363 * Init IPRT.
364 */
365 int rc = RTR3InitExe(argc, &argv, 0);
366 if (RT_FAILURE(rc))
367 return RTMsgInitFailure(rc);
368
369 /*
370 * Parse arguments.
371 */
372 const char *pszFilename = NULL;
373 uint32_t cRounds = 4096;
374 uint32_t cPages = 128;
375 bool fLargePages = false;
376
377 static const RTGETOPTDEF s_aOptions[] =
378 {
379 { "--rounds", 'r', RTGETOPT_REQ_UINT32 },
380 { "--pages", 'p', RTGETOPT_REQ_UINT32 },
381 { "--filename", 'f', RTGETOPT_REQ_STRING },
382 { "--large-pages", 'l', RTGETOPT_REQ_NOTHING },
383 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
384 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
385 };
386
387 RTGETOPTSTATE State;
388 RTGetOptInit(&State, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
389 RTGETOPTUNION ValueUnion;
390 int chOpt;
391 while ((chOpt = RTGetOpt(&State, &ValueUnion)) != 0)
392 {
393 switch (chOpt)
394 {
395 case 'r': cRounds = ValueUnion.u32; break;
396 case 'p': cPages = ValueUnion.u32; break;
397 case 'f': pszFilename = ValueUnion.psz; break;
398 case 'l': fLargePages = true; break;
399 case 'q': g_cVerbosity = 0; break;
400 case 'v': g_cVerbosity += 1; break;
401 case 'h':
402 RTPrintf("usage: ntFlushVirtualMemory [-c <times>] [-p <pages>] [-l|--large-pages] [-f <filename>]\n"
403 "\n"
404 "Aims at testing memory mapped files on NT w/ NtFlushVirtualMemory / FlushViewOfFile.\n");
405 return 0;
406
407 default:
408 return RTGetOptPrintError(chOpt, &ValueUnion);
409 }
410 }
411
412 /*
413 * Allocate buffers and initialize the original with page numbers.
414 *
415 * We keep a original copy that gets updated in parallel to the memory
416 * mapping, allowing for simple file initialization and memcpy checking.
417 *
418 * The second buffer is for reading the file from disk and check.
419 */
420 size_t const cbBuf = cPages * X86_PAGE_SIZE;
421 size_t const cItemsPerPage = X86_PAGE_SIZE / sizeof(uint32_t);
422 uint32_t *pu32BufOrg = (uint32_t *)RTMemPageAllocZ(cbBuf);
423 uint32_t *pu32BufChk = (uint32_t *)RTMemPageAllocZ(cbBuf);
424 if (pu32BufOrg == NULL || pu32BufChk == NULL)
425 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to allocate two %zu sized buffers!\n", cbBuf);
426
427 for (uint32_t iPage = 0; iPage < cPages; iPage++)
428 pu32BufOrg[iPage * cItemsPerPage + cItemsPerPage - 1] = MAKE_PAGE_SIGNATURE(iPage);
429
430 rc = CheckBuffer(pu32BufOrg, cbBuf, 0, 0);
431 if (RT_FAILURE(rc))
432 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Internal error: CheckBuffer failed on virgin buffer: %Rrc\n", rc);
433
434 /*
435 * Open the file and write out the orignal one.
436 */
437 RTFILE hFile;
438 if (!pszFilename)
439 {
440 char *pszBuf = (char *)alloca(RTPATH_MAX);
441 rc = RTFileOpenTemp(&hFile, pszBuf, RTPATH_MAX, RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_NONE);
442 if (RT_FAILURE(rc))
443 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create temporary file: %Rrc\n", rc);
444 pszFilename = pszBuf;
445 }
446 else
447 {
448 rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_READWRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE);
449 if (RT_FAILURE(rc))
450 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open '%s': %Rrc\n", rc);
451 }
452
453 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
454
455 rc = RTFileWrite(hFile, pu32BufOrg, cbBuf, NULL);
456 if (RT_SUCCESS(rc))
457 {
458 RTFileClose(hFile);
459
460 /*
461 * Do the rounds. We count from 1 here to make verifying the previous round simpler.
462 */
463 for (uint32_t iRound = 1; iRound <= cRounds; iRound++)
464 {
465 rc = MakeModifications(pszFilename, pu32BufOrg, cbBuf, iRound,
466 ((iRound >> 5) & 1) == 1, ((iRound >> 5) & 3) == 3, (iRound >> 3) & 31, fLargePages);
467 if (RT_SUCCESS(rc))
468 {
469 rc = CheckBuffer(pu32BufOrg, cbBuf, iRound, 0);
470 if (RT_SUCCESS(rc))
471 {
472 rc = CheckFile(pszFilename, pu32BufChk, pu32BufOrg, cbBuf, iRound);
473 if (RT_SUCCESS(rc))
474 continue;
475 }
476 }
477 break;
478 }
479 }
480 else
481 {
482 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Error writing initial %zu bytes to '%s': %Rrc\n", cbBuf, rc);
483 RTFileClose(hFile);
484 }
485 RTFileDelete(pszFilename);
486 return rcExit;
487}
488
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